Archived
fix(loop-engine): 收尾复审二审 —— 兜底卡自己会抛 / 清单兜底不看面孔 / 空转写解闸
复审逐条核实上一波(C2/C3/M1/M2)修法本身引入的三处破坏,全部属实并修死: - I1 二审:配方办不下去时发的那张兜底卡(deps.cards.issue)从前裸调,调人预算 触顶抛 HumanCallBudgetExceededError 会精确复现 C3 那种无限热循环(推进器每 2 秒重来、不烧预算、不换状态、界面纹丝不动)。包进 try/catch,失败留痕、 状态退回 awaiting,不让它抛出去。配了变异验证:拿掉 try/catch 后新用例 真的变红(runTurn 抛出预算触顶错误),还原后转绿。 - C2 二审:decisionCardFace 的 checklist 兜底从前不看 reviewFace —— 一步只声明 acceptanceChecklist 而 cardVariant 不是 review(T_release 那种形状)时,配方发 的普通 decide 卡也会被兜上清单,前端不渲染勾选区、回传门却要求 checked ⊇ checklist,卡按不下去。改成只在 reviewFace 为真时才退回剧本声明的清单。 - M1/M2 二审:project 动作里 localExisting/localUnkeyed 触发的观察痕与真写 (localWrote/localDropped)共用 detail.auto + detail.ledger 这两个键,而 raiseAcceptance 的 revised 判据只认这两个键在不在 —— 一颗料 skipExisting、 或一行主键算不出来,这条"什么都没动"的痕照样能解开重做闸,人打回重做、 配方原样重跑一遍,闸却开了。auto-runner 补 detail.changed(真写了/真撤了 才为真),revised 判据对配方那一路改认这个标记。 另两笔记账: - save-wake.test.ts 的用例标题写着"验收态就是这一种(没有卡在等人)"是错的, pendingCards 把未办结工单也算在内,验收态恰恰有一张开着的验收工单——只改 标题措辞,不动断言与实现。 - docs/debt.md 补两笔:保存后唤醒的收窄几乎不生效(pendingCards 口径过宽); 模型路径的写同样会 un-land、只补了配方那一半(独立起一条,排在 W05 重写前)。 pnpm gates 退出码 0(2410 passed / 23 skipped); git diff --stat b995148...HEAD -- activities/ 为空,九个活动包一字未动。 报告:.superpowers/sdd/2026-08-29-deterministic-steps-auto-recipe/closing-fix-report.md Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -1349,3 +1349,33 @@ pnpm 硬链到全局 store,真实占盘远小于这个数,但 `du` 看着吓人
|
||||
写成 `kind: 'result'` 或顺手加 `internal: true`,它会**静默地**从工作日志上消失 ——
|
||||
没有测试会红、装包也不会拒,人只会发现「这一步好像没留痕」。目前靠代码注释提醒
|
||||
(`turn-runner.ts` R10 附近),没有静态检查兜底。
|
||||
|
||||
## 收尾复审二审(2026-08-29,B 项记账)
|
||||
|
||||
- **「保存后唤醒的收窄」几乎不生效**:`afterSave`(`save-wake.ts`)的 F1 门槛是
|
||||
`pendingCards` 为空才不叫醒(见 `apps/api/src/app/__tests__/save-wake.test.ts` 那条改过的
|
||||
用例标题)——而 `pendingCards`(`human-tool-service.ts:374`)把**未办结工单**也算在内,
|
||||
人能在件上按保存的时刻(工单开着让他填)几乎恒有一张这样的工单还没结。也就是说
|
||||
「没有卡在等人」这个分支在人真正会走到的场景里几乎从不成立,`wake` 照发、一步预算照烧 ——
|
||||
F1 那道门在设计上是对的,只是它保护到的场景比想象的窄得多。真要治得重新定义
|
||||
「什么时候算需要唤醒」(比如分清「这个工单是不是这次保存所在的那一张」),是新设计,
|
||||
不在本分支拍。
|
||||
- **模型路径的写同样会 un-land,只补了配方那一半**(复审判**中等偏高**):`DraftStore.put`
|
||||
尾部 `landed_at = null` 是无差别的 —— 同一行落草稿层一次,不论落笔的是配方(`project`
|
||||
动作)还是模型自己的 `write` 行动。本轮把配方那一侧堵上了(`skipUnchanged: true` +
|
||||
`detail.changed` 只在真写了/真撤了时为真,见 `auto-runner.ts` 的 `project` 分支与
|
||||
`turn-runner.ts` 的 `revised` 判据),**模型那一侧完全没动**——模型的 `write` 行动没有
|
||||
`skipUnchanged` 这层,值没变时 `draftWrite` 照样把 `landed_at` 打回 null。
|
||||
而 W05 的数据工单**今天**就在跑「确认即入库」(2026-08-27 的 `landed` 记号机制),
|
||||
模型只要对已入库的同一行同值重写一次(比如没读现值、把台账里已有的值原样再报一遍),
|
||||
landed 记号就没了 —— 整单放行会再写一遍正式表、再抛一遍 data 事件,而 CLAUDE.md 对
|
||||
landed 行的承诺是「不再写它、不抛事件」。**同一个洞只补了还没人走的那半边** ——
|
||||
配方(`auto`)目前一个活动包都没在用,模型路径每天都在跑。**判中等偏高**(复审原话):
|
||||
不在本分支动,原因两条 —— ①这是**共享面**(`turn-runner.ts` 的 `write` 行动、
|
||||
`engines.ts:1055` 的 `draftWrite`),改法要么给模型路径也接上 `skipUnchanged`
|
||||
(得先核实它是否会悄悄影响 :980 那道「一行都没变的写不是一个行动」拒回门 ——
|
||||
逐行看目前是不会,`changed` 在 `skipUnchanged` 判断之前就已经按值算好了,但没有测试钉死
|
||||
这条不变式,改了要重新证明一遍并补测)、要么另开一条更窄的路径,两种都要走全量回归
|
||||
+ 三套 e2e;②本轮改的是配方那一条新路,`git diff --stat b995148...HEAD -- activities/`
|
||||
为空这条硬约束意味着不能顺手把模型路径也捎带上、再靠一次浏览器走查收尾 ——
|
||||
排查与验收的工作量不是这一条闭合修复能捎带的。**应当独立起一条、排在 W05 重写之前**。
|
||||
|
||||
Reference in New Issue
Block a user