MR 0.1 (plan + tracking) |
This document, nav-linked Active; epic &72 + child issues per Status row incl. the four live today-bug issues (transient subscriber queues; notices hardcoded recipient + no-op delivery; retained-predicate lower bound; rolling-deploy/unbound-queue event discard) and the production-gap register successors. |
Done (2026-07-19) — !881; epic &72, issues #1087–#1117; two pre-commit J-flags fixed (present-tense gap claim, auto-close keyword) |
MR 0.2 (durable broker topology) |
fix: durable subscriber queues in canopy-mq (queue survives broker restart — regression-tested in devstack); RabbitMQ data volume in docker-compose; queue pre-declaration in devstack definitions (or publisher mandatory + alternate-exchange capture) so startup events cannot be discarded pre-binding.
|
Done (2026-07-19) — #1088: durable-by-default subscribe() + 406-scoped transient→durable self-heal; broker volume + pinned hostname; canopy.unrouted alternate-exchange capture (policy, so code-side declares stay arg-compatible); declare-equivalence + self-heal regression tests in the default battery; the opt-in restart suite now exercises plain subscribe() |
MR 0.3 (consumption + deployment protocol) |
Parked-state inbox (status, schema/capability version, claim lease, attempts, unpark scanner, targeted replay-to-queue by event id); envelope schema_version + additive-compatibility rule; queue migration protocol (versioned names, unbind, zero-depth/in-flight checks, DLQ preservation, rollback); binding-first deployment rule — every later phase splits topology/consumer MRs from producer-activation MRs — with an xtask lint. |
Done (2026-07-19) — #1089: ParkEvent + parked inbox rows (park/repark/claim with FOR UPDATE SKIP LOCKED pass-cursor) + per-subscriber unpark scanner + on-demand run_unpark_pass; classify row-lock kills the InFlightRetry double-run; envelope schema_version (legacy defaults to 1, proptest-pinned); inbox schema single-sourced with the outbox (ADR-039 generator, incl. 5 publish-only services); cargo xtask mq-topology binding-first gate (battery stage 9e) over a 69-key honest allowlist register; event-delivery-protocol.adoc (dispositions table, migration procedure, provenance caveat) |
MR 0.4 (retained predicate) |
fix: closure-month scoping for the retained flag (benefit_month >= date_trunc('month', retained_through)) + multi-month integration test. Latent today; live the moment terminations exist — precedes all entity work.
|
Done (2026-07-19) — #1090: lower bound added to the #447 LEFT-JOIN predicate; pinned by a three-month black-box test (June/July issued+unretained, August closure-month retained) against the rebuilt service |
MR 0.5 (notices execution reality) |
Work-item pattern (subscriber tx persists the item; persons/render/scan/upload run OUTSIDE any tx; fenced short commit; delayed retry + reconciliation, not 5-fast-to-DLQ). Real recipient resolution via canopy-persons (household-membership validation for overrides; mailing-address precedence; redacted/incomplete addresses rejected). Render/upload failure BLOCKS (no PDF-less rows satisfy anything). Delivery outbox: dispatched_at (provider-accepted) vs delivered_at (confirmed); best-effort dedup honestly labeled. Gated-clock dates; render context built AFTER clamping (PDF == stored row). |
Done (2026-07-19) — 1091: notice_work_items queue (UNIQUE (source_event_id, notice_type); enqueued atomically with the inbox row); worker with claim-fenced complete/fail (claimed_by+claimed_at — lease theft cannot double-mint, live-DB-pinned), exponential due_at backoff, terminal failed at 8 attempts with last_error; recipient resolution via canopy-persons (membership-validated, mailing-first, redacted/incomplete rejected); NoticeGenerator split prepare/persist; render failure BLOCKS (deterministic 400 on the direct POST’s unknown key); dispatcher stamps dispatched_at + notice.dispatched in one fenced tx (allowlisted pending the MR 3.1 consumer); notice_date on the gated decision clock (B8 held via the persons resolve_as_of tier-2 rewrite); en-route fix: mq-topology no longer truncates at outline [cfg(test)] mod …; declarations |
MR 0.6 (issue_benefits guard) |
fix: status guard (pending_issuance|active), standalone per house rule; black-box test.
|
Done (2026-07-20) — #1092: 409 guard after the enrollment fetch (before the pending-issuance row / EBT call, so the rejection is row-free); expedited pending_issuance flow preserved; black-box create→terminate→issue pins the 409 + zero issuance rows |
MR 0.7 (broker identity) |
Per-service RabbitMQ principals with publish/consume ACLs — envelope source_service is labeling, not authentication. Descoping this moves it to the production-gap register by explicit decision. |
Done (2026-07-20) — #1093: 18 canopy-<service> principals (scoped resource perms + per-service topic-write regex on canopy.events, generated from the mq-topology publish map; ^$ for publish-nothing services); broker-refused forgery pinned by canopy-mq/tests/acl_test.rs incl. channel-kill isolation and the admin-principal tooling invariant; replay↔ACL interaction documented (foreign-key replay → failed bucket) + follow-up issue filed |
MR 0.8 (security audit queue) |
fix: canopy-security audit subscriber: durable named queue; persistence failures propagate (no swallow-and-ack).
|
Done (2026-07-20) — 1094: the audit subscriber moved off subscribe_exclusive onto the #1088 durable-named-queue path (events buffer while the service is down; live queue properties pinned by a mgmt-API probe); extracted persist_audit_event propagates write failures (nack → DLQ), pinned by sabotaged-table fault injection |
MR 1.1 (adverse-action entity) |
enrollment_pending_terminations (the action; its id IS the adverse_action_id) + adverse_action_notices versioned child (legal DATES in America/New_York; the action’s current legal dates = latest DISPATCHED version; late-notice repair = successor version with new dates); termination_appeal_links (full constraints, monotonic transitions) + append-only action_signals; policy snapshot (policy_version, required advance days, CB rule per P4); source_generation + UNIQUE(created_source, source_reference, source_generation); exemption authority/actor; recipient snapshot. Enrollment row gains lifecycle_revision — the shared fence: terminate bumps it, mark_issued preconditioned on it (EBT-resurrection race dies). benefit_month first-of-month CHECK + API validation. Terminable-state matrix. Direct terminate endpoint REMOVED (410).
|
Done (2026-07-20) — #1095: all four tables + trigger landed; store-level enact_termination is the interim only-path-to-terminated (matrix + lock order enforced; the Phase-3 guarded enact wraps it at MR 3.2); fenced mark_issuance_issued_fenced wired into issue_benefits (race pinned by an interleaving test); proration re-anchored on the newly persisted application_date, application-month-only per 7 CFR 273.10(a)(1)(ii) (the request’s day-of-month is no longer a caller money knob); enactment already gates on zero stays / no veto; first-issuance activation fenced like the mark; first-of-month CHECK is NOT VALID for pre-existing devstack seeds (generator normalized; ADR-016 forward-only); the inert suspend-for-appeal machinery + its subscription deleted; terminate-dependent black-box tests moved to SQL-arrange until MR 3.2 (journey-recovered at 6.1); ApiError::Gone added for the explanatory 410 |
MR 1.2 (action API + stay commands) |
Schedule/list(cursor-paginated)/cancel (moots links atomically; idempotency keys; actor required); internal lookups for appeals (GET /v1/adverse-actions/{id}, household-scoped open list — service-caller); synchronous fenced stay/release/veto commands (PUT /v1/adverse-actions/{id}/stays/{appeal_id} with receipt) — the grant-vs-sweep race closes at filing. NO events emitted yet (topology-first). Contracts + roundtrips; full CreateEnrollmentRequest HoH fan-out (canopy-web, appeals tests, household-issuance tests, enrollment tests, roundtrips, test-lib). |
Done (2026-07-20) — #1096: all five endpoints landed (schedule snapshots policy per P4 + P8 one-open-action; cancel moots links atomically; stay receipts fenced in one tx with veto mooting the action); HoH column + full constructor fan-out (canopy-web resolves the head from the persons household-full view); notice-version supersession machinery gained its lifecycle test; renewals list_due tier-2 as_of offsets the new gated-clock read (B8 held) |
MR 1.3 (HoH backfill) |
One-shot canopy-persons backfill of head_of_household_person_id for existing enrollments + auto_enroll threading; Phase-5 automation refuses on residual nulls. |
Done (2026-07-20) — #1097: cargo xtask backfill-hoh (report-only default; --apply fill-only guard; residual-null report names the remediation) resolving heads via the persons membership predicate; devstack run 13/13 + idempotent; seed generator emits HoH so reseeds never recreate the debt; auto_enroll threading landed with #1096; the 422 schedule guard is the black-box-pinned interim automation guard |
MR 2.1 (appeals: action-bound filing + CB election) |
Every appeal of an existing adverse action REQUIRES its action id (validated via MR 1.2 lookups; server-stamped request_date via gated clock — web backdating dies); narrative grievances never carry CB. CB election per P1: 14-day window from notice date (timely/adequate), assume-continuation-unless-waived (Form 118), good-cause flag, reinstate-within-5-working-days command, repayment disclosure recorded. Synchronous stay BEFORE the CB grant commits; enrollment-unavailable → pending_stay + retry worker. Typed program enum. Duplicate appeals: veto absolute; action proceeds only at zero active stays; liability per-appeal. Until the Phase-3 notice machinery stamps dispatched-notice evidence, the election window anchors on the CONSTRUCTIVE notice date (enact_not_before − required_advance_days; enact_not_before itself for exempt actions; never-noticed = timely) — MR 3.1 upgrades the anchor to real dispatch dates. |
Done (2026-07-20) — #1098: filing rewritten around the Chart B2 election engine (pure evaluate_election + proptest’d 5-working-day math); pending_stay two-step commit (grant ONLY with the fenced stay receipt, persisted; retry worker under advisory lock; inline first attempt at filing) — the cross-service suite pins the receipt on BOTH sides (granted_timely + active_stays on the live action); good-cause-late recorded as pending_good_cause for OSAH (MR 2.2 resolves); GENERATED eligibility column replaced + backfilled; deny_unknown_fields pins backdating dead on the wire; canopy-web form drops its date fields (action-selection UI lands at MR 3.3, so web files narrative grievances until then) |
MR 2.2 (appeals: decision + withdrawal lifecycle) |
Decision records signed + received dates (validated, audited, actor-required); agency-favorable → schedules decrease/closure for the NEXT issuance cycle after receipt + adequate notice (successor notice version; NO immediate enact) per P3. Withdrawal state machine per P12 (oral/written, confirmation notice, 10-day reinstatement, finalization releases the stay). Dismissal typed basis. Decision vocabulary migration for legacy rows (map/quarantine). P13: decision_clock 90→60 + due-date recompute + postponement extensions. CB cessation records per P2 (typed reason + actual date; final-appeal 30-day extension per Chart B3). Events pinned (appeal_id, action_id, household_id, program, decision, signed/received dates, schema_version) — emitters activate only after Phase-3 consumers deploy. The recorded next-cycle date + cessation ARE the Phase-3 successor-notice inputs (appeals cannot mint enrollment notice versions; MR 3.1’s consumers do, from the pinned decision event). |
Done (2026-07-20) — #1099: typed decision vocabulary (legacy upheld/reversed mapped, unknowns quarantined with the raw value preserved); Chart B1 wired cross-service — reversal = VETO, agency-favorable = stay RELEASE + P2 cessation at the next issuance cycle (receipts persisted; failures routed to the 2.3 scanner); the P12 withdrawal machine landed with finalization as the only stay-releasing transition (release receipt precedes the commit; early finalize refused while the household’s 10-day window is open); decision_clock_days corrected 90→60 with open due dates recomputed + appeal_postponements; final_appeal_window_days = 30 (Chart B3) keyed + cited for the final-appeal machinery; the three Phase-3 payloads pinned with schema_version (unpublished); the pre-#1099 best-effort decision-event staging folded into the decision transaction |
MR 2.3 (appeals reconciliation skeleton) |
Scanner: stay receipts vs enrollment links disagreement; CB elections parked in pending_stay past SLA → alert. |
Done (2026-07-20) — #1100: nightly advisory-locked sweep + on-demand internal trigger returning a typed report; enrollment gained the per-appeal link lookup (GET …/stays/{appeal_id}, receipt-shaped, service-caller) as the ground truth; parked-election SLA keyed as the cited-operational pending_stay_alert_hours (24h); report-only by design (per-row enrollment outages counted as skipped_unreachable, never guessed); a dedicated lingering-stay sweep (terminal appeal + live link — both sides AGREE, so the equality sweep structurally cannot see the #1099 failed-post-commit residue; the lifecycle-vs-link predicate can); runbook appeals-reconciliation.adoc covers all three alerts |
MR 3.1 (action notices) |
Manifest routing (BOTH rulesets) with typed effective_date_policy; CB deadline rendered + persisted from the action’s notice version (P1 rule, inclusive semantics defined); notice rows persist action id/version/reason/exemption; currency formatting; DHS-333 verification task. Enrollment consumers for notice.generated/notice.dispatched stamp notice-version evidence on the action. |
Done (2026-07-20) — #1101: the enrollment.adverse_action_scheduled route (payload pinned as AdverseActionScheduledV1; producer activates at MR 3.2) renders noa-termination VERBATIM (typed effective_date_policy, clamp preserved for every legacy route); the CB deadline is notice_date + continued_benefits_election_days INCLUSIVE, read from the SAME [appeals] key appeals enforces (no drift), rendered + persisted; notice rows carry the binding (action id, source generation, reason, exemption); evidence events feed order-independent enrollment upserts (partial UNIQUE on action+notice; dispatch supersedes) — pinned end-to-end by an injected-event black-box test (an EXEMPT action effective today — strictly inside the clamp floor, so the assertions discriminate verbatim from clamp) through render → dispatch → adverse_action_notices, with store-level order-independence + supersession + idempotent-replay pins. DHS-333 RESOLVED: Form 333 is the Sanction/Penalty notice (CONFIRMED on sanction); the general adverse-action notice is system-generated per 3705:43 → fabricated labels removed. Currency arrives pre-formatted from the pinned producer payload (documented) |
MR 3.2 (the enact primitive + consumers + activation) |
ONE guarded enact path for sweep/decision-scheduled/withdrawal-finalized/immediate-exempt: verifies action state + zero active stays from ANY appeal; dispatch evidence for the CURRENT notice version (render/upload success + dispatched_date ≤ noticed_effective_date − required days, jurisdiction-tz DATE math; exempt = dispatched adequate notice); lifecycle_revision fence; enact_not_before re-read under the global lock order. Failure → successor notice version (new legal dates) + alert. Sweep + on-demand trigger. Enrollment consumers for Phase-2 appeal events (order-independent link upserts + tombstones). Producer ACTIVATION of enrollment events is the LAST MR-step, after 3.1/2.3 consumers deploy. |
Done (2026-07-20) — #1102: guarded_enact + enact_gate_locked (stays/veto → current-window dispatched evidence → advance-days or exempt-adequate → no-enact-late month window → enact_locked with terminated_date = the NOTICED date + PAMMS 2415 retention + the lifecycle fence); repair mints the successor window (enact_not_before re-anchor + notice_repair signal fence + re-published scheduled event — repair IS the normal pipeline) with PendingEvidence suppressing repeat mints; hourly advisory-locked sweep + POST /v1/adverse-actions/enact-sweep (202 on lock contention, typed EnactSweepReport); apply_appeal_resolution consumes the ACTIVATED appeal.decision_recorded/appeal.withdrawal_finalized inside the inbox tx (read-your-writes with the gate): reversal = durable veto + tombstone (late grant refused; post-enactment reversal signals Phase-4 restoration), upheld-with-cessation reschedules to next cycle as exempt-with-authority (successor letter drops CB per P3), withdrawal releases + gates. Legacy decision events DELETED; enrollment.adverse_action_scheduled producer ACTIVE (payload + created_source/source_reference; staged in the schedule tx; broker ACLs updated); jurisdiction-tz legal dates in enrollment (crate::clock::legal_today) AND notices (dispatched_date). Organic pipeline E2E (schedule → notice → dispatch → evidence → sweep → terminated) + live reversed-decision→veto convergence + 12 store-level gate/convergence pins |
MR 3.3 (worker surface) |
canopy-web schedule/cancel action: authz + household anti-tamper + verified actor propagation; exemption restricted by role; reason picker from policy TOML; action-selection UI for appeal filing; scheduleTerminationViaUi helper; negative tests. |
Done (2026-07-20) — #1103: actions_snap_adverse_action module (WritePermission + program scope + server-resolved enrollment + neutral-mismatch cancel anti-tamper; actor = authenticated worker); vocabulary in BOTH rulesets, boot-loaded (ActionReasons, fail-fast) and re-validated server-side; operator-tier exemption (template gate + server role re-check + pair-required authority); Determination-tab scheduled-actions read-back (labels, legal date, exemption/stay badges, per-action cancel); #974 filing gains validated action selection + waiver/disclosure checkboxes (Chart B2 rides the action-bound filing); e2e: scheduleTerminationViaUi/cancelTerminationViaUi, an end-to-end spec incl. CB-granted stay badge + forged-exemption/tamper negatives, backdating-era helper surface removed (upheld-decision journey’s CB leg explicitly deferred to the MR 6.1 recovery) |
MR 4.1 (assessment + claims schema) |
cb_assessments (window = CB start … cessation date per P2; HoH snapshot immutable; retention snapshot = lifecycle_revision + retention fields; post-decision issuances ITEMIZED per P7) + cb_assessment_lines (per-issuance allocation, P6) + assessment_work (UNIQUE per appeal, status, attempts, due, lease/fence, heartbeat, error, backoff, pruning). Claims: appeal/action/source_event ids + real UNIQUEs; liable person = HoH snapshot; append-only claim_adjustments with status/balance recompute, repayment-plan interaction + refund/credit ops-flag; void path for veto/cancel-after-assessment.
|
Done (2026-07-21) — #1104: the entity trio landed with P6 AS A CONSTRAINT (partial UNIQUE (issuance_id) WHERE disposition = 'billed' across ALL assessments; void releases lines in the same tx so a successor can bill) + one-active-per-appeal partial UNIQUE (supersession = void + successor) + the P7 itemized disposition for post-cessation lines. record_decision phase 3 records the entity inline as the INTERIM writer (liable person = the action’s recipient snapshot; retention snapshot via a new appeals→enrollment get_enrollment lookup; savepoint-guarded idempotent replay on decision re-record; degraded paths — legacy grants, unreachable snapshots — keep the claim event and WARN naming the 4.2 backfill) and the claim amount became the P2 window figure (post-cessation issuances itemized, no longer billed as if in-window). Zero-dollar assessments persist as computed rows with a $0 display projection and NO claim event. Claims: provenance quad + partial UNIQUEs (redelivered appeal.overpayment_assessed is structurally a no-op — all three program subscribers stamp it), void status (sticky; balance zeroed by a compensating adjustment; recouped-money flags refund review), append-only claim_adjustments with close/REOPEN recompute (closed is derived, not a ratchet). Canonical migration stamped ×3 with byte-parity crate-tested. 36 tests incl. the full invariant matrix against the devstack. |
MR 4.2 (assessment worker + fences + activation) |
Work item from inbox tx; HTTP outside; issuance finality fence (pending EBT → blocked + re-queued); apply fenced on (status, lifecycle_revision, allocation-set version); household-scoped allocation serialization with full-set recompute on corrections (P6 total order: decision_signed_date, filing date, appeal_id). Scanners cover ALL liable dispositions (upheld/withdrawn-finalized/dismissed); pre-cutover backfill; downstream claim-acknowledgment scanner; inline phase-2 assessment deleted in the activation step. |
Done (2026-07-21) — #1105: the worker drains assessment_work (SKIP LOCKED + 10-min stale-lease reclaim; attempts counted at claim; 8-attempt budget with exponential backoff; failed = operator surface) on appeals' own ADR-019 service identity (mandatory at boot; EnrollmentClient collapsed to service-token-only — the bearer mode died with the inline writer); HTTP snapshots outside any tx; ONE short apply tx under the per-household advisory lock re-validates the appeal, records a derived cessation when backfilling (P2 record-then-assess), resolves P6 by the total order (earlier holder keeps → already_billed; later holder voided reallocation + lines released + appeal re-queued; ties break on appeal_id), supersedes this appeal’s own stale assessment (allocation_version+1), and completes the work item. Withdrawal finalization became a first-class liable trigger: records cessation (withdrawal_finalized added to BOTH cessation-reason CHECKs) + enqueues in the fenced tx; decisions enqueue in the decision tx (dismissed typed separately). Ack loop event-driven: snap.overpayment_claimed (+appeal_id/assessment_id) consumed on canopy-appeals.claim-acks flips computed → applied; zero-dollar applies immediately. Hourly advisory-locked scan = backfill sweep (liable + no assessment + no work item + overpayment_amount IS NULL so legacy-claimed degraded rows never mint a second claim) + unacked re-emit (snap select-firsts on assessment_id and re-acks instead of DLQ-ing) + dead-action sweep (vetoed/cancelled → re-queue → worker voids + pinned appeal.overpayment_assessment_voided voids the downstream claim ×3 via stamped-store void_for_assessment). Interim inline writer DELETED. Tests: lease/defer/requeue lifecycle, ack idempotency, pure allocation matrix incl. same-day ties, household-lock TOCTOU, zero-dollar concurrency convergence, displacement end-to-end, snap void/re-ack convergence. |
MR 5.1 (periodic-report cohort + state model) |
periodic_report_required cohort per P11 (2026-03-02 phase-out; legacy extended-cert cohorts only; Senior SNAP waiver) + migration backfill. PR state machine (notice_sent_15th; form complete/incomplete per 3730:35; VCL sent/10-day due; discrepancies; verified; processed). Scheduler predicate COMPLETE (cohort, status, active, non-null dues, calendar cutoffs, bounded batches). Completion + change-report writes in one locked tx with outbox events.
|
Done (2026-07-21) — #1106: cohort flag decided by the SAME predicate at creation and in the marker-delimited migration backfill (interval comparisons, not whole months — a 6.5-month cert is "longer than 6 months" per 3730:18; senior waiver = 36-month duration proxy documented against #956; the backfill-parity test re-executes the marked SQL against the Rust twin), and recertification cancels open cycles in the create tx (3730:21-23). snap_periodic_reports = one row per (certification, due_month) with generation reserved for 5.2’s tombstones + the stored 3730 calendar + status machine whose transitions are single conditional UPDATEs (WHERE = legality; 409s name the current status; VCL ≥10 days CHECK-backed); "discrepancies" modeled as the typed vcl_reason/vcl_detail on the VCL transition rather than a distinct status. Six service-caller endpoints (list/get/form/vcl/verified/complete); legal dates server-stamped via a single legal_today() funnel (appeals B8 precedent — net B8 15→12 with the three query feeds + scheduler converted to tier-2 as_of seams). Scheduler materializes cycles (bounded, idempotent, missed calendars tombstoned calendar_elapsed_at_cutover) and gauges the three due-list predicates 5.2 will drain. Completion tx: cert row FOR UPDATE (cert→cycle lock order shared with supersede — deadlock-free), processed stamp + change-report rows (130%-FPL flag decided in the INSERT; the swallow-on-error mark_requires_redetermination deleted; three insert fns unified executor-generic) + typed renewal.snap_periodic_report_processed (mq-topology-allowlisted until the 5.2 consumer). Interim-contact handler made atomic. Store-level lifecycle + due-list + backfill-parity tests consolidated into one sequential DB test (global-scan materializer + shared devstack = parallel tests would steal batch slots); API tests cover the state machine, atomicity evidence (change rows + outbox in one commit), concurrent double-complete (one 200/one 409/one event), and completion-after-recert losing cleanly. |
MR 5.2 (the two-notice calendar as the action pipeline) |
15th-prior informational notice; 5th-of-month combined notice = THE adequate termination notice → creates the adverse action with that notice as its dispatched evidence version (NO duplicate NOA); closure by month-end SOP via the enact primitive; generation tombstones both directions (completion-before-trigger no-ops the late trigger); renewals terminal consumer stamps certification terminated_at; NO CB on these actions (P4); veto → worker re-determination queue (schema’d, authorized, idempotent) — no auto-retrigger. |
Done (2026-07-21) — #1107: the drains ride the DAILY renewals scheduler (bounded, idempotent — the 15th trigger is a one-tx stage+status-flip; the combined trigger is an idempotent provenance-triple schedule call + cycle stamp, so a crash between them converges next tick). The action is the FIRST non-worker created_source (periodic_report/certification/generation via the new ActionSourceClaim); exempt-shaped per 3730:98-108 so cb_available=false falls out of the P4 snapshot (cb_rule cites 3730:37; appeals refuses CB unchanged); effective date = last workday ≤ month-end (weekend-only, appeals-precedent rationale — moving earlier only WIDENS the enact window due..EOM). The combined notice = dispatched evidence via the EXISTING evidence loop, discriminated to the pr-combined template by a new manifest created_source discriminator (status-mechanism parallel; worker actions keep the generic NOA); the 15th letter is deliberately NOT action-bound (would mint enact-gate evidence). Closure = the existing hourly enact sweep, unchanged. NEW pinned terminal events enrollment.adverse_action_{terminated,vetoed,cancelled} (provenance triple; staged in the enact/veto/cancel txs — enactment previously published NOTHING); renewals' canopy-renewals.adverse-actions consumer stamps cert (single terminated_at setter, now executor-generic) + cycle, or feeds pr_redeterminations (idempotent on the dead action; 2 endpoints; cancelled-with-reason periodic_report_completed is benign supersession and never queues). Tombstones both ways in enrollment’s periodic_report_completions (409 the late trigger; cancel the in-flight action), serialized on a per-(certification, generation) advisory lock against the schedule path. Deviations from the row as written: the combined notice is minted by the ACTION’s scheduled event (ordering (a) — action first), not by a renewals-side notice that then binds — the evidence machinery only supports action-first binding, and it makes the no-duplicate-NOA property structural; the VCL-failure closure arm (Chart 3730.1 row 2) needs its own notice-content decision → #1128. Also fixed in-MR: the #1106 broker-ACL gap for renewal.snap_periodic_report_processed. Cross-service in-module pipeline test (drains → routed letters → evidence → sweep → terminated → stamps; veto → queue + 404 double-action; completion → cancel + tombstone refusal). J-review deltas fixed pre-commit: the periodic_report source_reference is CANONICALIZED before locking/storing (a parseable-but-non-canonical UUID claim would have taken a different advisory lock and evaded the completion-cancel match); the periodic_report_completed reason is a pinned contracts constant; source: None omits from the wire (additive against pre-#1107 deny_unknown_fields). The advisory-lock INTERLEAVING itself has sequential-orderings coverage only — the concurrent race is MR 6.1’s interleaving matrix (completion-before-trigger is on its list). |
MR 5.3 (reopen/proration per Chart 3730.1) |
Verification-or-form within 30 days after the due month → reopen + prorate from receipt — a narrow scoped reopen path on the enrollment (the general restoration builder stays a successor, #1113). TWO SOPs per the chart (the row as originally written conflated them): verification cure (row 4) = 5 workdays from receipt; nonfiler late filing (row 5) = 30 days from receipt, expedited N/A. Calendar journey-grade tests. |
Done (2026-07-21) — #1108: the reopen is ARM-DERIVED from the terminated row (vcl_sent_date → row-4 cure, else row-5 late filing with a complete form required per 3730:35 — arm-generic now so the #1128 VCL-failure termination arm plugs in), window + SOPs as cited policy keys ×2 rulesets. Converge-on-retry order: enrollment’s idempotent per-action reopen FIRST (narrow un-terminate: periodic_report source only, enacted only, terminated-by-this-action belt, no-live-successor fence, lifecycle_revision bump, append-only enrollment_reopens receipt + reopened signal; the action STAYS enacted — no status-vocabulary migration), then one renewals tx: cycle rejoins the NORMAL machine (terminated → verified|form_complete + reopened_date/sop_due_date; completion machinery unchanged) and reinstate_certification (only path away from terminated; clears terminated_at for the depth trend; the one-active-cert partial unique = the re-application fence). Proration = the EXISTING initial-month fn generalized (prorate_month_from) with the reopen receipt as a second anchor in issue_benefits; missed intervening months NOT restored (#1113). The #1107 enacted-arm operator alert goes quiet on a reopen receipt. Tests: store fence matrix, add_workdays proptest, pipeline phase D (reopen negatives → prorated issuance → completion leaves the enacted action untouched). Discovered: existing proration rounds nearest-cent and the under-$10 initial-allotment rule is unimplemented — R4 issue filed rather than silently changing money math in a reopen MR. |
MR 6.1 (journeys + interleaving matrix) |
Upheld journey (real action via UI, CB election, next-cycle enactment, async assessment polling); pipeline journey (schedule → dispatched NOA evidence → clock advance → sweep → terminated); periodic-report calendar journey (15th/5th/month-end); Playwright clock helper. The full review interleaving matrix distributed to phase tests (decision-before-grant, completion-before-trigger, reversal-with-residual, overlapping upheld appeals, grant-vs-sweep, issuance-vs-termination, allocation ties + concurrent correction, assessment-to-claim loss, pending-EBT recovery, stale notice acks, final-appeal continuation, oral-withdrawal reinstatement, mixed-version replicas, broker restart, pre-binding publication, malformed/wrong-source, multiply-stayed cancellation, zero-dollar concurrency, retry-budget exhaustion). |
Done (2026-07-21) — #1109: matrix coverage was AUDITED first (six contextless readers over the phase suites; verdicts + the build list are the issue’s second comment) — already-covered items cited, not re-tested (overlapping upheld appeals, zero-dollar concurrency, oral-withdrawal reinstatement; broker restart is opt-in fault-injection by posture; reversal-with-residual = 3.2’s reversal_after_enactment signal path + the dead-action void/requeue tests added here; allocation ties are pinned pure + the concurrent-correction contest is the racing-full-applies test here; pre-binding publication stays the audited configuration posture — the capture-recovery leg is runbook territory). The genuinely-concurrent gaps landed as races: grant-vs-sweep, decision-before-grant, issuance-vs-termination (+ the untested fenced initial-issuance activation), multiply-stayed n-ary cancel, racing dispatch stamps (arrival-order versions + supersession + the gate’s latest-dispatched read; the documented 23505 redelivery retried in-test), and the 5.2-deferred completion-vs-trigger advisory-lock race. The zero-coverage 4.2 worker paths all gained tests (retry-budget exhaustion, apply_dead_action, ack re-emission, dead-action sweep against a REAL vetoed action, pending-EBT Defer→settle→commit, two FULL apply passes racing → billed-once). Journeys: NEW POST /v1/renewals/scheduler/run (advisory-locked on-demand daily pass; gated clock, no wire date; caller-gate + outcome-shape test) + the fleet-wide /test/clock Playwright helper (advance together, always reset; setClocksTo pins an epoch so the calendar journey is date-independent) enable the NEW pipeline journey (premature sweep REFUSES → dynamic advance past the CURRENT legal date → terminates on the NOTICED date; verified green under full profile + test-clock, 2026-07-21) and the NEW calendar journey (cohort via backdated START under the pinned epoch; 15th → 5th mints the termination → month-end sweep closes; verified green same run). Both #1123 journeys REBUILT action-bound (upheld: CB via the binding, continued month issued AFTER the election, clocks past the P2 cessation, ASYNC assessment polled; change-during-hearing: appeal #2 is a narrative grievance with provably NO CB) — but the rebuild EXPOSED #1136 (portal files the applicant membership as self; the #1097 HoH resolver accepts head only → worker enrollment creation refuses every portal-filed household), so both re-parked under test.fixme(#1136) — and went green with that fix (2026-07-22, 10.3s/9.9s under full profile + test-clock). The un-parking surfaced two more product gaps, both issue-tracked and honestly compensated in-spec: #1137 (the portal apply address is dropped at finalize, so the notices recipient gate blocks every portal-filed household — each journey gained an explicit worker addAddressViaUi beat) and #1138 (auto-enroll’s first issuance never settles — no EBT drive on the subscriber path — so the upheld journey’s contested arc moved a couple of days into the NEXT logical month, keeping the stuck-pending row pre-window for the month-granular Chart B2 window; the enrollment beat became ensureLiveEnrollmentViaUi because auto-enroll now legitimately races the worker’s create into the #1130 fence). Deviations: matrix items whose machinery is absent are issue-tracked, not faked — final-appeal continuation #1132, mixed-version parking #1131 (first ParkEvent caller wired by #1130), reopen-vs-reapplication fenced+raced in #1130; malformed-input nacks to DLQ by contract (parking is for recognized-but-unhandleable). recordDecisionViaUi was latently broken since #1099 (required date fields unfilled) — fixed here. |
MR 6.2 (seeds + catalogue + sweeps) |
Seeds (HoH model/generators; linked actions for synthetic appeals/termination notices; suspended seeds retained; ADR-033 background density); compliance action-catalogue bindings; doc-consistency + clock-consistency sweeps. |
Done (2026-07-22) — #1110, built from three contextless surveys (the build list is the issue’s second comment). Seeds: HoH was already DONE (#1097) and suspended seeds intact — the real work was replacing the fabricated standalone termination notices with REAL scheduled adverse actions (FUTURE legal windows so the live enact sweep never eats seeded enrollments; every third cancelled as history) + dispatched version-1 evidence + #1101-bound notices rows; action-bound seeded appeals (not_applicable election — fresh granted_legacy only as unbound pending history, never decided/liable, killing the #1105 backfill scanner’s hourly FAILED spam on seeded rows); a GUARANTEED P11 cohort (counted over approved households — a raw-index slice left the cohort empty on real seeds) with midpoint cycles in three rotating kinds (processed / recert-cancelled / live-inert notice_sent); four new seed-verify ref checks + the core.enrollment-head-resolvable invariant. Catalogue: #799’s two rows flipped bound, five rows added (incl. the previously ROWLESS termination-execution mandate on the enact sweep), five re-pointed, gate clean 358/508. Clock: the last two ungated reads gated (enrollment summary year, renewals trend anchor); the structural gaps filed (#1140 web off-clock, #1141 dispatch-evidence wall-clock, #1134 annotated with the UTC-frame residue). Docs: 8 pages corrected (incl. services.adoc’s absent enrollment/renewals pipeline roles and the appeals 90→60-day fix) + both restructured walkthroughs rewritten from the verified specs. Deviations: per-service test-clock passthrough normalization noted in #1140, not done (cosmetic — the workspace flag covers the fleet); seed-verify’s pre-existing runtime-orphan noise filed as #1142. |
MR 6.3 (UAT-milestone closeout) |
Plan Status → Done; nav → Archive; UAT-labeled closing comment on #1084 enumerating the production-gap register with issue links; the final MR carries the auto-close keyword for #1084 (written out only there — never earlier, per the non-final-MR keyword rule). |
Done (2026-07-22) — #1111: plan archived (file + nav entry to Archive per house convention); the production-gap register below now carries its tracked successor issues inline; #1115 (broker principals IF 0.7 descoped) closed moot — MR 0.7 shipped, the condition can never trigger; the UAT-milestone closing comment lives on #1084. |