chore: close #[utoipa::path] response-annotation gaps surfaced by the #620 doc refresh

Context

The #620 (closed) API-page refresh (rendered each page against the regenerated OpenAPI snapshots) surfaced a consistent gap: several handlers emit error/response codes at runtime that their #[utoipa::path] decorators do not declare, so the published OpenAPI contract under-describes the real surface. The doc pages now describe the true behavior in prose (and conservatively keep the codes in their Error Codes tables), but the machine-readable spec — which is the ATO API-reference artifact per #265 (closed) — should be made to match the handlers.

Annotation gaps to reconcile (per service, verify actual emit before adding)

  • canopy-appeals — no 403 declared on any operation despite role-gated handlers.
  • canopy-applications — 409 (status-transition) emitted but not declared; POST /v1/applications/{id}/programs/{program}/determination declares only 200 (no 4xx for the denial-reason-required path).
  • canopy-enrollmentPOST /v1/enrollments/{id}/terminate has no request_body annotation though it accepts TerminateEnrollmentRequest; 403/409/422 undeclared.
  • canopy-medicaid — 422 (invalid enum) undeclared; GET /v1/determinations prose claims household_id/person_id filters the contract doesn't declare.
  • canopy-notices — 400/500 (Typst template compile failure) undeclared; confirm whether validation is 400 or 422.
  • canopy-persons#[into_params] query structs on the export endpoint render as in:path / required:true in the spec (utoipa quirk — they are optional query params); 409 (duplicate SSN / household conflict) undeclared.
  • canopy-renewals — 409 (duplicate active certification) undeclared.
  • canopy-reporting — 502 (upstream service unavailable during HTTP assembly, ADR-001) undeclared; status query param marked required+nullable.
  • canopy-eligibility — 502 (circuit-breaker open / program unreachable), 400, 401 undeclared per-operation.

Acceptance criteria

  • For each item: confirm the code is actually emitted (read the handler), then add the matching responses(...) / request_body annotation (or fix the prose if the code is NOT emitted).
  • Resolve the #[into_params] path-vs-query mislabeling on canopy-persons export.
  • cargo xtask api-docs --update regenerates clean snapshots; the #620 (closed) pages already match the intended surface, so re-running should show the new codes appear in the JSON.

Out of scope

Doc prose (already corrected in #620 (closed)). This issue is the source-side #[utoipa::path] reconciliation only.