# 任务书 01:vocab(共用词汇)与工具链 这是 InfoRSI 的第一个代码任务。项目里现在只有文档,没有代码。这个任务做两件事: 1. 搭好工具链:npm 包、TypeScript、测试、依赖方向检查; 2. 写 vocab 模块:各模块共用的类型、名单、提交字段定义,以及两个函数。 本任务书的内容已由用户和监督者在 2026-10-01 一起确定。下面说的"监督者",是在参考项目目录里和用户讨论设计的另一个会话。 ## 开工前先读 - `CLAUDE.md`:全部。 - `docs/结构.md`: - 第一部分(切分原则); - 第二部分(模块表,以及表下面讲 vocab 的那一段); - 第六部分(模块化程度与检查); - 第七部分(规模)。 - `docs/方法.md`: - 第二部分(核心概念); - 第三部分(角色); - 4.2、4.3(流程里的各个阶段); - 第五部分(每种提交,**这是提交字段的唯一依据**); - 第七、八部分(运行要记录什么)。 - 参考项目(只读,不要改):`/home/iomgaa/Projects/RivalBits` - `src/core/types.ts`:旧的共用类型。下面"保留与删除"一节说明哪些要、哪些不要; - `src/rounds/submissions.ts`、`src/rounds/task-submissions.ts`:旧的提交字段,里面混着收件检查。只借字段本身,按方法.md 第五部分重写; - `package.json`、`tsconfig.json`:版本号可以参考。 ## 一、工具链 - 建一个 npm 包,`"type": "module"`,Node ≥ 22.19(本机是 22.22.2)。 - 这次只装这次用得到的依赖:`typebox` 1.3.7、`typescript`、`@types/node`、`dependency-cruiser`。 - Pi 的包等写 agents 模块时再装。 - Pi 0.85.1 用的也是 `typebox` 1.3.7,这次定的字段定义以后要直接当 Pi 工具的参数用,所以版本必须一致。 - `npm run check`:TypeScript 类型检查加 dependency-cruiser,两项都要过。 - `npm test`:用 Node 自带的 `node --test`。 - dependency-cruiser 把 `docs/结构.md` 第六部分列的规则全部写进去,模块按 `src/<模块名>/` 的目录来认: - 依赖方向按第二部分的模块表; - 不许循环引用; - 不许绕过 `index.ts` 直接引用模块内部的文件; - rules 和 vocab 不许引用文件、数据库、网络、子进程和 Pi; - 只有 ledger 能用 SQLite,只有 agents 和 plugin 能用 Pi,只有 runner 和 workspace 能起子进程; - tasks 不许引用 runner。 现在只有 vocab 一个目录,但规则要一次写全,后面的模块建目录就自动受检查。 - 要证明这些规则真的会报错,不能只是写了配置:临时写几处违规的引用(比如 vocab 引用 `node:fs`、一个模块绕过 `index.ts`),把 `npm run check` 的报错输出记下来,然后删掉这些违规引用。 ## 二、vocab 放什么 判断标准(来自结构.md):一样东西要有两个互相不能引用的模块都用到,才放进 vocab;只有一个模块用的,放在那个模块里。上限 600 行,预期 450–600 行。 建议分四个文件,由 `src/vocab/index.ts` 统一对外。文件名和英文标识符你来定,以读得懂为准。 ### 1. 基础 - `Json` 类型。 - "测到 / 测不到"的取值:测到就带值,测不到就带原因。全项目凡是可能测不到的量(资源用量、分数、指标、价格)都用它,不用 0 或 null 冒充测不到。 - 指标方向:越大越好、越小越好,只有这两种。 - 编号的格式:只允许字母、数字和 `._-`,长度有限。编号会被拼进文件路径和命令行,所以要限制。写成一条 TypeBox 的字段规则,各字段定义里复用,不写成函数。 - 两个函数: - **规范序列化**:对象的键排序后再拼;数字必须是有限数;只接受纯 JSON 对象。这样内容相同就一定得到同一个字符串; - **哈希**:对规范序列化的结果算 sha256。 这是 vocab 里仅有的两个函数。 ### 2. 名单 - **三个角色**:研究者、实验员、核验员。 - **研究者阶段和提交种类**:按方法.md 4.2、4.3 和第五部分列出。每种提交写清两件事:属于哪个阶段、由哪个角色交。这份对应关系写成一张常量表,以后 ledger、materials、submissions、flow、agents 都从这里取,不在别处另写。 - 监督者整理的对应关系如下,作参考。以方法.md 为准,对不上时按方法.md 来,并在最后的报告里写出差别: | 提交 | 阶段 | 角色 | |---|---|---| | 入口 | 写入口 | 研究者 | | 讨论 | 讨论 | 研究者 | | 志愿 | 认领 | 研究者 | | 提案 | 提案 | 研究者 | | 第一次预测 | 第一次预测 | 研究者 | | 挑刺 | 挑刺 | 研究者 | | 回应 | 回应 | 研究者 | | 统一 | 回应 | 研究者 | | 第二次预测 | 第二次预测 | 研究者 | | 实现 | 实现 | 实验员 | | 修复 | 实现 | 实验员 | | 核验 | 核验 | 核验员 | | 消融实现 | 消融实现 | 实验员 | | 消融核验 | 消融核验 | 核验员 | | 解释 | 解释 | 研究者 | | 收尾 | 收尾 | 研究者 | - 程序自己做的步骤,比如判重、选批、采纳、选格,不是提交阶段,不放这里。步骤清单归 config。 - 私人笔记不在这次范围内。 ### 3. 运行与指标 - **指标定义**,包括:编号、层次(最终 / 常规辅助 / 假设专用)、含义、单位、方向、怎么采样和汇总、"测不到时怎么办"。 - "测不到时怎么办"只有两档:缺了只记缺失;缺了整次作废。 - 每一档的后果用一句白话写在常量里,字段说明和给模型看的材料都用这一份。白话的内容见方法.md 第五部分"指标"。 - **运行定义**:tasks 交给 runner,告诉它怎么起这个进程。要能回答这几件事: - 命令和工作目录; - 镜像:按 sha256 钉死,不接受 tag;运行时必须写明; - 限时、核数; - 环境变量:候选进程**不继承**宿主的环境变量。类型上只允许写 false。 - 原因:参考项目实测过,宿主环境被污染时,同一份代码的分数会从 0.9598 悄悄变成 0.8849,退出码是 0,没有任何报错。这属于"出错后无法察觉"的那一类。 - 参考项目里环境变量分成两块("协议那块"和"逐候选那块"),是为了算两个"可比条件哈希"。那两个哈希这次删掉了,所以这里只要一块。 - **运行结果**,分两层: - runner 交出的进程层面结果:有没有启动;以什么方式结束(正常退出、超时、被取消、没启动);退出码;CPU 秒、墙钟时间、峰值内存。资源用量都可能测不到,用"测到 / 测不到"表示。 - tasks 交出的判分结果: - 判定:三选一,算数 / 不算数 / 判不了,附理由; - 分数; - 各指标的取值; - 答案路径和各条保障动手的次数(方法.md 第五部分"答案路径")。读不到,或者读到的取值不在位置表里,都要有明确状态; - 一份任务自己的附加记录,任务想记什么就记什么,比如填圆的合法化。 - **模型用量**:普通输入、缓存命中、缓存写入、输出四种 token 数,加上价格。价格可能测不到。 ### 4. 提交字段 - 按方法.md 第五部分,每种提交写一个 TypeBox 字段定义。同一份定义要同时当三样东西用:类型、收件时的格式检查、发给模型的提交工具参数。 - 字段说明也写在定义里,它会原样发给模型。 - 方法.md 要求每个字段都写明出口,比如"只有一个因子就写一个"、"不适用:原因"、"没有:为什么"; - 说明用中文。 - **三个数**是一个共用的定义:q₁、q₅、q₉ 三个有限数。 - 正文统一是一个 markdown 字符串字段,程序不拆它。 - 不需要写的字段: - 只存不读的栏目、程序自己推得出的栏目,不设(方法.md 第五部分总原则); - 编号由程序分配的(候选编号、挑刺编号),不放进模型要填的字段。 - 跨字段的规则**不在这里写**,留给以后的 submissions 模块。比如:q₁ ≤ q₅ ≤ q₉;因子数不超过上限;同一份提案里两个因子不能延续同一个因子;解释最多 M 条。字段定义只管单个字段长什么样。 ## 三、参考项目 `src/core/types.ts` 的保留与删除 | 处理 | 内容 | |---|---| | 改写后保留 | `Json`、`Measurement`、`Direction`、`MetricDefinition` 与两档"测不到时怎么办"、`ModelUsage`、`RunResult` 里的启动状态和资源用量、`canonical`、`hash`、运行定义里"镜像按 sha256 钉死、运行时写明、不继承宿主环境变量"这几条 | | 删除 | `Lease`、`StudyStatus`(改用文件锁;进度从账本现算) | | 删除 | 观测量表、观测量四组、`HardRule`、`validateObservable*`、`validateObservationSet`、`hardRuleVerdict`(不再用模型审查观测量;硬规则校验以后放进 tasks 内部) | | 删除 | `comparisonConditionsHash`、`scoreConditionsHash`,以及为它们分出的两块环境变量 | | 删除 | `assert`、`finite`、`positiveInteger`、`nonempty`、`identifier`、`validateManifest`、`validateMetric`、`validateRound`、`validateLimits`、`thresholds`(改用 TypeBox 字段定义检查) | | 删除 | `Thresholds`、`StudyLimits`、`ComputeLimits`、`processEnvironment`、`collectedMetrics`、`worstCaseCost`、`RoundDefinition`、`SYNERGY_LABELS`(都只有一个模块用,等写那个模块时放进去) | | 不搬 | 注释里的修改历史和"参考项目以前怎样"的说明。注释只写现在 | `assertNever` 这类只有一两行的类型辅助,如果几个模块确实都要用,可以放进来,但要先说清楚哪几个模块会用它。 ## 四、测试 - vocab 基本没有逻辑,字段定义不写逐字段的测试。 - 规范序列化和哈希各写测试。预期值要独立得到,不能拿实现算出来的结果当预期值: - 规范序列化的预期字符串,自己手写; - 哈希的预期值,用命令行的 `sha256sum` 算。 - 至少覆盖这几种情况:键的顺序不同,结果相同;嵌套数组;遇到 NaN 或 Infinity 报错;遇到带自定义原型的对象报错。 - 每种提交挑一份合法的样例,确认能通过字段定义的检查。这一步也是在检查字段写得是不是方法.md 第五部分的意思。 ## 五、完成后要更新的文档 - `docs/结构.md` 第八部分:补上 vocab 对外的类型、名单和两个函数,每项一句话。 - `WORK_LOG.md`:按它的格式更新当前状态。 - 不新建其他 md 文件。 ## 六、验收与报告 做完后本地提交,然后在回复里交一份报告,监督者会对着实际文件逐项核对: 1. 提交号,新增文件列表,每个文件的行数,vocab 的总行数。 2. `npm run check` 和 `npm test` 的完整输出。 3. 临时违规引用时 `npm run check` 的报错输出(见第一部分最后一条)。 4. 你和这份任务书或方法.md 不一致的地方,以及为什么。 5. **规则不清楚的地方**:读 CLAUDE.md、结构.md、方法.md 时,哪里让你犹豫、哪里两份文件说法不一致、哪里你是靠猜的。这一条对这次任务特别重要,没有就写"没有"。