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

On this page

Status

Step Description Status

Step 0

GitLab filing: #1069 weight 3→8 + Plan::ERR-FLOW + the decision record (U1–U4/F1–F9); issues #1102 (M0) / #1103 (per-diem UTC projection) / #1104 (craig-mq durable drift) / #1105 (queue_drained event omission) / #1106 (legacy index contract step) / #1107 (service-scoped placement authz) filed + related; #1098 related to M1

Done (2026-07-23) — all filed; decision comment on #1069

M0 (fix MR)

Acting-worker office proofs (authorize_worker/supervisor_session/sweep executor attribution via claims.acting_worker()) + single-purpose proof bindings (worker → action kind, system → exact enforcement leg; covers() refuses cross-use with typed ProofActionMismatch); service+actor grant/refusal/lending tests; per-kind proof helpers in 4 test files

Done (2026-07-23) — MR !1027 (impl 3c56030925d67b0779307e50b992ec4fa7251e09, merge 20269dde6045e7317c98d42fa45b393477b627dc); #1102 closed; J-record on the MR

M1 (MR1)

Generator months: payment_month GENERATED column + v2 voided-exclusive month-identity unique index (EXPAND — legacy index kept for rolling replicas, drop = #1106); month-window CHECK replacing calendar-month; expected_month_row as the ONE coverage judgment (candidacy = active-interval overlap; rules: per-diem exclusivity → handoff-free month semantics via F6 backdating, full as-of month start, native-ERR partial birth month priced terms_as_of(activation), none) used by generate + derivation-compare + reconcile; per-agreement ARBITRATION (incumbent wins; >1 incumbents = anomaly skip + WARN); the F5 standing exclusivity-repair void arm (payments_voided cause perdiem_exclusivity; disbursed frozen); reconcile-queue transactional claim/settle (the SETTLE IS THE CLAIM — first statement of the repair tx; attempts bump commits independently; dead-letter cap 10 + invariant — closes #1098); per-diem-lifecycle re-enqueue (cause perdiem_change, month-granular scan) from placement.ended AND the per-diem writer’s reciprocal check (no manual per-diem void endpoint exists — #1028); placement.ended subsidy arm (ended-anchor WARN transient for every anchor; the invariant re-surfaces durably only fully-voided anchors — financial holds no placement status, recorded limit); below-floor WARN at the enqueue site (the report never sees dropped months); invariant SQL rework (month grouping, proration-aware pricing arm, partial-row provenance probe, remediation-text truth); partial-gross event emission; constraint probes

Done (2026-07-23) — MR !1028 (merge 177cc273af8e2704c3c131ca181161d54ab43a5b); 5 commits, each J-reviewed with findings remediated pre-commit (DataGap touch-nothing; settle-as-claim closing the reviewer-found lost-signal window; month-granular recovery scan; honest ended-anchor scope). #1098 closed by the claim/settle rework; #1108 filed (devstack tick vs exact-assertion suites, pre-existing). Full battery green

M2 (MR2)

ERR creation: subsidy/policy.rs jurisdiction seam (fail-closed Option; CalendarDays proration, 120-day clock, {kinship} qualifying set); 22.8 reason tokens + program/reason/witness matrix (per_diem_begins needs the live-per-diem witness; lapse needs the elapsed clock; ERR reinstatement refused under live per diem); agreements migration (clock pair CHECK due>start, evidence keys, create_request_id+hash, partial clock index); create_agreement_active over a stamp-free CreateActiveParams (attribution = acting worker + M0 bound CreateActive proof; case/assigned derived from the placement; predecessor locked+validated; −12-month floor; pay-excl child lock + in-tx per-diem re-check); generic create_agreement rejects ERR (F1); craig-placement minimal subsidy-eligibility endpoint (Eastern date_of dating placement-side); financial placement_client (mock-seam, 503 fail-closed); feature-gated (F4 default-OFF) create endpoint with F8 client_request_id+canonical-hash idempotency checked before volatile dependencies; wire validation (roles/money scale/education/parties/dates → 400 never 500); ruleset v1.3.0 supervisor create row (TX default-deny recorded); financial.subsidy_agreement_created (pinned no-PII payload, no term_id) + parse arm; clock read surfacing (active-ERR-only approval_clock_overdue, business-date parameterized invariant); seed family C/D (as-of-relative; models+renderers+tests+hash re-bless); typed test-lib create_agreement (B7). As-built deviations (living spec): authz runs BEFORE the F8 idempotency lookup (a replay can never leak cross-session; the reviewed property — idempotency before every VOLATILE dependency — is preserved and test-pinned); the pre-tx per-diem advisory check is dropped (the in-tx gate is the single gate; identical wire contract); the "business-date parameterized invariant" surfaces as the ONE approval_clock_overdue derivation + due-today/due-plus-one boundary pins (an overdue clock is legitimate operational state, not a zero-rows catalog violation); the evil-corpus adoption stays on /rates (one adoption per service — the create endpoint carries its own 7-arm wire battery); the S2S-outage 503 arm is code-reviewed only (the shared devstack placement service cannot be stopped mid-battery — J-reviewed, accepted); seed subsidy payments stay uniformly pending (the disbursed-variant pin lives in the M1 store test that flips status in-test); validate_predecessor reads WITHOUT FOR UPDATE (the held history lock excludes the only terminal exit — a row lock would ABBA the reinstatement edge, found by the C2 J-review); lock_child_payment_exclusion moved generator→store (creation takes it too)

Done (2026-07-23) — MR !1029 (C1 59a485b0 / C2 a7df8983 / C3 7ae5bde6 / C4 81b85e33 / C5 7cddabf8 + budget-gate fixes c4d9f86f, 5b71446f; merge f56a799fd8f3e9ef56e987923eff2f9b1de87a6f). Each commit fresh-subagent J-reviewed with findings remediated pre-commit (J-record on the MR). Battery green incl. fresh keyed reseed (verify-seed OK, 50 invariants clean over the seeded partial row). Bonus: #1108 root cause fixed (nextest lease membership)

M3 (MR3)

Per-diem handoff: subsidy_perdiem_handoffs durable fact table (detect-once, enforced_run_id, correction path); sweep leg 3 (Active+Suspended heads, open-head join, same-placement earliest pinned (period_start, id) evidence, period_start ⇐ as_of, F6 termination business_date = max(perdiem_started_on, head.from + 1); different-placement per diem = leg3_moved_children report only); leg precedence (leg-3 candidates excluded from legs 1/2); execute acknowledgement-count handshake (missing ack = 0 → 409 when handoffs pinned — old UIs cannot execute unseen legs); SweepConfig::any_writes(); completion-event finalize fix (committed counters never rewritten); leg-3 access-path partial index + EXPLAIN pin; knob auto_per_diem_handoff default off; sweep-demo overlay + metrics/contracts/API/web counter ripples

Done (2026-07-23) — MR !1032 (merge 6e393149c5c4418e1c5b9a9da1e4f4c9d690098b): two J-reviewed commits (5c1447c3 authority/schema/finalize-fix; 6f3da8ee engine + handshake) + two gate fixes. As-built deviations: the engine and wire commits FOLDED into one (enforcement and handshake interleave in the same files); detection facts write on EVERY scheduled scan posture, observe included (the third documented observe-run write; previews stay pure-read — execute upserts on enforce); the placement.ended void handler now takes the pay-exclusion child lock per sorted child (review finding — parity with every other per-diem writer, so voids serialize against leg-3 enforcement); the fact table’s agreement_id FK is ON DELETE CASCADE (house child-table pattern); the pre-existing run_fatal_failure_stamps_the_row_failed test pinned the finalize DEFECT and was rewritten to the new contract (run-fatal coverage preserved via a detection-fault injection); web ripples deliberately ZERO (serde-defaulted contracts; craig-web sends acknowledgement counts in M4, so a non-empty preview 409s from the old web until then — the designed protection); leg-3 scan window is [head start, as_of], so a per diem predating a later manual suspension head is invisible to the sweep (manual termination path remains; record in ADR-055, M4). Found+fixed en route on its own branch: #1109 (M2 api-test fixtures used UTC dates against the Eastern business clock; MR !1031).

M4 (MR4)

BFF + docs close-out: ERR create form (PRG; BFF-minted client_request_id; verbatim 4xx flash, generic 5xx) + clock panel/badge + sweep leg-3/moved-children counters + acknowledgement counts; e2e as dana.county (create-against-fixture-D produces the overdue badge — no seeded invariant poisoning; double-submit replay; non-kinship refusal; feature-gate-off spec); ADR-055 + named ADR-053/054 amendments; event catalog/state machine/endpoint tables/shared-crates/config reference/.env.example/data-model/runbook (generator prerequisite + queue metrics); CHANGELOG; ⁂ #1073 comment; #1069 AC walk + close; epic &70 tick; plan → archive

Done (2026-07-24) — MR !1033 (merge e9f4f49344b31a161efab9c44b09182ac581c7f4); program closed (#1069 closed, epic &70 ticked, ⁂ list on #1073, follow-up #1110 filed). As-built deviations: the e2e non-kinship refusal arm is replaced by the deterministic placement-not-found verbatim-flash arm (non-kinship + inactive shapes are API-covered by M2’s placement-shape refusals); the feature-gate-off e2e spec is NOT shipped (the devstack runs gate-ON stack-wide — a second stack posture for one knob is not worth the battery cost); instead the gate-off contract gained its first direct coverage (require_err_enabled unit pins the typed 403 + the named knob); the F8 replay e2e drives the BFF layer with the form’s own minted id (Playwright disables bfcache, so browser back+resubmit would mint fresh); the supervisor gate on the create POST lives IN the handler (the path is shared with the caseworker list GET); bobsmith e2e pins the non-supervisor button-absence + /new 403 (write-parity #812)

Issues: #1069 (tracking; M4 closes) · #1102 M0 (closed) · #1098 (M1 closes) · follow-ups #1103 #1104 #1105 #1106 #1107
Branches: fix/1102-acting-worker-proofs (merged) · feature/1069-generator-months (this plan = first commit) · feature/1069-err-create · feature/1069-perdiem-handoff · feature/1069-err-web
Provenance: 8-reader recon workflow + Plan-agent design + internal 3-lens pass (6 P1/7 P2/10 P3, reworked) + external stop-ship review (2026-07-22, ~47 findings — authoritative) driving the M0–M4 split and the F-series decisions. The full finding→disposition index is in the approved working plan (mirrored below in §Review dispositions).

Context

The subsidy ledger (ADR-052), review workflow + verified-office authority (ADR-054), monthly generator (ADR-053), and BusinessClock exist — but no agreement can be created via the API: ADR-052 deferred creation to the eligibility-bearing flows so "nothing payable can exist unvalidated". ERR (Enhanced Relative Rate, UAS 542) is the first such flow: the kinship subsidy paid while a relative caregiver awaits foster-home approval. GA 22.8: creation requires an ACTIVE kinship placement; payments run from the FIRST DAY of placement; a 120-day home-approval clock applies; ERR terminates when the caregiver’s foster per diem begins; ERR carries no paper review (already encoded in types_for_program).

Decisions

# Decision Source

U1

Prorate the first partial month; policy constants behind ONE jurisdiction-keyed seam (subsidy/policy.rs, fail-closed Option) pending the #1072 StateBundle lift

user fork + review

U2

120-day clock report-only + manual termination (new reason token through the existing transition endpoint)

user fork

U3

Per-diem handoff = third sweep enforcement leg — with a durable handoff-fact table, truth-dated terminations, suspended-head support, and a mixed-version execute handshake

user fork + review

U4

Full BFF surface in this program

user fork

F1

ONE-SHOT CREATE-ACTIVE (the strictly-after transition-date rule makes two-step activation structurally unable to start coverage on the placement date); generic create_agreement REJECTS err

forced + review

F2

Backdating bounded at current_month − 12 — below the generator floor, coverage would be approved but silently never-payable

review

F3

Dedicated ApprovalAction::CreateActive (county floor) over action/leg-BOUND proofs (M0) — no phantom pending-head pricing

forced + review

F4

ERR feature gate CRAIG_FINANCIALSUBSIDY_ERRENABLED, DEFAULT OFF until the ⁂ money policies are DFCS-confirmed; enabling is the operator’s recorded consent

review

F5

The generator’s exclusivity-repair void arm is a STANDING policy (completes ADR-053’s per-diem-wins), not knob consent — declared openly, ⁂-confirmed

review

F6

Handoff termination business_date = the per-diem start date (the truth; max(head.from + 1) for suspended-after-per-diem) — the status and money ledgers agree without special month rules

review

F7

Same-placement per diem auto-terminates (per_diem_begins); different-placement per diem is REPORT-ONLY (moved child ≠ caregiver handoff; payments carry placement identity)

review

F8

Creation idempotency = client-supplied client_request_id + stored canonical payload hash, checked BEFORE any volatile dependency

review

F9

Expand/contract for the payments month identity: GENERATED payment_month column + v2 index alongside the legacy index; drop = #1106 after rollout

review

Design (per MR)

Authoritative detail for each MR lives in the M-row of the Status table plus the sections below; the externally reviewed working plan (approved 2026-07-22) is the drafting source.

M1 — generator months

  • Migration (EXPAND only): payment_month DATE GENERATED ALWAYS AS date_trunc('month', period_start::timestamp::date) STORED — the cast makes the expression immutable (a bare date_trunc('month', DATE) resolves through the timezone-sensitive timestamptz overload and cannot back a unique index); v2 unique (agreement_id, payment_month) WHERE agreement_id IS NOT NULL AND status <> 'voided'; the calendar-month CHECK becomes the month-window CHECK (period_end = the month’s last day; period_start may be mid-month). The legacy (agreement_id, period_start) index REMAINS this release so old replicas' ON CONFLICT stays bound (a transitional 23505 on a new-partial collision fails loud and re-enqueues); #1106 drops it after rollout. The unit-month CHECK is unchanged: a subsidy row is ONE unit of the month’s entitlement (day_count = 1, daily_rate = gross); the period columns state coverage; partial birth rows carry the prorated award (contracts + UI comments updated in M4).

  • expected_month_row(tx, agreement, month) → Full(terms) | Partial{from, terms} | None(reason) — the ONLY status_as_of/terms_as_of caller in the month loop; used by the generate gate, the derivation compare (including expected-None), and reconcile_inactive_rows/reconcile_uncovered_month (expected-Partial months are COVERED — the churn-loop kill). Rule order: (1) live per-diem overlap → None(perdiem_exclusivity), checked BEFORE the existing-row match — the F5 repair arm: undisbursed subsidy rows voided (payments_voided cause perdiem_exclusivity), disbursed frozen + derivation_mismatch + WARN; (2) active as of month start → Full priced terms_as_of(month_start); (3) the agreement’s first ACTIVE interval — native ERR births only (imports and SG/RCS excluded; reinstatement months explicitly zero, ⁂) — starting in-month → Partial priced terms_as_of(activation) (a term append inside the window prices from the NEXT month); (4) None(not_active).

  • Candidacy = active-interval OVERLAP with the month, at candidate_children’s intervals arm AND `child_active_agreements (the drain path). Arbitration replaces the exactly-one-agreement rule: expected rows are computed for EVERY overlapping agreement (cross-program included); exactly one non-None pays; two-plus → the agreement covering month start (the incumbent) wins and a successor’s birth month is None(superseded_month) (⁂); two-plus incumbents → anomaly, generate nothing + WARN + counter.

  • Reconcile queue: transactional claim/settle — the SETTLE IS THE CLAIM (the repair transaction’s first statement flips processed_at, so every concurrent enqueue waits on the whole tx; rollback restores the row live); the attempts bump commits independently so error loops converge on the dead-letter cap (10, subsidy_reconcile_queue_dead_letter invariant + operator reset). Closes #1098.

  • Recovery loops: per-diem lifecycle changes (placement.ended voids AND the per-diem writer’s reciprocal overlap check — no manual per-diem void endpoint exists, #1028) enqueue perdiem_change months for the child’s covering agreements via a MONTH-granular scan (exclusivity judges whole months); below-floor months WARN at the enqueue site; placement.ended gains the subsidy arm — a transient WARN for every ended ERR anchor, durable invariant re-surfacing only for fully-voided anchors (financial holds no placement status; the cross-service check is #1107 territory).

  • Invariants: month grouping via payment_month; the disbursed-derivation pricing arm becomes proration-aware AND expected-row-parity based; a blocking partial-row provenance probe (mid-month period_start ⇒ the agreement’s first-active-interval start + program err + native provenance); the per-diem-overlap invariant’s remediation text rewritten; the warn_on_subsidy_overlap NOTE in main.rs rewritten (J5).

  • payment_created emits the ROW’s gross (a partial row’s prorated award), never the term monthly amount.

M2 — ERR creation

See the Status row for the full inventory. Key contracts: creation is feature-gated (F4) and idempotent by client_request_id + canonical hash BEFORE volatile checks (F8); all attribution flows from the acting worker + the bound CreateActive proof through a stamp-free CreateActiveParams; case_id/assigned_worker_sub derive from the placement record; the S2S read is the NEW craig-placement subsidy-eligibility minimal endpoint returning an Eastern-dated started_on (financial never converts timestamps); S2S failures are 503 fail-closed pre-tx; a live per diem beginning at/after the proposed effective date refuses creation (holdovers do not); the pay-excl child lock + in-tx re-check close the create-vs-payment race; the −12-month floor refuses silently-unpayable coverage; evidence keys are REQUIRED, honestly-unverified references.

M3 — per-diem handoff

See the Status row. Key contracts: the durable subsidy_perdiem_handoffs fact is the authoritative "per diem began" record (detection insert-once; enforcement stamps enforced_run_id; correction = state_office reinstatement + corrected_at, refused while the per diem is live); the scan pins deterministic earliest (period_start, id) evidence and re-verifies the PINNED payment in-tx; terminations are truth-dated (F6) so reconcile months cover the overlap naturally; the execute handshake carries per-leg acknowledged counts (mixed-version safety); a handoff-only scheduled run records executing/completed via SweepConfig::any_writes(); completion-event staging failure never rewrites committed run counters.

M4 — BFF + docs

See the Status row. The e2e produces the overdue-clock state through its OWN create against the as-of-relative fixture-D placement (~5 months back ⇒ immediately past the 120-day clock) so the seeded data stays clean for cargo xtask invariants.

Review dispositions

The external stop-ship review (2026-07-22, ~47 findings) is fully dispositioned; the finding → fix map lives in the approved working plan and is reflected in the M-row inventories and F-decisions above. Highlights: the non-immutable date_trunc index → generated column; the as-of-month-start selection gates → overlap candidacy
expected_month_row; the unreachable partial month and handoff repair → F5/F6 semantics; mutable-row handoff evidence → the fact table; proof forgery vectors → M0; unsound natural-key replay → F8; ungated money policy → F4; delivery shape → this M0–M4 split.

Verification

Per MR: fmt/clippy/nextest over touched crates → fresh-subagent J1–J8 per substantive commit → token commit → full pre-push battery (budgets, axis, route-role, evil-corpus advisory, e2e where applicable) → MR (Relates to #1069; M4 Closes) → merge per the standing procedure → J-record note → closing/related comment → Status row here updated. M1 additionally: the FULL existing generator lifecycle suite passes with only the sites this plan names changed; forced month-identity collision test; queue crash-recovery test (kill between claim and repair → the row survives). Program close: #1069 AC walk, epic &70 tick, plan → archive, ⁂ list appended to #1073.

Edit this page · latest