im_wower
·
2026-03-22
T-029-stability-regression.md
1---
2task_id: T-029
3title: 多节点长时间稳定性回归
4status: done
5branch: feat/T-029-stability-regression
6repo: /Users/george/code/baa-conductor
7base_ref: main@926860a
8depends_on:
9 - T-028
10write_scope:
11 - docs/ops/**
12 - docs/runtime/**
13 - tests/e2e/**
14 - scripts/smoke/**
15 - scripts/failover/**
16 - scripts/runtime/**
17updated_at: 2026-03-22 05:35:00 CST
18---
19
20# T-029 多节点长时间稳定性回归
21
22## 目标
23
24在真实 mini / mac / VPS / Cloudflare 环境上完成一轮长时间稳定性回归,确认主备、探活、控制面和 smoke 脚本在真实环境下可持续工作。
25
26## 本任务包含
27
28- 基于 `T-028` 已上线环境跑多轮 smoke / failover / 恢复
29- 记录长时间运行期间的 `healthz`、`readyz`、`rolez`、`/v1/system/state`、`/v1/status`
30- 至少覆盖一次 planned failover 和一次 switchback
31- 把异常、抖动、人工干预点和最终结论写入回归报告
32
33## 本任务不包含
34
35- 重新部署 Cloudflare / VPS
36- 重写 control-api / conductor / status-api 代码
37- 修改 `baa-firefox` 仓库
38
39## 建议起始文件
40
41- `tests/e2e/smoke.test.mjs`
42- `scripts/smoke/run-e2e.sh`
43- `scripts/failover/rehearsal-check.sh`
44- `scripts/runtime/check-node.sh`
45- `docs/ops/README.md`
46
47## 交付物
48
49- 一份真实环境稳定性回归记录
50- 一份 failover / switchback 实测结果
51- 必要时更新后的 smoke / failover / on-node 文档
52
53## 验收
54
55- 能给出至少一轮真实环境 smoke 结果
56- 能给出至少一次 failover 和一次 switchback 的结果
57- `git diff --check`
58
59## 更新要求
60
61完成时更新 frontmatter 的:
62
63- `status`
64- `base_ref`
65- `updated_at`
66
67并补充下面这些内容:
68
69## files_changed
70
71- coordination/tasks/T-029-stability-regression.md
72- docs/ops/README.md
73- docs/ops/real-stability-regression-2026-03-22.md
74- scripts/smoke/README.md
75- scripts/smoke/live-regression.mjs
76
77## commands_run
78
79- `npx --yes pnpm install`
80- `./scripts/failover/print-topology.sh --env /Users/george/.config/baa-conductor/ops.env`
81- `./scripts/failover/rehearsal-check.sh --env /Users/george/.config/baa-conductor/ops.env --basic-auth "$DIRECT_BASIC_AUTH" --bearer-token "$CONTROL_API_OPS_ADMIN_TOKEN" --expect-leader mini`
82- `node scripts/smoke/live-regression.mjs --env /Users/george/.config/baa-conductor/ops.env --control-secrets /Users/george/.config/baa-conductor/control-api-worker.secrets.env --basic-auth-file /Users/george/.config/baa-conductor/direct-node-basic-auth.env --expect-leader mini`
83- `./scripts/runtime/check-node.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mini --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.71.210.78:4317 --local-api-allowed-hosts 100.71.210.78 --status-api-base http://100.71.210.78:4318 --status-api-host 100.71.210.78 --expected-rolez leader --check-loaded`
84- `ssh george@100.112.239.13 'cd /Users/george/code/baa-conductor-T028-v2 && ./scripts/runtime/check-node.sh --repo-dir /Users/george/code/baa-conductor-T028-v2 --node mac --service conductor --service status-api --install-dir /Users/george/Library/LaunchAgents --local-api-base http://100.112.239.13:4317 --local-api-allowed-hosts 100.112.239.13 --status-api-base http://100.112.239.13:4318 --status-api-host 100.112.239.13 --expected-rolez standby --check-loaded'`
85- `for i in 1 2 3 4 5 6 7; do node scripts/smoke/live-regression.mjs ... --expect-leader mini --compact; sleep 300; done`
86- `ps -axo pid=,command= | grep '/apps/worker-runner/dist/index.js' | grep -v grep || true`
87- `ssh george@100.112.239.13 "ps -axo pid=,command= | grep '/apps/worker-runner/dist/index.js' | grep -v grep || true"`
88- `curl -X POST ... https://control-api.makefile.so/v1/system/drain`
89- `curl -X POST ... https://control-api.makefile.so/v1/system/pause`
90- `launchctl bootout "gui/$(id -u)" "$HOME/Library/LaunchAgents/so.makefile.baa-conductor.plist"`
91- `./scripts/failover/rehearsal-check.sh --env /Users/george/.config/baa-conductor/ops.env --basic-auth "$DIRECT_BASIC_AUTH" --bearer-token "$CONTROL_API_OPS_ADMIN_TOKEN" --skip-node mini --expect-leader mac`
92- `curl -X POST ... https://control-api.makefile.so/v1/system/resume`
93- `ssh george@100.112.239.13 'launchctl bootout "gui/$(id -u)" "$HOME/Library/LaunchAgents/so.makefile.baa-conductor.plist"'`
94- `cd /Users/george/code/baa-conductor-T028-v2 && ./scripts/runtime/reload-launchd.sh --service conductor --install-dir /Users/george/Library/LaunchAgents`
95- `./scripts/failover/rehearsal-check.sh --env /Users/george/.config/baa-conductor/ops.env --basic-auth "$DIRECT_BASIC_AUTH" --bearer-token "$CONTROL_API_OPS_ADMIN_TOKEN" --skip-node mac --expect-leader mini`
96- `ssh george@100.112.239.13 'cd /Users/george/code/baa-conductor-T028-v2 && ./scripts/runtime/reload-launchd.sh --service conductor --install-dir /Users/george/Library/LaunchAgents'`
97- `dig @giancarlo.ns.cloudflare.com +short conductor.makefile.so`
98- `echo | openssl s_client -servername conductor.makefile.so -connect 192.210.137.113:443 2>/dev/null | openssl x509 -noout -dates`
99
100## result
101
102- 新增 `scripts/smoke/live-regression.mjs`,把 control-api、公网入口、直连入口、status-api 和鉴权结果收成一份真实环境快照;`scripts/smoke/README.md` 与 `docs/ops/README.md` 已补用法与报告入口。
103- 在 live `mini/mac/VPS/Cloudflare` 环境上完成了一轮基线 smoke、一轮 `30.3` 分钟持续观察、一次 planned failover 和一次 switchback,并把过程写入 `docs/ops/real-stability-regression-2026-03-22.md`。
104- steady-state 结论:control-api、公网 conductor、mini/mac 直连 conductor、Basic Auth、mini/mac on-node `check-node.sh` 都可以工作;final steady-state 已回到 `mini-main` leader、`mac-standby` standby、`mode=running`。
105- failover / switchback 都真实成功,但都观察到了短暂外部不一致窗口:停掉当前 leader 后,公网或恢复中的节点会先返回 `standby`,随后才稳定到新的 `leader`。
106- 本轮 smoke 没有完全通过:两台节点的 `status-api /v1/status` 全程都停在 `source=empty`, `mode=paused`, `leaderId=null`,与 control-api 真相不一致。
107
108## risks
109
110- live `status-api` 目前不是可靠真相源;它不能用来判断自动化是否真的在 `running`,也不能确认当前 leader、queueDepth 或 activeRuns。
111- mini/mac 两侧 launchd 安装副本仍然指向 `/Users/george/code/baa-conductor-T028-v2`,还没有整理到 canonical repo path `/Users/george/code/baa-conductor`。
112- switchback 不是“停 mac、起 mini”就完全结束;要恢复 canonical `mini leader + mac standby`,还需要额外显式 reload `mac` conductor。
113- authoritative DNS 仍是 DNS-only,Cloudflare proxy 没有重新开启;本轮没有尝试把 conductor hosts 切回橙云。
114- 本机非权威 DNS / 代理层对 `conductor.makefile.so`、`mac-conductor.makefile.so` 的解析与权威结果不一致,直接做 DNS/TLS 诊断时必须区分 authoritative 结果和本地 resolver 结果。
115- 在 planned failover 前,我有一次把 `GET /v1/system/state` 和 `POST /v1/system/pause` 并行发出,所以那次 `mode=paused` 读数不能单独拿来证明 `drain` 的独立效果;后续状态读取都改回串行。
116
117## next_handoff
118
119- 优先排查 live `status-api` 为什么持续返回 `source=empty`,并让 `/v1/status` 跟随 control-api 的真实 leader / mode / queue state。
120- 规划一次 runtime canonicalization,把 mini/mac 的 launchd、logs、runs、worktrees 路径从 `/Users/george/code/baa-conductor-T028-v2` 收口到 `/Users/george/code/baa-conductor`。
121- 如果要把 switchback 降为更可重复的运维流程,runbook 或脚本层需要显式补上“恢复 mac standby”的尾步骤。
122- 如果后续要重新启用 Cloudflare proxy,先把 DNS / TLS / zone SSL 模式的真实控制面整理清楚,再做单独演练,不要在当前 DNS-only 基线之上直接切橙云。
123
124开始时建议直接把 `status` 改为 `in_progress`。
125
126做完并推送后:
127
128- 如果等待整合,改为 `review`
129- 如果确认结束,改为 `done`