Files
Jyotisha/docs/tasks/TASK-rectification-dead-d9-choice-20260916.md
T

122 lines
13 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.
# 任务书 · 感情判别题被盖上学业分盘探针(错分 + 无卡,BUG-375 复发)、答完直接出交付卡、卡头把一对相反性格列成共同点(2026-09-16)
## 0. 基线
- 基线 commit`6aabbe38``origin/staging` headstaging 已部署同一 SHA;它是 `TASK-rectification-year-focus-overlay-20260916.md` 的实现,**尚未验收**)。
- 分支:`codex/rectification-dead-d9-choice-20260916`,基于 `origin/staging`
- 串行:在 year-focus-overlay 验收之后;若验收出修复单,本单排在修复单之后。与 `TASK-console-noise-20260916.md` 无交集。
- BUG 段:**BUG-912 起**(基线最大 BUG-911,开工时再核)。
- 范围:TS 计划层 / 投影层 + Python `refinement_packet.py` 一处;不动 Skill。
## 1. 事故实证(产品负责人 2026-09-16 staging 真机,模型 deepseek-v4-flash
删卡后的流程本身已通:健康题选 A →「大概哪年几月?」→ 打字给年月 →「记下了:YYYY-MM 做过手术。」没有追问主体。接下来出的问题在这里:
1. 同一条助手消息正文接着写了判别题:「2020 年前后,你感情上有没有开始过一段认真的关系?」——**正文里有题,下面没有 A/B/C/D**。消息之后是只读范围行「目前范围 04:48–05:07,再说一件带年月的事就能继续」,再往下是「没有拿到下一个问题。」和「接着问」按钮。
2. 当时快照(用户从接口抓的):`interview.session_outcome=collect_evidence``precision_stage=collect_events``stop_reason=null``precision_gate_met=false``collection_progress={scoreable:5,minimum:3,missing:0}``current_question.question_id="d9_relationship:relationship_style:score"`。即:**焦点落库了**,前端拿不到卡。
3. 用户在输入框打字答「没有」。助手:「已记录,范围收到 04:48–04:59。……现在还剩 04:48–04:59 里 3 个候选。这几个候选按现有信息分不开。」随后直接出三列交付卡(04:59 44% / 04:53 35% / 04:51 21%,均「6 件经历里 6 件对得上」)。
4. 交付卡头部三行「共同点」:「三个时间的事业盘都在巨蟹座:做事以照顾人为主,在意团队里的感受」「希望两边都能说得过去」「必要时会直接选边」——后两行是**同一对相反描述的两极**,被当成三个候选共有的性格写在一起。
## 2. 代码定位与根因
### F1 感情判别题被盖上学业分盘的探针:答案计错分 + GET 出不了卡(BUG-912**复发自 BUG-375 (1)**P0
产品负责人补抓的快照(已脱敏):
```json
"current_question": {
"question_id": "d9_relationship:relationship_style:score",
"probe_id": "contrast:varga.d24.白羊座|金牛座|双子座|巨蟹座|狮子座|处女座",
"prompt": "2020 年前后,你感情上有没有开始过一段认真的关系?",
"kind": "choice", "intent": "distinguish_candidates", "domain": "relationship"
},
"choice_card": null,
"next_user_action": { "id": "ask_method_followup", "user_meaning": "关系盘仍会换升。按探针年份问感情这条线的前事…本题绑定 D9…" }
```
- 题是 D9 感情题,盖的探针却是 **D24(学业分盘)六星座对照探针**。链路:`method-followup.ts:2929-2945``varga_observation``d9.candidates_differ`)生成 D9 followup**没有自己的 `semantic_key` / 探针**`server-focus.ts:133-141` `expectedAnswerSchemaFor` 把它交给 `stampChoiceSchemaWithProbe``inference-adapter.ts:553` 在没有 preferred key 时退到 `selectHighestGainProbe(state.probes…)`——拿引擎里增益最高的任意探针盖上去,这次是 D24 对照探针。`:149` 又规定计分题没有探针就不落库,所以这道题**只能**借探针才存得下来。
- BUG-375 (1) 原话:「`stampChoiceSchemaWithProbe` 用引擎最高增益探针盖 schema,把对比探针换成已答教育题」。当时修的是对比探针改用自己的 `semantic_key`,**没有堵住「无探针的分盘风格题借最高增益探针」这条**,本次是同一根因换了入口。
- 后果一(计分错):用户打字答「没有」,`applyChoiceWithoutEvidence` 按 schema 上的探针计分,即按 **D24 六星座的 `expected_outcomes`** 更新后验;正文「已记录,范围收到 04:48–04:59」就是这次错分的产物。**该 Case 现在的区间与三列卡不可信**,任务书 §8 让产品知悉。
- 后果二(无卡):GET `projectRectificationChoiceCard``method-followup.ts:3308-3333`)重跑计划得到的 D9 followup 仍无 `semantic_key`/`split_hash`,走到 `focus.questionId !== frame.question_id` 这一条(落库 id 带 `:score`frame 的 id 由 `stableFollowupQuestionId` 生成)→ `null` → 前端 `deadChoice` →「没有拿到下一个问题」。执行方用回放确认是这一条还是 `mergeChoiceCard` 内部。
- 同族:BUG-375(原发)、BUG-578、BUG-582、BUG-674。
### F2 打字答「没有」直接出交付卡(BUG-913,先 `investigating`
- 「没有」被当作 D9 判别题的否定答案入账(正文「已记录,范围收到 04:48–04:59」),范围收窄、3 候选平局 → `tied_first` → 交付卡。平局出卡本身是 BUG-689 的设计出口。要核的是两件事:
- BUG-751「线没问完不出卡」:此时是否还有未问的线(引导窗口题、跳过线重问、未覆盖领域)。快照 `missing:0`,但 `precision_gate_met=false`
- BUG-685~688「平局先问参考题再出卡」:三候选 D9 两蝎一秤、D10 同巨蟹,风格题是否可渲染;若可渲染而没问,是 hold 逻辑没触发。
- 用回放定性:设计如此就写成 `investigating → closed_by_design` 并在进度记录说明;是缺陷就修。
### F3 交付卡把一对相反描述都列成共同点(BUG-914)
- `scripts/rectification/refinement_packet.py:26-55` `NAKSHATRA_TRAITS` 每行是一对**相反**的日常描述(如「希望两边都能说得过去」/「必要时会直接选边」);`:534-548``NAKSHATRA_TRAITS[earlier_index]` **整对**塞进 `options[0].traits``[later_index]` 整对塞进 `options[1].traits`。于是一个候选列的性格行里同时出现一对矛盾句。
- `frontend/src/lib/rectification-agentic/v9/divergence-panel.ts:231-237` `nakshatraTraitsForTime`:代表时间与早于代表的候选拿 `earlier` 那对,晚于代表的拿 `later`。事发卡代表分钟是排第一的 04:59,三列都 ≤ 代表 → 三列同一对 → `splitSharedTraits` 把两极都判成「共同点」抬到卡头。
- 同一张表还被 `inference-adapter.ts:134-182` 用来出风格参考题的 A/B 选项标签(`optionLabel("A", optionA.traits)`),A 选项标签也是「两极合一」。
- 修法(产品口径:一列只写一句、不得自相矛盾):`refinement_packet.py` 每个 option 只取该 nakshatra 的**第一极**作 `traits``[earlier[0]]` / `[later[0]]`),`user_meaning` 同步改;前端不改结构。golden 若含 `traits` 两句需按 BUG-733 口径只追加/替换这一处并贴 diff。
## 3. 决策记录
- 产品负责人 2026-09-16:正文里念出来的题必须有可点的卡;候选列与卡头的性格描述不得自相矛盾。
- F1 修法定为两条:**风格题不得借别的分盘的探针**(宁可不落库也不错分);判别题以落库副本投影,过期不静默而是 `superseded` + 落下一问。不推翻 BUG-559 的 split-hash 过期判定,只改过期后的去向。
- 事发 Case 的 04:48–04:59 收窄是错分产物;本单不做数据回滚(用户可用「更像这个」之外的路径继续补经历重算),但 CHANGELOG 要写明「2026-09-16 前分盘风格题答案可能计错分」。
- F2 结论由回放决定,不预设。
- 不改 Skill、不改出卡门槛、不动 BUG-751 的闸。
## 4. 硬红线
1. BUG-674「判别题落不了库不得写进正文」、BUG-582「无框区分题不得退化成采集句」、BUG-559/578 的过期判定原样保留。
2. 前端 `persisted_question` 块不得为绕过投影而自己拼卡(BUG-673 防复发)。
3. `page.tsx` 不增长;Python 改动跑 `tests/test_rectification_*.py` 全量与快速门。
4. 既有断言只因形状变化更新,写「原值 / 新值 / 原因」。
## 5. 任务分解
### T1 分盘风格题不得借探针;从落库副本投影(BUG-912)
- **T1a 堵借探针**`stampChoiceSchemaWithProbe``preferred``semantic_key` 时**不得**退到 `selectHighestGainProbe`;无探针即返回未盖戳 schema。`expectedAnswerSchemaFor:149` 的「计分题无探针则 null」保留,于是 D9/D10 风格题要么自带探针要么不落库(落不了库走 BUG-674 的 `persistExhaustionCollect`,不进正文)。
- **T1b 给风格题自己的探针**`varga_observation` / `precision_stage` 生成 `d9_relationship` / `d10_career``distinguish_candidates` followup 时,按 BUG-375 对比探针的做法建自己的探针:`semantic_key = <method_id>:<ask_theme>``supports/conflicts` 是**剩余候选分钟**按该分盘上升星座分组,`expected_outcomes``choice_frame` 的 A/B/C/D 一一对应;`candidate_split_hash` 按现有规则算。这样答案按 D9 计分,GET 投影也有 key 可匹配。
- **T1c 从落库副本投影**`projectRectificationChoiceCard``persistedTargetedCard` 后加 `persistedDistinguishCard`:焦点 `intent=distinguish_candidates``parseAgentChoiceCopy(schema)` 非空、`probe_id` 存在且该探针仍在当前 `inference_state`(未过期)→ 直接投影,`question_id` 用焦点的。过期(split-hash 不同)时由服务端把焦点 `superseded` 并按 `persistNextInterviewIfIdle` 落下一问,不得留下 `kind=choice` 无卡的快照。
- 验收:
- 回放事发形状:D9 `candidates_differ`、inference_state 里只有 D24 对照探针 → 修前 schema `probe_id``contrast:varga.d24` 开头;修后 `probe_id`/`semantic_key` 为 D9 自有,`supports` 为候选分钟;答 B/「没有」后后验只按 D9 分组变化,D24 探针的 `answered_probes` 不含它。
- GET 快照 `choice_card.question_id === current_question.question_id`,且 `interviewChoiceCardUnavailable` 为 false。
- 过期路径:改账本重算使探针失效 → 焦点 `superseded` + 新 `current_question` 有卡。
- BUG-375 的 `rectification-server-focus` / `rectification-choice-card` / `rectification-inference-machine` 原断言绿;BUG-375 条目补「2026-09-16 复发(BUG-912):无探针的分盘风格题借最高增益探针」。
### T2 「没有」后直接出卡的回放定性(BUG-913)
- 用事发快照形状(5 可评分事件、3 候选、D9 两蝎一秤、D10 同巨蟹、`precision_gate_met=false`)回放:答 D9 否定后 `decideFromDossier``nextAction` / `stopReason`;列出此时 `buildMethodFollowupPlan` 是否还有未问的线;`shouldHoldForTieBreak` 是否为真及为何没 hold。
- 结论三选一写进 BUG-913:设计如此(关闭)/ BUG-751 复发(修)/ BUG-688 复发(修)。复发则关联原号。
### T3 性格描述一列一句(BUG-914)
- `refinement_packet.py` 每个 option 只取第一极;`user_meaning` 改为「A{earlier[0]}。B{later[0]}。」;`tests/test_rectification_refinement*.py` 补断言:`options[*].traits` 长度为 1 且两 option 不同句。
- 前端 `splitSharedTraits` 不改;补一条断言:同一对里的两极不得同时出现在 `shared_traits`
- 验收:Python 定向 + 快速门;前端相关测试绿。
### T4 记录
- BUG-912914CHANGELOG(正文念出的题必有卡;性格描述不再自相矛盾);`PROGRESS-rectification-dead-d9-choice-20260916.md`;真机清单 `docs/testing/rectification-dead-d9-choice-20260916.md`
## 6. 让步顺序
1. T1 的「过期即 superseded + 落下一问」若与 `persistNextInterviewIfIdle` 的幂等早退打架,先只做「从副本投影」,过期仍显示旧卡但可答(答案按当前账本计分),进度记录写明。
2. T3 若 golden 牵连过多,先只改 `user_meaning` 与前端去重,Python `traits` 拆分作第二提交。
3. T2、T4 不可让步。
## 7. 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/rectification-dead-d9-choice-20260916 .worktrees/rectification-dead-d9-choice-20260916 origin/staging
cd .worktrees/rectification-dead-d9-choice-20260916
.venv/bin/python -m pytest tests/test_rectification_refinement_packet.py -q 2>&1 | tail -3 # 若文件名不同用 ls tests | grep refinement
cd frontend && ./node_modules/.bin/tsc --noEmit && npm run lint && npm test 2>&1 | tail -20
grep -o "BUG-[0-9]\+" ../docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1
```
## 8. 环境缺口
- 事发快照的 `current_question` / `choice_card` / `next_user_action` 已由产品负责人补抓并写进 §2 F1;`method_followup_plan` 不在公开快照里,不需要再抓。
- **产品知悉**:该 Case 当前显示的 04:48–04:59 与三列卡建立在错分的答案上,不要采用;修复部署后补一件带年月的事触发重算,或新开 Case。
- 真机:正文里出现「有没有…」判别题时下面必有 A/B/C/D;交付卡每列性格只有一句、卡头共同点不自相矛盾。