feat(rectification): read the adopted segment result's unreliable charts for reports and chat (BUG-1138, T1)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
Jesse_Chen
2026-10-01 10:15:10 +08:00
co-authored by Claude Opus 5.5
parent 44cdd9c9b9
commit 35b89bdf21
2 changed files with 234 additions and 0 deletions
@@ -0,0 +1,119 @@
import { parseSegmentSummary, TARGET_CHARTS, type SegmentSummary, type TargetChart } from "./rectification-agentic/core/segment-summary.ts";
/**
* Which divisional charts a segment-v1 adoption could not determine (BUG-1138/1139).
*
* The rectification delivery card already says "D9 cannot be determined" for a
* wide window; reports and chat used to read every chart from the saved minute
* anyway. This module reads the adopted result's own segment summary so they can
* say the same thing. Anything else (older adoptions, unrectified profiles, a
* missing or malformed result) returns null and leaves callers unchanged.
*/
export const UNRELIABLE_CHART_TIERS = new Set(["blocked", "indistinct"]);
export const CASE_DOSSIER_RPC = "get_agentic_rectification_case_dossier";
export type SegmentProvenance = Readonly<{ caseId: string; resultId: string }>;
export type AdoptedChartTiers = Readonly<{
source: "segment-v1";
/** "adopted": the summary of the adopted result; "latest": a newer result of the same Case. */
resultMatch: "adopted" | "latest";
unreliable: readonly TargetChart[];
}>;
type RpcResponse = PromiseLike<{ data: unknown; error: unknown }>;
export type AdoptedChartTierClient = Readonly<{ rpc: (fn: string, args: Record<string, unknown>) => RpcResponse }>;
export type AdoptedChartTierWarn = (payload: Readonly<{ event: "adopted_chart_tiers_unavailable"; reason: string }>) => void;
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
function record(value: unknown): Record<string, unknown> | null {
return value !== null && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : null;
}
/** Only the segment-v1 adoption contract carries a result whose summary describes the saved minute. */
export function segmentProvenance(value: unknown): SegmentProvenance | null {
const row = record(value);
if (!row || row.contract !== "segment-v1") return null;
const caseId = typeof row.case_id === "string" ? row.case_id : "";
const resultId = typeof row.result_id === "string" ? row.result_id : "";
return uuidPattern.test(caseId) && uuidPattern.test(resultId) ? { caseId, resultId } : null;
}
export function unreliableChartsFromSummary(summary: SegmentSummary): TargetChart[] {
return TARGET_CHARTS.filter((chart) => summary.charts.some((row) => row.chart === chart && UNRELIABLE_CHART_TIERS.has(row.tier)));
}
/**
* Prefer the adopted result. A later result of the same Case (answers after
* adoption) carries strictly more evidence about the same window, so its tiers
* are used instead; a different Case is never read.
*/
export function chartTiersFromDossier(dossier: unknown, provenance: SegmentProvenance): AdoptedChartTiers | "invalid" {
const root = record(dossier);
const caseRow = record(root?.case);
const latest = record(root?.latest_result);
const caseId = typeof caseRow?.case_id === "string" ? caseRow.case_id : typeof caseRow?.id === "string" ? caseRow.id : null;
if (!root || !latest || (caseId !== null && caseId !== provenance.caseId)) return "invalid";
const resultId = typeof latest.result_id === "string" ? latest.result_id : null;
if (!resultId) return "invalid";
const receipt = record(latest.decision_receipt);
const state = record(receipt?.inference_state);
const summary = parseSegmentSummary(state?.segment_summary);
if (!summary) return "invalid";
return {
source: "segment-v1",
resultMatch: resultId === provenance.resultId ? "adopted" : "latest",
unreliable: unreliableChartsFromSummary(summary),
};
}
const CACHE_TTL_MS = 5 * 60 * 1000;
const CACHE_LIMIT = 500;
const cache = new Map<string, { at: number; value: AdoptedChartTiers }>();
export function clearAdoptedChartTierCache(): void {
cache.clear();
}
function defaultWarn(payload: Readonly<{ event: string; reason: string }>): void {
console.warn(JSON.stringify(payload));
}
function errorCode(error: unknown): string {
if (error && typeof error === "object" && "code" in error && typeof error.code === "string" && error.code.trim()) return error.code.trim();
if (error instanceof Error && error.name.trim()) return error.name;
return "unknown";
}
export async function loadAdoptedChartTiers(
client: AdoptedChartTierClient,
input: Readonly<{ userId: string; provenance: unknown }>,
warn: AdoptedChartTierWarn = defaultWarn,
now: () => number = Date.now,
): Promise<AdoptedChartTiers | null> {
const provenance = segmentProvenance(input.provenance);
if (!provenance) return null;
const key = `${input.userId}|${provenance.caseId}|${provenance.resultId}`;
const hit = cache.get(key);
if (hit && now() - hit.at < CACHE_TTL_MS) return hit.value;
try {
const result = await client.rpc(CASE_DOSSIER_RPC, { p_user_id: input.userId, p_case_id: provenance.caseId });
if (result.error) {
warn({ event: "adopted_chart_tiers_unavailable", reason: errorCode(result.error) });
return null;
}
const tiers = chartTiersFromDossier(result.data, provenance);
if (tiers === "invalid") {
warn({ event: "adopted_chart_tiers_unavailable", reason: "invalid_segment_summary" });
return null;
}
if (cache.size >= CACHE_LIMIT) cache.delete(cache.keys().next().value!);
cache.set(key, { at: now(), value: tiers });
return tiers;
} catch (error) {
warn({ event: "adopted_chart_tiers_unavailable", reason: errorCode(error) });
return null;
}
}