feat: 配置 config 模块(任务书 04)
- src/config:research.json 的字段定义(TypeBox,全部必填、不认识的字段报错,每个字段带中文说明和类别)、一次列出全部错误的校验、步骤清单纯函数。 - 流程写成完整流程 / 朴素循环两选一,第零轮、消融写成开 / 关两选一,冲突组合写不出来,不另写检查。 - 规则名取自 rules 登记表,config 改为依赖 rules(结构.md 第二部分、.dependency-cruiser.cjs)。 - ledger 顺手两处(结构.md 第四部分):消融结算按轮次唯一、去掉消融编号;私人笔记去掉版本号;表结构版本号升到 2。 - 结构.md 第八部分补 config 对外内容、更新 ledger 一节;WORK_LOG 更新。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
|
||||
import { join } from "node:path";
|
||||
import { DatabaseSync } from "node:sqlite";
|
||||
import { test } from "node:test";
|
||||
import { Ledger } from "../src/ledger/index.ts";
|
||||
import { Ledger, type LedgerRecord } from "../src/ledger/index.ts";
|
||||
import { hash } from "../src/vocab/index.ts";
|
||||
import {
|
||||
callStart,
|
||||
@@ -116,12 +116,33 @@ test("唯一约束:同一次模型调用结束两次报错", (t) => {
|
||||
assert.equal(countRows(path), 2);
|
||||
});
|
||||
|
||||
test("唯一约束:同一次消融结算两次报错", (t) => {
|
||||
test("唯一约束:同一轮结算两次报错,不同轮各一次照写;结算不带消融编号", (t) => {
|
||||
// 每轮最多采纳一个改法,所以最多一次消融(docs/结构.md 第四部分):结算按轮次只发生一次。
|
||||
const { ledger, path } = freshLedger(t);
|
||||
ledger.append("settlement", { ablation: "a1", result: { cells: { p1: "valid" } } });
|
||||
assert.throws(() => ledger.append("settlement", { ablation: "a1", result: { cells: { p1: "valid" } } }), /settlement_once/);
|
||||
ledger.append("settlement", { ablation: "a2", result: { cells: { p1: "insufficient" } } });
|
||||
assert.equal(countRows(path), 2);
|
||||
ledger.append("settlement", { round: 1, result: { cells: { p1: "valid" } } });
|
||||
assert.throws(() => ledger.append("settlement", { round: 1, result: { cells: { p1: "insufficient" } } }), /settlement_once/);
|
||||
ledger.append("settlement", { round: 2, result: { cells: { p1: "insufficient" } } });
|
||||
ledger.append("settlement", { round: 3, result: { cells: { p1: "valid" } } });
|
||||
assert.throws(() => ledger.append("settlement", { ablation: "a1", result: {} } as never), /settlement/);
|
||||
assert.throws(() => ledger.append("settlement", { round: 4, ablation: "a4", result: {} } as never), /settlement/);
|
||||
assert.equal(countRows(path), 3);
|
||||
});
|
||||
|
||||
test("私人笔记:同一人写两条都存下,以后写的(序号大的)为准;笔记不带版本号", (t) => {
|
||||
// 笔记不属于任何阶段,以最后写入的一条为准,序号就是先后(docs/结构.md 第四部分)。
|
||||
// 后写的正文按字典序排在前面,中间夹着别人的笔记:取"序号最大的那条"才得到后写的。
|
||||
const { ledger, path } = freshLedger(t);
|
||||
ledger.append("note", { author: "r1", body: "z 先写的" });
|
||||
ledger.append("note", { author: "r2", body: "别人的笔记" });
|
||||
ledger.append("note", { author: "r1", body: "a 后写的" });
|
||||
const mine = ledger
|
||||
.records()
|
||||
.filter((r): r is LedgerRecord<"note"> => r.kind === "note")
|
||||
.filter((r) => r.content.author === "r1");
|
||||
assert.deepEqual(mine.map((r) => [r.seq, r.content.body]), [[1, "z 先写的"], [3, "a 后写的"]]);
|
||||
assert.equal(mine.at(-1)!.content.body, "a 后写的");
|
||||
assert.throws(() => ledger.append("note", { author: "r1", version: 2, body: "带版本号" } as never), /note/);
|
||||
assert.equal(countRows(path), 3);
|
||||
});
|
||||
|
||||
test("没有唯一约束的记录照写:同样的事件、决定写两次就是两条", (t) => {
|
||||
@@ -233,7 +254,7 @@ test("关掉再打开:读出的记录不变,序号接着往下排", (t) => {
|
||||
ledger.append("config", { researchers: ["r1", "r2"] });
|
||||
openStage(ledger, "s1", "proposal", 1);
|
||||
ledger.append("submission", { stageId: "s1", author: "r1", target: null, item: "r1-1", version: 1, kind: "proposal", content: proposal(range(1, 2, 3)) });
|
||||
ledger.append("note", { author: "r2", version: 1, body: "笔记" });
|
||||
ledger.append("note", { author: "r2", body: "笔记" });
|
||||
closeStage(ledger, "s1");
|
||||
planRun(ledger, "run1", "forward", 1);
|
||||
ledger.append("callStarted", callStart("k1"));
|
||||
|
||||
Reference in New Issue
Block a user