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

110 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 修复单 · 采用旁白 Agent 在点选入口从不运行(2026-09-04)
基线:`origin/staging` `0aaa0d70``TASK-rectification-adopt-narration-20260904.md` 的实现)。本单只改 `answer-choice.ts` 与新测试,不动 Agent 提示词以外的决策逻辑。
## 0. 验收结论(对照原任务书)
| 项 | 结论 | 证据 |
| --- | --- | --- |
| 4.1.1 nakshatra 计划/决策同口径,无 frame 的 distinguish 不进 `deferred_followup` | 通过 | `rectificationFollowupCatalog``inspectDiscriminatorProbes`;计划层兜底丢进 `dropped_probes(not_renderable)` |
| 4.1.2 `isRemainingDiscriminatorFollowup` / `interviewToPersist` 忽略无 frame distinguish | 通过 | 代码 + 14 探针 fixture |
| 4.1.3 `persistableFocusDomain` 非白名单返回 nullappearance 零 RPChorary 断言三栏说明 | 通过 | `rectification-server-focus``rectification-collect-stall` |
| 4.1.4 采用旁白 Agent + 模板兜底,三条入口都接 | **未通过(P1** | 见 §1;点选入口 Agent 从不运行,且模板改用了过期决策 |
| 4.1.5 采用轮 `current_question` 为 null、零次 set_focus | 通过 | 新测试 `assertNoFocusWrite` |
| 4.2 区分题答否不关整条领域 | 通过 | `declinedDomains` 按 intent`dossierWithClosedFocus` 带 intentBUG-520 |
| `rectification-collect-stall` live fixture 补 nakshatra + window_scan 后原断言仍过 | 通过 | 本地重跑 |
| tsc / lint / 定向套件 | 通过 | `tsc` 0 错;lint 0 error 74 warning(既有);`rectification-* / agentic-rectification-* / birth-time-*` 869 条 fail=0 |
| 全量 `tests/*.test.ts` | 环境缺口 | 2638 条,fail 25 全部为 `docker ENOENT` 的 DB / 部署 / 备份套件,与无 Docker 基线一致;无一条校正相关 |
| BUG-519 / BUG-520、CHANGELOG、docs/testing 清单 | 通过 | 无案例 ID、无用户资料 |
## 1. 事故实证(P1 · 点选入口 Agent 从不运行)
`answer-choice.ts` `persistNextInterviewAfterChoice` 的采用早退分支:
```ts
const decision = decideFromDossier(input.dossier, { birthDate });
const facts = adoptDeliveryFacts(decision, input.dossier);
const fallback = adoptHostNarration({ dossier: input.dossier, decision, ... });
```
点选路径(`applyRectificationChoice``persistApplied`)传进来的 `input.dossier` 是**点选前**加载的 dossier,其 `latestResult.decisionReceipt.inference_state` 还是上一轮;本轮新状态在 `input.decisionState`,本轮决策在 `input.nextAction``persistApplied` 手里还有完整的 `nextDecision`)。函数开头已经把两者合成了 `latest`,但早退分支没有用它,而是从旧 dossier 重新 `decideFromDossier`
- 旧 dossier 的决策是「还有一道题要问」(`theme_refine` / `ask_candidate_discriminator``probe` 非空),`precisionStage !== "ready_to_adopt"``shouldWriteAdoptNarration` 为 false → **`narrateAdopt` 直接返回 fallback,模型一次都没调**。
- fallback 模板的 `credibleRange / representativeTime / stopReason` 也来自这份旧决策,而不是本轮的 `nextAction`。改造前模板用的是 `nextAction.credible_range`,这是回归。
本地复现(用 14 探针 fixture,把 dossier 里的 receipt 回退成「最后一道题未答、分数 23/16/7」,`decisionState` 用答完后的状态,`nextAction` 用答完后的决策):
```
pre-click decision: theme_refine, probe=true
agent called: false
narration: <模板>
fresh nextAction: offer_provisional_range [05:00, 05:06] 05:00
```
新测试「three adopt entry points call the model once」没拦住,是因为它给点选入口传的 `dossier` 已经是答完后的状态(receipt 与 `decisionState` 相同),与生产的点选路径不一致。
用户可感知:**点最后一道区分题**(采用卡最常见的出现方式)时,旁白仍是模板句;只有采集题答「没有」和空闲兜底两条入口才会走 Agent。
## 2. 根因
早退分支把「决策」当成可以从 dossier 随手再算一遍的东西,违反 BUG-440 防复发条款「同一次调用里出题层与决策层必须用同一个 sessionOutcome」——这里是决策层自己算了两遍、用了两份不同输入。
## 3. 决策记录
原任务书决策不变。本单不新增产品决策。
## 4. 硬红线
原任务书 §3 全部沿用。追加:
1. `persistNextInterviewAfterChoice` 内不得再调用 `decideFromDossier`;决策由调用方传入,或用 `decideAfterInferenceChange({ dossier, state: input.decisionState })``persistApplied` 保持同一入口。
2. 「三条入口」测试的点选用例必须用**点选前** dossier + 答完后 `decisionState` + 答完后 `nextAction` 的形状;不得再用已答完的 dossier 冒充。
## 5. 任务分解
### 5.1 P1 · 早退分支用本轮决策(BUG-521)
1. `persistNextInterviewAfterChoice` 新增可选入参 `decision?: RectificationDecision`;三条调用方都传:`persistApplied``nextDecision``persistNextInterviewIfIdle` 传已算好的 `decision``applyCollectFocusDenial` 传它用来生成 `nextAction` 的那份决策。未传时用 `decideAfterInferenceChange({ dossier: input.dossier, state: input.decisionState, userStopped: false, birthDate })`,不得用 `decideFromDossier(input.dossier)`
2. `adoptDeliveryFacts``adoptHostNarration` 的 dossier 参数改传合成后的 `{ ...input.dossier, latestResult: latest }`(函数开头已有的 `latest`),保证活跃候选与相对支持度是本轮的。
3. `persistApplied``adoptionFacts = adoptDeliveryFacts(nextDecision, input.dossier)` 同样改用合成 receipt。
验收:
- `rectification-adopt-narration-20260904.test.ts`「three adopt entry points」点选用例改成 §1 的形状:`dossier` receipt 为「最后一道题未答、分数 23/16/7、revision 5」,`decisionState = fourteenProbeState()``nextAction = publicNextAction(decideFromDossier(caseDossier()))`。断言:模型被调用 1 次;传给模型的 `facts.representative_minute === "05:00"``facts.active_candidates` 的分数为 21/18/9(不是 23/16/7)、`facts.precision_stage === "ready_to_adopt"`
- 同一形状下让 fake model 返回非法文本,断言 fallback 模板里的范围与代表分钟来自 `nextAction`05:0005:06 / 05:00),并含「分不开 05:00 和 05:06」。
- 加一条源码级断言:`persistNextInterviewAfterChoice` 函数体内不出现 `decideFromDossier(`
- `docs/BUG_HISTORY.md` 新增 BUG-521,复发自 BUG-440(决策层重复计算)。
### 5.2 P2 · 校验器把「不是确认」也打回(BUG-522,可与 5.1 同批)
`validateAdoptNarration``/确认|精确/` 是全文匹配;提示词只说「不得承诺确认或精确」,模型很自然会写「这不是确认的分钟」——会被整段打回、静默退到模板,Agent 路径在真实环境的命中率会被这条拉低而且没人知道。
1. 提示词改成「不要出现『确认』『精确』这两个词」(与校验器同口径)。
2. 增加一条可观测:Agent 路径的结果写进本轮 run diagnostic / 服务端日志一个枚举字段 `adopt_narration: "agent" | "template:<validate reason>" | "template:model_error" | "template:not_ready"`,不写模型原文。没有这条,部署后没人能证明 Agent 到底有没有写。
3. `agent.generate` 加超时:`AbortSignal.any([request.signal, AbortSignal.timeout(8000)])`,超时走模板。分类器没有超时是既有缺口,不在本单修,写进 `BLOCKED.md` 或进度记录。
验收:fake model 返回「这不是确认的分钟,先用 05:00。」→ 仍打回(校验器不变),但提示词测试断言含「不要出现」;诊断字段在三种结果下各有一条测试;超时用 fake `generateText` 挂起 + 短 timeout 断言走模板且不抛。
### 5.3 P3 · `stopFactsFromDropped` 的「婚恋题只覆盖窗口两端」标签
`insufficient_candidates || not_renderable` 一律标成婚恋题,而 `not_renderable` 现在也用于 nakshatra。改成按 `semantic_key` 前缀分桶(`relationship.` → 婚恋,其它 → 「有几道题渲染不出来」),或去掉领域词。可推迟。
## 6. 让步顺序
1. 5.1 必做,否则 4.1.4 在最常见入口上等于没做。
2. 5.2 的可观测字段与超时优先于提示词改动。
3. 5.3 可推迟到下一轮。
## 7. 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/rectification-adopt-narration-fix-20260904 .worktrees/rectification-adopt-narration-fix-20260904 origin/staging
cd .worktrees/rectification-adopt-narration-fix-20260904/frontend
./node_modules/.bin/tsc --noEmit
npx tsx --test tests/rectification-adopt-narration-20260904.test.ts tests/rectification-answer-choice.test.ts tests/rectification-collect-stall.test.ts
```
## 8. BUG 编号起点
截至本单:BUG-520。本单从 **BUG-521** 起;开工时再核对。