Plan: Backlog Cleanup Campaign — clear the genuine loose ends

On this page
NOTE

A campaign of independent MRs, not one feature. Each MR Closes its own issue(s) (the "only the final MR closes" rule is for many-MRs-per-one-issue and does not apply here). No .claude/CLAUDE.md change — these standalone issues are not tracked there; this plan is the tracker. Each batch table below is a living Status table (cargo xtask plan-lint scans the Status column; canonical tokens only). Rev. 1 was rejected by external review for proposing issues already owned or deferred by active plans; rev. 2 cross-checked every issue against docs/modules/ROOT/pages/plans/.

Context

Epic &56 / Track 2 (worker fact-authoring) is closed. The user asked to clear the loose ends out of the 263-issue standalone backlog before the next large track. A 7-agent triage classified all 263, a strict-verifier pass closed the genuinely-done ones, cluster agents mapped the dependency/collision graph, and an external review caught that ~12 proposed issues are already owned or deferred by active plans (the rev. 1 reviewers never cross-checked the plans directory). Rev. 2 removes those, defers the items needing real design, drops one non-issue, and ships the rest as the batches below.

Already executed

  • 7 stale issues closed with evidence: #349, #403, #465, #467, #517, #593, #714.

  • #916 closed (Phase A) — its "loosen the wall-clock bound" ask is moot now the flaky slow_program_does_not_block_combined_result runs on a multi-thread runtime (services/canopy-eligibility/tests/orchestrator_dispatch_test.rs:518). The residual design debt stays under #572/#573; the semantic-invariant rewrite is C3/#692.

Disposition of the external-review findings

Finding Action

#856 deferred by medicaid-resource-medical-aggregation.adoc (false-deny safety)

Removed — stays deferred in that plan

#378/#379 owned by the acf-196/cms-64 plans (Not started, full scope)

Removed — dedicated plans own them

#575/#576/#580 are demo-dataset-seed follow-ups; #595 absorbed by generative-seed-harness MR4; #730 Deferred in portal-fidelity-followups; #862 in input-requirements-coverage burndown

Removed — homed in active plans

#902 adds person_id to AlienEligibilityInput; the snapshot schema_version window is strict [4,4] → bumping it strands sealed v4 snapshots

Deferred — needs a v4→v5 re-seal / version-tolerance design

#404 GA-seal SVG lives only in gitignored .policy-cache/; not committed anywhere

Deferred — needs a vendoring + PAMMS-IP decision

#870/#871 (member PUT endpoint + secure SSN capture), #879 (deliberate T2-1 Decision-A deferral)

Deferred — own focused design, not casual quick-fixes

#899 — ApplicationContext.members already typed; SNAP/TANF members are intentionally Vec<Value> (ADR-002 black-box)

Dropped — close won’t-fix-by-design

#906 partially done (graph types already derive ToSchema)

Rescoped (D4) — remaining = DeterminationSnapshot + SnapshotFacts + leaf types

#464 cargo-machete was CI-only → violates the local-battery convention

Fixed (B3) — also wire cargo machete into cargo xtask validate

E1 too broad; E2 resolved_fact_id chain under-specified; B6 oversized

Decomposed / spec’d / split below

Removed (homed in active plans): #856, #378, #379, #575, #576, #580, #595, #730, #862.
Deferred (filed, need design/decision): #902, #404, #870, #871, #879.
Dropped: #899.

Phase overview

Phase Theme MRs Risk

A

Zero-code: close #916, archive the completed worker-fact-authoring master, epic-group

0

none

B

Tooling & doc hygiene

7

low

C

Test coverage & flakes

4

low

D

DRY / snapshot / type-safety

6

med (D9 last, destructive)

E

CLI/API parity + IEVS chain + event-glue

5

med

F

Portal / UI

3

low

~24 code MRs; each a ~10–30 min pre-push battery (+ a ~15–18 min cargo-doc tail) ⇒ a multi-session campaign; checkpoint between phases.

Campaign MR conventions (every row)

  • Branch <type>/cleanup-<slug> where <type> = the row’s Type; commit/MR title <type>: <imperative> (#N…) (<72 chars) + the Co-Authored-By: trailer naming the session model.

  • MR body = Summary / Changes / Test Plan; Closes #N per issue the MR fully resolves.

  • Per-MR spec = its issue(s) + the row’s notes; rows marked THIN carry the missing spec inline. The first step of any MR confirms the issue’s acceptance criteria are complete; enrich if not.

  • Delivery: issue → branch → docs-on-branch → full pre-push battery → commit → push → MR → close with a comment (impl SHA + merge SHA + criteria). Status cell In progress at branch, Done (YYYY-MM-DD) — !MR at merge (update after each merge).

  • Quality budgets: a B3a (serde_json::Value) site removed/added must offset, never raise the lock.

  • New bug mid-MR → file a fix: issue + /relate; never expand the batch.

Phase A — Zero-code (GitLab + plan archive; do first)

  1. Close #916 — done (see above).

  2. Archive worker-fact-authoring-and-provenance.adoc — all rows now Done (epic &56 closed); its filed orphaned follow-ups #905/#906 are adopted below (D6/D4). #904 / FDSH-TMA / #879 stay deferred.

  3. Reclassify (leave open): #908 → defer; confirm #921/#927/#936 stay SME-blocked.

  4. Note (no work): #856/#378/#379/#575/#576/#580/#595/#730/#862 stay with their owning plans; #902/#404/#870/#871/#879 stay open as deferred-needs-design; #899 closed won’t-fix-by-design.

  5. Epic-group the defer + large-track + blocked issues into streams (standard epic body): journeys #849–#854 → &61; action-coverage gaps #774–#805 → &60; #858 → &63; deferred portal/Studio FUs → &51/&53; T2-8 follow-ons #928–#934 → a new fti-overpayment-recompute epic. Decision: the orphaned TANF #807–#824 / WIC #825–#837 / CAPS #838–#848 (eligibility epics &24/&29/&30 CLOSED) → create three new *-program-coverage-gaps epics (recommended) vs reopen vs leave flat.

Phase B — Tooling & doc hygiene (7 MRs)

MR Type Issues Surface & notes Status

B1

fix

#901, #918

DRY-collapse xtask/src/cmd/plan_lint.rs onto cmd::docs::run_plan_lint() (already excludes archive/ + checks Deferred-needs-tracker); keep the --strict .claude/plans warning; port the token tests into docs.rs.

Done (2026-06-27) — #901/#918; cmd::plan_lint now delegates to the canonical cmd::docs engine + a collector archive-exclusion test.

B2

chore

#653

Resource-pressure observability in xtask (SysMonitor / /proc sampling).

Done (2026-06-27) — #653; xtask::sysmon 1 Hz /proc sampler wraps the nextest/doctest/Playwright phases (JSONL stream + per-phase summary), parsers proptest-covered.

B3a

chore

#464

Wire cargo machete into xtask/src/cmd/validate.rs (blocking gate after fmt/before clippy + a hard preflight --version probe, scoped crates services tools xtask) + a CI cargo-machete parity job; triage + clear all 53 machete findings (52 removed, 1 ignored: canopy-web linkme macro-FP). Split from B3 (mutants → B3b). Also bundles a .githooks sync to clear check-docs drift (cargo-doc target-dir isolation, template #35 / #939; pre-commit J5, #37).

Done (2026-06-28) — !709, #464; cargo machete gate wired into validate (after fmt, before clippy) + CI parity job; 52 unused deps removed, 1 ignored (canopy-web linkme macro-FP); bundled the cargo-doc target-dir .githooks sync (#939).

B3b

chore

#466

cargo xtask mutants wrapper (cargo-mutants) + mutants-baseline.toml + --smoke shard + CI decision (scheduled/manual only, never per-MR). Heavy: a multi-hour baseline run populates the accepted-mutants list; reimplement from cargo-mutants' public docs (not the sibling template). Own MR.

Not started

B4

chore

#657

Extend the ADR-011 audit-literals gate to crates/canopy-contracts-* (xtask/src/cmd/policy.rs).

Done (2026-06-28) — !706, #657; audit-literals now also walks crates/canopy-contracts-/src (reuses the services walk; hermetic canopy-contracts- selection test; 0 violations, 537 files). ATO-evidence + frequency-normalization docs synced.

B5

docs

#767, #668, #455

TANF overpayment CFR citation fix; Plan-3 doc-sweep (roadmap applicant-portal refresh). #455 deviation: the canopy-tanf endpoint table is already satisfied by the Antora migration — api/canopy-tanf.adoc is a complete reference + services.adoc is a thin index pointing to it — so #455 is closed as resolved-by-migration (the recon’s "fat table in `services.adoc`" would have regressed the thin-index convention). (#917 broken-intra-doc-links landed early with the template-v2026.8 sync.)

Done (2026-06-28) — #767/#668; 45 CFR 264.1042 USC 609(a)(1); 45 CFR 263.11 at 10 overpayment sites (the 4 retention/processing-deadline 264.10 mis-cites are out-of-scope → filed as #937 + /related to #767); Phase-6 roadmap NOTE + i18n row refreshed. #455 closed as resolved-by-migration.

B6

chore

#463

Decomposed into its own plan + epic (the "50–200 sites" estimate described the already-done panic class). Measured: 3,240 missing_docs + 128 unreachable_pub across 24 in-scope library crates (service crates excluded by documented policy; unused_crate_dependencies already covered by the B3a machete gate). Executed as 14 per-crate-batch MRs under epic &68 (#940–#953) — see Library-API Docs Burn-down.

Done (2026-06-29) — epic &68 complete (#940–#953; #463 closed)

B7

docs

#484

Escape the AsciiDoc #…# / & pairs in CHANGELOG.adoc rendering as stray <mark>; verify zero with asciidoctor.

Done (2026-06-29) — 484; canonical AsciiDoc sweep (passthrough #N for issue refs / […] attrs + \ident`` for emphasis-bearing code spans, `*`→ constrained bold). asciidoctor: 0 <mark> / 0 <code><em> / 0 <code><strong> / 0 nested-strong / 0 literalblock / 0 warnings; content byte-identical after stripping formatting chars.

Phase C — Test coverage & flakes (4 MRs)

MR Type Issues Surface & notes Status

C1

test

#866

Extend the no-PII raw-key guard tests to asset.claimed / expense.claimed.

Done (2026-06-29) — #866; asset_claimed_*/expense_claimed_* raw-key guard tests in events.rs mirror the income guard (exact top-level + nested value key set; banned ssn/name/dob; PII value/amount/description sealed ct, no plaintext leak).

C2

test

#923, #925

Mock-HTTP: dry-run degradation branches; renewals materiality subscriber.

Done (2026-06-29) — #923 (!727) + #925 (!728). Reused the existing in-process axum mock harness (canopy_test_lib::mock::spawn_router) — no wiremock/new dep (the issue premise was stale). #923: dry_run_degradation_test.rs forges canopy-snap to assert the 4×422 + 5xx→500 branches (DB-free). #925: in-module subscriber.rs glue tests (renewals is bin-only — no lib target, matching 17/19 services) forge canopy-eligibility + canopy-persons to assert material→nudge+event, immaterial→nudge-no-event, no-cert/retroactive no-ops, idempotency, 4xx→skip / 5xx→retry, person→household resolution; asserted inside the inbox tx + rolled back (no devstack pollution).

C3

test

#584, #529, #528, #692

render_on_demand; IdP issuer override; dashboard panels; #692 — replace the wall-clock bound in slow_program_does_not_block_combined_result with a semantic outcome invariant.

Done (2026-06-29) — #692 (!729) semantic-invariant rewrite; #584 (!731) render_on_demand PDF-fallback tests; #529 + #528 extract apply_devstack_issuer_override (5 tests) + worker-dashboard 3-state render matrix (all 12 panels).

C4

test

#542, #893

Consolidate axe-core into one e2e suite; fix the caps/wic case-detail fixture tie-break + sub-resource guard.

Done (2026-06-30) — #893 (!733) deterministic CAPS authorization-tab selection (typed CapsDeterminationRead, (effective_date, id) tie-break) + sub-resource-requiring caps/wic fixtures; #542 axe-core consolidated into a data-driven accessibility.spec.ts registry + shared lib/axe.ts runner (stateless surfaces in the suite; flow-coupled audits routed through the runner).

Phase D — DRY / snapshot / type-safety (6 MRs)

D9 is last (destructive). The rest are independent.

MR Type Issues Surface & notes Status

D2

refactor

#900

case_detail.rs: type the canopy-persons /full HouseholdFull bundle across the 4 tab deserializers; preserve per-member degrade-resilience. B3a offset.

Done (2026-06-30) — !737; typed HouseholdFullView/MemberView across the 4 case-detail tabs via a lenient_vec custom deserializer preserving (strengthening to per-element) the per-member degrade-and-warn; the warn names the element type, never its PII value. B3a 753→747, B5 306→301; 5 example tests + a proptest. (A latent D3 #885 snap.json description drift surfaced on first push + was fixed separately as #957/!736.)

D3

refactor

#878, #885

Delete the orphaned Ecdsa*Signer/Verifier (canopy-eligibility/src/determination.rs, zero callers); re-export SnapshotStatus from contracts-eligibility, drop the snap-local copy.

Done (2026-06-30) — #878 deleted the whole orphaned determination.rs (legacy Determination + Ecdsa*Signer/Verifier, zero production callers; live path is canopy_signing::SignableDetermination); #885 collapsed SNAP’s byte-identical SnapshotStatus onto the shared canopy_contracts_eligibility::snapshot::SnapshotStatus (re-export; models::SnapshotStatus path + present/no_input_snapshot wire + OpenAPI unchanged).

D4

feat

#880, #906

#906 (graph types already derive ToSchema): add ToSchema to DeterminationSnapshot + SnapshotFacts + the leaf types, flip the snap snapshot-read body off Object, regen OpenAPI. #880: add the required policy_params_version (ADR-028 §39) sourced from jurisdiction.toml [meta].version via a shared canopy_common::settings::load_jurisdiction_policy_version, threaded through all 5 programs (snap via determine() arg, the other four off their *ParameterTable); bump schema_version 4→5 (window [5,5], drop v4, no back-compat pre-1.0; rename the below-floor error variant PlaintextSchemaVersionRejectedSchemaVersionBelowFloor). ADR-028 Amendment 5.

Done (2026-06-30) — !740; required policy_params_version stamped on every program’s snapshot via the shared canopy_common loader + [meta].version (+ citations.toml); schema_version 4→5 [5,5]; full ToSchema sweep + snap /snapshot typed body (snap.json 940). B2 offset by extracting `assemble_asset_leaves` (no lock raise). ADR-028 Amendment 5; #880#906 closed. Discovered RUSTSEC-2026-0190 (anyhow) → filed #959.

D6

refactor

#905

Adopt the orphaned worker-fact-authoring follow-up: hoist subject_firing_edges + rust_fn_edge into contracts-eligibility/src/derivation.rs; import in medicaid/caps/wic.

Done (2026-06-30) — !738; hoisted subject_firing_edges + rust_fn_edge into canopy_contracts_eligibility::derivation (pub), adopted in medicaid/caps/wic (re-export + private import), deleted the per-service copies + the 3 redundant generic tests (consolidated into the shared derivation_test.rs). Dropped the now-unused canopy-contracts-rules dep from caps/wic. Budgets neutral; net −75 LOC. (A pre-existing e2e nav-spec flake surfaced under host I/O contention during the battery — 5-agent diagnosis confirmed environmental, not D6; filed the #578-residual harness fragility as #958.)

D7

refactor

#924

Extract crates/canopy-persons-client/; consume in applications/renewals/snap; delete the copies. Scope correction (recon): the issue’s "notices" client targets canopy-applications (not persons) — excluded, single consumer, no premature crate; snap carried a 4th persons-client copy the issue missed — included. Crate returns Result<_, ApiError> (mirrors canopy-rules-client).

Done (2026-06-30) — !739; extracted crates/canopy-persons-client (PersonsClient, Result<_, ApiError> mirroring canopy-rules-client, 5 mock-HTTP tests), consumed in applications/renewals/snap, deleted the 4 per-service copies. The issue’s "notices" client was actually canopy-applications' (single consumer → excluded, no premature crate); snap’s 4th copy (issue-missed) included.

D9

chore

#883

LAST — destructive. Remove the live tanf_household_snapshots INSERT; forward-only DROP TABLE migrations for tanf_household_snapshots + magi_household_snapshots. Zero-readers verified immediately before. Scope (recon): larger than "THIN" — also rips out the Tanf/MagiHouseholdSnapshot store models, the vestigial seed-model fields, the demo SQL generators, and the xtask seed --reset lists.

Done (2026-06-30) — !741; zero readers re-confirmed (no SELECT anywhere); removed the fire-and-forget tanf INSERT + both *HouseholdSnapshot models + seed/demo/xtask refs; forward-only DROP TABLE IF EXISTS x2 (no FK targets); data-model docs + CHANGELOG Removed. (Done-flip + MR number rode this E1a branch, per the rolling convention.)

Phase E — CLI/API parity + IEVS chain + event-glue (6 MRs)

MR Type Issues Surface & notes Status

E1a

refactor

#892

Canonical address_type enum (contracts-persons/src/addresses.rs) + primary-address selection determinism; migration + store read.

Done (2026-06-30) — !742; AddressType wire enum (strum snake_case + ToSchema) across all 4 address contract shapes; native PG enum (CREATE TYPE, migration 20260630120000 converts in place) + sqlx::Type store mirror bridged by exhaustive From; shared primary_address helper (first residential else first) used by both canopy-web case-detail picks, now fetching typed Vec<Address> (drops 2 serde_json::Value; +canopy-contracts-persons dep); FOIA CSV via Display; removed dead seed ADDRESS_TYPES + normalized demo residenceresidential. OpenAPI persons.json regen. Validate caught 4 raw-SQL non-canonical literals ('home'/'residence') the Rust sweep missed; fixed. #892 closed.

E1b

feat

#869

canopy person update CLI verb; the PUT /v1/persons/{id} endpoint already exists.

Done (2026-06-30) — !743; added the Update variant to PersonAction + the dispatch arm + cmd::person::update (mirrors create; optional --first-name/--last-name/--dob, ≥1 required; sends via the existing client put, omitted fields ride as null = unchanged). SSN deliberately omitted (argv/shell-history leak; matches create). Integration test pins "null field left unchanged". ADR-007 already lists the verb. Filed follow-up #960 (full editable-field CLI coverage of create/update). #869 closed.

E1c

feat

#897

canopy address claim / claim-delete CLI; the POST /v1/persons/{id}/addresses/claims endpoint already exists.

Done (2026-06-30) — !744; new address CLI group (cmd/address.rs + AddressAction + dispatch) mirroring income: claim (POST addresses/claims; --address-type/--line-1/…/--source/--author-sub/--valid-from, authors as Worker) + claim-delete (DELETE …/claims/{fact_id}?as_of). address_type rides as a string the endpoint parses to the AddressType enum. The 14-flag claim args are grouped into a [derive(clap::Args)] AddressClaimCli held boxed on the variant (Claim(Box<…>)) — the idiomatic fix for clippy::large_enum_variant, no [allow] (clap impls Args/FromArgMatches for Box<T>). Added the cli = "canopy address claim" binding to the record-address-change action catalogue (closes the policy action-coverage gap). 2 integration tests (claim append + idempotent close). #897 closed.

E2a

feat

#876, #877

Backend + audit for the IEVS resolve→fact link. (#876) resolved_fact_id UUID column on snap ievs_discrepancies (forward-only nullable migration; no cross-service FK per ADR-001; no index — no query path filters by it, the retry-idempotency check is by discrepancy PK), threaded through IevsDiscrepancyRow / IevsDiscrepancy / ResolveDiscrepancyRequest + store::verification::resolve_discrepancy + the snap resolve handler. The resolved_fact_id is the persons fact_id the worker authored (ADR-025 cross-service handle), not a snap-minted id: the canopy-web accept handler captures ClaimResponse.fact_id from the /claims write it already performs and sends it as resolved_fact_id — a race-free discrepancy↔fact link + belt-and-suspenders idempotency over the persons-OK/snap-fail retry window. (#877) ievs.discrepancy_resolved payload gains resolved_fact_id; canopy-security named parse arm → (resolve, ievs_discrepancy) + discrepancy_id added to the resource_id candidate list so the audit row indexes by the discrepancy, not the person (resolved_by_sub already rides author.sub). OpenAPI snap.json regen.

Done (2026-06-30) — !745; #876 + #877 closed.

E2b

feat

#872, #567

Web/BFF surface. (#872 bug) SNAP-only after verification: actions::resolve_discrepancy .post().put() on the PUT-only canopy-snap /v1/verification/discrepancies/{id}/resolve route (a POST was a 405 swallowed by the Err(Html)=HTTP-200 quirk) + thread resolved_by_sub from AuthenticatedWorker.worker_id. The issue’s "likely the TANF sibling" guess was wrong — canopy-tanf mounts its resolve route as POST (so the BFF .post() is correct) and its ResolveDiscrepancyRequest has no attribution field at all; TANF resolve-attribution is a separate canopy-tanf feature, filed as #961 (/relate #872). (#567) 4 SNAP caseworker action chips added to actions_for("snap") + 4 inline form blocks in templates/cases/det_action_form.html (interim-contact→notices, change-report→household, abawd-activity→abawd, resolve-discrepancy→income) matching the sibling-program inline pattern — the issue’s separate _action_form*.html prescription was stale (no such files exist; all programs use inline {% if %} blocks). The handlers + routes + target_section allowlist already existed. #872 e2e resolves a REAL seeded discrepancy asserting a 3xx redirect (maxRedirects:0) — the old actions.spec smoke test’s <500 passed with the bug.

Done (2026-06-30) — !746; #872 + #567 closed. Filed #961 (TANF resolve-attribution) + #962 (e2e seed defect that skips IEVS-discrepancy browser tests), both /relate #872.

E3a

feat

#651, #652

ELE real-time lapse wiring (both durability-biased → federal outcome is keep, so v1 = projection-correction + forward-compat, no benefit lost). (#651) canopy-snap publishes snap.case_closed on a denied determination (mirrors tanf.case_closed); medicaid’s handle_ele_case_closed parametrized on closed_program (drops the hardcoded Program::Tanf) + new canopy-medicaid.ele-case-closed-snap consumer group removing Program::Snap from granting_program_history. (#652) canopy-persons publishes a new minimal persons.income_changed (IDs only, ADR-004 — deliberately not the PII-bearing income.claimed) on income claim/close, resolving household_id via the existing household_member_versions lateral join; new canopy-medicaid.ele-income-changed group routing trigger_event="income_change"evaluate_ele_lapse.

Done (2026-07-01) — !747; #651 + #652 closed. Hoisted member_person_ids to canopy_common::household (SNAP+TANF) + broadened the scheduler apply_lapse to &Publisher (reused by the income-change subscriber); named canopy-security audit arms for the case-closure family + income signal; quality budgets ratcheted down (B3b 191→188).

E3b

bug

#649

ELE late-consent grant replay (event-ordering race): the express-lane subscriber acks-and-drops an application_approved when consent has not yet landed, permanently losing the ELE grant. New ele_deferred_approvals table (medicaid DB); express-lane persists a deferred row instead of dropping; the ele-consent subscriber drains + replays via a shared extracted grant fn, idempotent on (household_id, source_program) while unprocessed.

Done (2026-07-01) — !748; #649 closed. New ele_deferred_approvals table; the ~145-line inline grant body extracted into evaluate_and_grant_ele/household_income_pct/grant_one_member/resolve_grant_window/persist_member_grant (shared by the live approval + the replay). Race e2e green on the live devstack; B3b ratcheted 188→187.

Phase F — Portal / UI (F1 recon-split into F1a functional + F1b design-fidelity; F2 split into F2a seed (superseded by #716) + F2b devstack-guard; after D so case_detail.rs lands first)

MR Type Issues Surface & notes Status

F1a

fix

#591, #599, #525, #527

Worker-portal functional fixes (recon-split from F1). (#591) Run Determination hx-target=#panel-active fails silently on the scroll/card-grid shells → drop hx-target, respond HX-Redirect to ?focus_section=determination (works on every shell; preserves the recomputed banner via ?notice=). (#599) thread session_worker_id into render_activity_tab (both the legacy dispatcher + the composition sections/activity.rs) so the viewer’s own audit events render you. (#525) per-panel htmx refresh button + "loading" in the 22 manifests (the fragment endpoint + skeleton + retry wiring already exist). (#527) per-call timeout override on InternalClient (cheap .with_timeout() clone → RequestBuilder::timeout) fed by each panel’s manifest data.timeout_ms (default 5s), no per-fetcher churn.

Done — !749

F1b

fix

#689

Worker-portal design-fidelity (recon-split from F1): frameless KPI-strip tiles on the at_a_glance panel — the hero panel drops the panel_frame card entirely (it is the one dashboard panel without standard chrome; a frameless param on panel_frame would be an oxymoronic "frameless frame") so its tiles read as standalone raised cards (a scoped .kpi-tile-row—​standalone modifier; shared .kpi-tile/panel_frame untouched), first tile gold-accented via a decorative border/ring (never gold text — WCAG AA). Plus a file-wide token-fallback sweep in canopy-web.css: 91 var(--sp-/--r-, LIT) fallbacks where LIT was one rung off the real :root value — dead code (:root unconditionally defines them), zero runtime effect, but the source lied; corrected to match :root. Live --orchard-*/--font-mono fallbacks verified + left untouched. No pixel-diff gate exists (screenshots.spec.ts is a capture-only doc tool, not a regression assertion).

Done — !750

F2a

fix

#713

Demo-persona application UUID-tail disambiguation. Superseded — folded into #716 (converge seed profiles / eliminate the demo profile): #716 deletes the committed devstack/demo-dataset/*.sql this fix would edit, and the collision is non-observable today anyway because the demo personas fail to load (#965). The distinct-APP-<last8> requirement + exact UUID mapping are captured on #716; #713 and #965 closed as superseded.

N/A — superseded/folded into #716

F2b

fix

#732

dev-refresh stale-WASM guard. The portal Dockerfile’s RUN --mount=type=cache,target=/app/target persists dx’s incremental compile cache, which can bake stale WASM into a fresh-id image (the anti-latch guard stale_app_containers only compares image ids). Fix: remove that mount (keep the content-addressed cargo registry/git download mounts) so every portal build cold-compiles a fresh bundle — correctness over warm-rebuild speed — plus a regression-guard test.

In progress

Cross-cutting execution rules

  • OpenAPI drift (D4, E1a, E2): regenerate then format-gate:

    cargo xtask dev refresh
    cargo xtask api-docs --update
    cargo fmt --all
    cargo fmt --check --all          # a bare gate — never append `; echo $?`, which masks a non-zero exit
  • Risky MRs (D9): a fresh-subagent J1–J8 review + the zero-readers grep as the last pre-push step.

  • Never --no-verify / squash; signed merge commits as the human author.

Verification (per phase)

  • B/C: cargo xtask validate green; B1 adds a unit test that archive/ plans are excluded; each C MR’s new test must fail on the pre-fix code.

  • D: per-service integration on the dedicated postgres (set -a; source .ports.env; set +a; cargo nextest run -p <svc> --profile integration); D4 regenerates
    diffs the OpenAPI snapshots; D9 confirms migrations apply on a fresh dev refresh and re-greps zero readers immediately before the drop:

    grep -rn "tanf_household_snapshots\|magi_household_snapshots" \
      services/canopy-tanf services/canopy-medicaid --include=*.rs --include=*.sql
    # only the (now-removed) tanf write + the two CREATE migrations may match
  • E/F: focused integration + the relevant gated e2e (E2 SNAP discrepancy-resolve flow).

Out of scope (next large tracks — epic-grouped in Phase A)

Whole-program compliance coverage (#774–#805, #807–#848), the caseworker-architecture wishlist (#601–#608), the trading-partner framework (#605), seed convergence (#716), the orchestrator per-program context track (&63 / #858), and #908 (rule-citations restructure). The deferred-needs-design items (#902, #404, #870, #871, #879) await their own focused decisions.

Edit this page · default