refactor(rectification): split runV9AgentTurn into prepare / attempt / retry / finish

Move-only (TASK-rectification-code-split-20260926 T3/T4). No behavior,
receipt, billing, Skill or scoring change.

- agent-run.ts: 1400 -> 135 lines; runV9AgentTurn 1046 -> 25 lines, no
  nested functions. It keeps the public types, the budget constants and the
  phase order: agent-run-prepare.ts (reliability, session, year re-ask, Skill
  identity, delivered guard, reserve, turn row / replay),
  agent-run-retry.ts (attempt loop), agent-run-attempt.ts (the old nested
  streamAttempt), agent-run-finish.ts (billing settle, receipts, interview,
  finalize), agent-run-support.ts (outcome types, retry classification, turn
  receipt writers), agent-run-messages.ts (buildAgentMessages /
  buildOpeningBrief, re-exported).
- One token changed with the move: the attempt passes its own
  `previousErrorCode` argument to buildAgentMessages instead of reading the
  enclosing `lastAttemptError`; the loop passes that same value (clears the
  old unused-parameter warning).
- Tests: whole-source contracts read tests/rectification-agent-run-surface.ts;
  behavioral runV9AgentTurn tests unchanged. agent-run growth caps added.

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-09-26 19:52:51 +08:00
co-authored by Claude Opus 5.5
parent eb773f1299
commit a00c40d4b8
25 changed files with 1665 additions and 1332 deletions
@@ -30,6 +30,7 @@ import { attachQuestionsToTurns } from "../src/lib/rectification-agentic/v9/turn
import { CASE_ID, FOCUS_ID, TURN_ID } from "./rectification-v9-test-support.ts";
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { rectificationAgentRouteSurface } from "./rectification-agent-route-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
/** Task 0 frozen “before” snapshot: accident case f83d9b42, origin/staging @ 8617eb56. */
export const ACCIDENT_CASE_BEFORE_COPY = {
@@ -253,7 +254,8 @@ test("settled assistant body with a focus has no question-mark sentences", () =>
assert.doesNotMatch(withRescoreSkippedNotice("记下了:2016 年 3 月入学。范围在收窄。"), /收窄/);
assert.doesNotMatch(RECTIFICATION_USER_COPY.evidenceNotRecorded, /系统|模型/);
assert.doesNotMatch(RECTIFICATION_USER_COPY.collectHandoff, /请回答下面的问题/);
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
assert.match(agentRun, /stripQuestionSentences/);
assert.match(agentRun, /withRangeChangedAfterEvidence/);
});
@@ -0,0 +1,29 @@
import { readFileSync } from "node:fs";
/**
* The V10 rectification turn runner as source text: agent-run.ts (public
* types and the order of the phases) plus the phase modules it was split into
* (TASK-rectification-code-split-20260926), in the order the code ran inside
* the old runV9AgentTurn: preparation, the retry loop, the finish, one
* attempt, then the shared pieces and the model messages. A whole-source
* contract that held on agent-run.ts before the split holds on this
* concatenation after it. Slices that no longer sit in one file are
* rewritten per test.
*/
export const rectificationAgentRunFiles = [
"../src/lib/rectification-agentic/v9/agent-run.ts",
"../src/lib/rectification-agentic/v9/agent-run-prepare.ts",
"../src/lib/rectification-agentic/v9/agent-run-retry.ts",
"../src/lib/rectification-agentic/v9/agent-run-finish.ts",
"../src/lib/rectification-agentic/v9/agent-run-attempt.ts",
"../src/lib/rectification-agentic/v9/agent-run-support.ts",
"../src/lib/rectification-agentic/v9/agent-run-messages.ts",
] as const;
export function readRectificationAgentRunFile(relativePath: (typeof rectificationAgentRunFiles)[number]): string {
return readFileSync(new URL(relativePath, import.meta.url), "utf8");
}
export const rectificationAgentRunSurface = rectificationAgentRunFiles
.map((relativePath) => readRectificationAgentRunFile(relativePath))
.join("\n");
@@ -17,6 +17,7 @@ import { deriveRectificationChatView } from "../src/lib/rectification-chat-view.
import { completedReceiptFromPersisted } from "../src/lib/rectification-chat-messages.ts";
import { RectificationQuestionGapNotices } from "../src/components/rectification-question-gap-notices.tsx";
import { rectificationAgentRouteSurface } from "./rectification-agent-route-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const component = readFileSync(
new URL("../src/components/conversational-birth-time-rectification.tsx", import.meta.url),
@@ -288,10 +289,8 @@ test("candidate acceptance is non-billable, mutually exclusive, and continues th
});
test("usage completes or releases without hiding settlement failures", () => {
const run = readFileSync(
new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url),
"utf8",
);
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const run = rectificationAgentRunSurface;
assert.match(run, /billing\.complete\(/);
assert.match(run, /billing\.release\(/);
assert.match(run, /usage_settlement_failed/);
@@ -55,6 +55,7 @@ import {
} from "./rectification-v9-test-support.ts";
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { rectificationAgentRouteSurface, readRectificationAgentRouteFile } from "./rectification-agent-route-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const ACTION_ID = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
const QUESTION_ID = "question-1";
@@ -1508,7 +1509,8 @@ test("the public agent route treats structured choice as a non-model command", (
});
test("rectification attempt timeout stays under the agent route budget", () => {
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
const budget = readFileSync(new URL("../src/lib/rectification-run-budget.ts", import.meta.url), "utf8");
const regenerate = readFileSync(
new URL("../src/app/api/rectification/cases/[caseId]/turns/[turnId]/regenerate/route.ts", import.meta.url),
@@ -6,6 +6,7 @@ import { composeCollectSpokenAssistantText, detachCollectSpokenAssistantText, st
import { attachQuestionsToTurns } from "../src/lib/rectification-agentic/v9/turn-question.ts";
import { GENERIC_COLLECT_QUESTION, RECTIFICATION_USER_COPY, USER_COLLECT_QUESTION } from "../src/lib/rectification-agentic/user-copy.ts";
import { CASE_ID, FOCUS_ID, TURN_ID } from "./rectification-v9-test-support.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
test("composeCollectSpokenAssistantText joins by exact prompt identity", () => {
const greeting = "你好,我是生时校正助手。";
@@ -89,7 +90,8 @@ test("composeCollectSpokenAssistantText drops a near-duplicate restatement of th
});
test("runtime no longer composes the stem into assistant_message", () => {
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
const attach = readFileSync(new URL("../src/lib/rectification-agentic/v9/turn-question.ts", import.meta.url), "utf8");
assert.doesNotMatch(agentRun, /composeCollectSpokenAssistantText/);
assert.match(attach, /stripQuestionSentences/);
@@ -17,6 +17,7 @@ import {
import { RectificationVerificationReport } from "../src/components/rectification-verification-report.tsx";
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { rectificationAgentRouteSurface } from "./rectification-agent-route-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
test("delivery body is three sentences and omits the eight-method report", () => {
const text = deliveryTurnNarration({
@@ -74,7 +75,8 @@ test("already_delivered blocks a second no-message run for the same result withi
});
test("agent-run returns already_delivered before billing and the route skips a second turn", () => {
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
// 原值: 读 app/api/rectification/agent/route.ts 单文件。新值: rectificationAgentRouteSurface(route.ts + 拆出的 agent-route-*.ts,见 rectification-agent-route-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const route = rectificationAgentRouteSurface;
const exit = readFileSync(new URL("../src/lib/rectification-agentic/v9/turn-exit.ts", import.meta.url), "utf8");
@@ -94,7 +96,8 @@ test("agent-run returns already_delivered before billing and the route skips a s
});
test("agent-run trims evidence after persistNextInterviewIfIdle so delivery can keep three sentences", () => {
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
const persistIdx = agentRun.lastIndexOf("persistNextInterviewIfIdle({");
const trimIdx = agentRun.lastIndexOf("trimSpokenTurnForInterview(");
const replaceIdx = agentRun.lastIndexOf('type: "answer.delta", text: spokenAnswer, replace: true');
@@ -122,3 +122,41 @@ test("rectification agent POST stays an assembly of branch handlers", () => {
assert.match(post, new RegExp(`\\b${handler}\\(`), `POST must delegate to ${handler}`);
}
});
// ---------------------------------------------------------------------------
// T3 · lib/rectification-agentic/v9/agent-run.ts
// Measured 2026-09-26 after the runner split. Before (origin/staging 12cbe6f8):
// file 1400 lines, runV9AgentTurn 1046 lines with four nested functions
// (streamAttempt, failedAttempt, persistCommittedPhase, finalizeTurn). After:
// file 135, runV9AgentTurn 25 lines, no nested functions; the phases live in
// agent-run-{prepare,retry,attempt,finish,support,messages}.ts.
const RUN_PATH = "../src/lib/rectification-agentic/v9/agent-run.ts";
const RUN_LINE_BASELINE = 135;
const RUN_LINE_CAP = RUN_LINE_BASELINE + 150;
/** D2 of the task: runV9AgentTurn stays at or under 300 lines. */
const RUN_FUNCTION_LINE_CAP = 300;
const RUN_FUNCTION_MARKER = "export async function runV9AgentTurn(";
function functionBody(source: string, marker: string): string {
const start = source.indexOf(marker);
assert.notEqual(start, -1, `missing marker: ${marker}`);
const end = source.indexOf("\n}\n", start);
assert.notEqual(end, -1, `unterminated function: ${marker}`);
return source.slice(start, end + 2);
}
test("agent-run.ts line count stays within the coarse guardrail", () => {
const n = lineCount(read(RUN_PATH));
assert.ok(n <= RUN_LINE_CAP, `agent-run.ts has ${n} lines; cap is ${RUN_LINE_CAP} (${RUN_LINE_BASELINE} baseline + 150).`);
});
test("runV9AgentTurn stays the order of its phases", () => {
const body = functionBody(read(RUN_PATH), RUN_FUNCTION_MARKER);
const n = lineCount(body);
assert.ok(n <= RUN_FUNCTION_LINE_CAP, `runV9AgentTurn has ${n} lines; cap is ${RUN_FUNCTION_LINE_CAP}. Phase logic belongs in agent-run-*.ts.`);
assert.doesNotMatch(body, /^\s+(?:async )?function\s/m, "runV9AgentTurn must not grow nested functions again.");
const prepare = body.indexOf("prepareV9AgentTurn(");
const attempts = body.indexOf("runV9AttemptsWithRetry(");
const finish = body.indexOf("finishV9AgentTurn(");
assert.ok(prepare >= 0 && attempts > prepare && finish > attempts, "prepare → attempts → finish");
});
@@ -60,6 +60,7 @@ import { finalizeSuccessfulTurnExit } from "../src/lib/rectification-agentic/v9/
import { RECTIFICATION_TURN_PROGRESS_LABELS } from "../src/lib/rectification-activity-labels.ts";
import { rectificationInitialLiveLabel } from "../src/lib/rectification-surface-state.ts";
import { rectificationAgentRouteSurface } from "./rectification-agent-route-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const dummyModel = { id: "test-model" } as ResolvedLanguageModel;
const PROGRESS_LINES = Object.values(RECTIFICATION_TURN_PROGRESS_LABELS);
@@ -527,7 +528,8 @@ test("D5: the exit gate skips its idle re-read only when told the run settled th
});
test("D5 contract: the Agent run reports interviewSettled only from the focus-active exit; the route forwards it", () => {
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
// 原值: 读 app/api/rectification/agent/route.ts 单文件。新值: rectificationAgentRouteSurface(route.ts + 拆出的 agent-route-*.ts,见 rectification-agent-route-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const route = rectificationAgentRouteSurface;
assert.match(agentRun, /interviewSettled = interviewIdle\.focusActive === true;/);
@@ -1,5 +1,4 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { OPEN_ENGINE_CAPABILITY_CEILING } from "./rectification-v9-test-support.ts";
@@ -34,6 +33,7 @@ import {
} from "./rectification-v9-test-support.ts";
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { rectificationAgentRouteSurface, rectificationTypedMessageFastPath } from "./rectification-agent-route-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const STYLE_OPTIONS = [
{ label: "明确发生且时间吻合", answer_class: "yes" as const },
@@ -828,7 +828,8 @@ test("idle persist on the covered-domain accident delivers adopt and writes no f
});
test("agent-run still writes the exhaustion gate when idle persist returns terminalNote", () => {
const agent = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agent = rectificationAgentRunSurface;
// 原值: 读 rectification-agentic-chat.tsx 单文件。新值: rectificationChatSurface(容器 + 拆出的 hook / 参数函数 / 块,见 rectification-chat-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改为调用函数。
const chat = rectificationChatSurface;
assert.match(agent, /interviewIdle\.terminalNote && interviewIdle\.hostNarration/);
@@ -31,6 +31,7 @@ import {
receiptHandlers,
} from "./rectification-v9-test-support.ts";
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
type ExecutableTool<T = unknown> = {
execute(input: unknown): Promise<T>;
@@ -325,7 +326,8 @@ test("orphan collect:other focus is skipped so idle persist can deliver", async
});
test("agent-run writes the exhaustion gate when idle persist returns terminalNote", () => {
const agent = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agent = rectificationAgentRunSurface;
// 原值: 读 rectification-agentic-chat.tsx 单文件。新值: rectificationChatSurface(容器 + 拆出的 hook / 参数函数 / 块,见 rectification-chat-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改为调用函数。
const chat = rectificationChatSurface;
assert.match(agent, /interviewIdle\.terminalNote && interviewIdle\.hostNarration/);
@@ -1,5 +1,4 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import {
@@ -23,6 +22,7 @@ import {
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { deriveRectificationChatView } from "../src/lib/rectification-chat-view.ts";
import { chatViewInput, offerableCandidateResult, settledAssistant } from "./rectification-chat-run-fixtures.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const S1_TIMES = [
"04:50", "04:51", "04:52", "04:53", "04:54", "04:55", "04:56", "04:57", "05:13",
@@ -286,10 +286,8 @@ test("BUG-588 evidence narration names a widened range after compare", () => {
);
assert.equal(rangeChangedAfterEvidence(["04:50", "04:57"], ["04:50", "04:57"]), null);
const agentRun = readFileSync(
new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url),
"utf8",
);
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
const askedFocusAt = agentRun.indexOf("const askedFocus = latestDossier.conversationSummary.activeFocus");
const settlement = askedFocusAt >= 0 ? agentRun.slice(askedFocusAt) : "";
const stripAt = settlement.indexOf("stripQuestionSentences");
@@ -23,6 +23,7 @@ import {
type MethodFollowupEvidence,
} from "../src/lib/rectification-agentic/v9/method-followup.ts";
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const SEVEN_WITHOUT_HEALTH: MethodFollowupEvidence[] = [
{ id: "e-edu", status: "confirmed", domain: "education", datePrecision: "month", occurredFrom: "2016-09-01", occurredTo: null, eventKind: "education_start" },
@@ -149,7 +150,8 @@ test("unavailable gap button posts repair-exit and says 接着问", () => {
new URL("../src/app/api/rectification/cases/[caseId]/repair-exit/route.ts", import.meta.url),
"utf8",
);
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
assert.equal(RECTIFICATION_QUESTION_RELOAD_LABEL, "接着问");
assert.match(surface, /RECTIFICATION_QUESTION_RELOAD_LABEL = "接着问"/);
assert.match(surface, /暂时接不上,请新建一次校正/);
@@ -1,14 +1,12 @@
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
// 原值: 读 rectification-agentic-chat.tsx 单文件。新值: rectificationChatSurface(容器 + 拆出的 hook / 参数函数 / 块,见 rectification-chat-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改为调用函数。
const chat = rectificationChatSurface;
const agentRun = readFileSync(
new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url),
"utf8",
);
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
test("live and persisted assistant text stay verbatim after spoken-answer removal", () => {
assert.match(chat, /const raw = failed \? "" : turn\.text \?\? ""/);
@@ -39,6 +39,7 @@ import type { DiscriminatingEventProbe } from "../src/lib/rectification-agentic/
import { collectFocusCloseStatus } from "../src/lib/rectification-agentic/v9/turn-intent-classifier.ts";
import { rectificationChatSurface, readRectificationChatFile } from "./rectification-chat-surface.ts";
import { rectificationAgentRouteSurface, rectificationTypedMessageFastPath } from "./rectification-agent-route-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const RELATIONSHIP_PROMPT = USER_COLLECT_QUESTION.relationship;
@@ -162,7 +163,8 @@ test("collect_spoken stem lives on turn.question inside the same assistant artic
const chat = rectificationChatSurface;
const row = readFileSync(new URL("../src/components/chat-message-row.tsx", import.meta.url), "utf8");
const styles = readFileSync(new URL("../src/app/globals.css", import.meta.url), "utf8");
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
assert.match(chat, /currentQuestion\?\.kind === "collect_spoken"/);
assert.match(chat, /const collectSpokenPrompt =/);
assert.match(chat, /parseTurnQuestion\(turn\.question\)/);
@@ -482,7 +484,8 @@ test("agent route keeps question ownership in the server Case projection", () =>
);
assert.match(successExit, /await finalizeSuccessfulTurnExit/);
assert.doesNotMatch(successExit, /send\(\{\s*type:\s*"error"/);
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
assert.doesNotMatch(agentRun, /collectSpokenPromptForNewFocus/);
assert.doesNotMatch(agentRun, /composeCollectSpokenAssistantText/);
assert.match(agentRun, /askedTurnId: turnId/);
@@ -578,7 +581,8 @@ test("after one or two dated events the next collect is an invite, not a domain
assert.match(tools, /recordV10EvidenceBatch/);
assert.match(tools, /result\.acceptedCount > 0[\s\S]*autoRescoreAfterEvidenceChange/);
const agent = readFileSync(new URL("../src/mastra/agentic-rectification.ts", import.meta.url), "utf8");
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
assert.doesNotMatch(agent, /还能想起别的吗/);
assert.doesNotMatch(agentRun, /还能想起别的吗/);
});
@@ -37,6 +37,7 @@ import {
fakeAccounting,
receiptHandlers,
} from "./rectification-v9-test-support.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const ACTION_ID = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa";
const QUESTION_ID = "question-1";
@@ -219,7 +220,8 @@ test("compare failure copy and receipt detail stay user-visible without PII", ()
});
assert.equal(detail?.safe_error_code, "engine_request_failed");
assert.match(String(detail?.engine_message), /asked_probe_keys/);
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
assert.match(agentRun, /withCompareFailedRetryNotice/);
assert.match(agentRun, /rectification-compare-candidates/);
const tools = readFileSync(new URL("../src/mastra/rectification-v9-tools.ts", import.meta.url), "utf8");
@@ -32,6 +32,7 @@ import {
} from "./rectification-v9-test-support.ts";
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { rectificationAgentRouteSurface, readRectificationAgentRouteFile } from "./rectification-agent-route-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const REJECTION = {
error: true,
@@ -164,10 +165,8 @@ test("retry bootstrap uses the unwritten-evidence constraint only for that error
test("route passes classifier expectedWrite into the agent runner", () => {
// 原值: 读 app/api/rectification/agent/route.ts 单文件。新值: rectificationAgentRouteSurface(route.ts + 拆出的 agent-route-*.ts,见 rectification-agent-route-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const route = rectificationAgentRouteSurface;
const agentRun = readFileSync(
new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url),
"utf8",
);
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
assert.match(route, /classifyTurnIntentWithRetry/);
// 原值: route 含 `expectedWrite,`(runV9AgentTurn 参数)与 `let expectedWrite: "evidence" | "none" | "unknown" = "none"`。
// 新值: 分类结果放在 turnState 上:类型 `expectedWrite: "evidence" | "none" | "unknown"`,route.ts 初值 `expectedWrite: "none"`,
@@ -31,6 +31,7 @@ import { RECTIFICATION_SKILL_NAME } from "../src/lib/rectification-agentic/v9/ca
import { messageContentHash } from "../src/lib/rectification-agentic/v9/message-origin.ts";
import { safeToolErrorCode } from "../src/lib/rectification-agentic/v9/tool-service.ts";
import { rectificationAgentRouteSurface } from "./rectification-agent-route-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
type StreamChunk = {
type: string;
@@ -1868,7 +1869,8 @@ test("whole-run budget is less than both route maxDuration values", () => {
assert.ok(RECTIFICATION_AGENT_ATTEMPT_TIMEOUT_MS <= RECTIFICATION_RUN_BUDGET_MS);
assert.ok(RECTIFICATION_MIN_RETRY_ATTEMPT_MS > 0);
const budget = readFileSync(new URL("../src/lib/rectification-run-budget.ts", import.meta.url), "utf8");
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
const labels = readFileSync(new URL("../src/lib/rectification-activity-labels.ts", import.meta.url), "utf8");
assert.match(budget, /export const RECTIFICATION_AGENT_ATTEMPT_TIMEOUT_MS = 210_000/);
assert.doesNotMatch(agentRun, /210_000/);
@@ -25,6 +25,7 @@ import {
fakeAccounting,
} from "./rectification-v9-test-support.ts";
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
import { rectificationAgentRunSurface } from "./rectification-agent-run-surface.ts";
const YEAR_Q = "collect:targeted:health_pressure:year";
const SERVER_PROMPT = TARGETED_YEAR_PROMPT;
@@ -135,7 +136,8 @@ test("yearless short replies are held by the host pre-step (BUG-910)", () => {
};
assert.equal(shouldHostReaskYearEntry(focus, "是"), SERVER_PROMPT);
assert.equal(shouldHostReaskYearEntry(focus, "2019年3月做过手术"), null);
const agentRun = readFileSync(new URL("../src/lib/rectification-agentic/v9/agent-run.ts", import.meta.url), "utf8");
// 原值: 读 lib/rectification-agentic/v9/agent-run.ts 单文件。新值: rectificationAgentRunSurface(agent-run.ts + 拆出的 agent-run-*.ts,见 rectification-agent-run-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改写。
const agentRun = rectificationAgentRunSurface;
const reliability = agentRun.indexOf("classifyDateReliabilityUtterance");
const yearHost = agentRun.indexOf("shouldHostReaskYearEntry");
const reserve = agentRun.indexOf("billing.reserve");