docs(tasks): regression fix brief for premature tied-first delivery
d5a8db0a 把 tied_first 早退放到 coverageBlocks 之前且没有 probe 守卫,新案子
答两题、区间仍是整个开局窗口就判 adopt_representative/can_adopt:true/代表
分钟(BUG-683)。配套测试传 discriminatorProbe: null,与实现语义不一致,所以
没拦住。另立 BUG-684(investigating):交付态下仍有画不出的死卡,delivered
终态因要求无题而不触发,界面仍印「没有拿到下一个问题」。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
This commit is contained in:
co-authored by
Claude Fable 5
parent
d5a8db0a4e
commit
e42a62dfac
@@ -184,6 +184,8 @@
|
||||
|
||||
| `TASK-rectification-targeted-card-dead-20260913.md` | `PROGRESS-rectification-targeted-card-dead-20260913.md` | **P0**:定向补事卡在快照投影里拿不到 `choice_card`(承接焦点分支不重建 `choice_frame`),卡片看得见点不动、流程停在采集等待态;模型还会把定向题改写成口述题(BUG-669~671)。先于 tie-break 修复单执行 | 待验收 | `codex/rectification-targeted-card-dead-20260913` |
|
||||
|
||||
| `TASK-rectification-tied-first-premature-delivery-fix-20260914.md` | `PROGRESS-rectification-tied-first-fix-20260914.md` | **P0 回归修复单**:BUG-680 的 `tied_first` 早退被实现到 `coverageBlocks` 之前且无 `probe` 守卫 → 新案子答两题、区间还是整个开局窗口就宣布 `can_adopt`/代表分钟(BUG-683);交付态下仍出现死卡致 `delivered` 终态不触发、印「没有拿到下一个问题」(BUG-684 investigating) | 待执行 | `codex/rectification-tied-first-fix-20260914` |
|
||||
|
||||
| `TASK-rectification-delivery-vs-collect-split-20260914.md` | `PROGRESS-rectification-delivery-vs-collect-20260914.md` | **P0**:并列到顶(tied_first)时 POST 播了交付结论、GET 又算回 collect_evidence——刷新尝试落库失败仍回报「已尝试」+ `stillNeedNarrowing` 压过 exhausted(BUG-680);交付话术闸门与交付卡闸门不一致,`completed_with_range`/`provisional_range` 能说不能画(BUG-681);缺口状态机没有终态,兜底印「没有拿到下一个问题」(BUG-682) | 待验收 | `codex/rectification-delivery-vs-collect-20260914` |
|
||||
|
||||
| `TASK-rectification-spoken-orphan-and-engine-representative-20260914.md` | `PROGRESS-rectification-spoken-orphan-20260914.md` | **P0**:口述题(定向补事年份追问)没有 `askedTurnId` 时仍是无头像裸行——BUG-675 的挂回规则只覆盖选择题(BUG-678);同屏两个范围口径(旁白用活跃候选首尾、时间轴用 credible_range);引擎 `representative_time` 把已淘汰分钟的宫位表带进模型上下文(BUG-676 收敛为 resolved) | 待验收 | `codex/rectification-spoken-orphan-20260914` |
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
# TASK · 修复单:并列早退把第二题就判成"可采用",交付态下又出现死卡 — 2026-09-14
|
||||
|
||||
- 基线:`origin/staging` @ `d5a8db0a`(staging 已部署该 SHA)。
|
||||
- 分支:`codex/rectification-tied-first-fix-20260914`,worktree `.worktrees/rectification-tied-first-fix-20260914`。
|
||||
- 关联:**本单是 `TASK-rectification-delivery-vs-collect-split-20260914.md`(BUG-680/681/682,实现 `d5a8db0a`)的回归修复单**。BUG-680 的 D2 被实现得比任务书宽,造成线上更严重的问题。另关联 BUG-674(不可盖章探针 → 死卡)、BUG-671。
|
||||
- 串行:改 `core/rectification-decision.ts` 与其测试;与其他在途分支无重叠。
|
||||
|
||||
## 1. 事故实证(产品负责人 staging 真机,2026-09-14,部署 SHA `d5a8db0a`)
|
||||
|
||||
新建校正,只回答了两轮采集(2016-09 上大学 / 2020-06 毕业;2020-04 实习 / 2020-10 离职),助手随即问第三题「2023 年 5 月前后,你有没有入职、换工作,或者当时手上的职责明显变重?」,界面同时弹出 **「没有拿到下一个问题。」+「接着问」按钮**。
|
||||
|
||||
同一会话 GET 快照(已脱敏):
|
||||
|
||||
```jsonc
|
||||
"interview": {
|
||||
"type": "complete_with_range", "session_outcome": "adopt_representative",
|
||||
"can_offer_range": true, "can_adopt": true,
|
||||
"selection_allowed": true, "propose_allowed": true,
|
||||
"precision_stage": "ready_to_adopt", "stop_reason": "tied_first",
|
||||
"representative_time": "05:06", "credible_range": ["04:45","05:15"],
|
||||
"collection_progress": { "scoreable": 3, "minimum": 3, "missing": 0 }
|
||||
}
|
||||
```
|
||||
|
||||
候选(9 个,前三名并列):05:06 = 13、05:08 = 13、05:14 = 13、05:03 = 12、04:59 = 11、04:46 = 10、04:53 = 10、05:15 = 10、04:50 = 8。
|
||||
|
||||
**`credible_range` 就是开局窗口 04:45–05:15(30 分钟,完全没有收窄),系统却宣布 `can_adopt: true`、代表分钟 05:06、`precision_stage: ready_to_adopt`。**
|
||||
|
||||
### P0-1 · 并列早退缺少"还有题可问"的守卫(BUG-683)
|
||||
|
||||
`d5a8db0a` 在 `decideRectification` 里加的早退:
|
||||
|
||||
```ts
|
||||
// core/rectification-decision.ts,插在 `if (coverageBlocks) {` 之前
|
||||
if (
|
||||
stopClass?.kind === "exhausted"
|
||||
&& stopClass.reason === "tied_first"
|
||||
&& input.trainingGateOpen !== false
|
||||
&& separation.ranked.length > 0
|
||||
) {
|
||||
return completeWithRange(separation, holdout, range, "exhausted", capability, stopClass.reason);
|
||||
}
|
||||
```
|
||||
|
||||
问题有两层:
|
||||
|
||||
1. **位置比任务书写的高一层。** `TASK-…-delivery-vs-collect-split-20260914.md` 的 D2 原文是「`decideRectification` 的 **`!separation.sufficient` 分支里**,把 `tied_first` 的判断提到 `stillNeedNarrowing` 之前」。该分支的第一行是 `if (probe) return discriminateOrExhaust(...)`,所以按任务书实现时"还有带年月探针可问"会先接管。实现把它提到了 `coverageBlocks` 之前、整个 `!separation.sufficient` 分支之外,于是**连"还有探针"和"方法覆盖未达标"都绕过了**。
|
||||
2. **缺 `probe` 与定向补事守卫。** 校正刚开始时,9 个候选分数都很接近,前两名并列是常态(本例第 2 轮就三方并列 13 分),此时 `tiedForFirst` 为真 → 直接交付。结果是**任何新案子答两题就被宣布"可采用代表分钟",而区间等于整个开局窗口**。
|
||||
|
||||
配套测试 `frontend/tests/rectification-delivery-vs-collect-20260914.test.ts:213` 传的是 `discriminatorProbe: null`,也就是说**测试锁的是"没有探针可问时才交付",实现却没有这个条件**——测试与实现的语义不一致,所以回归没被拦住。
|
||||
|
||||
### P0-2 · 交付态下仍出现死卡,`delivered` 终态因此不触发(BUG-684,investigating)
|
||||
|
||||
`d5a8db0a` 新增的 `interviewDeliveredGap` 要求 `questionMissing === true`。本例屏幕上有一道问题(「2023 年 5 月前后…」),说明 `current_question` 不为空 → `delivered` 不触发;而这道题又没有可点选项 → `interviewChoiceCardUnavailable` 判死卡 → `questionLoadFailed` → 落到 `unavailable` → 印「没有拿到下一个问题」+「接着问」。
|
||||
|
||||
即:**决策层已经终局(complete_with_range),对话层还在问题态,而这道题是一张画不出来的死卡。** 这与 BUG-674 同源(焦点写得进、卡画不出,或反之)。
|
||||
|
||||
**证据缺口**:需要同一会话 GET 的 `current_question`、`choice_card`、`question_source`,以及最后一条助手 turn 的 `offer_result_id`。缺这四项之前,本条按 `investigating` 立号,不得臆断是哪条写入路径。
|
||||
|
||||
## 2. 根因
|
||||
|
||||
- BUG-683:并列(`tied_first`)被当成"无论如何都问不下去"的终局信号,但它在采集早期只是"分数还没拉开"。终局的真正条件是**没有带年月探针可问、且定向补事也问完**。
|
||||
- BUG-684:待取证(见上)。
|
||||
|
||||
## 3. 决策记录
|
||||
|
||||
| 决策 | 内容 |
|
||||
| --- | --- |
|
||||
| D1 | **`tied_first` 早退必须同时满足:没有可问的带年月探针(`probe === null`)、定向补事不再有题(`targetedCollectExhausted !== false`)、训练门开、`ranked` 非空。** `refreshExhausted` 不参与该判断(它的落库不可靠,BUG-680 已证)。这修正上一单 D2 的表述含糊:不是"提到 `stillNeedNarrowing` 之前就完事",而是"在**已经没有别的问法**时才算终局"。 |
|
||||
| D2 | **区间没有收窄就不得宣布可采用。** 当 `credible_range` 等于开局候选窗口(首尾与 `case.candidateRange` 相同)时,即便走到交付,`sessionOutcome` 也只能是 `provisional_range` / `completed_with_range`,`can_adopt` 必须为 `false`:可以给并列区间、可以让用户看候选,但不得给"推荐采用的代表分钟"。这是 `AGENTS.md` Part B B4 的诚实边界,不得因为想让流程收尾而放宽。 |
|
||||
| D3 | 测试与实现语义必须一致:`tied_first` 的交付测试要**成对**——有探针时不交付、无探针且定向补事问完时交付。上一轮只写了后者。 |
|
||||
| D4 | BUG-684 本轮只做取证:在死卡分支加一条结构化日志(`event: "rectification_delivered_state_dead_choice"`,带 `session_outcome`、`stop_reason`、`question_id`、`has_choice_card`),并把 `delivered` 终态的判定从"必须无题"放宽为"无题**或**当前题是画不出来的死卡"——交付态下不得再出现「没有拿到下一个问题」。根因确认后另立修复单。 |
|
||||
| D5 | 不动数据库结构与迁移、不动 `deploy/**` 与 workflow、不动 `page.tsx`、不新增依赖。不得放宽任何置信度或确认边界。 |
|
||||
|
||||
## 4. 硬红线
|
||||
|
||||
1. `tsc --noEmit` 0 错;`npm run lint` 0 error;`npm test` 失败清单与基线逐条一致;`next build` 通过且 `/` 仍 `○ Static`;首屏 gzip ±2%。
|
||||
2. 新增断言必须是行为断言。
|
||||
3. 不得回退 BUG-680/681/682 的三项修复(刷新落库失败不算已尝试、交付话术与交付卡同真同假、`delivered` 终态)——本单只收紧 `tied_first` 的触发条件。
|
||||
4. 不得为了让并列局面"能收尾"而把 `can_adopt` 或 `confirmation_allowed` 放宽。
|
||||
|
||||
## 5. 任务分解
|
||||
|
||||
### 任务 1 · 收紧 `tied_first` 早退(BUG-683,P0)
|
||||
|
||||
- `core/rectification-decision.ts`:早退条件加 `&& !probe && input.targetedCollectExhausted !== false`;位置维持在 `coverageBlocks` 之前即可(有了 `!probe` 守卫就不会抢走判别题)。
|
||||
- 验收标准(成对):
|
||||
- 行为单测:并列 + `discriminatorProbe` 非空 → 决策**不是** `complete_with_range`,仍走判别/采集,`can_adopt === false`。
|
||||
- 行为单测:并列 + `discriminatorProbe: null` + `targetedCollectExhausted: true` → `complete_with_range`、`stop_reason = tied_first`、`can_offer_range = true`(保留 `:213` 现有用例,改成 `targetedCollectExhausted: true`,并在测试里写"原值 / 新值 / 原因"三栏说明)。
|
||||
- 行为单测:并列 + `discriminatorProbe: null` + `targetedCollectExhausted: false` → 不交付,继续定向补事。
|
||||
- 回归用例(真机形状):9 个候选、前三名并列 13 分、`datedEventCount: 3`、探针非空、`credible_range` = 开局窗口 → 断言 `session_outcome !== "adopt_representative"`。
|
||||
|
||||
### 任务 2 · 未收窄不得宣布可采用(BUG-683 的第二半,P0)
|
||||
|
||||
- `decideRectification` / `completeWithRange`:当 `range` 与开局候选窗口相同(需把 `case.candidateRange` 传进决策输入,`followupCaseArgs` 已有该字段,确认可直接取用)时,`capability.canAdopt` 与 `selectionAllowed` 的"采用"语义降级为 `provisional_range`。
|
||||
- 验收标准:行为单测——`credible_range = ["04:45","05:15"]` 且开局窗口相同 → `session_outcome` 属于交付集合但 `can_adopt === false`;收窄过(如 04:48–05:07)→ 维持现有行为。
|
||||
|
||||
### 任务 3 · 交付态死卡取证与兜底(BUG-684,P1,investigating)
|
||||
|
||||
- `interviewDeliveredGap`:`questionMissing` 放宽为 `questionMissing || deadChoice`(入参增加 `deadChoice`),调用点同步。
|
||||
- 死卡且处于交付态时打上述结构化日志。
|
||||
- 验收标准:行为单测——交付 outcome + 死卡(`kind = choice`、无 `choice_card`)→ gap 返回 `delivered`,不是 `unavailable`;非交付态的死卡仍返回 `unavailable`(不得误伤 BUG-671 的修复入口)。
|
||||
|
||||
### 任务 4 · 记录
|
||||
|
||||
- `docs/BUG_HISTORY.md`:BUG-683 `resolved`(**必须写明"复发自 BUG-680 的修复",并说明实现位置比任务书更宽、测试用 `discriminatorProbe: null` 而实现无此守卫,所以没被拦住**);BUG-684 `investigating`,列出缺的四项证据。
|
||||
- `CHANGELOG.md` 一行;`PROGRESS-rectification-tied-first-fix-20260914.md`;`docs/testing/` 清单加一条:新建校正答两题后,不得出现「可采用 / 代表分钟」,范围应仍在收窄中。
|
||||
|
||||
## 6. 让步顺序
|
||||
|
||||
1. 任务 1 必做且优先——线上每个新案子答两题就被判"可采用",是当前最严重的问题。
|
||||
2. 任务 2 必做(诚实边界)。
|
||||
3. 任务 3 可只做兜底与日志。
|
||||
|
||||
## 7. 开工前置命令
|
||||
|
||||
```bash
|
||||
git fetch origin --prune
|
||||
git worktree add -b codex/rectification-tied-first-fix-20260914 \
|
||||
.worktrees/rectification-tied-first-fix-20260914 origin/staging
|
||||
cd .worktrees/rectification-tied-first-fix-20260914
|
||||
ln -s /workspace/Jyotisha/.venv .venv
|
||||
cd frontend && npm ci
|
||||
```
|
||||
|
||||
开工前读 `docs/BUG_HISTORY.md` 的 BUG-680、681、682、674 四条,以及上一份任务书的决策记录。
|
||||
|
||||
## 8. BUG 编号起点
|
||||
|
||||
- 起点 **BUG-683**(当前最大号 682)。
|
||||
Reference in New Issue
Block a user