Skip to content

Harden production demo guards and fallback behavior - #90

Merged
BigSimmo merged 6 commits into
mainfrom
codex/production-demo-guard-cleanup
Jun 28, 2026
Merged

Harden production demo guards and fallback behavior#90
BigSimmo merged 6 commits into
mainfrom
codex/production-demo-guard-cleanup

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • Harden production/demo guardrails, setup status, private route behavior, and source-governance refusal paths.
  • Improve extractive RAG fallbacks so unsupported clinical intents fail closed and source-support questions return source names instead of advice.
  • Clean up the favourites UI implementation, application launcher filtering, and mobile/sidebar selectors; reconcile Supabase migration history with an explicit no-op for hosted hnsw.ef_search.

Verification

  • npm run verify:cheap
  • npm run verify:ui when UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed
  • npm run verify:release before release or handoff confidence claims
  • npm run format:check
  • npm run check:production-readiness when clinical workflow, privacy, environment, Supabase, source governance, or deployment behavior changed

Additional checks run:

  • npm run build
  • npm run check:supabase-project
  • npx vitest run tests/supabase-schema.test.ts
  • git diff --check

Notes on unchecked boxes:

  • npm run verify:release was attempted after verify:cheap, verify:ui, and build; the attempt timed out after 10 minutes while running the full Playwright set. Equivalent local coverage completed separately for runtime, lint, typecheck, unit tests, production build, and Chromium UI.
  • npm run format:check currently fails repo-wide on pre-existing .claude/worktrees files and SQL files without a configured Prettier parser. Branch-touched code/docs files were formatted directly where Prettier supports them.

Clinical Governance Preflight

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only
  • Demo/synthetic content remains clearly separated from real clinical sources
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked when clinical decision-support behavior changed

Notes

  • Production readiness reports the expected local .env.local service-role warning and no blocking failures.

@BigSimmo
BigSimmo merged commit e080d29 into mainJun 28, 2026
6 checks passed
@BigSimmo
BigSimmo deleted the codex/production-demo-guard-cleanup branch June 28, 2026 14:18

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

query: queryTextForStorage(parsed.data.query),

P2 Badge Preserve the real question for promoted eval cases

With the default RAG_PERSIST_RAW_QUERY_TEXT=false, this stores query as a redacted-query:<hash> placeholder while line 158 still marks the row as promoted_eval_case; loadCapturedRagEvalCases later uses row.query as the eval question, so npm run eval:quality will ask the hash string instead of the clinician's prompt. For promoted eval captures, either require/retain the real question under an approved retention path or avoid promoting redacted rows.

ℹ️ 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".

Comment threadsrc/lib/rag.ts
return {
answer: boldHighYieldClinicalText(answer, args.query),
body: boldHighYieldClinicalText(answer, args.query),
citationChunkIds: Array.from(new Set(facts.flatMap((fact) => fact.citationChunkIds))),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Surface citations for synthesized facts

When the new fact synthesizer chooses facts from a lower-ranked result, these citationChunkIds can point at chunks that are not included in the top-level citations, because buildExtractiveAnswer still returns the compact citations seeded from the result list and never merges these ids. In that scenario the answer is marked grounded but the visible citations can omit the actual supporting chunk or cite an unrelated top result, which breaks source verification for clinical answers.

Useful? React with 👍 / 👎.

Comment threadsrc/lib/rag.ts
return tokens.some((token) => queryTokenMatchesText(token, text));
return (
answerIntentEvidencePattern(intent).test(normalized) &&
(intentTokens.length === 0 || intentTokens.some((token) => queryTokenMatchesText(token, normalized)))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Accept synonym evidence for intent-gated facts

For monitoring, renal-dose, and red-result questions, queryIntentTokens injects canonical words such as monitoring, renal, or threshold, but this check then requires one of those exact words even after answerIntentEvidencePattern has already accepted equivalent source language like “check levels every 3 months”, “eGFR < 30”, or “ANC < … stop”. Those directly relevant sentences are filtered out before synthesis, causing unsupported gap answers despite matching evidence.

Useful? React with 👍 / 👎.

Comment threadsrc/lib/rag.ts
.replace(extractiveLabelPattern, " ")
.replace(/^[\s\-•:]+/, "")
.replace(/^(?:monitoring|dose|dosing|source|section|table|guideline)\s*[.;:,-]\s*/i, "")
.replace(/([A-Za-z)])\s*(\d{1,2})(?=(?:[,.;]|\s|$))/g, "$1")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Do not strip clinical numbers from extracted facts

This cleanup regex removes any 1–2 digit number that follows a word, so common evidence such as “maximum dose 20 mg daily”, “reduce dose 5 mg”, or “check every 3 months” becomes “maximum dose mg daily”, “reduce dose mg”, and “check every months” before fact synthesis. For dose, threshold, and monitoring answers this drops the actual clinical values from otherwise supported source sentences, which can produce unsafe or unusable guidance.

Useful? React with 👍 / 👎.

BigSimmo pushed a commit that referenced this pull request Aug 13, 2026
… writer (#313)
Re-prioritising is the mutation triage performs most often, and it was the one
mutation the ledger tooling could not express. AGENTS.md and the ledger both
require mutating rows only through the writer — precisely because hand-authored
rows produced the width, escaping and wrong-table failures the writer exists to
stop — so every demotion had to take the forbidden path.
npm run issues:update -- '#151' --pri P3
The gap turned out to be three layers deep, and each one looked fine while the
others were broken:
1. scripts/outstanding-issues.mjs — updateIssue's editable map had no `pri`
entry, so the Pri cell was unreachable.
2. scripts/ledger-inbox.mjs createRequest — the update payload was built from
summary/detail/source only, so even once updateIssue accepted pri, the CLI
could not emit it.
3. scripts/ledger-inbox.mjs validateRequest — required one of
summary/detail/source, so a pri-only request was rejected before it reached
either of the above. A re-prioritisation with no prose change is a
legitimate triage edit and now counts on its own.
Values are validated against the same P1/P2/P3 set `add` uses, at both the
request boundary and the writer, and rejected before the guarded write so a bad
value reports itself rather than surfacing as a gate refusal about a malformed
row. Deliberately per-row: no bulk re-prioritise mode, because a sweep that
moves many rows at once should be visible row by row in review.
Every assertion here was mutation-tested rather than merely observed passing.
Removing `pri` from the editable map makes the writer throw on a pri-only
request; dropping the createRequest passthrough makes the CLI reject it;
dropping it from the validator fails the inbox self-test. The writer's own
self-test additionally pins that ONLY the Pri cell moves — an off-by-one there
would write a priority over the ID or Type cell and still produce a
structurally valid row.
Verified end to end through the real CLI: `issues:update '#90' --pri P3` queues
a valid merge-safe request.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuYJz8hauCsCdx8r4fXiZU
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@BigSimmo