fix(rectification): dedupe same-domain probe years and drop unanchored style cards (BUG-559)

Pass asked probe keys into the engine without changing result fingerprints, block nearby years already asked, and require a dated same-domain ledger event before rendering varga_style cards.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-06 23:03:11 +08:00
co-authored by Cursor
parent 150d7ef189
commit 3a9ae736e1
8 changed files with 372 additions and 9 deletions
@@ -457,6 +457,7 @@ function engineRequestBody(input: {
baselineBirthSnapshot: Readonly<Record<string, unknown>>;
candidateRange: { start_time: string; end_time: string };
events: readonly V9EngineEvent[];
askedProbeKeys?: readonly string[];
}): Record<string, unknown> {
const snapshot = input.baselineBirthSnapshot;
const birthDate = String(snapshot.birth_date ?? "");
@@ -483,6 +484,7 @@ function engineRequestBody(input: {
timezone_id: snapshot.timezone_id,
timezone_source: snapshot.timezone_source,
local_time_status: snapshot.local_time_status,
...(input.askedProbeKeys?.length ? { asked_probe_keys: [...input.askedProbeKeys] } : {}),
};
}
@@ -595,6 +597,7 @@ export async function runV9CandidateScore(input: {
baselineBirthSnapshot: Readonly<Record<string, unknown>>;
candidateRange: { start_time: string; end_time: string };
events: readonly V9EngineEvent[];
askedProbeKeys?: readonly string[];
}): Promise<V9EngineScoreResult> {
const data = await postEngine("/api/rectification/v5/score", engineRequestBody(input));
const candidates = readCandidates(data.candidate_decisions, input.candidateRange);
@@ -632,6 +635,7 @@ export async function runV9Diagnostics(input: {
baselineBirthSnapshot: Readonly<Record<string, unknown>>;
candidateRange: { start_time: string; end_time: string };
events: readonly V9EngineEvent[];
askedProbeKeys?: readonly string[];
}): Promise<V9EngineDiagnostics> {
const data = await postEngine("/api/rectification/v5/diagnostics", engineRequestBody(input));
const candidates = readCandidates(data.candidate_decisions, input.candidateRange);
@@ -34,7 +34,8 @@ export type ProbeRejectReason =
| "insufficient_candidates"
| "insufficient_outcomes"
| "yearless_ungrounded_contrast"
| "no_split_among_active";
| "no_split_among_active"
| "unanchored_varga_style";
export type StyleOptionsResult =
| { ok: true; options: ProbeStyleOption[] }
+11 -2
View File
@@ -50,7 +50,7 @@ import {
evidenceSubjectForDomain,
applyOccupationCollectLedgerNorm,
} from "@/lib/rectification-agentic/v9/evidence-model";
import { USER_COLLECT_QUESTION } from "@/lib/rectification-agentic/user-copy";
import { GENERIC_COLLECT_QUESTION, USER_COLLECT_QUESTION } from "@/lib/rectification-agentic/user-copy";
import {
isHoldoutVerificationQuote,
} from "@/lib/rectification-agentic/v9/choice-card";
@@ -839,6 +839,10 @@ export function createRectificationV9Tools(ctx: RectificationV9Context) {
compute.baselineProfileFingerprint,
);
const events = toEngineEvents(scorableEvidence(dossier.evidence));
const askedProbeKeys = askedDiscriminatorKeys(
dossier.latestResult?.decisionReceipt,
parsed.evidence,
);
const latest = dossier.latestResult;
const liveIdentity = await readV9EngineScoringIdentity();
if (
@@ -940,6 +944,7 @@ export function createRectificationV9Tools(ctx: RectificationV9Context) {
baselineBirthSnapshot: compute.baselineBirthSnapshot,
candidateRange: parsed.case.candidateRange,
events,
askedProbeKeys,
});
const engineCompareMs = Date.now() - scoreStarted;
const vedastroStarted = Date.now();
@@ -1263,7 +1268,7 @@ export function createRectificationV9Tools(ctx: RectificationV9Context) {
resultFingerprint: JSON.stringify({ reason: spoken.reason }),
});
const fallbackDomain = nextFollowup.domain ?? "";
const fallbackPrompt = USER_COLLECT_QUESTION[fallbackDomain] ?? USER_COLLECT_QUESTION.other;
const fallbackPrompt = USER_COLLECT_QUESTION[fallbackDomain] ?? GENERIC_COLLECT_QUESTION;
if (
spokenPromptFailures < 2
|| nextFollowup.intent !== "collect_method_evidence"
@@ -1910,6 +1915,10 @@ export function createRectificationV9Tools(ctx: RectificationV9Context) {
baselineBirthSnapshot: compute.baselineBirthSnapshot,
candidateRange: parsed.case.candidateRange,
events: toEngineEvents(scorableEvidence(dossier.evidence)),
askedProbeKeys: askedDiscriminatorKeys(
dossier.latestResult?.decisionReceipt,
parsed.evidence,
),
});
const projection = {
engine_result_id: diagnostics.engineResultId,