# 给改这个仓库的 Agent 先读 [docs/protocol.md](docs/protocol.md) 和 [架构图](docs/architecture.svg):它们说明最终实现。 ## 原则 - **内核只收、只分、只派、只记。** `kernel/` 不主动连接任何东西,不知道任何产品的名字。`tests/purity.test.ts` 守着这几条,也守着 host、protocol、worker 不认识具体产品。新的产品、新的连接方式都是外面的插件。 - **没有界面。** 中台只有口子:事件插件、接单程序、通道、本人。总览页、大屏都是别人用这些口子做的客户端,不进这个仓库。唯一的页面是卡片链接:一个不跑脚本的表单,给只会发文字的通道用。 - **协议是产品。** 改协议就是改所有插件的约定:先核对 `docs/protocol.md` 与现有行为测试,改了要写清兼容。 - **文字是资料,规则才是指令。** 事件里的字不进提示词,也影响不了交给谁、在哪、用什么权限。 - **一轮不等人。** 要问人就结束这一轮。不加任何让 Agent 阻塞等待的东西。 - **没有运行时依赖。** Node 24 自带的够用。`package.json` 不写 `dependencies`。 - 代码只写现在需要的。加锁、重试、兜底,要有真实事故或接口文档作依据。 ## 怎么改 - 测试写在实现之前,每个测试文件开头先写它防的失败方式;修 bug 先写复现它的失败测试。一个行为一条测试,边角情况不单写。 - `npm test`、`npm run typecheck` 和 `npm run format:check` 都要过。没动 `kernel/`、`protocol/`、`sim/` 时,`npm run test:quick` 跳过模拟和穷举,其余测试照跑。 - CI 由 `scripts/ci-scope.sh` 按改动范围分流:纯文档全部跳过;其他代码的 PR 跳过模拟和穷举;`kernel/`、`protocol/` 和模拟本身的 PR 另跑 2 万步模拟(`npm run test:sim`);只有改 `tests/simulation.test.ts`、`sim/enumerate.ts` 的 PR 跑完整检查,别处跑不到它们。合进 main 后都再跑一遍快速测试和安装冒烟,跑起来的这一遍不会被后面的推送取消;还在排队的由更新的那一遍接替,它覆盖前面所有提交。`nightly` 工作流每天跑一次 20 万步模拟、穷举、强杀和变异,并用最新的 Node 跑快速测试和安装冒烟;改 `sim/` 或它自己的 PR 只跑强杀和变异。让测试读取某份文档时,同步改这个脚本。 - `npm test` 跑固定种子的 20 万步模拟与有界穷举;涉及日志、到期表或并发时还跑 `npm run test:nightly`。变异必须先证明原版测试实际执行且通过,不拿语法错误当检出;漏过任何一处都算失败。 - 升级不写数据迁移。事实日志是唯一的权威状态;投影及索引在同一事务内持久化,正常启动复用,格式指纹变化时从日志逐条重建;查询视图的列写在 `host/state.ts`,每次启动重建,不按版本加迁移文件。 - 面向用户的字用中文大白话;代码注释用英文,只写代码看不出来的原因。 ## 社区贡献 - 外部 PR 尽量合:要改的地方维护者直接在对方分支上改好再合,保留 PR 作者,不让对方反复修改。方向不对或合不了才关闭,并说明原因。 - 外部 Issue 能做的由维护者修,不等对方提 PR。提交写 `Fixes #编号`,用 `Co-authored-by: <编号>+<账号>@users.noreply.github.com` 署上报告者。 ## 审查要点 改 `kernel/`、`worker/`、`protocol/`、`host/` 的 PR,合并前让一个没写这段代码的 Agent 审一遍:只给它代码,不给作者的结论。它要回答三句:改的函数还有谁在用;接外部程序的有没有真实运行记录;同类问题别处还有没有。 PR 一开出来就开审查,几个 PR 同时审,不排队。审查报的问题只修必须修的。必须修只算两种:真实用户大概率会碰到的,或者踩了下面这份清单的。几个罕见条件凑在一起才发生的记下来,有人碰到再修,不排进下一批;假程序复现不等于事故,没有真实事故依据的兜底不加。 审计只审真有人在用的功能,结果先交清单,不默认当批全修。 这些算出错: - 没有密钥、拿错种类的密钥也能进门;一个来源能以别的来源的名义发事件;一轮的 token 能碰别的轮次;通道的回答没核对回答者和原消息。 - 事件里别人写的文字进了提示词,或者能影响交给谁、在哪个目录、用什么权限。 - 同一条事件被记两次或派两次;同一组同时跑起超过规定的轮数;没跑成的活丢了、马上重派、或没和期间新来的同一事项的事件合在一起。 - 回答没叫醒提问的事项,或者一张卡片的回答被算了两次;回答和结案都改变了同一张卡片。 - 回执写错导致业务重做。 - 接单程序丢了跑完的结果、把还在跑的当成跑完、把接上原会话后断了的一轮换新会话重做、停止时留下子进程。 - 内核里出现了对外请求、产品名字或界面。 - 卡片页面把别人写的字当成标记显示,或者打开链接就算回答。 - 老插件、规则或配置不改就跑不了,这一版却不是第一位版本(见[协议](docs/protocol.md#版本与兼容))。 ## 一批怎么收尾 改动按批做。开工时定下这批的清单,中途的新发现记到下一批。这些都做完才算完: - `main` 的检查和每日检查都过。 - 相关 Issue 的状态都说对了。 - 工作树和临时分支清掉。 - 让 Codex 审一遍这批的全部改动,查出的问题留在这一批修。第一个改内核或测试的 PR 合并后就跑一遍每日检查和这次审查,不放到最后。 一批做完不跟着发版,改动攒到下一个常规版。 ## 怎么发版 常规改动一天最多发一版,崩溃、丢数据、安全问题、装不上的修复随时发;版本号怎么加见[协议](docs/protocol.md#版本与兼容)。版本号只写在 tag 上,不进任何文件。选一个 `main` 的检查已经通过、真实跑过并核实过的提交,在它上面跑每日检查(`gh workflow run nightly.yml --ref main -f commit=<完整提交号>`,20 万步模拟和穷举也在里面),过了再把 tag 打在它上面,写清这一版改了什么。发出去的 tag 不挪不删。 ## 内核边界 `decide(view, input, now, environment)` 只能产生事实;判断和提交在同一事务中,视图按索引读取,判断只保留这次的改动;`evolve` 独占持久投影写入,`commit` 独占日志追加。内核不读时钟、不取随机数、不读环境或文件。时间、规则变更、主密钥和请求从外壳传入;`host/http.ts` 只把 Node 请求变成 Request。视图随时从日志重建,不能绕过日志改业务数据。规则版本、回答、回执都能用 `why` 和 `/v1/stream` 读到。 ## 插件与交付边界 - 内置与第三方插件统一为 `plugins/<名字>/{agent,channel,source}.ts`,安装目录优先,内核不导入插件。命令行在 `cli/`,接单程序在 `worker/`;不要再加另一套发现路径。 - 外部事件只能带资料。Agent、目录、权限、交付仓库、审查命令由可信规则与项目 main 决定。回执事件继承 hops,超过 8 层留记录但不继续派发。 - 交付进程不能检出或运行 PR 的代码、测试、配置、hooks;只能把裸 Git 对象变成文字快照。默认 Codex 审查忽略用户配置与记忆,保留快照只读沙箱。 - 队列、并发、失败计数和通知属于中台;交付插件只走审查、检查、合并、Ship、健康核实,不再维护另一套调度。 - `ops/delivery.json`、四个交付标签和 `delivery` 状态保持约定,审查状态与审查命令可配置。Ship 判断放本仓库 Action,依赖的 GitHub Action 固定到完整 SHA。 - 必须保住的事故逻辑,只收真实运行或真实用户碰到过的:设置只来自 main;分支自己的改动才审查;冲突解决保留;草稿检查不放行;提交或 main 变化不合并;自动合并确认关闭;丢失的合并响应读回;Ship 成功与健康版本一起验证;后续文档提交不掩盖未发布代码;已完成的交付不被后续发布倒退。 - 交付的其他约定,没有事故依据,要调整时直接改:发现问题的提交不再审第二次;看不到内容的章鱼合并不放行;只审查的 PR 不因清标签失败被合并;目标分支改了不合并。 - 改同一个约定时一起检查代码、协议、示例、两份 README、INSTALL、SKILL、SECURITY 和架构图。搜索旧路径、旧名称、旧数量和旧的能力限制。设计直接写进协议和维护约定。 ## 验证范围 现有测试是行为规格,不为迁就实现改预期。模拟、穷举、强杀和变异检查在本地只在相关改动需要时运行,不扩写验证框架。新测试先写失败方式再写实现,不补复述实现的测试。性能只记录实测,不设达标线,不作为发布门槛;不为加速模拟保留另一套内核。实测采用 README 和协议的口径:空闲内存与千连接内存分开,持久化写入不换成批量提交。不写守数字的测试(行数、内存、检查条数):边界用查越没越界的测试守,长了多少写进汇报让人判断。 真机脚本在 `scripts/real-agents.ts`、`scripts/real-protocols.ts` 和 `scripts/delivery-e2e.ts`。真实交付脚本要求 `owner/name --run`,会创建并删除指定私有测试仓库;只有用户明确要求运行时才执行。其他测试使用临时 `AGENT_AUTOLOOP_HOME` 和随机端口,退出清理。飞书用假 CLI;不能拿已在线的应用再开一个事件监听。通道用本地假 HTTP 服务,不发真消息。桌面通知用放在 PATH 最前面的假命令,不真的弹:`init` 之后再跑 `up` 的测试都要带上。`scripts/install-smoke.sh` 在临时 HOME 里走一遍从安装到卸载,装的是已提交的代码,CI 在 ubuntu 和 macOS 上都跑;Windows 用 `scripts/install-smoke-windows.ts`,共用行为和原生进程检查用 `npm run test:windows`。Unix shell、Homebrew 与服务模拟仍在 Unix 验证,Windows 原生安装、通知和进程树由专用测试覆盖;登录服务在真实桌面会话验证,CI 不注册用户任务。