Skip to content

fix(rag): route the medication_dose_risk class to the strong route before the deadline (A1 R1, #231) - #2035

Merged
BigSimmo merged 3 commits into
mainfrom
claude/s1b-rag-dosing-routing-6u1mik
Aug 17, 2026
Merged

fix(rag): route the medication_dose_risk class to the strong route before the deadline (A1 R1, #231)#2035
BigSimmo merged 3 commits into
mainfrom
claude/s1b-rag-dosing-routing-6u1mik

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • Packet S1b (A1 mitigation-ladder rung 3, residual R1 of issue fix(ci): bump checkout/setup-node/cache actions to v5 #231): route the medication_dose_risk query class to the strong answer route in chooseAnswerRoute (src/lib/rag/rag-routing.ts) at the routed-generation fallthrough, before the route deadline is created. Evidence: after S1 (PR fix(rag): stop verification rejecting verbatim-faithful answers (#231 S1) #2022), "Lithium dosing?" remained 4/4 source-only, 3/4 as provider_timeout, because the fast-routed attempt failed the quality gates and fast_unsupported_retry_strong launched a strong generation into the fast route's leftover ~10–13 s. Routing the class to strong up front gives the attempt the strong route's 35 s budget and removes that unbudgeted-retry failure mode. The new route carries the distinct reason medication_dose_risk_strong_route for telemetry and canary attribution.
  • Authority framing: README §A1 rung 3 names broad_summary/comparison as its exemplar classes; extending rung 3 to medication_dose_risk is the owner decision of 2026-08-17 (R1 before S2) plus S1's recorded residual R1 — not a claim that the README already names the class. Per refuted-approach 4, strong routing cannot manufacture evidence retrieval did not surface: this PR removes the unbudgeted-retry provider_timeout failure mode; it does not promise fallback elimination for evidence-absent dosing cases.
  • drug_dosing is not routed on: it is a ClinicalQueryIntent, not a RagQueryClass, assigned only within the medication_dose_risk/table_threshold classes. Routing on it would widen chooseAnswerRoute's signature and would only add table_threshold-class queries with dose wording — which S1's probe evidence does not cover and which already have extractive paths plus strongClinicalEscalationPattern coverage. Excluded per the packet's own condition ("drug_dosing only if evidence supports").
  • Knock-on updates in the same PR: the two validated-extractive short-circuits pinned to the retired fast/clinical_fast_grounded_synthesis signature for this class (hasValidatedGenericLaiManagementExtractiveAnswer, hasValidatedAgitationArousalTypoDosingExtractiveAnswer in src/lib/rag/rag-extractive-first.ts) now match the new strong route, so their provider-free timeout skips keep firing; and the six supported dosing-class golden cases whose allowedRoutes were ["extractive","fast"] (clozapine-monitoring, agitation-arousal-pharmacological-management, long-acting-injectables, clozapine-monitoring-paraphrase, clozapine-missed-dose-table, agitation-arousal-typo-dosing) additionally allow "strong", because a strong route is now a by-design legitimate outcome for this class; "unsupported" remains excluded, so fail-closed regressions are still caught. The membership list was produced by classifying every golden question with classifyRagQuery, not by hand. No alias-tier, comparator, imputation-formula, or scoring edit.

RAG impact: behaviour change — canary pair 32025082010 (2bd146eed) -> planned post-merge. The eval-canary workflow is repository_dispatch from main only, so the post run cannot execute pre-merge; the owner approves the dispatch after merge. Baseline is S1's green post run (document/content recall 1.0/1.0, zero per-case rr regressions, answer gate 45/45) unless a newer green canary exists at merge time.

Non-inferiority argument for non-dosing classes: routing for every class other than medication_dose_risk is byte-identical — the only edited return in chooseAnswerRoute is the block-final fast fallthrough of the medication_dose_risk/table_threshold block, and it is guarded by queryClass === "medication_dose_risk"; table_threshold still reaches the original fast return (pinned by a new test). No score threshold (0.32/0.48/0.64/0.76) moved. All unsupported refusals and all provider-free extractive routes fire before the new branch and are unchanged. For the dosing class itself, mode only changes fast→strong at the fallthrough, which raises the generation budget 25 s→35 s and replaces the leftover-budget strong retry with a fully budgeted strong attempt; quality gates are unchanged, so the fallback rate for the dosing class cannot increase by this mechanism, and non-dosing classes cannot change at all. The two measured timeout-skip short-circuits for this class remain active under the new route signature (regression-tested), so their queries stay provider-free.

Scope fences honoured (packet stop conditions): shouldRetryWithStrongAfterFast unchanged; answerRouteBudgetMs and OPENAI_ANSWER_TIMEOUT_MS untouched (#231 stop condition stands, re-confirmed by S1); no quality gate weakened; no generation-fallback caching introduced; rag.ts untouched (4,362-line no-growth budget respected); rag-route-budget.ts untouched; src/lib/rag/rag-row-contracts.ts untouched (#212 sibling stream).

Verification

  • npm run verify:pr-local — heavy fail-closed scope for this RAG-surface diff; decisive lines: completed: check:runtime, check:installed-lock-parity, format:changed, lint, typecheck, test, build, eval:rag:offline, check:medication-interactions, check:medication-lexicon-report / failed: (none) / not reached: (none), exit 0
  • Focused routing/short-circuit proof: npx vitest run tests/rag-routing.test.ts tests/rag-extractive-first.test.tsTest Files 2 passed (2) / Tests 103 passed (103) (npx vitest run used directly because test:focused fails closed on test-file edits)
  • npx vitest run tests/rag-answer-fallback.test.tsTests 90 passed (90) (full-file order, catching the cross-test env-freeze mode)
  • npm run eval:rag:offlineTest Files 23 passed (23) / Tests 586 passed (586) / Offline RAG fixture and production-contract checks passed.
  • npm run check:rag:fixturesOffline RAG fixture and manifest validation passed (36 golden cases, 23 suites).
  • npm run check:production-readiness — 2 PASS / 5 WARN / 2 FAIL, where both FAILs are the documented offline-container provider capability gaps (NEXT_PUBLIC_SUPABASE_URL/SUPABASE_SERVICE_ROLE_KEY, OPENAI_API_KEY absent by design in this environment); no routing-related finding
  • Verification not run: npm run eval:retrieval:quality (provider-backed; retrieval, ranking, selection, chunking, and scoring are untouched — answer routing only). Verification not run: npm run eval:rag -- --limit 15 and npm run eval:quality -- --rag-only (provider-backed; generation prompt and post-processing untouched; the owner-approved post-merge canary pair is the live validation for this behaviour change). UI verification not run: no UI, routing-page, or styling change.
  • Live probes (scripts/probe-generation-quality.ts "Lithium dosing?") were not run in this session — provider-gated; the owner can probe post-merge to confirm the route now enters strong pre-deadline

Risk and rollout

  • Risk: dosing-class queries that previously succeeded on the fast route now run the strong model at the fallthrough — higher per-answer latency ceiling (35 s budget vs 25 s) and strong-model cost for that class; mitigated by every extractive/unsupported path staying ahead of the new branch (provider-free answers still short-circuit first, regression-tested) and by the class being exactly the high-stakes dosing surface where strong is clinically defensible. A live canary regression triggers the standing rule: immediate single-commit revert plus one confirmation dispatch.
  • Rollback: single-commit revert of this squash merge restores the prior routing (README §A1 rollback line: "revert the separately measured routing/quality fix"); no schema, cache-format, or config migration is involved.
  • Provider or production effects: None from this PR itself (all gates offline). The post-merge eval-canary dispatch is provider-backed (~$1–2) and requires explicit owner approval.

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

  • The budget-cap contract test (caps a generation attempt so source-backed recovery fits inside the route budget) was made cap-independent: full-file runs of rag-answer-fallback.test.ts pin OPENAI_ANSWER_TIMEOUT_MS at its 30 000 ms default via an earlier frozen env import (stubs stop applying), which under the 35 s strong budget would have made the reserve assertion non-discriminating (min(cap, budget−reserve) = cap = what a reserve-free revert also grants). The retrieval mock now burns 10 000 ms of fake time once, so the deadline term (23 000 ms) is the binding one below every plausible cap in both isolated and full-file runs, and a reserve-free revert (25 000 ms) still fails the assertion.
  • HANDOVER §2 S1b status row updated and review recorded via npm run ledger:append in this PR.

Generated by Claude Code

…fore the deadline (A1 R1)
Packet S1b, mitigation-ladder rung 3 for issue #231's residual R1: after S1,
"Lithium dosing?" stayed 4/4 source-only, 3/4 as provider_timeout, because the
fast-routed attempt failed the quality gates and fast_unsupported_retry_strong
launched a strong generation into the fast route's leftover ~10-13s.
chooseAnswerRoute now returns the strong route (new distinct reason
medication_dose_risk_strong_route) at the routed-generation fallthrough for the
medication_dose_risk class, so the deadline is created with the strong 35s
budget and the class gets one fully budgeted strong attempt instead of a doomed
leftover-budget retry. All unsupported refusals and provider-free extractive
paths fire before the new branch and are unchanged; table_threshold and every
other class keep byte-identical routing (pinned by new tests). No budget,
threshold, quality-gate, or shouldRetryWithStrongAfterFast change.
Knock-ons in the same change: the LAI-management and agitation-typo-dosing
validated-extractive short-circuits now key on the new strong signature so
their measured provider-free timeout skips keep firing (with negative tests
for the retired fast signature), and the six supported dosing-class golden
cases additionally allow the strong route ("unsupported" stays excluded). The
budget-cap contract test burns 10s of retrieval fake-time so its reserve
assertion stays discriminating below every plausible timeout cap.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NWGr4LHgBRDVM1sGVdfCSa
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@supabase

supabaseBot commented Aug 17, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitaiBot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in:7 minutes

Limit details: You’ve used all 1 included review currently available under your plan. You completed 94 included PR reviews in the past 7 days; at that activity level, included reviews refill at 1 review per hour.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b9a32eb7-ea88-4936-9eb2-096fbe83df0c

📥 Commits

Reviewing files that changed from the base of the PR and between 850475d and 7a9c95b.

📒 Files selected for processing (8)
  • docs/branch-review-records/bcb5e0bf76d1501946c3d093a30dfd8c1351c6ebd83d0ae0a980e65d12eda5b6.record.md
  • docs/rag-improvement/HANDOVER.md
  • src/lib/rag/rag-eval-cases.ts
  • src/lib/rag/rag-extractive-first.ts
  • src/lib/rag/rag-routing.ts
  • tests/rag-answer-fallback.test.ts
  • tests/rag-extractive-first.test.ts
  • tests/rag-routing.test.ts

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo enabled auto-merge August 17, 2026 13:12
@BigSimmo
BigSimmo merged commit 92f7618 into mainAug 17, 2026
25 checks passed
@BigSimmo
BigSimmo deleted the claude/s1b-rag-dosing-routing-6u1mik branch August 17, 2026 13:12
BigSimmo pushed a commit that referenced this pull request Aug 17, 2026
…e conflict resolution
The branch merged origin/main twice while open. The baseline record now names
the current base and carries the re-measured offline numbers: the contract list
grew to 25 suites / 602 tests because two suites were added concurrently -
tests/rag-adversarial-fixtures.test.ts here and
tests/search-route-round-trip-budget.test.ts on main - with the remaining test
movement coming from packet S1b (PR #2035). ragEvalCases is still 44 and
answerQualityEvalCases still 30, so both provider-backed gate denominators are
unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWiDFShT7JuZAjTJHhJ7Wq
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.

2 participants

@BigSimmo@claude