Files
Jyotisha/docs/tasks/TASK-rectification-adopt-narration-fix2-20260904.md
T

6.6 KiB
Raw Blame History

修复单 2 · 采用旁白超时测试挂死事件循环,拖垮同文件后三条用例(2026-09-04)

基线:origin/staging 13dded9fTASK-rectification-adopt-narration-fix-20260904.md 的实现)。本单只改 adopt-narration-agent.ts 的超时实现与对应进度记录,不动决策逻辑、不动提示词。

0. 验收结论(对照修复单 1

结论 证据
5.1 早退分支用本轮决策;三条调用方传 decisionfacts / 模板用合成 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_narrationconsole.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 0cancelled 4,进程退出码 1

1. 事故实证(P1 · 超时测试挂死)

adopt-narration-agent.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.19npx 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

独立验证(去掉所有业务代码):

const s = AbortSignal.timeout(30);
await Promise.race([new Promise(() => {}), new Promise((_, rej) => s.addEventListener("abort", () => rej(s.reason)))]);
// 进程直接退出,退出码 13,abort 从未触发

后果:backend-quality-gatenpm 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 转发到 controllerdispose() 清计时器并移除监听。
  2. deliverAdoptNarrationPromise.racetry / 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. 开工前置命令

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 起;开工时再核对。