Skip to content
Merged
8 changes: 8 additions & 0 deletions data/repo-awareness-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -17738,6 +17738,14 @@
"outcome": "Merged #1376 from main with real conflicts: kept admission-before-scope + L1-2 REFUTED docs; took #1376 invalidation epochs / empty-scope Server-Timing / stream signal. Codex threads resolved earlier. MERGEABLE; CI re-running.",
"checks": "vitest preamble+rag-cache-invalidation 7/7; check:rag:fixtures 36/21; prior tip PR-required green before #1376 land"
},
{
"date": "2026-10-17",
"ref": "fix/final-non-design-recommendations-20261017",
"head": "8babcd56ad73a08ee314fda8f7d02f3e279bf72d",
"scope": "Telemetry switch & wire for generation degradation (#231), CAMHS crisis support lines (#8CKX6F), hostile PDF ingestion-to-answer pipeline tests (#7D8WZ2), and issue-inbox closures (#231, #FZP7VW, #ES3KTG; the #7D8WZ2 and #8CKX6F done requests were later cancelled in f7b67e374 and both issues stay open)",
"outcome": "Adversarial review completed; all 3 findings remediated; zero UI/design or DB migration changes",
"checks": "vitest (145/145 across 5 suites), typecheck:internal, lint:internal, check-clinical-hazard-controls"
},
{
"date": "2026-08-15",
"ref": "1976",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
| 2026-10-17 | fix/final-non-design-recommendations-20261017 | 8babcd56ad73a08ee314fda8f7d02f3e279bf72d | Telemetry switch & wire for generation degradation (#231), CAMHS crisis support lines (#8CKX6F), hostile PDF ingestion-to-answer pipeline tests (#7D8WZ2), and issue-inbox closures (#231, #FZP7VW, #ES3KTG; the #7D8WZ2 and #8CKX6F done requests were later cancelled in f7b67e374 and both issues stay open) | Adversarial review completed; all 3 findings remediated; zero UI/design or DB migration changes | vitest (145/145 across 5 suites), typecheck:internal, lint:internal, check-clinical-hazard-controls |
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 2,
"id": "4225c6be-7433-4217-a1d9-6e2f62cc6028",
"createdOn": "2026-10-17",
"action": "cancel",
"payload": {
"requestId": "d4e961f3-7642-4350-8405-ad335d16e01d",
"reason": "The hostile-class extraction cases use scripted extractor outcomes on an ordinary PDF, so they do not prove the real extractor handles hostile PDF bytes; #7D8WZ2 stays open until real hostile fixtures reach the production extractor."
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 2,
"id": "78baadbb-7abe-4714-b94f-131131546930",
"createdOn": "2026-10-17",
"action": "done",
"payload": {
"id": "#FZP7VW",
"outcome": "Already implemented on main: interactive query embedding uses interactiveSearchEmbeddingBudget (4000 ms timeout, maxRetries: 0) in src/lib/rag/rag.ts and src/lib/rag/rag-governed-search.ts with lexical fallback and telemetry; pinned by tests/rag-governed-entrypoint.test.ts.",
"baseRowFingerprint": "b237f643f228e621dd2e8da91d5991bad89dce38bb596d232c13e791ffe0ff52",
"targetUlid": "01M3GND2NVFZP7VWK32ZS8QSZP"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": 2,
"id": "7fa40430-3e1e-41d7-abc9-005660219c26",
"createdOn": "2026-10-17",
"action": "done",
"payload": {
"id": "#231",
"outcome": "Wired per-attempt generation degradation observations into rag_queries.metadata via generationDegradationTelemetryFields and answerScopedEvidenceMetadata in src/lib/rag/rag-answer-telemetry-metadata.ts and src/lib/rag/rag-generation-degradation.ts without altering the rollout-gated RagAnswer.generationDegradation response property; pinned by tests/rag-generation-degradation.test.ts and tests/rag-telemetry-canary-absence.test.ts.",
"baseRowFingerprint": "ed0831eef465d29a6bfa9f3fccd33ffee7cb985b59c7e27b72c1633b2e803cb9"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": 2,
"id": "d4e961f3-7642-4350-8405-ad335d16e01d",
"createdOn": "2026-10-17",
"action": "done",
"payload": {
"id": "#7D8WZ2",
"outcome": "Added end-to-end upload -> extraction -> chunking -> retrieval -> answer prompt assembly integration tests across all 7 hostile document classes in tests/hostile-document-ingestion-matrix.test.ts.",
"targetUlid": "01M51JV9GC7D8WZ29KQKRSF5HP"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"version": 2,
"id": "d8a01da8-9b9b-47c3-befc-eca71092fdf6",
"createdOn": "2026-10-17",
"action": "cancel",
"payload": {
"requestId": "d8f0c7b0-3e94-4b02-a17a-339f317274ec",
"reason": "The national-line promotion for camhs_crisis queries was removed from this PR (d95d6cd32) pending owner clinical approval, so #8CKX6F stays open."
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 2,
"id": "d8f0c7b0-3e94-4b02-a17a-339f317274ec",
"createdOn": "2026-10-17",
"action": "done",
"payload": {
"id": "#8CKX6F",
"outcome": "Surfaced active national and youth crisis support lines (Lifeline WA, Suicide Call Back Service, Kids Helpline, 13YARN) immediately after urgent-route pins for camhs_crisis queries in src/lib/service-ranker.ts while preserving CAMHS Crisis Connect and MHERL priority; pinned by tests/service-urgent-routing.test.ts.",
"baseRowFingerprint": "9670257d2eec72cae4a29b9f245a9f0759995b16da76d1627a40371bf6fbb303",
"targetUlid": "01M3CT4CFM8CKX6FCRKGSK2RRS"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 2,
"id": "f175ee1d-0a4b-4233-9fe7-880babc8ee28",
"createdOn": "2026-10-17",
"action": "done",
"payload": {
"id": "#ES3KTG",
"outcome": "Already implemented on main: /api/documents/upload enforces PDF object, stream, xref, and page-tree structural limits via assertUploadStructure (src/lib/upload-structure.ts) before storage upload.",
"baseRowFingerprint": "1428fcd55f89d7ff52ad8b04054c4a57469c0d4b79cbdcabcfff2a4a3d740020",
"targetUlid": "01M2R5TH2HES3KTGGWNZ752BHM"
}
}
44 changes: 43 additions & 1 deletion src/lib/rag/rag-answer-telemetry-metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { rankAnswerEvidence } from "@/lib/answer-ranking";
import { buildCrossDocumentFusionBrief, buildCrossDocumentSynthesisPlan } from "@/lib/cross-document-synthesis";
import { buildAnswerScoreExplanations, buildIndexingQuality, collectMemoryCards } from "@/lib/rag/rag-answer-support";
import { memoryCardChunkScore } from "@/lib/rag/rag-candidate-sources";
import {
readObservedGenerationDegradation,
type RagGenerationDegradationReason,
} from "@/lib/rag/rag-generation-degradation";
import type { RagAnswer, RagQueryClass } from "@/lib/types";

type AnswerLatencyTimings = NonNullable<RagAnswer["latencyTimings"]>;
Expand Down Expand Up @@ -52,11 +56,48 @@ export function answerLatencyMetadata(
};
}

/**
* Flattened scalar telemetry projection of `RagGenerationDegradationRecord` (#231).
*
* Separates `provider_initial_attempt_timeout` (zero-response initial call timeout)
* from `provider_quality_retry_exhausted` (response-bearing quality-gate rejection
* followed by retry-ladder exhaustion) in `rag_queries.metadata` without changing
* the rollout-gated `RagAnswer.generationDegradation` response contract.
*
* Every emitted field comes from `projectGenerationDegradation`, which allowlists
* enum strings and bounds integers, so free text and adversarial canary tokens can
* never pass through this helper.
*/
export function generationDegradationTelemetryFields(
answer: Pick<RagAnswer, "generationDegradation"> | object | null | undefined,
): {
generation_degradation_reason?: RagGenerationDegradationReason | null;
generation_degradation_attempt_count?: number;
generation_degradation_completed_response_count?: number;
generation_degradation_total_attempt_latency_ms?: number;
generation_degradation_initial_outcome?: string | null;
generation_degradation_initial_latency_ms?: number | null;
generation_degradation_initial_timeout_ms?: number | null;
} {
const degradation = readObservedGenerationDegradation(answer);
if (!degradation) return {};
const initialAttempt = degradation.attempts[0];
return {
generation_degradation_reason: degradation.reason,
generation_degradation_attempt_count: degradation.attempts.length,
generation_degradation_completed_response_count: degradation.completedResponseCount,
generation_degradation_total_attempt_latency_ms: degradation.totalAttemptLatencyMs,
generation_degradation_initial_outcome: initialAttempt?.outcome ?? null,
generation_degradation_initial_latency_ms: initialAttempt?.latencyMs ?? null,
generation_degradation_initial_timeout_ms: initialAttempt?.timeoutMs ?? null,
};
}

/** Persist answer-scoped evidence metadata from the exact sources that survived final retention. */
export function answerScopedEvidenceMetadata(
query: string,
queryClass: RagQueryClass,
answer: Pick<RagAnswer, "sources" | "indexingVersion">,
answer: Pick<RagAnswer, "sources" | "indexingVersion" | "generationDegradation">,
) {
const sources = answer.sources;
const ranking = rankAnswerEvidence(query, sources, queryClass);
Expand Down Expand Up @@ -91,6 +132,7 @@ export function answerScopedEvidenceMetadata(
indexing_extraction_quality: indexingQuality.extractionQuality,
indexing_stale: indexingQuality.stale,
...scoreExplanationLogMetadata(scoreExplanations),
...generationDegradationTelemetryFields(answer),
};
}

Expand Down
23 changes: 18 additions & 5 deletions src/lib/rag/rag-generation-degradation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,17 @@ export function generationCompletedOutput(answer: OutputLike): GenerationComplet
};
}

const observedGenerationDegradationByAnswer = new WeakMap<object, RagGenerationDegradationRecord>();

export function readObservedGenerationDegradation(answer: unknown): RagGenerationDegradationRecord | undefined {
if (!answer || typeof answer !== "object") return undefined;
const candidate = (answer as { generationDegradation?: unknown }).generationDegradation;
return (
projectGenerationDegradation(candidate as RagGenerationDegradationRecord | null | undefined) ??
observedGenerationDegradationByAnswer.get(answer)
);
}

export function createGenerationDegradationRecorder(options: {
enabled: boolean;
routeBudgetMs: number;
Expand All @@ -161,7 +172,6 @@ export function createGenerationDegradationRecorder(options: {
"route" | "timeoutMs" | "outputBudget" | "retrievalHealthy" | "coverage" | "contextCount"
>,
) {
if (!options.enabled) return;
if (attempts.length >= 4) {
observationComplete = false;
return;
Expand Down Expand Up @@ -228,10 +238,10 @@ export function createGenerationDegradationRecorder(options: {
if (attempt) attempt.verificationFailed = true;
},
finish(answer: OutputLike, failed: boolean): RagGenerationDegradationRecord | undefined {
if (!options.enabled || !isRagAnswerGenerationContract(contract)) return undefined;
if (!isRagAnswerGenerationContract(contract)) return undefined;
const last = current();
if (failed && last?.qualityReason && last.retryAdmission === "not_requested") last.retryAdmission = "exhausted";
return (
const record =
projectGenerationDegradation({
version: "generation-degradation-v1",
policy: "legacy-generation-policy-v1",
Expand All @@ -244,8 +254,11 @@ export function createGenerationDegradationRecorder(options: {
completedResponseCount: attempts.filter((a) => a.responseReceived).length,
totalAttemptLatencyMs: attempts.reduce((sum, a) => sum + a.latencyMs, 0),
completedOutput: generationCompletedOutput(answer),
}) ?? undefined
);
}) ?? undefined;
if (record && answer && typeof answer === "object") {
observedGenerationDegradationByAnswer.set(answer, record);
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
Resolved
}
return options.enabled ? record : undefined;
},
};
}
Expand Down
13 changes: 6 additions & 7 deletions src/lib/rag/rag-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,12 @@ export function createObservedAnswerGenerator<T>(args: {
let attempted = false;
try {
const timeoutMs = args.deadline.generationRequestTimeoutMs(env.OPENAI_ANSWER_TIMEOUT_MS);
if (args.recorder.enabled)
args.recorder.start({
...args.contextObservation(context),
route: options?.strong ? "strong" : "fast",
timeoutMs,
outputBudget: options?.maxOutputTokensOverride ? "recovery" : "standard",
});
args.recorder.start({
...args.contextObservation(context),
route: options?.strong ? "strong" : "fast",
timeoutMs,
outputBudget: options?.maxOutputTokensOverride ? "recovery" : "standard",
});
attempted = true;
const result = await args.deadline.race(
args.generate(input, args.schemaFor(context), {
Expand Down
Loading
Loading