fix: resolve reconciled adversarial review findings R01-R34

Merged
BI
1mo ago
+5686-1094

Summary

Resolves all 34 reconciled adversarial review items (R01–R34) plus the 5xx and database-advisor diagnostic items:

  • R01, R02, R18 & R34 (Unified Release Identity, Worker Pause & Control-Plane Deadlines): Bakes deploy/release-identity.json (commitSha, sentryRelease) into both Dockerfile and Dockerfile.worker, exposes deploymentCommitSha and claimLoopPaused on /api/health and worker /healthz, supports WORKER_PAUSED=true / WORKER_CLAIM_LOOP_ENABLED=false so the worker stays paused when unified main promotes, and wraps all worker control-plane Supabase queries/RPCs in finite 30s deadlines (WORKER_CONTROL_PLANE_TIMEOUT_MS).
  • R03, R09, R17 & R32 (UI, Citation Hit Targets, CPD Draft Persistence, Pilot Labels & Restored Answer Notice): Clips .answer-source-mark::before and .answer-source-mark-overflow::before hit areas at calc(-0.5 * var(--answer-mark-gap)) with center/inner-edge elementFromPoint assertions; persists CPD plan edits across internal navigation and failed saves in sessionStorage; labels uncited "Also found" rows as "Searched, not cited", populates fallback inline snippets, and distinguishes .txt ("TXT", "View original text") from .pdf; and renders a "Restored from earlier in this session" age banner with a "Recheck now" button on restored answers.
  • R04, R05, R06, R07, R08, R20 & R23 (RAG Cancellation, Latency, Tie-Break, Visual Evidence, Comparison & Pilot Isolation): Threads AbortSignal into correct_clinical_query_terms, fetchMemoryCardsForQuery, loadChunksForMemoryCards, attachDocumentContextPackAdmission, fetchBestDocumentLookupChunks, fetchDocumentTitleAliasRows, loadChunksForSignalMatches, fetchRelatedDocuments, hydrateCandidatesWithMetadataAndMemory, and packAdjacentSourceContext; adds document_title_phrase clamped-score tie-breaking in rankCandidatesForSelection; hydrates chunk-linked result.image_ids, re-evaluates clinical_use_class === "ambiguous" table crops, and retains same-document visual evidence; applies withAdmissionDischargeContextPair / withAdmissionDischargeSideEvidence to answerInputResults and selectedContextPair; isolates pilot-ticket expiry so OWNER_ID is never blocked on unrelated documents/queries; deduplicates deep-memory.ts; and adds worker ranking producer paths to ragRankingPatterns in scripts/pr-policy.mjs.
  • R10, R11, R12, R21 & R22 (Pre-Parse Streamed Body Limits, Paperwork Sync, Strict Schemas & Open-Shift Idempotency/Alerts): Adds readBoundedFormData and readBoundedJson pre-parse byte limits to /api/cme/missed/[id], /api/teaching/assessments/dual-sign-off, /api/teaching/import/read, /api/speech/transcribe, and /api/upload; enforces server-side firstPaperworkPatientProblem on adminPaperwork sync; enforces .strict() recursive Zod schemas on teachingTermTracker, teachingExamPrep, and cpdApplications; removes client-side similarity suppression in open-shifts-post-page.tsx and evicts non-active shift keys in public.roster_open_post_once; and tracks alert delivery in public.roster_open_post_alerts so replayed open.post actions recover failed notifications without duplicating delivered ones.
  • R13, R14, R15, R16, R19, R24, R25, R26, R27, R28, R29, R30, R31 & R33 (Trivy Scan Gate, Runtime Check, Live Admin Role Check, Credentials, Retention, SLOs & Test Boundaries): Requires structured JSON STATUS=ok for both app and worker images in scripts/trivy-image-scan.mjs and docker-image.yml; verifies @types/node major parity in scripts/check-runtime.ts; re-verifies live auth.users administrator status inside public.admit_administrator_document; enforces a 90-day inactivity TTL in public.calendar_feed_owner, adds revokeEpaMagicLinkById and display.revoke by occurrenceId, and supports DEVELOPER_AREA_ACCESS_KEY_PREVIOUS / HEALTH_DEEP_PROBE_SECRET_PREVIOUS rotation overlap; expands SLO alerting (source-gap and unsupported rates are now counted server-side over the whole window) and 6-hourly ops-digest.yml cadence; tightens maintainability budgets; and adds SQL/unit boundary tests.

Areas touched: Data platform and access, Personal practice, App experience, Delivery and assurance, Answer engine, Source intake and indexing, Knowledge and records, Clinical reference content, Design system, Prototypes
RAG impact: no retrieval behaviour change — the title-phrase tie-break and the other ranking/evidence changes moved to draft #3599 (canary pair required there); this PR keeps only cancel-signal plumbing in the retrieval path.

The RAG-affecting parts are the document_title_phrase clamped-score tie-break (rag.ts / retrieval-selection.ts) and the admission/discharge side-evidence file. The AbortSignal plumbing, visual-evidence retention and worker producer path coverage are not intended to change ranking. A live eval-canary before/after pair is required before merge and has not been run.

Verification

  • npx vitest run on all 27 affected unit and DOM test suites (176 tests passed)
  • npm run typecheck (passed, 0 errors)
  • npm run lint (passed, 0 warnings)
  • npm run check:maintainability-budgets (passed)
  • npm run check:migration-immutability & npm run check:migration-role & npm run check:function-grants (passed)
  • npm run check:diff-integrity (passed, 340 -> 403 test cases)
  • npm run check:organisation & npm run check:pr-policy & npm run check:runtime & npm run docs:maintain:check (passed)
  • npm run drift:manifest (replayed cleanly against scratch Postgres)

Risk and rollout

  • Database migration and merge gate: supabase/migrations/20261016210000_adversarial_review_rpc_and_ttl_hardening.sql is additive and backward-compatible (CREATE OR REPLACE FUNCTION, CREATE TABLE IF NOT EXISTS public.roster_open_post_alerts, ALTER TABLE public.calendar_feed_tokens ADD COLUMN IF NOT EXISTS rotated_at). This PR contains a migration, so merging it reaches the live clinical database within seconds. Merge only inside an approved window and only with the owner's explicit permission for this database change.
  • Drift manifest: supabase/drift-manifest.json must be regenerated with npm run drift:manifest (needs Docker) before merge; the Migration replay check fails until it is.
  • Worker pause preservation: WORKER_PAUSED=true is configured on the Railway worker service so promoting the unified main release keeps the background PDF claim loop paused while serving /healthz with claimLoopPaused: true and deploymentCommitSha.
  • Rollback: Revert the commit on main; database functions and roster_open_post_alerts remain backward-compatible with prior app/worker builds.

Clinical Governance Preflight

  • If this PR changes clinical prompts, answer generation, citation logic, retrieval ranking, source policy, or clinical UI presentation, I reviewed docs/clinical-governance.md and recorded the clinical safety impact above.
  • If this PR adds or changes clinical guidance sources, I checked approval metadata and provenance requirements (npm run check:source-governance where applicable).
  • Real patient identifiers, clinical notes, and private health information are not included in code, tests, fixtures, screenshots, or PR text.

Summary by cubic

Resolves the 34 reconciled adversarial review findings (R01–R34) across the app, worker, and database, hardening security and operational boundaries without changing retrieval behavior. The ranking and evidence changes originally planned here (title-phrase tie-break, community admission preference, admission/discharge context pairing, ambiguous-clinical-image handling, direct image_ids attachment) were split out into a separate draft PR that needs a live eval-canary pair before merge.

Application behavior

  • Threads AbortSignal cancellation through RAG query, candidate, and context functions; isolates pilot-ticket expiry so unrelated documents and queries never block the pilot owner.
  • Fixes in-prose source mark hit areas, labels uncited rows "Searched, not cited", distinguishes .txt from .pdf, and shows a "Restored from earlier in this session" banner with a recheck action that survives a failed recheck.
  • Persists CPD plan drafts in sessionStorage scoped to the account owner; adds bounded pre-parse body limits to five upload/transcribe/import routes, strict schemas on teaching and CPD sync, and server-side open-shift post idempotency whose alert delivery is deduplicated, persisted only after success, and retried after lookup or in-flight failures.
  • Snapshots prior teaching display links before creating replacements, so revoking by occurrenceId never orphans an active link.

Release, security, and operations

  • Bakes a release-identity manifest into both Dockerfiles; health endpoints expose the deployment commit SHA and worker claim-loop pause state, and worker control-plane queries run under finite 30s deadlines.
  • Makes the Trivy image scan fail closed on structured STATUS=ok JSON evidence for both images (writable cache mount so the scans actually run) and verifies @types/node major parity.
  • Re-verifies live administrator status in document admission, enforces a 90-day calendar feed TTL, adds magic-link revocation by occurrenceId with replacement links created before revocation, and supports previous-secret rotation overlap.
  • Adds source-gap, unsupported-answer, and latency-budget SLO alerts (budgets keyed to each route's routing mode), moves the ops digest to 6-hour sampling, records recovery-drill RPO/RTO metrics, and ships a backward-compatible migration with incident runbook SQL switched to payload::text.

Summary by CodeRabbit

  • New Features
    • Restored answers now show when they were saved and note that library documents may have changed, with an option to recheck the answer.
    • Answer sources more clearly distinguish documents that were searched from those cited, and link to the appropriate original file.
    • CME plan drafts persist across page visits and are kept separate between accounts.
  • Bug Fixes
    • Uploads are checked against size limits, and failed image scans now stop the release check.
    • Open-shift retries handle cancelled or otherwise unavailable shifts without navigating to them.

🤖 Generated with Claude Code

https://claude.ai/code/session_016yeqgRrWD7MVZn8jHbpVsH