ADR-055: ERR (UAS 542) Kinship-Subsidy Flow — One-Shot Enrollment, Month Judgment, Per-Diem Handoff

On this page

Status

Accepted (2026-07-24). The #1069 program (epic &70) landed as five MRs, each independently green: M0 acting-worker proofs + action-bound approvals (#1102, MR !1027), M1 generator month judgment + transactional reconcile claim/settle (MR !1028, closes #1098), M2 one-shot ERR creation (MR !1029), M3 sweep leg 3 per-diem handoff (MR !1032), M4 the operator surface + this record. Program plan: ERR subsidy flow. An external stop-ship review (2026-07-22, ~47 findings) reshaped the program; its dispositions are recorded in the plan and below.

Amends (named): ADR-053 (the "enforcement business dates are always business today" rule gains the leg-3 truth-dating exception; the whole-month per-diem exclusivity void arm is STANDING policy, not knob-gated) and ADR-054 (the enforcement-leg set grows from two to three; the execute contract gains the acknowledgement handshake; the completion event is best-effort after the run row finalizes). Matching amendment sections live in both ADRs.

Context

GA 22.8: ERR (Enhanced Relative Rate, UAS 542) pays a relative caregiver from the FIRST day of an eligible kinship placement while the caregiver awaits foster-home approval (a 120-day clock), and ends when the caregiver’s foster per diem begins — the same child must never be double-covered. ADR-052 deliberately shipped no public creation path; ERR is the first eligibility-bearing flow to need one. The binding user principles carry over from ADR-054: one automation path, per-write consent knobs (default off), fully idempotent APIs, no fig leafs, pre-1.0 no backward compat, and CRAIG never decides for a jurisdiction what is acceptable to automate. Unrecorded 22.8 details (rates, proration, the kinship set, clock arithmetic) ship as best readings on the ⁂ #1073 confirmation list — production write policy until DFCS confirms, every one gated behind the F4 consent knob.

Decision

One-shot CREATE-ACTIVE enrollment (M2)

POST /v1/financial/subsidy-agreements births an agreement whose coverage is ACTIVE from placement_started_on — no pending prehistory (F1: the generic two-step create path REFUSES program = err (since #1070 the guard lives in create_guardianship_agreement — the generic create_agreement was deleted), so no path can mint a pending ERR). Ordering in the handler: F4 feature gate (CRAIG_FINANCIALSUBSIDY_ERRENABLED, default OFF, typed 403 naming the knob) → wire validation (parties ≤ 10, roles caregiver/co_caregiver, money scale ≤ 2 normalized to 2 dp BEFORE hashing) → canonical SHA-256 of the payload → authz → jurisdiction → idempotency (F8: client_request_id lookup — hash match replays the existing agreement as 200, mismatch is 409; checked BEFORE every volatile dependency so a replay can never flip to 400/503) → placement S2S verification → one transaction (authorize_worker(CreateActive) → the store create, which re-checks the proof and takes the history lock, then the pay-exclusion child lock, re-runs the per-diem gate against committed state, and inserts → event). An unauthorized caller never reaches the locks or the gate.

  • Authority (F3): ApprovalAction::CreateActive is its own proof kind at the county floor — a transition proof can never mint an agreement, and vice versa (M0’s action-bound proofs).

  • Derivations, never inputs: case_id and assigned_worker_sub come from the placement record; renewal_review_due = effective_from + 12 months; foster_home_approval_due = placement_started_on + policy.clock_days. Evidence keys are honestly named UNVERIFIED references.

  • Backdating floor (F2): coverage below current month − 12 is refused naming the floor — the generator’s reach ends there, and an approved but never-payable agreement is a lie.

  • Placement S2S: craig-placement’s minimal GET /v1/placement/placements/{id}/subsidy-eligibility returns the six fields creation needs; started_on is computed PLACEMENT-side via the Eastern business clock so financial never converts timestamps. Transport failure is a fail-closed 503; service-scoped narrowing is #1107.

  • Policy seam (U1): the fail-closed policy lookup (None → 400); Georgia carries calendar-day proration, the 120-day clock, and the kinship-only qualifying set. As-built after the #1072 lift: the constants live in the GeorgiaBundle’s subsidy_policy() (craig-state-bundle SubsidyPolicySpec), boot-resolved by craig_financial::subsidy::bundle and consulted through SubsidyJurisdictionReference::policy_for — same fail-closed contract, now also refusing on a bundle↔jurisdiction mismatch.

The 120-day clock is report-only (U2)

approval_clock_overdue is computed backend-side for ACTIVE ERR only. An overdue clock never auto-terminates: termination is a human act with reason foster_home_approval_lapsed, witness-checked in the store (a recorded clock that has actually run out; an agreement without a clock fails CLOSED). The reason/program matrix scopes per_diem_begins and foster_home_approval_lapsed to ERR at the store for every caller, imports included.

Per-diem handoff — sweep leg 3 (M3)

A mutable billing row is not a "per diem began" fact, so the handoff is recorded in the durable subsidy_perdiem_handoffs ledger: detection inserts once per (agreement, payment) on EVERY scheduled scan (observe posture included — detection is knowledge, not enforcement), the enforcing run stamps enforced_run_id, and a state-office reinstatement out of a per_diem_begins termination stamps corrected_at + the transition note. Rows are never deleted.

  • Scan shape: ERR agreements with an ACTIVE or SUSPENDED open head that began before as_of, joined to the child’s EARLIEST live placement-origin payment with period_start ∈ [head start, as_of] — deterministic (period_start, id) evidence, pinned (payment + head interval) in the candidate, served by the idx_payments_perdiem_by_child partial index (EXPLAIN-pinned: the index order IS the earliest contract).

  • Same-placement witness (F7): only a per diem on the agreement’s OWN anchor placement is a handoff. A different placement (or a missing anchor) is a MOVED-CHILD report — counted, previewed, never auto-terminated (the operator acts via child_no_longer_in_home).

  • Truth-dating (F6): the termination’s business_date = max(perdiem_started_on, head start + 1 day) — the money and status ledgers agree, and the transition primitive enqueues the overlap months for reconciliation naturally. This is the ONE exception to ADR-053’s enforcement-dates-are-today rule, and it is an exception because the date is a recorded FACT, not a policy choice.

  • Precedence: handoff candidates are excluded from legs 1/2 unconditionally — suspending an agreement whose coverage already ended at the truth date would write a wrong interval on top of it. With the knob off, the backlog waits visibly for the operator.

  • Enforcement: per-agreement transaction under the pay-exclusion child lock (every per-diem writer takes it, including the placement.ended void handler — a void serializes to drift-before or correction-after, never mid-flight), with an in-tx re-verify of the pinned evidence (void, backdated earlier row, or re-point → drift skip, never force). Authority is the third enforcement leg (auto_per_diem_handoff, default OFF), bound to exactly the per_diem_begins termination shape — reason-disjoint from leg 2.

  • Known scan limit (recorded): the window is [head start, as_of], so a per diem that began BEFORE a later manual suspension head is invisible to the sweep — the store’s manual per_diem_begins witness shares the same window. The manual path (operator terminates with the evidence in view) remains; widening the window would let a prior placement’s trailing billing masquerade as a handoff.

The execute acknowledgement handshake (M3)

POST …/sweep/{run_id}/execute carries per-leg acknowledged counts that must EQUAL the preview’s pinned set sizes, else 409 sweep-preview-stale WITHOUT burning the preview. A client that predates a leg serializes no field → 0 → refused whenever that leg has pinned candidates: an old UI can never execute work it never rendered. Checked after the token digest (counts never leak to a token-less probe) and before the CAS flip. craig-web sends the counts it rendered as hidden fields on the preview form.

Completion-event finalize fix (M3)

The sweep’s completion event is staged best-effort AFTER the run row is finalized: a staging failure is WARN + the subsidy_sweep_completion_stage_failures_total counter, never a rewrite of committed counters to a defaulted failed row. The durable run row is the record; the event mirrors it.

Operator surface (M4)

The craig-web enrollment form mints client_request_id once per RENDER (hidden field) — a double-submit of the same rendered form replays to the same agreement. Refusals follow the U5 contract: 4xx ProblemDetails flash VERBATIM (gate, jurisdiction, placement shape, per-diem conflict, office), 5xx/503 flash the generic message. The agreement list and detail surface the approval clock (badge + panel, active-ERR-only semantics); the sweep pages render the leg-3 and moved-children sets and acknowledge exactly the counts they rendered.

Consequences

  • Enabling ERR is ONE knob, and that knob is the jurisdiction’s recorded consent to the ⁂ #1073 best readings (renewal anchor +12 months, the kinship set, calendar-day proration at 2 dp half-away-from-zero, the 120-day clock, whole-month exclusivity, truth-dated handoff, same-placement-only automation, superseded-month arbitration, reinstatement floors, zero-pay mid-month reinstatement months, age-out = age_limit_reached, unverified evidence references, Eastern placement dating, the −12 creation floor). Nothing is silently defaulted into production money policy.

  • Creation only ENQUEUES birth months — the generator tick (or a manual run) pays them; payment visibility follows the generator cadence (runbook: developer-guide § ERR enrollment).

  • The accepted verify→commit TOCTOU on placement state (financial verifies, placement later ends) has detection (the placement.ended subsidy arm
    the ended-anchor invariant) and correction (state-office reinstatement, fact-ledger stamping) rather than prevention — financial holds no placement authority (deliberate; the alternative is a distributed lock across services).

  • The security parser’s ("create", "subsidy_agreement") arm deploys BEFORE or WITH the emitter (parser-first ordering) — noted in the event catalog.

  • Deferred, tracked: #1107 service-scoped placement authz; #1106 legacy payment-index drop (landed 2026-08-07 — the F9 contract step, with a compat-floor bump recording M1 as the compatibility boundary); #1022 api-docs regeneration.

Review record

Every substantive commit shipped behind a fresh-context J1–J8 review with findings remediated pre-commit; the per-MR J-records live as MR notes (!1027–!1032). The M1 fault-abort test satisfies the plan’s kill-based crash test for the reconcile queue (recorded here per the plan’s close-out note). The external review’s disposition index is preserved verbatim in the archived program plan.

Amendment (#1070, 2026-07-24) — the tagged create wire + the created-event payload

POST /v1/financial/subsidy-agreements is now the program-TAGGED request (ADR-056 U1): the ERR arm’s body — and therefore every persisted F8 canonical hash — is byte-stable (the ERR hash stays tag-free by design; new arms bind the tag into their hashes). The pinned subsidy_agreement_created payload gains the ADDITIVE initial_status key (active for this record’s one-shot arm, pending for the guardianship two-step) and its effective_from is arm-specific (ERR: the coverage start, unchanged in value; guardianship: the signing date). The F1 guard’s home moved with the generic create’s deletion; its property is unchanged and test-pinned.

Edit this page · latest