fix(rectification): persist the next interview after a choice tap
Independent Staging Quality Gate / validate (push) Successful in 13m56s
Independent Staging Quality Gate / publish (push) Successful in 16m46s

Closing a discriminator used to leave GET without a card after refresh.
Write the next dated question in the same request, skip childhood career
and move probes, and do not continue a read-only turn when that question
is already persisted.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-08-28 16:30:09 +08:00
parent 8c75ebb196
commit 68f0759eef
26 changed files with 2455 additions and 311 deletions
@@ -2,6 +2,7 @@ import { NextResponse } from "next/server";
import { z } from "zod";
import { getRectificationV9Agent, type RectificationAgentAction } from "@/mastra/agentic-rectification";
import {
loadV9CaseCompute,
loadV9CaseDossier,
persistV9DeterministicTurn,
RectificationToolServiceError,
@@ -280,6 +281,8 @@ export async function POST(request: Request) {
sourceQuote: applied.sourceQuote,
derivedContext: applied.derivedContext,
nextAction: applied.nextAction,
nextInterviewPersisted: applied.nextInterviewPersisted,
nextChoiceReady: applied.nextChoiceReady,
});
} catch (error) {
if (error instanceof RectificationToolServiceError) {
@@ -386,11 +389,19 @@ export async function POST(request: Request) {
dossier.latestResult,
dossier.evidence,
);
let birthDate: string | null = null;
try {
const compute = await loadV9CaseCompute(accounting, userId, caseId);
birthDate = String(compute.baselineBirthSnapshot.birth_date ?? "") || null;
} catch {
// Ranking stays fail-open when compute is unavailable.
}
const plan = buildMethodFollowupPlan({
evidence: dossier.evidence,
declinedTopics: dossier.conversationSummary.declinedSkippedTopics,
sessionOutcome: "discriminate_candidates",
...catalog,
birthDate,
candidatesSeparated: false,
});
const persisted = await persistServerOwnedFocus({