fix(rectification): stop stamping pending receipts and document aggregate blocker

Implement BUG-984 F2 option A and reproduce mixed completed identities through real tools. Stop at the required SQL authorization boundary; F1/F3/F4 remain pending.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
jesse-ux
2026-09-20 15:55:09 +08:00
co-authored by Claude Code
parent f09f3d809a
commit d575e89a83
9 changed files with 186 additions and 21 deletions
@@ -109,7 +109,11 @@ test("unreachable versions retain the existing unknown-identity cache fallback,
assert.equal(cachedEngineScoreIsReusable({ ...fingerprints, algorithmVersion: PREVIOUS }, fingerprints, live), true);
});
test("new Case scoring writes the new version on both started and completed receipts", async (t) => {
async function runGoldenToolSequence(t: test.TestContext, options: {
compareVersion?: string;
diagnosticsVersion?: string;
diagnosticsFailure?: boolean;
} = {}) {
isolateIdentityEnv(t);
const request = golden.request;
const candidateRange = { start_time: request.start_time, end_time: request.end_time };
@@ -123,7 +127,13 @@ test("new Case scoring writes the new version on both started and completed rece
const path = new URL(String(url)).pathname;
calls.push(path);
if (path.endsWith("/versions")) return Response.json(golden.versions);
if (path.endsWith("/score") || path.endsWith("/diagnostics")) return Response.json(golden.score);
if (path.endsWith("/score")) {
return Response.json({ ...golden.score, algorithm_version: options.compareVersion ?? CURRENT });
}
if (path.endsWith("/diagnostics")) {
if (options.diagnosticsFailure) return Response.json({ error: "fixture_unavailable" }, { status: 503 });
return Response.json({ ...golden.score, algorithm_version: options.diagnosticsVersion ?? CURRENT });
}
throw new Error(`unexpected engine request ${path}`);
});
const accounting = fakeAccounting({
@@ -152,15 +162,44 @@ test("new Case scoring writes the new version on both started and completed rece
});
const tools = createRectificationV9Tools({ userId: USER_ID, caseId: CASE_ID, turnId: TURN_ID, accounting: accounting.client as never });
for (const name of ["rectification-compare-candidates", "rectification-read-diagnostics"] as const) {
await (tools[name] as unknown as { execute(input: unknown): Promise<unknown> }).execute({ caseId: CASE_ID });
const receipts = accounting.calls.filter((call) => call.fn === "insert_agentic_rectification_tool_receipt" && call.args.p_tool_name === name);
assert.deepEqual(receipts.map((call) => [call.args.p_status, call.args.p_engine_version]), [
["started", CURRENT], ["completed", CURRENT],
]);
const execute = () => (tools[name] as unknown as { execute(input: unknown): Promise<unknown> }).execute({ caseId: CASE_ID });
if (options.diagnosticsFailure && name === "rectification-read-diagnostics") await assert.rejects(execute);
else await execute();
}
assert.ok(calls.includes("/api/rectification/v5/score"));
const persisted = accounting.calls.find((call) => call.fn === "persist_agentic_rectification_candidate_v2");
assert.equal(persisted?.args.p_algorithm_version, CURRENT);
assert.equal(persisted?.args.p_algorithm_version, options.compareVersion ?? CURRENT);
return accounting.calls.filter((call) => call.fn === "insert_agentic_rectification_tool_receipt");
}
test("new Case scoring writes actual identity only on completed receipts", async (t) => {
const receipts = await runGoldenToolSequence(t);
assert.deepEqual(receipts.map((call) => [call.args.p_status, call.args.p_engine_version]), [
// 原值: started=CURRENT;新值: null;原因: BUG-984,开始不是成功结果身份。
["started", null], ["completed", CURRENT], ["started", null], ["completed", CURRENT],
]);
});
test("failed diagnostics do not claim an engine result identity", async (t) => {
const receipts = await runGoldenToolSequence(t, { diagnosticsFailure: true });
assert.deepEqual(receipts.map((call) => [call.args.p_status, call.args.p_engine_version]), [
["started", null], ["completed", CURRENT], ["started", null], ["failed", null],
]);
});
test("BUG-984 blocker: real tool sequence permits mixed completed identities in one turn", async (t) => {
// Identity-only mutation models rolling backend versions; all response shape and values
// remain the real native golden. This is not a claim to have run future algorithms.
const version9 = "rectification-v5-matrix-scoring-9";
const version10 = "rectification-v5-matrix-scoring-10";
const receipts = await runGoldenToolSequence(t, { compareVersion: version9, diagnosticsVersion: version10 });
const completed = receipts.filter((call) => call.args.p_status === "completed");
assert.equal(new Set(completed.map((call) => call.args.p_turn_id)).size, 1);
assert.deepEqual(completed.map((call) => call.args.p_engine_version), [version9, version10]);
// Preserve the blocker as positive evidence, not a false passing acceptance test.
// F2 acceptance must replace this diagnostic once the approved aggregate fix lands.
assert.equal([version9, version10].sort().at(-1), version9);
assert.notEqual([version9, version10].sort().at(-1), completed.at(-1)?.args.p_engine_version);
});
test("history opens with its bound Skill and reads old engine receipt values unchanged", async (t) => {