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)

  1. 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).

  2. canopy-eligibility second (the consumer + order sweep). Since #1504 the determination.requested consumers attach on EVERY boot — no longer gated on bulk_runs_enabled — so CANOPY_MQ_PREFETCH_COUNT=1 is now a boot requirement for canopy-eligibility everywhere (C7; boot fails loudly without it).

  3. 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.requested envelopes (pre-#1504 bulk payloads without the kind tag) 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 in previewing/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 by tracing::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 on eligibility_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.

Edit this page · default