docs(tasks): adopt narration fix accepted except hanging timeout test; fix2 brief

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
This commit is contained in:
Jesse_Chen
2026-09-04 07:06:55 +00:00
parent 13dded9fff
commit 5c9e98790b
2 changed files with 103 additions and 1 deletions
+2 -1
View File
@@ -45,7 +45,8 @@
| `TASK-rectification-adopt-flow-fix-20260903.md` | — | 采用流程修复单(含删「用这个时间看盘」) | 已验收 | `e8c98c37`BUG-501/502 |
| `TASK-rectification-uncertainty-stop-20260903.md` | — | 不确定度停止规则加样本下限 | 已验收 | `0c0df426`BUG-503 |
| `TASK-rectification-adopt-narration-20260904.md` | `PROGRESS-rectification-adopt-narration-20260904.md` | 探针池耗尽时采用卡旁白改 Agent 生成 + 已丢弃探针绕过 BUG-472 早退 + 区分题答否关线 | 已验收(4.1.4 点选入口未通过,见修复单) | `0aaa0d70`BUG-519/520 |
| `TASK-rectification-adopt-narration-fix-20260904.md` | `PROGRESS-rectification-adopt-narration-fix-20260904.md` | 采用旁白 Agent 在点选入口从不运行(早退分支重算过期决策)+ 校验器/可观测/超时 | 验收 | 基线 `e18bd25b`(任务书写 `0aaa0d70`),BUG-521 / BUG-522 |
| `TASK-rectification-adopt-narration-fix-20260904.md` | `PROGRESS-rectification-adopt-narration-fix-20260904.md` | 采用旁白 Agent 在点选入口从不运行(早退分支重算过期决策)+ 校验器/可观测/超时 | 验收(5.1/5.2 实现通过;超时测试挂死事件循环,见修复单 2) | `13dded9f`BUG-521/522 |
| `TASK-rectification-adopt-narration-fix2-20260904.md` | — | 采用旁白超时用了 unref 的 `AbortSignal.timeout`,测试挂死取消同文件后三条用例,门禁 `npm test` 退出码 1 | 待执行 | 基线 `13dded9f`BUG-523 起 |
| `TASK-rectification-ux-20260902.md` | `PROGRESS-rectification-ux-20260903.md` | 会话面空白假死与交互摩擦 | 已验收 | `d159f08e`(09-03 在新基线重做后合入,BUG-505509 |
### 聊天主链路与首页
@@ -0,0 +1,101 @@
# 修复单 2 · 采用旁白超时测试挂死事件循环,拖垮同文件后三条用例(2026-09-04)
基线:`origin/staging` `13dded9f``TASK-rectification-adopt-narration-fix-20260904.md` 的实现)。本单只改 `adopt-narration-agent.ts` 的超时实现与对应进度记录,不动决策逻辑、不动提示词。
## 0. 验收结论(对照修复单 1)
| 项 | 结论 | 证据 |
| --- | --- | --- |
| 5.1 早退分支用本轮决策;三条调用方传 `decision`facts / 模板用合成 receipt | 通过 | `persistNextInterviewAfterChoice` 函数体无 `decideFromDossier(``dossierWithCurrentInference`;点选用例改为点选前 dossierrevision 5、23/16/7),断言模型 1 次、facts 05:00 / 21/18/9 / `ready_to_adopt`;非法输出时模板范围来自本轮决策 |
| 5.2 提示词「不要出现」;诊断枚举 `adopt_narration=`8s 超时 | 通过(实现)/ **未通过(测试,见 §1** | 提示词与校验器同口径;`deliverAdoptNarration` 返回 `adopt_narration``console.info` 不含模型原文;四种结果各有断言 |
| 5.3 `stopFactsFromDropped` 婚恋标签 | 推迟(允许) | 进度记录已写明 |
| BUG-521 / BUG-522 | 通过 | 无案例 ID、无用户资料;复发链接 BUG-440 |
| CHANGELOG、`docs/testing` 清单 | 通过 | 点选入口列为优先手测项 |
| tsc / lint | 通过 | `tsc` 0 错;lint 0 error 74 warning(既有) |
| 定向套件 | **未通过** | `rectification-* / agentic-rectification-* / birth-time-*`871 条,pass 858fail 0**cancelled 4**,进程退出码 1 |
## 1. 事故实证(P1 · 超时测试挂死)
`adopt-narration-agent.ts`
```ts
function composedAbortSignal(signal, timeoutMs) {
const timeout = AbortSignal.timeout(timeoutMs);
return signal ? AbortSignal.any([signal, timeout]) : timeout;
}
```
Node 的 `AbortSignal.timeout()` 内部计时器是 **unref** 的(所有版本,含 CI 用的 Node 22):它不会让事件循环保持活跃。测试「adopt narration times out to the template without throwing」把 `generateText` 挂成永不 resolve 的 Promise,此时进程里唯一待办就是这个 unref 计时器 → 事件循环直接排空,测试运行器判 `cancelledByParent: Promise resolution is still pending but the event loop has already resolved`。同一文件后面三条用例(`applyCollectFocusDenial …``distinguish declined …``family collect declined vs extra distinguish declined …`——后两条正是 BUG-520 的回归锁)也被一并取消。
本地复现(Node 20.19`npx tsx --test tests/rectification-adopt-narration-20260904.test.ts`):
```
ok 1 … ok 6
not ok 7 - adopt narration times out to the template without throwing (cancelledByParent)
not ok 8 / 9 / 10 (cancelledByParent)
# tests 10 # pass 6 # fail 0 # cancelled 4
exit=1
```
独立验证(去掉所有业务代码):
```js
const s = AbortSignal.timeout(30);
await Promise.race([new Promise(() => {}), new Promise((_, rej) => s.addEventListener("abort", () => rej(s.reason)))]);
// 进程直接退出,退出码 13,abort 从未触发
```
后果:`backend-quality-gate``npm test --prefix frontend` 会以退出码 1 失败,`13dded9f` 不会部署(本单落笔时 staging `/api/health` 仍是 `0aaa0d70`)。
进度记录写的「adopt-narration 10/10、871 pass / 0 fail」与实测不符:Node 摘要行 `# fail 0` 后面还有 `# cancelled 4`,且退出码为 1。
生产侧:`next start` 有监听 socket 撑住事件循环,超时在真实请求里会生效,本单**不是**线上功能故障;但计时器不随模型返回而清理,每次调用都会留一个 8 秒的悬挂计时器。
## 2. 根因
超时用了 `AbortSignal.timeout()`,没意识到它的计时器是 unref 的;测试又依赖它在空事件循环里触发。验收只看了 `# fail`,没看 `# cancelled` 与退出码。
## 3. 决策记录
原任务书与修复单 1 的决策不变。本单不新增产品决策。
## 4. 硬红线
修复单 1 §4 全部沿用。追加:
1. 超时必须用 **ref** 的计时器(`setTimeout` + `AbortController`),并在模型返回、校验完成或外部 signal 触发后 `clearTimeout`;不得靠 `AbortSignal.timeout()`
2. 不得为了让测试过而删掉超时用例或把它改成 `todo` / `skip`
3. 进度记录必须贴 Node 摘要的完整六行(tests / pass / fail / cancelled / skipped / todo)与进程退出码。
## 5. 任务分解
### 5.1 P1 · 超时改为可清理的 ref 计时器(BUG-523
1. `composedAbortSignal` 改为返回 `{ signal, dispose }`:内部 `new AbortController()``setTimeout(() => controller.abort(new DOMException("adopt narration timed out", "TimeoutError")), timeoutMs)`(不 `unref`),若有外部 `signal` 则监听其 `abort` 转发到 controller`dispose()` 清计时器并移除监听。
2. `deliverAdoptNarration``Promise.race``try / finally` 调用 `dispose()`;超时仍归 `template:model_error`(枚举不变)。
3. `whenAborted` 的监听在 `dispose` 时一并移除,避免每次调用留下悬挂监听器。
验收:
- `npx tsx --test tests/rectification-adopt-narration-20260904.test.ts``# tests 10 # pass 10 # cancelled 0`,退出码 0。
- 定向套件 `rectification-* / agentic-rectification-* / birth-time-*``# fail 0 # cancelled 0`,退出码 0。
- `npm test`(全量):与无 Docker 基线一致,即除 `docker ENOENT` 的 25 条外无其它失败,`# cancelled 0`
- 新增一条用例:fake `generateText` 立即返回合法文案时,`deliverAdoptNarration` resolve 后不再有活跃计时器(可用 `setTimeout` 计数或断言 `dispose` 被调用),防止每次调用漏一个 8 秒计时器。
- `docs/BUG_HISTORY.md` 新增 BUG-523,关联 BUG-522;防复发写明「超时不用 `AbortSignal.timeout`」与「验收看 cancelled 与退出码」。
- `PROGRESS-rectification-adopt-narration-fix-20260904.md` 补一节更正:原「10/10、871 pass / 0 fail」实测为 pass 858 / cancelled 4 / exit 1。
## 6. 让步顺序
只有 5.1 一条,必做;没有它 `13dded9f` 进不了 staging。
## 7. 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/rectification-adopt-narration-fix2-20260904 .worktrees/rectification-adopt-narration-fix2-20260904 origin/staging
cd .worktrees/rectification-adopt-narration-fix2-20260904/frontend
npx tsx --test tests/rectification-adopt-narration-20260904.test.ts; echo "exit=$?" # 开工前应复现 cancelled 4 / exit 1
```
## 8. BUG 编号起点
截至本单:BUG-522。本单从 **BUG-523** 起;开工时再核对。