From 008ac5143dc6ba66361e4a6bfb6a7a9c5acd874c Mon Sep 17 00:00:00 2001 From: BigSimmo <87357024+BigSimmo@users.noreply.github.com> Date: Thu, 30 Jul 2026 22:54:16 +0800 Subject: [PATCH 1/6] refactor(rag): extract second-stage ranking --- docs/codebase-index.md | 22 +-- docs/maturity-backlog-workorders.md | 5 + scripts/check-maintainability-budgets.mjs | 6 +- src/lib/rag/rag-second-stage.ts | 199 ++++++++++++++++++++++ src/lib/rag/rag.ts | 196 +-------------------- 5 files changed, 220 insertions(+), 208 deletions(-) create mode 100644 src/lib/rag/rag-second-stage.ts diff --git a/docs/codebase-index.md b/docs/codebase-index.md index b7e027ac3e..4187ae24ee 100644 --- a/docs/codebase-index.md +++ b/docs/codebase-index.md @@ -115,17 +115,17 @@ The `rag.ts` orchestrator and its `rag-*` cluster live in **`src/lib/rag/`** (th domain-extracted directory; imported as `@/lib/rag/rag*`). Other modules below remain flat in `src/lib/`. -| Module | Role | -| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | -| `rag.ts` | Main answer pipeline orchestrator | -| `rag-routing.ts`, `rag-provider.ts`, `rag-answer-text.ts`, `smart-rag-api.ts` | Model routing, provider modes, API surface | -| `rag-contracts.ts`, `rag-answer-support.ts`, `rag-query-guard.ts` | Shared RAG contracts and pure answer/query policy | -| `rag-evidence-gates.ts`, `rag-coverage-gate.ts` | Evidence-sufficiency predicates and the fast-path evidence coverage gate | -| `rag-cache.ts`, `rag-retrieval-variants.ts` | Bounded caches and retrieval variants | -| `clinical-search.ts`, `clinical-query-mode.ts`, `retrieval-selection.ts` | Query modes and retrieval selection | -| `answer-ranking.ts`, `answer-verification.ts`, `answer-formatting.ts`, `answer-follow-up.ts`, `answer-render-policy.ts` | Answer quality and rendering | -| `citations.ts`, `cross-document-synthesis.ts`, `evidence-relevance.ts` | Evidence and synthesis | -| `ranking-config.ts`, `search-scope.ts`, `rag-eval-cases.ts` | Ranking tuning and eval fixtures | +| Module | Role | +| ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| `rag.ts` | Main answer pipeline orchestrator | +| `rag-routing.ts`, `rag-provider.ts`, `rag-answer-text.ts`, `smart-rag-api.ts` | Model routing, provider modes, API surface | +| `rag-contracts.ts`, `rag-answer-support.ts`, `rag-query-guard.ts` | Shared RAG contracts and pure answer/query policy | +| `rag-evidence-gates.ts`, `rag-coverage-gate.ts`, `rag-second-stage.ts` | Evidence predicates, coverage gating, and second-stage ranking | +| `rag-cache.ts`, `rag-retrieval-variants.ts` | Bounded caches and retrieval variants | +| `clinical-search.ts`, `clinical-query-mode.ts`, `retrieval-selection.ts` | Query modes and retrieval selection | +| `answer-ranking.ts`, `answer-verification.ts`, `answer-formatting.ts`, `answer-follow-up.ts`, `answer-render-policy.ts` | Answer quality and rendering | +| `citations.ts`, `cross-document-synthesis.ts`, `evidence-relevance.ts` | Evidence and synthesis | +| `ranking-config.ts`, `search-scope.ts`, `rag-eval-cases.ts` | Ranking tuning and eval fixtures | ### Ingestion and indexing diff --git a/docs/maturity-backlog-workorders.md b/docs/maturity-backlog-workorders.md index 05463d089d..0ca3ebd0de 100644 --- a/docs/maturity-backlog-workorders.md +++ b/docs/maturity-backlog-workorders.md @@ -92,6 +92,11 @@ structural change, not a single mixed PR. `rag.ts`: it is pipeline orchestration that calls the metadata/visual hydration and second-stage rerank helpers, so moving it would need a runtime back-edge to `rag.ts`. The hydration cluster is the separate later extraction (`rag-hydration.ts`). +- **Progress (#086, second-stage extraction):** extracted the cohesive second-stage reranking + unit into `src/lib/rag/rag-second-stage.ts`: engagement policy, score adjustment, document + diversity, subject-match protection, and its retrieval-layer telemetry moved together without + changing the public `@/lib/rag/rag` export. `rag.ts` remains the retrieval orchestrator and + calls the extracted unit at the same pipeline points (4,780 → 4,592; budget ratcheted to 4,592). - **Progress (`DocumentViewer.tsx`):** extracted the cohesive leaf modules into `src/components/document-viewer/` — shared row `types.ts`, `source-panels.tsx` (summary profile, high-yield summary, source images/tables, pinned evidence, indexed-text panel), the diff --git a/scripts/check-maintainability-budgets.mjs b/scripts/check-maintainability-budgets.mjs index 3d190cf457..6569bd9776 100644 --- a/scripts/check-maintainability-budgets.mjs +++ b/scripts/check-maintainability-budgets.mjs @@ -5,9 +5,9 @@ const budgets = new Map([ // Chrome ownership/reporting lives in use-dashboard-chrome-coordinator; keep // the reclaimed monolith budget so it cannot silently drift back to 4160. ["src/components/ClinicalDashboard.tsx", 4140], - // The evidence coverage gate lives in rag-coverage-gate; keep the reclaimed - // budget so it cannot silently drift back to 5030. - ["src/lib/rag/rag.ts", 4780], + // Evidence coverage and second-stage ranking live in focused rag modules; + // keep the reclaimed monolith budget so it cannot silently drift back. + ["src/lib/rag/rag.ts", 4592], ["src/components/DocumentViewer.tsx", 1734], ["supabase/functions/indexing-v3-agent/index.ts", 2191], ]); diff --git a/src/lib/rag/rag-second-stage.ts b/src/lib/rag/rag-second-stage.ts new file mode 100644 index 0000000000..3c3a977b07 --- /dev/null +++ b/src/lib/rag/rag-second-stage.ts @@ -0,0 +1,199 @@ +import { rankingConfig } from "@/lib/ranking-config"; +import { visualEvidenceUnitTypes } from "@/lib/rag/rag-evidence-gates"; +import type { SearchTelemetry } from "@/lib/rag/rag-contracts"; +import type { RagQueryClass, SearchResult } from "@/lib/types"; + +// Extracted from rag.ts (maturity X3): second-stage engagement, scoring, and +// telemetry. The implementation is unchanged; rag.ts remains the orchestrator. + +const tableVisualEvidenceUnitTypes = new Set([ + "table_fact", + "table_threshold", + "medication_chart_row", + "risk_matrix_cell", +]); + +/** Layer top score. */ +export function layerTopScore(results: SearchResult[]) { + return Number(Math.max(0, ...results.map((result) => result.hybrid_score ?? result.similarity ?? 0)).toFixed(4)); +} + +/** Record retrieval layer. */ +export function recordRetrievalLayer( + telemetry: SearchTelemetry, + layer: string, + count: number, + options: { latencyMs?: number; topScore?: number } = {}, +) { + telemetry.retrieval_layer_counts = { + ...(telemetry.retrieval_layer_counts ?? {}), + [layer]: count, + }; + if (typeof options.latencyMs === "number") { + telemetry.retrieval_layer_latencies_ms = { + ...(telemetry.retrieval_layer_latencies_ms ?? {}), + [layer]: Math.max(0, Math.round(options.latencyMs)), + }; + } + if (typeof options.topScore === "number") { + telemetry.retrieval_layer_top_scores = { + ...(telemetry.retrieval_layer_top_scores ?? {}), + [layer]: Number(Math.max(0, options.topScore).toFixed(4)), + }; + } +} + +/** Should use second stage rerank. */ +function shouldUseSecondStageRerank(queryClass: RagQueryClass | undefined, results: SearchResult[], topK: number) { + if (results.length <= 1) return false; + const topScore = Math.max(0, results[0]?.hybrid_score ?? results[0]?.similarity ?? 0); + const secondScore = Math.max(0, results[1]?.hybrid_score ?? results[1]?.similarity ?? 0); + const topScoresClose = Math.abs(topScore - secondScore) <= 0.04; + const hasVisualEvidence = results.some((result) => visualEvidenceUnitTypes.has(result.index_unit?.unit_type ?? "")); + const hasTableVisualEvidence = results.some((result) => + tableVisualEvidenceUnitTypes.has(result.index_unit?.unit_type ?? ""), + ); + if (queryClass === "table_threshold" || queryClass === "medication_dose_risk") { + return hasVisualEvidence || hasTableVisualEvidence || topScoresClose; + } + if (queryClass === "comparison") return results.length > topK || topScoresClose; + return topScoresClose && hasVisualEvidence; +} + +/** Second stage score. */ +function secondStageScore(result: SearchResult, queryClass: RagQueryClass | undefined, index: number) { + const baseRankScore = + result.score_explanation?.rankScore ?? + result.score_explanation?.preClampFinalScore ?? + result.score_explanation?.finalScore ?? + result.hybrid_score ?? + result.similarity ?? + 0; + let adjustment = 0; + const unitType = result.index_unit?.unit_type ?? ""; + const source = result.index_unit?.metadata?.source; + const sourceQuality = Number(result.index_unit?.quality_score ?? 0.65); + const doseAmountText = `${result.section_heading ?? ""} ${result.content} ${(result.images ?? []) + .map((image) => `${image.caption ?? ""} ${image.tableTextSnippet ?? ""} ${image.tableTitle ?? ""}`) + .join(" ")} ${(result.table_facts ?? []) + .map( + (fact) => `${fact.table_title ?? ""} ${fact.row_label ?? ""} ${fact.threshold_value ?? ""} ${fact.action ?? ""}`, + ) + .join(" ")}`; + const hasDoseAmount = /\b\d+(?:\.\d+)?\s?(?:mg|mcg|microgram|micrograms)\b/i.test(doseAmountText); + const w = rankingConfig.secondStage; + adjustment += Math.max(0, w.positionBase - index * w.positionStep); + if (result.memory_cards?.length && (queryClass === "broad_summary" || queryClass === "comparison")) + adjustment += w.memorySummaryBoost; + if (queryClass === "document_lookup" && (result.match_explanation?.titleHit || result.match_explanation?.labelHit)) + adjustment += w.documentLookupTitleBoost; + if ((queryClass === "table_threshold" || queryClass === "medication_dose_risk") && result.table_facts?.length) + adjustment += w.tableThresholdEvidenceBoost; + if (queryClass === "medication_dose_risk" && hasDoseAmount) adjustment += w.doseAmountBoost; + if (tableVisualEvidenceUnitTypes.has(unitType)) adjustment += w.tableVisualBoost; + else if (visualEvidenceUnitTypes.has(unitType)) adjustment += w.visualBoost; + if (source === "visual_intelligence") + adjustment += Math.min( + w.visualIntelligenceMax, + Math.max(0, sourceQuality - w.visualIntelligencePivot) * w.visualIntelligenceSlope, + ); + if (result.source_metadata?.document_status === "outdated") adjustment -= w.outdatedPenalty; + // D4: ships 0 (no-op) — activate via RAG_RANKING_CONFIG only behind a green golden eval. + if (result.source_metadata?.document_status === "unknown") adjustment -= w.unknownCurrentnessPenalty; + if (result.source_metadata?.extraction_quality === "poor") adjustment -= w.poorExtractionPenalty; + if ( + result.indexing_quality?.quality_score !== undefined && + result.indexing_quality.quality_score < w.lowIndexQualityThreshold + ) + adjustment -= w.lowIndexQualityPenalty; + return { rankScore: baseRankScore + adjustment, adjustment }; +} + +/** Apply second stage rerank if needed. */ +export function applySecondStageRerankIfNeeded(args: { + queryClass?: RagQueryClass; + results: SearchResult[]; + telemetry: SearchTelemetry; + topK: number; +}) { + if (!shouldUseSecondStageRerank(args.queryClass, args.results, args.topK)) return args.results; + const startedAt = Date.now(); + // CI-16 document diversity: subtract a demotion from each EXTRA chunk of a document that + // has already appeared higher up, so a single doc's sibling chunks can't crowd out other + // documents. Applied AFTER the additive-boost floor so it can actually lower the effective + // rank. Keep this separate from the selection-rescue floor in retrieval-selection.ts. + const seenPerDocument = new Map(); + const reranked = args.results + .map((result, index) => { + const secondStage = secondStageScore(result, args.queryClass, index); + let rankScore = secondStage.rankScore; + let confidenceAdjustment = secondStage.adjustment; + const releasedHybridScore = result.hybrid_score ?? result.similarity ?? 0; + let releaseRankScore = Math.max( + releasedHybridScore, + (result.score_explanation?.finalScore ?? result.hybrid_score ?? result.similarity ?? 0) + + secondStage.adjustment, + ); + const priorOccurrences = seenPerDocument.get(result.document_id) ?? 0; + seenPerDocument.set(result.document_id, priorOccurrences + 1); + if (rankingConfig.documentDiversityPenalty > 0 && priorOccurrences > 0) { + const diversityPenalty = Math.min( + rankingConfig.documentDiversityPenaltyCap, + rankingConfig.documentDiversityPenalty * priorOccurrences, + ); + rankScore -= diversityPenalty; + confidenceAdjustment -= diversityPenalty; + releaseRankScore -= diversityPenalty; + } + const selectionReasons = result.match_explanation?.reasons ?? []; + const clinicalSubjectRequired = selectionReasons.includes("retrieval_required_signal:clinical_subject"); + const clinicalSubjectMatched = selectionReasons.includes("retrieval_signal:clinical_subject"); + if (clinicalSubjectRequired && !clinicalSubjectMatched) { + // A wrong-medication chunk can carry attractive numeric dose/monitoring signals. Keep it + // available at its released hybrid strength, but do not let second-stage evidence boosts + // promote it above chunks that contain the medication subject requested by the query. + releaseRankScore = Math.min(releaseRankScore, releasedHybridScore); + } + const finalScore = Math.min( + 1, + Math.max( + 0, + (result.score_explanation?.finalScore ?? result.hybrid_score ?? result.similarity ?? 0) + + confidenceAdjustment, + ), + ); + return { + rankScore, + result: { + ...result, + score_explanation: result.score_explanation + ? { + ...result.score_explanation, + rankScore: Number(rankScore.toFixed(4)), + releaseRankScore: Number(releaseRankScore.toFixed(4)), + preClampFinalScore: Number(rankScore.toFixed(4)), + finalScore: Number(finalScore.toFixed(4)), + } + : result.score_explanation, + match_explanation: { + ...result.match_explanation, + reasons: Array.from(new Set([...(result.match_explanation?.reasons ?? []), "second_stage_rerank"])), + }, + }, + }; + }) + .sort((left, right) => right.rankScore - left.rankScore || left.result.id.localeCompare(right.result.id)) + .map(({ result }, index) => + result.score_explanation + ? { ...result, score_explanation: { ...result.score_explanation, finalRank: index + 1 } } + : result, + ); + args.telemetry.second_stage_rerank_used = true; + args.telemetry.second_stage_rerank_latency_ms = + (args.telemetry.second_stage_rerank_latency_ms ?? 0) + Date.now() - startedAt; + recordRetrievalLayer(args.telemetry, "second_stage_rerank", reranked.length, { + latencyMs: Date.now() - startedAt, + topScore: layerTopScore(reranked), + }); + return reranked; +} diff --git a/src/lib/rag/rag.ts b/src/lib/rag/rag.ts index 276b66199a..7197aa1c7b 100644 --- a/src/lib/rag/rag.ts +++ b/src/lib/rag/rag.ts @@ -214,10 +214,11 @@ import { hasDocumentAliasWithoutTopTitleSupport, hasRiskFlowchartActionEvidence, isRiskFlowchartNextStepQuery, - visualEvidenceUnitTypes, } from "@/lib/rag/rag-evidence-gates"; import { applyCoverageGateTelemetry, evaluateEvidenceCoverageGate } from "@/lib/rag/rag-coverage-gate"; export { evaluateEvidenceCoverageGate } from "@/lib/rag/rag-coverage-gate"; +import { applySecondStageRerankIfNeeded, layerTopScore, recordRetrievalLayer } from "@/lib/rag/rag-second-stage"; +export { applySecondStageRerankIfNeeded } from "@/lib/rag/rag-second-stage"; import { cleanClinicalSummaryText, isLowYieldClinicalText } from "@/lib/source-text-sanitizer"; import { hasClinicalAnswerQualityIssue, @@ -238,7 +239,6 @@ import { clinicalModePrompt, queryClassForClinicalMode, queryForClinicalMode } f import { annotateSearchResults, buildEvidenceRelevance } from "@/lib/evidence-relevance"; import { committedIndexGeneration, isCommittedGenerationMetadata } from "@/lib/reindex-pipeline"; import { buildRetrievalIntent, selectRetrievalEvidence } from "@/lib/retrieval-selection"; -import { rankingConfig } from "@/lib/ranking-config"; import { resultsHaveReleaseRankScore, stabilizeReleasedSearchOrder } from "@/lib/released-search-order"; export { stabilizeReleasedSearchOrder } from "@/lib/released-search-order"; import { semanticRerankIfAmbiguous } from "@/lib/semantic-rerank"; @@ -504,13 +504,6 @@ type AnswerQuestionWithScopeArgs = SearchChunksArgs & { signal?: AbortSignal; }; -const tableVisualEvidenceUnitTypes = new Set([ - "table_fact", - "table_threshold", - "medication_chart_row", - "risk_matrix_cell", -]); - /** Provenance layer keys. */ function provenanceLayerKeys(result: SearchResult) { const layers = new Set(["chunk"]); @@ -527,36 +520,6 @@ function provenanceLayerKeys(result: SearchResult) { return layers; } -/** Layer top score. */ -function layerTopScore(results: SearchResult[]) { - return Number(Math.max(0, ...results.map((result) => result.hybrid_score ?? result.similarity ?? 0)).toFixed(4)); -} - -/** Record retrieval layer. */ -function recordRetrievalLayer( - telemetry: SearchTelemetry, - layer: string, - count: number, - options: { latencyMs?: number; topScore?: number } = {}, -) { - telemetry.retrieval_layer_counts = { - ...(telemetry.retrieval_layer_counts ?? {}), - [layer]: count, - }; - if (typeof options.latencyMs === "number") { - telemetry.retrieval_layer_latencies_ms = { - ...(telemetry.retrieval_layer_latencies_ms ?? {}), - [layer]: Math.max(0, Math.round(options.latencyMs)), - }; - } - if (typeof options.topScore === "number") { - telemetry.retrieval_layer_top_scores = { - ...(telemetry.retrieval_layer_top_scores ?? {}), - [layer]: Number(Math.max(0, options.topScore).toFixed(4)), - }; - } -} - /** Record search score telemetry. */ function recordSearchScoreTelemetry(telemetry: SearchTelemetry, results: SearchResult[]) { if (!results.length) { @@ -609,161 +572,6 @@ function recordSearchScoreTelemetry(telemetry: SearchTelemetry, results: SearchR }, 0); } -/** Should use second stage rerank. */ -function shouldUseSecondStageRerank(queryClass: RagQueryClass | undefined, results: SearchResult[], topK: number) { - if (results.length <= 1) return false; - const topScore = Math.max(0, results[0]?.hybrid_score ?? results[0]?.similarity ?? 0); - const secondScore = Math.max(0, results[1]?.hybrid_score ?? results[1]?.similarity ?? 0); - const topScoresClose = Math.abs(topScore - secondScore) <= 0.04; - const hasVisualEvidence = results.some((result) => visualEvidenceUnitTypes.has(result.index_unit?.unit_type ?? "")); - const hasTableVisualEvidence = results.some((result) => - tableVisualEvidenceUnitTypes.has(result.index_unit?.unit_type ?? ""), - ); - if (queryClass === "table_threshold" || queryClass === "medication_dose_risk") { - return hasVisualEvidence || hasTableVisualEvidence || topScoresClose; - } - if (queryClass === "comparison") return results.length > topK || topScoresClose; - return topScoresClose && hasVisualEvidence; -} - -/** Second stage score. */ -function secondStageScore(result: SearchResult, queryClass: RagQueryClass | undefined, index: number) { - const baseRankScore = - result.score_explanation?.rankScore ?? - result.score_explanation?.preClampFinalScore ?? - result.score_explanation?.finalScore ?? - result.hybrid_score ?? - result.similarity ?? - 0; - let adjustment = 0; - const unitType = result.index_unit?.unit_type ?? ""; - const source = result.index_unit?.metadata?.source; - const sourceQuality = Number(result.index_unit?.quality_score ?? 0.65); - const doseAmountText = `${result.section_heading ?? ""} ${result.content} ${(result.images ?? []) - .map((image) => `${image.caption ?? ""} ${image.tableTextSnippet ?? ""} ${image.tableTitle ?? ""}`) - .join(" ")} ${(result.table_facts ?? []) - .map( - (fact) => `${fact.table_title ?? ""} ${fact.row_label ?? ""} ${fact.threshold_value ?? ""} ${fact.action ?? ""}`, - ) - .join(" ")}`; - const hasDoseAmount = /\b\d+(?:\.\d+)?\s?(?:mg|mcg|microgram|micrograms)\b/i.test(doseAmountText); - const w = rankingConfig.secondStage; - adjustment += Math.max(0, w.positionBase - index * w.positionStep); - if (result.memory_cards?.length && (queryClass === "broad_summary" || queryClass === "comparison")) - adjustment += w.memorySummaryBoost; - if (queryClass === "document_lookup" && (result.match_explanation?.titleHit || result.match_explanation?.labelHit)) - adjustment += w.documentLookupTitleBoost; - if ((queryClass === "table_threshold" || queryClass === "medication_dose_risk") && result.table_facts?.length) - adjustment += w.tableThresholdEvidenceBoost; - if (queryClass === "medication_dose_risk" && hasDoseAmount) adjustment += w.doseAmountBoost; - if (tableVisualEvidenceUnitTypes.has(unitType)) adjustment += w.tableVisualBoost; - else if (visualEvidenceUnitTypes.has(unitType)) adjustment += w.visualBoost; - if (source === "visual_intelligence") - adjustment += Math.min( - w.visualIntelligenceMax, - Math.max(0, sourceQuality - w.visualIntelligencePivot) * w.visualIntelligenceSlope, - ); - if (result.source_metadata?.document_status === "outdated") adjustment -= w.outdatedPenalty; - // D4: ships 0 (no-op) — activate via RAG_RANKING_CONFIG only behind a green golden eval. - if (result.source_metadata?.document_status === "unknown") adjustment -= w.unknownCurrentnessPenalty; - if (result.source_metadata?.extraction_quality === "poor") adjustment -= w.poorExtractionPenalty; - if ( - result.indexing_quality?.quality_score !== undefined && - result.indexing_quality.quality_score < w.lowIndexQualityThreshold - ) - adjustment -= w.lowIndexQualityPenalty; - return { rankScore: baseRankScore + adjustment, adjustment }; -} - -/** Apply second stage rerank if needed. */ -export function applySecondStageRerankIfNeeded(args: { - queryClass?: RagQueryClass; - results: SearchResult[]; - telemetry: SearchTelemetry; - topK: number; -}) { - if (!shouldUseSecondStageRerank(args.queryClass, args.results, args.topK)) return args.results; - const startedAt = Date.now(); - // CI-16 document diversity: subtract a demotion from each EXTRA chunk of a document that - // has already appeared higher up, so a single doc's sibling chunks can't crowd out other - // documents. Applied AFTER the additive-boost floor so it can actually lower the effective - // rank. Keep this separate from the selection-rescue floor in retrieval-selection.ts. - const seenPerDocument = new Map(); - const reranked = args.results - .map((result, index) => { - const secondStage = secondStageScore(result, args.queryClass, index); - let rankScore = secondStage.rankScore; - let confidenceAdjustment = secondStage.adjustment; - const releasedHybridScore = result.hybrid_score ?? result.similarity ?? 0; - let releaseRankScore = Math.max( - releasedHybridScore, - (result.score_explanation?.finalScore ?? result.hybrid_score ?? result.similarity ?? 0) + - secondStage.adjustment, - ); - const priorOccurrences = seenPerDocument.get(result.document_id) ?? 0; - seenPerDocument.set(result.document_id, priorOccurrences + 1); - if (rankingConfig.documentDiversityPenalty > 0 && priorOccurrences > 0) { - const diversityPenalty = Math.min( - rankingConfig.documentDiversityPenaltyCap, - rankingConfig.documentDiversityPenalty * priorOccurrences, - ); - rankScore -= diversityPenalty; - confidenceAdjustment -= diversityPenalty; - releaseRankScore -= diversityPenalty; - } - const selectionReasons = result.match_explanation?.reasons ?? []; - const clinicalSubjectRequired = selectionReasons.includes("retrieval_required_signal:clinical_subject"); - const clinicalSubjectMatched = selectionReasons.includes("retrieval_signal:clinical_subject"); - if (clinicalSubjectRequired && !clinicalSubjectMatched) { - // A wrong-medication chunk can carry attractive numeric dose/monitoring signals. Keep it - // available at its released hybrid strength, but do not let second-stage evidence boosts - // promote it above chunks that contain the medication subject requested by the query. - releaseRankScore = Math.min(releaseRankScore, releasedHybridScore); - } - const finalScore = Math.min( - 1, - Math.max( - 0, - (result.score_explanation?.finalScore ?? result.hybrid_score ?? result.similarity ?? 0) + - confidenceAdjustment, - ), - ); - return { - rankScore, - result: { - ...result, - score_explanation: result.score_explanation - ? { - ...result.score_explanation, - rankScore: Number(rankScore.toFixed(4)), - releaseRankScore: Number(releaseRankScore.toFixed(4)), - preClampFinalScore: Number(rankScore.toFixed(4)), - finalScore: Number(finalScore.toFixed(4)), - } - : result.score_explanation, - match_explanation: { - ...result.match_explanation, - reasons: Array.from(new Set([...(result.match_explanation?.reasons ?? []), "second_stage_rerank"])), - }, - }, - }; - }) - .sort((left, right) => right.rankScore - left.rankScore || left.result.id.localeCompare(right.result.id)) - .map(({ result }, index) => - result.score_explanation - ? { ...result, score_explanation: { ...result.score_explanation, finalRank: index + 1 } } - : result, - ); - args.telemetry.second_stage_rerank_used = true; - args.telemetry.second_stage_rerank_latency_ms = - (args.telemetry.second_stage_rerank_latency_ms ?? 0) + Date.now() - startedAt; - recordRetrievalLayer(args.telemetry, "second_stage_rerank", reranked.length, { - latencyMs: Date.now() - startedAt, - topScore: layerTopScore(reranked), - }); - return reranked; -} - const citationSchema = z.object({ chunk_id: z.string(), document_id: z.string().optional(), From f7d099d6e8c51c3f6629af2c8ea8dbde21d0e447 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 30 Jul 2026 21:24:37 +0000 Subject: [PATCH 2/6] fix(docs): correct rag.ts budget numbers in maturity backlog workorders --- docs/maturity-backlog-workorders.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maturity-backlog-workorders.md b/docs/maturity-backlog-workorders.md index 664dce6d3d..1ead6036d0 100644 --- a/docs/maturity-backlog-workorders.md +++ b/docs/maturity-backlog-workorders.md @@ -96,7 +96,7 @@ structural change, not a single mixed PR. unit into `src/lib/rag/rag-second-stage.ts`: engagement policy, score adjustment, document diversity, subject-match protection, and its retrieval-layer telemetry moved together without changing the public `@/lib/rag/rag` export. `rag.ts` remains the retrieval orchestrator and - calls the extracted unit at the same pipeline points (4,780 → 4,592; budget ratcheted to 4,592). + calls the extracted unit at the same pipeline points (4,780 → 4,351; budget ratcheted to 4,351). - **Progress (`DocumentViewer.tsx`):** extracted the cohesive leaf modules into `src/components/document-viewer/` — shared row `types.ts`, `source-panels.tsx` (summary profile, high-yield summary, source images/tables, pinned evidence, indexed-text panel), the From 3537957ef8b0a4ef479a56d7a78f708c67b4da70 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 00:15:32 +0000 Subject: [PATCH 3/6] fix(docs): correct X3 second-stage ratchet baseline for PR #1472 Use the post-hydration 4,543-line baseline and name PR #1472 so the #086 second-stage progress note no longer collides with the coverage-gate entry. Co-authored-by: BigSimmo --- docs/maturity-backlog-workorders.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/maturity-backlog-workorders.md b/docs/maturity-backlog-workorders.md index 1ead6036d0..4033b0bbd1 100644 --- a/docs/maturity-backlog-workorders.md +++ b/docs/maturity-backlog-workorders.md @@ -92,11 +92,12 @@ structural change, not a single mixed PR. `rag.ts`: it is pipeline orchestration that calls the metadata/visual hydration and second-stage rerank helpers, so moving it would need a runtime back-edge to `rag.ts`. The hydration cluster is the separate later extraction (`rag-hydration.ts`). -- **Progress (#086, second-stage extraction):** extracted the cohesive second-stage reranking - unit into `src/lib/rag/rag-second-stage.ts`: engagement policy, score adjustment, document - diversity, subject-match protection, and its retrieval-layer telemetry moved together without - changing the public `@/lib/rag/rag` export. `rag.ts` remains the retrieval orchestrator and - calls the extracted unit at the same pipeline points (4,780 → 4,351; budget ratcheted to 4,351). +- **Progress (#086, X3 second-stage extraction, PR #1472):** extracted the cohesive + second-stage reranking unit into `src/lib/rag/rag-second-stage.ts`: engagement policy, + score adjustment, document diversity, subject-match protection, and its retrieval-layer + telemetry moved together without changing the public `@/lib/rag/rag` export. `rag.ts` + remains the retrieval orchestrator and calls the extracted unit at the same pipeline + points (4,543 → 4,351; budget ratcheted to 4,351). - **Progress (`DocumentViewer.tsx`):** extracted the cohesive leaf modules into `src/components/document-viewer/` — shared row `types.ts`, `source-panels.tsx` (summary profile, high-yield summary, source images/tables, pinned evidence, indexed-text panel), the From cb07a6c3698bc5683a0783555032490cbf09b674 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 00:19:40 +0000 Subject: [PATCH 4/6] fix(codex-cloud): inspect stored origin URL, not insteadOf rewrite Read remote.origin.url via git config so tokenized url.*.insteadOf rewrites in Cursor Cloud do not falsely treat a clean origin as credential-bearing and break Codex Cloud setup tests. Co-authored-by: BigSimmo --- scripts/ensure-codex-cloud-git-remote.mjs | 5 ++++- tests/codex-cloud-setup.test.ts | 9 ++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/scripts/ensure-codex-cloud-git-remote.mjs b/scripts/ensure-codex-cloud-git-remote.mjs index 9d821ce3a3..544e42dc6e 100644 --- a/scripts/ensure-codex-cloud-git-remote.mjs +++ b/scripts/ensure-codex-cloud-git-remote.mjs @@ -38,7 +38,10 @@ export function remoteRepositoryIdentity(remoteUrl) { } export function inspectOriginRemote(root) { - const result = run("git", ["remote", "get-url", "origin"], root); + // Read the configured remote URL, not the insteadOf-rewritten fetch URL. + // Environments that inject tokenized url.*.insteadOf rewrites (Cursor Cloud) + // would otherwise make a clean stored origin look credential-bearing. + const result = run("git", ["config", "--get", "remote.origin.url"], root); if (result.status !== 0) { return { configured: false, diff --git a/tests/codex-cloud-setup.test.ts b/tests/codex-cloud-setup.test.ts index 6629a224c0..fe9573893f 100644 --- a/tests/codex-cloud-setup.test.ts +++ b/tests/codex-cloud-setup.test.ts @@ -173,7 +173,8 @@ describe("Codex Cloud origin repair", () => { const directory = temporaryGitRepository(); expect(ensureOriginRemote(directory).action).toBe("added"); expect(ensureOriginRemote(directory).action).toBe("preserved"); - expect(git(directory, "remote", "get-url", "origin").stdout.trim()).toBe(CODEX_CLOUD_ORIGIN_URL); + // Assert the configured remote URL (not insteadOf-rewritten get-url output). + expect(git(directory, "config", "--get", "remote.origin.url").stdout.trim()).toBe(CODEX_CLOUD_ORIGIN_URL); expect(inspectOriginRemote(directory)).toEqual({ configured: true, repositoryMatch: true, @@ -185,12 +186,14 @@ describe("Codex Cloud origin repair", () => { const wrong = temporaryGitRepository(); expect(git(wrong, "remote", "add", "origin", "https://github.com/example/other.git").status).toBe(0); expect(() => ensureOriginRemote(wrong)).toThrow(/refusing to overwrite/); - expect(git(wrong, "remote", "get-url", "origin").stdout.trim()).toBe("https://github.com/example/other.git"); + expect(git(wrong, "config", "--get", "remote.origin.url").stdout.trim()).toBe( + "https://github.com/example/other.git", + ); const credentialed = temporaryGitRepository(); const unsafe = "https://token-value@github.com/BigSimmo/Database.git"; expect(git(credentialed, "remote", "add", "origin", unsafe).status).toBe(0); expect(() => ensureOriginRemote(credentialed)).toThrow(/embedded credentials/); - expect(git(credentialed, "remote", "get-url", "origin").stdout.trim()).toBe(unsafe); + expect(git(credentialed, "config", "--get", "remote.origin.url").stdout.trim()).toBe(unsafe); }); }); From 84fdfd72a5d23e79798be85ffee2dda4f6f6e94a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 00:23:26 +0000 Subject: [PATCH 5/6] docs(ledger): record PR #1472 reopen-prep review Append the closed-PR readiness review at tip cb07a6c3 after main sync, CodeRabbit baseline fix, and Codex Cloud origin inspect fix. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 02fb182afc..9018fd84f7 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -329,3 +329,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-07-31 | claude/root-dir-coverage-gate-v2 | 398660144d93aeefc2e5649c156948a68925cb64 | docs:check-index repo-root coverage, stale script counts, ledger correction | MERGED as PR #1458 (squash 907fd9f4a). Root-directory coverage pass for docs:check-index, red-then-green proven (flagged .cursor/.design-sync/.vscode, then 49 entries vs 31). Main landed an equivalent pass independently in #1480, so the two overlapped; no duplication reached main. Row not recorded at the time - appended retrospectively | verify:cheap exit 0, 435 test files / 4574 tests pass; codebase-index-coverage 10/10 incl 4 new root cases; eslint clean; docs gates green; prettier clean | | 2026-07-31 | claude/pre-commit-fail-open | 7b96a09b8500adc917cf5549b1c61142b2244b39 | pre-commit hook fail-open when the inventory script is absent | MERGED as PR #1494 (squash 387c3b653). Resolves ledger #153: core.hooksPath is absolute to the primary checkout, so the hook ran in worktrees lacking scripts/update-docs-inventory.mjs and aborted with MODULE_NOT_FOUND. Guard drops the inventory task and re-checks the all-tasks-empty exit; grep carries \|\| true because set -e treats a fully-filtering grep as failure | isolated-repo probe with the script genuinely absent: prints skipping inventory sync, commit succeeds; sh -n clean; no-op when the script is present; prettier does not parse shell so format:check skips it | | 2026-07-31 | claude/ledger-relanding | 30ec06964e4235d9f0b4bb782f357e6b4fb59430 | re-land the three session findings lost when PR #1490 was closed | MERGED as PR #1508 (squash 7b551abc4). Ledger-only: #151 corrects the claim that CI is unreadable (PAT has Actions:read though not Checks:read), #152 re-lands the at-risk worktree inventory with the four preservation snapshots, #153 archives the hook fix. Verified landed by content on main, not by PR state or row id | CI, PR Policy, PR mergeability, SAST, Secret Scan all completed/success via the Actions API; check:outstanding-issues 151 rows 45 open unique ids next-id=154; docs:check-links 1414 refs; prettier clean | +| 2026-07-31 | codex/complete-repository-maturity-programme | cb07a6c3698bc5683a0783555032490cbf09b674 | PR #1472 reopen prep | approved-with-notes | verify:pr-local green (444 files / 4645 tests); maintainability 4336/4351; focused RAG 730; bugbot none; CodeRabbit baseline fixed; main merged clean; PR left CLOSED | From febf00b7c588a544d5d7e6c1406d6a0f89cdeadc Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 31 Jul 2026 00:23:50 +0000 Subject: [PATCH 6/6] docs(ledger): supersede PR #1472 reopen-prep at tip Record the ledger-only tip after the approved reopen-prep review so lookup matches the current branch HEAD. Co-authored-by: BigSimmo --- docs/branch-review-ledger.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/branch-review-ledger.md b/docs/branch-review-ledger.md index 9018fd84f7..e5c6df67a2 100644 --- a/docs/branch-review-ledger.md +++ b/docs/branch-review-ledger.md @@ -330,3 +330,4 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie | 2026-07-31 | claude/pre-commit-fail-open | 7b96a09b8500adc917cf5549b1c61142b2244b39 | pre-commit hook fail-open when the inventory script is absent | MERGED as PR #1494 (squash 387c3b653). Resolves ledger #153: core.hooksPath is absolute to the primary checkout, so the hook ran in worktrees lacking scripts/update-docs-inventory.mjs and aborted with MODULE_NOT_FOUND. Guard drops the inventory task and re-checks the all-tasks-empty exit; grep carries \|\| true because set -e treats a fully-filtering grep as failure | isolated-repo probe with the script genuinely absent: prints skipping inventory sync, commit succeeds; sh -n clean; no-op when the script is present; prettier does not parse shell so format:check skips it | | 2026-07-31 | claude/ledger-relanding | 30ec06964e4235d9f0b4bb782f357e6b4fb59430 | re-land the three session findings lost when PR #1490 was closed | MERGED as PR #1508 (squash 7b551abc4). Ledger-only: #151 corrects the claim that CI is unreadable (PAT has Actions:read though not Checks:read), #152 re-lands the at-risk worktree inventory with the four preservation snapshots, #153 archives the hook fix. Verified landed by content on main, not by PR state or row id | CI, PR Policy, PR mergeability, SAST, Secret Scan all completed/success via the Actions API; check:outstanding-issues 151 rows 45 open unique ids next-id=154; docs:check-links 1414 refs; prettier clean | | 2026-07-31 | codex/complete-repository-maturity-programme | cb07a6c3698bc5683a0783555032490cbf09b674 | PR #1472 reopen prep | approved-with-notes | verify:pr-local green (444 files / 4645 tests); maintainability 4336/4351; focused RAG 730; bugbot none; CodeRabbit baseline fixed; main merged clean; PR left CLOSED | +| 2026-07-31 | codex/complete-repository-maturity-programme | 84fdfd72a5d23e79798be85ffee2dda4f6f6e94a | PR #1472 reopen prep | approved-with-notes | supersede cb07a6c3: tip is ledger-only after approved reopen prep; branch ready; PR remains CLOSED (GitHub freezes closed PR head until reopen) |