fix(rectification): keep skipped health out of holdout and repair empty exits (BUG-626, BUG-627)
Independent Staging Quality Gate / validate (push) Successful in 12m11s
Independent Staging Quality Gate / publish (push) Successful in 7m54s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-10 11:59:47 +08:00
co-authored by Cursor
parent 0263658477
commit d96b24c276
16 changed files with 615 additions and 76 deletions
+32
View File
@@ -9716,6 +9716,38 @@
- 复发自:无
- 修复版本:待发布
## BUG-626 | 跳过的健康线在 holdout 里被当成没用过,撞同 id 焦点后静默断流
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:`holdoutFollowupFor``persistNextInterviewAfterChoice``persistExhaustionCollect`
- 用户现象:健康采集点「记不清」后再答完职业,界面只剩「没有拿到下一个问题。」
- 触发条件:七领域采集接近穷尽,健康焦点 `target_domain=health``skipped`,盘外提示仍是 `health_pressure`
- 根因:BUG-590 只给 occupied 归并 health / health_pressureholdout 的 declined 也曾漏过这一层;即便归并后,`duplicate_focus` 仍可能落到空旁白。DB 约束只有 `health`,计划层用 `health_pressure`
- 修复:holdout 的 declined 与 occupied 一样归并 health / health_pressure。`duplicate_focus``rectification_focus_duplicate` 并走 `persistExhaustionCollect`;穷尽旁白为空时用 `noCandidatesGate` 兜底。
- 验证:`frontend/tests/rectification-collect-direction-20260904.test.ts``frontend/tests/rectification-skipped-health-deadend-20260909.test.ts`
- 防复发:跳过的健康线本会话不得再作 holdout;`duplicate_focus` 必须留下可见载体。
- 相关记录:BUG-558、BUG-590、BUG-591、BUG-605
- 复发自:BUG-590 决策 4occupied 已归并,declined 曾漏)
- 修复版本:待发布
## BUG-627 | 出口闸门把穷尽当成已交付,断流后不修复
- 状态:resolved
- 首次发现:2026-09-09
- 最近更新:2026-09-09
- 影响面:`inspectNonTerminalTurnExit``ensureNonTerminalTurnExit``agent-run` idle catch、`POST /api/rectification/cases/[caseId]/repair-exit`
- 用户现象:穷尽后没有任何问题、卡或句子落库,闸门仍放行;「重新加载」只重取空快照。
- 触发条件:`exhausted` 为真,无活动焦点、未采用、无 terminalNote host turn。
- 根因:`inspectNonTerminalTurnExit.satisfied``exhausted`。穷尽只是状态,不是用户能看见的出口。idle 异常被 `agent-run` 吞掉后也到不了闸门。
- 修复:satisfied 只认活动问题 / 已采用 / 已确认 / 已落库的 terminalNote host turn / `publicCanAdopt`。无载体时 `ensureNonTerminalTurnExit` 写出门槛 host turn。idle 失败后仍调用闸门。缺口按钮改调 `repair-exit`,文案「接着问」;连续两次仍无载体才提示新建。
- 验证:`frontend/tests/rectification-exhaustion-exit-20260906.test.ts``frontend/tests/rectification-skipped-health-deadend-20260909.test.ts`
- 防复发:穷尽不得单独满足出口闸门;无可见载体必须写出 host turn。
- 相关记录:BUG-558、BUG-626
- 复发自:BUG-558 防复发条款被闸门绕过
- 修复版本:待发布
## BUG-628 | 口述采集「没有 / 记不清」按钮被用户差评
- 状态:resolved
@@ -0,0 +1,39 @@
# PROGRESS · 跳过健康后 holdout 断流 + 出口闸门(2026-09-09
工作树:`.worktrees/rectification-skipped-health-deadend-20260909`
分支:`codex/rectification-skipped-health-deadend-20260909`
任务书:`docs/tasks/TASK-rectification-skipped-health-deadend-20260909.md`
基线:开工时 `origin/staging` @ `4eea9c10`
## 开工回执
- 目标:BUG-626 跳过的健康线不再进 holdout,`duplicate_focus` 必须留下可见载体;BUG-627 出口闸门不再把穷尽当成已交付;缺口按钮改为服务端修复。
- 编号:BUG-628 已被「没有 / 记不清」按钮占用,本单只用 626/627。
- 决策 1 在基线里已有 `declinedHealth` 特判;本单改成与 occupied 一样把两个别名写入 blocked 集合,并补 skipped `health` 回归。
## 已完成
- **4.1 / BUG-626**`holdoutFollowupFor``declinedForHoldout``duplicate_focus``rectification_focus_duplicate` 并走 `persistExhaustionCollect`;穷尽旁白为空时用 `noCandidatesGate``persistExhaustionCollect` 只在 `duplicate_focus` 时落到门槛句,其它 persist 失败仍返回采集旁白,避免证据轮被误标 `terminalNote` 而不裁句。
- **4.2 / BUG-627**`inspectNonTerminalTurnExit.satisfied` 去掉 `exhausted`,改认活动问题 / 采用 / 确认 / terminalNote host turn / `publicCanAdopt`。无载体时写出门槛 host turn。idle catch 仍调 `ensureNonTerminalTurnExit`
- **4.3**`POST /api/rectification/cases/[caseId]/repair-exit`;按钮「接着问」;两次仍无载体显示「暂时接不上,请新建一次校正。」
- **4.4**BUG-626 / 627、CHANGELOG、本文件、走查第 12 条。
## 验收命令
`frontend/`
- `./node_modules/.bin/tsc --noEmit`:通过
- `npm run lint`0 error(既有 107 warning
- 定向 `tsx --test`
- `rectification-skipped-health-deadend-20260909.test.ts`
- `rectification-collect-direction-20260904.test.ts`
- `rectification-exhaustion-exit-20260906.test.ts`
- `rectification-surface-contract.test.ts`
- `rectification-agentic-entry.test.ts`
- `rectification-spoken-collect.test.ts`
- `rectification-collect-stall.test.ts`
**195 / 195 通过**(含 `rectification-v9-stream.test.ts``rectification-answer-choice.test.ts`
- 全量 `tests/rectification-*.test.ts`(排除 database):**1067 / 1067 通过**
- 相关补充:`rectification-server-focus.test.ts``rectification-decision-authority.test.ts``rectification-covered-domain-recollect-20260908.test.ts`**39 / 39 通过**
本单状态:**待验收**。未改 Skill 版本、未改采用/确认门、未新增迁移。
+1 -1
View File
@@ -90,7 +90,7 @@
| `TASK-rectification-compare-card-polish-20260909.md` | `PROGRESS-rectification-compare-card-polish-20260909.md` | 三列卡两列经历对照 0、三列无未来窗:引擎按引擎前三算 by_time,卡片按推断前三查表,键不匹配(BUG-614);交付旁白被 BUG-606 裁成一句(BUG-615);设计减法:相同性格句只写一次、经历对照说人话、删小标题、右栏本命宫位改北印星盘、换升时刻默认折叠 | 已验收通过(by_time 只算 9 个 → BUG-632 | BUG-614615 |
| `TASK-rectification-history-open-20260909.md` | `PROGRESS-rectification-history-open-20260909.md` | 历史生时校正打不开:`open_agentic_rectification_case_v2` 把 Case 绑定 Skill 身份与当前注册版本做相等比较,两天内 10.0.15→10.0.19 五次 bump 让所有旧 Case 被拒;错误码未映射成 500;错误只画在首页卡下 → 表现为点了没反应。决策:session 打开用绑定身份(v1 取 Case → 读身份 → 注册表核验 → v2),不改迁移 | 已验收通过 | `a31a5e24`BUG-621 |
| `TASK-rectification-window-cluster-cap-20260909.md` | `PROGRESS-rectification-window-cluster-cap-20260909.md` | **P0** 真实用户:一小时窗口分 17 个签名簇,`select_signature_representatives` 按时间取前 12 个,14:40 之后整簇丢弃(本机复现表);区间按代表分钟跨度而非簇覆盖;中途说出真实时段时助手口头答应却未改 → 改为固定回复"范围开始时按资料定、中途不改"(产品否决口头改范围);intake 自定义范围等产品答复 | 已验收通过(申报拦截误伤经历 → BUG-631;intake 自定义范围等产品) | `6008c07c`BUG-623625Skill 10.0.20 |
| `TASK-rectification-skipped-health-deadend-20260909.md` | `PROGRESS-rectification-skipped-health-deadend-20260909.md` | 真实用户:健康题「记不清」后职业答完即断(『没有拿到下一个问题』):holdout 的 `declined` 未归并 health/health_pressure,把跳过的健康线再问一次,撞同 id 焦点 `duplicate_focus` 静默;出口闸门把 `exhausted` 当已交付不修复;『重新加载』只重取快照 | 待执行 | `codex/rectification-skipped-health-deadend-20260909`BUG-626627 |
| `TASK-rectification-skipped-health-deadend-20260909.md` | `PROGRESS-rectification-skipped-health-deadend-20260909.md` | 真实用户:健康题「记不清」后职业答完即断(『没有拿到下一个问题』):holdout 的 `declined` 未归并 health/health_pressure,把跳过的健康线再问一次,撞同 id 焦点 `duplicate_focus` 静默;出口闸门把 `exhausted` 当已交付不修复;『重新加载』只重取快照 | 待验收 | `codex/rectification-skipped-health-deadend-20260909`BUG-626627 |
| `TASK-rectification-domain-alias-audit-20260909.md` | `PROGRESS-rectification-domain-alias-audit-20260909.md` | 领域命名审计:健康线 `health`(账本/焦点)与 `health_pressure`(计划/引擎)在十处比较里六处未归并(holdout declined、reverse-verify、conflict probes、probeYearAlreadyCovered、引擎 oos_blind_prompts / _event_years / volunteered);职业线焦点存为 `other` 只靠 questionId 兜底。其余六领域三层同名无问题。决策:TS/Python 各一个归并函数 + 源码合同禁字面量比较 | 待执行 | `codex/rectification-domain-alias-audit-20260909`BUG-628 |
| `TASK-rectification-yearless-probe-downgrade-20260909.md` | `PROGRESS-rectification-yearless-probe-downgrade-20260909.md` | 产品拍板:无年月性格题(D9/D10 风格、月宿边界)降级为平局裁决——带年月题问完且候选仍分不开才问,分值减半(±1)、不计淘汰,报告标「参考」;三列卡性格描述不动;新增离线命中率测量脚本 + 导出 SQL(有出生证用户,聚合);Skill 10.0.21 | 已验收通过 | `fef54b6e`BUG-629Skill 10.0.21 |
| `TASK-rectification-followups-20260909.md` | `PROGRESS-rectification-followups-20260909.md` | 验收补漏:申报时段拦截只看钟点样式,带钟点的经历(『20:00 左右分手』『3 点到 5 点被车撞』)会被吞(BUG-631);by_time 只算引擎前 9 个候选,一小时窗 17 个候选时卡片列写『还没对照』(BUG-632) | 待验收 | `codex/rectification-followups-20260909`BUG-631632 |
@@ -175,7 +175,7 @@
- 每轮助手正文一句复述(可另接一句范围变化);气泡里没有「本轮对照了…」,也没有「很有帮助 / 很有价值 / 很有分量 / 特别有用」
- 报经历时带钟点(「20:00 左右分手」「下午 3 点到 5 点被车撞」)不得被当成申报出生时段,不得回「搜索范围开始时按资料定、过程中不改」
## 8. 性格题只作平局参考(BUG-629)
## 12. 性格题只作平局参考(BUG-629)
资料与开场同第 0 条。虚构经历。
@@ -187,7 +187,7 @@
- 交付后展开验证报告,「D9 / D10 类型对照」和「月宿边界」状态是 `reference`,说明是性格自评、只作排序参考、不参与淘汰
- 三列对照卡上的性格句子仍按类型表展示,不改写
## 8. 证据轮模型没写正文时必须出现「记下了」和下一问
## 13. 证据轮模型没写正文时必须出现「记下了」和下一问
资料与开场同第 0 条。虚构经历说到搬家或入职一件带年月的事。
@@ -197,3 +197,17 @@
- 下一问可见(气泡里或主持人问题行),输入框在问题可见时才写「请回答上面的问题…」
- 点选题范围没变时应能看到领先或落后;全零才只说「范围没变」
- 顶部时间线不得写「还在收窄」
## 14. 健康题点「记不清」后职业答完必须出卡或门槛句
资料:家人记得大概时间,钟点任意,范围「差不多准」。地点任意公开城市。虚构经历。
采集走到健康题时打字「记不清」,再答完职业题。
期望:
- 职业答完后必须出现下一张卡、下一问或门槛句
- 不得出现「没有拿到下一个问题。」
- 缺口按钮若出现,文案是「接着问」,点了会向服务端修复,不是只重取快照
- 连续两次仍无载体才显示「暂时接不上,请新建一次校正。」