fix(rectification): keep choice options and scoring on the server
Independent Staging Quality Gate / validate (push) Successful in 9m30s
Independent Staging Quality Gate / publish (push) Successful in 1m50s

Agent set-focus was writing or dropping choice schema, so probes never scored. Force server-owned options, fail closed when focus list RPC errors, and require the probe year in spokenPrompt.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-02 20:47:16 +08:00
co-authored by Cursor
parent 679d773b28
commit 8dfe457f21
17 changed files with 942 additions and 117 deletions
@@ -10,7 +10,8 @@ import {
receiptStatusFromTurn,
RectificationToolServiceError,
listV10ConversationFocuses,
type ConversationFocus,
questionSourceFromFocusList,
type ConversationFocusList,
type V9CaseDossier,
} from "@/lib/rectification-agentic/v9/tool-service";
import { choiceCardFromCaseDossier, decideFromDossier, overlayPublicDecision } from "@/lib/rectification-agentic/v9/interview-state";
@@ -72,8 +73,8 @@ export async function GET(request: Request, context: RouteContext) {
}
}),
);
const focuses = await listV10ConversationFocuses(accounting, user.id, caseId);
return NextResponse.json(dossierResponse(dossier, receipts, skillIdentity, { fullDetail, focuses }));
const listed = await listV10ConversationFocuses(accounting, user.id, caseId);
return NextResponse.json(dossierResponse(dossier, receipts, skillIdentity, { fullDetail, listed }));
} catch (error) {
if (error instanceof RectificationToolServiceError) {
const message = error.message;
@@ -88,16 +89,17 @@ export async function GET(request: Request, context: RouteContext) {
}
}
function dossierResponse(
export function dossierResponse(
dossier: V9CaseDossier,
receipts: Array<Awaited<ReturnType<typeof loadV9TurnReceipt>>>,
skillIdentity: Awaited<ReturnType<typeof loadV9CaseSkillIdentityStatus>>,
options: { fullDetail?: boolean; focuses?: readonly ConversationFocus[] } = {},
options: { fullDetail?: boolean; listed?: ConversationFocusList } = {},
) {
const decision = decideFromDossier(dossier, {
currentEvidenceFingerprint: evidenceLedgerFingerprint(dossier.evidence),
});
const turns = attachQuestionsToTurns(dossier.turns, options.focuses ?? []);
const listed = options.listed ?? { focuses: [] as const, available: true };
const turns = attachQuestionsToTurns(dossier.turns, listed.focuses);
return {
case: {
case_id: dossier.case.caseId,
@@ -131,6 +133,7 @@ function dossierResponse(
interview: publicDecisionFields(decision),
current_question: projectCurrentQuestion(dossier.conversationSummary.activeFocus),
choice_card: choiceCardFromCaseDossier(dossier),
question_source: questionSourceFromFocusList(listed),
};
}