fix(rectification): delivery gate turn skips text the last reply already said (BUG-1153)
Independent Staging Quality Gate / publish (push) Canceled after 0s
Independent Staging Quality Gate / validate (push) Canceled after 8m58s

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 22:34:35 +08:00
co-authored by Claude Opus 5.5
parent d58df1b48b
commit 279650a684
6 changed files with 75 additions and 3 deletions
@@ -209,6 +209,11 @@ export async function persistExhaustionGateTurn(input: {
if (input.resultId) {
markDeliveryTurn({ caseId: input.caseId, resultId: input.resultId });
}
// BUG-1153: the answered choice's own reply often already carries this delivery text
// (「已记录,…」 + the same sentences). Once BUG-1149 let the gate turn persist, the same
// narration showed twice. The gate only fills a gap; it never repeats the last reply.
const dossier = await loadV9CaseDossier(input.accounting, input.userId, input.caseId);
if (narrationAlreadyInLastAssistant(input.hostNarration, dossier)) return;
await persistV9DeterministicTurn(input.accounting, input.userId, input.caseId, {
requestId: exhaustionGateRequestId(input.askedTurnId, input.caseId),
userMessage: null,
@@ -1288,6 +1293,24 @@ export async function persistNextInterviewAfterChoice(input: {
};
}
export function narrationAlreadyInLastAssistant(
narration: string,
dossier: { turns?: readonly { role?: string; text?: string | null }[] },
): boolean {
const squash = (value: string) => value.replace(/\s+/g, "");
const needle = squash(narration);
if (needle.length < 8) return false;
const turns = dossier.turns ?? [];
for (let index = turns.length - 1; index >= 0; index -= 1) {
const turn = turns[index];
if (turn?.role !== "assistant") continue;
const text = typeof turn.text === "string" ? squash(turn.text) : "";
if (!text) continue;
return text.includes(needle);
}
return false;
}
function stemAlreadyInLastAssistant(
stem: string,
dossier: { turns?: readonly { role?: string; text?: string | null }[] },