一条事实链
这本指南里的每条命令都在读写同一条链:worktree → 清单 → 服务 → 运行 → 运行事实。每个站点只有一个写入者,下一站只读上一站发布的东西。没有第二个状态来源。
五个站点
| 站点 | 谁写 | 发布什么 |
|---|---|---|
| worktree | Git,由引擎观察 | checkout 根路径,以及一次运行所属的 wt_… 身份。 |
| 清单 | 人 | oberth.yaml:服务、端口、依赖、健康检查、引用。 |
| 服务 | 启动时的引擎 | 启动的是哪条声明、按什么顺序、落在哪个地址上。 |
| 运行 | 引擎的运行注册表 | 一个进程的 pid、开始、结束、退出码与日志路径。 |
| 运行事实 | 本轮观察 | 活着、在监听、健康,以及更早那次运行为什么结束。 |
顺序就是契约
只用一个“它起来了吗”的布尔值会把整条链压平,每个读者都得重新推导剩下的部分。所以每一步都先发布,下一步才被允许据此决定任何事:归属先于生命周期,生命周期先于观察,观察先于就绪。
也正因为如此,up 能诚实地等待,status 也能一边说服务在运行、一边说端口未知,而不自相矛盾。
一次运行属于启动它的 checkout
归属靠这一轮的路径观察、清单内容索引和运行身份三者共同确定。因此同一个仓库的两个 worktree 永远不会串服务:不串端口、不串数据库名、不串停止动作。
端口遵守同一条规则。port: auto 按一次运行预留、停止时交还,所以第二个 checkout 问“那个 API 端口”时得到的是它自己的答案,而不是一次冲突。
未知也是一条事实
失败、取消和不完整的扫描都不能变成“空闲”、“已消失”或“健康”。监听表里没有,意味着这条事实没被观察到——绝不意味着端口空闲。信号发送成功也只说明信号送到了,不代表进程已经退出。
每一站在哪一章
- 声明该跑什么——清单,以及模型不许对它做的那件事。
- up——依赖顺序、自动端口,以及就绪到底证明了什么。
- 多个 worktree 与运行——归属、数据路径和遗留 checkout。
- logs——运行自己的输出,不用重拼一条 shell 单行。
- status——四种运行事实,以及那句诚实的“我看不见”。