feat: 任务书 11,指标名加候选前缀、消融重做接着改、判重说明补关键设定
- 假设专用指标短名不能含"."(最长 64),冻结时由 materials 的 freezeProtocol 改为全名"候选编号.短名";可引用清单用全名,已占用清单只剩任务指标和两个保留键。 - 消融格核验不通过重做时,工作目录是上一次交的这一格代码的副本,能读的版本加上它,完整代码照旧列出;消融实现提示词同步改。 - jev 判重材料说明补 keySettings 一句。 - 结构.md 第八部分 vocab、materials、jev,WORK_LOG 同步更新。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+3
-3
@@ -21,6 +21,7 @@
|
||||
- 任务书 08(jev):src/jev 3 个文件、515 行,九问的问法和阈值照参考项目,只改说法和字段名,阈值注明未在新材料上校准;判重一对一次请求、冻结条款三问各一次、代码改动一块一次(五问同材料);`npm run test:jev` 三处各问一次真实 Jev,九问都拿回 0 到 1 的概率和请求编号。vocab 加 `JevJudgment`。ledger 表结构版本升到 3:`callEnded` 四种结局对上 agents、墙钟时间用 `Measured`(中断恢复时补写出错、原因"进程中断"、时间测不到);退回和提交记录都带 `jev`(判断数组或 null)。config 加必填的 `models.jev`。`npm test` 316 条通过。账本改动全新上下文审阅两轮。
|
||||
- 任务书 09(materials):src/materials 12 个文件、1831 行(预期 1200–1800;超出部分主要是用户决定 2 新加的 shapes.ts 116 行,复审判定没有专门路径增多),vocab 加 `Checklist`(1105 行,上限 1150)。19 份提示词以参考项目为底稿只做必要改动搬进 `prompts/`;TASK.md 补了 `unversionedNames` 不存进版本。`npm test` 356 条通过(materials 39 条)。全新上下文审阅三轮(可见性一轮、规模与正确性一轮、修复复审一轮),materials 内部没有发现泄露;要靠 flow 写入时机保证的几条记进"未完成"的"给 flow"。
|
||||
- 任务书 10(submissions):先改 materials 第一部分 7 处,另加两处小改动(jev 判重材料带关键设定、核验 `body` 字段说明补同行提醒)。src/submissions 6 个文件、391 行(预期 400–600);materials 1850 行;vocab 1107 行;ledger 退回记录加对象字段,表结构版本升到 4。`npm test` 400 条通过;`npm run test:jev` 新增三个真实用例:写得好的回应收下(三问 0.06 / 0.04 / 0.04),改评测的回应退回(changes_measurement 0.91,阈值 0.21),实现 3 块 15 条判断全否。全新上下文审阅一轮:采纳消融实现退回文字前后矛盾、两处测试加强;另发现同轮指标重名,用户定的改法见"未完成"。结构.md 第七、八部分已更新。
|
||||
- 任务书 11(收尾,提交 待填):假设专用指标冻结时加候选前缀,全名"候选编号.短名",只在 materials 的 `freezeProtocol` 加;短名恢复不能含 `.`(最长 64),已占用清单只剩任务的指标和两个保留键。消融格核验不通过重做时,材料写"工作目录是上一次交的这一格代码的副本",能读的版本加上它,完整代码照旧列出;消融实现提示词同步改。jev 判重的材料说明补 `keySettings` 一句。materials 1868 行(上限 1850,多出主要是 `freezeProtocol`),submissions 391 行,vocab 1118 行。`npm test` 405 条通过;`npm run test:jev -- jev-latest` 判重一问照常拿回概率。结构.md 第八部分 vocab、materials、jev 已更新。
|
||||
|
||||
## 关键决定(用户原话见各提交说明)
|
||||
|
||||
@@ -71,8 +72,7 @@
|
||||
- 给 flow:取出时目标目录必须是空的,workspace 不查。
|
||||
- 给 flow(materials 审阅):冻结协议只在回应阶段关闭后写进账本,阶段内给 Jev 查的协议只放内存;解封只在本轮收尾阶段关闭后写;阶段关闭只在名单上的人交齐后写。materials 把这几条记录的出现当作"已公开",提前写会泄露(审阅者在临时账本里实测过)。
|
||||
- 给 flow:没采纳时也写一条采纳决定(adopted 为 null),本轮正向运行靠它公开;修复任务要在核验阶段关闭后再发。
|
||||
- 同一轮两位研究者可能给假设专用指标起同一个编号、定义不同:两份都会收下,下一轮引用时取第一条定义。审阅时在参考项目真实账本里查到 4 次:11-real-1 第 1 轮 min-radius、15-real-1 第 1 轮 lp-polish-gain、15-tight-2 第 2 轮 final-radius-spread 与 last-improve-elapsed、16-real-1 第 2 轮 restart-gain。用户选的改法:"冻结时加候选前缀":冻结协议里指标全名为"候选编号.短名",可引用和已占用清单都用全名,恢复"短名不能含 ."。涉及 vocab、materials、submissions、tasks 读指标文件,待写进下一份任务书。
|
||||
- 给 flow:消融格核验不通过、修复次数用完时写 `cellNotRunEvent(格, 原因)` 事件,结算记 insufficient。消融重做时实验员从哪份代码出发(完整代码副本,还是上一次这一格的提交),方法没写;flow 取代码时要和 materials 的写法对齐。
|
||||
- 给 flow:消融格核验不通过、修复次数用完时写 `cellNotRunEvent(格, 原因)` 事件,结算记 insufficient。flow 重做时,把上一次这一格的提交取出来当工作目录(materials 的消融实现材料就是这样写的)。
|
||||
- 给 flow:修复的差异块按本批基线算(任务书 10 第二部分第 2 步),切好交给 submissions;形状不合格的提交由 agents 报给提交回调,flow 直接记退回,不经 submissions。
|
||||
- 给 flow:消融实现交了 `cannotRun` 时 `known.code` 传 null,否则传存版本的结果。
|
||||
- 给以后换任务镜像:镜像里必须有 `setpriv`(runner 用它把命令降到当前用户);容器里不要用 `python -I`,它会忽略 `PYTHON_CPU_COUNT`。
|
||||
@@ -88,4 +88,4 @@
|
||||
|
||||
## 下一步
|
||||
|
||||
任务书 10 已核对通过(09c4403)。用户定:export 先不做,先把已完成模块收尾,再一起过 flow。任务书 11(收尾:指标名加候选前缀、消融重做接着改、判重说明补关键设定,`任务书/11-收尾.md`)已写好,等用户另开新 Agent 执行。
|
||||
任务书 11(收尾)已执行完,等监督者核对。之后由用户和监督者一起过 flow。
|
||||
|
||||
+8
-7
@@ -230,7 +230,7 @@ Jev 是一个判别器,和程序检查地位相同,不单独成一种记录
|
||||
- `METRIC_LAYERS`、`MetricLayer`:指标的三个层次,最终、常规辅助、假设专用。
|
||||
- `MISSING_POLICIES`、`MissingPolicy`:测不到时怎么办的两档,缺了只记缺失、缺了整次作废。
|
||||
- `MISSING_POLICY_EFFECTS`:两档各自后果的白话说明,字段说明和给模型的材料共用这一份。
|
||||
- `HypothesisMetric`(字段定义):研究者在提案里登记的一条假设专用指标。
|
||||
- `HypothesisMetric`(字段定义):研究者在提案、回应、统一里登记的一条假设专用指标。编号是短名:字母、数字和 `_-`,不能含 `.`,最长 64 个字符。冻结时改为全名"候选编号.短名"(materials 的 `freezeProtocol`,用户 2026-10-03 定),冻结协议和 tasks 收的指标沿用这个类型,编号是全名;短名不带 `.`、全名都带,两者不会重名。
|
||||
- `MetricDefinition`(字段定义):一条完整的指标定义,带编号和层次。
|
||||
|
||||
**运行**
|
||||
@@ -253,7 +253,7 @@ Jev 是一个判别器,和程序检查地位相同,不单独成一种记录
|
||||
**Jev**
|
||||
|
||||
- `JevJudgment`(字段定义):Jev 对一个问题的一次判断:问题名、问题全文的哈希(措辞、示例、阈值一起算)、阈值、原始概率、结论(概率是否超过阈值)、Jev 返回的请求编号。jev 交出它,ledger 在提交和退回记录里原样存它,两个模块互不引用,所以放在这里。
|
||||
- `Checklist`、`ChecklistOf<K>`(类型):对号清单,materials 为一次任务算出、submissions 拿来判收不收;按提交种类区分,每种只放它要用的(入口和讨论的入口个数范围;认领的入口编号和至少排几个;提案的 M、因子数上限、能延续的因子、能引用的假设专用指标、已占用的指标编号;两次预测的候选;挑刺的原提案和它的因子;回应、统一的挑刺编号(统一另有被合并的原提案);实现、修复、核验的条款、因子、保障(核验另有没有归属的改动);解释的格和 M;消融实现的格、要关的因子、全部因子;消融核验的格和全部因子;收尾无)。用户 2026-10-03 决定:vocab 只放这一个,任务材料的其余类型放 materials。
|
||||
- `Checklist`、`ChecklistOf<K>`(类型):对号清单,materials 为一次任务算出、submissions 拿来判收不收;按提交种类区分,每种只放它要用的(入口和讨论的入口个数范围;认领的入口编号和至少排几个;提案的 M、因子数上限、能延续的因子、能引用的假设专用指标(全名)、已占用的指标编号(任务的指标和两个保留键);两次预测的候选;挑刺的原提案和它的因子;回应、统一的挑刺编号(统一另有被合并的原提案);实现、修复、核验的条款、因子、保障(核验另有没有归属的改动);解释的格和 M;消融实现的格、要关的因子、全部因子;消融核验的格和全部因子;收尾无)。用户 2026-10-03 决定:vocab 只放这一个,任务材料的其余类型放 materials。
|
||||
|
||||
### rules
|
||||
|
||||
@@ -506,14 +506,15 @@ Jev 是一个判别器,和程序检查地位相同,不单独成一种记录
|
||||
- `buildMaterial(req)`:生成一份任务材料:给模型看的各节、可查询的记录、拼好的第一条消息、对号清单、能读的代码版本。研究者的材料按"任务说明 → 你自己的 → 公共材料 → 现在交什么"排;实验员、核验员的按"任务说明 → 分给你的材料 → 现在交什么"排,不给信念本和成绩。同一人重新发起同一任务时,最前面放上一次被退回的原因:按阶段、作者、提交种类、对象找本人最后一条退回记录。"现在交什么"由 vocab `SUBMISSION_SCHEMAS` 的字段说明生成。几种任务的取法:
|
||||
- 第二次预测:冻结协议不带答复,"挑刺与答复"从这个候选已公开的最后一份回应或统一里读(回应另给答复正文);留池候选读它冻结那一轮的(用户 2026-10-03 定)。
|
||||
- 核验:不放 Jev 对这份实现的判断。能走到核验说明 Jev 已判过,Jev 是判别条件,不是给核验员的提示。
|
||||
- 消融实现:这一格已有公开的消融核验(核验不通过、重做)时,另放"退回意见":最近一次核验里关错了的因子和核验员写的依据,写法同修复的退回意见。
|
||||
- 提案、回应、统一的对号清单里,已占用的指标编号含任务的指标、`answer-path` 和 `safeguard-triggers` 两个保留键、已登记的假设专用指标(用户 2026-10-03 定)。
|
||||
- 消融实现:这一格已有公开的消融核验(核验不通过、重做)时,和修复一样从上一次交的这一格代码接着改(用户 2026-10-03 定):工作目录写"上一次交的这一格代码的副本",能读的版本里加上它,完整代码照旧列出作对照;另放"退回意见":最近一次核验里关错了的因子和核验员写的依据,写法同修复的退回意见。
|
||||
- 指标:冻结之后一律用全名"候选编号.短名":已登记指标清单、对号清单的 `reusableMetrics`、实验员材料的"要实现的指标定义"(候选代码在结果文件里也用全名当键)。提案、回应、统一的对号清单里,已占用的指标编号只有任务的指标、`answer-path` 和 `safeguard-triggers` 两个保留键(用户 2026-10-03 定);已登记的全名带 `.`,新登记的短名不带,不用列进来。
|
||||
- `queryMaterial(material, id)`:只在这份材料的记录里按编号找,交回全文;找不到交回 null(agents 的查询回调据此答"看不到")。
|
||||
- `readPrompts(dir)`:读 `roles/<角色>.md` 和 `tasks/<提交种类>.md`,交回文字和每个文件原始字节的 sha256;研究开始时 flow 把哈希冻结进研究配置记录(方法.md 4.1)。
|
||||
- `beliefBook(ledger, study, round)`:信念本全文(方法.md 6.10 五条),数字全部用 rules 现算,只写已公开的记录;`round` 是读者所在轮次,导出时写 null。选批历史的每次运行后面原样写出任务的附加记录(判分结果的 `extra`,合法化扣掉的分在里面,字段由任务定)。消融格记"证据不足"时,有运行的原因取判分,没有运行的(核验不通过、重做次数用完)原因取这一格的 `CELL_NOT_RUN` 事件。
|
||||
- `TaskMaterial`、`MaterialSection`、`MaterialRecord`、`ReadableVersion`(类型):任务材料,以及其中的一节、一条可查询记录(编号、标题、全文)、一个能读的代码版本(版本号、说明、取出目录)。
|
||||
- `TaskRequest`、`StudyFacts`、`CodeChanges`、`Prompts`、`PromptFile`(类型):flow 传进来的东西,全部必填;`CodeChanges` 与 workspace 的比较结果同形(照 jev `codeHunks` 的先例,不引用 workspace)。
|
||||
- 账本里只存成 Json 的内容的形状(用户 2026-10-03 决定 2:materials 定义,flow 照着写,ledger 不改):`FrozenProtocol`(冻结协议:方法.md 4.3 ⑥ 列的因子、保障、指标、正文、实现条款,加轮次、出发的基线、合并了哪些原提案;不带答复,主体由 submissions 的 `protocolBody` 拼)、`LocationTable`(代码记录的位置表)、`DecisionKind`、`DECISION_KINDS`、`DecisionShapes`(六种决定:入口分配、判重合并、选批、采纳、选格、押的因子,输入输出用 rules 的类型;没采纳时也写一条采纳决定)、`DedupOutput`(判重合并的输出)、`Settlement`(结算)。
|
||||
- 账本里只存成 Json 的内容的形状(用户 2026-10-03 决定 2:materials 定义,flow 照着写,ledger 不改):`FrozenProtocol`(冻结协议:方法.md 4.3 ⑥ 列的因子、保障、指标、正文、实现条款,加轮次、出发的基线、合并了哪些原提案;不带答复,主体由 submissions 的 `protocolBody` 拼,指标编号是全名)、`LocationTable`(代码记录的位置表)、`DecisionKind`、`DECISION_KINDS`、`DecisionShapes`(六种决定:入口分配、判重合并、选批、采纳、选格、押的因子,输入输出用 rules 的类型;没采纳时也写一条采纳决定)、`DedupOutput`(判重合并的输出)、`Settlement`(结算)。
|
||||
- `freezeProtocol(body, candidate, round, baseline, proposals)`:flow 写冻结协议时调用。输入 submissions 交回的冻结协议主体、候选编号、轮次、基线、合并了哪些原提案,交回 `FrozenProtocol`;新登记的假设专用指标改用全名"候选编号.短名"(例如 `r2-c1.restart-gain`),引用的本来就是全名,照抄。加前缀只在这一处:同一轮的研究者互相看不到提案,会起同一个短名、定义却不同,候选编号判重合并之后才有,所以只能在冻结时加(用户 2026-10-03 定)。
|
||||
- 事件:`CELL_NOT_RUN`、`cellNotRunEvent(格, 原因)`:一格消融核验不通过、重做次数用完而不跑时,flow 用它写事件记录(`objects` 只有这一格),结算记证据不足(方法.md 4.3 ⑯、6.6)。
|
||||
- 编号规则:`ORIGINAL_BASELINE`、`entryId`、`proposalId`、`critiqueId`、`candidateId`、`factorId`、`cellId`、`splitCell`、`explanationId`、`callId`,以及协议内的局部编号 `factorKey`(p1…)、`clauseKey`(c1…)。入口编号按作者一直往后编,讨论时整份替换也不复用旧编号。
|
||||
|
||||
@@ -535,9 +536,9 @@ Jev 是一个判别器,和程序检查地位相同,不单独成一种记录
|
||||
|
||||
- `judgeSameChange(model, a, b)`:③ 判重,两条提案的因子清单是不是同一个改法,一对一次请求,交回一条 `JevJudgment`。
|
||||
- `checkProtocol(model, protocol)`:⑥ 查冻结条款,三问(改动了评测、自相矛盾、太含糊)各一次请求,交回 `JevCheck`。
|
||||
- `checkHunk(model, hunk, protocol)`:⑨ 查一段代码改动,五问(不固定种子的随机数、没登记就读时钟、算登记指标的方式与定义不符、比登记的停止条件更早结束、测不到的量写成 0)同一份材料一次请求,交回 `JevCheck`。
|
||||
- `checkHunk(model, hunk, protocol)`:⑨ 查一段代码改动,五问(不固定种子的随机数、没登记就读时钟、算登记指标的方式与定义不符、比登记的停止条件更早结束、测不到的量写成 0)同一份材料一次请求,交回 `JevCheck`。对照的指标取自冻结协议,编号是全名。
|
||||
- `codeHunks(comparison)`:把 workspace 的比较结果(文件清单和差异原文)切成差异块;没有 `@@` 行的改动(二进制、只改可执行位、空文件增删)不出块,由程序检查。
|
||||
- `DedupFactor`、`FrozenProtocol`、`ProtocolMetric`、`ProtocolRules`、`CodeHunk`(类型):三个函数收的带类型输入,取自 vocab 的字段定义;材料由 jev 自己拼。`DedupFactor` 是因子的名称、做法和关键设定:问题写着"设定不同就不算同一个改法",所以关键设定要给 Jev 看。
|
||||
- `DedupFactor`、`FrozenProtocol`、`ProtocolMetric`、`ProtocolRules`、`CodeHunk`(类型):三个函数收的带类型输入,取自 vocab 的字段定义;材料由 jev 自己拼。`DedupFactor` 是因子的名称、做法和关键设定:问题写着"设定不同就不算同一个改法",所以关键设定要给 Jev 看,判重的材料说明里也用一句话讲了 `keySettings` 是什么。
|
||||
- `JevCheck`(类型):每一问的判断,以及有没有任何一问判"是"。
|
||||
|
||||
### submissions
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 消融实现
|
||||
|
||||
本轮被采纳的改法由几个因子组成。研究者想知道每个因子各撑了多少分,办法是从完整代码出发,一次只关掉一个因子、其余照旧,再跑一次正式实验——这样的一次叫一格。分派给你的是其中一格:材料"这一格"一节写明要关哪个因子、关掉时换成什么。你的工作目录是完整代码(被采纳的那份)的副本,每次都从完整代码出发;完整代码和本批基线的冻结目录都写在材料"完整代码(消融的起点)"一节。
|
||||
本轮被采纳的改法由几个因子组成。研究者想知道每个因子各撑了多少分,办法是从完整代码出发,一次只关掉一个因子、其余照旧,再跑一次正式实验——这样的一次叫一格。分派给你的是其中一格:材料"这一格"一节写明要关哪个因子、关掉时换成什么。第一次做这一格时,你的工作目录是完整代码(被采纳的那份)的副本;退回重做时,是你上一次交的这一格代码的副本(见下面"退回意见")。完整代码和本批基线的冻结目录都写在材料"完整代码(消融的起点)"一节。
|
||||
|
||||
### 只关这一个因子
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
材料里没有任何成绩和研究者的判断,这是有意的——你照因子清单做就行,不用知道哪个因子被认为更重要。不要自己跑正式实验去看关掉以后的分数(系统提示词"两条纪律");想确认能被正式实验接上,用 `research_check`。
|
||||
|
||||
材料"本任务要处理的东西"最前面有"退回意见"时,说明上一次交的这一格没通过消融核验:意见列出了关错的因子和核验员看到的位置。照意见把它们改对,仍然从完整代码出发、只关这一格要关的因子;核验员看错了的,在 `concerns` 里写清实际在哪几行。
|
||||
材料"本任务要处理的东西"最前面有"退回意见"时,说明上一次交的这一格没通过消融核验:意见列出了关错的因子和核验员看到的位置。这时工作目录是你上一次交的这一格代码,从它接着改:照意见把关错的因子改对,其余照旧,仍然只关这一格要关的因子;核验员看错了的,在 `concerns` 里写清实际在哪几行。
|
||||
|
||||
交之前用 `diff -ru <完整代码冻结目录> .` 看一遍:改动应该只落在这个因子(和你写进一起关的因子)的位置上,加上把这个因子的指标改写成 null 的那几行。
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ export type DedupFactor = Pick<Factor, "name" | "method" | "keySettings">;
|
||||
/** 一份冻结协议的条款(docs/方法.md 4.3 ⑥"冻结协议包括"),字段与统一提交相同。 */
|
||||
export type FrozenProtocol = Pick<Submission<"unification">, "factors" | "safeguards" | "metrics" | "body" | "clauses">;
|
||||
|
||||
/** 协议要求候选代码采集的一条指标(新登记的和引用的都在内,由调用方查全)。 */
|
||||
/** 协议要求候选代码采集的一条指标(新登记的和引用的都在内,由调用方查全),编号是冻结协议里的全名"候选编号.短名"。 */
|
||||
export type ProtocolMetric = Pick<HypothesisMetric, "id" | "howMeasured" | "missing">;
|
||||
|
||||
/** 查代码改动时对照的那部分协议:协议正文和要采集的指标。没有要采集的指标时是空数组。 */
|
||||
|
||||
@@ -31,6 +31,7 @@ const TASKS: Readonly<Record<Site, string>> = {
|
||||
dedup:
|
||||
"Two research proposals from the same round of a study that improves a program by changing its code. " +
|
||||
"Each proposal lists its change as `factors`: one or more separate edits, each with a short `name` and a `method` saying what to change and how. " +
|
||||
"Each factor also has `keySettings`: the setting values that decide its effect, such as a step size or a time window (\"无\" when there are none). " +
|
||||
"Together the factors of one proposal are the whole change that one implementation would carry out. " +
|
||||
"The proposals were written by different researchers, in Chinese or English.",
|
||||
freeze:
|
||||
|
||||
@@ -33,7 +33,7 @@ function metricSection(d: Draft, p: FrozenProtocol): void {
|
||||
d.section("要实现的指标定义", [
|
||||
all.length === 0 ? "(这份协议没有要实现的假设专用指标)" : json(all),
|
||||
"",
|
||||
"每个指标都由候选代码在正式运行时算出、写进结果文件;测不到的写 null,不写 0。",
|
||||
"每个指标都由候选代码在正式运行时算出、写进结果文件,键照抄这里的 id(全名\"候选编号.短名\");测不到的写 null,不写 0。",
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -145,17 +145,17 @@ export function verification(d: Draft): ChecklistOf<"verification"> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 消融两种任务共用(方法.md 4.3 ⑯):任务与本轮、完整代码与本批基线、这一格(关哪个因子、换成什么)。
|
||||
* 消融两种任务共用(方法.md 4.3 ⑯):任务与本轮、完整代码与本批基线(cellCode 不为 null 时加上这一格已交的代码)、这一格(关哪个因子、换成什么)。
|
||||
* 交回本任务要处理的东西的公共部分:因子清单、完整代码的位置表、冻结协议。
|
||||
*/
|
||||
function ablation(d: Draft, role: string, codeNote: string[]): { cell: string; key: string; factors: string[]; lines: string[] } {
|
||||
function ablation(d: Draft, role: string, cellCode: string | null, codeNote: string[]): { cell: string; key: string; factors: string[]; lines: string[] } {
|
||||
const cell = d.req.object!;
|
||||
const { candidate, key } = splitCell(cell);
|
||||
const { p, items } = frozen(d, candidate);
|
||||
taskSection(d, role, `消融格 ${cell}(候选 ${candidate} 关掉 ${key})`);
|
||||
d.read(candidate, "完整代码");
|
||||
d.read(p.baseline, "本批基线(因子没写关掉时换成什么,就回到它在这件事上的做法)");
|
||||
if (d.req.kind === "ablationVerification") d.read(cell, "要核验的这一格的代码");
|
||||
if (cellCode !== null) d.read(cell, cellCode);
|
||||
d.section("完整代码(消融的起点)", [...codeLines(d), ...codeNote]);
|
||||
const f = p.factors[items.factors.indexOf(key)]!;
|
||||
d.section("这一格", [
|
||||
@@ -167,12 +167,13 @@ function ablation(d: Draft, role: string, codeNote: string[]): { cell: string; k
|
||||
}
|
||||
|
||||
/**
|
||||
* ⑯ 消融实现:从完整代码出发只关指定的因子。核验不通过重做时(这一格已有公开的消融核验)另放退回意见:
|
||||
* 最近一次核验里关错了的因子和核验员写的依据,写法同修复的退回意见。
|
||||
* ⑯ 消融实现:从完整代码出发只关指定的因子。核验不通过重做时(这一格已有公开的消融核验),和修复一样从上一次交的这一格代码
|
||||
* 接着改(用户 2026-10-03 定),另放退回意见:最近一次核验里关错了的因子和核验员写的依据,写法同修复的退回意见。
|
||||
*/
|
||||
export function ablationImplementation(d: Draft): ChecklistOf<"ablationImplementation"> {
|
||||
const a = ablation(d, "实验员", ["你的工作目录是完整代码的副本,改动都落在工作目录里。"]);
|
||||
const verified = d.board.published("ablationVerification").filter((s) => s.target === a.cell).at(-1);
|
||||
const verified = d.board.published("ablationVerification").filter((s) => s.target === d.req.object).at(-1);
|
||||
const start = verified === undefined ? "完整代码的副本" : "上一次交的这一格代码的副本,从它接着改;完整代码列在这里作对照";
|
||||
const a = ablation(d, "实验员", verified === undefined ? null : "你上一次交的这一格代码", [`你的工作目录是${start},改动都落在工作目录里。`]);
|
||||
const opinion: string[] = [];
|
||||
if (verified !== undefined) {
|
||||
const off = new Set([a.key, ...lastAblation(d, a.cell).alsoOff]);
|
||||
@@ -189,7 +190,7 @@ const lastAblation = (d: Draft, cell: string) => d.board.published("ablationImpl
|
||||
|
||||
/** ⑯ 消融核验:只查是不是只关了指定的因子(加实验员报的一起关的因子)。不算没有归属的改动。 */
|
||||
export function ablationVerification(d: Draft): ChecklistOf<"ablationVerification"> {
|
||||
const a = ablation(d, "核验员", []);
|
||||
const a = ablation(d, "核验员", "要核验的这一格的代码", []);
|
||||
const also = lastAblation(d, a.cell).alsoOff;
|
||||
d.section("这一格实际要关的因子", [
|
||||
[a.key, ...also].join("、") + (also.length === 0 ? "(只关指定的因子)" : "(指定的因子加实验员报的一起关的因子)"),
|
||||
|
||||
@@ -151,14 +151,14 @@ export function beliefSection(d: Draft): void {
|
||||
d.section("信念本", [beliefBookOf(d.board, d.req.study, d.round)]);
|
||||
}
|
||||
|
||||
/** 已登记指标清单(方法.md 第五部分"指标"):程序采集的,加冻结协议里登记过的假设专用指标。 */
|
||||
/** 已登记指标清单(方法.md 第五部分"指标"):程序采集的,加冻结协议里登记过的假设专用指标(全名)。 */
|
||||
export function metricsSection(d: Draft): void {
|
||||
const registered = registeredMetrics(d.board);
|
||||
d.section("已登记指标清单", [
|
||||
"程序每次都会采集(不用登记):",
|
||||
...d.req.study.metrics.map((m) => `- ${m.id}(${m.layer === "final" ? "最终指标" : "常规辅助"},${directionText(m.direction)},单位 ${m.unit}):${m.meaning}`),
|
||||
"",
|
||||
"研究者在冻结协议里登记过的假设专用指标(要测的量这里已经有,就写进 reused,不要重新定义;引用时沿用原登记的\"测不到时怎么办\"):",
|
||||
"研究者在冻结协议里登记过的假设专用指标,编号是全名\"候选编号.短名\"(要测的量这里已经有,就把全名写进 reused,不要重新定义;引用时沿用原登记的\"测不到时怎么办\"):",
|
||||
...(registered.length === 0
|
||||
? ["(还没有)"]
|
||||
: registered.map((m) => `- ${m.id}(候选 ${m.candidate} 登记,${directionText(m.direction)},单位 ${m.unit},测不到时${m.missing === "recordMissing" ? "只记缺失" : "整次作废"}):${m.meaning};怎么测:${m.howMeasured}`)),
|
||||
|
||||
@@ -18,6 +18,7 @@ export {
|
||||
explanationId,
|
||||
factorId,
|
||||
factorKey,
|
||||
freezeProtocol,
|
||||
ORIGINAL_BASELINE,
|
||||
proposalId,
|
||||
splitCell,
|
||||
|
||||
@@ -40,14 +40,16 @@ function frame(d: Draft, own: Own, middle: () => void): void {
|
||||
if (d.round >= 1) metricsSection(d);
|
||||
}
|
||||
|
||||
/** 提案、回应、统一共用的因子规则(方法.md 第五部分"延续""指标"、4.3 ②)。答案路径、保障动手次数的两个键也算占用(用户 2026-10-03 定)。 */
|
||||
/**
|
||||
* 提案、回应、统一共用的因子规则(方法.md 第五部分"延续""指标"、4.3 ②)。能引用的是已登记指标的全名;已占用的只有任务的指标和
|
||||
* 两个保留键(用户 2026-10-03 定),已登记的全名带"."、新登记的短名不带,不会重名。
|
||||
*/
|
||||
function factorRules(d: Draft) {
|
||||
const registered = registeredMetrics(d.board).map((m) => m.id);
|
||||
return {
|
||||
factorLimit: d.req.study.factorLimit,
|
||||
continuable: d.board.protocols().flatMap((p) => p.protocol.factors.map((_, i) => factorId(p.candidate, factorKey(i)))),
|
||||
reusableMetrics: registered,
|
||||
takenMetricIds: [...d.req.study.metrics.map((m) => m.id), ANSWER_PATH_KEY, SAFEGUARD_TRIGGERS_KEY, ...registered],
|
||||
reusableMetrics: registeredMetrics(d.board).map((m) => m.id),
|
||||
takenMetricIds: [...d.req.study.metrics.map((m) => m.id), ANSWER_PATH_KEY, SAFEGUARD_TRIGGERS_KEY],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+16
-2
@@ -54,11 +54,25 @@ export const callId = (stageId: string, actor: string, kind: string, object: str
|
||||
// 冻结协议(账本记录 protocol.protocol,方法.md 4.3 ⑥)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/** 冻结协议的主体:submissions 的 protocolBody 交回的五项,与它的 ProtocolBody 同形(materials 不引用 submissions)。 */
|
||||
type ProtocolBody = Pick<Submission<"unification">, "factors" | "safeguards" | "metrics" | "body" | "clauses">;
|
||||
|
||||
/**
|
||||
* 一个候选的冻结协议(方法.md 4.3 ⑥"冻结协议包括"),拼法在 submissions;答复留在回应或统一的提交里(用户 2026-10-03 定)。
|
||||
* 一个候选的冻结协议(方法.md 4.3 ⑥"冻结协议包括"),由 freezeProtocol 写成;答复留在回应或统一的提交里(用户 2026-10-03 定)。
|
||||
* baseline 是出发的基线对象编号,round 是冻结的轮次,proposals 是合并进来的全部原提案。留池候选沿用这一份(4.3 ①)。
|
||||
* metrics 里的编号都是全名"候选编号.短名"。
|
||||
*/
|
||||
export type FrozenProtocol = Pick<Submission<"unification">, "factors" | "safeguards" | "metrics" | "body" | "clauses"> & { round: number; baseline: string; proposals: string[] };
|
||||
export type FrozenProtocol = ProtocolBody & { round: number; baseline: string; proposals: string[] };
|
||||
|
||||
/**
|
||||
* flow 写冻结协议时调用:主体加上轮次、基线、合并了哪些原提案,新登记的假设专用指标改用全名"候选编号.短名"
|
||||
* (用户 2026-10-03 定)。同一轮的研究者互相看不到提案,会起同一个短名、定义却不同;候选编号判重合并之后才有,
|
||||
* 所以只能在冻结时加。引用的指标本来就写全名,照抄。
|
||||
*/
|
||||
export function freezeProtocol(body: ProtocolBody, candidate: string, round: number, baseline: string, proposals: string[]): FrozenProtocol {
|
||||
const defined = body.metrics.defined.map((m) => ({ ...m, id: `${candidate}.${m.id}` }));
|
||||
return { ...body, metrics: { defined, reused: body.metrics.reused }, round, baseline, proposals };
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 冻结代码的位置表(账本记录 code.locations)
|
||||
|
||||
@@ -10,8 +10,12 @@ import type { SubmissionKind } from "./roster.ts";
|
||||
type FactorRules = {
|
||||
factorLimit: number;
|
||||
continuable: string[];
|
||||
/** 冻结协议里登记过的假设专用指标的全名("候选编号.短名")。 */
|
||||
reusableMetrics: string[];
|
||||
/** 最终指标、常规辅助指标和冻结协议里登记过的假设专用指标,新登记的不能和它们重名。 */
|
||||
/**
|
||||
* 新登记的短名不能用的编号:最终指标、常规辅助指标和答案路径的两个保留键。
|
||||
* 已登记的假设专用指标不在这里:它们的全名都带".",短名不带,不会重名。
|
||||
*/
|
||||
takenMetricIds: string[];
|
||||
};
|
||||
|
||||
|
||||
+10
-3
@@ -52,12 +52,19 @@ const METRIC_FIELDS = {
|
||||
}),
|
||||
};
|
||||
|
||||
/** 研究者在协议里登记的一条假设专用指标。编号由研究者起,候选代码按这个编号写出取值。 */
|
||||
/**
|
||||
* 研究者在提案、回应、统一里登记的一条假设专用指标,id 是研究者起的短名。
|
||||
* 冻结时 materials 的 freezeProtocol 把它改成全名"候选编号.短名"(用户 2026-10-03 定):同一轮的研究者互相看不到提案,
|
||||
* 会起同一个短名、定义却不同。短名不含"."、全名都含,两者不会撞。冻结协议、tasks 收的指标沿用这个类型,id 是全名。
|
||||
* 短名最长 64 个字符,加上候选编号后的全名仍在 Id 的 128 个字符以内。
|
||||
*/
|
||||
export const HypothesisMetric = Type.Object(
|
||||
{
|
||||
id: Type.String({
|
||||
...Id,
|
||||
description: "指标短名,只用字母、数字和 ._-,候选代码按它写出取值",
|
||||
pattern: "^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$",
|
||||
description:
|
||||
"指标短名:只用字母、数字和 _-,不能含\".\",最长 64 个字符。冻结时程序在前面加上候选编号,成为全名\"候选编号.短名\"" +
|
||||
"(例如 r2-c1.restart-gain);冻结之后引用它、候选代码在结果文件里写它的取值,都用全名",
|
||||
}),
|
||||
...METRIC_FIELDS,
|
||||
},
|
||||
|
||||
@@ -176,10 +176,10 @@ const SAFEGUARDS = Type.Array(SafeguardName, {
|
||||
const METRICS = Type.Object(
|
||||
{
|
||||
defined: Type.Array(HypothesisMetric, {
|
||||
description: "新登记的假设专用指标;没有就交空数组",
|
||||
description: "新登记的假设专用指标,编号只写短名;没有就交空数组",
|
||||
}),
|
||||
reused: Type.Array(Id, {
|
||||
description: "引用别人登记过的假设专用指标,照材料里已登记指标清单的编号写;没有就交空数组",
|
||||
description: "引用已登记的假设专用指标,照材料里已登记指标清单的全名(\"候选编号.短名\")写;没有就交空数组",
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
||||
+2
-1
@@ -231,7 +231,8 @@ test("材料:给定带类型的输入,发出去的材料与手写预期一
|
||||
const states = sent.map((s) => s.body.state);
|
||||
const [dedup, freeze, , , diff] = states;
|
||||
for (const state of states) assert.equal(typeof state.task, "string");
|
||||
// 判重放名称、做法和关键设定:关掉时换成什么、延续哪个因子不放。
|
||||
// 判重放名称、做法和关键设定:关掉时换成什么、延续哪个因子不放。材料说明里也讲了关键设定是什么(任务书 11 第三部分)。
|
||||
assert.match(dedup.task, /`keySettings`: the setting values that decide its effect/);
|
||||
assert.deepEqual(dedup, {
|
||||
task: dedup.task,
|
||||
proposal_a: { factors: [{ name: "外圈环半径缩到 0.45", method: "把 construct_packing 里外圈环半径常数 0.7 改为 0.45。", keySettings: "环半径 0.45" }] },
|
||||
|
||||
@@ -31,8 +31,8 @@ const adoptInput = (baseline: number, candidates: Array<{ id: string; score: num
|
||||
function roundOneNoAdoption(t: import("node:test").TestContext, before: (w: Writer) => void): Writer {
|
||||
const w = fresh(t);
|
||||
before(w);
|
||||
w.protocol("r1-c1", protocolOf(P_A1, 1, "base", ["r1-a1-1"], ["c"]));
|
||||
w.protocol("r1-c2", protocolOf(P_A2, 1, "base", ["r1-a2-1"], ["c"]));
|
||||
w.protocol("r1-c1", protocolOf("r1-c1", P_A1, 1, "base", ["r1-a1-1"], ["c"]));
|
||||
w.protocol("r1-c2", protocolOf("r1-c2", P_A2, 1, "base", ["r1-a2-1"], ["c"]));
|
||||
const b1 = batch(1, 0.96, { "r1-c1": { a1: r(1.0, 1.1, 1.2) }, "r1-c2": { a1: r(0.95, 0.97, 0.99) } });
|
||||
assert.deepEqual(b1.output.chosen, ["r1-c1"]);
|
||||
w.decision("batch", 1, b1.input, b1.output);
|
||||
@@ -124,7 +124,7 @@ test("信念本的数按条文手算:少多少分、撑几成、世界比特
|
||||
};
|
||||
// 这里只要 c3 入选并跑过:选批记录的其余内容照规则算出,chosen 写成 c3。
|
||||
w.decision("batch", 1, input, { ...selectBestExpected(input), chosen: ["r1-v1-c3"] });
|
||||
w.protocol("r1-v1-c3", protocolOf(P_A1, 1, "base", ["r1-a1-1"], ["c"]));
|
||||
w.protocol("r1-v1-c3", protocolOf("r1-v1-c3", P_A1, 1, "base", ["r1-a1-1"], ["c"]));
|
||||
w.l.append("code", { object: "r1-v1-c3", version: "v-c3", locations: TABLE_C1 });
|
||||
w.run("run-1", "forward", 1, "r1-v1-c3", judged(full, "main"));
|
||||
const a = adoptInput(b, [{ id: "r1-v1-c3", score: full }]);
|
||||
@@ -195,7 +195,7 @@ test("解释的现在交什么写明每人最多 M 条", (t) => {
|
||||
/** 第 1 轮冻结 r1-c1(两个因子)、选中、跑出 run1Judged、采纳。 */
|
||||
function roundOneAdopted(t: import("node:test").TestContext, run1Judged: ReturnType<typeof judged>): Writer {
|
||||
const w = fresh(t);
|
||||
w.protocol("r1-c1", protocolOf(P_A1, 1, "base", ["r1-a1-1"], ["c"]));
|
||||
w.protocol("r1-c1", protocolOf("r1-c1", P_A1, 1, "base", ["r1-a1-1"], ["c"]));
|
||||
const b1 = batch(1, 0.96, { "r1-c1": { a1: r(1.0, 1.1, 1.2) } });
|
||||
w.decision("batch", 1, b1.input, b1.output);
|
||||
w.l.append("code", { object: "r1-c1", version: "v-c1", locations: TABLE_C1 });
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "../src/rules/index.ts";
|
||||
import {
|
||||
buildMaterial,
|
||||
freezeProtocol,
|
||||
readPrompts,
|
||||
type CodeChanges,
|
||||
type FrozenProtocol,
|
||||
@@ -24,6 +25,7 @@ import { join } from "node:path";
|
||||
|
||||
// materials 测试共用:在真实账本(临时文件)里一步步写入一段研究进程。
|
||||
// 研究者 a1、a2,实验员 x1,核验员 v1。第 1 轮采纳 r1-c1(两个因子),消融测 p1、p2 没测;第 2 轮采纳 r2-c1。
|
||||
// 第 1 轮两条提案都登记了短名 restart-count,冻结成 r1-c1.restart-count、r1-c2.restart-count;r2-c1 引用前一个。
|
||||
// 好认的数和字(7.77x 作者自评、5.55x 甲的第一次预测等)用来检查"不该看到的没出现"。
|
||||
|
||||
export const study: StudyFacts = {
|
||||
@@ -62,10 +64,11 @@ const P_A1: Submission<"proposal"> = {
|
||||
authorEstimate: SEALED_A1,
|
||||
body: "甲的提案正文",
|
||||
};
|
||||
// 乙和甲同一轮互相看不到提案,起了同一个短名 restart-count、定义却不同(参考项目真实账本出过 5 次,任务书 11 第一部分)。
|
||||
const P_A2: Submission<"proposal"> = {
|
||||
factors: [factor("收缩半径修复", null)],
|
||||
safeguards: [],
|
||||
metrics: { defined: [], reused: [] },
|
||||
metrics: { defined: [{ id: "restart-count", meaning: "修复阶段重启了几次", unit: "次", direction: "minimize", howMeasured: "修复循环每重启一次加一", missing: "recordMissing" }], reused: [] },
|
||||
authorEstimate: SEALED_A2,
|
||||
body: "乙的提案正文",
|
||||
};
|
||||
@@ -121,8 +124,9 @@ export class Writer {
|
||||
}
|
||||
}
|
||||
|
||||
export function protocolOf(p: Submission<"proposal">, round: number, baseline: string, proposals: string[], clauses: string[]): FrozenProtocol {
|
||||
return { factors: p.factors, safeguards: p.safeguards, metrics: p.metrics, body: p.body, clauses, round, baseline, proposals };
|
||||
/** 候选 candidate 的冻结协议:照 flow 的做法,用 freezeProtocol 写(新登记的指标改全名)。 */
|
||||
export function protocolOf(candidate: string, p: Submission<"proposal">, round: number, baseline: string, proposals: string[], clauses: string[]): FrozenProtocol {
|
||||
return freezeProtocol({ factors: p.factors, safeguards: p.safeguards, metrics: p.metrics, body: p.body, clauses }, candidate, round, baseline, proposals);
|
||||
}
|
||||
|
||||
export function batch(round: number, baseline: number, ranges: Record<string, Record<string, Range>>): { input: BatchInput; output: ReturnType<typeof selectBestExpected> } {
|
||||
@@ -192,8 +196,8 @@ const STEPS: Array<[string, (w: Writer, s: Record<string, number>) => void]> = [
|
||||
w.sub("s1-resp", "a1", "response", "r1-c1", null, { replies: [{ critique: CRITIQUE_ID, reply: "甲答复:保留扰动" }], changes: {}, clauses: ["加多起点", "加扰动"], body: "无" });
|
||||
w.sub("s1-resp", "a2", "response", "r1-c2", null, { replies: [], changes: {}, clauses: ["加修复"], body: "无" });
|
||||
w.close("s1-resp");
|
||||
w.protocol("r1-c1", protocolOf(P_A1, 1, "base", ["r1-a1-1"], ["加多起点", "加扰动"]));
|
||||
w.protocol("r1-c2", protocolOf(P_A2, 1, "base", ["r1-a2-1"], ["加修复"]));
|
||||
w.protocol("r1-c1", protocolOf("r1-c1", P_A1, 1, "base", ["r1-a1-1"], ["加多起点", "加扰动"]));
|
||||
w.protocol("r1-c2", protocolOf("r1-c2", P_A2, 1, "base", ["r1-a2-1"], ["加修复"]));
|
||||
}],
|
||||
["secondOpen", (w) => w.open("s1-sp", "secondPrediction", 1, ["a1", "a2"])],
|
||||
["second", (w) => {
|
||||
@@ -273,8 +277,8 @@ const STEPS: Array<[string, (w: Writer, s: Record<string, number>) => void]> = [
|
||||
w.sub("s2-resp", "a1", "response", "r2-c1", null, { replies: [], changes: {}, clauses: ["加局部搜索"], body: "无" });
|
||||
w.sub("s2-resp", "a2", "response", "r2-c2", null, { replies: [], changes: {}, clauses: ["改修复", "加早停"], body: "无" });
|
||||
w.close("s2-resp");
|
||||
w.protocol("r2-c1", protocolOf({ ...P_A1, metrics: { defined: [], reused: ["restart-count"] }, factors: [factor("新的局部搜索", "r1-c1-p1"), factor("更多起点", null)], body: "甲的第二轮提案" }, 2, "r1-c1", ["r2-a1-1"], ["加局部搜索"]));
|
||||
w.protocol("r2-c2", protocolOf({ ...P_A2, factors: [factor("更细的修复", null), factor("早停", null)], body: "乙的第二轮提案" }, 2, "r1-c1", ["r2-a2-1"], ["改修复", "加早停"]));
|
||||
w.protocol("r2-c1", protocolOf("r2-c1", { ...P_A1, metrics: { defined: [], reused: ["r1-c1.restart-count"] }, factors: [factor("新的局部搜索", "r1-c1-p1"), factor("更多起点", null)], body: "甲的第二轮提案" }, 2, "r1-c1", ["r2-a1-1"], ["加局部搜索"]));
|
||||
w.protocol("r2-c2", protocolOf("r2-c2", { ...P_A2, factors: [factor("更细的修复", null), factor("早停", null)], body: "乙的第二轮提案" }, 2, "r1-c1", ["r2-a2-1"], ["改修复", "加早停"]));
|
||||
w.open("s2-sp", "secondPrediction", 2, ["a1", "a2"]);
|
||||
const ranges = { "r2-c1": { a1: r(1.2, 1.24, 1.3), a2: r(1.19, 1.22, 1.27) }, "r2-c2": { a1: r(1.18, 1.2, 1.22), a2: r(1.17, 1.2, 1.22) } };
|
||||
for (const [c, rs] of Object.entries(ranges)) for (const [a, range] of Object.entries(rs)) w.sub("s2-sp", a, "secondPrediction", c, null, { range, body: "理由" });
|
||||
|
||||
+39
-4
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { test } from "node:test";
|
||||
import { beliefBook, queryMaterial, readPrompts, type TaskMaterial } from "../src/materials/index.ts";
|
||||
import { beliefBook, freezeProtocol, queryMaterial, readPrompts, type TaskMaterial } from "../src/materials/index.ts";
|
||||
import { SUBMISSION_KINDS, type SubmissionKind } from "../src/vocab/index.ts";
|
||||
import { tempDir } from "./ledger-helpers.ts";
|
||||
import { CHANGES_C1, CRITIQUE_ID, everything, material, NONE, study, studyUpTo, TABLE_C1 } from "./materials-helpers.ts";
|
||||
@@ -81,8 +81,8 @@ test("第 2 轮提案:上一轮全部收尾、上一轮实验员的提醒、
|
||||
maxProposals: 2,
|
||||
factorLimit: 3,
|
||||
continuable: ["r1-c1-p1", "r1-c1-p2", "r1-c2-p1"],
|
||||
reusableMetrics: ["restart-count"],
|
||||
takenMetricIds: ["sum-of-radii", "wall-seconds", "answer-path", "safeguard-triggers", "restart-count"],
|
||||
reusableMetrics: ["r1-c1.restart-count", "r1-c2.restart-count"],
|
||||
takenMetricIds: ["sum-of-radii", "wall-seconds", "answer-path", "safeguard-triggers"],
|
||||
});
|
||||
for (const x of ["上一轮的收尾", "上一轮实验员实现时的提醒", '可以写进"延续"的因子', "已登记指标清单", "研究记录", "信念本"]) {
|
||||
assert.ok(headings(m).includes(x), `应有一节"${x}"`);
|
||||
@@ -157,6 +157,31 @@ test("新登记指标不能用的编号里有答案路径、保障动手次数
|
||||
assert.ok(c.takenMetricIds.includes("answer-path") && c.takenMetricIds.includes("safeguard-triggers"), JSON.stringify(c.takenMetricIds));
|
||||
});
|
||||
|
||||
test("冻结时新登记的指标改全名:同一轮两个候选登记同一个短名、定义不同,冻结后是两个不同的全名", () => {
|
||||
const body = (meaning: string) => ({
|
||||
factors: [{ name: "因子", method: "做法", keySettings: "无", continues: null }],
|
||||
safeguards: [],
|
||||
metrics: { defined: [{ id: "restart-gain", meaning, unit: "无", direction: "maximize" as const, howMeasured: "测一次", missing: "recordMissing" as const }], reused: ["r1-c1.min-gap"] },
|
||||
body: "正文",
|
||||
clauses: ["条款"],
|
||||
});
|
||||
const a = freezeProtocol(body("重启段内最好 Σr 减进入重启段时的 Σr"), "r2-c1", 2, "r1-c1", ["r2-a1-1"]);
|
||||
const b = freezeProtocol(body("最终 Σr 减池阶段最好 Σr"), "r2-c2", 2, "r1-c1", ["r2-a2-1", "r2-a3-1"]);
|
||||
assert.deepEqual(a, { ...body("重启段内最好 Σr 减进入重启段时的 Σr"), metrics: { defined: [{ ...body("").metrics.defined[0]!, id: "r2-c1.restart-gain", meaning: "重启段内最好 Σr 减进入重启段时的 Σr" }], reused: ["r1-c1.min-gap"] }, round: 2, baseline: "r1-c1", proposals: ["r2-a1-1"] });
|
||||
assert.deepEqual([b.metrics.defined[0]!.id, b.metrics.defined[0]!.meaning, b.proposals], ["r2-c2.restart-gain", "最终 Σr 减池阶段最好 Σr", ["r2-a2-1", "r2-a3-1"]]);
|
||||
});
|
||||
|
||||
test("下一轮:同短名的两个全名都能引用,各自对应自己的定义;引用的指标在实验员材料里是原登记的定义", (t) => {
|
||||
const m = material(studyUpTo(t, "proposal2Open"), "proposal", "a2", "s2-prop", null, NONE);
|
||||
const list = m.sections.find((s) => s.heading === "已登记指标清单")!.text;
|
||||
assert.match(list, /- r1-c1\.restart-count(候选 r1-c1 登记,越大越好,单位 次,测不到时只记缺失):重启次数;/);
|
||||
assert.match(list, /- r1-c2\.restart-count(候选 r1-c2 登记,越小越好,单位 次,测不到时只记缺失):修复阶段重启了几次;/);
|
||||
const impl = material(studyUpTo(t, "implOpen2"), "implementation", "x1", "s2-impl-c1", "r2-c1", NONE);
|
||||
const defs = impl.sections.find((s) => s.heading === "要实现的指标定义")!.text;
|
||||
assert.ok(defs.includes('"id": "r1-c1.restart-count"') && defs.includes("重启次数"), defs);
|
||||
assert.ok(!defs.includes("修复阶段重启了几次"), defs);
|
||||
});
|
||||
|
||||
test("解释:本轮运行、提醒、要给三个数的格、产物只进查询;看不到别人的第一次预测", (t) => {
|
||||
const l = studyUpTo(t, "explOpen1");
|
||||
const m = material(l, "explanation", "a2", "s1-expl", null, { changes: null, artifacts: { "run-1": { "stdout.log": "日志正文" } } });
|
||||
@@ -191,7 +216,8 @@ test("实现:冻结协议、指标定义、要交的位置表、起点代码
|
||||
assert.deepEqual(m.readable.map((r) => r.version), ["v-base"]);
|
||||
for (const x of ["任务与本轮", "基线代码", "本任务要处理的东西", "要实现的指标定义", "要交的因子位置表"]) assert.ok(headings(m).includes(x));
|
||||
assert.ok(!headings(m).includes("信念本"));
|
||||
has(m, "restart-count");
|
||||
has(m, '"id": "r1-c1.restart-count"');
|
||||
has(m, "键照抄这里的 id(全名");
|
||||
lacks(m, "乙的提案正文");
|
||||
lacks(m, "收缩半径修复");
|
||||
lacks(m, "6.661");
|
||||
@@ -269,6 +295,14 @@ test("消融实现重做:材料放上一次消融核验关错的因子和依
|
||||
has(m, "- 因子 p1 应当关着,核验员看到的是照留:solve.py:12 仍在跑多起点循环");
|
||||
lacks(m, "solve.py:30 照留");
|
||||
assert.deepEqual(m.checklist, { kind: "ablationImplementation", cell: "r1-c1-p1", off: "p1", factors: ["p1", "p2"] });
|
||||
// 重做从上一次交的这一格代码接着改(用户 2026-10-03 定):工作目录是它的副本,能读的版本里有它,也有完整代码作对照。
|
||||
assert.deepEqual(m.readable.map((r) => [r.version, r.label]), [
|
||||
["v-c1", "完整代码"],
|
||||
["v-base", "本批基线(因子没写关掉时换成什么,就回到它在这件事上的做法)"],
|
||||
["v-c1-p1", "你上一次交的这一格代码"],
|
||||
]);
|
||||
has(m, "你的工作目录是上一次交的这一格代码的副本,从它接着改");
|
||||
lacks(m, "你的工作目录是完整代码的副本");
|
||||
});
|
||||
|
||||
test("消融实现:这一格、因子清单、完整代码的位置表;看不到运行成绩", (t) => {
|
||||
@@ -277,6 +311,7 @@ test("消融实现:这一格、因子清单、完整代码的位置表;看
|
||||
assert.deepEqual(m.checklist, { kind: "ablationImplementation", cell: "r1-c1-p1", off: "p1", factors: ["p1", "p2"] });
|
||||
assert.ok(headings(m).includes("完整代码(消融的起点)"));
|
||||
assert.deepEqual(m.readable.map((r) => r.version), ["v-c1", "v-base"]);
|
||||
has(m, "你的工作目录是完整代码的副本");
|
||||
lacks(m, "1.2");
|
||||
lacks(m, "甲的解释");
|
||||
});
|
||||
|
||||
@@ -20,12 +20,13 @@ const r = (q1: number, q5: number, q9: number) => ({ q1, q5, q9 });
|
||||
const factor = (name: string, continues: string | null) => ({ name, method: `${name}的做法`, keySettings: "无", continues });
|
||||
const metric = (id: string) => ({ id, meaning: "次数", unit: "次", direction: "maximize" as const, howMeasured: "数", missing: "recordMissing" as const });
|
||||
|
||||
const RULES = { factorLimit: 2, continuable: ["r1-c1-p1", "r1-c1-p2"], reusableMetrics: ["restart-count"], takenMetricIds: ["sum-of-radii", "wall-seconds", "answer-path", "safeguard-triggers", "restart-count"] };
|
||||
// 能引用的是冻结后的全名"候选编号.短名";已占用的只有任务的指标和两个保留键(任务书 11 第一部分)。
|
||||
const RULES = { factorLimit: 2, continuable: ["r1-c1-p1", "r1-c1-p2"], reusableMetrics: ["r1-c1.restart-count", "r1-c2.restart-count"], takenMetricIds: ["sum-of-radii", "wall-seconds", "answer-path", "safeguard-triggers"] };
|
||||
|
||||
const PROPOSAL: Submission<"proposal"> = {
|
||||
factors: [factor("多起点", null), factor("扰动", "r1-c1-p2")],
|
||||
safeguards: ["guard"],
|
||||
metrics: { defined: [metric("kick-count")], reused: ["restart-count"] },
|
||||
metrics: { defined: [metric("kick-count")], reused: ["r1-c1.restart-count"] },
|
||||
authorEstimate: r(1.1, 1.2, 1.3),
|
||||
body: "提案正文",
|
||||
};
|
||||
@@ -188,13 +189,20 @@ test("规则 2、3、4、10 对统一自己的字段查:因子超上限、保
|
||||
});
|
||||
|
||||
test("规则 10:reused 要在可引用清单里;新登记的编号不重复,也不能和已占用的重名(含答案路径两个保留键)", async () => {
|
||||
assert.match(await rejected(proposal({ ...PROPOSAL, metrics: { defined: [], reused: ["kick-count"] } }, 0)), /metrics.reused 只能写 restart-count;你写的 kick-count 不在里面/);
|
||||
assert.match(await rejected(proposal({ ...PROPOSAL, metrics: { defined: [], reused: ["kick-count"] } }, 0)), /metrics.reused 只能写 r1-c1\.restart-count、r1-c2\.restart-count;你写的 kick-count 不在里面/);
|
||||
const reason = await rejected(proposal({ ...PROPOSAL, metrics: { defined: [metric("a"), metric("a"), metric("wall-seconds"), metric("answer-path")], reused: [] } }, 0));
|
||||
assert.match(reason, /metrics.defined 里编号 a 写了不止一次/);
|
||||
assert.match(reason, /metrics.defined 的编号 wall-seconds 已被占用/);
|
||||
assert.match(reason, /metrics.defined 的编号 answer-path 已被占用/);
|
||||
});
|
||||
|
||||
test("规则 10:引用写短名而不是全名时退回,原因里列出能引用的全名;新登记的短名和已登记的全名同名不算占用", async () => {
|
||||
const reason = await rejected(proposal({ ...PROPOSAL, metrics: { defined: [], reused: ["restart-count"] } }, 0));
|
||||
assert.equal(reason, "这份提交没有收下,要改的地方:\n- metrics.reused 只能写 r1-c1.restart-count、r1-c2.restart-count;你写的 restart-count 不在里面");
|
||||
const v = await receive(proposal({ ...PROPOSAL, metrics: { defined: [metric("restart-count")], reused: ["r1-c1.restart-count"] } }, 0));
|
||||
assert.equal(v.accepted, true);
|
||||
});
|
||||
|
||||
test("提案条数:已收下 M 条再交就退回", async () => {
|
||||
assert.match(await rejected(proposal(PROPOSAL, 2)), /本阶段你已经收下 2 条提案,每人最多 2 条/);
|
||||
});
|
||||
@@ -301,7 +309,7 @@ test("回应拼协议:changes 只改保障时,因子、指标、正文取原
|
||||
assert.deepEqual(p, {
|
||||
factors: [factor("多起点", null), factor("扰动", "r1-c1-p2")],
|
||||
safeguards: ["clip"],
|
||||
metrics: { defined: [metric("kick-count")], reused: ["restart-count"] },
|
||||
metrics: { defined: [metric("kick-count")], reused: ["r1-c1.restart-count"] },
|
||||
body: "提案正文",
|
||||
clauses: ["条款一"],
|
||||
});
|
||||
@@ -402,7 +410,7 @@ test("回应全判否:收下,交回拼好的协议和三问的判断", async
|
||||
assert.deepEqual(v.accepted && v.protocol, {
|
||||
factors: [factor("多起点", null), factor("扰动", "r1-c1-p2")],
|
||||
safeguards: ["guard"],
|
||||
metrics: { defined: [metric("kick-count")], reused: ["restart-count"] },
|
||||
metrics: { defined: [metric("kick-count")], reused: ["r1-c1.restart-count"] },
|
||||
body: "新正文",
|
||||
clauses: ["条款一"],
|
||||
});
|
||||
|
||||
@@ -344,6 +344,16 @@ test("判定表 5:缺了整次作废的假设专用指标测不到,不算数
|
||||
assert.equal(value(voided.score), 0.203125);
|
||||
});
|
||||
|
||||
test("假设专用指标用冻结后的全名\"候选编号.短名\"当键,读得到值(任务书 11 第一部分)", async (t) => {
|
||||
const full: HypothesisMetric = { id: "r2-c1.restart-gain", meaning: "测试用", unit: "无", direction: "maximize", howMeasured: "测试用", missing: "invalidateRun" };
|
||||
const runDir = runDirWith(t, loose());
|
||||
writeFileSync(join(runDir, "metrics.json"), JSON.stringify({ "r2-c1.restart-gain": 0.125, "restart-gain": 9, "answer-path": "main", "safeguard-triggers": {} }));
|
||||
const result = await judgeRun(t, "fullname", runDir, EXITED_OK, [full]);
|
||||
assert.equal(result.verdict, "valid");
|
||||
assert.deepEqual(Object.keys(result.metrics).filter((k) => k.includes("restart")), ["r2-c1.restart-gain"]);
|
||||
assert.equal(value(result.metrics["r2-c1.restart-gain"]), 0.125);
|
||||
});
|
||||
|
||||
test("答案路径、保障动手次数:读不到时记读不到及原因,不作废运行", async (t) => {
|
||||
const missing = await judgeRun(t, "nometrics", runDirWith(t, loose()), EXITED_OK, []);
|
||||
assert.equal(missing.verdict, "valid");
|
||||
|
||||
@@ -172,6 +172,16 @@ test("关掉消融时,不带 cells 的解释通过字段定义检查", () => {
|
||||
assert.equal(Value.Check(SUBMISSION_SCHEMAS.explanation, submission), true);
|
||||
});
|
||||
|
||||
test("假设专用指标的短名不能含\".\":带点的新登记不收;引用写全名照收(任务书 11 第一部分)", () => {
|
||||
const proposal = (defined: string, reused: string[]) => ({ ...samples.proposal, metrics: { defined: [{ ...metrics.defined[0]!, id: defined }], reused } });
|
||||
assert.equal(Value.Check(SUBMISSION_SCHEMAS.proposal, proposal("restart-gain", ["r1-c1.restart-gain"])), true);
|
||||
assert.equal(Value.Check(SUBMISSION_SCHEMAS.proposal, proposal("r1-c1.restart-gain", [])), false);
|
||||
assert.equal(Value.Check(SUBMISSION_SCHEMAS.proposal, proposal("restart.gain", [])), false);
|
||||
// 短名最长 64 个字符,加上候选编号的全名仍在编号的 128 个字符以内。
|
||||
assert.equal(Value.Check(SUBMISSION_SCHEMAS.proposal, proposal("g".repeat(64), [])), true);
|
||||
assert.equal(Value.Check(SUBMISSION_SCHEMAS.proposal, proposal("g".repeat(65), [])), false);
|
||||
});
|
||||
|
||||
// 复现:TypeBox 的 Record 对带格式的键只生成 patternProperties,不合格式的键原先完全不检查。
|
||||
test("环境变量和指标取值里,不合格式的键被拒收", () => {
|
||||
const run = {
|
||||
|
||||
Reference in New Issue
Block a user