Files
Jyotisha/docs/tasks/TASK-rectification-dead-d9-choice-fix-20260916.md
T
Jesse_ChenandClaude Fable 5.1 5113d457b7 fix(rectification): 自建分盘探针按 receipt 重建;风格题不再计分;死卡不配采集占位
BUG-915:BUG-912 首版只在盖戳那一刻把自建探针注入内存 state,答题 / GET /
idle 三处读持久化 receipt 都找不到它 → 点选与打字回答报 stale_probe、GET 无卡、
刚落库的焦点每次 idle persist 被 superseded。新增
withOwnedDistinguishProbes(state, receipt) 按 window_scan.transitions + state
候选确定性重建(照 withNakshatraBoundaryProbe 的既有模式),接入盖戳、点选答题、
打字答题、GET 投影、idle 过期五处,并加源码契约测试钉住。重建探针的 source 为
owned_varga_style,像 nakshatra_boundary 一样从 contrast packet 与 event 探针池
排除,避免答题写回 state 后它们变成可问的题。

产品决策(任务书 §1d,选项 b):分盘风格题不再作为 distinguish_candidates 计分题。
分层判别题只在引擎给出该领域带年份事件探针时以「某年前后有没有…」出题并按该探针
计分;没有事件探针时不出题,计划直接走下一条线。删除 attachVargaDistinguishIdentity
与 withFollowupOwnedProbe,保留 buildVargaDistinguishFields / conflictProbeFromFollowup
供重建与将来选项 (a)。

BUG-917:最新助手消息带未答点选而 GET 无卡时,缺口态改走 unavailable 修复出口,
不再与「再说一件带年月的事」同屏;superseded 且未作答的焦点不再渲染成灰色选项。

BUG-916:年月阶段 host 前置挪到会话校验之后,phase 复用 answer.host_fallback。

测试改走生产路径(inferenceForPersistedAnswer / choiceCardFromCaseDossier /
persistedDistinguishFocusStale),删掉手工把自建探针塞进 state 的 fixture。
tsc 0 错;lint 0 error / 116 warning;npm test 3421 条 / 31 红,失败清单与基线
c32f81e7 逐条一致;next build --webpack 后 / 仍 ○ Static,产物 JS gzip
1,496,339 → 1,496,408(+0.005%);pytest rectification 定向 63 绿,快速门 Python
段 798 绿。Skill 未 bump。

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
2026-09-17 00:48:50 +00:00

88 lines
11 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.
# 验收修复单 · D9 自建探针只活在内存里:答题报 stale_probe、GET 无卡、idle 立刻 superseded(2026-09-16)
## 0. 基线与验收对象
- 验收对象:`227a7571` + `8d7dfbf0`(任务书 `TASK-rectification-dead-d9-choice-20260916.md`,已合入并部署 staging)。
- 代码基线:`8d7dfbf0`。分支:`codex/rectification-dead-d9-choice-fix-20260916`。
- BUG 段:**BUG-915 起**(基线最大 BUG-914)。
- 门禁数字(本机 Linux,`node_modules` 软链,`next build --webpack`):`tsc` 0 错;lint 0 error / 116 warning;`npm test` 3414 条、31 红,失败清单与基线 `05cf20fb`(3405 条、31 红)**逐条相同**;Python 定向 `refinement_packet` / `event_probes` / `engine_memoization` 63 绿。门禁本身是绿的,**红在生产路径没被测试覆盖**。
## 1. 验收结论
| 项 | 结论 | 证据 |
| --- | --- | --- |
| T1a 不再借最高增益探针 | 通过 | `inference-adapter.ts:553` `const next = matched`;测试断言 `probe_id` 不再是 D24 |
| T1b 风格题自建探针 | **未通过(P0)** | 探针只在 `expectedAnswerSchemaFor` 盖戳那一刻由 `withFollowupOwnedProbe` 注入内存 state(`server-focus.ts:136`),**没有进 `decision_receipt.inference_state`**。生产的答题路径 `answer-choice.ts:591` 用 `withNakshatraBoundaryProbe(previousInferenceFromReceipt(receipt))` 读持久化 state,`matchProbeForChoice` 找不到 `probe:d9_relationship:relationship_style` → `stale_probe` → `:852` 抛 `agentic_rectification_stale_probe`。我用任务书自己的 fixture 按生产路径回放(state 不含自建探针):`applied=false reason=stale_probe`。**点选或打字回答新的 D9 题都会报错,比修前更糟**(修前至少错分入账,修后答不了) |
| T1c 从落库副本投影 | **未通过** | `persistedDistinguishCard`(`method-followup.ts:3319-3350`)先要 `liveInferenceDistinguishProbe(input.inferenceState)` 在持久化 state 里找到该探针,找不到即 null;同一回放里 GET `choice_card` 为 **null**。任务书第二条测试是手工把自建探针塞进 `buildInferenceState({probes:[D24, 自建]})` 再断言有卡——fixture 不是生产形状(AGENTS §7.4 精神) |
| T1c 过期 → superseded | **未通过(反向误伤)** | `persistNextInterviewIfIdle`(`answer-choice.ts:1607-1643`):`liveDistinguishProbe(持久化 state, schema)` 对自建探针恒为 null → `staleDistinguish=true` → 刚落库的 D9 焦点在**每次 idle persist 时被 superseded**(route.ts 四处 + agent-run 一处都会调) |
| T2 BUG-913 closed_by_design | 通过 | `yearlessPersonalityCanAsk({datedCount:5})===false`;结论与 BUG-689 出口一致 |
| T3 BUG-914 一列一句 | 通过 | Python 断言 `traits` 长度 1、两 option 不同句;前端断言两极不同时进 `shared_traits`;63 绿 |
| T4 记录 | 通过(需随本单改状态) | BUG-912 现状写 `resolved` 不成立,改 `investigating` 直到本单过门 |
**总结论:未通过。** 根因是一个:自建探针没有像月宿边界探针那样在**每个读 state 的地方**重建。
## 1b. 2026-09-17 07:58 真机复现(产品负责人手机截图)
- 定向健康题答「2024 年 8 月」入账后,正文出「亲密关系里,你更接近哪一种相处方式?」A/B/C/D。**四个选项灰显(`opacity .48`,`fieldset:disabled`)、点不了**——消息内的卡是 `embeddedCard` 且 `disabled={!liveQuestion}`(`rectification-message-entry.tsx:169-180`),GET 没有 `choice_card`(§1 T1c)就是死卡。这正是 §1 预测的现象,不是新缺陷。
- 同屏矛盾:只读范围行写「再说一件带年月的事就能继续」,输入框占位也是「再说一件带年月的事」。原因:`currentQuestion` 为 null(idle 已把焦点 superseded,§1 T1c 反向误伤)→ `interviewCollectWaiting` 为真 → `collect_waiting`;而最后一条消息上还挂着未答的死卡。`rectificationQuestionGapState` 只看 `currentQuestion`,看不到消息级的 `unansweredDeadChoice`。
- **补进 T1 验收(BUG-917)**:最后一条助手消息带未答的点选题而 GET 无卡时,缺口态不得是 `collect_waiting`;要么是修好后的活卡,要么走 `unavailable` 修复出口(「没有拿到下一个问题」+ 接着问),占位与范围行同步。把 `unansweredDeadChoice`(或等价的「最新消息未答点选」)作为 `rectificationQuestionGapState` 的输入,加断言。
## 1c. 产品决策待确认:D9/D10「你更像哪种」题的计分口径
- 实现把 D9/D10 判别题做成 `choice_kind=varga_style`:选项是「尽量避免冲突…」「投入很深…」这类分盘上升类型描述,答案按 `expected_outcomes` 全额更新后验、淘汰候选。
- 与既有口径冲突:`SKILL.md:84`「不要问两套盘哪个更像」;产品 2026-09-09 拍板「性格题降级为平局裁决(±1、不淘汰、报告参考)」;hint 原文是「按探针年份问感情这条线的**前事是否发生**」。
- **本单默认按既有口径执行(b)**:分盘风格题不得作为 `distinguish_candidates` 全额计分。D9/D10 判别题只在引擎给出该领域带年份事件探针(`discriminating_event_probes` / `prospective_probes`)时以「某年前后有没有…」出题并计分;没有事件探针时不出风格题,直接走下一条线(引导窗口 / 跳过线重问 / 未覆盖领域 / 交付)。风格描述只留在既有的平局参考题(±1)与交付卡列。产品若改口为 (a)「允许风格题全额计分」,在本单决策记录追加一行即可,执行方不得自行选边。
- 这条不改变 T1 的重建要求:事件探针来自引擎 state,本就在持久化里;自建探针的重建逻辑仍要做,供 (a) 与将来使用,但默认路径不再产生风格计分题。
## 1d. 决策记录(2026-09-17,产品负责人)
- **§1c 采用 (b)**:分盘风格题不得作为 `distinguish_candidates` 全额计分题。D9/D10 等分层判别题只在引擎给出该领域带年份事件探针时才出题(题面是「某年前后有没有…」,按该事件探针计分);没有事件探针时该题不出,计划直接走下一条线(引导窗口 / 跳过线重问 / 未覆盖领域 / 交付)。风格描述只留在既有的 ±1 平局参考题与交付卡列。
- T1 的重建要求照做:`withOwnedDistinguishProbes` 在每个读 state 的地方按 receipt 确定性重建,保证 (b) 决策之前已落库的风格焦点可答、有卡、不被 idle 误 supersede。
- 执行方据此删除了「给风格题自建探针再出题」的生产入口(`attachVargaDistinguishIdentity` / `withFollowupOwnedProbe`),保留 `buildVargaDistinguishFields` + `conflictProbeFromFollowup` 供重建与将来 (a) 使用。
## 2. 修法(照 `withNakshatraBoundaryProbe` 的既有模式)
月宿边界探针也不在引擎 state 里,它的做法是三处都从 receipt 重建:`server-focus.ts:124`(盖戳)、`answer-choice.ts:591`(答题)、`inference-adapter.ts:235`(`previousInferenceFromReceipt` 之后)。自建分盘探针照抄:
### T1 `withOwnedDistinguishProbes(state, receipt)`(BUG-915)
- 新函数从 `decision_receipt` 的 `window_scan.transitions` + `candidates` **确定性**重建所有 `varga_observation` / `precision_stage` 风格题的自建探针(对每个 `candidates_differ` 的层调 `buildVargaDistinguishFields`),追加进 `state.probes`(同 id 不重复)。不依赖当前 followup,避免「哪个 followup 在问」的时序问题。
- 接入点(缺一不可,写成源码契约断言):`server-focus.ts` 盖戳;`answer-choice.ts:591` 答题;`inference-adapter.ts` `previousInferenceFromReceipt` 出口(或紧随其后的同一包装);`interview-state.ts` `choiceCardFromCaseDossier` 传给投影的 `inferenceState`;`persistNextInterviewIfIdle` 的 `inferenceForStale`;`method-followup.ts` 计划层承接焦点用的 `liveDistinguishProbe`。
- 重算后 `answered_probes` 里的自建条目要能被引擎回写的 state 保留:确认 `autoRescoreAfterEvidenceChange` / compare 回写 `inference_state` 时 `answered_probes` 不按 `probes` 白名单过滤;若过滤,自建条目要在重建时一并保留。
- 验收(**必须走生产路径**):
- 回放:持久化 state 只含 D24 → 盖戳得到自建 `probe_id` → 用 `answer-choice.ts` 同一段代码(导出一个薄函数或直接调用 `answerRectificationChoice` 的纯部分)答 C → `applied=true`、`answered_probes` 末位 `semantic_key=d9_relationship:relationship_style`、后验只按 D9 分组变化。
- GET:同一持久化 state 下 `choiceCardFromCaseDossier` 返回卡且 `question_id` 与 `current_question` 一致。
- idle:同一状态下 `persistNextInterviewIfIdle` **不** supersede 该焦点;改账本使 `candidate_split_hash` 变化后才 supersede 并落下一问。
- 删掉手工塞探针的 fixture,改用上面的生产形状。
### T2 顺带(BUG-916,P2/P3)
- `agent-run.ts:341-360` 年月阶段 host 前置(BUG-910)排在 `dossier.case.sessionId !== sessionId` 校验之前,跨会话消息也会被写成确定性轮;把前置挪到该校验之后(计费之前不变)。
- `phases: ["answer.host_year_entry"]` 不在 `PUBLIC_RECTIFICATION_PHASES`;要么登记,要么复用 `answer.host_fallback`。tsc 不报是因为类型是 string。
### T3 记录
- BUG-912 改 `investigating` → 本单过门后 `resolved`,并写「首版只在盖戳时注入内存 state,答题/GET/idle 三处读持久化 state 找不到」。BUG-915/916 新增。BUG-375 复发段补一句。CHANGELOG、PROGRESS、真机清单更新。
## 3. 让步顺序
1. 若 `previousInferenceFromReceipt` 出口统一注入会让既有 `rectification-inference-machine` 断言变红(state.probes 数量),允许只在六个接入点各自包装,但要有源码契约测试钉住六处。
2. T2 的 phase 登记可让步为复用 `answer.host_fallback`。
3. T1 验收三条与 T3 不可让步。
## 4. 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/rectification-dead-d9-choice-fix-20260916 .worktrees/rectification-dead-d9-choice-fix-20260916 origin/staging
cd .worktrees/rectification-dead-d9-choice-fix-20260916/frontend
./node_modules/.bin/tsx --test tests/rectification-dead-d9-choice-20260916.test.ts tests/rectification-inference-machine*.test.ts 2>&1 | tail -5
grep -o "BUG-[0-9]\+" ../docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1
```
## 5. 给产品负责人
- **staging 当前状态**:D9/D10 风格题不再错分,但**答不了**(点选会报错)。在修复单过门前,遇到风格题请先跳过(D)或补一件带年月的事。
- 事发 Case 的 04:48–04:59 仍不可采用(建立在错分上)。
- 09-17 手机上那张灰卡就是本单 P0;1c 的计分口径请产品确认或默认 (b)。