epic: worker program scope, enforced (#742)
## Summary
canopy-web's program scope is **advisory**, not enforced. Three defect classes share one root:
1. **Fail-open** — `session::program_in_scope` and `audit::event_visible_to_programs` both treat an
empty `primary_programs` claim as *see everything*. An IdP without the claim mapper silently
grants jurisdiction-wide read+write.
2. **The gate checks a label, not a resource** — 13 production mutations pass caller-supplied
`form.program` to the scope helper (income x3, members x3, assets x2, expenses x2, ievs x2,
address x1). A SNAP-only worker posts `program=snap` and edits person facts on a TANF-only
household, or resolves a SNAP IEVS discrepancy out of scope. `ensure_household_member` (#996)
binds person -> household but never household -> scope. Three document actions
(`accept_document`, `reject_document`, `scan_override_document`) have no gate at all.
3. **Reads are almost entirely unscoped** — case search, command palette, the application /
appeals / notices / renewals / team-queue indexes, notice PDFs and document bytes by direct id,
every dashboard panel, the audit page + CSV export + citation-by-id, and `/sse`, which
broadcasts the full event envelope to every connected worker with no filter.
This epic makes program scope a **required, enforced** attribute of worker identity across the
whole authorization surface. The decision is recorded in ADR-044: a missing-or-empty
`primary_programs` claim is a rejected login, with **no canopy-side override** — the deployment's
override is the IdP claim mapper, which already owns role and identity.
Driver: IRS Pub 1075 AC-6 (§4.1) least privilege. Also satisfies epic &62 phase B2.
## Plan
[docs/modules/ROOT/pages/plans/program-scope-enforcement.adoc](https://gitlab.com/gadhs/application/eligibility/canopy/-/blob/main/docs/modules/ROOT/pages/plans/program-scope-enforcement.adoc) (committed in MR-1)
## Children
- [x] #1515 feat(web): require the primary_programs claim at admission (weight: 5) — MR !1173, merge 8e0c073e
- [x] #1516 fix(web): authorize mutations against the resource, not the posted program (weight: 8) — MR !1174, merge 71b26326
- [x] #1517 feat(notices): program filter on the notices list endpoint (weight: 2) — MR !1175, merge 9b1d92b8
- [x] #1518 fix(web): scope every protected read surface to the worker's programs (weight: 8) — MR !1179, merge 7e1aae90
- [x] #1519 feat(security): authoritative program metadata on audit rows + scoped audit reads (weight: 8) — MR !1180, merge 156abb1b
- [x] #1520 fix(web): stop broadcasting full event envelopes on /sse (weight: 3) — MR !1181, merge b7bd337b
Umbrella issue: #742 (closed by the final MR).
epic