# CHANGELOG 语义化版本见 `VERSION`(当前 0.1.0)。以下按日期倒序,只列已交付 的大项,每项附实测证据;未建成的东西在“诚实边界”里明说,不夸大。 ## 2026-10-26:收尾(Docker 沙箱实测 + 云端合并状态) - Docker 沙箱执行器接通并在外部主机上真实跑通:镜像 `codex-app-server:latest`(codex 0.164.0-alpha.9,codex-resources 必须放在 codex 二进制旁边、且 bwrap 进 PATH,否则容器内报 bubblewrap is unavailable)、worker `--executor docker`(额外 挂载 `/app/work` 与 `/var/lib/codex-app-server`,容器用户取 宿主服务用户 uid/gid)、Docker 任务 triple 加函数约 18 秒完成、 容器内合并后主仓库含 add + mul2 + triple、容器经 --rm 清理。 诚实边界:这台主机/内核禁非特权 user namespace,bwrap 在 主机和容器内都起不来,因此生产模式是 Docker 隔离 + 容器内 danger-full-access(host-config-docker.json),不是双层沙箱。 - 云端合并状态:新增 `POST /tasks/{id}/merge`,只同步状态、 不执行合并(合并仍由 host.py merge 执行,成功后再调本接口); 仅 completed 可改为 merged,maintainer 以上(approve_merge), 其它状态与重复调用返回 409,越权 403,审计记 merge_task。 - 实测证据:云端测试 219 个、host 测试 27 个、合并状态单测 4 个,全部通过(`python3 -m unittest discover -s cloud/tests` 与 `python3 -m unittest discover -s tests`)。 ## 2026-10-26:缺口补齐(云端控制面第二批) - 记忆 / KB / 成本 / 模型接成 HTTP 端点:记忆写/查/ supersede、 KB 索引/搜索(按项目隔离)、按任务与按项目/按日的成本(实际与 估算分开)、模型总览;全部走 `X-API-Key` + 项目 RBAC, 401 / 403 / 404 语义与跨项目隔离均有测试。 - SQLite 并发:共享 db 助手(WAL + busy_timeout + 有界重试), 并注明单机上限,Postgres 是下一阶段,不是本版已做。 - OAuth 脚手架:`cloud/oauth.py`,state 10 分钟过期、生成授权 URL、 回调验 state 后用可注入的 HTTP 客户端换 code;secret 只走环境变量, 没有假登录页,真实 OAuth 应用注册仍缺。 - 推送订阅与分发:`cloud/push.py`,webhook / slack / webpush / apns / fcm 通道,即时/摘要/静默偏好,只发状态变更事件;缺发送方/ 凭证时报 error,绝不静默报成功;真实推送凭证未配、真实推送未发。 - 实测证据:云端测试从 102 个涨到 157 个,全部通过 (`python3 -m unittest discover -s cloud/tests`); host 测试同期 27 个,全部通过 (`python3 -m unittest discover -s tests`); 另有一次 live smoke:记忆写 201/读通、KB 索引/搜索命中、 OAuth 登录 URL 只返回不跳转、推送分发报“无发送方”错误、 无 key 查项目返回 401。 ## 2026-10-26:云端控制面(第一批) - 新 `cloud/` 包,零第三方依赖、只用 Python 标准库: `server.py`(ThreadingHTTPServer + SQLite 状态,任务状态机与 host.py 对齐,含心跳与 stale 回收)、`auth.py`(API key 只存 SHA-256 哈希、按项目 RBAC owner / maintainer / contributor / viewer、追加式审计日志)、`queue.py`(依赖拓扑 + 优先级 + 预算门)、`sandbox.py`(Local / Docker / Cloud 三种执行器, 缺 Docker 或缺云账号时明确报错、不静默降级)、`memory.py` (线程摘要,可删/可 supersede,只取 top-k)、`kb.py` (SQLite FTS,LIKE 兜底,按项目隔离)、`models.py` (404 / 429 / 5xx / 超时才 fallback,不对 401 / 400 fallback, 实际用到的模型如实记录)、`cost.py`(读不到账单时返回 available=false,估算值明确标 estimated,实际与估算分开)、 `webhooks.py`(GitHub HMAC + Slack 签名与重放窗口,只解析, 只在状态变更时推送)。 - 实测证据:云端测试 102 个、host 测试 27 个,全部通过; smoke:health 正常、无 key 查项目返回 401;代码里无密钥。 - 诚实边界:这是本地可跑的控制面演示,不是已建成的真实云服务; 仍缺常驻宿主、GitHub 远端 + PAT、Slack App、云容器账号、 定时/worker 跑模型时的模型 key 供给方式、真实 OAuth 应用注册、 真实推送凭证;server 本身不跑模型回合。 ## 2026-10-26:GitHub PR / CI 一条线 - `host.py github-workflow`:生成 `.github/workflows/host-verify.yml` (checkout + python + unittest),幂等。 - `host.py pr`:校验任务已完成且有分支,push 分支 + `gh pr create`, 成功后把 pr_url / pr_number 写回任务记录。 - `host.py pr-checks`:解析 `gh pr checks`,all_pass 仅在 checks 非空且全部 pass 时为真,空 checks 不算过。 - `host.py pr-merge`:先查 checks,未全过拒绝且不调 merge, 全过才 `gh pr merge --merge` 并标 merged,不动本地主分支 (与本地 `merge` 是两条线)。 - 实测证据:全部 gh 调用走可 mock 的 run_gh;测试从 19 个涨到 27 个,全部通过,含本地 bare 远端 push + 假 gh PR(写回编号 42) 与 workflow 幂等。 - 诚实边界:真实 PR 本轮没跑——本仓没有 git remote,gh 未登录; 真实 PR 仍缺远端与经 Secure Vault 的 PAT,那两样齐了才能跑。 ## 2026-10-26:第二阶段(daemon / health / 自动恢复) - `host.py daemon`:双 fork 到 PID 1,pidfile + host-daemon.log, 每轮跑 recover + run-due(各为独立 host.py 子进程); `daemon-status` / `daemon-stop` 配套。 - `host.py health`:不跑模型,只查二进制、配置、state 有效性、 备份、daemon pid、server initialize + list、缺失 worktree; 失败时 exit 1。 - `host.py restore-state`:state 损坏/丢失时从 .bak / .bak.1 / .bak.2 恢复,损坏文件改名为 .corrupt.<时间戳> 留证。 - `host.py recover`:超过 `--older-than`(默认 3600 秒)未更新的 running 任务标 interrupted 并记日志,可用 `run` 续跑; 故意不自动重跑模型回合,避免未经批准花 relay 额度。 - 附带修掉一个静默丢数据洞:旧 state 读取把 JSON 解析错误 当空 state 吞掉。 - 实测证据:daemon pid 的 PPID 为 1,测试中每 5 秒 tick、 stop 后 pidfile 删除;health 先 ok=true(7 个 server 线程), 损坏 state 后 ok=false / exit 1,restore 后 ok=true(备份版本 9); recover 把 2 小时未动的 running 标 interrupted、1 分钟的保留; 测试从 15 个涨到 19 个,全部通过。 - 诚实边界:daemon 本身活不过平台 VM 回收(与微信桥 serve 同理); VM 上更稳的定时是 Muse cron 调 run-due / health,不是只靠 daemon 或 crontab;本轮没有自动装 Muse cron 或 crontab,因为 run-due 到期会花模型额度,装定时需用户明确指定间隔与是否允许自动跑模型。 ## 2026-10-26:第二类缺口(全部补齐) - 合并自动验测 + 回滚:`merge` 默认仓库有 tests/ 就跑 unittest, 验测失败 reset 回合并前 HEAD,分支/worktree 保留; `--no-verify` 跳过,`--verify` 可指定命令。 - 任务依赖 + 优先级 + 自动重试:jobs 里 depends_on / priority, 拓扑排序 + 按波次跑,依赖失败时依赖任务标 skipped 不跑; 依赖任务从依赖分支建 worktree(有血缘关系),不再文件拷贝; dispatch / parallel 里 `--max-retries`,失败后同 prompt 再跑, 重试前查预算,默认 0、行为不变。 - 评审评论回流:`review-comment --file --line --comment` 把评论 存进任务,`review` 列出 open 评论,`fix-review` 按 open 评论 逐条修、修完标 resolved。 - server 崩溃重连:Server.ensure_alive,死了重拉并 resume thread (thread 在 CODEX_HOME 里不丢);request 遇 BrokenPipe 重试一次; 单测 kill 进程后重拉 + list 能通。 - 成本预算 + 失败通知:配置 max_daily_turns,按 turn 数算 (协议不暴露真实 token 数,这是诚实边界),超预算不派发/ 不重试;usage 记在 state 里,`doctor` 可看;失败时写 host-run.log + 跑配置的 notify_command(本地 shell, 模板 {task} {status});`failures` 列失败任务,`doctor` 列 失败/缺失 worktree/open 评论。 - 多机器 state 版本:state 加 version / last_writer / updated_at, 保存时磁盘版本比加载时新就记 STATE-CONFLICT 日志并合并保留 双方任务,不静默丢;真多机中心同步仍是检测、不是云同步。 - 合并时不再提交缓存文件:merge 前清 worktree 的 __pycache__ / *.pyc,主仓的 .pyc 改动先还原;单测覆盖 pyc 不入库。 - 实测证据:测试从 3 个涨到 15 个,全部通过;模型验证在 gap2 / gap3 仓上跑通(依赖并行、评审评论修复、带自动验测的合并), 并在实测中拦下两处真问题:文件拷贝依赖分支导致重复加同一段的 合并冲突(-X ours 兜底的新增行核对拦下一次 mul2 丢失并回滚, 后改从依赖分支建 worktree 修复)、merge 的 git add -A 提交 __pycache__ / *.pyc(已修)。 - 诚实边界:独立任务在同一文件位置追加仍会合并冲突,这是 git 自身行为,host abort / 回滚、不猜合并结果;多机共享 state 现在能检测、不静默丢,但 ChatGPT 云端式中心同步不在本地 host 范围内。 ## 2026-10-26:生产加固 - state 文件锁(fcntl,只锁短读/写;保存时按任务/自动化名合并 磁盘状态,多进程并发写不丢对方任务;双进程 0.5 秒窗口竞写实测 双方任务都保留)。 - 项目配置 host-config.json(`init` / `config` 命令,CLI > 配置 > 默认;API key 绝不进配置文件,只走环境变量)。 - 定时自动化:run-due 在 host-run.log 记派发/跳过, `automations --cron-line` 只打印 crontab 行不装机, `--install-cron` 幂等装机;本轮测试里没有实际装 crontab, 避免未经批准的模型花费,且 VM crontab 可能活不过平台回收, Muse cron 调 run-due 是更稳的 VM 选项。 - 诚实边界:锁只防同一台机器,不防两台机器共享同一 state 文件; host.py 本身不常驻,仍需外部调度器触发 run-due。 ## 2026-10-26:真实项目跑一轮(在打包前) - 在 codex-app-server 本仓(非敏感的 wechat-bridge)上 dogfood: 先 git init(初始提交,.gitignore 排除 367M bin/、work/、 host-state*.json、host-run.log、本地 host 配置),真实任务用 单独的 host-state-real.json。 - 并行真实任务:real-tests 加 tests/test_host.py(3 个 unittest, 全部通过,host.py 本身未改);real-backup 加 save_state 备份轮转 (.bak / .bak.1 / .bak.2,在锁内)。 - 只读委派评审 real-backup 后发现多步轮转非原子、有截断拷贝风险、 无 fsync、fcntl-less 平台问题;便宜的修复已在 fix 回合里落地 (with open 读 state / 写临时文件、修掉 real-tests 发现的 ResourceWarning、备份经临时文件 + os.replace 拷贝)。 - 两任务都合并进 main,main 测试全过且无警告,手动双保存确认 .bak 里是上一版。 - 真实项目才有的教训:非 git 项目要先 git init 才能用 worktree / 合并门;缺 .gitignore 会把 bin/ 提交进去; 真实任务与玩具任务必须用不同的 state 文件。 - 记录在 TEST-REPORT.md;本轮打包前仍欠的下一步是:安装文档、 host 快速开始、版本/变更日志、合并后主仓自动验测(那一步当时 仍是手动)——其中合并后自动验测已在“第二类缺口”里补上, 安装文档与版本/变更日志由本 CHANGELOG 与 install.sh / README 快速开始补上。 ## 2026-10-25:host 编排(多线程并行、worktree 隔离、评审/合并门) - 单个 app-server 进程托管多 thread,后台读线程按 threadId 分发 turn / completed,多 thread 的 turn 真正并发,不是轮流排队。 - `dispatch` 自动 `git worktree add -b host/`,一任务一 worktree;非 git 仓库退化为独立目录拷贝。 - `review`:status + diff + untracked;`merge`:先在 worktree 提交,再 `--no-ff` 合并进主仓,冲突自动 abort; `discard`:删 worktree 和分支。 - `run`(resume + 新回合)、`fork`、`archive`、`list`; `delegate`:子 thread(默认 read-only)跑完,结果可 `--report-to-parent` 汇总回主 thread。 - `goal`:同一 thread 多回合,直到 `--verify` 验收命令通过 或到 `--max-rounds`。 - `automations --add` + `run-due`:到期任务自动派发,结果进 host-state.json 评审队列;未到期不重复派发(幂等实测)。 - 边界实测:read-only thread 的写文件请求被拦下、文件确实没建; untrusted + deny 时 agent 收到审批拒绝、没拿到命令输出; 模型内 3 子代理并行检查视角确实不同;turn/interrupt 后状态 为 interrupted,`compact` 需先 resume(host.py 的 compact 命令 已包好这一步);合并冲突时主仓干净、冲突 worktree 留着人工解; 隔离 CODEX_HOME 下的 MCP 工具能被发现并调用(MCP 审批是 elicitation 协议、不是通用 decision 字段,host.py 已分流处理, 修过一轮才通);同一 thread 内换模型续跑、turn 的 model 字段 随之改变;turn/steer 能把进行中的长任务改短;3 并行 worktree 互不污染;review 时 diff 为空也要看 untracked;不存在的模型 导致 task failed 并带上游 404 错误、host 不崩。 - 工具执行修复(VM 专属):系统裸 codex 二进制缺两个同伴 (codex-code-mode-host 与 codex-resources/bwrap),工具会 fail closed;配对目录 `bin/`(codex + codex-code-mode-host + codex-resources/bwrap,约 367M,官方 0.164.0-alpha.9)经 start.sh 优先使用;relay 支持的模型回合实测 10.3 秒内在 work/xcm-test/test-result.txt 写出“codex-ok”。Mac 上正常 npm 安装自带这三件套,不需要 bin/。 - 协议层:start.sh 启动官方 codex app-server(0.164.0-alpha.9), 零依赖 Python stdio JSON-RPC 客户端(initialize / thread/start / turn/start、审批、resume / fork),宿主级并行多 thread 编排, 配置示例与官方协议 schema(generate-json-schema 导出)。 隔离 CODEX_HOME 下验证到模型边界为止:initialize / thread/start / turn/start 与事件流都通,turn 随后因缺模型登录返回 401 failed, 这是登录边界、不是协议 bug。 - 诚实边界:云端派发、推送通知、Slack / GitHub 触发、GUI inline diff 评论、Computer Use 都不在开源里、没有复刻;本版是本地 host + 本地可跑的控制面演示,不是真实云服务。 ## 诚实边界(贯穿所有版本) - 没有建成真实云服务:cloud/ 是本地可跑的控制面演示, 生产仍缺常驻机器、GitHub 远端 + PAT、Slack App、云容器账号、 模型 key 供给方式、真实 OAuth 应用注册、真实推送凭证。 - 没有真实 PR、没有真实推送:PR 只做到代码 + 本地可测部分 (假 gh、本地 bare 远端);推送只做到订阅与分发逻辑, 缺凭证时明确报错。 - 没有真实 token 成本:协议不暴露真实 token 数,预算按 turn 数算;成本端点里实际与估算严格分开,估算值标 estimated。 - 没有在任何文件、state、日志或汇报里写密钥:API key、PAT、 Slack token、OAuth secret 一律只走环境变量或 Secure Vault, 配置文件里只放 env_key 这类名字,不放值。