Completed Plans Archive

On this page

Plans listed here have been fully implemented and merged. Each links to the original plan document for historical reference.

Plans marked "pre-MR" were implemented before the MR workflow was established. Commit SHAs are provided for traceability. Verify via git show <sha>.

Features

Plan MR(s) Completed

DB-Degradation Posture — Server-Yields-First (ADR-068, #1404)

feature/1404-degradation-posture (single MR, Closes #1404)

2026-08-23 (epic &83’s last child — the ratified mode-B posture end-to-end: route-class request ceilings (20s/300s/600s) with caller-budget pairing, the 10s funnel statement budget (post-claim hook in claim_first; claim_first_unbudgeted opt-out), 8s S2S hop budgets, the phase-aware sqlx classifier (57014→504, PoolTimedOut→503+Retry-After fleet-wide, connection-lost 503, commit-death db-outcome-unknown), the blocking db-error-construction lint at zero baseline, saturation hysteresis on dedicated 2s probes (db_pool_degraded; never gates /readyz), edge 503/504 pass-through, seven live fault legs + the outbox drain-after-stall leg, and the C21 promotion. Design survived one internal (11-finding)
one external stop-ship (26-finding) review before ratification.)

Per-Jurisdiction Search-Scheme Override (#1025)

!1336/!1337/!1338/!1339/!1341/!1342/!1343 + the U5 close-out MR (Closes #1392–#1398, #1025)

2026-08-11 (epic &80 — ADR-049’s deferred override wired end-to-end, fail-closed, and ACTIVATED in one day: allowlist-of-one (persons.ssn_last_four → {BlindIndex, Opaque}), the crypto_field_lineage gate-stamped marker as single-writer scheme truth (ADR-063 post-apply seam; serving verifies read-only, presence-required), the two-tier planner path (presence-independent lookup = invariant 500, presence-gated capability re-check = generic 400), every boundary on the boot-materialized EffectiveRegistries (zero-allocation GA short-circuit; shared craig_cases_fields::intern leak-once), tx-stub’s reference Opaque declaration, seed identity + devstack staleness bundle bindings, and the verify-seed divergence teeth. As-built: ADR-049 § Amendment — #1025; follow-ups #1399 (mid-life migration tooling), #1400 (capabilities ∩ descriptor ops).)

OpenTelemetry Distributed Observability

pre-MR — db918e6c; enrichments !-tracked via #1160/#1186

2026-03-22 (traces + metrics + trace-correlated MQ envelopes: craig_common::telemetry::init gated on OTEL_EXPORTER_OTLP_ENDPOINT, /metrics on every backend, EventEnvelope.trace_context, the compose observability profile (jaeger/prometheus/grafana). Step 4’s MqHealth channel was superseded by #1186/ADR-061; outbound injection consolidated into send_with_metrics (#485); db-pool metrics rode #1160. Status reconciled + archived 2026-08-08 per #1207.)

Repo-Committed Encrypted Secrets (SOPS + age, ADR-064)

!1323/!1324/!1326/!1327/!1328/!1330/!1331/!1332 + the U8 close-out MR (Closes #1378/#1379/#1380/#1381/#1382/#1383/#1384/#1385/#1386/#1031/#1032)

2026-08-10 (epic &79 — secrets management end-to-end in a day: the sops+age store secrets/dev.yaml + .sops.yaml live encrypted in the PUBLIC tree (devstack values only; production stays orchestrator-store), three age recipients (dev/CI/offline-recovery), and rotation is a git commit — every mutating devstack command runs the two-phase teardown-before-publish adoption (crash-resume via a root pending record, zero-sops warm path via the .devstack/ marker), proven LIVE when U6’s own battery adopted a real rotation mid-run. Guards: the blocking sops-policy lint + secrets-policy CI job + the extended .dockerignore canary. Operator surface: cargo xtask secrets (init/decrypt/check/bootstrap-store/edit/add-recipient/ remove-recipient = REAL revocation/fork-rebootstrap). CI decrypts the store with CRAIG_CI_AGE_KEY in the pinned devtools container. Three maintainer gates (G-A key ceremony, G-B CI-keypair proof, G-C store-mode + old-variable retirement) all satisfied; #1031 closed. As-built ADR-064 + ADR-048 §D5/ADR-020 amendments; follow-ups #1387-#1390.)

Migration Gate + Verify-Only Boot (ADR-063)

!1162/!1163/!1164/!1165/!1166/!1167/!1169 + the M5 close-out MR (Closes #1305/#1306/#1307/#1308/#1276/#1275/#1309)

2026-08-03 (epic &78 — schema application became a DEPLOY phase: ONE schema state machine in craig-db (Dirty|ChecksumMismatch|Diverged|Behind|Ahead|Exact, deterministic precedence; Diverged deliberately wide so Behind ⇒ applied ⊊ embedded BY CONSTRUCTION — a rolled-back gate no-ops instead of re-bricking rollback) consumed by BOTH modes; the same-image <binary> migrate gate (exact argv, one env var, logging-only telemetry); compose gates ×8 sharing image+build via merged fragments with service_completed_successfully edges; serving boot VERIFY-ONLY through bootstrap_verified (data plane → verdict under the schema_compat_floor → control plane — a JWKS/MQ failure can never mask a schema refusal; pre-1.0 BREAKING: deployments must run the gate); the destructive-migration floor lint (BLOCKING) + the migration-apply isolation lint; cargo xtask migrate apply; the xtask typed service registry (MF — snapshot/rollback had silently skipped craig_composition); the #1275 test-plane pre-warm (runtime-enforced WARMABLE_BASES registry, 1 leader apply + 4 file-copied security siblings, fingerprint parity ×4). Release evidence in-battery: the rollback boundary over the REAL cases set via runtime N−1 migrators, upgrade-in-place with data preserved, the Postgres-only scrubbed-env binary proof ×8 across all verdicts, the poisoned-gate compose-semantics pin. Live: fresh-volume reseed through the gates + a verify-only fleet reboot on existing volumes. As-built: ADR-063. Interleaved fix: #1311 (!1168 — AdvisoryLease close-only release raced its own backend exit; explicit unlock). Follow-up: #1310 (migration-owner vs runtime DB roles).)

Send-Jobs Claim Lease + Generation Fencing (double-send on rolling restart)

!1097 (Closes #1185)

2026-07-28 (epic &74 child — exchange_send_jobs had kept the pre-#1128 claim pattern: its recovery sweep keyed on next_attempt_at (scheduled eligibility, untouched at claim), so a job claimed out of a >60s-overdue backlog looked "stuck" instantly and a peer re-claimed it MID-SEND — two workers POSTing the same transmission — while the unfenced bare-id finalizers let the racing workers fight over sent/failed, both staging events. Shipped: the #1128 claim lease ported (claimed_at; CLAIM_LEASE_SECS = 90 = 3× the verified 30s transport cap, compile-time floor-asserted; recovery folded into the claim WHERE — continuous on the 1s poll, the wrong-clock bootstrap sweep DELETED; legacy rows heal via claimed_at IS NULL) PLUS the claim_generation CAS the outbox does not need (a double partner POST is not inbox-deduped; finalize writes job + transaction + events): every terminal/retry transition fenced, typed Applied/Stale, worker rolls back on Stale — conflicting-event emission closed structurally. update_transaction_status gained the defense-in-depth terminal guard. Test seam: lib.rs exposes a NARROW inline store module so the 8-test scratch-DB matrix drives the REAL fns with the REAL migration files (the old recovery test’s inline-SQL copies deleted — the #1131 drift class). RED 5/6 against the pre-fix fns; 8/8 green post-fix. As-built: ADR-022 #1185 amendment + the previously-undocumented exchange_send_jobs data-model section. Deploy: drain old replicas first. Follow-up: #1195 (early-terminal arms' missing transaction stamp/event + the guard-disjunct note).)

Idempotency Middleware Body Integrity (empty/synthetic body substitution)

!1096 (Closes #1183)

2026-07-28 (epic &74 child — the craig-api idempotency middleware substituted empty bodies for successful responses: an over-cap 2xx was emptied and PERSISTED as a canonical succeeded row (2xx/empty live + on every replay until expiry; the documented sentinel only fired at exactly cap+1), a finalize DB error served 2xx/empty while logging "returning live response", and oversized non-2xx bodies were truncated for a row that caches nothing. Shipped per the externally-reviewed v2 plan (~27 findings; steer: standalone, fencing → expanded #1182): the POST-2XX TERMINAL RULE — a committed 2xx may never finalize re-claimable failed (Path-D would re-execute committed work); over-cap, mid-stream body error, and client cancellation (new FinalizeGuard drop-backstop) all land on the existing succeeded+body_too_large sentinel, no migration. Frame-level capture (trailers survive the live path) splits the crossing frame zero-copy at the 1 MiB cap; the live winner ALWAYS receives its full body (over-cap streams prefix+remainder+rest); finalize outcomes typed Applied/Lost/outcome-UNKNOWN; non-2xx is pure from_parts passthrough (extensions + version now survive). 17-scenario throwaway-DB matrix (scoped-trigger faults, cancellation, one-connection-pool, real-TCP, the #1182 lease-crossing evidence pin) + capture proptest; RED-proof 13/17 failures on the pre-fix binary. ADR-022 §D5 refreshed (stale pre-atomic-claim DDL; unsafe "client re-executes" instruction removed)
amended. Follow-ups: #1182 scope expanded (fencing, fingerprint-checked reclaim, sentinel fidelity, client recovery); #1194 NEW (expiry-window claim 500).)

Per-Diem Payment Idempotency (placement.activated double-pay)

!1095 (Closes #1179)

2026-07-28 (epic &74 child — the per-diem placement.activated INSERT had no idempotency key; any inbox replay silently minted a second live payment AND a second payment_created event. Shipped per the v3 externally-reviewed plan (~25 findings folded): the LIFETIME unique index payments_one_perdiem_per_placement_period (placement_id, period_start) WHERE agreement_id IS NULL with NO status filter (voided does NOT free the key — the ended-placement resurrection hole), a fail-closed destructive dedupe migration (auto-repair ONLY identical/untouched/pending groups, keep-earliest into the payments_dedupe_repairs ledger + outbox reconciliation; every other shape RAISEs; 9-scenario template-DB migration test), the arbiter-first consumer (early probe on immutable payload data before dependency lookups; overlap flag + event stage only on a real insert; race-lost branch runs the identity+money derivation compare, detection-only on mismatch), the any-status BLOCKING invariant perdiem_placement_duplicate + the generic xtask blocking-name classification test. Crash-window replay test verified deterministically RED against the pre-fix binary. Deploy: stop-all runbook; rollback to the old binary unsafe. As-builts: ADR-053 + ADR-022 amendments; follow-ups #1192 (fixture residue), #1193 (ended-before-activated tombstone).)

Reports List Summary Projection (pre-1.0 wire fork)

!1085 (Closes #1158)

2026-07-27 (epic &73 child, audit F23 — the reports collection GET forked to a 6-key summary (id, received_at, reporter_type, reporter_relation, admin_unit, narrative_preview at prefix-160/wire-161) backed by the new craig_search::Projection validated newtype + the hard-forked execute_search(projection): heavy columns never fetched, exactly ONE decrypt/row (down from up to 7 incl. multi-KB raw_submission JSONB × 500 rows/page), the SUMMARY_PROJECTION static seam-binding SELECT + decrypt walk, and the consuming decrypt_into_summaries typestate making ciphertext previews unrepresentable. Detail GET
create echo unchanged; ip_hash/partner_id/received_request_id/names/phone/children/adults/ raw_submission are detail-only. Consumers (web queue + chain, CLI, e2e) updated in-MR; stop-the-world deploy procedure in CHANGELOG. Benchmark PASS on both plan thresholds: per_page=500 mean 398→114 ms (−71.4%, non-overlapping ranges), bytes 3.35 MB→180 KB (5.4%). Shaped by 1 internal + 1 external (~29-finding) review round; as-built in the ADR-049 read-projections amendment; follow-up #1176 (detail-GET ip_hash exposure).)

Closed-Cohort SHINES Conversion Import (#1071 program)

!1038 (MR-A substrate) + the MR-B close-out MR

2026-07-25 (epic &70 child — ADR-057: the closed cohorts (rcs/ercs/ersg/enrsg) enter CRAIG through a STAGED conversion ledger (canonical payload + sha256 replay evidence per record), reshaped by the 10-stop-ship external review: manifest-verified FINALIZE is the single money-consent act (per-record txs, crash-resume exactly-once, per-batch lease), every materialized agreement born with a generator-enforced payment_cutover_month (scheduled
manual + reconcile paths all bounded — SHINES owns pre-cutover months by machine rule), store-checked cohort closure dates, the family-exclusivity taxonomy (guardianship index widened + relative-care index + ALL-families lock order + the native ERR sibling gate), refuse-beyond-grace 22.9 posture (never auto-suspend), StateOffice ImportHistory proofs (the first named state-floor action; historical stamps stay outside the matrix under hygiene rules), imported-pending UNREPRESENTABLE (closes #1116), the explicit-admin operation-discriminated ruleset, the cargo xtask import-subsidy-history operator tool (device-code OAuth as the state-office human; shared canonicalization — proven live), seed family F, the web read scope, four BLOCKING invariants, and the conversion-cutover runbook. Each commit J-reviewed; as-builts ADR-057 + ADR-052/054 amendments; ⁂ readings on #1073; follow-ups #1115 (unchanged) and #1121.)

SG/NRSG (UAS 552/550) Guardianship-Subsidy Flow (#1070 program)

!1035 (G0 #1111 noon instants) + !1036 (G1 backend) + the G2 close-out MR — interleaves !1034 (#1117 occupancy-trigger race)

2026-07-24 (epic &70 child — ADR-056: GA 22.8’s post-permanent-guardianship subsidy over the ADR-052..055 stack, reshaped by the ~45-finding external stop-ship review. ONE program-tagged create route (ERR hash LEGACY tag-free so persisted F8 hashes survive; closed cohorts typed CLOSED_PROGRAM); two-step CreatePending + the witnessed /activate at the DERIVED transfer (S2S guardianship ending + child match + goal proxy; store-side 6-month residence floor from the snapshot; sibling-open refusal — ERR terminates first; replay-after-success 200; county proofs); the stored payments_begin_month = max(month after transfer, month after TANF) generator boundary (fact-keyed — legacy rows untouched); truth-dated guardianship_finalized ERR handoff (business_date == ended_on); F5 approval-anchored review re-derivation; family one-open exclusivity; seed family E; the craig-web surface (county_director/admin U6, the SS6 refuse-first end-placement interstitial with scope-honest acknowledgement, flow-shaped-only Activate) and the dana.county 8-step handoff e2e. Knob CRAIG_FINANCIALSUBSIDY_SGENABLED = enrollment consent only; lifecycle grandfathered. ⁂ 22.8 readings on #1073. Each MR J-reviewed; as-builts ADR-056
ADR-052..055 amendments; follow-ups #1112–#1116, #1118 (seed ruleset DO-NOTHING fallback, found by the C5 reseed), #1119, #1120.)

ERR (UAS 542) Kinship-Subsidy Flow (#1069 program)

!1027 (M0 #1102) + !1028 (M1) + !1029 (M2) + !1032 (M3) + the M4 close-out MR — interleaves !1030 (M2 close-out) + !1031 (#1109 test-clock fix)

2026-07-24 (epic &70 child — ADR-055: GA 22.8’s ERR flow over the ADR-052/053/054 stack, reshaped by the ~47-finding external stop-ship review. One-shot CREATE-ACTIVE enrollment (F1 generic-create refusal; F4 default-off consent gate; F8 client-keyed canonical-hash idempotency before volatile deps; CreateActive county proofs over M0’s action-bound authority; placement S2S Eastern-dated; −12-month floor); the generator month judgment (GENERATED payment_month identity, ONE expected_month_row + per-child arbitration, F5 whole-month per-diem exclusivity STANDING, prorated native birth months, transactional reconcile claim/settle closing #1098); the report-only 120-day clock + ERR reason/witness matrix; sweep leg 3 (durable subsidy_perdiem_handoffs fact ledger — detect-once/enforced/corrected; truth-dated F6 same-placement F7 terminations under the pay-excl child lock; moved children report-only; leg precedence; the execute acknowledgement handshake; the completion-event finalize fix); and the craig-web operator surface (per-render idempotent enrollment form, clock badge/panel, leg-3/moved rendering + acknowledged counts, dana.county e2e). The ⁂ 22.8 money policies ride #1073 behind F4. Each MR J-reviewed; as-builts ADR-055 + ADR-053/054 amendments; follow-ups #1106/#1107/#1022.)

22.9 Review Workflow, Lifecycle Mutations & Automation (#1081 program)

!1019 (U0 #1093) + !1020 (U1 #1094) + !1021 (U2 #1095) + !1023 (U3 #1092) + !1024 (U4 #1096) + !1026 (U5 #1097) — interleaves !1022 (#1100 advisory gate) + !1025 (#1101 flash cookie)

2026-07-22 (epic &70 child — ADR-054: the GA 22.9 review workflow over the ADR-052 ledger, reworked from a rejected attestation draft by the ~40-finding external stop-ship review. Verified IdP office authority (county/regional/state realm roles + three pinned principals; nothing approval-shaped is caller-supplied; supervisor approval = the supervisor’s own session; store writers take ApprovedAction proofs — authorize_worker matrix-checked, authorize_system knob-witnessed for exactly the two enforcement legs, approval_level = "system"); the three-state review chain (completed / open slot / superseded — hard deletes never happen) with cycle identity, one-open partial unique, CHECK-refused impossible states, anchor preservation, and suspended-head recovery; a durable payment-reconcile queue drained by the #1068 generator (void AND generate — retroactive approved money); the jurisdiction BusinessClock (GA = America/New_York) retiring ADR-053’s UTC approximation; the two-step run-recorded enforcement sweep (pinned sets, CAS single-use digest token, 15-min expiry, regional SweepExecute office gate, lock-free observe, 4 default-off consent knobs); and craig-web’s first subsidy surface — ledger views, the five PRG forms flashing backend refusals VERBATIM, sweep queue pages, runbook. 22.9 unknowns ship as ⁂ best readings on #1073, production write policy until DFCS confirms. Each unit its own J-reviewed MR; per-unit as-builts in ADR-054; follow-ups #1098/#1099.)

Monthly Subsidy Payment Generator

!1017 (MR-A enablers #1088/#1089/#1090) + !1018 (#1068)

2026-07-20 (epic &70 child (b) — ADR-053: monthly full-month subsidy payments over the ADR-052 ledger. One shared payments table with nullable origins + term-revision pricing provenance (composite FK); UNIT-MONTH rows (day_count=1, rate=gross=the monthly award) over exact calendar months, structurally never IV-E (TANF per 22.8); child/month advisory locking in BOTH payment writers with per-diem-wins exclusivity; the reconcile-then-generate correction protocol (void+regenerate undisbursed under a voided-exclusive idempotency key; disbursed rows frozen AND visible — counted + invariant coverage arm); a run lease on a DETACHED connection (drop = release on error/panic/cancellation); default-OFF Georgia-gated scheduler with an immediate first tick; a SubsidyAgreement/Approve-gated trigger with the strict YearMonth token bounded to [current−12, current]; seed fixtures whose 3-month history ends AT the current month so the first tick no-ops. MR-A first fixed three pre-existing bugs the program exposed (open-token BFF views #1088, pagination-corrupt eligibility handler #1089, case-uuid payment audit ids #1090). Shaped by one ~30-finding external review + four internal rounds + a branch-wide contextless J1–J8 pass whose two hard finds (lease cancellation leak; invisible uncovered-disbursed rows) were fixed in-branch; provenance in the plan.)

Subsidy-Agreement Temporal Foundation

!1012 (MR0 plan) + !1013 (#1082) + !1014 (#1067) + !1015 (#1083)

2026-07-20 (epic &70 foundation — ADR-052: kinship-subsidy agreements (GA 22.8/22.9) as a close-only temporal ledger: immutable identity + parties + effective-dated term revisions + business-effective status intervals + review records, single-writer store (craig_financial::subsidy) with head-ID CAS, advisory-lock coverage guard (terminal tails excluded), import primitive with replay key, 8 blocking invariants, and a read-only API behind ResourceType::SubsidyAgreement with a leading service-caller DENY row. NO public mutation surface by design — creation lands with the eligibility flows (#1069/#1070/#1071), reviews + mutations with #1081. Also landed: the fourth devstack principal carol.reader (readonly) across the identity model + all four IdP configs + the four-role harness matrix (#1082). Shaped by two external review verdicts + six internal contextless reviewer rounds; provenance in the plan.)

Capability-Based Encrypted Search (C3)

!970 (#983) + !971 (#1015) + !972 (#1016) + !973 (#1017) + !974 (#1018) + !975 (#1019) + !976 (#1020) + !977 (#1021)

2026-07-13 (sub-epic &67 under epic &66 — ADR-049: a field’s search capability = f(encryption_scheme, logical_type) through ONE craig-search/craig-cases-fields registry every boundary consults. All five cases entities retrofitted onto the injection-safe fail-closed planner (one SearchPlan feeds list + count; LIKE-escape); reports search repointed to plaintext admin_unit+reporter_type (pre-1.0 semantics change — the old encrypted-column search silently matched nothing); persons name search on the trigram concat + ssn_last_four blind index through the planner with the SSN HMAC migrated to a per-field domain (hmac_domain("ssn", 1)) derived via the single FieldSpec::blind_index at all four sites; authz scope a separate non-degradable channel (resolve_scope, hostile-?worker= suppression); the write path/seeder/verify-seed registry-driven (craig_search::row, encrypt_field/decrypt_field retired). Each MR shipped review-first after a 3-reviewer adversarial J1–J8 pass; sub-epic &67 closed; C7 activation unblocked.)

SHINES Signed-Path Attachments

!870 (#940) + !871 (#941) + !872 (#942) + !873 (#943) + !874 (#944) + !875 (#945) + the final docs/limits MR (#946)

2026-07-01 (epic &65 — signed SHINES attachments via a signed SHA-256 hash manifest: contracts (Sha256Hex + SupportingDocument), content-negotiating edge handler + multiset hash verification (atomic, no BOLA surface), sink multipart forward, signed debug output, browser attachment UI, 3-SDK submit helpers + cross-language parity vector, and docs/ADR + the signed_body_limit nest override. Each unit adversarially reviewed; epic &65 closed.)

craig-intake Hardening & Total Test Coverage

!848 + !849 + !850–!854 + !856–!859 + !861–!864 + !866 (+ earlier step MRs; see the plan Status table; !865 was the separate #935, not part of this plan)

2026-06-30 (epic &61 — born from the 2026-06-28 coverage audit; 11 children closed. Per-dimension test passes + input-integrity/SHINES-adult fixes, the #754 chaos/fault 4-MR program, and #756’s 5-MR cross-cutting close-out (COMP-01/03/04/05/06/07/08/10/12/13). One product enabler: optional in-process rustls TLS for the edge (ADR-046) — a devstack-only self-signed-tls feature powers the secure-context browser-crypto (COMP-05) + visual-regression (COMP-06) e2e. COMP-05 surfaced + fixed a latent browser-keygen JWK defect (#936). Epic &61 closed; open follow-up #933 (CHAOS-04 attachment-proxy info-leak))

craig-intake as the Single Public Intake Portal (program)

!806 + Phases 1–5 (see rows below)

2026-06-28 (epic &60 — the single-public-portal program, all 5 phases complete; ADR-043. craig-intake is now THE themed public report portal for every deployment, craig-web’s duplicate /report* removed, /report reaches the edge by path-based ingress. Epic &60 closed)

craig-intake Portal — Phase 5 (Remove craig-web /report)

!832 (plan) + !838 (P5.1) + this MR (P5.2)

2026-06-28 (epic &60 Phase 5; deleted craig-web’s public /report* flow outright — routes/templates/static/intake-client/CRAIG_WEB__INTAKE_URL/contracts/65 i18n keys — with http() repointed to keep OIDC login, the public e2e + a11y coverage ported to the edge first, and the path-based-ingress cutover documented as a pre-1.0 breaking change #736; this MR archives the program + closes the epic #737)

craig-intake Edge Public-Form Parity Remediation (Phase-5 prerequisite)

!833 (R0) + !834 (R1) + !835 (R2) + !837 (R3)

2026-06-28 (epic &60; a pre-deletion audit found the edge NONE/cases form was a reduced field subset of craig-web’s — R0–R3 restored the 911 banner + child is_victim + the full narrative/reporter/adult/child field set under None, POST-body-asserted, making the edge a verified superset before Phase 5 deleted craig-web’s form #739/#740/#741/#742)

craig-intake Portal — Phase 4 (Mandated-Reporter Signing)

!826 + !827–!830

2026-06-27 (epic &60 Phase 4; ADR-043 amendment; SHINES-scoped mandated-reporter signing — PII-free report.signed accountability audit event #727 + browser mandated-signing UI + required O.C.G.A. category #728 + approval-gate UX via the keyring by-kid lookup #729 + the structural Signed ⇒ /public 422 gate #730. L4 satisfied for SHINES; integrated individual signing is the deferred global gap #731)

craig-intake Portal — Phase 3 (Parity Migration)

!816 + !817–!822

2026-06-26 (epic &60 Phase 3; ADR-044; shared craig-i18n crate #716 + edge i18n adoption #717 + staged accessible wizard + edge axe audit #718 + public attachments endpoint forwarding to cases #719 + success-view upload UI #720 + embedded themed UI mounted in integrated mode / L7 #721 — the edge reaches full parity with craig-web’s public report flow)

craig-intake Portal — Phase 2 (Integration Aids)

!811–!813 + #715

2026-06-26 (epic &60 Phase 2; PII-guarded debug CpsRequest panel #713 + gated metadata-only keyring GET /keys + same-origin proxy #714 + view-keys page + per-profile nav #715)

Standalone-SHINES Intake Test-Coverage Remediation

!799–!805

2026-06-25 (epic &59; 6 waves W0–W5; mock SHINES fault harness + sad/happy/evil/chaos unit+integration fills + SDK suites baked into the pre-push gate + e2e top-ups; literal-100% coverage of the standalone-SHINES intake surface)

Standalone Intake Backend Plugin (SHINES Alignment)

!786–!797

2026-06-25 (epic &58; ADR-042; keyring sidecar + signed JWS API + proposed SHINES API spec; e2e + docs)

NCANDS Child File Federal Export (Capture-First)

!763–!775

2026-06-22 (epic &55; ADR-040; capture #644–#647 + Phase D #651–#657)

Report-Person Linking with Pluggable Jurisdictional Matching

!154–!162

2026-04-27 (ADR-019, Phase 2 of family chain view)

System-Wide Reference Data Management

pre-MR (7b2e6c7, 29b502f, 35e8c00)

Phase 1

Mock SPA — All 23 Screens

pre-MR (a45df95, 75c2fd3)

Phase 10

CRUD Completion — All Operationally Relevant Resources

!4, !6, !7, !8, !9

Phases 3-8

UI/UX Overhaul: Sort, Search, Name Resolution, Pagination

!12, !13, !16, !17

Phase 10

JWS Integrity & Multi-Language SDKs

!28, !29, !30, !31, !32

Phase 12

Standalone Mode for craig-intake

!37

Phase 12

Public Child Abuse Reporting (Phase 1 + 2)

!26, !27, !39

Phase 12

Case Management & Placement Feature Completion

!45

Phases 3-4

Data Exchange Adapters

!46

Phase 5

Reporting & Data Quality Dashboard

!50

Phase 7

Worker Display Names

!43, !44

Phase 10

Infrastructure & DevOps

Plan MR(s)

Retention Enforcement — Archive-Then-Prune (#1129 program, MR-B)

2026-07-26 — MR-B (5 J-reviewed commits on feature/1129-archive-engine). Outcome: the audit-class tables (audit_log, dead_letter_audit, rule_evaluations — the #1130 single copy) are never hard-deleted while unarchived — the shared craig-retention engine archives crash-atomic NDJSON batches (no-overwrite puts, self-describing manifests with [min,max] id ranges, ledger row + event inside the prune tx) behind the default-OFF consent knob with a boot storage probe (Local refused); purge refuses everything until DFCS names a schedule (D14 at every destructive statement, legal_hold honored, security-scoped); rules.evaluations_archived validate-then-write fleet bookkeeping (D16); cargo xtask archive-fetch addressability (D17); 3 report-only 97-day hot-window watchdogs; CLI destructive rework. As-built: ADR-058 + its MR-B amendments.

Event Outbox/Inbox 30-Day Retention Sweep (#965, MR-A of the #1129 retention program)

2026-07-26 — MR !1062 (merge a3e8a459). Outcome: the documented-but-never-enforced transport-table retention now runs — published-only outbox prune at 30 d / state-blind inbox at 31 d (load()-validated inbox ≥ outbox + 1, ADR-058’s async-prune boundary invariant), hourly inside every OutboxWorker under a detached-connection advisory lock, batched + cycle-capped; exchange exchange_send_jobs + financial settled subsidy_reconcile_queue local sweeps; the mechanical admin-replay retention boundary (pre-1.0 breaking); 4 report-only watchdog invariants + red-harness. The archive-then-prune half (audit-class tables) is MR-B of the #1129 program plan.

Field Encryption End-to-End + Devstack Activation (epic &66)

2026-07-14 — C1 bf9c3691; C2 !966; C3 = sub-epic &67 (!970–!977, see Features); C4a 3ca146fb + C4-floor (#1011) + C4d !967 + C4-gate (#1012/#984, final MR); C5 !968; C6 !969; C7 !978; C8 !979. Outcome: craig-cases field encryption runs end-to-end in devstack + CI — ADR-048 key lifecycle (gitignored dev key / masked CI variable), boot verify-only refusing key↔data drift, KCV-staleness auto wipe+reseed, compose completion gate + in-graph garage-init, verify-seed --expect keyed at both gates.

Docker Image Upgrades + UUIDv7 Migration

pre-MR (71fc288, 666fcd3, 099412d)

Devstack Script Durability Improvements

pre-MR (7b2e6c7)

CI/CD Pipeline Overhaul

!10

CI Pipeline Optimization

!20

Connection Pooling for Test-Lib HTTP Clients

pre-MR (4519db5)

Dependency Audit — March 2026

pre-MR (7315c4b)

claude-quickstart Alignment

!52

Devstack Hardening — Pre-commit, Staleness Guard, Ruleset Gate

#174 (9563af8 for implementation; plan-hygiene sweep 2026-04-17 for docs)

Platform Stabilization (Pre-Offline-Worker)

!166 (Step 1) / !170 (Step 2) / !172 (Step 3) / !168 (Step 4) / !167 (Step 5) / !171 (Step 6) / !169 (Step 7) / !173 (Step 8) / !174 (Step 9) / !175 (Step 10) / !176 (Step 11) / !177 (Step 12) / !178 (Step 13) / !179 (Step 14) / !180 (Step 15). Closes 4 P0 invariants (encryption fail-closed, JWT audience/scope, transactional outbox, DLX/DLQ wiring), 3 P1 durability gaps (persistent idempotency middleware, subscriber-stub retirement, cross-service reconciliation walker), 4 P2 ops gaps (split health endpoints, CI gate hardening, cargo-deny pre-push, image-tag pins), and 14 P3 cleanups. ADRs: ADR-020 (Encryption Fail-Closed), ADR-021 (JWT Validation Policy), ADR-022 (Event Durability + Idempotency).

Store Parameter-Struct Refactor (#272)

!188 (Step 2 — craig-cases pilot, 21 fns) / !189 (Step 3 — bundle for craig-{placement, security, exchange, financial, reporting, rules}, 55 fns). Mechanical refactor closing the citation chain from data-integrity-hardening.adoc § Deferred (Store Parameter Structs): every long-parameter-list store fn now takes a typed <Verb><Resource>Params struct with owned fields. Workspace-wide #[allow(clippy::too_many_arguments)] count under services/*/src/store/ dropped from 80 to 0. 4 remaining annotations workspace-wide are out of scope by design (2 Axum handlers, 1 domain fn, 1 internal helper).

Plan J — Env-var Documentation Coverage + Docker-compose DRY

3 step MRs across 2026-05-15 → 2026-05-25 closing 2 findings (F-038 + F-039). Step 1 (plan filing): !307 (2026-05-15). Step 2 (F-038 .env.example expansion) + Step 3 (F-039 docker-compose YAML anchors): combined into !449 (2026-05-25 / 2d844c98). Restructured .env.example from 74-var flat list into REQUIRED / RECOMMENDED / ADVANCED tiers; added 52 operator-tunable CRAIG_ vars + 41 supplemental (per-service SIGNING_JWK_FILE + SIGNING_KID for 9 services per Plan E § Step 14, CRAIG_PEER_JWKS_JSON_FILE, CRAIG_FIELD_ENCRYPTION_KEY, log-level + statement-timeout tunables). docker-compose.yml gained 6 top-level x- extension anchors (5 scalar + 1 mapping): x-oidc-issuer-default (10×) / x-oidc-internal-url (10×) / x-jurisdiction (9×) / x-admin-unit-label (7×) / x-rules-engine-url (7×) / x-garage-credentials (4× via <<: mapping merge). CRAIG-side architectural deviation from plan body draft: scalar anchors used for service-prefixed env vars (CRAIG_CASESOIDC_ISSUER: *oidc_issuer_default) because mapping-anchor merges (<<: *oidc_defaults) would emit unprefixed OIDC_ISSUER keys the config crate’s CRAIG_<SVC> prefix wouldn’t pick up. Garage credentials are the exception — consumed under shared CRAIG_STORE__ namespace so mapping anchor is config-equivalent there. Service-level config equivalence verified via docker compose config --format json + jq -S '{services,networks,volumes,configs,secrets}' diff returning 0 (empty). Step 4 (audit + archive): this MR.

Plan K — Canopy xtask + Hook Backports

11 step MRs across 2026-05-15 → 2026-05-25 closing 10 findings (F-040..F-049 minus F-046’s ADR cross-reference). Step 1 (plan filing): !307. Step 2 (F-040 lock + F-041 --remove-orphans): !310 — fs2::FileExt exclusive lock at $TMPDIR/craig-xtask.lock + --remove-orphans in every compose up/down (closes the recurring port-collision pain). Step 3 (F-042 reconcile_ports_env): !443 — drift detection between .ports.env and actual docker bindings + per-subprocess env migration replacing the unsafe env::set_var-based export_port_env_vars/load_and_export_ports_env antipattern; ~500 LOC port + 6 new docker tests. Step 4 (F-043 dev refresh): !444 — cargo xtask dev refresh smart minimum rebuild via SHA-256 staleness markers; 13-row decision table in .claude/docs/local-dev.md. Step 5 (F-044 cargo xtask coverage): !445 — cargo llvm-cov wrapper, soft-start (measure-only default, --threshold N opts in, --baseline writes JSON); new test-coverage-scorecard.adoc § Line coverage (Plan K F-044) section with 3-condition promotion checklist. Step 6 (F-045 cargo xtask migrate): !446 — snapshot/rollback/list sub-commands; CRAIG-side schema-version-aware bail per invariant #4 (rollback bails when live _sqlx_migrations > snapshot’s recorded head unless --force); 14 new unit tests; xtask count 241 → 255. Step 7 (F-046 ADR-030): 2026-05-16 — docs/modules/ROOT/pages/adrs/adr-030-plan-lifecycle-and-status-vocabulary.adoc defines canonical Status vocabulary + pre-commit Q4/Q6 reframe; mirrors canopy ADR-013. Step 8 (F-047 plan-lint): 2026-05-16 — cargo xtask docs plan-lint blocking at xtask validate [4b/14]; rewrote 348 baseline violations across 60+ plans. Step 9 (F-048 plan-archive): 2026-05-16 — cargo xtask docs plan-archive developer-invoked one-shot. Step 10 (F-049 e2e --no-refresh): !447 — --no-refresh flag bails on down devstack + skips seed-manifest regeneration; .githooks/pre-push:24 invokes after dev reseed. Step 11 (audit + archive): this MR (cargo xtask docs plan-archive eats its own dogfood). xtask test count 235 → 255 over the plan. All canopy xtask + hook patterns of operational value backported.

Test Framework Hardening — Concurrency, Fault Injection, Failure-Path Coverage

Steps 1–11 (pre-rebase) plus Steps 12–20 in this final epic phase: !206 (Step 12 — 6 xtask subcommands) / !207 (Step 13 — quality-budgets reporter) / !208 (Step 14 — coverage-matrix tooling) / !209 (Step 15 — DB constraint evil-test harness) / !210 (Step 16 — central evil-input corpus) / !211 (Step 17 — restart/replay helpers + CrashHarness) / !212 (review-fix follow-ups + Step 15/16 deferred work cleanup) / !213 (Step 18 — SQL invariant sweeper) / !214 (Step 19 — Phase B adoption bundle) / Step 20 (this archive MR). Workspace tests grew from baseline through 1642. Surfaced two real platform findings worth re-naming: (a) sanitize_filename non-idempotency bug fixed in-flight (Step 10); (b) craig-placement missing event_inbox migration (filed as #312 from Step 18 invariant sweep). Three §Potential improvements items filed as separate MRs: #309 (mutants baseline population), #310 (route-to-client typed bridge), #311 (RFC 9457 standardization). Seven Phase B tests blocked on platform-stab-2 fixes filed as #313–#319.

Platform Stabilization Phase 2 — Concurrency & Atomicity Hardening

!192 (Step 1 — plan filing, combined w/ test-framework-hardening) / !217 (Step 2 — verification + sequencing-doc) / !219 (Step 3 — idempotency atomic-claim state machine) / !220 (Step 4 — outbox FOR UPDATE SKIP LOCKED) / !221 (Step 5 — inbox three-state retry semantics + DLX surface) / !222 (Step 6 — atomic blob+DB+event upload across 4 sites) / !223 (Step 7 — exchange send outbox-driven worker) / !224 (Step 8 — placement state-read inside tx + foster-home FOR UPDATE) / !225 (Step 9 — BFF silent-degradation cleanup, fetch_page split + upstream-failure template) / !226 (Step 10 — P2 cluster: magic-byte upload validation + CAPTCHA guard + advisory-publishes-to-outbox + CORS warn) / !227 (Step 11 — encryption-mode configuration-layer fail-closed, default Required) / !228 (Step 12 — P3 cleanup bundle, D10.2–D10.7) / Step 13 (this archive MR). Closes 3 P0 race conditions surfaced by external review hours after the original Platform Stabilization plan archived (idempotency check-then-act, outbox no-FUSL multi-worker race, inbox silent-drop on retry-after-error), 4 P1 durability/correctness gaps (atomic blob+DB+event upload, exchange-send outbox-driven worker, placement state-read inside tx, BFF silent-degradation cleanup), and a P2 cluster (magic-byte upload validation, CAPTCHA guard, advisory-publishes-to-outbox, CORS warn) plus the encryption-mode configuration-layer fail-closed default. Workspace tests grew 1647 → 1696. Three deviations documented in plan §Errata: (a) Step 2 reduced from inline-test-infra to verification + sequencing-doc (test-framework-hardening shipped first and provided richer surfaces); (b) Step 3 Path C/D deferred to follow-up (require lower-level test infra not in current harness); (c) Step 12 D10.1 large-file decomposition deferred to follow-up issue #320. Successor plan filed: application-authz-and-pii-hardening.adoc (Draft / Pre-shaping; epic &23) capturing application-layer authorization findings (BOLA/IDOR record-level authz, HKDF blind-index, JWS replay defense, intake Uuid::nil() sentinel, partner-edge "constant-time" HashMap fix, pub(crate) discipline, test silent-skip lint, strum enum-boundary continuation, cargo deny ignore audit) that platform-stab-2 deliberately scoped out as application-shape rather than foundation-shape.

Security & Compliance

Plan MR(s)

Transactional Idempotency Claims (epic &77, ADR-062)

!1122–!1159 + this archive MR (35 units; anchors #1178/#1182) — 2026-08-02; made exactly-once-effects a property of the domain transaction at BOTH ingress boundaries, deleting the lease-based HTTP idempotency middleware outright (#1194, zero senders) instead of fencing it. Track A (!1123–!1128): the craig-mq single-transaction attempt machine — claim
savepoint’d handler + same-tx accounting under the held row lock (hard cap over COUNTED attempts, two-tier panic containment, the effect-bearing identity triple with payload_hash ×8, the collision ledger, the mandatory requeue-durable DLX surface; closes #1053 + #1209) — with every subscription handler fleet-wide converted (stop-then-start per service; the at-least-once legacy fn deleted at A6). Track B (!1129–!1145): request_claims ×8
per-endpoint versioned IntentV1 projections, claim_first as the FIRST statement of the domain tx, all 40 convert-class routes claim-first across 8 services + the intake client-held-id forward protocol + 3 SDKs; one-time secrets on pair columns (issue-before-revoke rotate); create_report on LIFETIME identity. §U (!1146–!1153): uploads/exports on generation-fenced immutable attempts (digest-first put_create, shared finalizers, the reconciler retiring AttachmentScanner semantics, deterministic exports; six-scenario race matrix). §G (!1155–!1158): the audit’s 30 stale-replay hazards + 5 event-dups fixed route-by-route (CAS/IDF/SUP/FWW + ABA version counters) and convert_report’s auto-link moved into cases' first domain-event consumer with degraded-pass refusal live-pinned end-to-end. BF (!1159): the orphaned idempotency_responses tables DROPPED ×8 (steer-authorized CONTRIBUTING deviation, recorded). Honest per-class guarantees: ADR-062 §H. Follow-ups #1272/#1273/#1274

Decision-Cache Sweep (#1188)

!1112/!1113/!1114 (#1188, epic &74) — 2026-07-31; bounded the unbounded-staleness hole where a LOST rules.cache_invalidated event left scaled craig-rules peers serving a superseded compiled decision until restart (no lazy load; a hit never consults the DB). U0 merged the ADR-006 #1188 amendment first; MR-A shipped the trigger-enforced (id, revision) change token + the identity-aware CAS protocol (craig_rules::decision_refresh: conditional installs everywhere, re-probe-confirmed reconciler removals, fail-closed compile eviction; the invalidation subscriber reconciles instead of wholesale-reloading — retiring its overwrite race — and every update recompiles the RETURNING row so cached content always matches its token, fixing the stale audit version stamp); MR-B added the periodic per-instance sweep (CRAIG_RULES__DECISION_REFRESH_SECONDS, default 300; deterministic per-instance offset) bounding lost-event staleness to ≤ ~2×interval + pass duration under a healthy DB, plus the fault e2e (RED 60.16s stale on the pre-sweep image → GREEN 4.3s). Root outbox gap → #1216; follow-ups #1217–#1219

Authz Staleness Recheck (#1187)

!1109/!1110/!1111 (#1187, epic &74) — 2026-07-30; closed the placement authz TOCTOU (pre-read authorization raced the case.assignment_changed inbox writer) with the ADR-060 conjunctive two-snapshot policy: decisive pre-tx authz.check + a pure in-memory staleness compare of the policy-consumed field under a FOR NO KEY UPDATE row lock (409 CONCURRENT_MODIFICATION; engine-under-lock rejected on named mechanisms). The AC-3 audit widened the surface to 4 handlers (kinship update/delete shared the hole with no tx at all; foster-home license transitions now validate against the locked row); kinship gained live-row semantics; U1 shipped the pid-scoped wait_for_blocked_behind test primitive. 7-test race matrix, RED observed 6/7 pre-fix. Follow-ups #1212–#1215

DLQ Forensic Durability & Idempotency (epic &75)

!1098/!1100/!1103/!1101/!1104/!1105/!1106 (epic &75: #1196/#1181/#1197/#1198/#1199/#1203/#1202) — 2026-07-30; restructured the #1181 audit finding into a no-loss, per-occurrence-idempotent, observable dead-letter tier: real consumer-queue identity threaded to the dead-letter path (C1), per-occurrence occurrence_token idempotency on the append-only audit table (C2), bounded DLQ retry with a client-confirmed parking replayer + quarantine (C3, ADR-059), the classic-queue-type/delivery-limit contract + mq-topology lint (C4, ADR-003 §#1198), DLQ observability gauges/counters + triage runbook (C5), handler-panic containment via a per-delivery task boundary (#1203), and operator-owned exchange topology + per-service least-privilege broker credentials closing the configure-includes-delete forensic-suppression hole (#1202, ADR-003 §#1202). Follow-ups #1205–#1211

Narrow the Authz Fail-Open Fallback (#786)

!1007 (#786) — 2026-07-18; retired the fail-open authz fallbacks end-to-end: PolicyMissing made SOURCE-ATTESTED (lazy_load fault/absent split + new AuthzError::PolicyLoad → 500; typed seams check_typed/auto_scope_list_typed); degraded boot keeps the REAL source (boot_degraded — the empty-InMemoryRulesetSource substitution that attested universal absence and never self-healed is gone; require_full_coverage double-failure refuses boot); craig-security’s two admin fallbacks DELETED (37+3 sites repointed, wrapper fail-closed, 40-comment sweep, 49 endpoints gain the 500 row) with the RED→GREEN in-process pins; craig-rules' three admits narrowed to the pure allow_bootstrap_fallback(err, claims, action) table (admin-any / service-Read

List) with EVERY admit staging rules.authz_fallback_admit; the four reference fixtures' wildcard service rows tightened (rule_set read+list; rule_evaluation create-only — the evaluate relay) with live black-box pins. Six contextless review rounds (21→20→11→6→6→1 findings; forks a–e user-decided). Follow-ups #1060/#1061/#1062; #873 scoped to its e2e half

Rules Evaluation Timeout (#784)

!1004 (#784) — 2026-07-17; bounded the rules eval dispatch (send + reply) with ONE tokio::time::timeout budget (CRAIG_RULES__EVAL_TIMEOUT_MS, default 5000, valid 1..=25000, boot-validated) closing the caseworker-reachable single-eval-thread wedge; typed EngineError::EvalTimeout → 503 via the existing ServiceUnavailable (504 rejected — craig-rules is the origin); best-effort dead-work skip behind the eval_loop_inner counter seam; ADR-006 amended to completed-evaluation wording; MQ path disclosed (dead-letter after ~2 attempts, blast radius ~zero, D3 analysis); paused-time test-util suite (first in repo) with red-before-green recorded. Follow-ups #1048–#1055; recovery track #1046 (bumped P2)

craig-web BFF JWT-Trust Hardening

!926 (#813, #814) + MR-B (#812) — 2026-07-07; made the craig-web BFF a proper OIDC Relying Party (per-login nonce, JWKS-verified id_token + access_token bound to the same subject with at_hash, verified-then-discarded id_token, hard-reject on failure instead of a silent empty-roles session — MR-A) and added a coarse fail-closed require_write_access edge guard that 403s a sole-readonly principal’s mutations outside the self-service /personalize/ tree (MR-B). Shared craig-auth gained Claims.{nonce,at_hash} + a configurable token typ (with_token_type, for Keycloak’s typ:"ID"). Follow-ups: #960 (RP logout id_token_hint), #961 (refresh rotation), #962 (shared JWKS cache), #963 (#812 e2e)

Encode BFF Backend GET Queries by Construction

!925 (#787) — 2026-07-06; closed the HTTP-parameter-injection class in the craig-web BFF by construction (typed reqwest .query(), percent-encoding), migrated all 42 backend GET query sites, and added the merge-gating validate-web-query-strings xtask guard so the antipattern cannot return

API Idempotency Audit & Implementation

!47 — superseded 2026-07-31 by ADR-062 / the Transactional Idempotency Claims program (epic &77), under which the Idempotency-Key middleware this plan built is being deleted in favor of in-domain-transaction claims

Data Breach Detection & Notification

!48

Penetration Testing & Vulnerability Management

!49

ATO Pathway & Continuous Compliance

!49

Security Culture Hardening

#168 (epic &5 closure). Residual: #178 (SBOM-in-CI)

Web Session Cookie Migration (ADR-013)

!81 (replaces in-memory tower-sessions::MemoryStore with AES-GCM encrypted cookies via tower-cookies; enables horizontal scale). Residual: token refresh + revocation blocklist deferred to Phase 11 per ADR-013.

Section 508 / WCAG 2.1 AA Accessibility

audit + remediation landed incrementally pre-MR (!51 color contrast; earlier tab-strip + nav work). This completion MR wires the audit as a blocking pre-push gate (41 pages clean) and publishes VPAT 2.5.

Multi-Jurisdictional Authorization

14 step MRs across 2026-04-21 → 2026-05-10. Step 1 plan filing + 5 ADR drafts (!230). Steps 2–4 ADR acceptance + identity normalization + worker_identities table + new craig-authz crate (!231 / !232 / !233). Step 5 RMQ-driven cache invalidation (!234). Step 6 default authz rulesets — georgia + texas (!236). Step 7 cross-service assigned_worker_sub denorm (!237). Step 8 / 8b cases-service engine adoption (!238 / !240). Step 9 placement + exchange (!241). Step 10 financial + reporting (!242). Step 11 security + rules (!243; cold-start hardening — boot retry, lazy_load, post-boot warmup; DbRulesetSource for craig-rules self-loopback). Step 12 Georgia hardcoded defaults removed (!244). Step 13a admin_unit_registry CSV seeding + Texas counties — closes #225 (!245). Step 13b i18n placeholders + ICPC deadline + PaymentPeriod config + BFF ?worker= cleanup (!246). Step 14 archive (this MR). ~140 handler sites enforce record-level authz; 11 cross-service tables denormalized; identity normalized to claims.sub UUID; worker_identities lazy-populated from JWT claims; 5 ADRs accepted (023–027).

Service Identity + On-Behalf-Of (canopy ADR-019-derived)

14 step MRs across 2026-05-09 → 2026-05-11. Step 1 plan filing + ADR-028 Proposed (!235). Step 2 + 2b OIDC discovery convergence in craig-auth + craig-web; destructive rename keycloak_*oidc_* across Rust + compose + docs (!248). Step 3 ADR-028 acceptance + Claims::{is_service, service_id, require_service_caller, acting_worker, actor} API (!250). Step 4 craig-auth::OidcServiceToken replacing intake KeycloakServiceToken + cases ROPC stopgap; JDM rulesets gain i_service input + service-caller admit rule (!251). Step 5 craig-auth::ActorTokenIssuer + WorkerActor projection + 7 inline tests (!252). Step 6 actor-verifier middleware (ActorJwksRegistry, StaticActorJwksRegistry); auth_middleware lifts claims.actor for service callers + fail-closes on validation failure (!253). Step 7 outbound CraigClientExt (with_service_identity, with_actor, with_craig_identity — Rust 2024 native async-fn-in-trait) (!254). Step 8 bootstrap wiring — BootstrapResult gains service_token + actor_issuer; per-service inline SIGNING_JWK / CRAIG_PEER_JWKS_JSON env-var loaders; 6 standard services migrated (!255). Step 9a devstack actor-key provisioning + craig-web bootstrap wiring (!256). Step 9b craig-web BFF flip — ApiClient method signatures token: &struser: &SessionUser; ~91 call sites updated across 29 route files; P0 fix routing JDM input + claims_sub_uuid through claims.acting_worker() (!257 / !258). Step 10 intake outbound flip — all 4 partner-edge call sites route through with_service_identity (!259). Step 11 audit-log enrichment — actor_service + actor_user_sub columns + filter API + wildcard subscriber populates from EventEnvelope.source_service + event_parsing::extract_actor_user_sub (!261). Step 12 cargo xtask identity verify — OIDC conformance gate with 3-variant outcome (AllPass / Failures / Inconclusive); soft-check wiring in pre-push validate [6c/14] (!262). Step 13 cargo xtask identity render --backend keycloak|authentik|dex — reference IaC generator covering 9 service-account clients + 2 user-facing clients + 6 worker roles + 9 service roles + 3 pinned dev users (canonical IdentityModel in xtask/src/cmd/identity/model.rs); 43 inline structural tests (!263). Step 14a cargo xtask gen-actor-keys + file-path env loaders (CRAIG_<SVC>SIGNING_JWK_FILE / CRAIG_PEER_JWKS_JSON_FILE) + 6-step rotation procedure docs (!264). Step 15 archive (this MR). Step 14b (xtask dev identity provision Keycloak admin-API automation) explicitly deferred — devstack works via committed config; Step 13 covers fresh-realm generation; per ADR-026 IdP-neutrality the admin-API client is a controlled exception not justified by current need. Tests grew 1696 → 1954 across the 14 MRs. ADR-028 Accepted.

Introspection-Mode Token Validation

6 step MRs across 2026-05-12 → 2026-05-13. Step 1 plan body + ADR-029 Proposed (!269). Step 2 introspection primitives — OidcDiscoveryDoc gains introspection_endpoint + new craig-auth/src/introspection.rs (~620 LOC) with IntrospectionClient + TokenClaimsCache (DashMap, SHA-256, Instant TTL) + RFC 7662 → Claims normalization layer with audience/token_type/exp/nbf parity to JWS path per ADR-021 + xtask identity verify introspection probe arm + 14 wiremock unit tests (!270). Step 3 AuthLayer trait refactor — new trait ClaimsExtractor (object-safe, boxed future); AuthLayer::extractor: Arc<dyn ClaimsExtractor> (backwards-compat AuthLayer::new<E>(…​) constructor); JwsExtractor / IntrospectionExtractor / AutoExtractor (3/5/0-dot dispatch); classify_token helper; OidcServiceToken impl ServiceBearerSource + client_id / client_secret accessors; ServiceSettings gains token_validation_mode (Jws / Introspect / Auto default) + 5 introspection knobs (cache TTL, max entries, serve_on_outage, endpoint_override, auth_method); init_auth dispatches on mode + hard-fails missing prerequisites; ADR-029 Proposed → Accepted (!271). Step 4 Kanidm devstack + xtask identity render --backend kanidm adapter — bash bootstrap script generator (~270 LOC + 12 inline tests); RenderBackend::Kanidm variant; kanidm service under identity-multibackend profile with auto-generated TLS cert via ensure_kanidm_cert(); devstack/kanidm/{server.toml, bootstrap.sh, README.md} (!272). Step 5 Kanidm integration test — identity_multibackend_available() probes Kanidm /status with self-signed-cert-permissive client; deadline 90s → 120s; kanidm_status_and_token_endpoint_reachable asserts /status + grant-not-unsupported_grant_type + /oauth2/token/introspect endpoint exists (!273). Step 6 archive (this MR). ADR-029 Accepted. Workspace tests grew 1962 (post-!268) → 2005 across the 6 MRs. Errata: E-01 (metrics deferred to follow-up; wiremock expect(N) covers request-count assertions in unit tests), E-02 (Kanidm + ZITADEL-opaque full-mint deferred — both need operator-side bootstrap; same gap !268’s original ZITADEL test has; tracked for a future sidecar-init-container MR).

PII Coverage + Concurrency-Correctness

7 step MRs across 2026-05-08 → 2026-05-14. Step 1 plan filing + 8 step issues + epic &25 (!230, joint with Plan A filing). Step 2 F-002 HKDF blind-index — craig_crypto::FieldEncryptor::hmac derives a purpose-specific subkey via HKDF-SHA256 (info = b"craig-crypto/blind-index/v1") per RFC 5869, separating it from the AES master key; pre-1.0 destructive UPDATE persons SET ssn_hmac = NULL migration; 3 new unit tests (!276). Step 3 F-001 reports PII encryption — extends platform-stab-2 §D9 pattern to cases.reports (45 CFR § 1355.52); encrypts 7 fields (reporter_first_name / reporter_last_name / reporter_phone / narrative as base64 TEXT + children / adults / raw_submission as JSONB {"v": "<base64-ct>"} envelopes per locked decision); encrypt_report_pii / decrypt_report_pii + 4 handler wires (create_report, list_reports, get_report, convert_report); 4 helper unit tests inline (!277). Step 4 F-003 IP hash salting — hash_ip becomes HMAC-SHA256(secret, ip) (hex-preserving) closing brute-force-over-IPv4 attack; new IntakeSettings::ip_hash_secret required at boot; secrecy = "0.10" workspace dep; 4 new unit tests + 1 validate-bails test (!278). Step 5 F-004 Zeroize on drop — Zeroize + ZeroizeOnDrop derives on FieldEncryptor (struct-level, #[zeroize(skip)] on the Aes256GcmSiv cipher field whose internal round-key schedule the upstream crate doesn’t expose for zeroization) and on WebSession (struct-level, zeroes access_token / refresh_token / id_token); zeroize = "1.8" workspace dep; TLS-URL validators dropped from F-004 scope (URL-level enforcement only fits self-hosted-with-TLS-direct; breaks sidecar-mTLS / VPC-managed-DB / devstack); new Transport security section in deployment-guide.adoc documents CRAIG’s transport-neutral posture across 4 supported patterns (!279). Step 6 F-015 approve_adjustment race fix — tx wrap + SELECT id FROM payments WHERE id = $1 FOR UPDATE row lock serializing concurrent approvers (plain tx-wrap was insufficient under READ COMMITTED: each tx saw only its own pre-commit writes to adjustment rows, so the SUM-and-overwrite pattern still lost updates); 5 store fns generalized from &PgPool to impl PgExecutor; 1 devstack-gated concurrency test with ground-truth-via-list (not the column we’re testing) — 20/20 deterministic (!280). Step 7 F-016 approve_case_plan race fix — tx wrap + SELECT id FROM cases WHERE id = $1 FOR UPDATE on parent case; new partial UNIQUE INDEX idx_one_active_case_plan_per_case ON case_plans (case_id) WHERE status = 'active' as defense-in-depth (rejects 2nd active row with 23505 if a future refactor reverts the lock-then-promote shape); 1 devstack-gated concurrency test asserting exactly-one-active — 20/20 deterministic (!281). Step 8 archive (this MR). Workspace tests grew 2005 → 2017 across the 7 implementation steps (planned +18; actual +12 because Step 5 TLS-validator tests didn’t land alongside the validators). Errata: location deviation on Step 3 tests (inline vs separate file); Plan A Step 2 dependency name correction; Step 5 TLS-URL validators scope-narrowed; Step 6 SELECT FOR UPDATE added beyond plan §D5. Open follow-up: cases.referrals encryption gap surfaced during Step 3 (decrypted reporter_* fields propagate to plaintext referrals row via convert_report) — tracked for a future scope-extension; same-data-class encryption parity invariant fully closed only when referrals encryption ships.

Plan I — Handler / Module Decomposition + DIP + ISP

12 step MRs across 2026-05-16 → 2026-05-17 (!318/!319/!320/!321/!322/!323/!325/!326/!339/!340/!341/!342/!343/!344/!345/!346/!347 + this archive). Closes 6 separation-of-concerns findings (F-033/F-034/F-035/F-036/F-037/F-054). F-034 god-module split: 4 god-modules (3,031 LOC) → 17 files (3,341 LOC, +310 boilerplate). F-035 test-client ISP MUST-split: CasesClient (65 → 7) + SecurityClient (51 → 8) + PlacementClient (36 → 6) + ExchangeClient (25 → 4) + FinancialClient (19 → 4); 29 new domain clients + 96 total typed-client harness accessors. F-036 DIP audit: 3 Plan G F-026-missed tx-helper sites migrated to execute_within_tx; 11 preserved-by-design sites annotated; trait-based DIP refactor deferred to F-067 / #464 / Plan M. F-037 typed event-payload DTOs in craig-financial + craig-reporting; 5 partner-edge Value sites annotated with cite to Plan L F-058/F-059. F-054 cargo xtask lints struct-method-count lint blocking at validate step [4c/14]; workspace at 0 violations across 461 files. F-033 function decomposition: 23 handlers decomposed across 6 services in the final MR (craig-web BFF list-handler shared helper collapsed 23 list-handler call-sites to 25-35 LOC each via paginate_window + append_search_sort + resolve_worker_filter; 5 misc craig-web; 7 craig-cases incl. 3 upload handlers via shared AttachmentParts pattern; 9 small-services bundle: craig-intake/exchange/reporting/rules); 100 over-40 sites marked important-stay per §Style "important functions can be larger" carve-out (Plan G F-026 manual-tx preservation, JDM wide-dispatch matches, denorm-inbox fan-out, parallel tokio::join! parents). Filed: F-067 trait-DIP refactor (#464) for future Plan M. ADR-030 §3 amendments throughout for scope corrections + posture changes.

Partner Edge + Browser-Facing Operational Hardening

Code Quality

Plan MR(s)

Prevention-Gate Lints — epic &63 wave 1 (#867 #866 #869 #870 #891 #879)

!1475 (2026-08-24; six blocking xtask gates closing the &63 enforcement residues — the arithmetic-blanket ratchet, the syn reqwest choke-point rebuild with the (file, count) builder allowlist, the workspace dep-inheritance gate + 19 lockfile-identical flips, partner parse-prop coverage, the advisory-ignore audit with review-by expiry, and serialize-credentials + the store-model bless-down baseline)

Rust Idiom Cleanup

!33

Rules Engine Cache Race Fix

pre-MR (ad30208)

Data Integrity & Code Hardening (Core Phases)

!24, !25

Test Coverage Gap Analysis — Road to 100%

pre-MR (merge 7c563ed) (Phase 1); !123, !125, !127, !128 (Phase 2 — all 22 step rows Complete)

UI Consistency Hardening

!42

Code Quality & Security Hardening

!21, !41, !62. Residual: #180 (CQ remediation)

Code Quality Review & Refactoring

!63 (Phases 2-5). Residual: #179 (Phase 1 bootstrap integration)

Placement Form Case/Child Picker

work landed incrementally across prior MRs — see plan Status table for per-step file paths

Tier 1 Review Remediation (2026-04-19)

!96, !97, !98, !99, !100, !101, !102, !103

Code Quality Remediation (#180)

!130 (Step 1), !131 (Step 2), !132 (Step 3), !133 (Step 4), !134 (Step 5), !135 (Step 6 audit)

Stateless Intake (ADR-017 + ADR-018)

!137 (Step 1), !138 (Step 2), !139 (Step 3), !140 (Step 4), !146 (Step 6), !147 (Step 7), !148 (Step 8), !149 (Step 9), !150 (compat-strip), !151 (Step 10), !152 (Step 12 audit + archive). Step 5 (migration tool) removed during compat-strip; Step 11 (deprecation cleanup) collapsed into Step 10.

Code Quality Review (April 2026)

Step 5b superseded by stateless-intake (ADR-017); see #197 closure.

Plan L — Partner Typed Schemas

14+ step MRs across 2026-05-15 → 2026-06-08 closing epic &34 + issues 441-#448. Step 1 (filing): commit ae4278a6 (2026-05-15). Step 2 (F-058 trait redesign): Step 2a foundation 2026-05-25 + Step 2b dispatch consolidation 2026-06-07 (closes #480 / #441); typed ExchangeAdapter trait with associated types + ExchangeAdapterKind enum + AnyAdapter wrapper extracted to NEW crates/craig-exchange-contracts leaf crate. Step 3 (F-059 per-partner crates): 4 batches across 2026-05-25 → 2026-06-06 — PILOT craig-partner-caps (!453 / 1beced2e), Batch 2 ies/ions/wic (!454 + !455 + !456), Batch 3 empi/smile/tcm (!457 + !458 + !459), Batch 4 doe-slds/cprs/stars (d96da36b + 35877cb3 + 753809e9). 10 typed per-partner crates; SHINES retained as catch-all via StandardAdapter per §Step 3 SHINES note. Step 4 (F-060 schema versioning, 2026-06-08): !647 / 1725e8f7 — scope-narrowed by empirical reality (pre-flight scan confirmed every interface-doc "Version X.Y" is a doc-revision marker, not a wire-protocol marker; no partner spec carries a wire-level schema_version field today). Delivered per-partner pub const SCHEMA_VERSION: &str = "<doc-rev>"; constants (caps=2.1, cprs=1.6, doe-slds=1.0, empi=1.0, ies=4.0, ions=2.3, smile=1.4, stars=1.4, tcm=1.3a, wic=1.0) + <Partner>Error::UnrecognizedSchemaVersion { received: String } typed-error slot per partner; envelope-dispatch deferred until a real partner spec gains a wire-level version field (closes #443). Step 5 (F-061 typed Audit payloads, 2026-06-08): !648 / 7eaa06b0 — NEW crates/craig-partner-audit/ aggregator crate exposing PartnerAuditEvent discriminated-union (10 variants Caps(CapsAuditPayload)…​Wic(WicAuditPayload)) with [serde(tag = "partner_kind", content = "payload")] representation matching ExchangeAdapterKind’s wire form + `kind() const helper + decode_jsonb(kind, jsonb_value) read-side decoder + typed PartnerAuditDecodeError enum. AnyAdapter retires dead audit_serialized + dispatch_audit in favor of typed audit_typed() + dispatch_audit_typed<A, F>() returning PartnerAuditEvent directly; SHINES + Noop fallbacks return new AnyAdapterError::AuditUnsupportedForKind { kind } fail-closed. Net: 2 Value-escape sites retired at dispatch boundary; B3a re-LOCKED 178 → 177. Storage layer (partner_audit_events table + write-side + read-side API) deferred until a real consumer materializes — same pre-1.0-don’t-lock-contracts logic as Step 4 (closes #444). Step 6 (F-062 codegen pilot, 2026-06-08): !649 / 20ff142dDeferred (no machine-readable spec exists for any partner; revisit when a partner publishes XSD / OpenAPI / WSDL) per ADR-030 vocabulary. Pre-flight scan confirmed zero machine-readable schema artifacts (XSD/OpenAPI/WSDL/JSON Schema) exist anywhere in the repo; all 17 interface docs are AsciiDoc prose only. Hand-written per-partner crates from Step 3 remain canonical. Revisit criterion + recommended tooling (quick-xml/progenitor/okapi) + recommended pilot target (STARS) all documented (closes #445). Step 7 (F-063 mock-server adapter integration, 2026-06-08): 10 sequential MRs converting per-partner mock modules to typed Json<<Adapter>::Outbound>Json<<Adapter>::Inbound> shapes + per-partner sentinels + 3 end-to-end round-trip integration tests per partner against craig_mock_server::spawn_for_test() ephemeral-port helper. PILOT CAPS (!637) + WIC (!638) + IONS (!639) + CPRS (!640) + DOE-SLDS (!641) + EMPI (!642) + IES (!643) + SMILE (!644) + STARS (!645) + TCM FINAL (!646 / 14399f3d). All 10 partner mocks typed; mock-server no longer orphaned (closes #446). Step 8 (F-064 retire partner-edge Value carve-out, 2026-06-08): !650 / 1c11e624 — final sweep retires all 23 // PARTNER-EDGE-UNTYPED: markers across 11 files in 3 groups (6 stale removed from per-partner adapter doc-comments; 12 by-design converted to STRUCTURAL-VALUE: SHINES catch-all by design on StandardAdapter/NoopAdapter/map_person; 5 redundant dropped on intake validation + BFF view-model). grep -rn "PARTNER-EDGE-UNTYPED" services/ crates/ returns zero matches. Plan I F-037 partner-edge carve-out formally closed (closes #447). Step 9 (plan completion audit + archive): this MR (closes #448). Plan-completion audit dispatched via fresh Explore subagent (per the plan-completion-audit bias) caught Step 1 date drift (2026-05-25 → 2026-05-15) + Step 7 future-tense #446 closes upon merge → past-tense; both tightened before archive. Manual archive workflow because Step 6 carries Deferred (…​) ADR-030 token, which the cargo xtask docs plan-archive helper’s plan_fully_done() predicate doesn’t accept (treats only Done + N/A as terminal — the Deferred token IS valid; forcing Done would lie about what shipped). Plan L final results: 10 typed per-partner crates (caps/cprs/doe-slds/empi/ies/ions/smile/stars/tcm/wic) + 1 contracts crate + 1 audit aggregator crate; ExchangeAdapter trait redesigned from Value/String-error to associated-types + typed error; AnyAdapter dispatch wrapper covers 11 variants (10 typed + Shines catch-all + Noop test-injection); audit_typed() produces PartnerAuditEvent discriminated-union; mock-server typed-shape; B3a serde_json::Value count 178 → 177; zero PARTNER-EDGE-UNTYPED markers anywhere. Plan I F-037 partner-edge carve-out retired. Storage layer + codegen pilot deferred with documented revisit criteria. Successor work (when triggered): partner_audit_events table + storage layer (Plan L Step 5 successor — needs a real consumer of audit_typed); codegen pilot (Plan L Step 6 successor — needs any partner to publish a machine-readable spec); Plan G Step 6 cross-handler DRY scan (#462 unblocks now that Plan L converged — Plan G can archive next).

Plan R — Function-Cohesion Discipline

7 step MRs across 2026-06-07 closing epic &42 + issues 540-#546. Step 1 (filing + §Style "no and" doctrine amendment): !628 / d8619002. Step 2 (NEW xtask lints fn-name-and lint scaffold + 18-entry baseline opt-out, report-only): !629 / b3a667b1. Step 3 (baseline sweep — 18 → 11 entries via 4 decompositions + 3 renames + 11 keeps with inline rationale:): !630 / 14d89f26. Sites cleared: parse_and_validate_headerdecode_jws_parts + assert_header_invariants; parse_and_validate_partner_bodyparse_partner_body; sanitize_and_validate_submissionprepare_submission + 2 helpers (Issue 492 order preserved); auto_link_and_auditdrive_auto_link_pipeline; propagate_cache_rename_and_notifyswap_compiled_decision_if_changed + broadcast_ruleset_update_events; log_and_generic_500emit_internal_500; reconcile_ports_env_and_heal_driftreconcile_ports_env_safely. Step 4 (flip fn-name-and to blocking via NEW [4k/14] validate step + coding-conventions.md § Function cohesion enforcement gate lock-raise procedure subsection mirroring § Quality-budget enforcement gate): !631 / 65e0478a. Step 5 (clippy::cognitive_complexity per-site allow audit — 0 lifts, 0 decompositions, 11 keeps confirmed the rationale-required [expect] discipline from Plan H Step 9 + Plan M Tier 2 + Plan N Step 6 is working as intended; no stale debt accumulated): !632 / 701483d0. Step 6 (NEW xtask lints fn-shape-report advisory diagnostic — 3 signals over 625 production files / 3952 fns produced 8 advisories, all documented patterns; no follow-on decomposition MRs needed): !633 / b9c5baa0. Step 7 (audit + archive, this MR). Plan R final state: workspace function cohesion mechanically enforced via 3 gates — [4k/14] fn-name-and (blocking, 12-entry opt-out with # rationale: discipline), clippy::cognitive_complexity = "deny" workspace-wide (already enforced since Plan H; Plan R Step 5 confirmed all 11 production per-fn allows are documented #[expect] carve-outs), xtask lints fn-shape-report advisory audit (always-clean baseline per Step 6). 23 new unit tests across fn_name_and_tests + fn_shape_report_tests. New deps: quote = "1" (xtask-only). Successor plans: Plan L Step 2b+ (partner-interface scaffolding) → Plan G Step 6 (cross-handler DRY scan; #462 unblocks after L convergence).

Plan N — Quality-Gate Enforcement

12 step MRs across 2026-06-06 → 2026-06-07 closing epic &40 + issues 514–#525. Step 1 (filing): !597 / cb1cbcd6. Step 2 (Phase A foundations — xtask/src/cmd/quality_budgets.rs let ceiling = if locked > 0 { locked } else { threshold } semantic fix stops B1+B2 silent masking; comment-skip mechanism for SILENT-OK / PARTNER-EDGE-UNTYPED / STRUCTURAL-VALUE markers; report-only this step — gate flip deferred to Step 9): !598 / 37395b3c. Steps 3–7 Phase B sweeps to bring all 7 OVER budgets at-or-below lock: Step 3 B7 untyped test-client (3 sites) !599 / a5ca406a; Step 4 B3b serde_json::Value in tests (32 sites, 28 STRUCTURAL-VALUE markers) !600 / ef862010; Step 5 B5 unwrap_or_default (212 SILENT-OK markers across 90 files; lock 175→10) !602 / e9dd2eb0; Step 6 B4 [allow] mass [allow][expect] conversion (309 sites flipped, 75 reverted after unfulfilled_lint_expectations; lock 142→87) !603 / 59588bbb; Step 7 B3a serde_json::Value in src (270 markers across 107 files; lock 236→178) !604 / 89555a45. Step 8 Phase C axis-coverage tagging: Step 8a !625 / 5c47d77f (workspace-wide scanner + 3 tagging mechanisms + [4j/14] validate step + 1071-entry baseline opt-out file); Step 8b !626 / e1140ddc (heuristic axis_sweep classifier applied — 322 tagged across 95 files / happy=107 sad=202 evil=13; opt-out 1071 → 749 entries). Step 9 Phase D enforcement-gate flip — [4i/14] blocking via enforce_quality_budgets() helper; .gitlab-ci.yml ci-tests gains the same check; quality-budgets.toml flipped to "ENFORCED"; .claude/docs/coding-conventions.md § Quality-budget enforcement gate documents the lock-raise procedure: !622 / 64ab6caa. Step 10 Phase E CI parity — --skip-devstack flag + enabled_phases() planner + phase_ci_unit_tests substitute; .gitlab-ci.yml ci-tests collapsed to single cargo xtask validate --skip-devstack --skip-docker; 2 parity-smoke fingerprint tests: !623 / f430f7db. Step 11 Phase F1 hooks-exec-check — defense-in-depth CI gate for the lost-exec-bit failure mode on .githooks/; triggers MR pipelines touching .githooks/ + every main-branch push: !624 / 2c9892c3. Step 12 Phase F2 audit + archive: this MR. Plan N final state*: 8 quality-budget gates LOCKED at strict-no-grandfather floors (B1=8, B2=15, B3a=178, B3b=31, B4=87, B5=10, B6=65, B7=194); 2 new validate steps [4i/14] quality-budgets + [4j/14] axis-coverage; CI runs the same lint battery as pre-push via cargo xtask validate --skip-devstack --skip-docker; quality-budgets-enforcement gate authoritative whenever lock set (no more silent masking); axis-coverage opt-out at 749 entries with monotonic shrinkage discipline; hooks-exec-check defense in depth.

Plan Q — Function Decomposition + Shared Abstractions Extraction

18 step MRs across 2026-06-06 → 2026-06-07 closing epic &41 + issues 526–#536. Step 1 (plan filing + §Style doctrine update): !605 / b719a770. Step 2 (NEW craig-bootstrap shared crate): !606 / fe34d5dd. Step 3 (service mains to final form across 4 substeps + batch-1 adoption): !607 batch-1 cases/exchange/security partial adoption / deeb5276; !608 (3a) bootstrap extension + build_authz_source + retrofit / baa7542b; !609 (3b) placement/financial/reporting/rules batch-2 forward-migrate / 812adfea; !610 (3c) setup_workers/setup_router extraction across 7 stateful mains (B2 56→49 LOCKED) / 1fc3fc04; !611 (3d) intake N/A rationale + craig-web router extraction / ed42a4a9. All 9 service mains in final form ≤ 50/80 LOC. Step 4 (SqlRow trait + seed builder refactor): !616 / e420b416sql.rs 1245 → 131 LOC (-89%); 34 impl SqlRow for X blocks; 4 hash-pinned tests (cases/placement/exchange/financial; reporting/security skipped due to pre-existing Utc::now() non-determinism in datagen.rs); B2 36→31 LOCKED. Step 5 (identity render Manifest extraction; deviated from plan body — per-section sibling-fn extraction shipped instead): !613 / 8234e251 — zitadel 244→14 / kanidm 236→17 / authentik 213→13; 4 hash-pinned regression tests; B2 49→46 LOCKED. Step 6 (CLI fat-arm decomposition, 3 batches): !617 batch 1 (case/home/exchange/financial/placement) / ce1aa035B2 31→26 LOCKED; !618 batch 2 (icpc/health/reporting/security/plan) / a5de4c30B2 26→21 LOCKED; !619 batch 3 (person/education/agreement/investigation/intake/rules) / b854621aB2 21→15 LOCKED. Total: 16 CLI run-fn sites cleared; 2638 LOC of fat-arm code decomposed into thin dispatchers + 107 per-arm helpers + 107 Args structs; CRUD trait deferred (helper signatures too heterogeneous). Step 7 (xtask command run decomposition): !614 / b2e8210b — 9 sites; validate::run 345→19 LOC + 6 phase fns; security::run/run_infra_tests/run_path_fuzzing decomposed; migrate::rollback 144→44; e2e::run 118→10; api_docs::render_service_page 118→17; identity/verify::run_with_probes 109→43; devstack_guard::check_staleness 112→25; B2 46→38 LOCKED. Step 8 (B1 service-api module decomposition): !612 / 02ccdb0c — 3 borderline B1 modules: placement/api/health.rs 504→409, cases/api/court_orders.rs 511→439, exchange/api/partners.rs 518→406; 3 NEW sibling helper files; B1 11→8 LOCKED. Step 9 (residual large-fn decomposition + B2 counter improvement): !615 / 4aa191c7ApiError::into_response 131→68; craig-api::bootstrap 136→50 via 4 helper extractions; B2 counter extended to skip [test]/[tokio::test]/[cfg(test)] fns; B2 38→36 LOCKED. Step 10 (lock ratchet + Plan N handoff): !620 / 7e701d73xtask/quality-budgets.lock ratcheted B2 49→15 LOCKED, B1 unchanged at 8 LOCKED; cargo xtask quality-budgets --fail-on-regression exits 0; cross-link comment posted on Plan N issue #522 marking Step 9 enforcement-flip precondition CLEAR. Step 11 (audit + archive): this MR. Plan Q final results: 1 new shared crate (craig-bootstrap); 51+ functions decomposed; 21 B2 sites cleared by Plan Q itself (in addition to 13 cleared by Plan N Steps 5-7 and Plan Q internal steps); B2 56 → 15 LOCKED; B1 11 → 8 LOCKED; service mains 200-300 LOC → ≤ 50 LOC orchestrators (~75% reduction); §Style "decompose unless decomposition makes the codebase worse" doctrine retired the "important function" carve-out. Plan N Step 9 (enforcement gate flip) is now unblocked.

Plan P — Nursery Promotion Sweep

9 step MRs across 2026-06-05 → 2026-06-06 closing epic &38 + issues #505–#513. Step 1 plan filing (filed in Plan M Step 13 !583). Step 2 (#505/!584) — use_self sweep, ~50 sites across 11 files in 8 crates; workspace flipped allow→deny. Step 3 (#506/!587) — too_long_first_doc_paragraph sweep, ~90 sites across 2 cascade rounds in craig-auth + craig-cli/clients (round 1: 44 sites) + craig-api + craig-authz + craig-test-lib (round 2: 46 sites); 2 crate-level allows added for macro-expansion false-positives (craig-auth + craig-api); workspace flipped allow→deny. Step 4 (#507/!591) — future_not_send audit. Real surface 140+ emissions across 5 architectural site classes ALL intentionally !Send by design (craig-cli/clients/ 33 with &Transport borrows, craig-test-lib/harness 98 with &self methods, craig-test-lib/client 4, craig-authz/eval_thread 1 per ADR-024, craig-web/routes 3). Workspace allow retained permanently with comprehensive rationale per ADR-031 § 1. Step 5 (#508/!589) — derive_partial_eq_without_eq sweep, 33 sites in craig-cases-contracts (32) + craig-store/config (1); workspace grep confirmed ZERO float-equivalent fields; Eq derive added at every site; workspace flipped allow→deny. Step 6 (#509/!588) — missing_const_for_fn audit. 38 emissions across craig-common/settings + craig-db + craig-mq + craig-partner-* adapters; workspace grep confirmed ZERO callers use these in const context; workspace allow retained permanently. Step 7 (#510/!585) — or_fun_call long-tail sweep, 6 sites across craig-exchange + craig-rules tests + craig-web + xtask; workspace flipped allow→deny. Step 8 (#511/!590) — redundant_clone sweep, 15 sites total (5 craig-auth + 10 cascade across craig-authz tests, craig-test-lib tests, craig-exchange/adapters, craig-intake/{main,sink}, craig-web/{auth,routes}, xtask/cmd/{coverage_matrix,identity,migrate}); workspace flipped allow→deny. Step 9 (#512/!586) — long-tail batch (6 single-emission lints flipped allow→deny: needless_collect, unused_peekable, suboptimal_flops, unnecessary_lazy_evaluations, collection_is_never_read, single_option_map, suspicious_operation_groupings; 2 retained allow with architectural rationale: redundant_pub_crate for CRAIG’s Plan D F-023 1,065-item pub(crate) discipline + option_if_let_else for 3 mock-server multi-line handler match-arms + 1 nursery false-positive on PageResponse<T>). Step 10 audit + archive (this MR). Final workspace state**: nursery = deny workspace-wide + 12 lints flipped from allow to deny + 4 workspace allows retained with documented architectural rationale (missing_const_for_fn, option_if_let_else, redundant_pub_crate, future_not_send) + 2 crate-level allows for macro-expansion false-positives (too_long_first_doc_paragraph in craig-auth + craig-api). Successor plans deferred per ADR-031 § 5 reusability principle.

Plan M — Quality Lint Followthrough

48 MRs across 2026-06-04 → 2026-06-05 closing epic &37 + issues 493–#504. Step 1 (plan filing): !535. Tier 1a–1e (lift the Plan H Step 2 transitional ![allow(clippy::pedantic, clippy::cargo, …​)] blocks on 38 production source roots): !536–!541 (Tier 1a 6 foundational crates / 493), !542–!551 (Tier 1b 10 contracts + shared crates / #494), !552–!559 (Tier 1c 8 backend services / #495), !560–!567 (Tier 1d 8 leaf binaries + 7 partner stubs / #496), !568 (Tier 1e cargo xtask lints no-transitional-allows regression gate at xtask validate [4h/14] / #497). Tier 2 (4 new strict clippy denies): !570 (Step 7 — indexing_slicing + string_slice + unwrap_in_result panic-surface / #498), !576 (Step 8 — arithmetic_side_effects overflow-surface / #499), !572 (Step 9 — print_stdout + print_stderr IO-surface / #500), !574 (Step 10 — partial_pub_fields struct hygiene / #501). Tier 3 (rustc lints): !579 (Step 11 — unused_must_use = "deny" + unsafe_code = "deny" + strip 33 per-crate ![forbid(unsafe_code)]; unused_crate_dependencies evaluated and DEFERRED at workspace allow because 434 per-target false positives dominated by [dev-dependencies] fan-out / #502). Tier 4 (nursery probe): !581 (Step 12 — clippy::nursery = deny workspace-wide; 24 sites swept across 9 crates; 17 lints allow-listed at workspace priority 1 with documented rationale; ADR-031 Accepted records the <10-same-MR-threshold promotion rule + nursery-stays-deny default + Plan P successor-plan handoff discipline / #503). Step 13 (audit + archive): this MR. Docs Status refreshes: !569 (Steps 2–6), !571 (Step 7), !573 (Step 9), !575 (Step 10), !577 (Step 8 + Tier 2 COMPLETE), !580 (Step 11), !582 (Step 12). Durability handoff: !578 (Plan M-era lessons promoted into .claude/docs/coding-conventions.md § Lint Policy + .claude/docs/known-issues.md — allow vocabulary, cfg_attr+crate-allow collision, validate step letter-suffix convention, force-merge 405 while preparing, workflow scout-agent misuse, grep-overcount estimation). Workspace lint count after Plan M: 17 clippy denies + 2 rustc denies + nursery group at deny with 17 documented escape hatches. Plan G Step 6 / #462 cross-handler scan remains BLOCKED (Plan M didn’t change the gating clause). Successor: Plan P — Nursery Promotion Sweep (deferred per-lint sweeps for use_self, too_long_first_doc_paragraph long-tail, future_not_send !Send-audit, etc.).

Plan H — Idiomatic Rust + Clippy Strictness

16 MRs across 2026-05-15 → 2026-05-21. !311 (Step 2 lint set), !362 (Step 3 burn-down), 7 MRs (Step 4 F-030 panicking-call sweep), !361/!364/!365/!366/!368/!371/!372/!373/!377 (Step 5 typed-everywhere), !358 (Step 6 sync/async), !359 (Step 7 silent-discard), !346/!350 (Step 8 parking_lot), !360 (Step 9 interior-mutability), !370 (Step 9 Phase A lint flip), !376 (Step 9 Phase B lint flip). Workspace clippy now deny for 15 lints workspace-wide; zero panicking calls in production code; zero anyhow at API boundaries.

Plan G — DRY + YAGNI Cleanup

15 MRs across 2026-05-15 → 2026-06-10 closing epic &29 + issues #416-#420 + #462. Step 2 (F-026 AppState::execute_within_tx tx-boilerplate helper): !318 helper (10d0781) + per-service sweeps !319/!321/!323/!325/!327 — 37 of 45 sites migrated; 7 manual-handling sites preserved by design (compensation patterns, retry loops, FOR UPDATE re-reads); closes #416. Step 3 (F-027 publish-error-chain collapse): !329 helper + !331-!335 per-service sweeps — typed source variants on ApiError (Db/ObjectStore/Http/Csv/Uuid); 407 .map_err(ApiError::internal)? sites collapsed; net -489 LOC; closes #417. Step 4 (F-028 CSP test-fixture extraction): !336 — 3 async helpers in crates/craig-test-lib/src/csp.rs; 9 service test files collapsed; closes #418. Step 5 (F-029 config knob audit): !337 — all 7 flagged knobs proved real operator-tunables; promoted into .env.example + docs (zero DELETE candidates); closes #419. Step 7 (completion audit, executed 2026-05-17 with Step 6 still Blocked): closes #420. Step 6 (F-065 cross-handler DRY consolidation scan; gated on Plans D/G/H/I/J/K/L convergence): !671 / 53dc0d79 (2026-06-10) — 2-lens scan (thesis-driven category verify + unbiased 290-helper sweep across 127 files / 9 services) found exactly ONE ≥3-instance cluster: multipart upload parsing (5 sites) → NEW craig_api::multipart::parse_upload_multipart + thin per-site wrappers, behavior-preserving at the wire; honest non-findings for the other 4 candidate categories (tx1+tx2-audit 1 site; idempotent-create 1 site; PII orchestration already factored; actor-sub parsing dead) + 0 further clusters from the unbiased sweep; B1 ratchet 8→7 + B6 65→64; closes #462. Plan G final state: execute_within_tx + typed-ApiError-source error propagation + shared CSP fixtures + craig_api::multipart as the durable shared-helper surface; closing #462 cleared the LAST open step of the 2026-05 code-quality plan slate (D/G/H/I/J/K/L + M/N/P/Q/R) and unblocked Plan T2 execution (T2.1 hard gate per D4).

Plan D — Code-Quality Discipline (strum / pub(crate) / silent-skip / cargo-deny)

31 MRs across 2026-05-08 → 2026-05-25. Step 1 (plan filing): !230. Step 2 (strum inventory): !433 — 1,568 fields surveyed; 195 CONVERT-EXISTING, 32 unique CONVERT-NEW (14 substantiated + 18 newly-discovered), 1,137 KEEP. Step 3 (F-022 phase 1 CONVERT-EXISTING, 4 batches + wrap-up): !434/!435/!436/!437/!438 — 111 fields tightened across craig-cases-contracts (21), craig-intake-sdk (18), craig-seed (31 + 16 const arrays), craig-web (41 view-models). Step 4 (F-022 phase 2 CONVERT-NEW, 3 phases): !439 cases-internal (4 enums), !440 exchange+financial (7 enums incl. SendJobStatus NEW), !441 placement+security (7 enums incl. PartnerKind/PartnerStatus/SignerKeyStatus NEW). 18 NEW enums shipped total. Step 5 (F-023 pub→pub(crate)): 10 first-pass sweeps !417–!425 + 7 retroactive async-fn follow-up !426–!432 covering pub async fn/pub const fn grep gap (plan body grep amended in !426); 1,065 pub→pub(crate) conversions. Step 6 (F-024 silent-skip): 10 sweep MRs !405–!414 + lint MR — 717 patterns removed; cargo xtask lints no-silent-skips blocking gate at xtask validate step [4d/14]. Step 7 (F-025 RUSTSEC ignore audit): !379 — 3 of 4 ignores dropped as advisory-not-detected; RUSTSEC-2023-0071 retained. Step 8 (audit + archive): this MR.

Testing & Documentation

Plan MR(s)

Contested-Environment Component Testing (epic &83)

!1413/!1414 (C0–C1 docs-first) + !1415–!1424 (C3–C12 foundations, injectors, transport suite, burst driver) + !1425–!1431 (C13/C14 fleet sweeps: 11 inbox handler-contract legs + 8 outbox round-trips) + !1432/!1435–!1442 (C15–C23: S2S matrix, partner triple, auth-plane, CLI, worker crash sweep, cluster rig, pool contention, degraded-UX e2e, the registry ratchet going blocking) + the C24 close-out MR (Closes #1498–#1519) — 2026-08-21 (25 units, ~97 weight, in four days. Adversity became a DETERMINISTIC test input across the whole codebase: two fault layers (native L1 + Toxiproxy L2) with the two-path rule machine-enforced per MQ consumer, a third cluster-scoped L3 (docker kill), a typed contested-surfaces.toml registry whose census promotes to BLOCKING per class, and a program gate that verifies a WORKING SYSTEM — fault layer REQUIRED, every armed fault fired, a this-run record stamp, the two self-tests pinned by name. The legs paid for themselves in defects: ~20 real issues filed and NOT folded into feature MRs (#1523–#1545 incl. the #1528 AMQP-establishment hang fixed in-flight as !1434). Honest residue recorded rather than papered over: nine classes do not yet emit fault records (#1546), the recorder’s CWD scatter (#1524), and seven hand-enumerated classes stay report-only with their promotion criterion in code. Merging C24 opens the #1466 (B5) start gate.)

Battery Isolation — Detection Standing-Alert Race + Contention Strata (#1167 program)

!1080 (U1 #1172 detection race: partial-unique arbiter + lock-first migration) + !1081 (U2 devstack scheduler off + security-detection-scan group) + !1082 (U3 clone-measurement exoneration record + U4 financial owned-payment fixtures) + !1083 (U5 auth-cookies dependency ordering + config-guard pin) + the U6 close-out MR — 2026-07-27 (epic &73 child; born from the audit’s contention findings, reshaped by a ~25-finding external stop-ship review of v1. Outcome measured by the U6 paired protocol: 0 strict-dominance regressions over 4694 tests, Σ median deltas −46.6 s, warm battery wall ≈18% faster; D7 event-heavy grouping REJECTED on the data; follow-ups #1173/#1174/#1175.)

Comment-Accuracy Audit Remediation

2026-07-16 (epic &64 — 421 confirmed stale comments remediated: all 25 step issues #908–#932 adjudicated (B2a/B2b/B2c bundles c6be0dd5/effe418e/6721d3f2, per-crate sweeps !991–!996, fixes !877/!988–!990), the 27 uncertain findings adjudicated in #907 (!997), Step 1 closed as a recorded deferral — the wiring itself lives on in re-scoped #908, still open under the epic. Closing MR: this one.)

Case Routing Rule Set

pre-MR (Status table all Complete)

Plain-Language Documentation

pre-MR (Status table all Complete)

E2E Manifest Integration

!3

E2E Test Hardening

pre-MR (1f51922)

Detailed Implementation Guide

pre-MR (73e078c)

User Testing Guide

pre-MR (d135b40)

Documentation Consistency Update

!38

Project Instructions Restructure

!11

Scalable, Deterministic Seed Data Generator

pre-MR (295cb45)

Ruleset Auto-Discovery Integration Test

!19

Comprehensive Documentation Overhaul

!38

Architecture

Plan MR(s)

Plan T1 — Foundations & Traits (Plan T sub-plan)

9 MRs across 2026-06-09 → 2026-06-10 closing epic &51 + issues #560 + #562-#569. T1.1 SHINES placement decision LOCKED — stays catch-all StandardAdapter at the host service; bundles contribute typed factories only (!662 / a0c02714). T1.2 ErasedAdapter object-safe trait + ErasedAdapterError (Serde/Inner/AuditUnsupportedForKind) + two-arm impl_erased_adapter! macro ($crate-hygienic) + BoxFuture re-export in craig-exchange-contracts (!663 / 06a5edb7). T1.3 AuditCodec write-side trait + 10 unit-struct codecs in craig-partner-audit per the Rust orphan rule; SHINES fail-closed absent per the closed PartnerAuditEvent aggregator (!664 / b1f1a8a8). T1.4 FederalPartnerCategory 9-variant enum in craig-reference (no sqlx::Type — closed-by-federal-design) (!665 / 5e2f56e5). T1.5 NEW crates/craig-state-bundleStateBundle trait (name / jurisdiction_code / contribute / federal_mapping) + BundleContribution (5 Phase-1 fields) + BootContext { http_client } + 5 from_bundles registries with typed BundleMergeError::DuplicateEntry detection (!666 / 00843cab). T1.8 10 per-partner audit_payload_from_value typed decoders + codec-body swaps — executed before T1.7/T1.6 per §Step DAG (!667 / 09fea89f). T1.7 macro adoption: 10 typed-partner + 1 SHINES-passthrough invocations; StandardAdapter gains send_with_format + audit_with_format direct methods (format field stays until T2.1); #462 soft gate waived by reviewer in-session, cited in the MR description (!668 / 9c8916e6). T1.6 NEW crates/craig-state-defaultDefaultBundle with 10 adapter factories (SHINES orchestrator-injected at T2.1), 10 audit codecs, 14 partner_type tokens verbatim from the migration CHECK, 5-entry Phase-1 federal mapping (!669 / 0a6e9437). T1.9 plan-completion audit + archive (this MR) — audit subagent PASS on all 6 checklist items. 82 new tests across the plan; workspace 25 → 27 members. Successor: Plan T2 (Registry Migration); execution gated on F-065 / #462.

Plan T2 — Registry Migration (Plan T sub-plan)

5 MRs on 2026-06-10 closing epic &52 + issues #570-#574. T2.1 AnyAdapter → registry dispatch: NEW bundle_orchestrator.rs + ExchangeRegistries::resolve single dispatch seam (noop:// sentinel inline per A3; SHINES orchestrator-injected after bundle merge); test_connectivity_value added as 4th ErasedAdapter method; audit_typed/dispatch_audit_typed/config_for_kind deleted with the enum wrapper (!673 / 3ef30320). T2.2 encode_audit registry seam keyed on the RESOLVED adapter.kind() per the identity contract — noop-substituted dispatch fails closed (!674 / d0588d1d). T2.4 (before T2.3 per the DAG) chk_exchange_partners_adapter_kind CHECK dropped; boot registry-membership validation in the orchestrator (UnregisteredAdapterKinds typed boot error; direct-SQL TOCTOU-closure devstack proof per §1.3); lockstep test → registry_inventory_matches_adapter_kind_enum (!676 / 0a427cdd). T2.3 mock-server → MockRouterRegistry: 10 mock modules moved to crates/craig-partner-*/src/mock.rs behind per-crate mock features; craig-state-default/mock fans out + populates mock_routes; routes nest at /partner/<kind> VERBATIM per §2.5; MockState deleted per D8; DEVIATION — craig-mock-validation crate NOT created, validation.rs deleted instead (zero consumers post-Plan-L typed migration); 30 round_trip sites + 7 seeded URLs swept; exchange seed hash re-blessed (!677 / 9af1c9f0). T2.5 plan-completion audit + archive (this MR). Successor: Plan T3 (Open Closures & Hardening); execution gate CLEAR.

Plan T3 — Open Closures & Hardening (Plan T sub-plan)

8 MRs on 2026-06-10/11 closing epic &53 + issues 575-#581 (+ follow-up #583). T3.1 ExchangeAdapterKind enum DELETED from craig-exchange-contracts; open String token carriage end-to-end (DTOs/store/resolve/partner-audit); temp 11-token write-path validator; wire change 422 plain-text → 400 RFC 9457 INVALID_ENUM_VALUE (!679 / 59bd3c92). T3.2 registry-driven validate_adapter_kind (const deleted); NEW /registry/adapter-kinds + /registry/partner-types discovery endpoints; CLI --adapter-kind (!680 / 38b35d44). T3.3 PartnerType enum DELETED from craig-reference; 14-token validator (deliberate 5→14 API widening); web new-partner form fed from the registry endpoints via a new fetch_json BFF helper (!681 / 20cc658b). T3.4 FederalPartnerMappingRegistry report-emit seam in craig-reporting (NEW ReportingError; resolve_federal_category with [cfg_attr(not(test), expect(dead_code))]; follow-up #583 for row-emit wire-up) (!682 / 848dbc3c). T3.5 chk_exchange_partners_partner_type CHECK dropped; all-rows boot membership validation for both token columns (resurrection hole closed) + effective-row update validation; craig-reference validate_partner_type deleted — registry is sole source of truth (!683 / 09a49395). T3.6 last georgia seed default removed — required --jurisdiction (clap env, non_empty_string parser) threaded Cli → SeedConfig → SeedGenerator; all invocation paths updated; Plan Q SQL hash-pin byte-identical (!684 / c32477de). T3.7 open-token evil-corpus sweep at NEW tests/security/ (workspace’s first); adapter_kind + partner_type text-shape categories + registry-endpoint probes — 0 validator gaps; lockstep sweep clean (!685 / 8b17286b). T3.8 plan-completion audit + archive (this MR) — audit caught + fixed 2 unresolved MR-cite placeholders. Successor: Plan T umbrella audit + archive (umbrella Step 7).

Plan T — Adapter Registry Pivot (umbrella; sequences T1/T2/T3)

Parent umbrella for the adapter-registry pivot — sequenced 3 sub-plans (T1 Foundations / T2 Registry Migration / T3 Open Closures), each authored + executed + archived in its own plan-mode session (epics &51/&52/&53; 22 MRs — T1 9 + T2 5 + T3 8 — spanning !662 → !686, with !671/!672/!675 belonging to other plans in that range; 2026-06-09 → 2026-06-11). Net result: the closed ExchangeAdapterKind + PartnerType enums and the AnyAdapter runtime switch are gone; adapter + partner-type + audit-codec + mock-route + federal-mapping identity is bundle-contributed via craig-state-bundle registries with open String token carriage and registry-driven dual-site (write-path + boot) validation. Umbrella Step 7 audit+archive (this MR) — fresh plan-completion-audit subagent PASS on all 7 checks; parent epic &45 closed. Plan S Step 9 → Done; Plan S Phase 3 continues with Plans U/V (transport) + W/X/Y (UI composability).

Plan U — State Bundle Pattern (Plan S sub-plan)

13 MRs across 2026-06-12 → 2026-06-15 closing epic &46 + issues #584-#595. Body !690 / a8c51f7f. Opened the state-bundle activation seam Plan T1 built and made CRAIG state-neutral. U1 neutral 14-token partner taxonomy → craig-state-bundle (!691 / a56895ec). U2 theme + terminology contribution types + product-default palette, grown additively on BundleContribution (!692 / 585468ab). U3 keystone rename craig-state-defaultcraig-state-ga (DefaultBundleGeorgiaBundle, name() → "georgia") (!693 / 19cbd450). U4 NEW craig-state-tx-stub minimal second bundle (!694 / 1d86f38a). U5 Cargo feature gating state-ga/state-tx-stub across exchange/reporting/mock-server (!695 / a791ac99). U6 cargo xtask feature-matrix subcommand + CI job + fingerprint drift test (!696 / 02548e56). U7 CRAIG__ACTIVE_STATE_BUNDLES activation + ADR-032 §2.7 boot fail-fast; pure resolve_active_bundle in craig-state-bundle (!697 / 5aa645d6). U8 theme per ADR-036 — GET /assets/theme.css, build.rs palette codegen, 47 screenshots regenerated (!698 / 3ae01701). U9 terminology per ADR-034 — bundle-overlaid Fluent catalogs, fallible I18n::load, shared bundle.rs resolver (!699 / 9ab900e7). U10 state-aware seed — SeedContribution.rate_tables; craig-seed joins the 5-crate feature matrix (contract complete); --jurisdiction removed (active bundle = source of truth) (!700 / 9caeca60). U11 NEW multi-jurisdiction-extensibility.adoc how-to-add-a-jurisdiction guide (!701 / 4fba69db). U12 plan-completion audit (CLEAN — all U0-U11 cites verified) + archive (this MR); epic &46 closed. Net: a second jurisdiction (tx-stub) builds + boots with zero changes to the consuming crates; theme + terminology + seed axes ship. Successor: Plan V (Transport + Substrate, epic &47).

Plan V — Transport + Substrate (Plan S sub-plan)

8 MRs on 2026-06-15 closing epic &47 + issues #597-#604. Body !703 / 2a6b7b57. Opened the last partner-axis seam ADR-032 §3.1 specifies — outbound transport. V1 NEW crates/craig-exchange-transportOutboundTransport trait (send/probe) + value-objects + DirectHttpTransport + recording StubTransport (test-util), co-located per craig-store; reqwest private (!704 / 84c2461b). V2 ATOMIC swap — the 10 partner adapters + the SHINES StandardAdapter + BootContext (http_clienttransport) + the craig-state-ga factory macro + the orchestrator route through Arc<dyn OutboundTransport>; explicit per-arm error mapping; reqwest dropped from the 10 partners + craig-state-bundle (exchange service keeps it); ADR-038 §Companion-BootContext + ADR-032 A4 amended "replace not add"; 10 byte-identity StubTransport tests (!705 / 28d31ef6). V3 WebMethodsTransport runtime broker stub (/invoke/<folder>:<service> envelope; probewm.server:ping) + host-owned /broker mock route; shared wire.rs reqwest-error helpers (!706 / 0abc59dc). V4 mock fault injection — spawn_for_test_with(MockServerOptions) + host-level from_fn_with_state layer for Latency/Status/MalformedBody (TCP-reset deferred) (!707 / 582a8d1b). V5 request inspection — capture.rs always-on capture middleware + MockServerHandle::captured()/last_request_to (wire-level byte-identity twin) (!708 / 3c38dd72). V6 proptest (transport status-split totality + caps round-trip/parse-never-panics) + NEW crates/craig-partner-caps/fuzz/ libFuzzer crate (repo’s first; workspace-excluded, nightly-only) + optional non-blocking fuzz-smoke CI (!709 / 556c265b). V7 per-partner interface-doc predicates (all 10 — caps pilot + 9-agent workflow) + the == Partner coverage matrix audit + probe-verb §Open-question (!710 / 472613ae). V8 plan-completion audit (CLEAN — V1-V7 cites verified) + cross-cutting-invariant verification + archive (this MR); epic &47 closed. Net: the wire path is a single Arc<dyn OutboundTransport> seam — a jurisdiction routes through its own broker by swapping the transport, with no change to the partner crates; reqwest is contained behind the seam; the mock substrate is broker-faithful (fault + inspection + fuzz). Successor: Plans W/X/Y (UI composability, epics &48/&49/&50).

Plan W — Plugin Manifest + Render Runtime (Plan S sub-plan)

8 MRs on 2026-06-16 closing epic &48 + issues 605-#612. Body !713 / 3555d395. The server-side plugin runtime for the craig-web BFF (ADR-033): source-agnostic plugins register a sync-pure render fn into a linkme slice; the host fetches the manifest-declared data + the plugin renders pure (four-state). W1 NEW crates/craig-plugin-contractsPluginManifest + the serializable render contract (RenderCtx/FetchOutcome/RenderedFragment/PanelState) + the [async_trait] PluginSource seam + the CRAIG_PLUGINS linkme slice + the boot-validated PluginRegistry (the repo’s first linkme use) (!714 / 82f9f250). W2 NEW crates/craig-plugin-macros — the [craig_plugin(slug, manifest)] proc-macro (CRAIG’s first first-party proc-macro) emitting the registration carrying slug + include_str!’d manifest + the render fn (registry-driven dispatch, no `match) (!717 / 0e6328b1). W3 craig-web boot materializes + cross-axis-validates the PluginRegistry into AppState (slug uniqueness + required_role ∈ the realm set + display_name {term.*} resolves against the materialized I18n); craig-state-bundle untouched (ADR-032 A12) (!718 / e9d4baf4). W4 four-state-contract build-time lint ([4m]) — plugin templates using the {% if state == "…" %} idiom must cover data/empty/error; loading is shell-owned (!719 / cbd49277). W5 BFF host-fetch render runtime — PluginFetcher (SSRF-guarded host allow-list, per-auth-mode, per-request timeout) + PluginCache (TTL, single-flight, Error never cached) + GET /plugins/<slug> inside protected_routes (!720 / 608ba7c6). W6 NEW plugins/example/ reference plugin (first [craig_plugin] consumer; opt-in plugin-example feature + CRAIG_WEB_FEATURES Dockerfile build-arg + linkme force-link); SSRF guard relaxed to permit http for allow-listed hosts (the BFF already calls backends over http; allow-list stays the anchor); term-open-cases added to GA; config list-parse fix for the plugin_allowed_hosts Vec (caught by the pre-push devstack health check) (!721 / 705826db). W7 feature-matrix plugin axis (state-ga/tx-stub × plugin-example) + plugin-runtime docs (shared-crates + architecture, both tiers) + CLAUDE.md member-count → live 49 (!722 / 9cbd54ed). W8 plan-completion audit (CLEAN — all W1-W7 cites verified; 10/10 cross-cutting invariants) + archive (this MR); epic &48 closed. Net: the BFF gained a stable, source-agnostic plugin seam (v1 linkme, v2 WASM with no contract change) — a jurisdiction ships UI panels as feature-gated plugins/ crates with zero changes to craig-web; the reference plugin proves the macro → registry → host-fetch → four-state pipeline end-to-end. Successor: ADR-035 → Plan X (Composition Layer Engine, epic &49).

Plan X — Composition Layer Engine (Plan S sub-plan)

10 steps across 2026-06-17 → 2026-06-18 closing epic &49 + issues #618-#627. Body !725 / 71a4f59c. The 5-layer composition engine (ADR-035) in a NEW backend service services/craig-composition (port 8009, DB craig_composition); the craig-web BFF becomes an HTTP client (ADR-004 stateless preserved). Ports canopy’s composition subsystem, adapted to CRAIG’s BundleContribution / PluginSource / Claims / craig-mq / craig-db. X1 NEW crates/craig-composition-engine — the pure merge (RFC 7396 + 6902 via json-patch) / role-filter / user_delta_v1 / RFC 8785+SHA-256 hash engine, compiled EMPTY product defaults, NO I/O (!726 / b7b88a17). X2 NEW services/craig-composition host — the ninth backend (boot + root-Dockerfile target + compose + DB); a REAL Plan E principal + the engine lib renamed craig-compositioncraig-composition-engine (the bare name → the service) (!727 / 982e6952). X3 CompositionLoader + authz-gated GET /v1/compositions/{surface} (5-layer walk; boot-read baselines from rulesets/<j>/<surface>.toml; NEW ResourceType::Composition + ga/tx composition rulesets; role-filter-after-merge; ComposedItem.required floor) (!728 / 33253e5d). X4 the slug-anchored user_delta_v1 envelope + apply_user_delta + validate_user_delta (5 arms incl. the CRAIG no-required-hidden floor) in the pure engine (!729 / 43b9e608). X5 NEW composition_overrides table + db.rs (ordered fetch_db_layers + staged upsert_override with RFC 7232 ETag + monotonic guard; resolve async-shell + pure resolve_with_layers; required-floor snapshotted from the TRUSTED tier — the X5 review finding) (!730 / 5df51b0e). X6 per-replica cache + composition.invalidated RMQ fanout (insert-time TTL sweep that reclaims memory; transactional-outbox stage_invalidation; subscribe_exclusive per-instance auto-delete queue self-skipping by instance_id) (!731 / 0c53d305). X7 the BFF becomes an HTTP client (CompositionClient over the shared Transportclient_credentials + X-Craig-Actor, ADR-028); the dashboard renders a 12-col composed grid lazy-loading the Plan W GET /plugins/<slug>; a tests/architecture_invariants.rs grep enforces no sqlx/craig-mq/craig-db in the BFF (!732 / 3d6ae84f). X8 the jurisdiction-live override WRITE path (layer 3) + the Studio admin UI (PUT/GET /override; persist_override = upsert + stage-invalidation in one txn + cache eviction; per-role dry-run write validation; admin-gated authz; the dashboard-layout control). Caught + fixed a latent X2 miss — the missing aud-craig-composition Keycloak audience mapper on all 12 realm clients (!733 / c35864bf). X9 the self-service user-delta WRITE path (layer 1) + READ-APPLY + personalization UI (PUT/GET /user-delta; user_sub always the actor’s own; Action::Read self-service authz; the shared api/write_support.rs DRYing X8+X9+the read; ApiError::unprocessable). The adversarial review caught the read-apply gap — X3’s read had hardcoded user_sub: None, making the delta write-only; fixed so personalization survives reload (!734 / 8e6499d7). X10 GA reference baselines (rulesets/georgia/{dashboard,case_detail}.toml) placing the example panel — the first non-empty composed surface end-to-end; the reference plugin force-linked into craig-composition (plugin-example feature); deterministic sorted-first resolve_worker_role; plan-completion audit (CLEAN — all X1-X9 cites verified; 10/10 cross-cutting invariants) + archive (this MR); epic &49 closed. Net: a dashboard/case-detail surface is COMPOSED in config (5 layers, role-filtered, content-hashed) not built in code — a jurisdiction places plugin panels via runtime baselines, admins override jurisdiction-live via Studio, and workers personalize their own view, all with cross-replica cache coherence and the BFF staying a stateless client. Successor: ADR-037 → Plan Y (Field Ownership + Authz, epic &50).

Plan Y — Field Ownership + Authz Extension (Plan S sub-plan)

2 steps on 2026-06-19 under epic &50 (kept OPEN as the Phase-11 durable tracker). Body !739 / f85d1350. Re-scoped (user-approved): field ownership is intrinsically cross-tenant (state agency vs external provider), so a faithful enforced surface needs the Phase-11 CWCA provider portal as its real consumer + 2nd tenant. Plan Y therefore delivers ONLY the surface-agnostic craig-authz field-permission evaluation capability (ADR-037 §2/§3); the surface-bound application (§4–§7) is sequenced to Phase 11. ADR-037 is unedited — satisfied incrementally (Plan Y §2/§3 + Phase 11 §4–§7), the umbrella being the sequencing authority. Y1 the capability: a FieldPermission { Read, Write, Propose, None } enum + a NEW AuthzEngine::resolve_field_permission(&Claims, ResourceRef, field, field_owner) → Result<FieldPermission, ApiError> evaluation path (default-deny default impl + ZenAuthzEngine impl) reusing the existing ruleset/cache/invalidation infra via build_check_input(…, Action::Update) + field/field_owner injected into resource.attrs, parsed through a NEW disjoint field_permission output token (parse_field_permission_output) so the boolean check/auto_scope_list are untouched; proven against a fixture ruleset (the owner-classification × role → permission matrix incl. the provider token + the hitPolicy-first discriminator contract in both directions + cache-miss→None + malformed→Err + default-impl→None; 11 tests) (!740 / c56c00c2). Y2 plan-completion audit (caught + fixed the body’s "12 tests" → 11) + archive (this MR); registered resolve_field_permission + the long-absent craig-composition-engine (folds in #628) in shared-crates.adoc; filed the Phase-11 §4–§7 application issue (#634) under epic &50 (kept open); epic &50 stays OPEN. Net: the craig-authz field-permission evaluation surface the Phase-11 provider portal will call unchanged is live + fixture-proven; the surface-bound field-ownership application (the field_ownership bundle axis, per-surface ownership tables, pending_edits_<surface> queue + propose/accept/reject + field audit, BFF lock-icon + Studio) is deferred to Phase 11 under the still-open epic &50. Successor: umbrella Step 25 (program-completion audit + archive — closes Plan S).

Plan S — Multi-Jurisdiction Foundation (umbrella)

25 steps across 2026-06-08 → 2026-06-19 closing epic &44 (child epics &45–&49 closed; &50 kept OPEN for the Phase-11 field-ownership deferral). The umbrella that took CRAIG from a Georgia-shaped CCWIS to a state-neutral platform — the two final jurisdiction-variability axes before 1.0: the partner + broker dimension (Plan T adapter-registry pivot + Plan V transport/substrate, anchored by ADR-032 + ADR-038) and the UI-composability dimension (Plan U state-bundle pattern + Plan W plugin runtime + Plan X composition engine + Plan Y field-permission authz, anchored by ADRs 033–037). All five design-team engineering contracts (The Five Engineering Contracts) landed as additive BundleContribution axes. Six child plans authored + executed + archived in their own plan-mode sessions (Plan T = 3 sub-plans T1/T2/T3; epics &45–&50); ADRs 032–038 filed. Net: a second jurisdiction (craig-state-tx-stub) builds + boots with zero changes to the consuming crates; partners/transport, theme, terminology, seed, plugins, composition, and field-permission evaluation are all bundle-contributed or surface-agnostic. Step 25 (this MR): program-completion audit run as a 7-slice fan-out + adversarial completeness critic + synthesis — ALL ARCHIVE_READY, 0 broken cites / 0 stale cells / 0 blockers; §Verification corrected for accuracy (PartnerAuditEvent retained by design; SHINES wire-path check folded in; Plan Y re-scope reflected). The surface-bound field-ownership application (ADR-037 §4–§7) is deferred to Phase 11 (the CWCA provider portal), tracked by #634 under the still-open epic &50. Per-step MR/sha history lives in the child-plan rows above + the archived umbrella body’s Status table — not duplicated here.

Edit this page · latest