fix: replace create_eligibility_request unconditional sweep with idempotency policy
Audit surfaced (external review of MR-e demo branch, 2026-05-26 EOD).
services/canopy-eligibility/src/store/mod.rs:17 currently marks every pending / in_progress row for a given (application_id, household_id) pair as failed before inserting the new row. The comment acknowledges this as a workaround for the partial unique index idx_unique_pending_request introduced in add_constraints.sql:8.
UPDATE eligibility_requests SET status = 'failed', ...
WHERE application_id = $1 AND household_id = $2
AND status IN ('pending', 'in_progress');Why this is a bug under real concurrency:
- A legitimate in-flight orchestrator run gets marked failed by a second Run Determination click
- Quietly drops the orchestrator's eventual
INSERTof the actual determination (no error surfaces) - The intended SQL invariant (one active request per pair) is bypassed via the same code path that was supposed to enforce it
Why it's currently in: demo retry path. Without the sweep, re-clicking Run Determination after a transient orchestrator error 500s on duplicate key value violates unique constraint "idx_unique_pending_request". The sweep papers over that for the single-worker demo.
Correct behavior:
- INSERT first; if it fails with the unique-constraint violation, return 409 Conflict with a
retry_afterheader tied to the in-flight row'screated_at + TTL. - Stale-TTL sweep runs in a background job (e.g. 10-min cutoff for
pending, 60-min forin_progress) rather than inline on every create. - Worker UI surfaces the 409 as "A determination is already running for this case (started X minutes ago)" — never silently overwrite.
Scope: services/canopy-eligibility/src/store/mod.rs::create_eligibility_request + a new background sweep job + canopy-web /cases/{hid}/run-determination 409 handling.
Labels: type::bug, priority::high, service::eligibility, program::cross-program