Roadmap
On this page
- Strategic Context
- Current Status (2026-08-03)
- SNAP UAT Critical Path
- Post-SNAP-UAT Phases
- Modular Deployment Summary
- Dependency Graph
- Remaining Work Tracker
- Tier 0 — Hygiene (stale plans, no code changes)
- Tier 1 — Gaps in "complete" work
- Tier 2 — Phase 3 plan residuals
- Tier 3 — New program services
- Tier 4 — Federal reporting completion
- Tier 4.6 — Worker portal multi-program expansion
- Tier 5 — Cross-program and integration
- Tier 5.5 — Deferred placeholders / simplifications
- Tier 5.6 — Confirmed hacks and workarounds
- Tier 5.7 — Plan errata cross-reference
- Tier 6 — Infrastructure and operations (post-UAT)
- Tier 7 — Pre-1.0 quality gate
- Tier 8 — Externally blocked
- Tier 9 — Open work snapshot (historical; 2026-05-04)
- Tier 10 — Master Tackle Order (phases, not snapshots)
- Post-1.0 — Deferred to after first stable release
- Go / No-Go Checkpoints
Strategic Context
Canopy’s architecture is driven by four ADRs (see ADR-001 through ADR-004) and two deployment principles:
-
SNAP-only UAT, September 2026 — The Month 1–6 critical path below (Foundation → SNAP UAT) is complete; the phases were built out ahead of schedule in Q1–Q2 2026. The work now standing between here and UAT is correctness + scale hardening, not net-new feature build-out — see Current Status.
-
Any jurisdiction, any program — Per ADR-005 and ADR-006, any jurisdiction may deploy any program subset. Georgia DHS is the reference implementation; other jurisdictions require only ruleset files and configuration.
Current Status (2026-08-03)
The Month 1–6 SNAP-UAT critical path and the post-UAT program build-out (TANF, Medicaid/CHIP, CAPS, WIC, applicant portal) are substantially complete; those milestones below are historical records. Canopy is in a scale-hardening + correctness phase ahead of the September 2026 SNAP UAT.
Canonical work-stream state lives in GitLab, not in this file (per the memory-hygiene / plan-lifecycle conventions). This roadmap is the phase narrative; for the live open-issue list use glab issue list --state opened (or the GitLab epic boards). The two active engineering streams:
-
Epic &73 — Scale readiness (the "ankle-biter lane"). The burndown of the 2026-07-25 80-agent scale-readiness audit: indexes, pagination, streaming, job models, scheduler fences, backpressure, bulk contracts — making everything already built rock-solid at the ~3M-applicant Georgia / 10–15M single-deployment horizon before adding capability.
-
Epic &74 — Configurable logging + jurisdiction-owned redaction (ADR-041). Retires the special-cased FTI-audit hash chain (ADR-014) and the chain-v2 external-anchor machinery in favor of a general, configurable structured-logging facility with per-field, jurisdiction-owned redaction; external tamper-evidence + retention are delegated to the deployment’s logging facility (canopy = mechanism, deployment = policy). Sequenced Decision MR → redaction mechanism → audit-export channel → conformance gate → retention/legal-hold → retirement (last, gated). See ADR-041.
The near-term execution order across the open backlog is: (1) correctness + safety defects in shipped code, (2) test/CI-harness reliability (the pre-push battery is the merge gate), (3) the ADR-041 stream, (4) the remaining &73 scale hardening, then compliance/docs correctness. The large program-coverage cohort (per-program regulatory gaps across SNAP/TANF/CAPS/WIC/CHIP/Medicaid) and the worker-portal/Studio feature backlog are SME- and roadmap-driven and largely post-UAT.
SNAP UAT Critical Path
Milestone Map (Target: September 2026)
Status: COMPLETE markers and the Go/No-Go checkpoints); the map is retained for traceability. Current work is the scale-hardening + correctness phase in Current Status.
| Month | Target | Key Deliverables | Exit Criteria |
|---|---|---|---|
1 (April 2026) |
Foundation |
Reference extensions, session middleware, persons model, rules engine, determination signing, security audit, application intake |
canopy-persons serves person data; canopy-rules evaluates rulesets; signing infrastructure compiles; canopy-applications accepts intake |
2 (May 2026) |
SNAP Core + Compliance |
SNAP eligibility, eligibility orchestrator, categorical eligibility + BBCE, ABAWD tracking |
Application submitted to canopy-eligibility for SNAP produces a signed, verified, benefit-calculated determination |
3 (June 2026) |
Verification |
SNAP IEVS integration (SWR, UI, SSA SDX/BENDEX) |
SNAP eligibility verifies income against Georgia DOL wage records and SSA benefits; discrepancies flagged and tracked |
4 (July 2026) |
Notices + Appeals |
Notice generation (NOA), fair hearings and appeals |
10-day advance notices issued; fair hearing requests accepted; continued benefits tracked |
5 (August 2026) |
Enrollment + Renewals |
SNAP EBT enrollment, SNAP certification periods, simplified reporting |
Benefits issued via EBT (NoopAdapter for UAT); certification periods tracked; renewal notices generated |
6 (September 2026) |
Reporting + Portal + UAT |
SNAP federal reporting (FNS-388, FNS-7176 QC), worker portal (canopy-web), UAT execution |
All FNS-required SNAP test scenarios pass; FNS-7176 QC extract generates valid CSV; workers can process cases end-to-end |
Month 1: Foundation
All four items can proceed in parallel. Reference extensions must complete in week 1 — it is a blocker for all downstream work.
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
Missing enum variants (DeterminationStatus, IncomeType, AssetType, NoticeType, VerificationItem type), Determination struct updates |
— |
None — must complete in week 1 |
|
tower-sessions-sqlx-store wired in canopy-web and canopy-portal; sessions table migration; 8-hour worker TTL, 30-minute applicant TTL |
— |
None — security gap, complete early |
|
canopy-persons with full CRUD, person/household/income/asset/expense/address schema, MAGI tax-filing-status fields, event publishing |
— |
Reference extensions (week 1) |
|
canopy-rules with zen-engine, CRUD, evaluation API, audit trail, ruleset import from |
ADR-003, ADR-006 |
Reference extensions (week 1) |
|
|
ADR-002 |
Reference extensions (week 1) |
|
canopy-security wildcard subscriber, event persistence to DB, audit log query API, breach detection rules, NIST control mapping |
ADR-004 |
None — listens to events as other services come online |
|
canopy-applications with single-streamlined-application (ACA §1413), expedited screening, authorized representative, event publishing |
— |
Persons model (for household_id foreign key) |
Exit criteria: canopy-persons serves person data; canopy-rules evaluates rulesets; signing infrastructure compiles and passes key generation tests; canopy-security captures all published events; canopy-applications accepts SNAP applications.
Status: COMPLETE (March 2026). All exit criteria met:
-
canopy-persons: 13 API endpoints, 7-table schema, event publishing (MR !6)
-
canopy-rules: zen-engine integration, CRUD + evaluate, audit trail (MR !8)
-
canopy-signing: ECDSA P-256 keygen, signer, verifier (commit eda958f)
-
canopy-security: wildcard subscriber, audit persistence, NIST controls (MR !9)
-
canopy-applications: ACA 1413 intake, expedited screening, 8 endpoints (MR !10)
-
Session middleware: PostgreSQL-backed sessions in canopy-web and canopy-portal (MR !5)
-
Reference extensions: all enum variants, types.rs, Determination struct fields (MR !1)
Month 2: SNAP Core + Compliance
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
canopy-snap with all mandatory income/deduction tests, asset test, signed determinations, IEVS data isolation |
ADR-001, ADR-002, ADR-003, ADR-004 |
Month 1 complete |
|
canopy-eligibility calling program services, verifying signatures, assembling combined results |
ADR-002 |
Month 1 (signing), SNAP eligibility |
|
Standard categorical eligibility (TANF cash, SSI), BBCE (Georgia 130% FPL, asset test eliminated), student exclusion rules |
ADR-003 |
SNAP eligibility |
|
All 6 mandatory deductions (earned income, standard, dependent care, medical, excess shelter, homeless shelter), net income test, benefit calculation from max allotment minus 30% net income |
ADR-003 |
SNAP eligibility |
|
ABAWD identification, 80-hour/month tracking, 3-month time limit in 36-month window, discretionary exemptions, waiver area support |
ADR-003 |
SNAP eligibility |
|
Drug felon screening (21 USC 862a), fleeing felon/probation violator, striker pre-strike income preservation, jurisdiction-specific reinstatement options |
ADR-003 |
SNAP eligibility |
Exit criteria: An application for a household with wages at 125% FPL produces an approved SNAP determination with a calculated benefit amount. A household with wages at 135% FPL is denied at the gross income test. A TANF cash recipient is auto-approved via categorical eligibility. An ABAWD with no work activity in month 3 receives AbawdExceeded status.
Status: COMPLETE (March 2026).
Month 3: Verification
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
State DOL SWR and UI adapters, SSA SDX/BENDEX adapters, IEVS discrepancy tracking, verification workflow in canopy-snap |
ADR-004 |
Month 2 complete (SNAP eligibility, canopy-snap database schema) |
|
SaveAdapter trait, NoopSaveAdapter, DHS SAVE step 1-3 verification, citizenship_verification table, alien eligibility rules (7 CFR 273.4) |
— |
Reference extensions (VerificationSource enum) |
Exit criteria: SNAP eligibility evaluation runs IEVS match against (NoopAdapter returning deterministic test data for UAT); discrepancies between self-reported income and IEVS data generate VerificationItem entries on the determination; workers can view and resolve discrepancies.
Status: COMPLETE (March 2026). NoopIevsAdapter and NoopSaveAdapter implemented with deterministic test data. 24 verification tests.
Month 4: Notices and Appeals
Both can proceed in parallel.
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
canopy-notices with Typst PDF generation, Orchard-branded templates, 10-day advance notice enforcement, delivery queue, SNAP approval/denial/termination/ABAWD/expedited/expungement notices |
— |
Month 2 (determination events to subscribe to) |
|
canopy-appeals with appeal request intake, continued benefits logic, 90-day decision clock, timeline enforcement |
— |
Notice generation (for AppealAcknowledgment notice) |
|
IPV case tracking, ADH workflow with 30-day notice and waiver, disqualification penalties (1yr/2yr/permanent), benefit recalculation, overpayment computation |
— |
Fair hearings (shared canopy-appeals service) |
Exit criteria: A SNAP denial produces a denial NOA with regulatory basis cited. An adverse action generates a 10-day advance notice with the effective date pushed forward if needed. An appeal request filed before the adverse action effective date results in continued benefits. Notice history is visible in the worker portal.
Status: COMPLETE (March 2026). Typst notice generation (14 templates), fair hearings with continued benefits, IPV/ADH with penalty calculator. MRs !22-!25.
Month 5: Enrollment and Renewals
Both can proceed in parallel.
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
canopy-enrollment with EBT issuance pipeline, benefit proration, NoopEbtAdapter, 12-month stale benefit expungement, Conduent adapter interface |
— |
Month 2 complete (determinations to trigger enrollment) |
|
canopy-renewals with 12-month cert periods (24-month for elderly/disabled), renewal notice scheduling, interim contact management, simplified reporting model, redetermination flow |
— |
Month 1 (application intake for renewal reapplication) |
Exit criteria: An approved SNAP determination creates an enrollment record with the correct initial issuance deadline. A certification period is created with the correct end date. Renewal notices are generated 75 days before certification end. An interim contact is scheduled at 6 months for standard households.
Status: COMPLETE (March 2026). EBT enrollment with proration and expungement job. Certification periods with interim contacts and renewal scheduling. MRs !26-!27.
Month 6: Reporting, Worker Portal, and UAT
Reporting and worker portal can proceed in parallel. UAT begins when both are complete.
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
canopy-reporting with FNS-388 monthly aggregate report, FNS-7176 QC universe extract (50+ elements per case), cross-service data assembly |
— |
Months 1-5 complete (data to aggregate) |
|
canopy-web with dashboard, case search, case detail, application processing, notice management, ABAWD tracking, renewal queue (Askama + htmx + Alpine.js) |
— |
Session middleware, months 1-5 (APIs to call) |
|
UAT Execution |
All FNS-required SNAP test scenarios; parallel run if legacy system available; FNS regional review documentation |
— |
All month 1-6 plans complete |
Exit criteria — SNAP UAT ready:
-
Happy path: single-adult household with wages at 100% FPL → approved, benefit calculated, EBT issued
-
Denial path: household at 140% FPL → denied at gross income test, denial NOA generated
-
Expedited path: household with income < $150 and assets < $100 → expedited flag, 7-day processing
-
Categorical eligibility: TANF cash recipient → auto-approved, no income/asset test
-
ABAWD path: able-bodied adult, no work in 3 consecutive months →
AbawdExceeded, termination NOA -
Appeal path: adverse action appealed before effective date → continued benefits, hearing scheduled
-
QC extract: FNS-7176 pull produces valid CSV for all active certifications
-
Worker portal: caseworker can process application end-to-end without API calls
Post-SNAP-UAT Phases
Phase 3 — FTI Compliance and TANF (Month 7-9)
Introduces the most compliance-sensitive service. Pattern proven by SNAP; TANF adds FTI complexity and SSA CMA data.
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
FTI Audit Logging (delivered; hash-chain mechanism superseded by ADR-041, epic &74) |
Shared FTI audit logging pattern, IRS Pub 1075 audit log, auditor endpoints, canopy-tanf and canopy-medicaid integration. The FTI-specific hash chain (ADR-014) is being retired for ADR-041’s general logging + redaction facility; the Pub 1075 §4 audit obligation is met by the general audit-export channel + per-field redaction. |
ADR-004, ADR-041 |
None — infrastructure pattern |
canopy-tanf with FTI, SSA SOLQ/BINDEX, 60-month time limit, 12-activity work requirements, IV-D referral, TANF data reporting (ACF-199) |
ADR-001, ADR-002, ADR-003, ADR-004 |
SNAP UAT complete (pattern proven), FTI audit logging |
|
ACF-199 quarterly individual-level extract, ACF-196 expenditure aggregation, work participation rate (WPR) calculation (45 CFR 261.22) |
— |
TANF eligibility |
Phase 4 — Medicaid/CHIP (Month 10-14)
The most complex eligibility logic. Medicaid and CHIP are co-deployed (ADR-001 notwithstanding, they share eligibility hierarchy logic).
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
canopy-medicaid with MAGI/non-MAGI, CHIP, EE15 hierarchy, FTI + FDSH + HIPAA, ex parte renewal support, Georgia Pathways work requirement |
ADR-001, ADR-002, ADR-003, ADR-004 |
FTI audit logging, SNAP UAT (FDSH pattern from IEVS work) |
|
canopy-exchange with FfeAccountTransferAdapter implementation, inbound/outbound ACPT XML, No Wrong Door workflow (42 CFR 435.1200) |
— |
Medicaid eligibility (ACA §1413 transfer only applies when Medicaid is live) |
|
T-MSIS monthly eligibility extract, CMS-64 quarterly expenditure aggregation, CMS-416 EPSDT screening rates |
— |
Medicaid eligibility |
Phase 5 — CAPS and WIC (Month 15+)
Simpler compliance posture — no FTI, no FDSH, no CMA. CAPS (DECAL) and WIC (DPH) require cross-agency data flows specific to Georgia.
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
canopy-caps with CCDF income test (50%/85% SMI), copayment tiers, activity requirements, provider authorization, 12-month authorization periods |
ADR-001, ADR-002, ADR-003 |
Application intake, persons model, rules engine |
|
canopy-wic with 5 participant categories, 185% FPL income test, adjunctive eligibility (SNAP/Medicaid/TANF), nutritional risk assessment, food-package families per 7 CFR 246.10(e)(1)-(7), certification periods (7 CFR 246.7(g)) |
ADR-001, ADR-002, ADR-003 |
Application intake, persons model, SNAP eligibility (adjunctive eligibility query) |
Phase 6 — Applicant Portal (Post-CAPS/WIC)
Delivered ahead of the original phasing via Plan 3 — applicant-intake-and-verification (complete 2026-06-01: online application, document upload, determination status, notice inbox, reference-number auth, Fluent plumbing, WCAG 2.1 AA + axe e2e). Remaining follow-ups are tracked as issues, not plan rows: #666 (data-rich Home sections + the Spanish/es Fluent catalog) and #667 (tabs-shell get_tab composition).
| Plan | Delivers | ADRs | Dependencies |
|---|---|---|---|
Applicant Portal |
canopy-portal with Fluent i18n (English + Spanish), WCAG 2.1 AA, online application flow, document upload, determination status tracking, notice inbox |
— |
All program services (portal exposes all programs), session middleware |
Modular Deployment Summary
Per ADR-005, any jurisdiction deploys a profile:
| Profile | Required services |
|---|---|
|
canopy-auth, canopy-persons, canopy-applications, canopy-rules, canopy-eligibility, canopy-snap, canopy-verification, canopy-notices, canopy-appeals, canopy-security, canopy-enrollment, canopy-renewals, canopy-reporting, canopy-web |
|
As |
|
As |
|
canopy-auth, canopy-persons, canopy-applications, canopy-rules, canopy-caps, canopy-eligibility, canopy-notices, canopy-appeals, canopy-security, canopy-enrollment, canopy-renewals, canopy-reporting, canopy-web |
|
All 21 services (Georgia DHS production) |
A new jurisdiction adds their configuration:
mkdir rulesets/my-state
cp rulesets/georgia/* rulesets/my-state/
# Edit jurisdiction.toml with state-specific thresholds and options
CANOPY_JURISDICTION=my-state COMPOSE_PROFILES=snap-only docker compose up -d
Dependency Graph
MONTH 1 (parallel): ✓ COMPLETE
reference-extensions ─── ✓ MR !1
session-middleware ───── ✓ MR !5
persons ──────────────── ✓ MR !6
rules ────────────────── ✓ MR !8
signing ──────────────── ✓ commit eda958f
security-audit ──────── ✓ MR !9
application-intake ───── ✓ MR !10
MONTH 2 (sequential after month 1):
snap-eligibility ─────── depends: persons, rules, signing
snap-deduction-calc ──── depends: snap-eligibility
eligibility-orchestrator depends: signing, snap-eligibility
snap-categorical ─────── depends: snap-eligibility (parallel with orchestrator)
snap-abawd ──────────── depends: snap-eligibility (parallel with orchestrator)
snap-special-situations ─ depends: snap-eligibility (parallel with orchestrator)
MONTH 3:
snap-ievs-verification ── depends: snap-eligibility schema
save-adapter ──────────── depends: reference-extensions (parallel with IEVS)
MONTH 4 (parallel):
notice-generation ────── depends: determination events from month 2
fair-hearings-appeals ── depends: notices (for AppealAcknowledgment)
ipv-disqualification ─── depends: fair-hearings-appeals (shared canopy-appeals)
MONTH 5 (parallel):
snap-enrollment-ebt ──── depends: month 2 determinations
snap-renewals ────────── depends: month 1 application-intake
MONTH 6 (parallel):
snap-federal-reporting ── depends: months 1-5 data
worker-portal-snap ────── depends: session-middleware + months 1-5 APIs
UAT execution ────────── depends: month 6 complete
Post-UAT:
fti-audit-logging ─────── can start anytime
tanf-eligibility ─────── depends: fti-audit-logging + SNAP UAT
tanf-federal-reporting ── depends: tanf-eligibility
medicaid-eligibility ───── depends: tanf (FTI pattern) + SNAP UAT
ffe-exchange ──────────── depends: medicaid-eligibility
medicaid-federal-reporting depends: medicaid-eligibility
caps-eligibility ─────── depends: persons + applications (can parallel with TANF)
wic-eligibility ──────── depends: persons + applications + SNAP (adjunctive)
applicant-portal ─────── depends: all program services
Remaining Work Tracker
Comprehensive list of all outstanding work, organized by priority tier. Updated by each implementation session. Canonical source of truth for "what’s left."
Tiers 0-8 below capture historical work identified at specific past dates (most are now Done). For the current snapshot of pending work — open GitLab issues, open plan Status rows, and ADR follow-throughs — see Tier 9 — Open work snapshot further down.
Tier 0 — Hygiene (stale plans, no code changes)
| Item | Effort | Status |
|---|---|---|
Update jdm-ruleset-rewrite plan status table (Steps 5-8 → Complete) |
5 min |
Done (2026-04-12) |
Update documentation-completeness plan (~10 of 18 steps actually done) |
15 min |
Done (2026-04-12) |
Correct tanf-pamms-alignment Steps 3-4 → "Partial (migration only)" |
5 min |
Done (2026-04-12) |
Correct tanf-federal-reporting Step 7 → "Partial (no integration tests)" |
5 min |
Done (2026-04-12) |
Correct medicaid-federal-reporting Step 7 → "Partial (no integration tests)" |
5 min |
Done (2026-04-12) |
Correct snap-deduction-calculation plan to reflect JSON-file approach |
10 min |
Done (2026-04-12) |
Tier 1 — Gaps in "complete" work
| Item | Plan / Source | Status |
|---|---|---|
tanf-pamms-alignment Steps 3-4: GRG API endpoints + personal-responsibilities store fns (migrations exist, no Rust) |
tanf-pamms-alignment |
Done (2026-04-12) |
tanf-federal-reporting: write the 9 integration test scenarios (ACF-199/WPR/ACF-196) |
tanf-federal-reporting |
Done (2026-04-12) — structural content tests added |
medicaid-federal-reporting: write the 10 integration test scenarios (T-MSIS/CMS-64/CMS-416) |
medicaid-federal-reporting |
Done (2026-04-12) — structural content tests added |
Dead |
Code review |
Done (2026-04-12) — debug_assert added |
|
Code review |
Done (2026-04-12) |
|
Code review |
Done (2026-04-12) — 151/151 citations |
Error messages from evaluate don’t identify which ruleset failed |
Code review |
Done (2026-04-12) — ruleset name in source chain |
|
Code review |
Done (2026-04-12) |
|
Code review |
Done (2026-04-12) |
openapi-contract-testing Step 1: add canopy-tanf + canopy-medicaid to api_docs.rs SERVICES |
openapi-contract-testing |
Done (2026-04-12) |
crate-quality-parity Step 1: |
crate-quality-parity |
Done (2026-04-12) — 243 warnings; fixing incrementally |
workflow-guidance-templates Steps 5-6: canopy-web guidance panel template |
workflow-guidance-templates |
Done (2026-04-12) |
Tier 2 — Phase 3 plan residuals
| Item | Plan | Status |
|---|---|---|
tanf-pamms-alignment Steps 3-4 endpoints (GRG MSP+CRISP API, personal responsibilities CRUD) |
tanf-pamms-alignment |
Done (2026-04-12) — completed in Tier 1 |
Medicaid 38-COA expansion Phase A: FormerFosterCare, Newborn, WHM, P4HB (FP/IPC/RM), FourMonthsExtended, Refugee (8 COAs — boolean flags + simple income tests) |
medicaid-implementation |
Done (2026-04-12) — 15/38 COAs now have real evaluation logic |
Medicaid 38-COA expansion Phase B: Q-Track (QMB/SLMB/QI-1) + Family MN spenddown (9 steps) |
Done (2026-04-13) — 20/38 COAs evaluable |
|
Medicaid 38-COA expansion Phase C: TMA cross-program (8 steps) |
Done (2026-04-13) — 21/38 COAs evaluable |
|
Medicaid 38-COA expansion Phase D: ABD FBR SSA-linked (8 steps) |
Done (2026-04-13) — 26/38 COAs evaluable. Orchestrator SSA wiring shipped 2026-05-11 against |
|
Medicaid 38-COA expansion Phase E: ABD waivers + AMN spenddown (10 steps) |
Done (2026-04-13) — 35/38 COAs evaluable |
|
Medicaid 38-COA expansion Phase F: FosterCare/Adoption/Chafee (6 steps) |
Done (2026-04-13) — 38/38 COAs evaluable |
Tier 3 — New program services
| Plan | Steps | Issues | Status |
|---|---|---|---|
12 |
#203-#210 |
Done (2026-04-13) — 3 tables, 3 API routes, 7 unit tests, JDM ruleset |
|
8 |
#211-#218 |
Done (2026-04-13) — 3 tables, 4 API routes, 6 unit tests, JDM ruleset |
Tier 4 — Federal reporting completion
| Item | Plan | Status |
|---|---|---|
Medicaid T-MSIS + CMS-64 + CMS-416 full pipeline (Issues #196-#202) |
medicaid-federal-reporting |
Done (2026-04-13) — T-MSIS 38-COA mapping, CMS-64 enrollment aggregation, CMS-416 EPSDT by age band, CSV export. Limitations in errata. |
TANF ACF-199/WPR/ACF-196 enrichment + WPR calculation |
tanf-federal-reporting |
Done (2026-04-13) — ACF-199 enriched (14 cols), WPR calculation, ACF-196 stubs, CSV export. Limitations in errata. |
SNAP FNS-388 endpoint content assembly (some return stub data) |
snap-federal-reporting Step 8 |
Partial |
Tier 4.6 — Worker portal multi-program expansion
| Item | Plan | Status |
|---|---|---|
canopy-web: TANF/Medicaid/CAPS/WIC case detail tabs + program-aware routing (7 steps) |
worker-portal-expansion |
Done (2026-04-14) — Program enum, ServiceClients expansion, 4 new program tabs, dashboard multi-program |
Tier 5 — Cross-program and integration
| Item | Plan | Status |
|---|---|---|
End-to-end cross-program functional testing (TSNAP/TMA/Express Lane with real determination flows) |
cross-program-functional-testing |
Done (2026-04-17) — All 9 steps complete. 877/877 tests pass end-to-end. Includes TSNAP/TMA/ELE E2E tests, TMA negative test, ELE positive + negative test, and |
Ephemeral port allocation (xtask port reservation + discovery + |
ephemeral-port-allocation |
Done (2026-04-14) — Verified with dev restart + full test battery, no port collisions with craig |
OpenAPI contract breaking-change detection CI gate |
openapi-contract-testing |
Moved to Post-1.0 — premature while we’re making breaking changes regularly |
Tier 5.5 — Deferred placeholders / simplifications
Code items tagged TODO / placeholder / stub that must be addressed before 1.0.
services/canopy-reporting/src/reporting/tanf.rs:287) moved to Tier 8 — Externally blocked (waits on state accounting system integration).
| Location | Issue | Tracking |
|---|---|---|
|
TMA subscriber now iterates per-member |
Done (2026-04-18) — tma-subscriber-person-lookup. Backfill xtask deferred (no historical placeholder rows exist pre-UAT). |
|
Continued-benefits overpayment now sums real issuances via |
Done (2026-04-20) — canopy-enrollment-household-issuances |
|
Work hours placeholder replaced — ACF-199 now aggregates real per-activity hours via |
Done (2026-04-19) — canopy-tanf-work-activities-list |
|
Program-specific income display — income tab now fetches per-member self-reported income from canopy-persons, renders program-specific rule pointer (citations only, no hardcoded thresholds), hides IEVS column for non-SNAP programs |
Done (2026-04-20) — canopy-web-persons-wiring |
CAPS authorization tab (canopy-web) |
Live — two new canopy-caps list endpoints wired through canopy-web. Template + view-model reconciled against the DB schema (Option A — no care_type column; authorization_status / end_date / rate_display / copayment_display). |
Done (2026-04-21) — canopy-caps-list-endpoints. Playwright E2E landed 2026-04-21 via canopy-seed-caps-wic-fixtures. |
WIC nutritional risk tab (canopy-web) |
Live — canopy-wic grew |
Done (2026-04-21) — canopy-wic-list-endpoints. Playwright E2E landed 2026-04-21 via canopy-seed-caps-wic-fixtures. |
Medicaid person names (canopy-web) |
Medicaid determination tab resolves real names via |
Done (2026-04-20) — canopy-web-persons-wiring |
|
Fluent .ftl bundles loaded at startup (main.rs); page-level i18n delivered (Plan 3, English) — the Spanish ( |
Delivered (Plan 3); |
Tier 5.6 — Confirmed hacks and workarounds
Intentional shortcuts documented in code comments and plan errata.
| Workaround | Location | Reason |
|---|---|---|
|
|
Fresh-boot |
|
|
FTI-scrubbed from wire payload per ADR-004; treated as |
|
Done (2026-04-22) — JDM |
|
Payload field |
|
Done (2026-04-22) — publisher now emits |
|
|
17 services × per-service pool exhausts default 100 under integration load |
ZEN expression unquoting workarounds |
JDM rulesets |
zen-engine 0.x limitations (see jdm-ruleset-rewrite plan errata) |
Tier 5.7 — Plan errata cross-reference
Plans with active errata (limitations, shortcuts, or deviations documented at end of plan):
| Plan | Errata topics |
|---|---|
cross-program-functional-testing |
Denial-reason categorization, payload schema mismatch, max_connections tuning, ADR-011 constants migration follow-up |
jdm-ruleset-rewrite |
zen-engine workarounds, quoting behavior |
tanf-federal-reporting |
WPR formula simplifications, ACF-196 category stubs |
medicaid-federal-reporting |
T-MSIS COA-to-coverage-group mapping limitations, CMS-64 expenditure data gaps |
eligibility-orchestrator |
SSA pre-dispatch wiring for Medicaid Phase D landed 2026-05-11 against |
medicaid-coa-phase-c-tma |
Placeholder person_id in TMA subscriber |
medicaid-coa-phase-d-abd-fbr-ssa |
SSA SOLQ data flow shipped 2026-05-11 (#384). Pickle / DAC / DW / Widow 60-64 / Former SSI Disabled Child COAs now evaluable end-to-end against the Noop adapter. |
playwright-e2e |
SNAP-only coverage; multi-program expansion pending (T7) |
shared-db-devstack |
Per-program postgres containers suppressed but not removed from compose file |
snap-categorical-eligibility |
PAMMS 3210 edge cases not fully covered |
application-intake |
ACA §1413 single-streamlined-application edge cases |
security-ci-remediation |
CI pipeline sequencing notes |
Tier 6 — Infrastructure and operations (post-UAT)
| Item | Plan | Status |
|---|---|---|
operational-infrastructure Steps 2-14: crypto key mgmt, backup/restore, migration CLI, monitoring, log aggregation, canary deploys, blue-green, secrets, load testing, DR, perf baselines, SLA dashboards, capacity planning |
operational-infrastructure (archived 2026-04-30) |
14/14 complete |
documentation-completeness residual ~8 steps: data dictionary, testing strategy, contributor guide, i18n guide, dependency update policy, monitoring guide, capacity planning guide, applicant help content |
documentation-completeness |
~10/18 complete |
Applicant portal (Dioxus fullstack, reference-number auth, Fluent i18n, WCAG 2.1 AA) |
ADR-008 |
Implemented (Dioxus fullstack, Plan 3 complete) |
Tier 7 — Pre-1.0 quality gate
| Item | Source | Status |
|---|---|---|
Playwright E2E multi-program expansion (TANF/Medicaid/CAPS/WIC flows) |
playwright-e2e |
Partial (2026-04-21) — SNAP + CAPS + WIC case-detail tab coverage landed via canopy-seed-caps-wic-fixtures (MR !104). TANF + Medicaid program-specific tabs still rely on permissive "renders without error" assertions against SNAP-seeded data. |
YAML config migration |
Open issue #291 + ADR-012 |
Direction ratified 2026-04-23 via ADR-012 (layered YAML + env overrides, struct schema, secrets-env-only). Implementation not started — per-service rollout tracked in issue #291. No scheduled cutover; each service’s migration MR is independently reviewable. |
Flaky application tests (2 tests fail under 8-thread parallel load) |
|
Done (2026-04-22) — not reproducible after the |
|
crate-quality-parity Step 1 |
Done (2026-04-14) |
Plan Status-vocabulary lint + archive directory |
Done (2026-04-23) — closed-set vocabulary ratified; |
|
Shared-db postgres suppression |
Open issue #297 |
Done (2026-04-14) — |
|
validate CI |
Done (2026-04-14) — no clippy/missing-docs blockers |
Ephemeral port allocation |
ephemeral-port-allocation |
Done (2026-04-14) |
|
Done (2026-04-21) — roadmap citation corrected from PAMMS 1605/1611 (1611 does not exist) to PAMMS 1540/1615 |
|
|
xtask |
Done (2026-04-18) |
JDM ruleset end-to-end happy-path tests (12 rulesets) |
Done (2026-04-27) — all 12 fixtures evaluable, drift gate live |
|
Orchestrator parallel-dispatch and circuit-breaker tests |
Partial (3 of 7) — parallel fan-out + per-service timeout isolation + signature-tamper quarantine tests landed (MR !55). Circuit-breaker tests (Steps 4-5) and optional-service degradation integration test (Step 7) deferred with plan errata. |
|
FTI / audit-events hash-chain verification (mechanism superseded by ADR-041, epic &74) |
Done (2026-04-18) — 3 DB-backed tests on |
|
ADR-004 SSA / IEVS / FTI authorisation audit (manifest + CI gate) |
Planned (2026-04-18) |
|
ADR-005 graceful-degradation verification (capability flags + Compose-profile matrix) |
Done (2026-04-26) — Steps 1, 2, 5 of plan verified: |
Tier 8 — Externally blocked
Work that cannot progress without a dependency outside the Canopy team’s control — partner-system readiness, signed CMAs, live production credentials, or state-level data integrations. Each row names the blocker explicitly so reviewers can see why the item sits here rather than in an earlier tier. Unblock-and-go plans exist where noted; the remaining work is purely the unblock step.
| Item | External dependency | Canopy-side readiness | Plan |
|---|---|---|---|
FFE Account Transfer (CMS-to-state account transfers) |
Georgia Access (state health exchange) not yet live; CMS-side FFE endpoint credentials pending state SOC-2 review |
Stub service exists ( |
|
ACF-196 TANF federal financial report (state-accounting integration) |
State accounting system data pipeline — categorized expenditure amounts come from Georgia’s financial system, not Canopy |
Stub at |
|
CMS-64 Medicaid quarterly expenditure data (MMIS integration) |
MMIS (Georgia Medicaid Management Information System) — dollar expenditures flow from MMIS claim processing, not Canopy |
Canopy-owned enrollment counts + member-month aggregation complete via T-MSIS extracts. Expenditure dollar columns are |
|
CMS-416 Medicaid EPSDT screening counts (MMIS clinical/claims integration) |
MMIS (same source as CMS-64) — screening-service utilization comes from claims, not eligibility |
Enrolled-children denominator + age-band breakdown complete from T-MSIS. Screening-numerator columns are |
|
SSA SOLQ / BINDEX wiring (Medicaid ABD income verification) |
Executed Computer Matching Agreement (CMA) with SSA + live SSA production credentials (42 USC §1320b-7, SSA §1106) |
|
|
IEVS live adapters (Georgia DOL SWR + UI; SSA SDX + BENDEX) |
Georgia DOL credentials + SSA CMA (same as above). Same data-sharing agreements that gate SSA SOLQ. |
|
Tier 9 — Open work snapshot (historical; 2026-05-04)
glab issue list --state opened (or the epic boards). The 2026-05 enumeration is retained below only for historical traceability of the May build-out wave.
Live state of pending work as of 2026-05-04, complementing the historical Tiers 0-8 above. The earlier tiers track work identified and prioritized at specific past dates; this section enumerates everything currently still open across plan Status tables, GitLab issues, and ADR follow-throughs.
glab issue list --state opened (56 open). Closed since the 2026-05-04 snapshot (newest first, see git log for MR refs):
-
#399 / #400 / #402 canopy-applications validation + PUT programs sync + caseload search (!248, 2026-05-10).
-
#382 follow-on —
appeal.overpayment_assessedauto-opens claims in the right program-service DB (!247, 2026-05-10). -
Per-service API reference pages for canopy-tanf / canopy-medicaid / canopy-caps / canopy-wic (!249, 2026-05-10) — partial address of #265.
-
Epic !49 (ADR-003 cross-service hygiene pass, closed 2026-05-10) bundling #443 canopy-notices event routing, #444 keycloak realm port-drift enforcement, #440 canopy-snap IEVS source names, #441 canopy-tanf federal time-limit, #442 canopy-appeals ADH + decision-clock.
-
#382 canopy-overpayments shared crate + per-program recovery pipeline + canopy-reporting roll-up CSV (2026-05-10).
-
#385 canopy-cli ADR-007 parity catchup, #401 canopy-applications authorized-rep CRUD, #339 xtask
validate-in-networkrunner, #411 canopy-web BFF service-to-service auth, #424 canopy-identity service-class token migration, #422 provider-agnostic OIDC, #439 ADR-019 hard cutover, #427 RFC 8785 canonical-JSON envelope, #428 scheduler leader election, #429 canopy-rules per-endpoint RBAC, #430 OTLP-direct observability migration, #432 SNAPincome_typefield alignment, #426 SNAP params jurisdiction-toml path correction, #425 dev-key fallback hardening.
Cumulative effect: every Phase B / C / D / Phase-E item from Tier 10 below has shipped, plus the Phase E.2 canopy-applications batch (#399 / #400 / #402 closed by !248) and per-service API reference pages (!249). The open queue is now (a) the FFE-blocked Phase G cluster, (b) the four Phase-F deferred trackers (#349-#352), and (c) the Phase E.2 residual (#265 partial / #267 / #308 / the reserve_ports race).
2026-05-11 → 2026-05-12 session delta (Tier B Wave 2-4 + #448 follow-up):
-
Wave 2 — !260 closes #384 deliverable (a) Medicaid SSA SOLQ pre-dispatch wiring (NoopSolqAdapter live; CMA-blocked real cutover stays open on #384).
-
Wave 3 — !261 closes #407 SNAP partial-month retention (filed follow-up #447 for canopy-appeals overpayment-math consumption); !262 chore: idempotency cache lookup logging (replaces silent
.ok()?with diagnostic WARN per two parallel agent investigations); !263 closes #408 enrollment-household-rbac withhousehold_assignmentstable in canopy-applications + Pub 1075 §9.3.1 gate onGET /v1/households/{id}/issuances. -
Wave 4 — !264 closes #420 worker portal design mockups; !265 closes #392 worker portal program action handlers (20 handlers × 4 programs + tab affordances; filed follow-ups #448 upstream gaps + #449 Playwright specs).
-
#448 follow-up to #392 — !266 closes 11 of 14 upstream gaps (canopy-renewals path-parameterization, canopy-caps PUT mutations, canopy-web WIC nutritional-risk BFF path bugfix); !267 closes the remaining 3 with new schema work (canopy-tanf
tanf_discrepancies+ resolve handler; canopy-medicaidmedicaid_cmd_events+ cmd-ingest + determination requeue; canopy-wicwic_appointments+ schedule-appointment). -
Wave 5 — #396 closed:
caps-provider-registryplan landed (caps_providers table, 5 CRUD endpoints, FK retypes on caps_applications/caps_authorizations, FK-validated #448 switch-provider, 422 mapping for FK/unique violations). canopy-caps route count 7 → 12; canopy-seed seeds a 5-row provider catalogue. -
Architectural follow-up filed: #450 test-seed harness needs atomic manifest+DB seeding + random-by-default + predicate fixtures (root cause of the WIC/CAPS Playwright flake during the session).
plans/archive/. The Tier 7 row below preserves its position for historical traceability. The canopy-caps-list-endpoints and canopy-wic-list-endpoints plans were archived 2026-05-01. The secret-and-config-migration plan closed out 2026-05-02 across MRs !163 through !183 (23 steps; 22 step-tracking issues #354-#375 all closed; ADR-017 ratified; ADR-012 implementation complete; multi-key SSN encryption rotation support shipped).
Open GitLab issue counts as of 2026-05-12 end-of-session (after Wave 2-4 + #448 closures, plus #447 / #449 / #450 follow-up filings): roughly 49 open standalone + 7 plan-step (the FFE issues #189-#195). The 2026-05-03 snapshot above (62 / 55 standalone) is preserved for delta reference; the actual count fluctuates as follow-ups are filed and closed. Distribution still skews FFE-blocked at the high-priority end; consult glab issue list for the live count.
The 2026-05-03 → 2026-05-04 delta: 33 new issues filed (#378-#390 production-readiness gaps from contextless audits; #391-#418 second-wave Potential Improvements bullets; #419-#421 documentation-completeness Not started rows). The PI sweep tied off all known plan "Potential Improvements" backlogs — every remaining bullet is either a tracked issue or an explicit deferral with rationale.
Active plan open rows
| Plan | Open rows | Open work summary |
|---|---|---|
1 (3 of original 4 now have GitLab issues) |
Step 1: Create role-based user guides (caseworker, eligibility specialist, supervisor, auditor) — only the caseworker guide exists. Steps 7/9/14 split out to #419 / #420 / #421 (data-model docs + ERDs, UI mockups, screenshots). |
|
7 |
Step 1: Define FfeAccountTransferAdapter trait methods for inbound a; Step 2: Create |
Standalone GitLab issues (not plan-step issues)
| iid | priority | workflow | type | title |
|---|---|---|---|---|
#267 |
low |
ready |
documentation |
Applicant-facing help content and accessibility guide |
#308 |
medium |
needs-spec |
feature |
Standalone EC2 Deployment Pipeline for Canopy project |
#323 |
low |
ready |
— |
refactor(canopy-tanf): typed expense_type enum replacing stringly-typed ExpenseItem.expense_type |
#325 |
low |
ready |
— |
refactor(canopy-medicaid): switch extract_person_ids to typed UUID when MemberContext.person_id becomes typed |
#337 |
low |
ready |
chore |
chore(canopy-notices): PDF/A conformance for archival-grade notice PDFs |
#339 |
low |
needs-spec |
chore |
Flip cargo xtask validate to in-network integration runner (ADR-015 follow-up) |
#348 |
low |
needs-spec |
feature |
SSE caseload filtering (op-infra Step 9 follow-up) |
#349 |
low |
needs-spec |
feature |
feat(canopy-web): render workflow guidance panel UI |
#350 |
low |
needs-spec |
compliance |
test(canopy-eligibility): compose-profile-matrix integration tests + CI job |
#351 |
low |
needs-spec |
feature |
feat(canopy-mq): compile-time EventPayload macro + CI grep lint (event-bus-enforcement Steps 2+4) |
#352 |
low |
needs-spec |
chore |
chore(post-UAT): revive openapi-contract-testing plan after September 2026 UAT |
#378 |
medium |
needs-spec |
feature |
feat(canopy-reporting): wire ACF-196 expenditures from state accounting |
#379 |
medium |
needs-spec |
feature |
feat(canopy-reporting): wire CMS-64 expenditure aggregation from MMIS |
#380 |
medium |
needs-spec |
feature |
feat(canopy-reporting): wire CMS-416 EPSDT screening data from clinical systems |
#381 |
low |
needs-spec |
feature |
feat(canopy-portal): wire Fluent i18n loader (replace stub LocaleManager) |
#382 |
medium |
needs-spec |
feature |
feat(canopy-enrollment): wire overpayment recovery pipeline (currently log-only) |
#383 |
medium |
ready |
security |
security(fti): dedicated fti_auditor role (currently uses admin) |
#384 |
medium |
needs-spec |
feature |
feat(canopy-eligibility): wire SSA-linked flags through orchestrator to canopy-medicaid Phase D |
#385 |
medium |
needs-spec |
feature |
feat(canopy-cli): catch up to ADR-007 — add household / income / asset / interview / determine subcommands |
#386 |
medium |
needs-spec |
— |
refactor(canopy-medicaid): move CMD cascade priority + TMA branching + denial-reason synthesis from Rust to JDM (ADR-003 drift) |
#387 |
high |
needs-spec |
— |
fix: normalised determination envelope for orchestrator signature verification (medicaid signatures broken in transit) |
#388 |
medium |
needs-spec |
feature |
perf(canopy-mq): persistent outbox table for at-least-once durability across crash |
#389 |
low |
ready |
feature |
feat(canopy-mq): configurable buffer-full strategy (drop-newest / drop-oldest / block) |
#390 |
low |
ready |
feature |
feat(canopy-mq): backpressure signal when publish buffer crosses threshold |
#391 |
medium |
ready |
feature |
feat(canopy-caps,canopy-wic): GET /v1/determinations?household_id=X list endpoints |
#392 |
medium |
ready |
feature |
feat(canopy-web): program-specific action handlers (TANF work-activity, WIC nutritional risk, CAPS authorization) |
#393 |
low |
ready |
feature |
feat(canopy-web): cross-program dashboard stats (TANF/Medicaid/CAPS/WIC widgets) |
#394 |
low |
ready |
feature |
feat(canopy-web): cross-program summary view per household |
#395 |
low |
ready |
feature |
feat(canopy-seed): multi-determination per CAPS household |
#396 |
low |
done (2026-05-12) |
feature |
feat(canopy-caps): provider registry — closed by |
#397 |
low |
ready |
feature |
feat(canopy-seed): WIC multi-participant households (pregnant mother + infant) |
#398 |
low |
ready |
feature |
feat(canopy-seed): CAPS/WIC renewals, transfers, and termination fixtures |
#403 |
low |
ready |
chore |
feat(canopy-typst): bundle Montserrat TTF in rulesets/georgia/notices/fonts/ |
#404 |
low |
needs-spec |
chore |
feat(canopy-typst): agency seal / logo SVG assets for letterhead.typ |
#405 |
medium |
ready |
feature |
feat(canopy-typst): form-building Typst components (checkbox-grid, data-table, field-row, conditional-section) |
#406 |
low |
ready |
feature |
feat(canopy-tanf): per-row activity_breakdown drill-down on work-activity summary endpoint |
#407 |
medium |
needs-spec |
feature |
feat(canopy-enrollment): partial-month retention rule per PAMMS 2415 |
#408 |
low |
needs-spec |
feature |
feat(canopy-enrollment): household-scope RBAC for issuance listing |
#409 |
medium |
ready |
feature |
feat(canopy-web): per-member income editing UI for caseworkers |
#410 |
low |
needs-spec |
feature |
feat(canopy-persons): historical income versioning with superseded_by pointer |
#411 |
high |
needs-spec |
feature |
feat(canopy-web): service-to-service auth for BFF → upstream API calls |
#412 |
low |
ready |
chore |
feat(jurisdiction.toml): consolidated [shared.timing] section for renewal/expungement/dashboard windows |
#413 |
medium |
ready |
compliance |
compliance(adr-011): formalise ATO evidence statement for policy-trace coverage |
#414 |
medium |
needs-spec |
feature |
feat(canopy-snap): self-employment standard deduction parity (PAMMS 3425 / 7 CFR 273.11(a)(2)) |
#415 |
low |
ready |
feature |
feat(canopy-medicaid): denial-code parity with canopy-tanf (DenialReasonCode enum) |
#416 |
low |
needs-spec |
feature |
feat(canopy-tanf): sanction denial path through eligibility ruleset |
#417 |
low |
ready |
feature |
feat(canopy-medicaid): dead-letter on missing person_ids in tanf.case_closed subscriber |
#418 |
medium |
ready |
feature |
feat(xtask): cargo xtask compliance capabilities — manifest enforcement in CI |
#419 |
low |
ready |
documentation |
docs: per-service data model documentation with column descriptions + ERDs |
#420 |
low |
needs-spec |
documentation |
docs: UI mockup / design docs for worker portal modules |
#421 |
low |
ready |
documentation |
docs: screenshots page organised by portal module |
[<plan>] Step N) tracked under the Active plan rows table above (the 7 ffe-account-transfer issues #189-#195). The 22 secret-and-config-migration step issues (#354-#375) all closed across MRs !163-!183 on 2026-05-02. Op-infra Step 12 follow-ups #341/#342 closed via !157, #343 closed via !156, #344 closed via !158 — all merged 2026-05-01. #291 and #346 closed via !163 (2026-05-02) as superseded by the secret-and-config-migration plan. #340 closed via !160, #345 closed via !162 (Phase B).
ADR follow-throughs broken into plans + complete
-
ADR-008 — Applicant portal (Dioxus fullstack, reference-number auth, Fluent i18n, WCAG 2.1 AA). Implemented (Plan 3 complete). Delivered under applicant-intake-and-verification and the filed-and-Done demo-workflow-build-and-e2e plan.
ADR follow-throughs not yet broken into plans
-
ADR-012 — YAML config migration. Direction ratified; per-service rollout coordinated under secret-and-config-migration plan alongside ADR-017 secret moves; supersedes issue #291.
-
ADR-017 — Encrypted secrets at rest with SOPS + age. Ratified 2026-05-02; implementation tracked under the same secret-and-config-migration plan; supersedes issue #346 (Vault).
Tier 10 — Master Tackle Order (phases, not snapshots)
Tier 9 captures what’s open right now. This tier captures the order to tackle it in — independent of churn in the issue list. Phases are ordered by dependency chains, not re-evaluated priority: existing GitLab priority::* labels are taken as authoritative within each phase.
Phase A — Plan archival housekeeping ✅ DONE
MR !159 (commit 441c3df, 2026-05-01) — archived 2 functionally-Done plans (canopy-caps-list-endpoints, canopy-wic-list-endpoints), shipped cargo xtask docs plan-lint Deferred-tracker enforcement, refreshed Tier 9 above.
Phase B — Op-infra follow-ups (RESIDUAL — both gated)
Done:
-
MR !160 — #340 idempotency replica-restart integration test + production bug fix.
-
MR !161 — QC half of #347 + rustdoc half of #348.
-
MR !162 — #345 forward-only migrations / ADR-016.
-
MR !186 — #353 PITR runbook for production schema rollback (ADR-016 follow-up).
-
MR !187 — #347 address-join half of person export.
-
MR !188 — #348 htmx-sse extension wiring half.
Folded into Phase C:
-
#346 (Vault SecretProvider) → superseded by ADR-017 in the combined plan; closed 2026-05-02.
Residual (both gated; don’t pick early):
-
#348 (
priority::low) — SSE caseload filtering. Gated on the per-worker caseload-membership store (post-UAT case-management work). -
#339 (
priority::low) — flipcargo xtask validateintegration step to in-network runner (ADR-015 follow-up). Deferred — needs CI infra changes that haven’t happened yet.
Phase C — Foundational ✅ DONE (2026-05-02)
secret-and-config-migration plan — coordinated 23-step rollout of ADR-012 (layered YAML config) + ADR-017 (SOPS+age secrets) shipped across MRs !163 through !183. All 22 step-tracking issues (#354-#375) closed. Plan archived under plans/archive/.
Highlights:
-
MR !163 — Step 0: ADR-017 ratification + plan + #291/#346 superseded.
-
MR !166 — Step 1:
cargo xtask secretstooling +canopy-devtoolscompose service +secrets-yaml-lintCI job. -
MR !168 — Step 2: layered YAML loader in
canopy-common. -
MR !170 + !171 fix — Step 3: canopy-snap canary.
-
MR !172 — Step 4: canopy-exchange empty-case template.
-
MR !173 — Step 5: canopy-persons + multi-key SSN encryption rotation support (canopy-common::crypto
EncryptionKeys/decrypt_with_rotation). -
MR !174 — Step 6: canopy-verification.
-
MR !175 — Step 7: canopy-applications.
-
MR !176 — Steps 8-10: canopy-enrollment + canopy-renewals + canopy-notices.
-
MR !177 — Steps 11-12: canopy-rules + canopy-appeals.
-
MR !178 — Step 13: canopy-security FTI URLs as secrets.
-
MR !179 — Steps 14-16: canopy-tanf + canopy-caps + canopy-wic.
-
MR !180 — Step 17: canopy-medicaid.
-
MR !181 — Step 18: canopy-eligibility.
-
MR !182 — Steps 19-21: canopy-portal + canopy-web + canopy-reporting.
-
MR !183 — Step 22: cleanup,
.env.exampleretired, plan archived, developer-guide updated.
Material plan deviations (preserved here so a future audit doesn’t need to walk all 22 commits): serde_yaml dropped (archived upstream) → sops --output-type json + serde_json::Value; age + sops live in canopy-devtools compose service rather than as host requirements; deny_unknown_fields deferred from per-service structs and ServiceSettings because the prefix-shared env source feeds shared baseline keys (port, database_url, rabbitmq_url, keycloak_*) through every service’s prefix — restoration requires a ServiceSettings-flatten refactor first; internal_api_key lives under shared: in dev.yaml (not canopy-snap:) so it emits as CANOPY_INTERNAL_API_KEY (no prefix) matching every service’s existing read site; multi-key SSN encryption rotation support implemented inline rather than deferred per feedback_no_deferral; canopy-portal kept lightweight (groundwork only — single-tunable Rust refactor was minimal-ROI); canopy-eligibility verifying keys (5 programs) stay env-or-.keys/-routed because they’re public keys, not secrets.
Residual operator action: set the GitLab masked variable CANOPY_CI_AGE_KEY from cargo xtask secrets init --for-ci output. Without this, the integration-tests CI job fails when sops-decrypting secrets/dev.yaml. The CI keypair private was emitted during MR !166’s bootstrap.
Out-of-window follow-ups (NOT separate issues; tracked here as the cleanup phase trigger):
-
Restore
[serde(deny_unknown_fields)]onServiceSettingsand per-service structs — requires the per-service config struct to flatten the shared baseline keys via[serde(flatten)]so the env source’s prefix-shared reads have somewhere to land. -
Retire residual
CANOPY_<SVC>__JURISDICTIONenv vars — same precondition; jurisdiction currently feedsServiceSettings.jurisdictionand the per-service struct.
Phase D — Tier 7 polish ✅ ESSENTIALLY DONE (2026-05-04)
17 of 20 issues closed across MRs !189 through !206 (16 MRs, including the JDM-namespacing Path B sweep that #331 blocked on). The 3 residual items are externally gated.
Shipped:
-
xtask ADR-004 audit cluster: #326 (!189), #327 (closed 2026-05-04 — no concrete bug), #328 (!189).
-
xtask rules-check cluster: #329 (!190), #331 (!196), #330 (!206 — input-side drift gate symmetric to #331’s threshold-side gate).
-
canopy-reference build.rs: #324 (!197).
-
canopy-eligibility test infra: #332 (!201), #333 (!201), #334 (!198).
-
canopy-notices cluster: #321 (closed 2026-05-04 — typst-render bench in !203 showed engine reuse saves <1ms per render; no ROI without state-scale profiling), #335 (closed — code review showed templates already hot-reloaded; only the manifest needs restart).
-
canopy-tanf/medicaid refactors: #320 (closed 2026-05-04 — !204 bench shows aggregate_summary is sub-millisecond, and ACF-199 hits each (person, month) at most once so cache hit-rate is near zero; bulk query is the right fix if profiling later flags it).
-
persons/enrollment perf cluster: #318 (closed — !205 load profile p95 0.31ms per income GET; N+1 fan-out costs ~6ms even at N=20), #319 (closed — !205 load profile p95 0.91ms for the household-scoped issuance list; bulk-fetch saves nothing).
-
e2e cluster: #322 (!200 — Playwright storage-state cached across runs, auth-setup 1.81s → 0.59s), #336 (!199 — vendor.toml + sha256 drift gate).
Residual (all externally gated; don’t pick early):
-
#337 (
priority::low) — PDF/A conformance for archival-grade notice PDFs. Upstream support landed (typst-pdf 0.12+; workspace on 0.14.2); enforced 2026-07-25 — the engine exports PDF/A-2b with a baseline-PDF loud fallback, and every template render test validates conformance. -
#323 (
priority::low) — typedexpense_typeenum. Explicitly deferred to the crate-quality-parity sweep (the same pattern appears acrossIncomeItem.income_type,AssetItem.asset_type, and should be swept together). -
#325 (
priority::low) — switchextract_person_idsto typed UUID. Conditional on the orchestrator-sideMemberContext.person_idtyping refactor happening first.
Phase D enabling work also shipped this session (not Tier 7 polish, but landed alongside):
-
Path B sweep — !191-!195 — namespaced every JDM ruleset (
input./context.thresholds.) so the #331 / #330 drift gates have a stable surface to assert against. -
k6 perf scripts !202 (smoke / load / stress / soak) — fixed the wired-but-never-written
cargo xtask perfharness; load profile is what produced the data closing #318/#319. -
Criterion benches !203 (typst) and !204 (tanf summary) — same role for #321 and #320.
-
k6 load auth + URL fixes !205 — made the load profile actually exercise the deferred-on-profiling endpoints.
Phase E — Bigger needs-spec (write plan, then build)
Done:
-
#424 canopy-identity service-class token migration (MRs !225-!232, !238-!239 across the ADR-019 cutover sequence; 2026-05-08 → 2026-05-09).
-
#439 ADR-019 hard cutover — drop transitional gates, require_service_caller-only on internal endpoints (MR !238).
-
#422 Provider-agnostic OIDC (discovery + neutral naming).
-
#411 canopy-web BFF service-to-service auth.
-
#427 RFC 8785 canonical-JSON envelope, #428 scheduler leader election, #429 canopy-rules per-endpoint RBAC, #430 OTLP-direct observability migration (E0.4 / E0.5 / E0.6 follow-ups; 2026-05-07).
-
#432 SNAP
income_typefield alignment, #426 SNAP params jurisdiction-toml path correction, #425 dev-key fallback hardening (regression triage 2026-05-07). -
#386 medicaid-jdm-completion — 3 new JDM rulesets (CMD cascade priority + TMA phase + denial reasons) replacing the last Rust-side decision logic in canopy-medicaid.
Phase E.2 — ADR-003 cross-service hygiene + cross-program overpayments (Done 2026-05-10)
-
Epic !49 (closed 2026-05-10) — multi-agent ADR-003 audit produced 5 child issues; all shipped:
-
#382 cross-program overpayment recovery (MR !245) — new
crates/canopy-overpaymentsshared crate, 5 endpoints × 3 program services (SNAP / TANF / Medicaid), canopy-reporting roll-up CSV, integration test for SNAP IHE full lifecycle. -
#443 canopy-notices config-driven event-to-notice routing (MR !240) — moved 7-arm match block from Rust to
notices/manifest.toml. -
#444 keycloak realm port-drift detection has enforcement authority (MR !241) —
ReconcileResult::keycloak_realm_drifts+ auto-heal incmd/e2e.rs+ bail incmd/validate.rs/cmd/test.rs. -
#440 canopy-snap IEVS source names from config (MR !242) —
[snap.verification]+VerificationClient::new(.., wage_match_source, unemployment_match_source). -
#441 canopy-tanf federal time-limit from config (MR !243) —
tanf_time_limits.federal_limit_monthsDEFAULT dropped; value now snapshotted fromparams.federal_time_limit_months()at INSERT. -
#442 canopy-appeals ADH + decision-clock from config (MR !244) —
adh_notice_advance_dayswired intoipv/api.rs::send_notice; newapproaching_deadline_warning_daysin[appeals]threaded intoclock.rs::run_daily_check.
-
-
#385 canopy-cli ADR-007 parity catchup (MR !237).
-
#401 canopy-applications authorized-rep CRUD (MR !236).
-
#339
cargo xtask validate-in-networkopt-in runner (MR !235).
Residual:
-
#265 Published API reference from OpenAPI specs (
priority::medium) — partially addressed by !249 (hand-written per-service ref pages for tanf / medicaid / caps / wic); OpenAPI-generated half (Redoc / Rapidoc in Antora) still open. -
#267 Applicant-facing help content + accessibility guide (
priority::low) -
#308 Standalone EC2 deployment pipeline (
priority::medium) -
reserve_ports()TcpListenerhold-through-compose-up hardening — out-of-scope of #444 per its CHANGELOG; file separately if reservation race reproduces.
Closed since snapshot:
-
canopy-applications batch #399 / #400 / #402 — closed by !248 (commit afa543f, 2026-05-10):
programs_requestedvalidation → HTTP 422,PUT /v1/applications/{id}acceptsprograms_requestedwith transactional sync,GET /v1/applicationsgainssubmitted_by/program/status/from/tofilters. -
#382 follow-on — !247 (commit 23ed004, 2026-05-10) wires
appeal.overpayment_assesseddirectly into each program service’s overpayment-claim store, replacing the canopy-enrollment log-only handler; canopy-appeals event payload extended to carryperson_id/program/determination_id/amount_cents.
Phase E.3 — Tier B Wave execution + cleanup (Done 2026-05-11 through 2026-05-14)
12-plan Tier B refresh (MRs !252-!254 lint-pass) executed across five waves and a tail of small bundles. All plans now archived; supporting follow-ups closed inline.
Wave 1 — Eligibility-rules pair (2026-05-10 → -11):
Wave 2 — Medicaid SSA flow (2026-05-11):
-
#384 deliverable (a) —
NoopSolqAdapterin canopy-verification, orchestrator pre-dispatch SOLQ for elderly/disabled Medicaid,ssa_solqon dispatch payload,derive_abd_flags_from_solqin canopy-medicaid (!260). Deliverable (b) staysBlocked (CMA execution).
Wave 3 — Enrollment (2026-05-11):
-
#407
partial_retention+retained_throughonsnap_enrollments, typedTerminateEnrollmentRequest,closure.rshelper, PAMMS 2415 cutoff (!261). Follow-up #447 tracks canopy-appeals overpayment-math consumption. -
#408
household_assignmentstable + 4 CRUD endpoints + service-class outbound client + inline RBAC gate + audit events (!263). -
Chore !262 — idempotency
check_cacheErr-path logging.
Wave 4 — Worker portal (2026-05-11 → -12):
#448 upstream endpoint gaps for #392 — FULLY CLOSED (2026-05-12):
-
Batch 1 (!266): canopy-renewals path-parameterization (8/8 interim-contact + change-report via
/v1/renewals/{program}/…), canopy-caps PUT mutations, canopy-web WIC nutritional-risk BFF path fix. Migration addsprogramcolumn + nullablecertification_idtosnap_change_reports. -
Batch 2 (!267): canopy-tanf
tanf_discrepancies+POST /v1/verification/discrepancies/{id}/resolve, canopy-medicaidmedicaid_cmd_events+POST /v1/cmd/ingest+POST /v1/determinations/{id}/requeue, canopy-wicwic_appointments+POST /v1/wic/certifications/{id}/appointments.
Wave 5 — Standalone (2026-05-12):
-
#396
caps_providerstable + FK refactor + CRUD endpoints (!269). -
#449 Playwright specs for the 20 #392 handlers (!270).
Other Tier B closures alongside the Waves (2026-05-11):
Cross-program views + small bundles (2026-05-13):
-
#393 #394 worker-portal cross-program landing pages (
feat/worker-portal-cross-program). -
#395 #397 #398 canopy-seed CAPS/WIC fixture richness (
feat/seed-caps-wic-multi-fixtures). -
#418
cargo xtask compliance capabilitiesADR-005 drift gate (feat/xtask-compliance-capabilities). -
#383 #423 fti_auditor role + hash-derived FTI lock ids (
feat/fti-auditor-role-and-advisory-naming). -
#417 TMA-subscriber DLQ routing (
feat/tma-dlx-routing). -
#412 consolidated
[shared.timing]section in jurisdiction.toml (feat/shared-timing-section). -
#431 canopy-snap SUA tier selection (
fix/canopy-snap-sua-tier-selection; PAMMS 3617). -
#406 #415 E3 small bundle (
feat/e3-bundle-415-406); #391 closed as already-done. -
#413 #419 #421 ATO evidence + data models + screenshots docs trio (
docs/docs-trio-413-419-421). -
End-of-session docs sweep (
chore/docs-sweep-end-of-session).
Tier A follow-up bundle (2026-05-14):
-
#451 canopy-eligibility infer
utility_tierfrom expense profile. -
#452 canopy-mq per-queue DLQ-depth metrics for Prometheus.
-
#453 canopy-web per-program pending-action counts on dashboard cards.
Test-seed harness refactor (2026-05-14):
-
#450 three-layer fix: single source-of-truth seed call, deterministic replay via
test-results/seed/last.txt, stable fixture surface attests/e2e/lib/fixtures.ts. Always-reseed + TRUNCATE-CASCADE before INSERT.
Tier 2 sweep + operational hardening (2026-05-14 second pass):
-
MR !282 (
docs/end-of-session-sweep-2026-05-14) — Antora nav gains ADR-018 + ADR-019; this Phase E.3 section authored;services.mdgains 3 missing endpoint rows + WIC appointments table; #455 filed for missing canopy-tanf section. -
MR !283 (
feat/tier-2-bundle-445-447) — Closes #447 (canopy-appeals consumespartial_retentionin continued-benefits overpayment math;SnapBenefitIssuance.retainedfield + LEFT JOIN inlist_issuances_for_household+ filter incompute_overpayment+ 3 new unit tests) and #445 (manifestform_numberaudit comment block: 0 CONFIRMED / 8 UNVERIFIED / 7 SYNTHESIZED; values not changed since they flow to rendered PDFs). -
MR !284 (
chore/precommit-subagent-verification) —.githooks/pre-commitgains an AI-agent verification block printed before Q1-Q8: non-trivial commits must answer from a fresh Explore subagent’s findings against the staged diff, not from primary memory. Conditioned on "If you are an AI agent that made these changes" — no human-committer impact. -
MR !285 (
docs/data-models-fill-13-services) — Closes #454. Fills in the 13 service data-model ERDs to canopy-caps reference depth (+2,591 LOC across canopy-applications, canopy-snap, canopy-tanf, canopy-medicaid, canopy-enrollment, canopy-renewals, canopy-appeals, canopy-notices, canopy-persons, canopy-reporting, canopy-rules, canopy-security, canopy-wic). Authored by 3 parallel agents against migration SQL; verifier subagent confirmed 13/13 PASS. First commit to exercise the new precommit-hook rule.
Phase E.4 — Operational follow-ups + outbox-drainer correctness (Done 2026-05-15 through 2026-05-18)
Series of ops-correctness fixes triggered by validate flakes that traced to structural antipatterns in the canopy-mq layer, plus the canopy-test-lib port’s final close-out.
canopy-test-lib port closure (2026-05-15 → -16):
-
MR !322 (
feat/phase-e-flake-fixes) — Phase E ephemeral-schema backfill across 3 direct-pool integration tests (capability_flag_test.rs,orchestrator_dispatch_test.rs,fti_audit_hash_chain_test.rs); Phase D primitive hardening (EphemeralSchema::cleanup(self).awaitsync DROP,sweep_orphans(base_url)static helper, schema-name suffix switched UUID v7 → v4 for collision robustness); xtask.ports.envauto-reconcile in bothauto_refreshandensure_readycold-start so every validate/test/e2e invocation gets a coherent ports file before any test reads it;IdempotencyCache::with_poolCREATE INDEX race fixed viapg_advisory_xact_lock(IDEMPOTENCY_DDL_LOCK_ID=9999)(since each service runs its ownsqlx::migrate!, layering canopy-api’s migrator on top breaks_sqlx_migrationsvalidation; advisory-lock + raw DDL avoids the conflict). -
MR !323 (
fix/canopy-rules-coalesce-audit-outbox-tx) — canopy-rulesrecord_audit+publish_evaluation_completedcoalesced into a single tx around the eval call. Integration suite 12.4s → 8.2s (-34%) under workspace nextest. Wired through Publisher::publish_tx-shaped pattern as a pilot for the broader publish_tx migration on the chore branch. -
MR !324 (
chore/436-step-10-closeout) — closes #436 (canopy-test-lib port). Plan archived toplans/archive/; Testing gains durable sections for every primitive the port introduced (EphemeralSchema + cleanup/sweep contract, EvilLayer + SpanCapture + time-mocking + goldenfile + insta + per-service typed clients). Net: contributors no longer need to read the archived plan to know which test pattern to reach for. Also includes #476 SOPS-empty-stdout fallback.
outbox-drainer lease refactor (2026-05-17 → -18):
-
MR !325 (
docs/outbox-drainer-lease-plan) — diagnostic plan +.diagnostics/pg_wait_event_poller.shfor the held-tx-across-broker-roundtrip antipattern discovered when validate kept failing on 10s timeouts in canopy-tanf/canopy-snap post_determine tests.pg_stat_activitypolling at 100ms captured drainer sessions sittingidle in transactionon per-rowUPDATE event_outbox SET published_at = now()while domain sessions waited on COMMIT — multi-second WAL writer serialization. NOT hardware / NVMe / Keycloak / pool starvation (all four chased and ruled out); the drainer’sdrain_onceopened one Postgres tx around N RabbitMQ publishes per batch. -
MR !326 (
fix/outbox-drainer-lease) — closes #478. Three-phase lease-based drainer: Phase 1 claims rows via a CTE (FOR UPDATE SKIP LOCKEDinside the CTE; outer UPDATE stamps two newclaimed_at/claimed_bycolumns), Phase 2 publishes outside any DB tx with channel-per-batch publisher confirms (confirm_selectonce, pipeline up toCANOPY_MQ_DRAINER_PIPELINE_DEPTHdeep,mandatorydeliberately not set), Phase 3 marks results in two short bulk UPDATEs both guarded byclaimed_by = $drainer_id.attemptsincrements only on per-message publish failure — crashed drainers' reclaimed batches don’t inflate the counter. Lease recovery via the next claim cycle, not the janitor. 19 byte-identical forward-only migrations adding the lease columns + a partial index. 4 in-sourcelease_tests(lease reclaim withattemptsunchanged, happy-path bulk confirm, deserialise-failure path, two-drainer no-double-publish). 3 new env vars (CANOPY_MQ_DRAINER_BATCH_SIZE/LEASE_TTL_SECS/PIPELINE_DEPTH) withDrainerConfig::from_envasserting boot-time invariants. ADR-018 amended. Plan archived per ADR-013.
publish_tx + sqlx::migrate centralization (2026-05-18):
-
MR !327 (
chore/centralize-sqlx-migrate-bootstrap) — closes 473 + #477. (a)canopy_api::bootstraptakes asqlx::migrate::Migratorparameter; all 19 services passsqlx::migrate!("./migrations")and drop the explicit.run(&*boot.db)prologue. Closes the gap that produced #471. (b) AllPublisher::publishcallsites migrated topublish_tx— 13 services, 55 sites — collapsing two separately-fsync’d COMMITs per request (domain write + autocommit outbox INSERT) into one atomic tx. Closes the silent atomicity gap where an audit row could land without its event when the autocommit hiccupped. IPVimpose_disqualificationcollapsed 5 fsync’d COMMITs to 1. (c)dispatch_is_parallel_not_sequentialtest refactored from wall-clock budget (1100ms) to SpanCapture-based assertion observingorchestrator.dispatchstart/complete events; insensitive to mock delay, DB latency, JWS verify cost; doubles as a permanent production observability signal. 5[allow(clippy::too_many_arguments)] replaced with named-field structs along the way;.githooks/pre-commitusesgit rev-parse --git-dirfor worktree compatibility. -
Issue #479 (
workflow::needs-spec) — per-commit performance baseline storage filed as follow-up. Architecture:.perf/baselines.jsonl(append-only, one row per main commit),cargo xtask perf check/chart/record(Rust-native HTTP harness for prepush gate + SVG chart generator), Antora trend page rendering from the JSONL. k6 in CI for fine-grained detection on bounded hardware; criterion microbenches for pure-CPU code paths. Discussion concluded k6 in prepush is structurally wrong for "subtle change" detection due to dev-workstation noise floor + time-budget conflict — telemetry belongs in CI / dashboards, not gates.
Phase E.5 — Chaos epic closure + #460 worker portal redesign epic & planning (Done 2026-05-19 through 2026-05-21)
Two large architectural deliverables back-to-back: closing the chaos observability epic that had been blocking deterministic assertions in 3 of 4 chaos tests, then launching the worker portal redesign as a 21-child epic with ratified architecture.
Chaos epic &50 closure (2026-05-19):
-
MR !333 (#480) — cross-process chaos observability harness.
canopy_test_lib::chaosmodule + 2 helper functions (spawn_jwks_provider_for_chaos,spawn_outbox_drainer_for_chaos) that spawn productionJwksProvider/OutboxDrainerin the test process pointed atEvilLayer-wrapped endpoints. Unblocks 3 chaos contracts (#481/#482 + multi-replica work) previously blocked becauseSpanCapture::install_scopedis thread-local and could not observe events fired inside devstack containers. -
MR !334 (#481) — JWKS chaos contract. 5
target: "jwks"emit sites incanopy-auth/src/jwks.rs;refresh()refactored from?-shortcut tomatchso both success and failure paths fire structured events. -
MR !335 (#482) — outbox chaos contract. 4
target: "outbox"emit sites incanopy-mq/src/outbox_drainer.rs. AMQP-transparent EvilLayer for transient-failure injection deferred (evil_proxyis JSON-only). -
MR !336 (#483) — durable chaos-observability docs + runbook.
docs/modules/ROOT/pages/runbooks/chaos-observability-contract.adocwith step-by-step adding-new-contract process; ADR-020 documents the strategy decision (in-process production fixtures over OTEL export vs Docker log scraping); Testing + Shared Crates cross-linked. Closes epic &50.
#484 Phase 1 — CHANGELOG render hygiene (2026-05-19):
-
MR !337 — render-bug hygiene sweep in
CHANGELOG.adoc(escaped#NNNissue refs to avoid AsciiDoc#text#mark collisions; passthrough...around code-like strings with underscores/asterisks; list-continuation replaces indented-Markdown sub-bullets). 19<mark>artifacts + 3 broken-code-em + 5 broken-code-strong + 7 literalblock divs all eliminated. Issue stays open; Phases 2-4 (terse-bullet entry reflow +{issue-base}NNNattribute links + ADR ratifying the convention) pending.
Epic &51 — worker portal redesign (2026-05-19 through 2026-05-21):
-
MR !338 (commit ecfab75) — epic &51 filed at
gadhs/application/eligibility+ 20 child issues #485-#504 filed viaglab issue createand linked to epic viaepic_id. Plan atdocs/modules/ROOT/pages/plans/worker-portal-redesign.adoc(initially landed pre-epic-filing; updated with epic + child issue table + ADR numbering coordination note). Originated from a May 2026 design exploration; all load-bearing design context now lives in this plan + ratified ADRs. 21 children total weight 73; 7 stages. -
MR !340 (commit 5716d45) — design decisions from design’s review locked durably. ADR-021 Stage 1 primitive list extracted as-is (the 8 primitives all read from
--orchard-*variables, palette via !295 didn’t change their internals); ADR-021 Stage 5 case-detail shell strategy is jurisdiction-selectable per role via[shell.<role>]TOML (NOT progressive enhancement at viewport breakpoints). Georgia defaults tostrategy = "tabs";scrollandcard_gridship as opt-in. NEW child issue #505 Stage 1.5 (panel-state primitives upgrade) filed — 4 utility classes upgrade to first-class Askama-macro primitives (EmptyState/Skeleton/SkeletonRow/ErrorBlock). Epic carries 21 children / weight 73. -
MR !341 (commit 990250e) — E2E subscribe-after-fire race fix (closes #506).
tests/e2e/specs/workflow.spec.ts:32-40previously usedawait page.click; await page.waitForResponseanti-pattern; htmx kicks off the XHR synchronously so the response could land in-browser BEFOREwaitForResponseattached its listener. Three contextless subagent investigations converged on the root cause (Subagent 3); Subagent 1 ruled out appeals-internal code-path race; Subagent 2 corrected my "validate-sweep contention" framing (Playwright and nextest do NOT run concurrently during prepush). Real fix:Promise.all([waitForResponse, click])applied across 4 anti-pattern surfaces (workflow.spec, lib/helpers.ts::clickTab, 5 instances in case-search.spec, lib/pages/case-search.page.ts::search). Reverted misleading 30s → 60s timeout bump from afdb2f7. The 10s → 30s (2026-05-14) → 60s (2026-05-19) journey was a series of band-aids on a real race that the user caught: "just because something passes intermittently doesn’t mean that there isn’t a race condition somewhere. This is dedicated hardware." -
MR !342 (commit 0a1d743) — ADR-021 ratifies composability runtime + plugin model. Three decisions locked: (1) Sandboxing posture — Option A′ in-process trusted Askama partials behind a
PluginSourcetrait; v1 ships onlyCompileTimePluginSource; v2 federation (WasmPluginSourceetc) is additive. (2) Plugin discovery —#[canopy_plugin]proc-macro +linkmedistributed slice with compile-timePlugin.toml↔ Rust handler signature validation. (3) Composition reload — invalidate-on-write single-replica v1; Studio writes invalidate the in-process cache; multi-replica RabbitMQ-fanout deferred post-UAT. Plugin.toml schema specified; composition loader contractload_composition(jurisdiction, role, user_id, surface) → Result<ComposedSurface, CompositionLoadError>; role filter applies AFTER override merge (silently drops items the role can’t use). Three open design questions routed via #486 comment. -
MR !343 (commit 3865b88) — ADR-022 ratifies composition override storage layering. Four decisions locked: (1) Schema — unified
composition_documentstable for all three DB-backed layers, polymorphicscope_key(user UUID / role slug /'jurisdiction'sentinel) with app-layer validation. (2) Merge — RFC 6902 JSON Patch op lists; Studio "add one panel" maps to one{"op":"add","path":"/items/-",…}op;testops support optimistic concurrency. (3) Lifecycle — explicit Studio Archive after promote-merge (no canopy-core-repo watcher in v1). (4) Audit retention — uniform 1-year for all override-layer events, every write emits a JWS-signedAuditEventper ADR-014. Forward-only schema per ADR-016. -
MR !344 (commit f77bf9b) — Stage-2 ADR-023 deferred; #507 filed for unified canopy config backend ADR. User reframe during ADR-023 drafting recognized composition isn’t a special config concern — canopy has 7 config domains today (service config / secrets / jurisdiction policy / rulesets / theme / IDP / composition), each handling backend concerns (filesystem vs git vs HTTP, reload, versioning) independently. Composition is the 7th instance of an under-generalized pattern. The right shape is a shared
ConfigBackendabstraction (plumbing) with domain-specific overlays (precedence, audit, mutation) staying per-domain. Filed as #507. Composition v1 ships using existing filesystemrulesets/{juris}/pattern; Studio promote affordance descoped from v1 (admin uses jurisdiction’s existing baseline workflow external to canopy). #488 + #492 closed-deferred. #500 weight 5 → 3. Epic Stage 2 closes at 2 of 3 ratified; epic carries 19 active children / weight 64.
Net epic state as of 2026-05-21: Stage 1 (#485 design system extraction) is workflow::ready and unblocked. Stage 1.5 (#505) non-blocking follow-up. Stage 2 closed at 2 of 3 ADRs ratified + 1 deferred to #507. Stage 3 has 3 active issues unblocked by ADR ratification (#489/#490/#491). Stages 4-7 await Stage 3.
Phase E.6 — Epic &51 Stage 1.5 / 3 / 4 / 5 deliveries + Epic &52 ADR ratification (Done 2026-05-21 through 2026-05-23)
Eight MRs over three days closing the foundational + dashboard-surface deliverables of the worker portal redesign epic. Stage 6/7 + Stage 5 MR4 case detail (#497) remain.
Stage 1.5 (#505) — panel-state primitives (2026-05-21, !350): 4 macros (empty_state / skeleton / skeleton_row / error_block) wrap existing utility classes. 19 of 20 consumers migrated. Server-side branching on Err(_) from canopy-persons; no htmx-response-targets dependency. 36 unit + 4-test panel-states.spec.ts.
Stage 3 MR1 (489 + #490, 2026-05-21, !351): NEW crates canopy-composition (5-layer composition resolver: system defaults via LazyLock<Value> → jurisdiction baseline TOML via RFC 7396 → DB-backed jurisdiction_live → role → user via RFC 6902) + canopy-plugin-macros ([canopy_plugin] minimal proc-macro). Forward-only migration creating composition_documents + composition_documents_archive. Georgia shell-only baselines + roles-only idp.toml. CompileTimePluginSource empty in MR1 (real plugins land Stage 5). 63 unit/integration tests. Drive-by fix: closed #511 (deny-modal x-transition race — 4-line CSS transition addition; root cause traced via 5 contextless subagents converging on pre-Stage-1 strict-CSP refactor b4ee102 that externalized inline styles to utility classes but didn’t preserve transition timing).
Stage 3 MR2 (#491, 2026-05-22, !352): 11 HTTP live-override APIs on canopy-web. RFC 7232/6585-clean PUT preconditions (If-Match / If-None-Match: * / 428 when missing / 400 when both); PATCH validates application/json-patch+json Content-Type before body deserialization (415 not 400). Per-row strict ETag monotonicity via SQL GREATEST(clock_timestamp(), updated_at + interval '1 microsecond'). Atomic mutation audit via publisher.publish_tx (outbox row commits with composition row, so the JWS hash chain integrity per ADR-014 extends across composition mutations without special-casing). Surgical cache invalidation: new invalidate_user(juris, user_id) for /user/me writes. JSON-aware session extractors share HTML BFF refresh semantics via resolve_worker_or_fail helper. canopy-web restructured as lib+bin. OpenAPI JSON-only (Swagger UI descoped in v1 due to strict CSP). 53 new tests.
Stage 4 MR1 (#493, 2026-05-22, !353, force-merged via glab API after cancelling pipeline): N-OIDC genericization aligned with CRAIG. rulesets/{juris}/idp.toml gains table-array + [local_accounts] table. v1 ProviderType narrowed to keycloak | oidc-generic; full multi-shape claims deferred to #515. NEW canopy-auth constructors: from_discovery_with_client (shared reqwest::Client) + from_split_discovery(external, internal, http) (iss from external, jwks_uri from internal — split-DNS correctness). NEW canopy-web IdpRuntime with per-IdP Arc<JwksProvider>; 3-rung startup ladder (multi-IdP / synthetic-single-IdP / empty). OidcConfig DELETED. /auth/callback validates via per-IdP JwksProvider + copies idp_slug into SessionData before OAuth-flow cleanup so /logout (4-branch tree) + slow-path refresh (Extension<Arc<IdpRuntime>>) find the right entry. Parse-time validation (slug regex/uniqueness/reserved, audience required, domain @ prefix). 7 follow-ups filed: #512 (SAML federation spike), #513 (local accounts password auth), #514 (introspection mode), #515 (multi-shape claims), #516 (multi-jurisdiction sign-in), #517 (cargo xtask identity verify), #518 (env-var retirement).
Stage 4 MR2 (#494, 2026-05-22, !354 / 786ea96): Sign-in template + chip-list + email-first IdP discovery (htmx hx-get="/v1/auth/discover" hx-trigger="input changed delay:300ms"). 3 inline-SVG IdP icon macros. New .idp-chip CSS (~60 lines, data-attribute selectors binding to Orchard tokens — matches existing .status-pill[data-kind] precedent; no inline styles). 4 handlers (sign_in_page / discover / select / local_login_stub) with pre-resolved ChipView view model (Askama 0.15 doesn’t allow method calls in templates — see reference_askama_015_canopy). Zero-IdP renders o::empty_state BEFORE .card wrapper to avoid double-nesting. is_safe_return_to rejects open-redirects. 142/142 canopy-web tests + 4 new unit tests.
Stage 5 MR1 (495, 2026-05-22, !355 / 7ae3f7f): Composition-driven 12-panel worker dashboard — first canopy-web surface consuming the Stage 3 composition runtime. 12 panel plugins registered via [canopy_plugin] + linkme; each panel is its own #[derive(Template)] struct rendered to a String in Rust and embedded in templates/dashboard/worker.html via {{ panel.html|safe }} (sidesteps askama {% include %} parent-context limitation). Georgia baseline at rulesets/georgia/composition/worker_dashboard.toml declares all 12 with row/span per ADR-021 breakpoint set; system defaults stay empty (jurisdiction baselines overlay them via RFC 7396). New services/canopy-web/src/dashboard/ module (panels/ with 12 + unknown_panel, role_map, util). 6 panels wire existing endpoints; 6 ship state = "empty" placeholders pending FU-1..FU-6 (#519-#524). Drive-by Stage-4 MR1 fix: IdpRuntime::build Rung 1 now applies CANOPY_WEB__OIDC_* env vars as in-memory overrides for the matching idp.toml entry (Stage 4 MR1 had hardcoded host.docker.internal:8180 in idp.toml which broke OIDC discovery on every devstack reload). 164/164 canopy-web + 95/95 canopy-composition tests pass. FUs #519-#530 filed.
Stage 5 MR2 (496, 2026-05-22, !356 / fbc5e29, force-merged): Supervisor + analyst dashboard surfaces via new surface_for_role(&WorkerRole) free fn + DashboardTemplate enum + 3 sibling [derive(Template)] structs sharing one _panel_grid.html Askama macro (FU-16). Sidebar + topbar nav adapt to worker role via new worker_role_slug: String context field plumbed through 11 base.html-extending templates. Worker role display strings are human-readable (FU-19); topbar gains Notices + Appeals to reach sidebar parity (FU-20). Renamed WorkerRole::QualityControl → ::Analyst across enum + Keycloak realm + idp.toml + Plugin.toml + tests (FU-15). 8 new panel plugins (5 supervisor + 3 analyst); 2 Georgia baselines; 3 dashboard templates. Real upstream endpoints: canopy-tanf /v1/tanf/sanctions/rollup (FU-9), canopy-reporting /v1/reporting/overpayments/summary (FU-10), real fetchers for team_queue (FU-7) + pending_hearings + analyst_case_search. 9 of 14 FUs landed in-MR; 5 deferred with scope-corrected comments. Composition audit envelope carries 2 new surface values. 185+ tests. New Playwright projects (supervisor, analyst).
Stage 5 MR2.1 (2026-05-23, !357 / a24856f) — drive-by visual fix + 4 design primitives: .worker-dashboard-grid 12-col CSS grid honoring [data-span="1..12"] (without it, all 3 dashboards collapsed to a single-column vertical stack regardless of declared spans; mobile <1024px collapses to single col). Supervisor Overpayment roll-up 403 fixed: canopy-reporting::get_overpayments_summary swapped require_supervisor_or_above → require_service_or_caseworker_or_above (matches canopy-tanf::get_sanctions_rollup); the service-class token canopy-web fans out with now passes. 4 new design-system primitive Askama macros (delta / editorial_flag / program_stripe / program_tag) matching canopy-design package’s primitives.jsx contract. Companion --sp-1..--sp-10 + --r-sm..--r-3xl CSS tokens.
Epic &52 / ADR-023 (2026-05-23, !358 / b8bd27a): ADR-023 OIDC validation at service boundaries + citizen-upload isolation (amends ADR-019). Preemptive security amendment routed to a new epic before Stage 5 MR3 (the ADR-019 service-class-token model assumed program services are reachable only by trusted internal callers; that assumption breaks once canopy-portal ships citizen document uploads per ADR-008). Decision: push OIDC validation to every program service via the shared canopy-auth Axum middleware (discovery-based config, normalized claims, IdP adapter pattern preserving Keycloak/Authentik/Kanidm/Zitadel portability); RFC 8693 token exchange for user-context requests with per-(request, audience) caching; narrowly-scoped credentials for citizen-upload processing; service-class credential narrowed to background/scheduled work only; auth.token_exchange audit events extending the ADR-014 hash chain. Migration sequenced over 3-6 months calendar (FTI-touching services first). Stub remediation plan at docs/modules/ROOT/pages/plans/oidc-at-services-and-citizen-upload-isolation.adoc with 8 steps + architect-input flags + off-ramps. No code in this MR — ADR + plan only. Placeholder issue #546. 5 architect-input flags await user decision.
Stage 5 MR3 (#498, 2026-05-23, !359 / 8e53848, force-merged): Customize My Dashboard. New GET /dashboard/customize page + sidebar / topbar nav link. New DELETE /v1/composition/{surface}/user/me (idempotent reset). New ADR-024 ratifying the user_delta_v1 semantic envelope (amends ADR-022): {"type":"user_delta_v1","hidden_slugs":[…],"span_overrides":{slug:span},"slug_order":[…]} body on user-layer composition rows for dashboard surfaces only (worker_dashboard / supervisor_dashboard / analyst_dashboard); case_detail and other surfaces continue using RFC 6902. Loader shape-detects per (layer, surface) at the new post-role-filter apply step. HTML5 native DnD + keyboard pickup (Space + arrows). 12 Playwright specs × 2 color schemes (28/28 incl. critical + serious axe AA). Bonus deliverables from the mid-implementation design-mock review (the user redirected me to Playwright-screenshot the in-flight page vs the design package mock + flag any deviations on already-completed work): theme system expanded from 13 → 23 tokens per design-system reference §3.1 — semantic -bg / -text triples + text-body + border-soft + nav-bg / -muted / -muted-dim; auto-theme data-theme="system" half-state bug fixed structurally (chrome migrated from [data-theme="dark"]-scoped overrides to first-class palette tokens — fixes the OS-dark + auto-mode mismatch); pre-existing dark-mode nav-chrome WCAG AA contrast failure fixed (was #a8d5c8 on #3a9080 = 2.37:1 → now #7faa9a on #0e1612 = 7.5:1); orphan cy-btn--* classes (zero CSS rules) purged in favor of established btn btn-primary / btn btn-ghost. 10 follow-ups filed before commit: #547 (pinning), #548 (required-panel lockout), #549 (touch DnD), #550 (CSRF + session ID rotation on auth), #551 (role-change row cleanup), #552 (global csrf meta + htmx listener), #553 (Fluent panel titles), #554 (live cell previews), #555 (Preview-as-worker button), #556 (locked-item tooltip). Plan: docs/modules/ROOT/pages/plans/archive/worker-portal-redesign-stage5-customize-my-dashboard.adoc.
Stage 5 MR3 follow-up — humanize panel slugs + case numbers (2026-05-24, !361 / 732c340, force-merged): Mid-implementation Playwright screenshot review (user redirect: 'just look at your screenshots, you’ll see where it populates') surfaced that the customize page cells, dashboard panels (my_queue / recent_applications / recent_determinations / recent_notices), the notices list, and 4 other surfaces were leaking raw compose-time slugs (worker-dashboard-my-queue-panel) and UUID-prefix truncations (HH-018cc251 repeated for every row of 10 visible cases on the dashboard) as visible text. Two new helpers in canopy-web::dashboard::util: humanize_panel_slug (strips surface/-panel affixes + Title-Cases + keeps known acronyms IEVS / SNAP / TANF / etc. uppercase) and presentational_case_number (takes the last 8 hex chars of a UUID — random bytes for both v4 and v7 — instead of the first 8 which is the timestamp prefix that collides across UUID v7 inserts in the same millisecond bucket). 12 sites migrated. 9 new unit tests. All 28 customize Playwright specs × 2 color schemes still green. 4 new follow-ups filed before commit + linked to Epic &51: #557 (UI identifier-leak sweep — medium-severity items: snake_case notice types, FU-N placeholder copy, at-a-glance / my-queue stat mismatch, audit-events panel showing framework telemetry), #558 (ADR-007 parity: move helpers to canopy-common + add case_number field to backend response structs so CLI sees the same value), #559 (architectural spike: canopy-workers / canopy-identity service consideration — user later proposed cookies + canopy-security audit API as the cleaner long-term path), #560 (responsive design: sidebar dominates + tables overflow below ~900px viewport, surfaced via Playwright capture at 1920 / 1440 / 1024 / 768 / 375).
Net epic state as of 2026-05-24: Epic &51 is 14/35 children closed (14 new follow-up issues filed across MR3 + the humanize/case-number sweep; all linked to the epic). Remaining open (21): Stage 5 MR4 #497 (case detail with 3 shell strategies + 13 section types; weight 8), Stage 6 #499 / #500 / #501, Stage 7 #502 / #503 / #504, plus 14 follow-ups #547-#560 (mix of feature, refactor, spike, ux, security; all priority::low except #550 priority::high security + #557 / #558 / #559 / #560 priority::medium). Stage 6 Plugin Studio (#501) needs design questions resolved first (routed via #486 comment). All composition runtime (Stage 3) + auth (Stage 4) + 3 dashboard surfaces (Stage 5 MR1 / MR2 / MR3) are on main. Epic &52 ADR-023 awaiting 5 architect-input flags.
Phase E.7 — Demo dataset sequence (Done 2026-05-26 through ongoing)
Five MRs over one day standing up a hand-curated demo dataset for the 2026-05-28 stakeholder demo. The random-seed default emits 71 orphan cross-service refs out of 300 sampled rows; the demo profile reaches 0 orphans across 1,863 rows.
Plan (2026-05-26, !375 / 3020ef7): docs/modules/ROOT/pages/plans/demo-dataset-seed.adoc ratifies 24 archetypes × ~400 households × 13 SQL files with cross-service-ref ledger + drift gate + MR slicing (a/b/c/d).
MR-a (2026-05-26, !376 / db421ff): Plumbing. cargo xtask demo verify cross-service ref auditor (10 ref pairs, exits non-zero on orphans). cargo xtask seed --profile {default,demo} + --reset flags. DATABASES const expanded with canopy_verification + canopy_tanf + canopy_medicaid (previously silently skipped). Baseline measurement captured at docs/modules/ROOT/pages/plans/demo-dataset-baselines/random-seed-baseline-2026-05-26.txt (71 orphans / 300 rows).
MR-d 9c-A (2026-05-26, !377 / 18a6965): Cases-search status badges. New GET /v1/eligibility/case-status?household_id={hid} returns latest program_determinations row. canopy-web’s cases-search replaces hardcoded status: "Active" with parallel per-result fetches + 5-state color badges via pure render_status_badge (6 unit tests). Required for archetypes 4-7 (Denied / Sanctioned / Terminated / ABAWD-exceeded) to show real lifecycle.
MR-d 9c-B (2026-05-26, !378 / 08d9d6f): Auto-issuance subscriber. publish_determination_completed now carries monthly_allotment + effective_date via new DeterminationCompleted struct payload. canopy-enrollment’s determination.completed.snap subscriber reads both, uses real monthly_allotment (was Decimal::ZERO), and auto-creates the first snap_benefit_issuances row atomically in the same inbox transaction — closes Workflow 1 from the demo audit ("approve doesn’t issue benefits"). Pre-9c-B this required a separate /v1/enrollments/{id}/issue_benefits POST no demo-flow caller made.
MR-b (2026-05-26): Hand-curated 401-household dataset. New tools/canopy-seed/src/demo/ (personas + names + 8-phase orchestrator + sql_extras writers) + tools/canopy-seed/src/bin/demo.rs emit 13 deterministic SQL files (RNG seed 0xDE40_DA7A_5EED) into devstack/demo-dataset/. 24 archetypes (Marcus Williams, Sarah Johnson, …) × cohorts summing to 401 instances. New cargo xtask demo regenerate shells the bin; new cargo xtask demo check-drift diffs the regenerated tree (CI gate). Verifier: 10/10 cross-service ref checks pass, 0 orphans across 1,863 rows. Program-service-local rows (snap_applications / tanf_* / medicaid_* / caps_authorizations / wic_participants) deferred to follow-up #579 — not in the 10 verifier checks, worker portal cases-search queries through canopy-eligibility’s combined view.
MR-d 9a / 9d / 9e / 9b (2026-05-26): the four remaining demo-dataset-seed Step 9 surfaces shipped — Run Determination button on case-detail (9a, household-scoped; later superseded by Plan 1 MR4a’s per-application/per-program handler), Pending Hearings server-side filter (GET /v1/appeals/hearings/upcoming, 9d), the Action ▾ dropdown wired to five real handlers (9e), and the chain-through ?notice=eligibility-changed banner (9b). Per-MR detail in CHANGELOG.adoc.
MR-e — demo deferred-cleanup + polish (2026-05-26, feat/demo-deferred-cleanup): ~30 worker-portal demo bugs fixed (clickable case numbers, ?focus_section= deep-links, Process-button state-gating, friendly not-applicable placeholders, styled error pages) plus demo income rows (phase1b, ~333 earned_wages rows across the 24 archetypes). Closes #531 / #581 / #582 / #583; filed #584 / #585 as post-demo follow-ups.
Demo polish rounds 5–8 (2026-05-27) — five user walk-through passes hardening the recorded surfaces:
-
Round 5 (
fix/demo-polish-audit-pdf-chips): SNAP on-demand PDF render 404 gap (3 templates missing ~23inputs.*keys), WIC appointment-type humanizer, Audit Events domain-first display with telemetry toggle, Recent Applications outcome status, program-chip underline fix. #581 stays open for the S3-persist backfill. -
Round 6 (
fix/process-app-hero-banner-and-determination-amount): Process Application page — duplicated Run Determination button (htmx swap target), $0.00 income/benefit bugs (frequency-normalized member income +parse_decimal_amountfor string-encodedDecimal), verification-panel layout + 5 canonical SNAP verification items (7 CFR 273.2(f)(1)), eligibility-verdict hero banner. -
Round 7 (
fix/case-detail-activity-tab-telemetry-filter): Activity tab telemetry leak — domain rows shown,composition.render/rules.evaluatedgated behind a client-side toggle via the new sharedcrate::auditmodule. #598 (household filter) + #599 (self-as-"you") filed. -
Round 8 (
fix/list-humanization-and-demo-notice-subjects): humanize raw enum slugs across every list page via the new sharedcrate::formatmodule; demo notice subjects regenerated from{program}: {slug}to readable headlines.
Cross-program-alerts auth fix (#590, fix/cross-program-alerts-worker-id-auth): GET /v1/eligibility/cross-program-alerts now enforces worker_id == token sub for caseworker / eligibility_specialist / quality_control (was a decorative param exposing the jurisdiction-wide list); supervisor / admin / service callers pass through. Result-set scoping by household_assignments landed with #596 (2026-08-19): the endpoint restructured into the assignment-scoped /v1/eligibility/workers/{worker_id}/cross-program-alerts + the supervisor-only /all view.
cargo xtask e2e --profile {default,demo} (#592, feat/xtask-e2e-profile-flag): the e2e runner no longer silently wipes a loaded demo dataset; --profile demo loads devstack/demo-dataset/.sql with auto---reset. *(Retired in #716 MR4e — the demo dataset folded into the default seed’s cast; the --profile flag and the committed devstack/demo-dataset/ tree were removed.)
Caseworker wishlist (filed 2026-05-27, #600–#608): stakeholder-sourced backlog captured as discrete issues — per-program case numbers, notice scoping, client-data isolation, Studio editing, shared-content single-source, trading-partner framework, batch elimination, work-tracking revamp, audit-log-level reporting. Queued for post-demo prioritization; not on the September UAT critical path.
Infra debt (filed 2026-05-27): #609 (xtask refresh SHA-gap) + #610 (pre-push validate flake-state — the seed-profile brittleness of cargo xtask validate). The sender-side signing-key-deletion facet once bundled with #610 (a cached service token outliving its deleted IdP signing key → determine-500) is a distinct root cause, split into epic &70 / ADR-037; #610 remains the seed-profile item.
Phase E.8 — SNAP+TANF+ELE demo-video three-plan split (Started 2026-05-27)
After the 2026-05-28 stakeholder demo landed, the team committed to a recorded 10-minute end-to-end video showing SNAP and TANF intake worked separately by different workers, then ELE auto-grant. The combined plan was rejected by external reviewer with 20+ findings (factual errors, internal contradictions, repo-drift). Re-scoped 2026-05-27 into three independently-shippable plans.
Three-plan split, all three now committed to Antora:
-
Plan 1 — Worker intake + program independence — COMPLETE, archived 2026-05-28. 8 MRs, ~5,650 LOC: !390 (SectionName + SectionPayload + DocumentId contracts) → !391 (
application_sectionstable + axe contrast fix) → !393 (Claims.primary_programs+ Keycloak mapper + snap/tanf worker fixtures + plural ListParams + MyQueue rewire) → !394 (per-program Run Determination) → !395 (intake page UI, 9 SNAP / 10 TANF sections) → !396 (audit_events.household_id) → !397 (Audit section +ChainVerificationResponsecontract) → !398 (multi-program intake demo seed + the load-bearingaxum_extra::extract::Queryfix for the MR3-era latent regression whereserde_urlencoded400’d on repeatedVec<T>keys, silently emptying MyQueue for per-program workers). Plan 1 alone yields the recordable SNAP-worker/TANF-worker independence cut (~4.5 min). Plan committed MR !387 (2026-05-27) after 7 reviewer-pass iterations + 2 external user-side passes (26 findings); implemented !390–!398 (2026-05-28). Plan 3 unblocker: MR1 (!390) shipped the SectionName + SectionPayload + DocumentId contracts first. -
Plan 2 — ELE 1-year-flag expansion — COMPLETE, archived 2026-05-29. 7 MRs (#642–#648): #642 (ELE wire contracts + typed IDs + hash-chain primitive) → #643 (
ele_consents/ele_status/ele_grant_eventstables + store layer + advisory-locked hash-chain INSERT) → #644 (POST /v1/applications/{id}/ele-consent+Claimsguard + consent subscriber — the Plan 3 dependency) → #645 (three federal JDM rulesetsele-grant-2026/ele-renewal-2026/ele-lapse-2026, ADR-003) → #646 (grant subscriber over service-token persons HTTP + deletion of the pre-Plan-2 hardcodedexpress_lane.rstier ladder — the decision now runs entirely through JDM) → #647 (source-closure lapse folded into thecanopy-medicaid.tmatanf.case_closedhandler — one consumer group per event per service, theevent_inboxevent-id-only dedup constraint — plus adminPOST /v1/ele/{person_id}/revoke) → #648 (advisory-locked daily renewal scheduler +GET /v1/ele/household/{id}+ case-detail identity-hero ELE badge + adminPOST /v1/ele/renewals/run). Durable 1-year flag (42 CFR 435.1102), hash-chain integrity per ADR-014. Demo seed carriesele_consentspreconditions only — the durable flag is derived live by the grant subscriber, never fabricated into seed SQL. Follow-ups: #649 (consent-after-approval re-eval), #650 (provisional-value signaling), #651 (snap.case_closedpublisher), #652 (persons.income_changedpublisher), #654 (coherent-scenario seed generator — unblocks the full approve→grant→badge Playwright walk). v6 plan: 7 reviewer rounds (5 internal + 2 user-external; 26 issues caught by user that internal subagents missed). -
Plan 3 — Applicant intake + verification (Dioxus 0.7+). 11 MRs (MR1 split into 1a/1b/1c), ~10,600 LOC. canopy-portal Dioxus 0.7+ rewrite + reference-number applicant auth (
HH-[a-f0-9]{8}+word-word-word-NNpasscode — NEVER DOB per user 2026-05-27) + document upload + verification round-trip + scripted IEVS/SAVE adapters + lost-credential recovery with 24h pending + side-channel notification + kill-switch (applicant-portal design ref §3.4-3.8 intimate-threat protection). 6 reviewer rounds (4 internal + 2 user-external; 11 blockers/P1s caught by user that internal subagents missed). v6 READY-TO-IMPLEMENT 2026-05-27. Depends on Plan 2 MR3 (POST /v1/applications/{id}/ele-consentendpoint).
Strict CSP regime locked per user 2026-05-27: wasm-unsafe-eval is the only -unsafe- directive allowed across the applicant portal. ADR-008 §214 (which permits 'unsafe-inline' for styles) is amended in Plan 3 MR1a. Tracking issue #630.
Predecessor: archived combined plan (superseded; not implementable as-written).
Meta-plan handoff: durable memory file at ~/.claude/projects/-home-bitskrieg-code-canopy/memory/project_demo_video_3plan_handoff.md carries the 20+ original findings + locked decisions + verification command blocks for each child plan.
Phase E.9 — Epic &52 OIDC-at-services program activation (2026-08-11)
The 2026-08-10 maintainer ruling on #546 (note 3666918785) activated the full
ADR-023 program and resolved all five architect-input flags: full program now,
foundations first; the citizen-path mechanism swapped from RFC 8693 exchange to a
dedicated narrow IdP service account (ADR-026 opaque sessions mean no citizen
token exists — recorded in amending ADR-043 riding the first implementing MR);
sequential FTI-first rollout; per-request exchanged-token cache; Keycloak-only
v1; X-Canopy-Actor retained until migration completes; mTLS post-migration
stretch.
Plan rewrite + decomposition (2026-08-11): the <TBD> stub at
the program plan
replaced with the ratified, receiver-first program spec (typed EffectiveUser
resolution over the fleet’s four incompatible no-actor readings; exact-audience
authorized-azp receiver contract; TokenExchanger broker with runtime output
validation + audit-before-use; portal credential narrowing as a rotation
sequence + ownership binding absorbing #665; conformance harness before the
first flip). 34 child issues filed under epic &52 (#1418–#1451: 5 foundations,
2 realm/audit, 15 per-service receiver slices FTI-first, 3 portal, 4
cutover/closure, 5 honest-scope deferrals at T5), full blocks/is_blocked_by DAG
wired, adjacent backlog swept (#665 re-pointed to #1442; #1008, #985, #874,
#731, #1356, #512, #514, #515, #518 related). #546 closes with the plan MR as a
decomposed placeholder. Docs-only MR — no product code, no realm changes;
implementation proceeds through the epic’s DAG starting at #1418.
Phase E.10 — Epic &52 OIDC-at-services program COMPLETE (2026-08-17 through 2026-08-24)
The full ADR-023/ADR-043 program shipped in 13 days: foundations (F1a inventory
467 branches read-verified; F1b EffectiveUser; F2 policy primitives; F3
TokenExchanger broker with output validation + audit-commit-before-release; F4
conformance harness), realm wiring + A1 chain audit (R1 — hop-2 chained exchange
proven live on KC 26.5), fifteen receiver slices FTI-first (every service on the
ADR-043 §C ReceiverContract; user-only routes mechanically enforced), the
portal isolation trio (P1 narrow per-target credential; P2 receiver-side citizen
class + 12-scope portal:* vocabulary; P3 origin-verified X-Canopy-Applicant
ownership binding — closed the #665 IDOR debt), and the C1 cutover (#1443:
X-Canopy-Actor retired unconditionally — middleware 401s the header,
ViaActor deleted, legacy service-token write arms drained fail-closed, the
document review trio rides exchanged bearers). N1 consolidated the deployable
realm contract in idp-integration (stand-up checklist
+ the honest no-exchange off-ramp); S6 closed the conformance matrix fleet-wide
(no-slice proofs for rules/exchange, mixed-version kind retired, floor 1046).
T1 (#1446) archived the program plan
and closed the epic. End state: worker identity ONLY in tokens (direct or
RFC 8693 exchanged); citizen identity ONLY in the narrow portal credential
signed ownership claims; service identity ONLY for system traffic. Honest-scope
deferrals live on as standalone issues: FU-A #1447 (per-service audience for
service-class tokens; relates #1571 admin-replay hardening), FU-B #1448
(nested-hop exchange + attribution), FU-C #1449 (citizen-content process
isolation, needs-spec), FU-D #1450 (RFC 7009 revocation guidance), non-KC #1451
(non-Keycloak deployment notes).
Phase F — Plan Deferred-row trackers (post-UAT or pre-1.0; don’t pick early)
Externally bounded by UAT entry, 1.0 release, or post-UAT scope. Don’t graduate without their gating event.
-
#349 Workflow guidance panel UI — post-UAT, depends on canopy-portal Dioxus work.
-
#350 Compose-profile-matrix integration tests + CI — post-graduate; lift only when capability-flag tests miss something.
-
#351 Event-bus compile-time macro + CI lint — post-UAT defense-in-depth.
-
#352 OpenAPI contract testing — post-1.0 by design (pre-1.0 contracts are expected to break).
Post-1.0 — Deferred to after first stable release
| Item | Reason deferred |
|---|---|
OpenAPI contract breaking-change detection CI gate (6 steps) |
Premature — breaking changes are expected pre-1.0; gate blocks iteration |
ADR-011 migration: move cross-program constants from |
Pattern established (SnapParameterTable, MedicaidParameterTable); retrofit when stable |
Go / No-Go Checkpoints
Week 4 (end of month 1):
-
canopy-persons CRUD endpoints working (POST /v1/persons, GET /v1/persons/{id}) — MR !6
-
canopy-rules evaluating stub SNAP ruleset (POST /v1/evaluate returns output) — MR !8
-
Determination signing compiles and passes key generation + sign/verify tests — commit eda958f
-
canopy-security captures and persists events from canopy-persons — MR !9
-
canopy-applications accepts SNAP applications (POST /v1/applications → 201) — MR !10
Checkpoint PASSED (March 2026).
Week 10 (end of month 2):
-
End-to-end determination: application → eligibility → snap → rules → signed determination
-
Gross income test working (130% FPL deny/approve)
-
Net income test with all 6 mandatory deductions
-
Asset test working ($2,750 limit)
-
Categorical eligibility: TANF cash receipt → auto-approved
-
ABAWD: month 3 exhausted →
AbawdExceededstatus
Status: COMPLETE (March 2026).
Week 26 (UAT entry):
All month 6 exit criteria met (see above). FNS-7176 QC extract validated. Worker portal caseworker walkthrough complete without errors.