From 3f1c0a4c185657b621e4d765b513fd43dc84ea21 Mon Sep 17 00:00:00 2001 From: iomgaa Date: Fri, 2 Oct 2026 11:28:09 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=96=B0=E5=A2=9E=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E4=B9=A6=2005-tasks-runner=EF=BC=8C=E7=BB=93=E6=9E=84.md=20?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=20runner/tasks=20=E8=81=8C=E8=B4=A3=E4=B8=8E?= =?UTF-8?q?=E8=A1=8C=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- WORK_LOG.md | 4 +- docs/结构.md | 12 +- 任务书/05-tasks-runner.md | 265 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+), 7 deletions(-) create mode 100644 任务书/05-tasks-runner.md diff --git a/WORK_LOG.md b/WORK_LOG.md index b5850cc..09bc9fd 100644 --- a/WORK_LOG.md +++ b/WORK_LOG.md @@ -32,10 +32,10 @@ - 整个系统不设默认值:配置每个字段必填,函数参数不写默认值,可以没有的值写 null(CLAUDE.md"不设默认值")。用户原话:"全部都不要默认值更好,甚至整个系统都不要默认值,因为默认值可能会掩盖错误"。 - 对照组(步骤开关、朴素循环)先不做,完整流程跑稳之后再按当时的方法补(方法.md 第十一部分)。用户原话:"先不做对照组,按你说的改"。 - 计算时间不再是预算,只记录;预算只有运行次数(42347e4)。用户原话:"CPU 秒不当预算但是要记录"。 -- 解释的每格三个数改为可选,关掉消融时不交。用户原话:"同意 A:解释的 cells 去掉 minItems,改为可选"。 - rules 的约定(方法.md 没写死的地方):入池 P < 0.5 严格;采纳和消融中发现都要严格大于采纳线;成本用 max(0, ĝ₀);没测的格附"含它的最好组合"的净值;战绩在分数原值上判;编号一律按自然序比较。用户原话:"1–8 都同意"。消融成分权重跟随 λ、分歧量不掺开放预留,由用户改进方法.md(a050771)。 - 三个数改为严格递增,去掉尖峰(bfb96ba)。用户原话:"凡是为'三个数相等''宽度为 0''开放预留缩成一个点'写的专门处理,全部删掉"。 - 答案路径、保障动手次数只记读到的原始内容或读不到的原因;在不在位置表里,由以后拿得到位置表的模块现算,不存。用户原话:"按'能现算的不存',tasks 只记读到的原始内容……对表由以后有位置表的模块现算,把这条记进 WORK_LOG。" +- tasks 与 runner(任务书 05):依赖跟着任务走,一份研究不能自己改依赖,镜像在研究开始时建一次、编号冻结进账本;runner、tasks 行数按结构.md 第七部分;两个模块合成一份任务书。用户原话:"都同意,写任务书 05 吧"。监督者另定并告知用户:凡是执行任务代码的事(正式运行、判分、轻量检查)都在任务镜像里跑;资源用量在容器里读,不建要 root 清理的 cgroup 分组;容器断网;同一份研究所有运行用同一个种子;不做"取消"函数;`VERDICTS` 删掉"判不了"。 ## 未完成 @@ -79,4 +79,4 @@ ## 下一步 -任务书 04(config)已核对通过(d2bfb97)。下一个:tasks 与 runner,监督者先讲给用户。 +任务书 05(tasks 与 runner,`任务书/05-tasks-runner.md`)已写好,等用户另开新 Agent 执行;完成后监督者核对。 diff --git a/docs/结构.md b/docs/结构.md index 5783e6e..b5692d7 100644 --- a/docs/结构.md +++ b/docs/结构.md @@ -7,6 +7,7 @@ - 用户 2026-10-01 同意:提交的字段定义放 vocab,submissions 只判收不收;vocab 可放规范序列化和哈希两个函数,上限调到 600 行;执行后实际 1030 行,用户同意上限改为 1100 行。 - 用户 2026-10-02 同意:"全部只追加":账本用一张只追加的表,全部记录都不能改。 - 用户 2026-10-02 指出可见性、截止序号、通用唯一键都是过度设计:"每次给到派的材料有哪些"和"一开始灌进去还是让它去查"是分开的两件事;Jev 本质上是一种可以代替程序的判别器。据此:谁能看什么归 materials(按任务定材料),ledger 只存;只在方法要求"只能一次"处加唯一约束;Jev 不单独成记录。ledger 预期 500–900 行。 +- 用户 2026-10-02 同意:依赖跟着任务走(一份研究不能自己改依赖),凡是执行任务代码的事都由 runner 在任务镜像里跑,runner 与 tasks 的行数按第七部分。 - 依据是两次开发调研。一次是模块切分,参考了 Parnas 1972 与 1979、Ousterhout、Cockburn 和同类项目的源码;一次是同类研究的实验设计。 --- @@ -39,8 +40,8 @@ | 组织 | submissions(收件) | 一份提交收下还是退回:先程序检查,再问 Jev(字段定义在 vocab) | jev | | 外部 | ledger(账本) | 记录怎么存、怎么保证不可改、怎么从记录算出当前状态;花钱动作的登记 | 无 | | 外部 | agents(角色会话) | Pi 的接口:开子会话、发工具和提示词、收提交、记用量 | 无 | -| 外部 | runner(运行器) | Docker 的用法:限时、限核、取消、记资源、取回产物 | 无 | -| 外部 | tasks(任务) | 一个任务怎么跑、怎么从原始产物独立算分(填圆的合法化也在这里) | 无 | +| 外部 | runner(运行器) | Docker 的用法:建镜像、限时、限核、断网、记资源、查一次运行的状态 | 无 | +| 外部 | tasks(任务) | 任务目录里的事实和文件(指标、ε、时限、核数、镜像的 Dockerfile 与依赖清单);一个任务怎么跑、怎么从原始产物独立算分(填圆的合法化也在这里) | 无 | | 外部 | workspace(代码版本) | git 的用法:基线快照、实验员工作目录、冻结候选、代码差异 | 无 | | 外部 | jev(Jev 判别) | 问法、阈值、校准 | 无 | | 纯计算 | rules(研究规则) | 全部公式和常数;信念本、战绩、余额的计算 | 无 | @@ -67,7 +68,8 @@ - **给模型看什么只归 materials。** 每个任务开始时,materials 按方法.md 第三部分定下这个任务能看到的材料。哪些放进第一条消息、哪些等模型用查询工具去读,是同一份材料的两种给法,查询工具查不到这份材料以外的东西。ledger 不判断谁能看什么。 - **收不收一份提交,只在 submissions 一处判。** 跟状态有关的两条由 ledger 在写入时拒绝:这个阶段还开着吗;要替换的那一份是不是本人刚交的。 - **信念本**:数字由 rules 算,写成文字由 materials 负责,不单独存,每次现算。 -- **runner 只管怎么跑,tasks 只管怎么判。** 两者互不认识,由 flow 串起来:先向 tasks 要运行定义,交给 runner 去跑,再把产物交回 tasks 算分。 +- **runner 只管怎么跑,tasks 只管怎么判。** 两者互不认识,由 flow 串起来:先向 tasks 要运行定义,交给 runner 去跑,再把产物交回 tasks 算分。凡是要执行任务代码的事(正式运行、判分时跑校验器、轻量检查)都由 runner 在任务镜像里跑,tasks 自己不起进程;只有正式运行登记、扣次数。 +- **依赖跟着任务走。** 镜像由任务目录里的 Dockerfile 和依赖清单建出来,研究开始时建一次,镜像编号冻结进账本的研究配置记录;恢复时只核对,本机没有这个镜像就报错停下,不重建。一份研究不能自己改依赖。 - **"下一步做什么"只写在 flow 的一个纯函数里。** 每个步骤一个小文件。步骤文件里如果出现公式、字段名单或可见性判断,说明知识漏进来了,要挪回下层。 --- @@ -189,8 +191,8 @@ Jev 是一个判别器,和程序检查地位相同,不单独成一种记录 | materials | 1000–1800 | | agents | 800–1300 | | workspace | 300–600 | -| runner | 500–900 | -| tasks | 契约 150–300;填圆 600–1000,另有 Python 文件 | +| runner | 250–450 | +| tasks | 公共部分与填圆合计 400–700,另有任务目录里的 Python 文件 | | export | 500–900 | | vocab | 上限 1100 | diff --git a/任务书/05-tasks-runner.md b/任务书/05-tasks-runner.md new file mode 100644 index 0000000..c365c2a --- /dev/null +++ b/任务书/05-tasks-runner.md @@ -0,0 +1,265 @@ +# 任务书 05:tasks(任务)与 runner(运行器) + +写两个模块: +- **runner**:用 Docker 把一条命令跑完,交回它怎么结束的、用了多少资源; +- **tasks**:任务目录里的事实和文件、怎么准备一次运行、怎么从原始产物独立判分。 + +顺带改 config 和 vocab 的几处小地方。 + +本任务书已由用户和监督者在 2026-10-02 一起确定。下面说的"监督者",是在参考项目目录里和用户讨论设计的另一个会话。 + +## 开工前先读 + +- `CLAUDE.md`:全部,尤其是"罕见情况怎么办"(什么算系统边界)、"不设默认值"、"写代码的标准"。 +- `docs/结构.md`: + - 第二部分:模块表里 runner、tasks 两行,以及"几条边界"里"runner 只管怎么跑""依赖跟着任务走"两条; + - 第六部分:只有 runner 和 workspace 能起子进程,tasks 不许引用 runner; + - 第七部分:行数; + - 第八部分:vocab 已导出的 `RunDefinition`、`ProcessResult`、`JudgedResult`、`AnswerPath`、`SafeguardTriggers`、指标定义,以及 config 一节。 +- `docs/方法.md`: + - 4.3 ⑨ ⑩(实现、正式运行); + - 第五部分"答案路径"; + - 第七部分(扣次、轻量检查); + - 第八部分(公平比较、合法化、防作弊靠结构); + - 第九部分(先登记、再执行、再登记结果); + - 第十部分(任务需要提供什么); + - 第十一部分(对照组暂不实现)。 +- `WORK_LOG.md`。 +- 参考项目(只读,不要改): + - `/home/iomgaa/Projects/RivalBits/tasks/`:任务目录和轻量检查脚本; + - `/home/iomgaa/Projects/RivalBits/src/tasks/`:只看 `circle-packing.ts` 里的 `protocolMetrics`、`validateSolution`、`readLegalization`、`readMetricsFile`,其余文件看第七部分的删除表; + - `/home/iomgaa/Projects/RivalBits/src/runner/container.ts`:`docker run` 的参数和每个参数的理由; + - `/home/iomgaa/Projects/RivalBits/tests/circle-validator.test.ts`:手算的布局,可以直接借用。 + +## 一、原则 + +- **系统边界**: + - `task.json` 是用户提供的东西,严格检查,一次列出全部错误; + - Docker 的返回是外部服务的返回,读不懂就报错停下。 + + 模块之间的调用不是边界:flow 传进来的参数不另做检查(CLAUDE.md"罕见情况怎么办")。 +- **不设默认值**:`task.json` 每个字段必填,可以没有的写 `null`;函数参数不写默认值。 +- **每加一道检查、一个分支之前,先问"不写会怎样"。** 如果答案是"会报错停下"或者"需要有人用 shell 搞破坏才会发生",就不写。参考项目这两个目录里大量代码属于后一种,第七部分已列出。 +- **凡是要执行任务代码的事,都由 runner 在任务镜像里跑**,包括正式运行、判分时跑校验器、轻量检查。tasks 不起子进程,只读写文件、给出运行定义、解读产物。宿主机不一定装了 numpy,三件事在同一个环境里做才一致。 +- 只有正式运行在账本里登记、扣次数。判分和轻量检查只是借 runner 跑一下,不登记。登记、扣次数是 flow 的事,runner 和 tasks 都不碰账本。 + +## 二、runner + +### 对外提供什么 + +经 `src/runner/index.ts` 导出,函数名由你定: + +1. **建镜像**:给一个目录(里面有 Dockerfile 和依赖清单)和一个镜像名,执行 `docker build`,交回镜像编号(`docker image inspect -f '{{.Id}}'` 的值,见 vocab `RunDefinition` 的说明)。 +2. **查镜像在不在**:给一个镜像编号,回答本机有没有。 +3. **启动**:给一个名字、一份 `RunDefinition`、一个宿主上的运行目录,起一个容器。交回"启动了",或者"没启动 + 原因"。 +4. **等结束**:给名字,等这个容器结束,交回 vocab 的 `ProcessResult`。要能接上之前另一个进程启动的容器,因为中断恢复时就是这种情况。 +5. **查状态**:给名字,回答这几种之一: + - 没有这个容器; + - 建了但没启动; + - 在跑; + - 已结束; + - 问不到(Docker 不应答)。 + + 恢复时 flow 用它决定写"确认启动"还是"确认没启动";问不到时,flow 记"状态未知"并停下(方法.md 第七、第九部分)。 +6. **删容器**:flow 写完结果后调用。 + +容器名由调用方给,必须是确定的,不用时间戳或随机串:恢复时要按名字去问。runner 只检查名字符合 Docker 的要求。 + +不做"取消"函数:方法里没有程序主动取消一次已启动运行的场合。用户中途停下研究时,只是不再等待;容器照常跑完,恢复时接着等。 + +### 实现要点 + +`docker run` 的参数照参考项目 `container.ts`,理由在那里的注释里: +- 显式写运行时; +- 用当前用户的 uid:gid 运行; +- 固定主机名; +- 只传 `RunDefinition.env` 里的环境变量; +- `--cpus=核数`,再加 `PYTHON_CPU_COUNT=核数`; +- 不加 `--rm`,否则中断恢复时容器已经没了; +- 把运行目录挂到 `RunDefinition.workdir`。 + +另外加 `--network none`:方法要求运行时不联网。 + +**资源用量在容器里面读,不用参考项目的 `--cgroup-parent`。** 参考项目每次运行建一个系统级的 cgroup 分组,这个分组要 root 才能删,每跑一次就留一个。新办法: +- 容器里的第一个进程是一小段 shell。它用 `timeout` 包着真正的命令;命令结束后(包括超时被杀),读容器自己的 `/sys/fs/cgroup/cpu.stat`(`usage_usec`)和 `/sys/fs/cgroup/memory.peak`,再加上墙钟时间,写进一个结果文件。 +- 监督者在本机实测过(Python 3.13 slim 镜像,普通用户,`--cpus=1`):一个 2 秒超时的 numpy 循环,`timeout` 退出码 124,读到 `usage_usec 2007945`、`memory.peak 177795072`。 +- 结果文件放在运行目录以外、单独挂进容器的一个目录里,不和候选的产物混在一起。 +- 怎么判"超时":命令自己也可能以 124 退出,所以超时要由 shell 自己确认,例如对比用时和时限,不能只看退出码。 + +**四种结局怎么定**: + +| 情况 | 结局 | +|---|---| +| `docker run` 失败,按名字再查一次,容器不存在或建了没启动(顺手删掉) | `notStarted`,原因写 Docker 的报错原文 | +| 结果文件在,命令自己结束 | `exited`,带退出码和资源用量 | +| 结果文件在,到时限被杀 | `timeout`,带资源用量 | +| 容器已结束,但没有结果文件:从外面被杀了(关机、手动 `docker kill`、shell 自己被杀) | `cancelled`;三项资源都记测不到,写明原因 | + +`docker run` 失败后按名字查到容器在跑或已结束的,按"启动了"处理。 + +**日志**:容器的标准输出和标准错误,结束后存成两个文件,存到调用方给的目录里。以后给模型看失败原因时要用。 + +## 三、tasks + +### 任务目录 + +把参考项目的 `tasks/circle_packing/` 和 `tasks/check-candidate.py` 搬到新项目的 `tasks/circle_packing/`。 + +**新增 `task.json`**:写任务本身的事实,字段名由你定。要有: +- 任务种类(现在只有填圆一种); +- 最终指标的编号和方向; +- 精度 ε; +- 已知最好,可以写 `null`; +- 单次时限、核数、Docker 运行时; +- 不许改的文件名单。 + +填圆的值: +- 最终指标 `sum-of-radii`,越大越好; +- ε = 0.01,已知最好 2.635983(方法.md 6.11); +- 120 秒、1 核(方法.md 第八部分); +- 运行时 `runc`。 + +常规辅助指标的定义写在填圆的代码里,和算这些值的代码放在一起,不写进 `task.json`。 + +**改**: +- `TASK.md`:这是给实验员看的说明。 + - 删掉"什么会被拦"一节:那是 Jev 审查运行观测量的说明,方法里已删除(方法.md 第八部分); + - "点"一律改成"因子"; + - 删掉 `RIVALBITS` 字样、"运行清单"、"调度器"这些参考项目的说法; + - 写清依赖只有镜像里那几个包,容器断网; + - 其余内容和方法.md 对不上的地方,按方法.md 改。 +- `harness.py`: + - 环境变量名改成 `INFORSI_` 开头; + - 假设专用指标的说明 `metrics-spec.json` 由 tasks 在准备运行目录时直接写好,harness 不再从环境变量生成它。 +- `validate.py`:加一个参数,把判定写进指定的文件,tasks 读那个文件;不再靠标准输出传。 +- `baseline/SOURCES.md`:去掉参考项目测试文件的说法,改成本项目里对应的测试。`baseline/` 下其余文件逐字节不动。 + +**删**: +- `research.template.json`、`research.template.md`:配置对话归 plugin; +- `__pycache__`。 + +Dockerfile 里拉基础镜像走 `dockerproxy.net`,原样保留;在 `TASK.md` 或 `task.json` 旁边写一句:换机器时可能要改这一行。 + +### 对外提供什么 + +经 `src/tasks/index.ts` 导出,函数名由你定: + +1. **读任务**:读 `task.json`,严格检查,交回任务事实;同时交回 Dockerfile 所在目录(给 runner 建镜像)和 `TASK.md` 的全文(以后 materials 给实验员)。 +2. **准备运行目录**:给候选代码目录、一个空的运行目录、这次要测的假设专用指标,把候选代码、`harness.py`、`metrics-spec.json` 放进运行目录。 +3. **正式运行的运行定义**:给任务、镜像编号、种子,交回 `RunDefinition`。 + - 种子由调用方给。同一份研究的全部运行用同一个种子:方法.md 第十部分要求评测确定,消融不重跑基线也靠这一点。 + - 种子用环境变量传给 harness。 +4. **轻量检查**: + - 给出轻量检查的运行定义:在镜像里跑 `check-candidate.py`,时长由调用方给(来自配置 `lightChecks.seconds`); + - 解读检查结果:通过,或者不通过及原因。 + - 次数上限归以后的 agents 或 flow 管,tasks 不数次数。 +5. **判分**:分两步。 + - 第一步:把运行目录里的 `solution.json` 和宿主保管的 `validate.py` 复制到一个单独的判分目录,交回校验器的运行定义。没有 `solution.json` 时不交运行定义,直接说明"没有产物"。 + - 第二步:读校验器写的判定和运行目录里的 `metrics.json`,加上正式运行的 `ProcessResult`,交回 vocab 的 `JudgedResult`。 +6. **查不许改的文件**:给改动过的文件名单,交回其中属于 `task.json` 里"不许改"的那些。 + +### 判定怎么定 + +| 情况 | 判定 | +|---|---| +| 正式运行 `timeout`、`cancelled`,或者 `exited` 但退出码不是 0 | 不算数,写明原因 | +| 没有 `solution.json` | 不算数:"没有产物" | +| 校验器判不合法:形状不对、非有限数、圆心出界、修不了 | 不算数,带校验器的原因 | +| 校验器判合法(含合法化后合法) | 算数,分数取校验器算的 | +| 某条假设专用指标测不到,且它的选法是"缺了整次作废" | 不算数 | + +另外几条: +- 消融格里假设专用指标一律只记缺失(vocab 指标说明)。调用方传进来的指标已经是按这条改好的,tasks 不需要知道这是不是消融格。 +- 校验器自己没正常跑完(结局不是 `exited`,或者退出码、判定文件和参考项目约定的对不上):这是宿主这一侧的工具坏了,报错停下,不当成候选的失败。 +- 合法化的三个数(原违反量、原分数、扣掉的分)放进 `JudgedResult.extra`,同时作为常规辅助指标的取值。修后分数高于原分数直接报错(方法.md 第八部分第 3 条)。 +- 答案路径、保障动手次数:按 vocab 的 `AnswerPath`、`SafeguardTriggers` 只记读到的原样内容,或者读不到及原因。 +- 常规辅助指标照参考项目 `protocolMetrics` 的那几项:墙钟、CPU 秒、峰值内存来自 `ProcessResult`;最小间隙、原分数、原违反量、扣掉的分来自校验器。测不到就记测不到并附原因,不记 0。 + +**vocab 的 `VERDICTS` 删掉"判不了"**(`undecidable`)。上表没有哪种情况会产生它,方法.md 里也没有这种结局。结构.md 第八部分 `VERDICTS` 那一行同步改。 + +### 留给以后 + +多个任务的接口:现在只写填圆。`task.json` 里的任务种类对应一份实现,用一个普通的对照表,不写登记函数。nanochat、AI4AI 以后按同样的形状加。 + +## 四、顺带改 config 和 vocab + +这些已写进 `WORK_LOG.md`"未完成",背景是用户 2026-10-02 定"对照组先不做"(方法.md 第十一部分、结构.md 第五部分)。 + +**config**: +- 删掉朴素循环,以及四个步骤开关:第零轮开关、第一次预测、挑刺与回应、消融。 +- 保留第零轮讨论次数(1 或 2)、选批规则名、选格规则名。 +- u、衰减挪到选格规则旁边:它们只在选格时用。λ 的位置你看着放。 +- `stepList` 跟着改:不再有"完整流程还是朴素循环",按配置跳过的只剩结构.md 第三部分说的两种(只讨论 1 次时的第 2 次讨论、只有一位研究者时的讨论)。 +- 测试跟着改。 + +**vocab**: +- 解释的 `cells` 改回必交,至少一格,字段说明里删掉"关掉消融时不交"的说法。 +- `ProcessResult` 说明里"启动后取消的记 cancelled"改成第二部分表里的含义:容器从外面被杀了。 +- `VERDICTS` 删掉 `undecidable`,见上一部分。 + +## 五、测试 + +runner 和 tasks 的测试用真实 Docker,放进 `npm test`。没有 Docker 时测试失败,不跳过。 + +- **镜像**:用任务目录的 Dockerfile 建。本机 Docker 有缓存,参考项目的镜像编号是 `7ed52793365c…`,同样的 Dockerfile 加依赖清单应当命中缓存。测试里的镜像名、容器名都用 `inforsi-test-` 开头的固定名字,跑完自己删。这台机器上还有别的项目在建、删容器,盘点时只看自己的前缀。 +- **runner**: + - 正常退出,带退出码; + - 超时:结局 `timeout`,CPU 秒接近时限; + - 命令自己以 124 退出:不能判成超时; + - 从外面 `docker kill`:结局 `cancelled`,三项资源测不到; + - 镜像编号不存在:`notStarted`; + - 断网:容器里连不上外网; + - 核数:容器里 `os.cpu_count()` 报 1; + - 查状态:五种回答各一个,"问不到"可以用一个不存在的 Docker 地址来造; + - 等结束能接上另一个 runner 实例启动的容器。 +- **tasks**: + - `task.json`:缺字段、多字段、类型不对都报错,带字段路径; + - 判分:借参考项目 `circle-validator.test.ts` 里手算的布局(宽松布局半径和 0.203125、相切布局 0.40625 等),以及差一点不合法、修不了的例子;判定表里每一行至少一个例子; + - 合法化:修后的分数不高于原分数,扣掉的分等于原分数减最终分数。 +- **一条从头到尾的测试**:用基线 `initial.py` 准备运行目录、正式运行、跑校验器、判分。 + - 分数应当是 `0.9597642169962064`:参考项目多次真实运行得到的基线分数,不是从新代码里推出来的。 + - 同一份代码跑两次,分数逐位相同。 +- 轻量检查:一份语法错的、一份没有入口函数的、一份正常的。 + +**审阅**:runner 的启动判断、四种结局、资源用量,以及判分的判定表,关系到扣次数和恢复(CLAUDE.md"工作方式")。写完后派一个全新上下文的子 Agent 审阅一轮。 + +告诉审阅者: +- 每条意见先回答"不写会怎样"; +- 模块之间传错参数这类问题只列出来,不改; +- 需要有人用 shell 搞破坏才会发生的不防。 + +审阅意见你逐条判断,采纳或不采纳都在报告里写理由。tasks 的其余部分按原型标准,不另审。 + +## 六、规模与文档 + +- 预期:runner 250–450 行,tasks 的 TypeScript 400–700 行,都不含测试;Python 文件另算。超过上限就停下来检查。 +- 完成后更新: + - `docs/结构.md` 第八部分:补 runner、tasks 对外的函数和类型,每项一句话;config 一节按第四部分改;vocab 一节里 `VERDICTS` 和 `ProcessResult` 的说法同步; + - `WORK_LOG.md`:在"已完成"补一条;从"未完成"里删掉已做的 config、vocab 两条;把 submissions 跨字段规则第 5 条改成"每条解释交 `cells`,恰好覆盖材料列出的格"。 +- 不新建其他 md 文件,`TASK.md` 和 `SOURCES.md` 是任务目录里本来就有的。 + +## 七、参考项目的保留与删除 + +| 处理 | 内容 | +|---|---| +| 借鉴 | `container.ts` 的 `docker run` 参数和理由;`circle-packing.ts` 的 `protocolMetrics`、`validateSolution` 里解读校验器输出的部分、`readLegalization`、`readMetricsFile`;任务目录的 Python 文件 | +| 删除 | `subprocess.ts` 整个文件:没有 Docker 时的备用路线,方法只用 Docker | +| 删除 | `audit.ts`、`audit-questions.ts`,以及 `circle-packing.ts` 里的观测量表、硬规则、校准记录、代码差异、审查接进判定:Jev 审查运行观测量已删除(方法.md 第八部分) | +| 删除 | `--cgroup-parent` 和父分组、`leftoverRunSlices`、`cleanupCommand`:改成在容器里读资源 | +| 删除 | `service.ts` 里的登记、预算、核销、宿主计时器:登记和扣次数归 flow;时限由容器里的 `timeout` 管 | +| 删除 | `measurement.ts` 里测量前后核对校验器和解文件字节:防的是有人用 shell 改文件 | +| 删除 | `environment-definition.ts`、`probeEnvironment`:镜像编号已经钉住环境;pip 源地址和凭据检查随"一份研究自己的依赖清单"一起删 | +| 删除 | `image-build.ts` 里"一份研究一个镜像名"、依赖清单哈希核对、建镜像记录文件:依赖跟着任务走 | +| 删除 | `TaskAdapter` 接口那 14 个方法、适配器登记表、`describeReproduction`(复现脚本留到写 export 时再说)、`researchBrief`、`formalRunProfile` | + +## 八、验收与报告 + +做完后本地提交,然后在回复里交一份报告,监督者会对着实际文件逐项核对: + +1. 提交号,新增和修改的文件列表,每个文件的行数,runner、tasks 各自的总行数。 +2. `npm run check` 和 `npm test` 的完整输出,以及 Docker 测试总共花了多久。 +3. 从头到尾那条测试的实际输出:分数、两次是否逐位相同、四项资源用量。 +4. `task.json` 全文,`TASK.md` 改动的摘要。 +5. 审阅意见逐条:内容、采纳或不采纳、理由。 +6. 你和这份任务书、结构.md、方法.md 不一致的地方,以及为什么。 +7. **规则不清楚的地方**:读 CLAUDE.md、结构.md、方法.md 时,哪里让你犹豫、哪里两份文件说法不一致、哪里你是靠猜的。没有就写"没有"。