feat: 任务书 08,jev 模块与账本记 Jev 判断

src/jev 3 个文件 515 行:判重、查冻结条款、查代码改动三个函数,九问问法和阈值照参考项目,只改说法和字段名,阈值注明未在新项目材料上校准,等真实数据。
新增 npm run test:jev(真实 Jev 三处各问一次)。vocab 加 JevJudgment。
ledger 表结构版本 3:callEnded 四种结局对上 agents,墙钟时间用 Measured(恢复时补写出错、原因"进程中断"、测不到);退回、提交记录都带 jev。
config 加必填的 models.jev。

用户原话:"这次就给提交记录加一个和退回记录同样的 Jev 判断字段";"墙钟时间改用 Measured,恢复时写出错加测不到(原因'进程中断')"。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 02:10:55 -04:00
parent 68e49fd01a
commit 0ac1e01613
18 changed files with 1228 additions and 57 deletions
+19 -19
View File
@@ -24,7 +24,7 @@ import {
// 4. 这一轮消融已经有格确认启动了,才来关这一轮的解释阶段 → 拒收(方法.md 4.3 ⑭)。
function submitWrapUp(ledger: Ledger, stageId: string, author: string, version: number, body = "收尾") {
return ledger.append("submission", { stageId, author, target: null, item: null, version, kind: "wrapUp", content: wrapUp(body) });
return ledger.append("submission", { stageId, author, target: null, item: null, version, jev: null, kind: "wrapUp", content: wrapUp(body) });
}
test("拒收 1:只收开着的阶段的提交:没开过的不收,关了以后不收、替换也不收;开着时收", (t) => {
@@ -42,9 +42,9 @@ test("拒收 2 的正常情况:替换本人在这个阶段、对同一个对
const { ledger } = freshLedger(t);
openStage(ledger, "s1", "firstPrediction", 1);
const slot = { stageId: "s1", author: "r1", target: "c1", item: null, kind: "firstPrediction" as const };
ledger.append("submission", { ...slot, version: 1, content: prediction(range(1, 2, 3)) });
ledger.append("submission", { ...slot, version: 2, content: prediction(range(1, 2, 4)) });
ledger.append("submission", { ...slot, version: 3, content: prediction(range(1, 2, 5)) });
ledger.append("submission", { ...slot, version: 1, jev: null, content: prediction(range(1, 2, 3)) });
ledger.append("submission", { ...slot, version: 2, jev: null, content: prediction(range(1, 2, 4)) });
ledger.append("submission", { ...slot, version: 3, jev: null, content: prediction(range(1, 2, 5)) });
assert.equal(ledger.latestSubmission(slot)?.content.version, 3);
});
@@ -53,13 +53,13 @@ test("拒收 2:要替换的那一份不是最新一版、不是本人的、不
openStage(ledger, "s1", "firstPrediction", 1);
openStage(ledger, "s2", "firstPrediction", 1);
const slot = { stageId: "s1", author: "r1", target: "c1", item: null, kind: "firstPrediction" as const };
ledger.append("submission", { ...slot, version: 1, content: prediction(range(1, 2, 3)) });
ledger.append("submission", { ...slot, version: 2, content: prediction(range(1, 2, 4)) });
const next = { ...slot, version: 3, content: prediction(range(1, 2, 6)) };
ledger.append("submission", { ...slot, version: 1, jev: null, content: prediction(range(1, 2, 3)) });
ledger.append("submission", { ...slot, version: 2, jev: null, content: prediction(range(1, 2, 4)) });
const next = { ...slot, version: 3, jev: null, content: prediction(range(1, 2, 6)) };
// 想替换第 1 版,但最新是第 2 版。
assert.throws(() => ledger.append("submission", { ...slot, version: 2, content: prediction(range(0, 2, 4)) }), /最新是第 2 版/);
assert.throws(() => ledger.append("submission", { ...slot, version: 2, jev: null, content: prediction(range(0, 2, 4)) }), /最新是第 2 版/);
// 要替换的第 3 版不存在。
assert.throws(() => ledger.append("submission", { ...slot, version: 4, content: prediction(range(1, 2, 6)) }), /最新是第 2 版/);
assert.throws(() => ledger.append("submission", { ...slot, version: 4, jev: null, content: prediction(range(1, 2, 6)) }), /最新是第 2 版/);
// 不是本人的、不是同一个对象的、不在这个阶段的:本人在那里还没有交过。
assert.throws(() => ledger.append("submission", { ...next, author: "r2" }), /还没有交过/);
assert.throws(() => ledger.append("submission", { ...next, target: "c2" }), /还没有交过/);
@@ -71,10 +71,10 @@ test("拒收 2:同一个人对同一个候选的几份挑刺按各自的编号
const { ledger } = freshLedger(t);
openStage(ledger, "s1", "critique", 1);
const slot = { stageId: "s1", author: "r1", target: "c1", kind: "critique" as const };
ledger.append("submission", { ...slot, item: "k1", version: 1, content: critique("第一份") });
ledger.append("submission", { ...slot, item: "k2", version: 1, content: critique("第二份") });
ledger.append("submission", { ...slot, item: "k1", version: 2, content: critique("第一份改") });
assert.throws(() => ledger.append("submission", { ...slot, item: "k3", version: 2, content: critique("x") }), /还没有交过/);
ledger.append("submission", { ...slot, item: "k1", version: 1, jev: null, content: critique("第一份") });
ledger.append("submission", { ...slot, item: "k2", version: 1, jev: null, content: critique("第二份") });
ledger.append("submission", { ...slot, item: "k1", version: 2, jev: null, content: critique("第一份改") });
assert.throws(() => ledger.append("submission", { ...slot, item: "k3", version: 2, jev: null, content: critique("x") }), /还没有交过/);
});
test("拒收 2:实现、修复、核验、消融实现、消融核验只收第一版,不能替换", (t) => {
@@ -95,8 +95,8 @@ test("拒收 2:实现、修复、核验、消融实现、消融核验只收第
{ ...base, author: "v1", stageId: "aver", target: "cell1", kind: "ablationVerification" as const, content: { factors: [] } },
];
for (const c of cases) {
ledger.append("submission", { ...c, version: 1 });
assert.throws(() => ledger.append("submission", { ...c, version: 2 }), /不能替换/, c.kind);
ledger.append("submission", { ...c, version: 1, jev: null });
assert.throws(() => ledger.append("submission", { ...c, version: 2, jev: null }), /不能替换/, c.kind);
}
});
@@ -105,14 +105,14 @@ test("拒收 2:版本号只在同一种提交里接:不能替换的实现不
const { ledger } = freshLedger(t);
openStage(ledger, "impl", "implementation", 1, ["e1"]);
const implSlot = { stageId: "impl", author: "e1", target: "c1", item: null };
ledger.append("submission", { ...implSlot, version: 1, kind: "implementation", content: implementation() });
assert.throws(() => ledger.append("submission", { ...implSlot, version: 2, kind: "wrapUp", content: wrapUp() }), /还没有交过/);
ledger.append("submission", { ...implSlot, version: 1, jev: null, kind: "implementation", content: implementation() });
assert.throws(() => ledger.append("submission", { ...implSlot, version: 2, jev: null, kind: "wrapUp", content: wrapUp() }), /还没有交过/);
assert.equal(ledger.latestSubmission({ ...implSlot, kind: "implementation" })?.content.kind, "implementation");
openStage(ledger, "mix", "proposal", 1);
const mixSlot = { stageId: "mix", author: "r1", target: null, item: "x1" };
ledger.append("submission", { ...mixSlot, version: 1, kind: "proposal", content: proposal(range(1, 2, 3)) });
assert.throws(() => ledger.append("submission", { ...mixSlot, version: 2, kind: "critique", content: critique() }), /还没有交过/);
ledger.append("submission", { ...mixSlot, version: 1, jev: null, kind: "proposal", content: proposal(range(1, 2, 3)) });
assert.throws(() => ledger.append("submission", { ...mixSlot, version: 2, jev: null, kind: "critique", content: critique() }), /还没有交过/);
assert.equal(ledger.latestSubmission({ ...mixSlot, kind: "critique" }), undefined);
});