DS V2 PR-E: answer safety, form foundation, announcements (slices 6+7+8) - #1576
Conversation
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>
…view
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>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 34 minutes Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (11)
📝 WalkthroughWalkthroughThe PR adds typed answer-state and provenance contracts, safety-focused answer components, shared form accessibility components, live route announcements, comprehensive tests, and updated design-system records. ChangesAnswer safety and provenance
Form accessibility
Live announcements and route focus
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aef5cf85a6
ℹ️ 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".
|
@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. The only repository destination is BigSimmo/Database, and the only branch destination is the pull request head branch claude/ds-v2-answer-safety at starting commit aef5cf8; never publish fixes to a detached or synthetic work branch and never create a stacked pull request. Use the authenticated GitHub connector to commit each approved fix to BigSimmo/Database:claude/ds-v2-answer-safety, then verify that the pull request head contains the published commit before reporting success. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. For a fixed thread, reply with as the first line and as the second line. For a no-code disposition, use followed by . These result markers authorize the workflow to close that exact thread only after it verifies a fixed commit is the pull request head; a local-only commit is not a fix. If publication or verification fails, do not use either result marker, do not claim success, and leave the thread open with the blocker. If human input or new authorization is required, do the same. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation. |
|
Codex Review: Didn't find any major issues. 🚀 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
|
@cursoragent Please meticulously resolve all comments and ensure CI passes and address any other issues |
There was a problem hiding this comment.
Pull request overview
This PR builds (but does not yet register/adopt) a DS V2 safety-and-foundation slice set: answer safety primitives (verification wording, degraded-answer banner/state, provenance rendering, clipboard safety), a shared form field shell, and a centralized live-announcement + route-focus mechanism, with accompanying DOM/unit tests and design-system documentation updates.
Changes:
- Introduces
AnswerState+ answer-safety UI components (AnswerCard,VerificationNotice,RetrievalStateBanner,DateDisplay,MissingValue, clipboard text/provenance behavior) with contract and DOM tests. - Adds a shared
FormField+ErrorSummaryfoundation component with accessibility-driven behaviors, plus tests. - Adds a singleton
announce()+LiveAnnouncer+RouteAnnouncerpattern for screen-reader announcements and route focus, plus tests and DS docs/ledger updates.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/ui-v2-form-field.dom.test.tsx | New DOM tests for FormField/ErrorSummary behaviors. |
| tests/ui-v2-components.dom.test.tsx | Updates existing component tests to new DoseLine/AnswerFooter contracts. |
| tests/ui-v2-answer-safety.dom.test.tsx | New DOM tests covering answer-safety components and clipboard text. |
| tests/ui-v2-announcements.dom.test.tsx | New DOM tests for LiveAnnouncer/RouteAnnouncer and dedupe behavior. |
| tests/answer-state-contract.test.ts | New contract test pinning the projection shape from app payload to AnswerState. |
| src/components/ui/verification-notice.tsx | New system-owned verification wording component with state/audience variants. |
| src/components/ui/retrieval-state-banner.tsx | New degraded-answer banner rendering overdue/missing-source details and actions. |
| src/components/ui/missing-value.tsx | New “missing value” component with explicit absence phrases + enum resilience. |
| src/components/ui/live-announcer.tsx | New singleton announcement store + route focus/announce behavior. |
| src/components/ui/form-field.tsx | New shared field shell (hint/error/ids/required labeling) + error summary. |
| src/components/ui/date-display.tsx | New centralized date formatting + missing/invalid handling + relative companion. |
| src/components/ui/answer-state.ts | New projection (answerStateFromRetrieval) and state types for answer surfaces. |
| src/components/ui/answer-card.tsx | Tightens answer surface contracts (required state/verification, actions, provenance, clipboard text, dose model). |
| docs/outstanding-issues.md | Records new follow-up items and advances next-id. |
| docs/design-system/SPEC.md | Updates SPEC phase table and records PR 6 notes/constraints. |
| docs/design-system/GATES.md | Updates Gate 4 status/evidence and clarifies partial-blocking scope. |
| docs/design-system/COMPONENTS.md | Updates component inventory to reflect newly-built/tested symbols. |
| docs/branch-review-ledger.md | Appends a review record for this branch/PR scope. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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.
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.
Import ComponentProps from react, require exact overdue/sourceCount equality for the totality headline, and derive the partial-retrieval numerator from the rendered missing list.
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (1)
src/components/ui/live-announcer.tsx (1)
124-132: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winMove the reactive
setStateout of the effect body or justify it explicitly.Static analysis flags
setSuppressed(true)as a synchronoussetStatecall inside auseEffectbody, which can trigger cascading renders. The call is reachable at runtime whenever a secondLiveAnnouncermounts in production.Consider deriving the suppression flag through
useSyncExternalStoreagainststore.mounted, or, if the effect-based detection is intentional because it must run after commit to observe sibling mounts, add an expliciteslint-disable-next-line react-hooks/set-state-in-effectwith a short justification comment so the exception is documented rather than silently tripping the rule on every lint run.🛠️ Proposed fix (documented suppression)
console.warn(JSON.stringify({ level: "warn", message: "live-announcer: duplicate instance mounted" })); + // eslint-disable-next-line react-hooks/set-state-in-effect -- reacts to a sibling + // instance mounting (external condition), not to committed props/state. setSuppressed(true);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/components/ui/live-announcer.tsx` around lines 124 - 132, Document the intentional effect-based update in the duplicate-instance branch of the LiveAnnouncer mount effect: add a brief justification comment and a narrowly scoped eslint-disable-next-line for react-hooks/set-state-in-effect immediately before setSuppressed(true). Keep the post-commit sibling detection and existing cleanup behavior unchanged.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/design-system/COMPONENTS.md`:
- Around line 203-220: Fix the documentation comment immediately above the
OverdueSource type by removing the premature closing marker between the
reviewDueOn and status paragraphs, leaving one continuous block comment that
closes only before the type declaration.
In `@src/components/ui/answer-card.tsx`:
- Around line 179-190: Update the provenance construction in answerClipboardText
so clipboardProvenanceLine is called only when metadata is supplied, omitting
provenance entirely when metadata is null or undefined. Preserve the existing
multi-source stale_evidence suppression and normalized metadata behavior for
provided metadata.
In `@src/components/ui/answer-state.ts`:
- Around line 135-148: Update overdueSourceFrom so review_due and outdated
sources with synthetic __unidentified_ keys are retained instead of returning
null. Preserve the synthetic key in OverdueSource, and update
RetrievalStateBanner to render these entries without invoking onOpenSource.
Ensure unidentified overdue sources no longer allow the answer state to project
as ready.
In `@src/components/ui/form-field.tsx`:
- Around line 152-165: Update the error-summary container in the form-field
component to expose an appropriate heading role and accessible name via the
heading text. Move the useId() call to the top of the component alongside the
other hooks, apply its generated id to the heading, and connect the focusable
container with aria-labelledby while preserving the existing focus behavior.
In `@src/components/ui/live-announcer.tsx`:
- Around line 119-140: Update the LiveAnnouncer singleton lifecycle around the
useEffect and shared store so suppressed instances can become active when the
current active instance unmounts. Track instance ownership/order and notify
mounted instances when active ownership changes, ensuring exactly one remaining
instance registers a listener and renders announcements while duplicates remain
suppressed; preserve duplicate detection and cleanup behavior.
In `@src/components/ui/missing-value.tsx`:
- Around line 30-43: Update missingValuePhrase to validate the resolved
PHRASES[reason] value is a string before returning it, so inherited keys such as
"toString" and "constructor" follow the unknown-reason fallback and logging
path. Preserve the existing known-phrase behavior and return PHRASES.unknown for
non-string or absent values.
In `@src/components/ui/retrieval-state-banner.tsx`:
- Around line 71-87: Update the everySourceOverdue/scope logic in the
retrieval-state banner so a positive overdue count with sourceCount less than or
equal to overdue.length is treated as totality, preventing contradictory “N of
0” wording. Preserve the existing zero-overdue fallback and use the totality
wording for the superseded and review-date headline branches.
---
Nitpick comments:
In `@src/components/ui/live-announcer.tsx`:
- Around line 124-132: Document the intentional effect-based update in the
duplicate-instance branch of the LiveAnnouncer mount effect: add a brief
justification comment and a narrowly scoped eslint-disable-next-line for
react-hooks/set-state-in-effect immediately before setSuppressed(true). Keep the
post-commit sibling detection and existing cleanup behavior unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: f788a2b2-6ce7-4506-98cc-0c073e0c3e80
📒 Files selected for processing (19)
.design-sync/config.jsondocs/branch-review-ledger.mddocs/design-system/COMPONENTS.mddocs/design-system/GATES.mddocs/design-system/SPEC.mddocs/outstanding-issues.mdsrc/components/ui/answer-card.tsxsrc/components/ui/answer-state.tssrc/components/ui/date-display.tsxsrc/components/ui/form-field.tsxsrc/components/ui/live-announcer.tsxsrc/components/ui/missing-value.tsxsrc/components/ui/retrieval-state-banner.tsxsrc/components/ui/verification-notice.tsxtests/answer-state-contract.test.tstests/ui-v2-announcements.dom.test.tsxtests/ui-v2-answer-safety.dom.test.tsxtests/ui-v2-components.dom.test.tsxtests/ui-v2-form-field.dom.test.tsx
|
@cursoragent Please review all the open comments from coderabbit AI and address all of them and then resolve them for me |
|
All 7 open CodeRabbit comments on PR #1576 are fixed and resolved. CI is green on
No review threads remain open. |
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.


Summary
AnswerCardnow takesstate: AnswerStateandverification: VerificationNoticePropsas required props, so a generated answer that renders without its verification wording, or a degraded answer that renders without its caveat, is unrepresentable rather than merely discouraged. A degraded state additionally cannot be constructed withoutonOpenSource, because the clinician's next act after "this might be stale" is re-verification, not abandonment (DECISIONS §Q1).VerificationNoticeowns the words — there is no children prop and no text prop, and the wording is never interpolated with a model or vendor name.RetrievalStateBannerrenders the caution above the prose and gives every affected source a one-click route back to its cited page.MissingValuereplaces the em-dash with four named phrases for absence, andDateDisplayputs every clinician-facing date through one locale, one timezone, and one absence rule.answerStateFromRetrieval()projects the payload the app layer already receives onto the state the answer surface renders. It reads the server-setdocument_statusgovernance enum and never compares a review date to the clock, so the review policy stays in the source-governance layer where it belongs. A source means a document, not a chunk:RagAnswer.sourcesis chunk-level and several chunks per document is normal, so the projection dedupes and counts bydocument_id. Chunk-level counting is wrong in both directions, and the direction that inflates the denominator under-warns.FormFieldfamily keeps hint and error both in the DOM and both indescribedBywhen invalid, merges caller-supplied ids ahead of its own, supports external ids, renders required/optional as label text rather than colour, and givesErrorSummaryfocus rather than an announcement.announce()with a dedupe window and a queue gap, two visually-hidden live regions, and aRouteAnnouncerthat moves focus to the new<h1>on navigation unless focus sits inside a dialog or adata-preserve-focusworkflow.outdated(superseded) no longer renders in thereview_duevocabulary;DoseRowreplaces an optionaloverdueboolean with a requiredstatus: DocumentStatuswhose overdue values also requiresource;stale_evidencewith an empty overdue list now throws in development and states the caution without a count in production, instead of announcing "0 of 3 sources are past their review date";VerificationNoticefalls back to the most cautionary wording for an unrecognised state rather than failing open toready;DateDisplayno longer invents a time for a date-only ISO; and two wordings that over-claimed were softened.answerClipboardText()now carries attribution and "Verify against the linked source documents before clinical use." on every state includingready, 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, and unattributed AI clinical prose pasted into a psychiatric record reads as clinician-endorsed. SPEC §13 originally scoped the copied caveat to non-ready states; exceeding that is deliberate and is recorded as a deviation indocs/design-system/SPEC.md.src/,worker/andscripts/, where the only importers are the new modules themselves. Adoption is PR 13's job, and three constraints it must satisfy first are recorded in SPEC/COMPONENTS and in the outstanding-issues ledger as fix(ui): clinical UI design-review fixes (differentials, services, forms, favourites) #208, Wire Services/Forms to a Supabase registry (hybrid); label Differentials demo content #209 and fix(eval): resolve tsx runtime from ancestor node_modules in worktrees #210.RAG impact: no retrieval behaviour change — nothing under
src/lib/rag/**, clinical-search, retrieval-selection, ranking-config, answer-ranking, the eval harness, the golden fixture, or the retrieval RPCs is touched.answer-state.tsreads the app-facingRagAnswershape structurally (it does not import the retrieval layer) and projects already-decided fields onto a render state;tests/answer-state-contract.test.tspins that a realRagAnswersatisfies that shape so the two cannot drift.Verification
npm run verify:pr-localnpm run verify:uiwhen UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changedverify:uireported 342 passed / 5 failed. Those five are not attributable to this diff, and the argument is mechanical rather than statistical: no rendered surface imports any of these modules, so Next never bundles them, and a test exercising rendered surfaces cannot execute code that is not in the bundle. On top of that, the same five specs were re-run scoped on this branch and on unmodifiedorigin/main(dd7b65e2a) in a separate worktree with its own build root:verify:ui(347 tests)dd7b65e2ascopedui-smoke.spec.ts:1053mobile search focusui-smoke.spec.ts:3849smart summary badgesui-smoke.spec.ts:3894source text accordionui-phone-scroll.spec.ts:30Tools modeui-phone-scroll.spec.ts:318shared headerThe three
ui-smokeassertion failures pass on this branch when run scoped, so the diff demonstrably does not produce them.ui-phone-scroll.spec.ts:30is parameterised over 13 modes and fails a different subset on every run on both trees — the base tree failedAnswer mode, which this branch passed twice — and where the failure text was captured it readsaddPhoneScrollRunway: #main-content was not rendered, a render-timing failure rather than a geometry assertion. Nothing was re-pinned from a local number.One residual is left open rather than papered over:
ui-phone-scroll.spec.ts:318failed both runs on this branch and passed the single base run. That is 2-vs-1 with no causal mechanism, inside the file already recorded as diverging locally from CI. Local re-runs cannot settle it; CI in the reference environment is the binding verdict and should be read before merge.verify:pr-local)tests/answer-state-contract.test.ts— the standing proof that the typed state contract reaches the app layer with no change tosrc/lib/rag/**orsrc/lib/source-review.tsnpm run eval:retrieval:quality— not applicable: no retrieval, ranking, selection, chunking, or scoring behaviour changed.npm run eval:rag/npm run eval:quality— not run: provider-backed, and no answer generation, synthesis prompt, or answer post-processing changed.npm run verify:release— not run; no release or handoff confidence is claimed here.npm run check:production-readiness— not run: nothing in this diff reaches production behaviour, since no module is imported by any shipped surface.Risk and rollout
formatAnswerRenderCopyText(Wire Services/Forms to a Supabase registry (hybrid); label Differentials demo content #209) or retire the "Review source match" warning the product shows for ungrounded answers (fix(ui): clinical UI design-review fixes (differentials, services, forms, favourites) #208). Both are recorded indocs/design-system/SPEC.md,docs/design-system/COMPONENTS.mdand the outstanding-issues ledger precisely so that adoption cannot inherit this merge as clearance for them.Clinical Governance Preflight
classifyPullRequestFilesreturnsclinicalRisk: falsefor this file list, so this section is not machine-required. It is completed in full because the change defines the wording and the degradation behaviour of the clinical answer surface, and the plan requires the preflight on this PR regardless of what the classifier returns.readyexplicitly does not mean verified: its wording still reads "Verify every clinical claim against the linked source before acting on it." The copied text now carries the same instruction on every state, so an answer pasted out of the app cannot lose it.Clinical KB Database(sjrfecxgysukkwxsowpy) — no Supabase configuration, migration, schema, RPC, or environment value is touched by this diff.MissingValuenames the absence rather than substituting a plausible default.document_statusand never inferred from a date.unknownis treated as unknown, never as current and never as overdue.outdatedis rendered as superseded, in its own shape and its own words, rather than being collapsed into the softerreview_duevocabulary. An overdue source with no recorded review date is kept and shown as "Not recorded" rather than dropped, because a source known to be past review with no review commitment at all is the most alarming case, not the least. Where governance data is internally inconsistent — a stale answer naming no stale source, chunks of one document disagreeing on status — the components fail toward the more cautionary reading and, in development, throw rather than render a caution that argues against itself. An unrecognised state falls back to the strongest available disclaimer, never the neutral one.Notes
docs/design-system/GATES.mdis recorded asimplemented-partialrather than being flipped to blocking. The non-colour status encoding it governs is genuinely in place for the surfaces this PR builds, but the gate cannot honestly block repository-wide while the components are unregistered and the older surfaces are unconverted. The label is deliberately conservative and is called out here so it can be overruled rather than discovered later.partial_retrievalhas no producer. Nothing app-facing names which expected sources were unavailable —retrievalDiagnosticscarries candidate counts andconflictsOrGapscarries prose. The component is built to its specified contract, but adoption can only emitready,stale_evidenceandsource_onlyuntil a separate RAG contract PR adds a named missing-source signal.tests/answer-state-contract.test.tspins the gap so it cannot be papered over in the component layer, and the gap is recorded as issue test(eval): recognize real Discharge Planning docs in golden alias #206.Sets used for enum-resilience warnings are module-level, so on the server they are per-process and unbounded: a persistent data defect logs once at boot and is then swallowed. Acceptable while these components are unregistered; recorded in fix(eval): resolve tsx runtime from ancestor node_modules in worktrees #210 with the Gate 1 contrast pair for--warningused at text tier.Summary by CodeRabbit
New Features
Documentation
Tests