fix(rectification): keep one spoken paragraph and strip body questions (BUG-584, BUG-585)
Independent Staging Quality Gate / validate (push) Successful in 9m48s
Independent Staging Quality Gate / publish (push) Successful in 7m33s

Set-focus must not append a second narration, and a server-owned stem must not also appear as a question in the assistant body.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-08 09:02:27 +08:00
parent 9aec502961
commit 9d1c08cab1
14 changed files with 430 additions and 53 deletions
+34 -2
View File
@@ -7517,7 +7517,7 @@
- 修复:不再渲染采集题槽。最新助手气泡按题干精确身份 `composeCollectSpokenAssistantText`;定稿后的 Case snapshot、发送下一条、以及挂载后的 snapshot 回调把组合文本写进该条消息状态,复制走组合文本。选择卡仍走问题槽。模型仍不自己提问,避免气泡里两句相近问法。不 bump Skill,不放宽确认门。
- 验证:`rectification-spoken-collect` 锁气泡拼接、禁止 `rectification-question-slot__prompt`;既有 collect-prompt / v9-agent / regenerate / voice 锁保持。staging 门禁 run 2304 因 `showActivity={bubbleMessage.state…}` 对不上 `rectification-agentic-entry` 源码锁失败;`showActivity` 改回 `displayedMessage.state`(气泡仍走 `bubbleMessage`)。run 2305 前端 2509/2509 后 ESLint 在 `currentQuestionRef.current = currentQuestion`render)和 snapshot `setMessages` effect 上报 `react-hooks/refs` / `react-hooks/set-state-in-effect`ref 改到 `useLayoutEffect`,题干写入改到 snapshot/send 回调。
- 防复发:口述采集题干不得作为动作图标下的兄弟节点。不得用正文字符串判断「有没有问过」。不得同时让模型提问又拼接同一句服务端题干。
- 相关记录:BUG-471、BUG-485、BUG-487
- 相关记录:BUG-471、BUG-485、BUG-487、BUG-585
- 复发自:BUG-487(服务端拼接后槽位兜底仍可见)
- 修复版本:待发布
@@ -8241,7 +8241,7 @@
- 修复:`rectification-set-focus` 的 call/result 不再收回已播出正文。读诊断、记证据仍收回。
- 验证:`rectification-step-answer`:打招呼 + set-focus 为 `none`,其后短句仍 `live`read-diagnostics 仍 `retract`
- 防复发:set-focus 不得把同轮已播出的正文 replace 成空。不得把这个例外扩到 record/compare/diagnostics。
- 相关记录:
- 相关记录:BUG-584
- 复发自:无
- 修复版本:待发布
@@ -9046,6 +9046,38 @@
- 复发自:无(区间卡 `ab57d03f` 引入;任务书 4.2 验收未过)
- 修复版本:待发布(`codex/rectification-range-delivery-fix-20260907`
## BUG-584 | 同一轮采集旁白说两遍
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-08
- 影响面:`applyStepAnswerChunk``KEEP_LIVE_SPOKEN_TOOLS``publishSpokenStep``POST /api/rectification/agent`
- 用户现象:每一轮采集助手气泡里有两段近似旁白。第一段已经承接用户刚说的事,第二段换个说法再讲一遍。
- 触发条件:模型在 `rectification-set-focus` 前写出正文,拿到工具结果后再写一段。
- 根因:BUG-533 让 set-focus 不撤回已直播正文。set-focus 之后下一个 step 的正文仍按 `shouldPublishStepText` 发布;`publishSpokenStep` 把两段无分隔拼进 `assistant_message`
- 修复:set-focus 结果后置 `stemAttached`。本轮已经发布过正文则丢掉后一段;尚未发布则仍放行开场问候。set-focus 调用时把未直播的中文半句 `publish` 出去,英文规划稿仍 discard。
- 验证:`frontend/tests/rectification-step-answer.test.ts``frontend/tests/rectification-v9-agent.test.ts` 文本→set-focus→文本只保留第一段。
- 防复发:set-focus 之后不得再拼接第二段正文。开场仍允许「先问候、后 set-focus」或「只在 set-focus 后说一句」。不得把这个例外扩到 record/compare/diagnostics。
- 相关记录:BUG-533
- 复发自:无(533 的放行例外把第二段也留下了)
- 修复版本:待发布(`codex/rectification-duplicate-narration-20260907`
## BUG-585 | 题干在正文和问题块各出现一次
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-08
- 影响面:`stripQuestionSentences``attachQuestionsToTurns``runV9AgentTurn``RECTIFICATION_USER_COPY.collectHandoff`
- 用户现象:旁白末尾已经用自然语言问了下一题,问题块再显示同一句服务端题干。
- 触发条件:本轮 set-focus 成功,模型把问句写进正文;`detachCollectSpokenAssistantText` 只剥逐字后缀。
- 根因:不让模型提问的约束只在提示词。BUG-488 防复发写「不得同时让模型提问又拼接同一句服务端题干」,`d9404976` 把题干改成消息内问题块后没有代码守卫。`collectSpokenEmitted` 是从未使用的常量。
- 修复:有本轮 focus 时,`answer.composed` 前按句删掉与题干相同、题干前 12 字开头、或以问号结尾的句子;剪空则用 `collectHandoff`。变化时 `answer.delta replace:true`。历史回看同样走 `stripQuestionSentences`。删除 `collectSpokenEmitted`
- 验证:`frontend/tests/rectification-collect-prompt.test.ts``frontend/tests/rectification-v9-agent.test.ts``frontend/tests/agent-voice-copy-contract.test.ts`
- 防复发:有 focus 的已结算助手正文不得再出现以问号结尾的句子。不得靠提示词单独保证模型不问。不得恢复把题干拼进 `assistant_message`
- 相关记录:BUG-488、BUG-461、BUG-471、`d9404976`
- 复发自:BUG-488(防线只在提示词)
- 修复版本:待发布(`codex/rectification-duplicate-narration-20260907`
## BUG-586 | 七领域问完落到补问兜底句,职业题被覆盖、区间卡出不来
- 状态:mitigated
@@ -0,0 +1,41 @@
# PROGRESS · 同一轮旁白说两遍、题干出现两次(2026-09-07)
工作树:`.worktrees/rectification-duplicate-narration-20260907`
分支:`codex/rectification-duplicate-narration-20260907`
任务书:`docs/tasks/TASK-rectification-duplicate-narration-20260907.md`
基线:`origin/staging` @ `6c7a8b02`(任务书写 `f3e72702` / 代码头 `ab57d03f`;其后 `a00b069d` 已合入,tsc 不再报 `rectification-agentic-chat.tsx` L1655
本单状态:**待验收**。未 commit、未 push。
未改:提示词、Skill 版本(仍 10.0.15)、采用门、确认门、`MIN_SEPARATION_LEAD``_relative_support``minute_step=1``page.tsx`
并行:`codex/rectification-other-collect-fallback-20260908`BUG-586)也改 `user-copy.ts``agent-voice-copy-contract.test.ts`。本单只加 `collectHandoff`;谁后合谁 rebase。
## 做了什么
- **4.2 / BUG-585** `stripQuestionSentences`:按句删掉与题干相同、题干前 12 字开头、或以问号结尾的句子;问号后的非叙事尾巴一并丢掉。`composeCollectSpokenAssistantText` 改调它。agent-run 在 `answer.composed` 前复用 discriminator 那次 `loadV9CaseDossier`:本轮 focus 有 spokenPrompt 则剪,剪空用 `collectHandoff`,变化时 `replace:true``attachQuestionsToTurns` 同样剪。删除 `collectSpokenEmitted`
- **4.1 / BUG-584** `StepAnswerState` 增加 `stemAttached``publishedAny`,不随 `resetBuffers` 清。set-focus 之后若本轮已发布正文,后续 `text-delta` discard。
- **决策 3** set-focus `tool-call` 把未直播的中文半句 `publish`;无 CJK 的规划稿 discard。未后置为 P3。
## 三栏(被触碰断言)
| 用例 | 原值 | 新值 | 理由 |
| --- | --- | --- | --- |
| set-focus 后第二段正文 | `live` 发布 | `discard` | 决策 2,丢掉工具结果后的复述 |
| set-focus 前无正文、后有一段 | (原测试未覆盖) | 后段 `live` | BUG-533 开场仍通 |
| 无句末标点的中文半句 + set-focus | 静默丢掉 | `publish` | 决策 3 |
| 英文规划稿 + set-focus | 不直播 | `discard` | 维持原 discard |
| `collectSpokenEmitted` | 恒 `false` | 字段删除 | 决策 4 |
| 历史回看剥题干 | 只剥 `\n\n` 后缀 | `stripQuestionSentences` | 决策 1 |
## 测试
- `frontend` `./node_modules/.bin/tsc --noEmit`exit 0
- 任务书指定 TS 切片(`tests/rectification-*.test.ts` + `agent-voice-copy-contract.test.ts`,排除 database):**988 passed / 0 failed**
- Docker `database-rectification-*.test.ts`:本机未跑,记 blocked
- 浏览器真人走查:无登录态,记环境缺口,见 `docs/testing/rectification-scenarios-20260907.md` §6
## 偏离
1. 任务书点名的 `rectification-v9-agent-run*.test.ts` 不存在;agent-run 序列写在既有 `rectification-v9-agent.test.ts`
2. 问号后的「有年份就行。」按非叙事续句丢掉,否则验收 (a) 会留下这半句。
+1 -1
View File
@@ -72,7 +72,7 @@
| `TASK-rectification-stop-rescore-fix-20260907.md` | `PROGRESS-rectification-stop-rescore-fix-20260907.md` | **P0**:BUG-579 的停止路径用「裸重算」(`rescoreMinuteAfterWindowChange` 不重建 `inference_state`、不重放已答探针、不写 transition),停止后范围变宽、卡片变成引擎裸支持度、采用 RPC 报 `candidate_state_inconsistent`;无框区分题经 `spokenCollectFallbackFollowup` 变成 domain=other 的采集题,再次冒出开场句 `GENERIC_COLLECT_QUESTION`;holdout 采集排在带年月采集之前 | 已验收通过(逻辑);但同提交含区间卡 tsc 错,整体不可部署 | `ab57d03f`BUG-581582 |
| `TASK-rectification-range-delivery-card-20260907.md` | `PROGRESS-rectification-range-delivery-card-20260907.md` | 结尾重设计:四张分钟卡 + 相对支持度换成一张区间交付卡(范围、代表分钟、稳定/敏感主题、边界句)+ 只列未问 D9/D10/月宿分歧的 2~3 列对比面板(文案只取类型表),动作只有「按这个范围用 / 再补一件经历」;删交付旁白里三行预测窗口;Skill §9 口径改、bump 10.0.15 | **未通过**:tsc 1 错(判空顺序);P2 预测窗口句兑现不了;P3 再补经历无提示 | `ab57d03f`;修复单 `TASK-rectification-range-delivery-fix-20260907.md` |
| `TASK-rectification-range-delivery-fix-20260907.md` | `PROGRESS-rectification-range-delivery-fix-20260907.md` | 区间卡修复单:`candidateResult` 判空提到最前(tsc 阻断);预测窗口接到采用后首轮或删句;「再补一件经历」隐藏卡后给采集提示;进度记录须贴 tsc 原文 | 待验收 | `codex/rectification-range-delivery-fix-20260907`BUG-583 |
| `TASK-rectification-duplicate-narration-20260907.md` | `PROGRESS-rectification-duplicate-narration-20260907.md` | 每轮采集旁白说两遍 + 题干在正文与问题块各一次:`step-answer` 的 set-focus 例外(BUG-533)放行了 set-focus 前后两个 step 的正文并拼接;`detachCollectSpokenAssistantText` 只剥逐字后缀,模型写进正文的问句没有代码守卫(BUG-488 防线只在提示词) | 待执行 | `codex/rectification-duplicate-narration-20260907`BUG-584585 |
| `TASK-rectification-duplicate-narration-20260907.md` | `PROGRESS-rectification-duplicate-narration-20260907.md` | 每轮采集旁白说两遍 + 题干在正文与问题块各一次:`step-answer` 的 set-focus 例外(BUG-533)放行了 set-focus 前后两个 step 的正文并拼接;`detachCollectSpokenAssistantText` 只剥逐字后缀,模型写进正文的问句没有代码守卫(BUG-488 防线只在提示词) | 待验收 | `codex/rectification-duplicate-narration-20260907`BUG-584585 |
| `TASK-rectification-other-collect-fallback-20260908.md` | `PROGRESS-rectification-other-collect-fallback-20260908.md` | 七领域问完落到「也可以再说一件」、职业题从未出现、不自动出卡:`USER_COLLECT_QUESTION.other` 仍有三条活路(职业焦点 `target_domain` 压成 other 后被 active-focus 承接 followup 重建;set-focus 两次无效兜底查表;无领域采集归 other),`collect:other:*` 焦点挂着让 `persistNextInterviewIfIdle` 的出卡路径永远不跑 | 待验收(2.2 触发链仍 investigating | `codex/rectification-other-collect-fallback-20260908`BUG-586 |
| `TASK-api-not-configured-mislabel-20260904.md` | `PROGRESS-api-not-configured-mislabel-20260904.md` | 16 处路由把数据库瞬断(部署切换窗口)兜底翻译成 503「服务尚未配置」;改为仅配置错误用该文案,其余 `service_unavailable`,收敛为共享 helper | 已验收 | `5483649b`(BUG-542);2 条子进程测试留 CI Node 22 复核 |
| `TASK-rectification-ux-20260902.md` | `PROGRESS-rectification-ux-20260903.md` | 会话面空白假死与交互摩擦 | 已验收 | `d159f08e`(09-03 在新基线重做后合入,BUG-505509 |
@@ -102,3 +102,15 @@
- 不得出现补问兜底句「也可以再说一件你记得大概时间的事」
- 职业答过、又没有剩余采集时,必须出现区间交付卡,不能只剩停止按钮
## 7. 每轮助手气泡只有一段旁白,题干只在问题块出现一次
资料:家人记得大概时间,钟点任意,范围「差不多准」。地点任意公开城市。
开场后说一件带年月的虚构经历,等到出现下一问。
期望:
- 助手气泡正文只有一段旁白,没有第二段换个说法再讲一遍
- 题干只出现在同一条消息的问题块里一次
- 正文不以问号结尾,也不把下一题再问一遍