Files
Jyotisha/frontend/src/lib/rectification-agentic/v9/score-persist.ts
T
Jesse_ChenandClaude Opus 5.5 564eede340 fix(rectification): health distinguish focuses persist again; segment-gain order on by default for windows <= 21 minutes (BUG-1142, BUG-1143)
BUG-1142: distinguish focuses wrote the planner domain verbatim, and
health_pressure is not in the focus table's target_domain check. The write
failed inside a swallowed retry and the session went straight to delivery
although tap cards remained. Every focus write now goes through
focusTargetDomain (persistableFocusDomain, BUG-672), and focus-to-probe
matching uses sameCollectDomain.

BUG-1143: segmentOrderEnabledFor turns segment-gain probe order on by default
when the scanned window is at most 21 minutes, where the 77-case persisted
replay gained head hits with no lost truth segment. RECTIFICATION_SEGMENT_ORDER
=off disables it; =on keeps the 61-minute research envelope.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-10-01 11:47:04 +08:00

502 lines
19 KiB
TypeScript

/**
* Single candidate-score persist path for compare, stop, idle, and window change.
*
* Stop / idle keep previous answers. Block-scan choice and window widen still
* write a fresh inference_state, but with an empty answer ledger.
*/
import { shouldForceMinuteAfterSubBlocks } from "./block-scan.ts";
import { segmentOrderEnabledFor } from "../core/segment-probe-order.ts";
import { scanCaseSegments } from "./segment-scan.ts";
import { targetChartsForDomain } from "../core/segment-summary.ts";
import { buildProductCaseInferenceState } from "./product-inference.ts";
import { productDeliveryForDomain } from "./product-delivery.ts";
import { candidateSetId } from "../core/build-state.ts";
import {
buildCandidateContrastPacket,
conflictProbesFromContrast,
datedDomainsFromEvidence,
inspectDiscriminatorProbes,
mentionedVargaKeysFromLedgerEvidence,
volunteeredDomainsFromEvidence,
} from "../core/candidate-contrast-packet.ts";
import { readVedastroMinuteSensitiveStatus } from "./confirmation-gate.ts";
import {
askedDiscriminatorKeys,
askedSemanticKeysForEngine,
authoritativeCandidateProjection,
previousInferenceFromReceipt,
} from "./inference-adapter.ts";
import { columnTimesForSlowCompare } from "./column-times-for-compare.ts";
import { refinementFromDecisionReceipt } from "./refinement-packet.ts";
import { declinedDomainNames } from "./collection-question-pool.ts";
import { blockScanRequestExtras } from "./search-window.ts";
import {
cachedEngineScoreIsReusable,
executedMethodsFromLedger,
mergeVedastroValidateIntoReceipt,
readV9EngineScoringIdentity,
scoringIdentityIsTrusted,
scoringIdentityMatches,
runV9BlockScan,
runV9CandidateScore,
runV9VedastroValidate,
toEngineEvents,
type V9EngineScoreResult,
} from "./engine-client.ts";
import { windowScanFromDecisionReceipt, type WindowScan } from "./varga-observations.ts";
import {
candidateRangeFingerprint,
ensureV9DatedWindow,
evidenceLedgerFingerprint,
loadV9CaseCompute,
loadV9CaseDossier,
persistV9Candidate,
refreshV9VedastroValidation,
RectificationToolServiceError,
scorableEvidence,
setV9CaseStage,
writeV9BlockScan,
type AccountingClient,
type V9CaseDossier,
type V9PersistedCandidate,
} from "./tool-service.ts";
export type ScorePersistTimings = Readonly<{
engine_compare_ms: number;
vedastro_validate_ms: number;
persist_ms: number;
}>;
export type ScorePersistResult = Readonly<{
readOnly?: boolean;
persisted: V9PersistedCandidate | Readonly<{
resultId: string;
cached: boolean;
candidates: readonly [];
overallConfidence: "low";
selectionAllowed: false;
confirmationAllowed: false;
representativeTime: null;
algorithmVersion: string;
eventContractVersion: string;
policyVersion: string | null;
decisionReceipt: Readonly<Record<string, unknown>>;
executionLedger: readonly [];
}>;
score: V9EngineScoreResult;
dossier: V9CaseDossier;
windowScan: WindowScan | null;
decisionReceipt: Readonly<Record<string, unknown>>;
timings: ScorePersistTimings;
}>;
export async function persistBlockScanPayload(input: {
accounting: AccountingClient;
userId: string;
caseId: string;
evidenceFingerprint: string;
rangeFingerprint?: string;
scan: Awaited<ReturnType<typeof runV9BlockScan>>;
previousRounds?: number;
}): Promise<void> {
const isSub = input.scan.blocks.some((block) => (
block.period === "sub_1" || block.period === "sub_2" || block.period === "sub_3"
));
await writeV9BlockScan(input.accounting, input.userId, input.caseId, {
evidence_ledger_fingerprint: input.evidenceFingerprint,
algorithm_version: input.scan.algorithmVersion,
decision_policy_version: input.scan.policyVersion,
candidate_range_fingerprint: input.rangeFingerprint ?? null,
minute_step: input.scan.minuteStep,
rounds: isSub ? (input.previousRounds ?? 0) + 1 : 0,
blocks: input.scan.blocks,
});
}
async function persistableReceipt(
score: V9EngineScoreResult,
input: {
baselineBirthSnapshot: Readonly<Record<string, unknown>>;
candidateRange: { start_time: string; end_time: string };
events: ReturnType<typeof toEngineEvents>;
},
): Promise<Record<string, unknown>> {
const base = score.windowScan
? { ...score.decisionReceipt, window_scan: score.windowScan }
: { ...score.decisionReceipt };
const ranked = [...score.candidates].sort((left, right) => left.rank - right.rank);
const primary = ranked[0]?.time;
const runnerUp = ranked[1]?.time;
if (!score.selectionAllowed || !primary || !runnerUp || primary === runnerUp) return base;
const validation = await runV9VedastroValidate({
...input,
candidateTimes: [primary, runnerUp],
});
return mergeVedastroValidateIntoReceipt(base, validation);
}
function emptyBlockScanScore(
candidateRange: { start_time: string; end_time: string },
algorithmVersion: string,
engineResultId = "",
): V9EngineScoreResult {
return {
engineResultId,
algorithmVersion,
eventContractVersion: "",
policyVersion: "",
candidateRange,
candidates: [],
overallConfidence: "low",
marginPercent: null,
acceptanceAllowed: false,
selectionAllowed: false,
proposeAllowed: false,
confirmationAllowed: false,
representativeCandidateId: null,
representativeTime: null,
decisionReceipt: {},
executionLedger: [],
executedMethods: [],
windowScan: null,
};
}
export async function scoreAndPersistCurrentEvidence(input: {
accounting: AccountingClient;
userId: string;
caseId: string;
keepAnswers?: boolean;
}): Promise<ScorePersistResult> {
const keepAnswers = input.keepAnswers !== false;
const dossier = await loadV9CaseDossier(input.accounting, input.userId, input.caseId);
const scorable = scorableEvidence(dossier.evidence);
if (scorable.length === 0) {
throw new RectificationToolServiceError("no_scorable_evidence");
}
let candidateRange = dossier.case.candidateRange;
if (!candidateRange) throw new RectificationToolServiceError("case_range_missing");
const compute = await loadV9CaseCompute(input.accounting, input.userId, input.caseId);
const liveIdentity = await readV9EngineScoringIdentity();
if (scoringIdentityIsTrusted(liveIdentity) && liveIdentity.algorithmVersion === "rectification-v5-matrix-scoring-9" && !candidateRange.candidate_intervals) {
candidateRange = await ensureV9DatedWindow(input.accounting, input.userId, input.caseId);
}
if (candidateRange.midnight_side_pending === true && scoringIdentityIsTrusted(liveIdentity)) {
throw new RectificationToolServiceError("midnight_side_pending");
}
const evidenceFingerprint = evidenceLedgerFingerprint(dossier.evidence);
const rangeFingerprint = candidateRangeFingerprint(candidateRange, compute.baselineProfileFingerprint);
const readOnly = !scoringIdentityIsTrusted(liveIdentity);
if (readOnly && !(dossier.case.stage === "block_scan" ? dossier.case.blockScan : dossier.latestResult)) {
throw new RectificationToolServiceError("engine_identity_unavailable");
}
if (
!readOnly
&& dossier.case.stage === "block_scan"
&& shouldForceMinuteAfterSubBlocks(dossier.case.blockScan)
) {
await setV9CaseStage(input.accounting, input.userId, input.caseId, "minute");
return scoreAndPersistCurrentEvidence(input);
}
if (dossier.case.stage === "block_scan") {
const cachedPayload = dossier.case.blockScan;
const cached = cachedEngineScoreIsReusable(cachedPayload, {
evidenceLedgerFingerprint: evidenceFingerprint, candidateRangeFingerprint: rangeFingerprint,
}, liveIdentity);
if (!cached && !readOnly) {
const events = toEngineEvents(scorable);
const extras = blockScanRequestExtras(candidateRange);
const scoreStarted = Date.now();
const scan = await runV9BlockScan({
baselineBirthSnapshot: compute.baselineBirthSnapshot,
candidateRange,
events,
...extras,
});
const engineCompareMs = Date.now() - scoreStarted;
const persistStarted = Date.now();
await persistBlockScanPayload({
accounting: input.accounting,
userId: input.userId,
caseId: input.caseId,
evidenceFingerprint,
rangeFingerprint,
scan,
previousRounds: cachedPayload?.rounds ?? 0,
});
const reloaded = await loadV9CaseDossier(input.accounting, input.userId, input.caseId);
return {
readOnly: !scoringIdentityMatches(scan, liveIdentity),
persisted: {
resultId: scan.resultId ?? "",
cached: false,
candidates: [],
overallConfidence: "low",
selectionAllowed: false,
confirmationAllowed: false,
representativeTime: null,
algorithmVersion: scan.algorithmVersion,
eventContractVersion: "",
policyVersion: scan.policyVersion,
decisionReceipt: {},
executionLedger: [],
},
score: emptyBlockScanScore(candidateRange, scan.algorithmVersion, scan.resultId ?? ""),
dossier: reloaded,
windowScan: null,
decisionReceipt: {},
timings: {
engine_compare_ms: engineCompareMs,
vedastro_validate_ms: 0,
persist_ms: Date.now() - persistStarted,
},
};
}
return {
readOnly,
persisted: {
resultId: "",
cached,
candidates: [],
overallConfidence: "low",
selectionAllowed: false,
confirmationAllowed: false,
representativeTime: null,
algorithmVersion: cachedPayload?.algorithmVersion ?? "",
eventContractVersion: "",
policyVersion: cachedPayload?.policyVersion ?? null,
decisionReceipt: {},
executionLedger: [],
},
score: emptyBlockScanScore(candidateRange, cachedPayload?.algorithmVersion ?? ""),
dossier,
windowScan: null,
decisionReceipt: {},
timings: {
engine_compare_ms: 0,
vedastro_validate_ms: 0,
persist_ms: 0,
},
};
}
const events = toEngineEvents(scorable);
const previousReceipt = keepAnswers ? dossier.latestResult?.decisionReceipt : null;
const askedProbeKeys = keepAnswers
? askedSemanticKeysForEngine(previousReceipt, dossier.evidence)
: [];
const latest = dossier.latestResult;
if (
latest
&& (readOnly || (keepAnswers && cachedEngineScoreIsReusable(
latest,
{ evidenceLedgerFingerprint: evidenceFingerprint, candidateRangeFingerprint: rangeFingerprint },
liveIdentity,
)))
) {
const ledger = latest.executionLedger ?? [];
const windowScan = windowScanFromDecisionReceipt(latest.decisionReceipt);
let decisionReceipt = latest.decisionReceipt ?? {};
const candidateProjection = authoritativeCandidateProjection(latest);
const candidates = candidateProjection.fromInference ? candidateProjection.candidates : latest.candidates;
const representativeTime = candidateProjection.fromInference
? candidateProjection.representativeTime
: latest.representativeTime;
const ranked = [...candidates].sort((left, right) => left.rank - right.rank);
const primary = ranked[0]?.time;
const runnerUp = ranked[1]?.time;
let vedastroMs = 0;
let persistMs = 0;
if (
!readOnly
&& latest.selectionAllowed
&& primary
&& runnerUp
&& primary !== runnerUp
&& readVedastroMinuteSensitiveStatus(decisionReceipt) !== "passed"
) {
const vedastroStarted = Date.now();
const validation = await runV9VedastroValidate({
baselineBirthSnapshot: compute.baselineBirthSnapshot,
candidateRange,
events,
candidateTimes: [primary, runnerUp],
});
vedastroMs = Date.now() - vedastroStarted;
const persistStarted = Date.now();
decisionReceipt = await refreshV9VedastroValidation(input.accounting, input.userId, input.caseId, {
resultId: latest.resultId,
evidenceFingerprint,
rangeFingerprint,
validation: {
status: validation.status,
search_events_primary_supports_local_winner: validation.searchEventsSupportsLocalWinner,
can_confirm_exact_minute: validation.canConfirmExactMinute,
failure: validation.failure,
},
minuteSensitiveStatus: validation.minuteSensitiveStatus,
});
persistMs = Date.now() - persistStarted;
}
return {
readOnly,
persisted: {
resultId: latest.resultId,
cached: !readOnly,
candidates,
overallConfidence: latest.overallConfidence,
selectionAllowed: !readOnly && candidateProjection.consistent && latest.selectionAllowed,
confirmationAllowed: !readOnly && latest.confirmationAllowed,
representativeTime,
algorithmVersion: latest.algorithmVersion,
eventContractVersion: latest.eventContractVersion,
policyVersion: latest.policyVersion,
decisionReceipt,
executionLedger: ledger,
},
score: {
engineResultId: latest.resultId,
algorithmVersion: latest.algorithmVersion ?? "",
eventContractVersion: latest.eventContractVersion ?? "",
policyVersion: latest.policyVersion ?? "",
candidateRange,
candidates,
overallConfidence: latest.overallConfidence,
marginPercent: null,
acceptanceAllowed: !readOnly && latest.selectionAllowed,
selectionAllowed: !readOnly && candidateProjection.consistent && latest.selectionAllowed,
proposeAllowed: !readOnly && latest.selectionAllowed,
confirmationAllowed: !readOnly && latest.confirmationAllowed,
representativeCandidateId: null,
representativeTime,
decisionReceipt,
executionLedger: ledger,
executedMethods: executedMethodsFromLedger(ledger),
windowScan,
},
dossier,
windowScan,
decisionReceipt,
timings: {
engine_compare_ms: 0,
vedastro_validate_ms: vedastroMs,
persist_ms: persistMs,
},
};
}
const scoreStarted = Date.now();
const latestReceipt = dossier.latestResult?.decisionReceipt;
const previousCompareMs = typeof latestReceipt?.column_compare_ms === "number"
? latestReceipt.column_compare_ms
: null;
const previousInference = previousInferenceFromReceipt(latestReceipt);
const columnTimes = columnTimesForSlowCompare({
previousColumnCompareMs: previousCompareMs,
activeTimes: previousInference?.candidates
.filter((candidate) => candidate.status === "active")
.map((candidate) => candidate.time) ?? [],
});
const score = await runV9CandidateScore({
baselineBirthSnapshot: compute.baselineBirthSnapshot,
candidateRange,
events,
askedProbeKeys,
declinedDomains: declinedDomainNames(dossier.conversationSummary.declinedSkippedTopics),
columnTimes,
});
const engineCompareMs = Date.now() - scoreStarted;
const vedastroStarted = Date.now();
const responseReadOnly = !scoringIdentityMatches(score, liveIdentity);
const receipt = responseReadOnly ? score.decisionReceipt : await persistableReceipt(score, {
baselineBirthSnapshot: compute.baselineBirthSnapshot,
candidateRange,
events,
});
const vedastroMs = Date.now() - vedastroStarted;
const refinement = refinementFromDecisionReceipt(receipt);
const windowScan = windowScanFromDecisionReceipt(receipt);
const scoredCandidateSetId = candidateSetId(
candidateRange.start_time,
candidateRange.end_time,
score.candidates.map((item) => item.time),
score.candidates,
);
const askedKeys = keepAnswers
? askedDiscriminatorKeys(previousReceipt, dossier.evidence)
: [];
const contrastPacket = buildCandidateContrastPacket({
candidateSetVersion: scoredCandidateSetId,
calculationResultId: score.engineResultId,
engineProbes: refinement.discriminating_event_probes,
vargaDifferences: [
...(windowScan?.d9_candidates_differ && windowScan.d9_sign_names.length >= 2
? [{ layer: "d9" as const, signs: windowScan.d9_sign_names }]
: []),
...(windowScan?.d10_candidates_differ && windowScan.d10_sign_names.length >= 2
? [{ layer: "d10" as const, signs: windowScan.d10_sign_names }]
: []),
],
candidateTimes: score.candidates.map((item) => item.time),
transitions: windowScan?.transitions ?? [],
askedKeys,
volunteeredDomains: volunteeredDomainsFromEvidence(dossier.evidence),
providedDomains: datedDomainsFromEvidence(dossier.evidence),
});
// A failed optional scan must not destroy the frozen scoring result. Candidate
// signatures can still support a partial summary, never a full-window claim.
const productDomain = dossier.case.rectificationDomain;
const segmentTargets = productDomain ? targetChartsForDomain(productDomain) : undefined;
const segmentMinutes = segmentTargets
? await scanCaseSegments({ snapshot: compute.baselineBirthSnapshot, range: candidateRange, targets: segmentTargets })
.catch(() => undefined)
: undefined;
const inference = buildProductCaseInferenceState({
segmentMinutes, segmentTargets, segmentScanComplete: Boolean(segmentMinutes),
segmentOrderEnabled: segmentOrderEnabledFor(segmentMinutes?.length, process.env.RECTIFICATION_SEGMENT_ORDER),
range: candidateRange,
candidates: score.candidates,
evidence: scorable,
probes: refinement.discriminating_event_probes,
extraProbes: conflictProbesFromContrast(contrastPacket),
previous: keepAnswers ? previousInferenceFromReceipt(previousReceipt ?? null) : null,
transitionTimes: windowScan?.transitions.filter((item) => !item.segment_start).map((item) => item.at) ?? [],
transitions: windowScan?.transitions ?? [],
}, productDomain);
const decisionReceipt = {
...receipt,
...(productDomain ? { product_delivery: productDeliveryForDomain(productDomain) } : {}),
inference_state: inference,
dropped_probes: inspectDiscriminatorProbes(contrastPacket, {
askedKeys,
mentionedKeys: mentionedVargaKeysFromLedgerEvidence(dossier.evidence),
birthDate: String(compute.baselineBirthSnapshot.birth_date ?? "") || null,
}).dropped,
};
const persistStarted = Date.now();
const persisted = await persistV9Candidate(input.accounting, input.userId, input.caseId, {
engineResultId: score.engineResultId,
algorithmVersion: score.algorithmVersion,
evidenceFingerprint,
rangeFingerprint,
skillVersion: dossier.case.skillVersion,
eventContractVersion: score.eventContractVersion,
policyVersion: score.policyVersion,
candidateRange,
candidates: score.candidates,
decisionReceipt,
executionLedger: score.executionLedger,
});
return {
readOnly: responseReadOnly,
persisted: responseReadOnly ? { ...persisted, selectionAllowed: false, confirmationAllowed: false } : persisted,
score: responseReadOnly ? { ...score, selectionAllowed: false, confirmationAllowed: false, acceptanceAllowed: false, proposeAllowed: false } : score,
dossier,
windowScan: score.windowScan,
decisionReceipt,
timings: {
engine_compare_ms: engineCompareMs,
vedastro_validate_ms: vedastroMs,
persist_ms: Date.now() - persistStarted,
},
};
}