Wire Services/Forms to a Supabase registry (hybrid); label Differentials demo content - #209
Conversation
…se 1) Adds the curated registry backing the Services and Forms modes: - clinical_registry_records (kind service|form, full ServiceRecord shape, conservative governance columns) + clinical_registry_record_sources join table for source-document linkage, service-role-only RLS - schema.sql mirror + supabase-schema test assertions - database.types.ts entries for both tables - src/lib/registry-records.ts bidirectional mappers with conservative governance derivation (never emits approved from seeding) - scripts/seed-registry-records.ts (registry:seed) upserting the mock fixtures per owner; dry-run by default Migration is a repo file only - NOT applied to the live project. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…(phase 2) - GET /api/registry/records?kind=service|form&q=&limit= and /api/registry/records/[slug]?kind= following the documents-route pipeline: demo-mode mock short-circuit, admin client, authenticated owner scoping on every query, new 'registry' rate-limit bucket (120/min), governance metadata + linked-document passthrough, Cache-Control private/no-store - non-breaking ranker extraction: rankServiceRecords/rankFormRecords operate on an injected records array; searchServiceRecords/ searchFormRecords delegate with the mock fixtures as defaults - tests/registry-records-route.test.ts covers the same per-route contract the aggregate suites check (demo short-circuit, 401, 400 invalid kind, owner scoping, 429, 404, demo detail) so no duplicate blocks were added to api-route-coverage/api-validation-contract Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- detail routes drop generateStaticParams/mock lookups; thin server pages render client loaders (RegistryRecordLoader) with skeleton, sign-in, not-found, and error states; ServiceDetailPage/FormDetailPage and saved-slug localStorage behaviour unchanged - services/forms home verification footers now count fetched registry records via useRegistryRecords (hidden until ready) - ClinicalDashboard record matches rank registry-fetched records client-side (one fetch per active mode; live-typing behaviour unchanged); forms search results page same treatment - searchScopeFiltersSchema gains labelTypesAny (match any label of the requested types); services/forms searches default the corpus scope to service-labelled / form-type documents with user filters winning - browser QA (demo mode): 13YARN detail, unknown-slug not-found state, services search shows the registry-backed verified record card, forms search best-matches table, home footers Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…(phase 4)
- warning banner (differentials-demo-content-notice) above the results
grid: ranked diagnoses are synthetic demonstration content; source
counts reflect real library matches
- replace the misleading 'Local results only / Reviewed content
prioritised' strip with 'Demonstration ranking - synthetic content'
- SourceStatusCard: real library-match count kept, fabricated '312
sources' row replaced with an explicit Demonstration row
- honest wording for the home subtitle, no-evidence notice, and
evidence pills ('Library matches'); dynamic demo result count
- Playwright: differentials search flow now asserts the demo notice
renders with the results view
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>Migration 20260703020000 applied to the live Clinical KB Database (sjrfecxgysukkwxsowpy) with explicit user approval. Both tables present, RLS enabled, service-role-only, 0 rows; security advisors clean. Notes the UTC version-recording nuance (live version 20260702183308) and the remaining per-owner seed step. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:d8652807f0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
BigSimmo
commented
Jul 2, 2026
@copilot resolve the merge conflicts in this pull request |
Head branch was pushed to by a user without write access
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:32fd31a006
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- registry hooks: gate fetches until the auth provider resolves the session (skip while status==='loading'); only markSessionExpired on 401 when authenticated, so a still-loading header can't clobber a valid session. List hook is keyed by kind so a Services<->Forms switch never ranks the previous kind's records (no wrong-mode links). - registry-record-loader: surface the retryable error state before the not-found branch, so a failed request (registry not migrated, Supabase down, network) no longer shows the misleading 'not seeded' copy. - forms-search-results-page: RegistryStatusNotice renders loading/ sign-in/error states instead of a silent '0 forms'. - /api/registry/records: return the full owner+kind set (capped at 500) so client-side ranking/counts see every row; 'limit' now bounds only the ranked matches. New test covers the >limit case. - differentials-home: 'Library matches' badge counts documentMatches, not the whole indexed library; dropped the now-dead documentCount prop chain (SearchResultsView -> DifferentialsHome -> ClinicalDashboard). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aude/bold-pare-3e568c
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:2d25f08c4a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- ClinicalDashboard: drop the auto-applied Services/Forms mode-default label scope. It routed every mode search through resolveSearchScope's label path, whose single document_labels.in(<all ids>) request builds an over-long PostgREST URL that fails on large libraries (the live corpus has 2065 docs). Corpus search now runs unscoped like Documents; users opt into label filters explicitly. - services/forms home footers: count verified records from the API's authoritative governance (validation_status) via countVerifiedRegistryRecords, not the copied fixture verification JSON, so later review/downgrade is reflected. Hook now exposes a governance map; the demo API path derives it from fixtures. - registry-record-loader: the unauthorized detail state now offers a 'Go to sign in' action routing to the auth-capable dashboard, instead of only a home link with no way to start sign-in. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
- registry detail hook + [slug] API now carry governance; the loader reconciles the verified badge from the authoritative validation_status so a reviewed/downgraded record no longer shows the stale fixture verification state. - DocumentSearchResultsPanel takes recordStatus and renders a loading/ sign-in/error notice for Services/Forms record matches, so the main dashboard no longer silently shows zero cards when the registry fails. - seed-registry-records preserves existing governance columns (source_status/validation_status/last_reviewed_at/review_due_at) on reseed, so a fixture-copy reseed can't downgrade reviewed rows. - the 'apply default mode scope to shortcut searches' finding is moot: the mode-default scope was removed entirely in the prior commit, so executeSearch and shortcut searches are consistently unscoped. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:8f094ade63
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .from("clinical_registry_record_sources") | ||
| .select("record_id, document_id") | ||
| .eq("owner_id", user.id) | ||
| .in("record_id", recordIds); |
There was a problem hiding this comment.
Batch optional registry source lookups
When an owner has hundreds of registry rows, this .in("record_id", recordIds) serializes every row UUID into one PostgREST request (roughly 20KB at the 500-row cap) before the list response can be returned. The current list hook does not consume linkedDocumentIds, so this optional source-link lookup can hit URL/proxy limits or otherwise fail the whole Services/Forms registry load even though the records themselves were fetched successfully; omit it from the list endpoint or fetch the links in smaller pages/batches.
Useful? React with 👍 / 👎.
| } | ||
| export function FormsHomePage() { | ||
| const registry = useRegistryRecords("form"); |
There was a problem hiding this comment.
Handle empty registry states on mode homes
When an authenticated owner is newly migrated but not seeded yet, or this registry request returns unauthorized/error, the hook state is only used to hide/change the footer while the Forms home still renders the static task cards, including the fixture default detail link; clicking it lands on a registry-backed 404 with no empty/sign-in/error state. Gate the actions on registry.status/registry.total or show the same registry status/empty notice here (the Services home uses the same pattern).
Useful? React with 👍 / 👎.
| status === "loading" | ||
| ? { icon: Loader2, spin: true, tone: "info", text: "Loading your forms registry..." } | ||
| : status === "unauthorized" | ||
| ? { icon: Shield, spin: false, tone: "warning", text: "Sign in to search your forms registry." } |
There was a problem hiding this comment.
Provide a sign-in action on forms search
On an authenticated deployment, a signed-out user who lands directly on /forms?q=...&run=1 now gets only this text notice because the route returns FormsSearchResultsPage instead of the auth-capable dashboard shell. There is no AuthPanel or sign-in link on this standalone search page, so the user cannot act on the message; add a sign-in control here or route unauthorized users to a surface that can start sign-in.
Useful? React with 👍 / 👎.
BigSimmo
commented
Jul 2, 2026
…+8) (#1576) * DS V2 PR-E: answer safety, form foundation, announcements (slices 6+7+8) Build the five specified safety components so a degraded answer, an absent clinical value, an invalid field and a route change all become states the type system and assistive technology can see. Slice 6 - answer safety. VerificationNotice owns eight approved strings (four states x clinician/plain); AnswerCard requires both `state: AnswerState` and `verification`, and a degraded state cannot be constructed without the `onOpenSource` route that turns the caution into an affordance. DoseLine takes a structured dose model, composes Quantity, and marks overdue in three channels (amber rule + "Source review overdue" + StatusMark shape). MissingValue replaces the bare dash with four phrases. DateDisplay takes ISO only and renders <time>. AnswerFooter now names an absent field instead of dropping it - on a provenance strip the absence is the governance signal. answerClipboardText carries the degraded caveat out of the app and routes provenance through the one existing clipboardProvenanceLine() implementation. Slice 7 - FormField/FieldHint/FieldError/ErrorSummary. Hint and error are both in the DOM and both in describedBy when invalid; caller ids merge ahead of them rather than being overwritten; required/optional is label text; ErrorSummary takes focus rather than announcing. Slice 8 - LiveAnnouncer/RouteAnnouncer/announce. One singleton owns every announcement, with a dedupe window and a queue gap; route change moves focus to the new <h1> unless focus sits inside a dialog or a preserve-focus workflow. Step 0 contract pre-check passed with no change to src/lib/rag/** or source-review: ready, stale_evidence and source_only all project from fields the retrieval layer already decides, so no component infers staleness from a date. partial_retrieval has no producer and is recorded as such in SPEC, COMPONENTS, /issues #206 and tests/answer-state-contract.test.ts. All components are built, not registered - registration and product adoption stay in PR 13. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ds-v2): tighten answer-safety states after clinical governance review Eight findings from the PR 6 clinical governance review, all in code this branch introduced, all safety-positive. No product surface imports any of these modules yet, so nothing rendered changes. Counting and identity - answerStateFromRetrieval() counts and keys by document, not chunk. RagAnswer.sources is chunk-level, so the previous projection produced duplicate React keys, repeated a document as several overdue rows, and reported fractions that were wrong in both directions — including the direction that under-warns ("1 of 6" when half the documents are stale). - Where chunks of one document disagree on governance status, the more severe reading wins. Governance status carried, not collapsed - OverdueSource gains `status`, and DoseRow replaces the optional `overdue` boolean with a required `status: DocumentStatus`. An overdue status additionally requires `source`, so a warned row with no route back to the document is unrepresentable. `outdated` (superseded) now renders the slashed mark and the word "superseded" rather than the half-ring "review overdue" vocabulary of a document still in force. Cautions that cannot argue against themselves - stale_evidence with an empty overdue list now throws in development (mirroring the partial_retrieval guard) and, in production, states the caution without a count instead of "0 of 3 sources are past their review date". Same rule in answerClipboardText(). - VerificationNotice falls back to the most cautionary wording for an unrecognised state, and logs once, rather than failing open to `ready`. - The stale clinician wording says "some of which are past their review date" rather than asserting every cited source is overdue. - SourceOnlyBody no longer claims "nothing has been paraphrased": the tier is inferred from the routing mode and the extractive builder composes sections, so the surface cannot stand behind verbatim fidelity. Provenance precision - DateDisplay renders a date-only ISO as a date, even for kind="generated". It previously printed "14/03/2026, 08:00" for "2026-03-14" — a precision that was never recorded, on a provenance strip. Clipboard (exceeds the original SPEC slice-8 scope, deliberately) - answerClipboardText() now carries attribution and "Verify against the linked source documents before clinical use." on every state including `ready`, enumerates the cited documents, and suppresses the single-document provenance line where it would contradict a multi-source stale caveat. A copied answer loses the banner, the notice and the links; unattributed clinical prose in a record reads as clinician-endorsed. It remains narrower than formatAnswerRenderCopyText and is not a replacement for it. Recorded rather than fixed, so PR 13 cannot inherit them as clearance: AnswerState has no channel for an ungrounded answer (#208), the clipboard constraint above (#209), and the Gate 1 contrast pair for --warning at text tier plus the per-process logged-once Sets (#210). All three are also written into SPEC.md and COMPONENTS.md. Co-authored-by: Cursor <cursoragent@cursor.com> * chore(ledger): record PR-E answer-safety review at 8ad91e3 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ds-v2): resolve PR-E review findings for answer safety Filter staleness to cited supporting sources, reject rolled calendar dates, make duplicate production announcers a no-op, re-focus ErrorSummary on repeated submits, clear-then-set identical live announcements, and realign AnswerCard/DoseLine dtsPropsFor declarations. * fix(ds-v2): suppress duplicate LiveAnnouncer without lint cascade Keep production duplicate ownership in an effect with a targeted set-state-in-effect exemption, matching the repo pattern, so the duplicate never registers a listener and unmounts its live regions. * fix(ds-v2): address Copilot findings on banner and form tests Import ComponentProps from react, require exact overdue/sourceCount equality for the totality headline, and derive the partial-retrieval numerator from the rendered missing list. * fix(ds-v2): address CodeRabbit findings on answer-safety surfaces Merge the broken OverdueSource doc comment, omit clipboard provenance when metadata is absent, keep unidentified overdue sources as stale, give ErrorSummary an accessible heading name, fail over LiveAnnouncer ownership when the active instance unmounts, harden MissingValue phrase lookup against prototype keys, and treat sourceCount underflow as totality. --------- Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
Replaces the static mock data behind the Services and Forms modes with an owner-scoped Supabase registry (hybrid strategy: curated records + real corpus search), and honestly labels the Differentials synthetic content. Four phases, one commit each:
clinical_registry_records(kind service|form, full record shape, conservative governance columns) +clinical_registry_record_sourcesjoin table for source-document linkage; service-role-only RLS; schema.sql mirror + test assertions;npm run registry:seedupserts the reviewed fixtures per owner (dry-run by default, never emitsapproved).GET /api/registry/records+/[slug]following the documents-route pipeline (demo-mode mock short-circuit, authenticated owner scoping on every query, newregistryrate bucket, governance + linked-document passthrough). 8 new contract tests.labelTypesAnyscope filter lets Services/Forms searches default the corpus scope to service-labelled / form-type documents (user filters win).Verification
npm run verify:cheapper phase — final: 915 unit tests pass, lint/typecheck cleannpm run verify:ui— 69/69 Chromium tests on the final tree (includes new demo-notice assertion; 13YARN specs pass through the registry API demo path)npm run format:check(prettier applied per phase)npm run check:production-readiness— env-key FAILs are the known worktree condition (no.env.local); unchanged from mainnpm run verify:release— not a release handoffClinical Governance Preflight
source_status/validation_status; seeding derives conservatively and never emitsapproved; differentials synthetic content now explicitly labelled)Clinical KB Database(sjrfecxgysukkwxsowpy)demoMode: true; authenticated users never silently see mock data; differentials demo banner)Post-merge user steps (NOT performed here)
20260703020000_clinical_registry_records.sqlto the live project (explicit approval step).npm run registry:seed -- --owner-id <uuid> --write --confirm.Until then, authenticated users see an honest empty-registry state; demo/env-less deployments are unaffected.
🤖 Generated with Claude Code