fix: normalised determination envelope for orchestrator signature verification (medicaid signatures broken in transit)

Context

The orchestrator (services/canopy-eligibility/src/orchestrator.rs) re-serialises ProgramDeterminationResponse and verifies against the persisted JWS. canopy-medicaid signs MedicaidDetermination — which carries fields NOT present on the response shape: assigned_coa_track, benefit_type, denial_reason, denial_reason_codes, fmap_rate, continuous_eligibility_end, medicaid_application_id, person_id, created_at.

Result: re-serialised bytes differ from signed bytes. Medicaid signatures verify as invalid on the orchestrator side today. The current band-aid is the sig_verified branch wrapping all Medicaid capture — quarantined determinations don't propagate medicaid_assigned_group into combined results, so the bug is contained but the program-service signing is effectively load-bearing waste for medicaid.

The medicaid-orchestrator-ee15-wiring plan (now archived, line 249 of its Errata) called this out and explicitly punted the fix to a follow-up plan — "File as determination-envelope-normalisation.adoc when the orchestrator signature-verification pipeline is reworked." That plan was never filed.

This is acknowledged broken in the CHANGELOG (line 184). Surfaced by the multi-agent audit 2026-05-05 as the highest-priority untracked correctness gap.

Acceptance criteria

  • Design doc / plan filed at docs/modules/ROOT/pages/plans/determination-envelope-normalisation.adoc with at least: a SignableDetermination envelope (or equivalent) shared between program services and orchestrator, the byte-stable serialisation policy, and migration-or-coexistence path for in-flight quarantined determinations.
  • All 5 program services (snap / tanf / medicaid / caps / wic) sign the normalised envelope rather than their internal *Determination struct.
  • Orchestrator verifies the same envelope; signature-quarantine becomes a real failure mode rather than a per-program design choice.
  • Integration test: a Medicaid determination at every COA path reaches approved status in the combined result (no signature_quarantined masking the bug).
  • CHANGELOG entry flips the "known issue" line under canopy-medicaid signature verification.

Why priority::high

Without this, Medicaid determinations cannot pass the orchestrator's signature gate — every single Medicaid case requires the quarantine band-aid. ADR-002 (signed determinations as the trust boundary) is effectively unenforceable for Medicaid. This blocks Pub 1075 ATO-quality evidence for the Medicaid path and any production deployment that would actually rely on per-program signing.

Surfaced by

Multi-agent plan-vs-code audit, 2026-05-05.