# TASK · 生时校正消息清理:题干只出现一次 / 正文复述变短 / 去掉结算后的步骤回执(2026-10-01) ## 基线 - `origin/staging` @ `00420f89`(写作时 head;开工时以最新 `origin/staging` 为准)。盘型口径(BUG-1115~1117、1131)已在其中。 - 分支 `codex/rectification-message-cleanup-20261001`,工作树 `.worktrees/rectification-message-cleanup-20261001`。 - **并行关系**:与 `TASK-rectification-chart-tier-caveats-20261001.md`(报告 / 聊天标注不可判分盘)文件不重叠,可并行;两单都会追加 `docs/BUG_HISTORY.md`、`CHANGELOG.md`、`frontend/DESIGN.md`,后合入者解冲突保留两侧。与 `TASK-consult-plain-answer-20261001.md` 不重叠。 - 不动冻结计分文件(`sealed_holdout_rerun.py::PRODUCTION_FILES`)、引擎、出题、出卡时机、盘型交付逻辑。 ## 事故实证 产品负责人 2026-09-29 staging 真机,一条校正回复呈现为: ``` 已完成 3 步 1. 记下了:2000年 随家人搬家、2006年 小学毕业、…、2025-02 工作开始不稳定。 2024 年 1 月前后,你有过搬家或者长期住到外地的情况吗? 2024 年 1 月前后,你有过搬家或者长期住到外地的情况吗? A. 明确发生且时间吻合 B. 发生过但程度较弱 C. 明确没有发生 D. 这段记不清楚 ``` 2026-10-01 产品追问「为什么 Agent 输出下面要跟一个问题,这一部分怎么优化」。Claude 只读调研(`origin/staging` @ `a8820f2c`): | 块 | 渲染位置 | | --- | --- | | 步骤回执「已完成 N 步」 | `components/chat-message-row.tsx`(rectification 分支)→ `ConsultationRunTimeline`(`components/consultation-run-timeline.tsx` 的 `已完成 ${rows.length} 步`),行来自 `rectification-message-entry.tsx::rectificationTimelineRows` | | 正文 | `ChatMessageContent`(`chat-message-row.tsx`) | | 题干 | `rectification-message-entry.tsx` 的 `
{question.prompt}
`——**只要 `question` 存在且消息已结算就打印,即使嵌入卡为 null(作废 / 被替换的旧题)** | | 选项 | `components/rectification-choice-card.tsx`(legend 是 sr-only,不另显示题目) | | 独立问题块 | `components/rectification-question-gap-notices.tsx` 两处打印 `currentQuestion.prompt` + 嵌入卡 | 题干可见位置共三处:正文(未剥净时)、消息内题干、独立问题块。 ## 根因 1. **题干两遍(主因,待执行方 DOM 复现确认)**:最新消息挂着一道已作废 / 被替换的旧焦点,题干文字与新焦点相同。`rectification-message-entry.tsx` 对它仍打印题干(BUG-917 路径,无选项);`rectification-snapshot-messages.ts::placePersistedQuestion` 因焦点 id 不同把新题设为 `standalone`,`rectification-question-gap-notices.tsx` 再打印一次题干 + 选项 → 「题干、题干、选项」。`turn-question.ts::attachQuestionsToTurns` 同一轮多个焦点只保留最后一个,是可能的诱因。 2. **次因(同时防住)**:`placePersistedQuestion` 把问题挂到最新已结算消息时**不剥正文里的题干**;正文剥离只在 `agent-run-attempt.ts`(焦点已绑本轮时)、`turn-question.ts`(GET)、`rectification-snapshot-messages.ts::mergeTurnQuestions`(turnId 命中时)三条路径做。 3. **正文过长**:证据轮正文按提示要求逐条复述「记下了:年 月 事件、…」,开场一次说 12 件时整段占满屏幕,题目被挤到下面。 4. **结算后步骤回执**:记录经历、重打分、出题是系统内部步骤,结算后折叠的「已完成 N 步」对用户无信息量;「1.」疑为列表序号泄漏(`globals.css` 已 `list-style: none`,需执行方定位)。 ## 决策记录(产品 2026-10-01) - **D1 一屏一题干**:任一时刻屏幕上只有**当前有效焦点**打印题干;作废 / 被替换的旧焦点在消息上**什么都不显示**(不留半句题干,不显示「已作废」)。题干只能出现一次(`DESIGN.md` 现有规则不变,本单补代码守卫)。 - **推翻**:BUG-917 中「作废题保留题干、去掉选项」的显示方式;执行方在该条防复发下追加「2026-10-01 产品修订:见本任务书 D1」,不删原文。 - **D2 正文复述变短**:证据轮正文只说「记下了 N 件事」(N = 本轮实际入账件数);入账清单作为可展开的列表,**数据来自服务端证据记录**,不来自模型正文。N ≤ 2 时可直接列出两件,不折叠。 - 推翻 `frontend/docs/VOICE.md` 与 Agent 提示中「证据轮正文只写一句复述『记下了:年 月 事件短语(、…)。』」的逐条复述要求,改为本条。 - **D3 校正回复结算后不再显示「已完成 N 步」**:生成中的步骤进度(等待态)保留不变;结算后整块不渲染。只作用于生时校正,普通对话不变。 - 推翻 `DESIGN.md` 中校正与普通对话「共享全部等待词汇」里**结算后回执**这一项;等待态共享不变。 - **不做**:不改选项、不改题干写法(仍由 Agent 经 set-focus 写、服务端核对)、不改出题与计分、不改盘型交付卡。 ## 硬红线 1. 不得再出现第二个问题渲染位置;不得手写第二个聊天输入框 / 滚动跟随 / 加载动画(AGENTS §6)。 2. 刷新、历史打开、实时三条路径(实时流式、GET 重建、快照合并)都满足 D1;旧 Case(盘型口径之前、旧 Skill 版本)打开不报错、不丢选项(BUG-621)。 3. 前端:tsc 0、lint 0 error、npm test 失败清单与开工基线逐名一致且基线测试名 0 丢失(按 `frontend/AGENTS.md` 的名单 diff)、`/` Static、首屏 gzip ±2%。 4. 改既有断言写「原值 / 新值 / 原因」;按源码文本断言的合同测试先 grep(`frontend/tests/`、`tests/`)。 5. Agent 提示改动按 CHANGELOG 规则记录;若改到 Skill 文本须 bump 并验证历史 Case 可打开。 6. 隐私:测试 fixture 只用虚构数据;Bug 历史不写真机事件内容。 ## 任务分解 - **T1 一屏一题干(BUG-1135)** - 先写失败测试复现两种形状:(a) 最新消息带作废旧焦点 + 独立问题块的新焦点、题干文字相同 → 页面题干出现 2 次;(b) 正文末尾含题干句 + `placePersistedQuestion` 挂题 → 题干出现 2 次。DOM 级(渲染后数 `rectification-message-question__prompt` 与 `persisted-question` 中题干文本出现次数),不只断言数据结构。 - 修:`rectification-message-entry.tsx` 只为当前有效焦点打印题干;`placePersistedQuestion` 挂题时同样剥正文题干句(复用 `collect-prompt.ts::stripQuestionSentences`,不新写正则);查清 `attachQuestionsToTurns` 同轮多焦点是否会让旧焦点落到最新消息,必要时只挂有效焦点。 - 验收:(a)(b) 先红后绿;实时 / GET / 快照三路径表驱动断言「题干恰好 1 次」;BUG-917、BUG-1045、BUG-1046 的既有测试不放宽。 - **T2 正文复述变短(BUG-1136)** - 服务端:证据轮正文改为确定性的「记下了 N 件事」,N 与清单来自本轮入账证据(`agentic_rectification_evidence` 读模型 / 现有 dossier 投影),模型不再写复述;Agent 提示相应删去逐条复述要求。 - 前端:清单作为该消息内可展开列表(默认折叠,N ≤ 2 直接显示),展开交互不新增加载动画。 - 验收:一次说 12 件 → 正文一行 + 折叠清单 12 条,日期与精度显示与 `display_date_label` 一致;0 件入账(全被拒)时正文说明而非「记下了 0 件」;`VOICE.md` / `DESIGN.md` 同提交更新。 - **T3 结算后不显示步骤回执 + 「1.」泄漏(BUG-1137)** - 校正消息结算后不渲染 `ConsultationRunTimeline`;生成中保留。定位截图中「1.」来源并修。 - 验收:结算 / 刷新 / 历史打开均无「已完成 N 步」;生成中步骤进度照常;普通对话回执不变(测试锁定)。 - **T4 记录**:`docs/BUG_HISTORY.md`(BUG-1135~1137,检索 BUG-585 / 917 / 1045 / 1046 并在 1135 写「复发自」关联及旧防线为何未拦住)、`CHANGELOG.md`、`frontend/DESIGN.md`、`frontend/docs/VOICE.md`、`docs/testing/rectification-message-cleanup-20261001.md` 真机清单(开场一次说 10 件以上;答一题后出下一题;刷新;打开旧 Case)、`docs/tasks/PROGRESS-rectification-message-cleanup-20261001.md`、`docs/tasks/README.md`。 ## 让步顺序 T1 > T3 > T2 > T4 之外的截图。T1 缺之不可合入。 ## 开工前置命令 ```bash git fetch origin --prune export PATH=/exec-daemon:$PATH && node -v # 22.x git worktree add -b codex/rectification-message-cleanup-20261001 .worktrees/rectification-message-cleanup-20261001 origin/staging cd .worktrees/rectification-message-cleanup-20261001/frontend && npm ci ./node_modules/.bin/tsc --noEmit && npm run lint npm test 2>&1 | grep -E "^(not )?ok [0-9]+ - " | sed -E 's/^(not )?ok [0-9]+ - //' | sort > /tmp/msg-cleanup-names-base.txt ``` ## BUG 编号 开工时核对 `docs/BUG_HISTORY.md` 最大号(写作时 `BUG-1131`;`1132`~`1134` 已被 `TASK-consult-plain-answer-20261001.md` 预留)。本单:**BUG-1135**(题干两遍)、**BUG-1136**(证据轮正文逐条复述过长)、**BUG-1137**(结算后步骤回执与「1.」泄漏)。号冲突顺延并在 PROGRESS 说明。关联 BUG-585、BUG-917、BUG-1045、BUG-1046、BUG-621。