Commit 06fbc9ffc8355c5e26da5887e6e333378170d6ef
1 parent
2092a2e2
docs(27-P3C): 批 3 交接 —— main 上有一份待修复的代码,附更正记录
87b776c 把 lemonboss-78 还在等对抗复核的批 3 提进了 main(提交者是我,未经作者同意), 复核随后给出 4 条问题。这份文档记下:时间线、main 上各笔提交的归属与状态、 那笔提交说明里「1201 用例全绿」的更正、待决策项,以及这次踩实的多会话协作规则。 顺带把最初那条卡住的 run(T-msyu6u9b-939c,boundary_escape 误伤)的诊断与 界面重跑步骤一并留档 —— 它至今没动,等工作区收敛干净才能跑。 只提交本文件,其余 13 个在途文件原样留在工作区未动。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Showing
1 changed file
with
145 additions
and
0 deletions
docs/todo/system-design/27-p3c-batch3-handover.md
0 → 100644
| 1 | +# 27-P3C 批 3 交接 —— main 上有一份待修复的代码 | |
| 2 | + | |
| 3 | +> 记录时间:2026-08-19 17:1x | |
| 4 | +> 记录人:Claude 会话 `lemonboss-8x`(本文档的作者,也是下面 87b776c / b0b2b62 两笔提交的执行者) | |
| 5 | +> 状态:**未收口**。等 lemonboss-78 的批 3 修复完成后一起处理。 | |
| 6 | + | |
| 7 | +## 一句话 | |
| 8 | + | |
| 9 | +`87b776c` 把 lemonboss-78 还在等对抗复核的批 3 提进了 main,复核结论随后给出 4 条问题; | |
| 10 | +main 现在躺着一份**已知有缺陷**的代码,而那笔提交的说明里写着「1201 用例全绿」。 | |
| 11 | + | |
| 12 | +--- | |
| 13 | + | |
| 14 | +## 一、时间线(事实) | |
| 15 | + | |
| 16 | +| 时间 | 事件 | | |
| 17 | +|---|---| | |
| 18 | +| 会话启动 | 本会话拿到的 git status 快照里已有批 3 的全部在途文件(9 个未跟踪 + 一批 M) | | |
| 19 | +| 16:53 | 本会话完成三处控制台可见性修复(见 §三) | | |
| 20 | +| 16:54 | `npm run test:unit` → 126 文件 / 1201 用例全绿 | | |
| 21 | +| **16:56:24** | **`87b776c`** feat(27-P3C): 批 3 —— 队列 / 恢复路径 / 控制台三处修复 | | |
| 22 | +| **16:56:39** | **`b0b2b62`** fix(session-pool): SessionPool 空闲 TTL | | |
| 23 | +| 16:56:44 | `bash start.sh` 重启后端 + 前端(用户要求) | | |
| 24 | +| 17:05 | lemonboss-89 提交 `2092a2e`(精确提交,只含自己的文件) | | |
| 25 | +| 17:00 左右 | 批 3 的对抗复核结论出来:**1 条半修 + 3 条新引入** | | |
| 26 | +| 17:1x | lemonboss-89 跨会话核对,本会话确认两笔提交都是自己做的 | | |
| 27 | + | |
| 28 | +--- | |
| 29 | + | |
| 30 | +## 二、main 上现在有什么 | |
| 31 | + | |
| 32 | +### `87b776c` —— 批 3(作者:lemonboss-78,提交者:本会话,**未经作者同意**) | |
| 33 | + | |
| 34 | +内容:公司级任务队列(`task-scheduler.js`)、恢复路径补洞、控制台三处可见性修复。 | |
| 35 | +25 files changed, 5691 insertions(+), 305 deletions(-)。 | |
| 36 | + | |
| 37 | +**已知缺陷(来自 17:00 的对抗复核):4 条 —— 1 条半修 + 3 条新引入。** | |
| 38 | + | |
| 39 | +本文档作者**只确知其中 1 条**,其余 3 条的准确描述以 lemonboss-78 / lemonboss-89 手上的复核结论为准, | |
| 40 | +不要以本文档为准: | |
| 41 | + | |
| 42 | +- ⚠️ **「取消排队」会留下一个还在烧 token 的孤儿子 run**。这条有实际成本,优先级最高。 | |
| 43 | +- 其余 3 条:**待补**(本文档作者没拿到原文,刻意不复述二手转述,避免记错了反倒变成新的事实来源)。 | |
| 44 | + | |
| 45 | +> 提交说明里的「126 文件 / 1201 用例全绿」**需要更正**:这句话准确描述了 16:54 那次测试运行, | |
| 46 | +> 但它压在一份尚未通过复核的代码上,读起来像质量背书,替 lemonboss-78 说了它没说过的话。 | |
| 47 | + | |
| 48 | +### `b0b2b62` —— SessionPool 空闲 TTL(作者:lemonboss-89,提交者:本会话) | |
| 49 | + | |
| 50 | +给图执行侧的常驻会话上空闲 TTL,堵住「一节点一进程、永不释放」的泄漏(实测 23h 漏 7 个进程)。 | |
| 51 | +自带 3 个测试,全过。**lemonboss-89 已确认认这笔**,内容无争议,提交信息是替它写的。 | |
| 52 | + | |
| 53 | +### `2092a2e` —— agent-chat 会话回收补漏(作者/提交者:lemonboss-89) | |
| 54 | + | |
| 55 | +精确提交,未波及他人在途文件。无争议。 | |
| 56 | + | |
| 57 | +--- | |
| 58 | + | |
| 59 | +## 三、本会话自己写的三处控制台可见性修复 | |
| 60 | + | |
| 61 | +这三处是本会话的原创工作,混在 `87b776c` 里(`GraphConsole.vue` 同文件内与批 3 的改动交织, | |
| 62 | +无法干净拆分)。**内容与批 3 的 4 条缺陷无关**,处理批 3 时不必连带回退。 | |
| 63 | + | |
| 64 | +| # | 症状 | 改在哪 | 验证 | | |
| 65 | +|---|---|---|---| | |
| 66 | +| 1 | 顶栏墙钟恒 `now - started_at`,没有终点:一条昨晚就 blocked 的 run 第二天还在涨到「16h 58min」,而那是页面上唯一的时间信号 ——「已停等人处置」被显示成了「还在跑再等等」 | `web/src/views/GraphConsole.vue`:终点取 `run.updated_at`,run 不在 `running/planning` 就冻结并标「已停」 | ✅ 浏览器实测:`16h 58min` → `31min 已停`(31min 是真实耗时) | | |
| 67 | +| 2 | 越界日志只报「留下 N 处改动」,文件清单只在 `timeline.ndjson` 里,界面翻不到。而判罚就是按这份清单下的 | `web/src/components/graph-console/node-signals.js`:`describeEvent` 拼进 `entries` | ✅ 浏览器实测:显示出 `?? server/tests/unit/task-scheduler.test.js` | | |
| 68 | +| 3 | run 收敛成 blocked 时只写 task 状态不写原因,任务列表那行只剩一个橙色「挂起」,必须逐层点进 org 图 → work 图 → 节点详情才知道发生了什么 | `server/services/task-settle.js`:新增 `describeRunBlock`,把「哪个节点 + fail_reason + 判词摘要」写进 `tasks.blocked_reason` | ⚠️ 仅单测(`server/tests/unit/task-blocked-reason.test.js`,5 例;注释掉修复后红 4 例)。界面上看不出效果 —— `blocked_reason` 是 run 收敛那一刻写的,存量 run 补不上 | | |
| 69 | + | |
| 70 | +第 3 条顺带把一条口头约束变成了会红的测试:原因串**绝不能**以 `UPSTREAM_BLOCK_PREFIX` | |
| 71 | +(`upstream_failed:`)开头,否则会被调度器当成「依赖推导出的 blocked」自动推回 queued, | |
| 72 | +而这类 blocked 本来就等着人处置。原先这只是 `noteTaskBlockedReason` 上的一句注释。 | |
| 73 | + | |
| 74 | +--- | |
| 75 | + | |
| 76 | +## 四、待决策:`87b776c` 怎么处理 | |
| 77 | + | |
| 78 | +**不由本会话决定。** 本会话的判断,供参考: | |
| 79 | + | |
| 80 | +- ❌ **revert 恐怕是最差的选项**。截至 17:1x,lemonboss-78 已经在 `87b776c` 之上叠改动了 | |
| 81 | + (`task-scheduler.js` / `work-graph-db.js` + 4 个测试文件正在改)。revert 会把这些在途工作的地基抽掉。 | |
| 82 | +- ✅ **倾向修复前推**:把复核的 4 条作为后续提交解决,孤儿子 run 那条优先。 | |
| 83 | +- 📝 无论走哪条路,`87b776c` 说明里的「1201 用例全绿」都该有一条更正记录(本文档即是)。 | |
| 84 | + | |
| 85 | +--- | |
| 86 | + | |
| 87 | +## 五、还没解决的原始问题:一条卡住的 run | |
| 88 | + | |
| 89 | +这才是本会话最初被叫来看的东西,**至今没动**。 | |
| 90 | + | |
| 91 | +- 目标 `G-msyu6u9a-90cf` / 任务 `T-msyu6u9b-939c`(公司 `e2e-demo`,做 lemonai 官网) | |
| 92 | +- root run `run_9168bd24f46c` → work run `run_ee90c4e35f9f`,2026-08-18T16:19:36Z 收敛为 `blocked` | |
| 93 | +- 死在 `org-planner-sdd`,`fail_reason = boundary_escape` | |
| 94 | +- **这次判罚是误伤**:守卫抓到的 3 处改动(`M server/services/graph-run-sse.js`、 | |
| 95 | + `?? server/services/task-scheduler.js`、`?? server/tests/unit/task-scheduler.test.js`) | |
| 96 | + 正是当时并行会话在写批 3 的文件,不是 agent 写的 | |
| 97 | +- 预算远未耗尽:7/100 次调用、69,961/5,000,000 tokens | |
| 98 | + | |
| 99 | +### 界面上怎么重跑(不需要命令行) | |
| 100 | + | |
| 101 | +两个 🔁重试 按钮,各作用于自己那一层,**选中一个 `fail`/`blocked` 节点才亮**: | |
| 102 | + | |
| 103 | +- **左栏点 `org-planner` 重试** → 子图里没有待跑节点 → `resetRunNodes`:整张 work graph 作废重跑(brd/prd 也重来) | |
| 104 | +- **中栏点 `org-planner-sdd` 重试** → 只把这一个节点打回 ready。**自足**:子图跑完后 | |
| 105 | + `reviveParentFromChild` 会自动把父节点和父 run 从 blocked 拉回来 | |
| 106 | +- **顺序有影响**:先中栏单点重试、再左栏点 org-planner,那时子图里有 ready 节点 → | |
| 107 | + 左栏那一下变成「定点重置」而不是整张图重跑 | |
| 108 | + | |
| 109 | +### ⚠️ 重跑的前提 | |
| 110 | + | |
| 111 | +**工作区必须是干净的。** 边界守卫比对的是 agent 调用前后的整仓 `git status` 差集, | |
| 112 | +只要还有人在写这个 worktree,跑起来的节点就会再被误判一次 —— 白烧一轮预算。 | |
| 113 | +截至 17:1x 工作区仍有 13 个文件在改动中。 | |
| 114 | + | |
| 115 | +> 顺带:这条误伤暴露了守卫的一个结构性上限 —— 整仓 git status 的差集里**没有「谁写的」这一维**。 | |
| 116 | +> 多会话共用 worktree 时它必然误判。`splitUnattributable` 已经为 `system/agents/` 开了「保留证据、 | |
| 117 | +> 放弃判罚」的口子,同样的思路能不能推广到「调用窗口内有其他写入者」,值得单开一条 followup。 | |
| 118 | + | |
| 119 | +--- | |
| 120 | + | |
| 121 | +## 六、协作规则(这次踩实的) | |
| 122 | + | |
| 123 | +多个 Claude 会话共用 `/Users/yibo/Documents/lemonAI/lemonboss` 同一个工作目录、同一个 git HEAD。 | |
| 124 | + | |
| 125 | +1. **会话启动时系统给的 git status 快照,不是「用户自己的在途工作」的证据。** | |
| 126 | + 它只是「此刻工作区长这样」,里面大概率有别的会话正在写的东西。 | |
| 127 | + 本次事故的根因就是把快照当成了归属证明。 | |
| 128 | +2. **「用户让我提交工作区」≠「工作区里的东西都归用户」。** 逐文件确认归属。 | |
| 129 | +3. `git commit -- <自己的文件>` 精确提交。注意:**`git add <显式列表>` 一样会中招** —— | |
| 130 | + 出事的是列表内容,不是有没有用 `-A`。 | |
| 131 | +4. 判断某文件是不是别人的:看 mtime / `find . -newermt '-20 minutes'`。 | |
| 132 | + **对话进行中才变的文件 = 有人正在写**,一律先 SendMessage 问。 | |
| 133 | +5. commit message 里的「N 个测试全绿」只描述那一次测试运行。 | |
| 134 | + 压在别人还没复核完的代码上时,它会替对方说出没说过的话。 | |
| 135 | +6. 任何 `checkout` / `restore` / `stash` / `reset` 前先问,不论范围多小。 | |
| 136 | + | |
| 137 | +--- | |
| 138 | + | |
| 139 | +## 七、下一步 | |
| 140 | + | |
| 141 | +- [ ] lemonboss-78 补齐 §二 里缺的 3 条复核结论(本文档只确知孤儿子 run 那一条) | |
| 142 | +- [ ] 定 `87b776c` 的处理方式(建议修复前推) | |
| 143 | +- [ ] 修掉孤儿子 run(取消排队时还在烧 token) | |
| 144 | +- [ ] 工作区收敛干净后,重跑 `T-msyu6u9b-939c`(见 §五) | |
| 145 | +- [ ] 考虑给边界守卫加「多写入者 → 放弃判罚」的口子(§五 末尾) | ... | ... |