Skip to content

fix: packet-only hosted default + no live-provenance claims on synthetic data - #4

Merged
CJud25 merged 3 commits into
mainfrom
fix/hosted-boundary-and-synthetic-assurance
Jul 26, 2026
Merged

fix: packet-only hosted default + no live-provenance claims on synthetic data#4
CJud25 merged 3 commits into
mainfrom
fix/hosted-boundary-and-synthetic-assurance

Conversation

@CJud25

@CJud25 CJud25 commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Two defects, both live on https://reconradar.streamlit.app right now.

1. The hosted case ledger was shared across all visitors

bd_page.py cached a single CaseRepository via @st.cache_resource bound to one on-disk SQLite path, with no per-session key and no tenant column — every query filters on WHERE case_id=? only. On a shared URL, any two visitors read and mutate the same rows. The public app ships Cases / New Case / Scan & Evidence tabs, so this is reachable, not theoretical.

The README already documented this and already prescribed the remedy ("host only the packet-only surface"). This implements it.

Fail-safe default: the ledger is now opt-in via TENS_HQ_ENABLE_CASE_LEDGER=1. A deployment with no configuration is packet-only. It is deliberately not "hide it when a PUBLIC flag is set" — that leaves the app exposed whenever someone forgets to set the flag, which is the failure being closed. case_ledger_enabled() sits beside the existing pilot_mode_enabled() in roles.py and follows its shape.

When disabled, the guard returns before _repository() is ever called, so no SQLite file is opened or created. There is a test asserting exactly that.

2. Synthetic data claimed live API provenance in eight places

incumbent_leads.py stamped API_RETRIEVED on bundled synthetic records. The repo's own example packet showed Source: bundled SYNTHETIC example and Provenance assurance: API_RETRIEVED three lines apart.

The repo had disclosed four such sites. Fixing them and adding a sweep surfaced four more: a general Origin provenance sentence, the prospective ACS-pull placeholder, the conditional handoff-manifest note, and a bundled-null gate that said "USAspending returned" over an offline fixture.

All now branch on contract_facts.synthetic_example, the signal that was already in scope — this was an unused-signal bug, not a missing-signal one. Informational content is preserved; only the false provenance claim changed.

An initial narrower fix gated on an exact (source_url, retrieved_at) match. That was an under-fix: _subaward_leads() copies its source tuple from SubawardsResult, which can differ, leaving a group still labeled API_RETRIEVED. Now gated on synthetic_example alone, with a regression test using two differing source groups.

The durable part

tests/test_synthetic_provenance_sweep.py sweeps the whole rendered export for API_RETRIEVED and non-negated live-claim language, and carries a coverage guard (_MIN_RENDERED_LINE_COUNT = 169, headers ≥ 8, ledger ≥ 11) so deleting a section cannot make it vacuously pass.

Mutation-tested: reverting one fix fails it with

synthetic packet carries live-provenance claim language:
line 153 [LIVE token]: | Eligibility gate | Yes | Gate fed by the LIVE retrieved set-aside value ... |

ADR-025 extended to cover these modules. docs/examples/example-packet.md regenerated by script, not hand-edited. Historical disclosure kept and updated to describe present state.

Gate

ruff check .            All checks passed!
pytest                  593 passed
validate_demo_data.py   Synthetic demo validation passed

Baseline was 585.

⚠️ After merging

Restart the Streamlit deployment so the packet-only default takes effect — until then the live app still commingles visitor cases. The repo homepage field was deliberately left unset for this repo; set it to the app URL once the redeploy is confirmed.

@CJud25
CJud25 merged commit 4beabfe into main Jul 26, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant