Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,13 +106,13 @@ Federal Register) need internet, and the ACS pull needs a free Census API key in
`TENS_HQ_CENSUS_API_KEY` (the key rides only the wire request — cited URLs stay
keyless by construction).

**Hosting note.** The case ledger (`data/runtime/tens_hq.sqlite3`) is local,
single-user, and unauthenticated — it is not an identity, access-control, or
multi-tenant boundary. A shared hosted URL running this app would commingle every
visitor's cases and let one visitor read or mutate another's. Run the full app
locally (or per pilot analyst), or host only the packet-only surface, until
per-identity isolation and auth land (see `docs/ARCHITECTURE.md` §"Trust and
storage boundaries").
**Hosting note.** Packet-only is the fail-safe default, and the hosted demo runs
without the case ledger. Set `TENS_HQ_ENABLE_CASE_LEDGER=1` only for a local or
per-pilot-analyst deployment. The ledger (`data/runtime/tens_hq.sqlite3`) is
local, single-user, and unauthenticated — it is not an identity, access-control,
or multi-tenant boundary. Enabling it on a shared hosted URL would commingle
every visitor's cases and let one visitor read or mutate another's (see
`docs/ARCHITECTURE.md` §"Trust and storage boundaries").

- `docs/DEMO_SCRIPT.md` — a guided walkthrough (the in-app "▶ Guided demo"
follows it).
Expand Down
1 change: 1 addition & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ The two uses of `Validated` are related but distinct. Evidence readiness is nonn

- Public workbook rows never enter the synthetic demonstration data.
- Live sources are called only after the analyst presses the corresponding control; the app does not scrape PLIMS, call undocumented endpoints, or fetch arbitrary URLs.
- Packet-only is the fail-safe default. The case ledger is constructed only when `TENS_HQ_ENABLE_CASE_LEDGER=1`; the shared hosted demo leaves it disabled.
- The case ledger is local, single-user pilot infrastructure. It is not an authentication, authorization, encryption, backup, records-management, or multi-user boundary.
- Role and team aliases stored on cases are descriptive metadata, not access control.
- The existing `TENS_HQ_*` environment-variable names are retained as the umbrella-family prefix.
Expand Down
27 changes: 22 additions & 5 deletions docs/decisions/ADR-025-offline-synthetic-example-facts.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,10 @@ resembling a real retrieval anywhere it appears.
invisible everywhere, so all three had to change in the same commit:
- **Packet body** (`opportunity_packet.py`): the Contract Facts heading
and provenance line switch to "SYNTHETIC example — offline, not a live
retrieval" / "does not carry an API_RETRIEVED assurance label" when the
flag is set -- deliberately worded so the literal substring "Assurance
API_RETRIEVED" never appears in that section, letting the honesty test
be a clean substring ban rather than relying on a human to parse a
negation correctly.
retrieval" / "no live-API provenance is claimed" when the flag is set --
deliberately worded so `API_RETRIEVED` never appears in a synthetic
packet, letting the honesty test be a clean whole-packet substring ban
rather than relying on a human to parse a negation correctly.
- **Section ledger** (`packet_export.py::_contract_facts_live_row`): the
synthetic row is named "Contract Facts (SYNTHETIC example)" with a basis
deliberately worded without the token "live" anywhere, so a reader (or a
Expand Down Expand Up @@ -138,3 +137,21 @@ resembling a real retrieval anywhere it appears.
an `AppTest` driving the real widgets offline (handoff → Pull contract
facts (live)) to confirm no `app.exception` and a populated, labeled
render through the actual UI.

## Amendment (2026-07-25)

The same rule now covers every non-directory group rendered by
`incumbent_leads.py` during a synthetic run: all use `SYNTHETIC_EXAMPLE`,
never `API_RETRIEVED`. This includes a subaward group whose source URL and
retrieval time differ from the Contract Facts because `_subaward_leads()`
copies those fields from `SubawardsResult`; the Contract Facts flag governs
the run's provenance label, not just an exact source-tuple match. The import
of the canonical assurance remains deferred because a module-scope import
forms the real `packet_export -> opportunity_packet -> incumbent_leads ->
packet_export` initialization cycle.

The remaining synthetic packet branches now label the Origin supersession,
Eligibility gate, and Capture window from `synthetic_example`, while a
whole-export sweep covers the rendered body, all eleven ledger rows, and the
Source manifest so another section cannot silently restore a live-provenance
claim.
52 changes: 21 additions & 31 deletions docs/examples/example-packet.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,18 @@ say the same thing:
as prose inside the Incumbent & teaming leads / PL-impact context section:
"No subaward records were retrieved for this award..."

**Known defect, disclosed rather than hidden.** Four provenance labels below
describe this offline synthetic run as if it were live API data:

1. Origin (Radar handoff) section: "Live USAspending Contract Facts are
attached to this packet".
2. Incumbent & teaming leads / PL-impact context section: "Provenance
assurance: API_RETRIEVED".
3. Section ledger, Eligibility gate row: "Gate fed by the LIVE retrieved
set-aside value ...".
4. Section ledger, Capture window row: "Computed from the attached live
Contract Facts pull's potential period end date."

All four branch on whether contract facts are attached and never on whether
those facts are synthetic: `src/tens_hq/opportunity_packet.py:360`,
`src/tens_hq/incumbent_leads.py:468` and `:535`, and
`src/tens_hq/packet_export.py:147-154` and `:202-208`. The check they are
missing is already written twice elsewhere, both under ADR-025:
`packet_export.py:170` (`_contract_facts_live_row`) and `packet_export.py:438`
(the Source manifest row). That is why the Contract Facts ledger row and its
`SYNTHETIC_EXAMPLE` manifest assurance ARE correct here. Fixing the four sites
above is a product-behavior change, not a documentation change, and is not made
here.
**Previously disclosed defect, now closed.** The preceding generated example
honestly listed three places that mislabeled attached SYNTHETIC example facts
as live API data: the Origin supersession line, the Eligibility gate ledger
row, and the Capture window ledger row. All three now branch on
`contract_facts.synthetic_example` and preserve their informational content
without claiming live or API-retrieved provenance.

`tests/test_synthetic_provenance_sweep.py` now builds the full synthetic export
and rejects `API_RETRIEVED` plus non-negated live-claim language across the
rendered packet body, all eleven Section ledger rows, and the Source manifest.
Its coverage meta-test pins the required sections and rendered line count so
removing content cannot make the assurance pass vacuously.

---

Expand All @@ -66,9 +56,9 @@ here.

## Origin — Radar handoff (context, not evidence)

- This packet's target was handed off from a Radar snapshot dated 2026-07-15. Every value below is a claim from that snapshot — NOT packet evidence. Every fact this packet asserts is either retrieved live with its own citation or explicitly analyst-entered. The handoff never feeds the Eligibility Gate or any other assessment.
- This packet's target was handed off from a Radar snapshot dated 2026-07-15. Every value below is a claim from that snapshot — NOT packet evidence. Every fact this packet asserts carries its own citation or is explicitly analyst-entered. The handoff never feeds the Eligibility Gate or any other assessment.
- SYNTHETIC example handoff — not real Radar output.
- Live USAspending Contract Facts are attached to this packet; where they differ from these snapshot claims, the live values govern.
- Bundled SYNTHETIC example Contract Facts are attached to this packet; where they differ from these snapshot claims, the attached facts govern.
- Radar source: GovConRadar 2.8.0 \(SYNTHETIC sample\)
- Radar-claimed PIID: SYNTH-A2-0001
- Radar-claimed recipient: SYNTHETIC EXAMPLE SERVICES LLC · UEI SYNTHUEI0001A
Expand All @@ -86,7 +76,7 @@ here.
- This gate presents the cited set-aside status plus a structural rule for analyst review; it does not determine eligibility. The human decides.
- Set-aside status not reported (blank ≠ unrestricted). Verify against SAM.gov / USAspending before relying on this.

- Source: USAspending returned no set-aside on the latest FPDS transaction (type_set_aside was null). Null = not reported, which is why this reads UNKNOWN and NOT unrestricted.
- Source: bundled SYNTHETIC example Contract Facts contain no set-aside value (type_set_aside is null). Null = not reported, which is why this reads UNKNOWN and NOT unrestricted.
- Retrieved from: bundled SYNTHETIC example (offline) -- not a live USAspending retrieval
- Retrieved at: 2026-07-22T12:00:00+00:00
- Raw set-aside (from USAspending FPDS): None (no value supplied)
Expand Down Expand Up @@ -117,7 +107,7 @@ here.

- Source: bundled SYNTHETIC example \(offline\) -- not a live USAspending retrieval
- Retrieved at: 2026-07-22T12:00:00+00:00
- Provenance: bundled SYNTHETIC example, offline — NOT a live retrieval and does not carry an API_RETRIEVED assurance label.
- Provenance: bundled SYNTHETIC example, offline — NOT a live retrieval; no live-API provenance is claimed.

## Contract Facts (analyst-entered)

Expand Down Expand Up @@ -147,7 +137,7 @@ here.
- Observed: Recipient business categories (registration-derived, self-reported via SAM registration; as of the latest transaction): Category Business; Small Business.
- Source: bundled SYNTHETIC example \(offline\) -- not a live USAspending retrieval
- Retrieved at: 2026-07-22T12:00:00+00:00
- Provenance assurance: API_RETRIEVED
- Provenance assurance: SYNTHETIC_EXAMPLE

- No subaward records were retrieved for this award. This is NOT evidence the contractor does not subcontract or team -- subaward reporting (FSRS) applies only above a reporting threshold and is known to be incomplete.

Expand Down Expand Up @@ -183,7 +173,7 @@ here.

## Geography context (ACS)

- Not yet retrieved. Use the live ACS pull to attach county-level disability context for this place.
- Not yet retrieved. No ACS context is attached. Use the ACS pull to attach county-level disability context for this place.
- Geographic context only -- a county-level ACS population statistic. This is NOT a candidate-supply, hiring-pool, capacity, eligibility, or partnership claim, and it is not evidence of available workers for this contract.

## Procurement List cross-reference (R2b)
Expand Down Expand Up @@ -237,10 +227,10 @@ here.
| Section | Included | Basis |
|---|---|---|
| Origin \(Radar handoff\) | Yes | A Radar handoff snapshot is attached and its PIID matches this packet's current PIID. |
| Eligibility gate | Yes | Gate fed by the LIVE retrieved set-aside value \(USAspending FPDS type_set_aside\), which supersedes any analyst-typed value once live Contract Facts are attached. |
| Eligibility gate | Yes | Gate fed by the attached SYNTHETIC example set-aside value \(USAspending FPDS type_set_aside\), which supersedes any analyst-typed value once the example Contract Facts are attached. |
| Contract Facts \(SYNTHETIC example\) | Yes | Bundled SYNTHETIC example facts attached — offline, not a real USAspending API retrieval. |
| Contract Facts \(analyst-entered\) | Yes | Always rendered from the analyst-pasted PIID and place of performance, independent of any other evidence attached. |
| Capture window | Yes | Computed from the attached live Contract Facts pull's potential period end date. |
| Capture window | Yes | Computed from the attached SYNTHETIC example Contract Facts' potential period end date. |
| Incumbent & teaming leads / PL-impact | Yes | Evidence attached: facts + directory. |
| Staffing what-if | Yes | An analyst-entered staffing baseline was attached to this render. |
| Geography \(ACS\) | Yes | Section rendered as a placeholder -- no ACS context retrieved \(not yet pulled\). |
Expand All @@ -252,7 +242,7 @@ here.

| Source | Reference | Retrieved at | Assurance | Notes |
|---|---|---|---|---|
| Radar handoff \(analyst upload\) | sample_radar_handoff.json \(SYNTHETIC example\) | 2026-07-15 | USER_ATTESTED | The handoff's claimed snapshot retrieval time; not independently verified. Live Contract Facts, where attached, supersede this claim. SYNTHETIC example handoff — not real Radar output. |
| Radar handoff \(analyst upload\) | sample_radar_handoff.json \(SYNTHETIC example\) | 2026-07-15 | USER_ATTESTED | The handoff's claimed snapshot retrieval time; not independently verified. Contract Facts, where attached, supersede this claim. SYNTHETIC example handoff — not real Radar output. |
| Contract Facts \(SYNTHETIC example, offline\) | bundled SYNTHETIC example \(offline\) -- not a live USAspending retrieval | 2026-07-22T12:00:00+00:00 | SYNTHETIC_EXAMPLE | SYNTHETIC example — not real USAspending data. |
| AbilityOne NPA directory \(analyst upload\) | sample_nib_npa.xlsx \(SYNTHETIC example\) | Not supplied \(analyst attestation absent\) | USER_ATTESTED | |
| Staffing what-if inputs \(analyst-entered\) | HOURS mode entry | Not supplied \(analyst attestation absent\) | USER_ATTESTED | |
Expand Down
34 changes: 12 additions & 22 deletions scripts/generate_example_packet.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,28 +85,18 @@
as prose inside the Incumbent & teaming leads / PL-impact context section:
"No subaward records were retrieved for this award..."

**Known defect, disclosed rather than hidden.** Four provenance labels below
describe this offline synthetic run as if it were live API data:

1. Origin (Radar handoff) section: "Live USAspending Contract Facts are
attached to this packet".
2. Incumbent & teaming leads / PL-impact context section: "Provenance
assurance: API_RETRIEVED".
3. Section ledger, Eligibility gate row: "Gate fed by the LIVE retrieved
set-aside value ...".
4. Section ledger, Capture window row: "Computed from the attached live
Contract Facts pull's potential period end date."

All four branch on whether contract facts are attached and never on whether
those facts are synthetic: `src/tens_hq/opportunity_packet.py:360`,
`src/tens_hq/incumbent_leads.py:468` and `:535`, and
`src/tens_hq/packet_export.py:147-154` and `:202-208`. The check they are
missing is already written twice elsewhere, both under ADR-025:
`packet_export.py:170` (`_contract_facts_live_row`) and `packet_export.py:438`
(the Source manifest row). That is why the Contract Facts ledger row and its
`SYNTHETIC_EXAMPLE` manifest assurance ARE correct here. Fixing the four sites
above is a product-behavior change, not a documentation change, and is not made
here.
**Previously disclosed defect, now closed.** The preceding generated example
honestly listed three places that mislabeled attached SYNTHETIC example facts
as live API data: the Origin supersession line, the Eligibility gate ledger
row, and the Capture window ledger row. All three now branch on
`contract_facts.synthetic_example` and preserve their informational content
without claiming live or API-retrieved provenance.

`tests/test_synthetic_provenance_sweep.py` now builds the full synthetic export
and rejects `API_RETRIEVED` plus non-negated live-claim language across the
rendered packet body, all eleven Section ledger rows, and the Source manifest.
Its coverage meta-test pins the required sections and rendered line count so
removing content cannot make the assurance pass vacuously.

---

Expand Down
11 changes: 11 additions & 0 deletions src/tens_hq/bd_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
)
from .pl_match import find_pl_service_matches
from .radar_handoff import RadarHandoffError, parse_radar_handoff
from .roles import case_ledger_enabled
from .scanner import ScanStatus, WorkbookScanner
from .staffing_whatif import StaffingWhatIfInput, WhatIfMode, assess_staffing_whatif

Expand Down Expand Up @@ -1360,6 +1361,16 @@ def render_public_bd_page(data: Any, target: float, scenario: str) -> None:
st.markdown(f'<div class="planning-banner">{_PUBLIC_BANNER}</div>', unsafe_allow_html=True)
st.warning("Public directory evidence is discovery evidence only. The scanner never infers capacity, candidate supply, a relationship, or an acquisition outcome.")

if not case_ledger_enabled():
st.info(
"The case ledger is disabled on this hosted demo because its local, "
"single-user store has no authentication."
)
(packet_tab,) = st.tabs(["Opportunity Packet"])
with packet_tab:
_render_opportunity_packet()
return

try:
repo = _repository(_default_db_path())
except Exception:
Expand Down
21 changes: 16 additions & 5 deletions src/tens_hq/incumbent_leads.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,9 +512,9 @@ def incumbent_leads_lines(assessment: IncumbentLeads, *, contract_facts: Contrac
"""Render the section as caveat-first Markdown lines (pure).

Leads are rendered in two evidentiary groups -- (1) competition/
registration facts sourced from the live Contract Facts pull and (2)
subaward evidence sourced from the live subawards pull -- each closed
with its own Source/Retrieved-at/Assurance footer, followed by any
registration facts sourced from the attached Contract Facts and (2)
attached subaward evidence -- each closed with its own
Source/Retrieved-at/Assurance footer, followed by any
directory cross-reference (Assurance USER_ATTESTED, since the uploaded
directory is an analyst attestation even though the matched subawardee
fact itself is API-retrieved) and the PL-impact context block (D3).
Expand All @@ -531,8 +531,19 @@ def incumbent_leads_lines(assessment: IncumbentLeads, *, contract_facts: Contrac
non_directory = [lead for lead in assessment.leads if lead.band is not LeadBand.COINCIDENT]
directory_leads = [lead for lead in assessment.leads if lead.band is LeadBand.COINCIDENT]

for _key, group in itertools.groupby(non_directory, key=lambda lead: (lead.source_url, lead.retrieved_at)):
_render_group(lines, list(group), assurance=_ASSURANCE_API_RETRIEVED)
for _source_key, group in itertools.groupby(
non_directory, key=lambda lead: (lead.source_url, lead.retrieved_at)
):
group_leads = list(group)
assurance = _ASSURANCE_API_RETRIEVED
if contract_facts.synthetic_example:
# ADR-025: reuse the manifest's one canonical synthetic assurance.
# Deferred to avoid packet_export -> opportunity_packet -> this
# module forming an import cycle during module initialization.
from .packet_export import _ASSURANCE_SYNTHETIC_EXAMPLE

assurance = _ASSURANCE_SYNTHETIC_EXAMPLE
_render_group(lines, group_leads, assurance=assurance)

if not assessment.subawards_attached or assessment.subawards_record_count == 0:
lines.append(f"- {SUBAWARDS_ABSENCE_CAVEAT}")
Expand Down
Loading
Loading