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::CreateActiveis 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_idandassigned_worker_subcome 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 − 12is 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-eligibilityreturns the six fields creation needs;started_onis 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’ssubsidy_policy()(craig-state-bundleSubsidyPolicySpec), boot-resolved bycraig_financial::subsidy::bundleand consulted throughSubsidyJurisdictionReference::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 withperiod_start ∈ [head start, as_of]— deterministic(period_start, id)evidence, pinned (payment + head interval) in the candidate, served by theidx_payments_perdiem_by_childpartial 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.endedvoid 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 theper_diem_beginstermination 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 manualper_diem_beginswitness 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.endedsubsidy 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.