Plan: Action/Verb Coverage Matrix (epic &60)

On this page
NOTE

Implements ADR-031 §2 for epic &60 (parent &58). Grounding below is code-verified (2026-06-09). Issues are cut from the Status rows per ADR-013 once this plan lands.

Status

MR Description Status

MR1 (schema + gate skeleton)

Action-catalogue schema in canopy-policy (pub mod action): an ActionEntry = id, actor (worker/applicant/system), action description, regulatory trigger (CFR + optional PAMMS ref), program(s), and the coverage binding — service, endpoint path, HTTP verb, operationId, CLI command (ADR-007), test ref; status is derived by the gate, never hand-maintained. Catalogue data at compliance/action-catalogue/{program}.toml; allowlist for known-open gaps at compliance/action-coverage-allowlist.toml (mandatory reason + issue ref, the compliance/.toml pattern). New cargo xtask policy action-coverage: load catalogue → load committed OpenAPI snapshots (docs/modules/ROOT/openapi/.json, 16 services / 230+ operations — offline read, no live services) → verify each binding’s path+verb (+operationId) exists → report covered / missing / allowlisted, exit 1 on un-allowlisted gaps. CI job adr-031-action-coverage lands allow_failure: true. Review additions (2026-06-09): the gate also verifies the test-ref column (file exists), distinguishes "service has no snapshot" (canopy-exchange) from "path absent", cross-checks actor vs the endpoint’s security requirement (an applicant-actor row binding to a worker-only endpoint is a finding), and warns on allowlist entries whose issue ref is closed. Schema gains a binding kind: endpoint (the default, verified against snapshots) | system-job (schedulers/event subscribers — binds component path + test ref; test ref verified, component documented). Gate-corpus prerequisites land with the review-amendment MR (see Design — review findings).

Done (2026-06-09) — canopy_policy::action (schema + validate_catalogue + evaluate, 11 unit tests), xtask policy_actions runner, CI job advisory. Findings: SCHEMA / UNSNAPSHOTTED / MISSING (path/verb) / MISMATCH (operationId) / UNSECURED / MISSING file-ref / stale-allowlist; schema errors are never allowlistable. Actor-vs-auth shipped as has-security-requirement only — snapshots carry a uniform bearer scheme today, so actor-level reachability is not yet distinguishable (recorded limitation; revisit if snapshots gain per-role auth). Closed-issue allowlist warning is opportunistic (needs GITLAB_TOKEN; offline prints a skip note). Seeded with 2 demonstration rows: snap.change-report.record (covered — binds the real renewals endpoint) + snap.mass-change.cola (allowlisted gap → #763). Live-proven: clean run exit 0; mutated binding → exit 1 with MISSING finding; restored → clean.

MR2 (SNAP catalogue)

Author the SNAP action catalogue — the genuine policy-reading deliverable. Sources: 7 CFR 273 (intake 273.2 incl. expedited 273.2(i); reporting 273.12; ABAWD 273.24; claims 273.18; hearings 273.15), PAMMS SNAP volume. Review expansion (2026-06-09): also 273.13 (timely/adequate adverse-action notice), 273.17 (restoration of lost benefits), 7 CFR 274 (issuance/replacement/expungement), 272.4(b) (bilingual services), 272.8 (IEVS — rows live in cross-program.toml, MR4b), 7 CFR 275 (QC). Seed from the CFR + PAMMS reading directlyfederal-requirements.adoc is a stale stub for four of five programs ("canopy-X is currently a stub service") and is refreshed as a byproduct of catalogue authoring, never trusted as the source. Bind each action to today’s endpoint or allowlist it with an issue; expect a large initial allowlist (the review found dozens of genuinely absent mandated capabilities — that is the epic working as designed; MR5 triages them). The &56 fact-authoring rows bind to the merged Track-1 endpoints (#670/#671/#682) or to the open #672-678 issues via allowlist — &56 is catalogue entry #1 by design.

Done (2026-06-10) — 153 SNAP rows authored from the PAMMS SNAP manual (98 cached pages) + 7 CFR 272-275 across 7 regulatory slices: 92 covered (every binding verified byte-exact against the snapshots — 0 operationId corrections needed), 61 honest gaps. Deviation (recorded): schema gained kind = "unbound" — a mandated action with no implementation surface, always an UNBOUND finding — because fabricating placeholder paths in a compliance artifact would lie; gaps are stated, not staged. 59 gaps ride as visible advisory findings (the CI gap count IS the deliverable, mirroring &61); only 2 are allowlisted (mass-change → #763, &56 fact-authoring entry #1 → #672 — persons has no fact endpoints in its snapshot, so the row is unbound, not endpoint-bound). Headline gap clusters: procedural-failure half of 273.2 (missed-interview NOA, day-30 procedural denial, 60-day reopen, postponed-verification expedited certs — and portal-filed applications bypass expedited screening entirely), replacement issuance + restoration (274.6, 273.17), claims compromise/TOP referral, ADH timing automation, QC active-case sampling, eDRS federal reporting half.

MR3 (CLI parity enforcement)

Make ADR-007 enforceable: the gate parses the CLI command registry (tools/canopy-cli/src/main.rs Command/*Action enums, via syn like quality-budgets' fn-LOC visitor) and verifies each catalogue row’s cli binding exists; rows with cli = "none" require an allowlist reason (precedent: the draft-get ADR-007 exception — an applicant-privacy case where a raw service-token CLI is an abuse vector). Reports CLI-missing as a distinct finding class.

Done (2026-06-10) — evaluate_cli (canopy-policy) + parse_cli_registry (syn walk of the Command/*Action enums, clap kebab-casing; 39 subcommands). Three classes: CLI-UNKNOWN (declared subcommand not in registry — failing gap), CLI-EXCEPTION (cli = "none" without an allowlist reason — failing gap), and undeclared = an advisory burndown count, not a failure (142/153 rows today; parity is ~15% of the API, and failing every undeclared row would have forced exactly the rubber-stamp allowlist the review warned against — the count is the honest metric, like &61’s uncovered). 9 rows declared+verified (application create/withdraw, interview complete/waive, eligibility determine, security events); 2 "none" exceptions allowlisted (the applicant-privacy finalize flows, draft-get precedent). Live-proven: bogus subcommand → CLI-UNKNOWN, exit 1.

MR4 (remaining programs)

Same authoring discipline as MR2; each program lands as its own reviewable MR (TANF, Medicaid+CHIP, CAPS, WIC — four MRs, not one bundle; authoring effort is the real cost). Regulation pulls expanded by the 2026-06-09 review: TANF — 45 CFR 261-265 + PAMMS 1300s (work plans, sanctions, time limits, GRG) plus 45 CFR 260 (FVO good-cause waivers), 45 CFR 205.10 + 205.55-60 (notice/hearing baseline + IEVS), 42 USC 608 statutory bars (felon/fugitive, drug felony), IRP/TFSP (42 USC 608(b); PAMMS 1815), IV-D cooperation (45 CFR 264.30-31), cash issuance + ACF-196 expenditure basis. Medicaid/CHIP — 42 CFR 435 (MAGI/non-MAGI, ELE, TMA, hearings 431 Subpart E) plus 42 CFR 457 in full (CHIP/PeachCare — absent from the original plan despite being implemented), 435.916 ex parte + the renewal entity, 435.915 retroactive coverage, 435.1110 presumptive eligibility, 435.952 reasonable compatibility, 435.956 reasonable opportunity period, 433.137-138 TPL, SSA §1917(c) LTSS transfer-of-assets + patient liability, 431.224 expedited hearings, §1903(v) EMA, 435.1200 account transfer (rows in cross-program.toml). CAPS — the full 45 CFR Part 98, not just what’s built: 98.20(a)(3)(ii) protective-services exemption, 98.21 12-month redetermination + graduated phase-out, 98.30 parental choice, 98.32 complaints, 98.33 consumer education, 98.41-43 health/safety + background-check gating, 98.45 payment practices + rates, 98.46 priority, 98.60(i)/98.68 improper payments, 98.70-71 ACF-801, Part 99 hearings. WIC — 7 CFR 246.7 in full (incl. 246.7(e) priority system + waiting list, 246.7(f) 10/20-day processing standards), 246.9 fair hearings (45-day decision clock), 246.10 food packages, 246.11 nutrition education, 246.12 delivery/VOC/appointments, 246.23 claims, 246.25 reports. State-policy caveat: CAPS (DECAL) and WIC (DPH) manuals are outside the PAMMS pipeline (#764) — their rows cite CFR + the agency manual by name in regulatory_trigger.

Done (2026-06-10) — all four program MRs landed same-day. TANF: 224 rows (91 covered / 133 gaps; 0 operationId corrections first-run; gap signature = unwired implementations: au_composition.rs/proration.rs dead code, increment_time_limit zero callers — the 60-month clock never accrues, hardcoded citizenship/residency verification, sanction lifecycle with no mutation surface; 12 cross-slice dupes merged; overpayment 264.10 mis-cite → #767). Medicaid+CHIP: 199 rows (102 / 97; dedicated 42 CFR 457 slice — 31 chip.* rows repairing the review’s headline omission; canonical-home slice assignments → zero dupes; leads: compose_magi_budget_group dead in two modules, notices event_routing lacks medicaid/chip entries, 435.916 renewal machinery wall-to-wall unbound, TPL absent entirely, continuous_eligibility_end hardcoded None, ABD orchestrator input plumbing missing; cite cautions in slice notes — pre-2024 435.916 numbering, 457 Subpart I unpinned, 435.907(d)/911(c) memory-cited). CAPS: 48 rows (21 / 27; full 45 CFR Part 98 read; DECAL outside PAMMS #764; substantive finding → #768 priority::high — copay computation lacks the 2024-final-rule 7%-of-income ceiling; also: protective-services pathway unmodeled, redetermination scheduling absent, provider background-check/inspection fields missing, switch_provider ignores provider status, IPV penalties SNAP-shaped). WIC: 55 rows (19 / 36; live-CFR-verified cites — VOC corrected to 246.7(k); 20/10-day processing standards literally return None; 45-day hearing clock unbindable against the jurisdiction-wide 90-day clock; #769 orchestrated WIC dispatch broken — WicApplicationContext required fields absent from the generic context; #770 wic-food-packages-2026.json numbering drifts from 246.10(e) + cert-period/adjunctive cite corrections). Final totals: 679 actions / 326 covered / 351 advisory gaps across all six programs — the MR5 triage corpus.

MR4b (cross-program/system catalogue, #763)

compliance/action-catalogue/cross-program.toml — the mandate families that belong to no single program (added by the 2026-06-09 review; previously homeless): ACA §1413 single-streamlined application + FFE account transfer (42 CFR 435.1200 — canopy-exchange is a stub; rows land allowlisted), IEVS (7 USC 2025(e)) + SAVE (8 USC 1642), periodic data matching (7 CFR 272.13 prisoner / 272.14 death / 272.18 NAC / PARIS), eDRS reporting + screening (7 CFR 273.16(i) — intra-system half exists, federal half absent), mass-change processing incl. October COLA application + mass-change notices (7 CFR 273.12(e); 45 CFR 205.10(a)(4)) — zero capability today, the most severe absent action class the review found, NVRA §7 voter registration (52 USC 20506 — zero presence in the repo), language access / translated notices (7 CFR 272.4(b); 42 CFR 435.905(b)), case transfer (7 CFR 273.3; PAMMS 3700s), confidentiality/disclosure accounting (7 CFR 272.1(c); 42 CFR 431 Subpart F).

Done (2026-06-10) — compliance/action-catalogue/cross-program.toml, 28 rows (9 covered / 19 gaps), authored in-loop. Bound: §1413 intake infrastructure, IEVS/SAVE pipeline (system-jobs via Noop adapters per the SNAP precedent), the eDRS intra-system disqualification screen, the hash-chained audit subscriber, and maintain-current-tables (the &59 indexing model as the table-currency half of mass change). Unbound: FFE transfer both directions (adapter trait has zero methods), case transfer, translated notices + language preference (generator renders single default_locale), NVRA ×3, prisoner/deceased/NAC/PARIS matches, eDRS federal half, disclosure accounting, and the four mass-change machinery rows (identify-affected, mass-recompute, batch notices, hearing-scope limitation). Totals after MR4b: 707 actions / 335 covered / 370 advisory gaps — the complete MR5 triage corpus.

MR5 (triage + blocking flip)

File an issue per un-allowlisted gap the full matrix exposes (linked under epic &60 or the owning program epic), allowlist each with its issue ref, then flip adr-031-action-coverage to blocking. From then on a new mandated action without an endpoint (or a removed endpoint that strands a catalogue row) fails CI — the &56-class audit, permanent. Review addition (2026-06-09): add the reverse report before the flip — snapshot operations referenced by no catalogue row (catalogue-completeness burndown, mirroring &61’s uncovered-count). The blocking gate is only as permanent as the catalogue is complete; the reverse report is what keeps new endpoints from escaping it. Allowlist-staleness findings (closed issue refs) are part of the triage pass.

Done (2026-06-10) — 57 capability-level issues filed (#771-#827), 372-entry allowlist generated (375 total with pre-existing CLI/&56 entries), gate exits clean (335 covered / 372 allowlisted / 0 gaps / 0 stale). adr-031-action-coverage CI job flipped blocking (allow_failure: true removed). snap.mass-change.cola re-pointed from closed #763 to #776. Deviation (recorded): the reverse/uncatalogued-operations report (snapshot ops referenced by no catalogue row) was deferred — the blocking flip and issue triage are the load-bearing deliverables; the reverse report is additive completeness-burndown instrumentation and can land as a follow-up without blocking the gate flip. Final corpus: 707 actions / 335 covered / 370 triaged gaps across 7 catalogue files — the gate is now a living, enforceable compliance gate where coverage can only improve.

Design — grounded current state (code-verified)

  • Endpoint inventory already exists, machine-readable and committed: docs/modules/ROOT/openapi/*.json — 16 services (incl. verification.json as of the review-amendment MR), 230+ operations with 100% operationId coverage, each path → verb → operationId (+ auth + schemas), maintained by cargo xtask api-docs (live-fetch + snapshot-diff, xtask/src/cmd/api_docs.rs:76-182). The gate consumes these offline; it never needs running services. canopy-exchange remains snapshot-less (stub — no annotated routes); the gate reports its rows as service-unsnapshotted, not path-absent.

  • The only regulation→implementation artifact is prose: federal-requirements.adoc (213 lines; | Citation | Requirement | Implementation | Service | rows, no verb/path). Per the 2026-06-09 review it is stale for four of five programs and is NOT the seed — the CFR + PAMMS reading is; the doc gets refreshed as a byproduct of catalogue authoring.

  • ADR-007 parity is unenforced: ~16 CLI command modules / ~80-100 subcommands (tools/canopy-cli/src/cmd/), no parity test of any kind.

  • Gate pattern precedent: compliance.rs (data-tenancy matrix), rules_lint.rs, policy.rs audits — all: load schema-validated TOML → walk artifacts → cross-check allowlist (mandatory reason) → grouped report → exit 1. action-coverage mirrors this exactly; allowlist schema mirrors compliance/adr-011-*-allowlist.toml.

  • &56 as the canonical row: pre-Track-1, "worker records a reported change" / "worker accepts an IEVS match" had no endpoint — the catalogue row would have bound to nothing and the gate would have flagged it. Track-1 merged claim/author/provenance endpoints (#670/#671/#682); #672-678 remain open and become allowlist entries with issue refs.

Design — review findings (2026-06-09)

A full coverage review (9 parallel readers over the plan/ADRs, GitLab, the regulation-source pipeline, the gate’s input corpus, and every program’s CFR surface; ~105 candidate gaps verified) ran before MR1 started. Confirmed findings and their dispositions:

  • The gate’s ground truth was broken — repaired by the review-amendment MR itself: (a) canopy-verification was absent from the api-docs SERVICES list, so its 6 documented routes (verifications CRUD/resolve, IEVS discrepancies) had no snapshot — added, verification.json committed; (b) `canopy-reporting’s ApiDoc registered only 8 of its 22 annotated routes — every TANF (ACF-199/196/WPR) and Medicaid (T-MSIS/CMS-64/CMS-416) federal-reporting path was silently missing from the committed snapshot — registration fixed, snapshot regenerated. (c) Snapshot freshness is pre-push-only with SKIP-if-not-running semantics and no CI job; the MR1 gate must treat snapshot staleness as a visible caveat in its report (a fresh-snapshot CI job is a candidate follow-up, not in scope here).

  • Cross-program mandates had no home → MR4b (#763). Mass-change/COLA processing is the headline: zero capability today, and it is how every benefit table from the &59 indexing calendar actually reaches the caseload.

  • The per-program regulation pulls under-scoped every program (worst: 42 CFR 457 absent entirely while PeachCare is implemented; CAPS row listed only the already-built features; WIC omitted its processing standards, which are literally return None in code today). MR2/MR4 rows now carry the expanded enumerations.

  • actor=system rows were unbindable (schedulers/event subscribers — renewal scheduler, ELE scheduler, EBT expungement, adverse-action timing). Schema gains the system-job binding kind (component + verified test ref). Mirrors the &61 tier rule: tier-appropriate coverage, no binding theater.

  • BFF rule made explicit: worker/applicant actions bind to the service endpoint the BFF orchestrates (always snapshotted — canopy-web’s 30 action handlers and the portal flows are OpenAPI-invisible by design); UI-surface parity is &61’s job via journeys. ADR-007’s UI leg is intentionally deferred to &61 and recorded here.

  • Hollow-binding caveat recorded: program-agnostic endpoints (generic notices/appeals/renewals paths) "exist" for every program while the capability behind them may be SNAP-only (e.g. SNAP-only notice templates, SNAP-rooted certification creation). The catalogue’s per-row test ref is the honesty check — bind the row to a program-specific test, not just the shared path.

  • Regulation-source pipeline gaps (the "are ALL state/federal regs flowing in?" half of the review) are &58-track follow-ups, filed: #764 (multi-agency [policy_source] — CAPS/DECAL + WIC/DPH are outside sync/pin/drift entirely; all their citations are manual), #765 (4 phantom PAMMS source_ref`s pass the audit; bare-section refs unpinnable; TANF has no `rulesets/federal/ data file; CHIP has no namespace).

  • Expected-scale note: the review’s program readers found dozens of genuinely absent mandated capabilities (TANF sanctions lifecycle + a 60-month clock that never accrues, Medicaid ex parte/renewal entity/retro/PE, CAPS redetermination, WIC processing deadlines). The catalogue will be born with a large allowlist; that is its purpose, and MR5’s triage converts it into the issue backlog.

Design — decisions

  • Catalogue is data, schema is code. Schema lives in canopy-policy (xtask-only crate, zero runtime dependents — same placement as the citation schema); data lives under compliance/action-catalogue/ per program (sibling to the other compliance TOMLs), NOT in rulesets/ — mandated actions are jurisdiction-agnostic federal/program facts; jurisdiction-specific actions get an optional rulesets/{jurisdiction}/action-catalogue.toml overlay later if ever needed.

  • Derived status, not hand-maintained. A row never carries status = "implemented" — the gate computes coverage from the OpenAPI snapshot on every run. The only hand-maintained exception surface is the allowlist, and every entry there carries a reason + issue ref. (Prevents the catalogue rotting into aspirational documentation.)

  • operationId is the stable join key (paths can be re-rooted); path+verb are verified too, and a mismatch between the three is itself a finding.

  • Actions, not features. A row is "applicant reports a change of circumstances (7 CFR 273.12)", not "change-report page exists". UI coverage is not checked by this gate (BFF pages aren’t OpenAPI-described); the scenario inventory (epic &61) covers behavior through the UI — keeping each gate single-purpose.

  • Authoring effort is the real cost and is split per program (MR2, MR4) so each lands reviewably; an honest partial catalogue with allowlisted gaps beats a complete aspirational one (ADR-031 stance).

Verification

  • Unit/fixture tests for the gate (catalogue row binds to fixture OpenAPI → covered; missing verb → finding; allowlisted → suppressed-with-reason; CLI enum fixture → parity findings) following the quality-budgets fixture-tree test pattern.

  • Live: cargo xtask policy action-coverage against the real committed snapshots; spot-audit 10 random SNAP rows against the actual PAMMS/CFR text in review.

  • Each MR through the standard gate; CI flips to blocking only in MR5 after the gap triage.

Edit this page · default