CMD Change-Report Pipeline — Rollout & Operations
On this page
Scope: the #575 pipeline (plan) —
determination.requested::Order → the eligibility order substrate → the signed
change-report re-determination → determination.completed (origin echo) → the
medicaid CMD settle.
Deployment ordering (binding-first — MUST)
-
Broker ACLs first. canopy-medicaid’s topic-write allowance gains
determination.requested(devstack:devstack/rabbitmq/definitions.json; production: the same regex change on the medicaid vhost user BEFORE any producer deploy — the broker `ACCESS_REFUSE`s the publish otherwise). -
canopy-eligibility second (the consumer + order sweep). Since #1504 the
determination.requestedconsumers attach on EVERY boot — no longer gated onbulk_runs_enabled— soCANOPY_MQ_PREFETCH_COUNT=1is now a boot requirement for canopy-eligibility everywhere (C7; boot fails loudly without it). -
canopy-medicaid + canopy-web last (the producers — #1506/#1507).
Rolling back reverses the order: producers off first; the consumer drains the queue; ACLs stay (harmless).
Deploy-window notes:
-
In-flight OLD-shape
determination.requestedenvelopes (pre-#1504 bulk payloads without thekindtag) fail to parse on the new consumer → nack-requeue → DLQ after 5 attempts; an affected bulk case recovers via the worker’s redispatch-TTL republish (~30 min). Prefer deploying eligibility with no bulk run inpreviewing/enacting, or pause the run across the window.
Operational surfaces
-
determination_orders(eligibility DB) — the durable order ledger. States:pending → dispatched → succeeded | failed_retryable | failed_terminal.UNIQUE (origin_source, origin_ref)is the dedup identity. -
Retry pacing lives in
next_attempt_at(60s doubling, 1h cap, ±10% jitter); the attempt cap is 12 (ORDER_MAX_ATTEMPTS— a ~8h horizon, so a busy household’s 409 chain cannot terminalize a re-determination inside one working session); the always-on 1-minute order sweep re-publishes due orders and abandons claims older than 2× the self-call timeout. MQ never hot-loops an order. Settles are fenced on the claim’s own timestamp token, so a zombie executor can never settle a successor’s claim. -
Terminal failures (
failed_terminal) are the DLQ-equivalent: surfaced bytracing::error(determination order failed terminally) with the origin identity. Recovery after fixing the cause: reset the row —UPDATE determination_orders SET state='failed_retryable', attempt_count=0, next_attempt_at=now() WHERE id=…— and the next sweep re-runs it; the D4 deterministic dispatch key + the result-replay adoption query make the re-run duplicate-safe. -
Crash recovery needs no operator action: redelivery converges on the order row; a completed-but-unsettled run is ADOPTED via the
(origin_source, origin_ref)correlation oneligibility_requests, never re-executed.
Program scope caution
An order’s programs list is unrestricted, and the orchestrator stamps the
order’s trigger into the generic context every program receives. snap
REFUSES a trigger-carrying context when its replica cannot emit the signed
policy attestation (422 attestation_disabled, the #1213 B8 fail-closed
writer) — so an order that includes snap on a non-attesting fleet burns
its retry budget and terminalizes. Keep orders scoped to programs whose
provenance plumbing is live (medicaid since #1505; snap requires
emit_policy_attestation=true).
Duplicate-order tolerance
A requester resubmitting the same business change under a NEW origin ref (e.g.
the worker resubmits the CMD form after a partial failure) creates a second
order. Both settle against the same facts; the second signed determination
supersedes with previous_determination_id linkage. Expected and harmless —
do not "clean up" the first order.