Skip to content

feat(rag): tag document-summary rows with a document_context provenance origin - #2053

Merged
BigSimmo merged 7 commits into
mainfrom
claude/g1-rag-document-context-qn9ubx
Aug 17, 2026
Merged

feat(rag): tag document-summary rows with a document_context provenance origin#2053
BigSimmo merged 7 commits into
mainfrom
claude/g1-rag-document-context-qn9ubx

Conversation

@BigSimmo

Copy link
Copy Markdown
Owner

Summary

  • Packet G1 (docs/rag-improvement/HANDOVER.md §3) — provenance tag for document-summary rows, owner decision 2026-08-17, Option B.buildDocumentSummaryResults stamps similarity: 1 on every document-summary row. That is a constant, not a measured cosine: summarizeDocument loads every committed chunk of one document, so there is no query to score against. Until now it carried no similarity_origin at all, which left a fabricated 1.0 indistinguishable from a perfect vector match at every surface that reads a row. This adds "document_context" to the similarity_origin union (src/lib/types.ts), accepts it in the streamed-preview client-source validator (src/lib/answer-stream-contract.ts), and stamps it in buildDocumentSummaryResults (src/lib/rag/rag-row-contracts.ts). Per the decision, deriveConfidence is unchanged — it still excludes only "synthetic_text", so document summaries still reach "high" — and rag.tssynthetic_similarity_count still counts only "synthetic_text". Option A (tag as "synthetic_text", capping every summary at "medium") is recorded as rejected. docs/clinical-hazard-analysis.md H5a is updated to mark the decision implemented and to name the remaining residual.
  • Ledger tooling fix: issueRowFingerprint resolves rows by display id, not only by number. Independent of the RAG change and separately revertible. npm run issues:done and issues:update were unusable for any row minted after the ULID migration: the lookup matched only /^#(\d+)$/ and keyed on entry.number, which is null on ULID-backed rows, so it returned null for every Crockford display locator — and ledger-inbox.mjs reads a null fingerprint as "no such row" and refuses the request. Discovered because it made the two closures below impossible to queue. Shape-based branching does not work here: Crockford's alphabet includes 0-9, so a ULID-derived locator can be entirely digits (#041061) and is indistinguishable from a legacy id by pattern alone; the fix resolves by exact display id first and falls back to the legacy numeric interpretation. Null-safety for absent, archived, and malformed ids is preserved and pinned.
  • Two queued /issues closures as immutable inbox requests, applied by a later npm run issues:reconcile from a serialized ledger branch: #J912J9 (P1 issue — the governance question this packet answers) and #0MSNT8 (P3 task — the G1 implementation row that decision created).
  • HANDOVER §2 status table: the G1 row moves to implemented, with branch, evidence, and the no-canary rationale.

recordSearchScoreTelemetry is exported solely so the telemetry pin exercises the real counter rather than pinning its source text; it is not a route surface.

RAG impact: no retrieval behaviour change — provenance tag only; confidence derivation unchanged, pinned by test.

Why this is safe to land without a canary

Retrieval, ranking, selection, and ordering are untouched: no comparator, no score, no threshold, and no candidate-source path changes. The only runtime difference is one additional string field on rows built by buildDocumentSummaryResults, whose sole caller is summarizeDocument — never the general answer path. deriveConfidence and synthetic_similarity_count both branch on === "synthetic_text", so neither observes the new value. The owner's 2026-08-17 decision recorded no canary for exactly this reason.

Every pin was mutation-checked rather than assumed — each was made to fail against the change it exists to catch:

PinMutation appliedResult
Two "document_context" citations ≥ 0.82 still yield "high"folded "document_context" into the deriveConfidence exclusion (rejected Option A)red
synthetic_similarity_count ignores the new valuewidened the telemetry filter to every non-cosine originred
Summary rows carry the tagremoved the stamp from buildDocumentSummaryResultsred
Stream validator accepts every declared union memberadded a fourth union member without listing itred at typecheck (compile-time exhaustiveness guard)
issueRowFingerprint resolves an all-digit display idresolved by id shape instead of by table lookupred

Verification

  • npm run verify:pr-local
PR-local verification summary:
- completed: check:runtime, check:installed-lock-parity, format:changed, sitemap:check, docs:check-index,
docs:check-inventory, docs:check-scripts, docs:check-links, check:branch-review-ledger,
check:outstanding-issues, check:ledger-write-discipline, lint, typecheck, test, build, eval:rag:offline,
check:medication-interactions, check:medication-lexicon-report
- failed: (none)
- not reached: (none)

Full offline unit suite (inside verify:pr-local, and captured directly):

Test Files 642 passed (642)
Tests 6879 passed | 4 skipped (6883)

eval:rag:offline (run directly and again inside verify:pr-local):

Offline RAG fixture and manifest validation passed (36 golden cases, 25 suites).
Test Files 25 passed (25)
Tests 603 passed (603)
Offline RAG fixture and production-contract checks passed.

check:rag:fixtures:

Offline RAG fixture and manifest validation passed (36 golden cases, 25 suites).

Focused suites for the touched files (npx vitest run tests/rag-score.test.ts tests/rag-retrieval-row-contract.test.ts tests/answer-incremental-delivery.test.ts tests/answer-stream-contract.test.ts tests/rag-imputation-contract.test.ts):

Test Files 5 passed (5)
Tests 58 passed (58)

npm run format was run and the result committed; whole-tree prettier --check . reports All matched files use Prettier code style!.

  • npm run verify:ui when UI, routing, styling, browser behavior, reduced-motion, or forced-colors behavior changed

UI verification not run: no component, route, style, or browser-behaviour file is touched. The diff is three src/lib modules, one script, tests, and docs.

  • npm run verify:release before release or handoff confidence claims

Verification not run: this is an ordinary PR handoff, not a release-confidence claim, and verify:release is provider-backed.

  • npm run eval:retrieval:quality (must stay 36/36) when retrieval, ranking, selection, chunking, or scoring behavior changed

Verification not run: no retrieval, ranking, selection, chunking, or scoring behaviour changed — this adds a provenance string to rows on a route that performs no matching. The gate is also provider-backed and needs explicit owner approval, which was not given for this packet; the owner's decision records no canary for it.

  • npm run eval:rag -- --limit 15 + npm run eval:quality -- --rag-only when answer generation, the synthesis prompt, or answer post-processing changed

Verification not run: answer generation, the synthesis prompt, and answer post-processing are untouched. Both commands are provider-backed and were not authorized.

  • npm run check:production-readiness when clinical workflow, privacy, environment, Supabase, source governance, or deployment behavior changed

Run because the diff classifies as clinical-risk. It reports the documented offline-profile provider capability gap, not a regression from this change — both failures are absent credentials in this container, and nothing in the diff touches env, Supabase, or provider configuration:

Project: Clinical KB Database (sjrfecxgysukkwxsowpy)
PASS (2):
- Node runtime 24.19.0 matches required Node 24.x.
- Boot guard invokes requireQueryHashSecret(); the query-hash HMAC fails closed in production (PIA-2).
FAIL (2):
- Missing server env config: Missing server environment variables: NEXT_PUBLIC_SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY.
- OpenAI configuration issue: Missing OPENAI_API_KEY.

The Supabase target line confirms the pinned project ref is unchanged.

  • npm run check:deployment-readiness when deployment startup, hosting, or rollout behavior changed

Verification not run: no deployment, startup, hosting, or rollout behaviour changed.

Risk and rollout

  • Risk: Low. The RAG change is additive provenance metadata on one non-answer route; the clinically consequential derivation (deriveConfidence) is deliberately untouched and pinned by a discriminating test pair, so the confidence label a clinician reads is identical before and after. The realistic failure mode was a consumer of similarity_origin that assumed a closed two-value union and would now see a third value; every such consumer was enumerated (deriveConfidence, rag.ts telemetry, answer-stream-contract.ts, answer-client-payload.ts) and each is either strict-equality on "synthetic_text" or now explicitly allows the new value. The ledger fix carries its own small risk of over-matching an id; it is bounded by resolving against the Open-items table itself and pinned with absent/archived/malformed cases.
  • Rollback: git revert any of the four commits independently — the RAG tag, the ledger tooling fix, the queued closures, and the ledger record are separate commits with no ordering dependency between them before merge. Reverting the RAG commit restores untagged similarity: 1 rows with no other behavioural difference.
  • Provider or production effects: None. No provider-backed command was run, no live canary was dispatched, no Supabase or OpenAI access occurred, and no deployment or environment configuration changed.

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

On the last item specifically: no clinical decision-support behaviour changed, so the classification is unaffected. The change makes an existing fabricated score legible rather than altering any answer, confidence label, or trust verdict a clinician sees.

Notes

  • This closes the legibility half of the H5a live residual, not the underlying question. Whether a constant 1.0 should contribute to a confidence label at all remains open by design — Option B was chosen because tagging alone has no measured safety cost, whereas Option A downgrades every document summary without measured gain. The tag is what makes a future gate cheap: any later control can now discriminate the document-summary route without re-deriving provenance. docs/clinical-hazard-analysis.md H5a records this residual explicitly.
  • Out of scope by instruction and untouched: rag-routing, rag-claim-support, and the rag-extractive-answer finalizer (packets S1b/S1c/S1d).
  • Merge-order note: PRs docs(rag): record the S1b canary pair, add packet S1d, mark S4/T4 merged #2048 and Allow ordinary pushes to a PR branch while its auto-merge is armed #2028 also modify docs/rag-improvement/HANDOVER.md. This PR edits only the single G1 status-table row, so a conflict should be a trivial one-row resolution, but it is worth landing these in a deliberate order.
  • #292 duplicate check performed before starting: no open PR touches similarity_origin, and none touches the ledger id tooling.
  • The two /issues closures are queued as inbox requests only. They take effect when npm run issues:reconcile runs from a serialized ledger branch after this lands.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VHxUr5eyRwrBiXKMoqoARq


Generated by Claude Code

…ce origin
Packet G1 (docs/rag-improvement/HANDOVER.md §3), owner decision 2026-08-17,
Option B. Closes the legibility half of the H5a live residual.
buildDocumentSummaryResults stamps similarity: 1 on every document-summary row.
That is a constant, not a measured cosine — summarizeDocument loads every
committed chunk of one document, so there is no query to score against. Until
now it carried no similarity_origin at all, which left a fabricated 1.0
indistinguishable from a perfect vector match at every surface that reads a row.
Adds "document_context" to the similarity_origin union (types.ts), accepts it in
the streamed-preview client-source validator (answer-stream-contract.ts, now an
allow-set rather than a !== chain so the two cannot drift), and stamps it on the
summary rows.
The tag is deliberately NOT "synthetic_text". That value marks scores imputed
from lexical/structural match strength on the general answer path, where a title
hit can masquerade as semantic evidence, and deriveConfidence excludes it from
the "high" bar. The summary route has no match strength to inflate and its
citations are verified by the same grounding pipeline, so per the owner decision
deriveConfidence is unchanged and document summaries still reach "high". Tagging
them "synthetic_text" would have silently capped every summary at "medium" —
recorded as rejected Option A. rag.ts synthetic_similarity_count likewise keeps
its strict equality so the RC9 imputed-score signal stays a single population.
Four discriminating pins, each mutation-checked against the change it exists to
catch (folding document_context into the deriveConfidence exclusion, widening the
telemetry filter, and removing the stamp each turn the matching pin red):
- summary rows carry the tag (rag-retrieval-row-contract.test.ts)
- two document_context citations at >= 0.82 still yield "high", while the
identical scores tagged synthetic_text still yield "medium" (rag-score.test.ts)
- synthetic_similarity_count ignores the new value (rag-score.test.ts)
- the stream validator accepts every declared union member, with a compile-time
exhaustiveness guard so a future union member cannot be added without updating
the test (answer-incremental-delivery.test.ts)
recordSearchScoreTelemetry is exported solely so the telemetry pin can exercise
the real counter rather than pinning its source text; it is not a route surface.
RAG impact: no retrieval behaviour change — provenance tag only; confidence
derivation unchanged, pinned by test.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHxUr5eyRwrBiXKMoqoARq
…y number
`issues:done` and `issues:update` were unusable for any row minted after the
ULID migration. `issueRowFingerprint` matched only `/^#(\d+)$/` and looked the
row up by `entry.number`, which is null on ULID-backed rows, so it returned null
for every Crockford display locator — and `ledger-inbox.mjs` reads a null
fingerprint as "no such row" and refuses the request. The symptom was
npm run issues:done '#J912J9'
ledger inbox: ledger request rejected: #J912J9 is not in Open items
about a row plainly present in Open items. The optimistic-concurrency check was
therefore unreachable for exactly the rows that can support it.
Resolves by exact display id first, falling back to the legacy numeric
interpretation. The order matters and shape-based branching does not work:
Crockford's alphabet includes 0-9, so a ULID-derived locator can be entirely
digits (the writer test's own id is `#041061`) and is indistinguishable from a
legacy id by pattern alone. Branching on shape reads such a row as a legacy id
and hunts for a sequential number no ULID row has — which is how the first
attempt at this fix still returned null for a row it could see.
Null-safety is preserved: an absent, archived, or malformed id still returns
null, so the inbox's refusal survives for rows that genuinely are not open.
Found while queueing the G1 closures in this PR, which is what made the two
requested rows unclosable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHxUr5eyRwrBiXKMoqoARq
Two immutable inbox requests, applied by a later `npm run issues:reconcile` from
a serialized ledger branch:
- `#J912J9` (P1 issue) — the governance question "may a fabricated similarity of
1 on document-summary rows earn the high confidence label a clinician reads?"
Answered by the owner's 2026-08-17 Option B decision and implemented here.
- `#0MSNT8` (P3 task) — the G1 implementation row that decision created.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHxUr5eyRwrBiXKMoqoARq
@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:5 minutes

Limit details: You’ve used all 1 included review currently available under your plan. You completed 96 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: 1b19a71a-4c40-454c-8be0-6ac575146c3d

📥 Commits

Reviewing files that changed from the base of the PR and between 58e7471 and 9ca2db7.

📒 Files selected for processing (15)
  • docs/branch-review-records/1048a2a62bd4a4306cb90f6c144e94427f6cf21d5a9f449f983b170ddb35a4b5.record.md
  • docs/branch-review-records/3f5dad0c7109ddc3f59bd3ce42fa9df1587d3cbcd938a01b8950f1f1e746472a.record.md
  • docs/clinical-hazard-analysis.md
  • docs/outstanding-issues-inbox/34c0f9bf-22fe-495b-a828-f73bbd4cfddf.json
  • docs/outstanding-issues-inbox/fb7d42c0-ee67-426e-907e-f2c306afc38b.json
  • docs/rag-improvement/HANDOVER.md
  • scripts/check-outstanding-issues.mjs
  • src/lib/answer-stream-contract.ts
  • src/lib/rag/rag-row-contracts.ts
  • src/lib/rag/rag.ts
  • src/lib/types.ts
  • tests/answer-incremental-delivery.test.ts
  • tests/outstanding-issues-writer.test.ts
  • tests/rag-retrieval-row-contract.test.ts
  • tests/rag-score.test.ts

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

@BigSimmo
BigSimmo enabled auto-merge August 17, 2026 16:49
@github-actions

Copy link
Copy Markdown
Contributor

CI triage

CI failed on this PR. Automated classification of the 2 failed job(s):

  • Static PR checksneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.
  • PR requiredneeds investigation: inspect the failing step and uploaded diagnostics; rerun only after classifying the cause.

Compared with main CI run #11693 (cancelled).

Classification is evidence routing, not permission to ignore a failure. Exact quarantined Playwright identities remain governed by the flake ledger.

…conflict (PR #2053)
Also condense the recordSearchScoreTelemetry provenance comment in src/lib/rag/rag.ts (6 lines -> 1) to clear the check:maintainability-budgets no-growth budget (4364 -> 4360 of 4362). No behavior change.
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