Files
Jyotisha/docs/tasks/PROGRESS-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

11 KiB
Raw Blame History

进度 · 自建探针只活在内存里(修复单,2026-09-17)

基线:origin/staging @ c32f81e7。分支:codex/rectification-dead-d9-choice-fix-20260916。 任务书:docs/tasks/TASK-rectification-dead-d9-choice-fix-20260916.md(§1d 已补 2026-09-17 决策记录)。 BUG915917(开工时 docs/BUG_HISTORY.md 最大号 914)。Skill 未 bump

0. 结论

任务 结果
T1 withOwnedDistinguishProbes 五处重建(BUG-915 完成,三条生产路径验收断言全绿
§1c 计分口径 (b) 落地:风格题不再是计分判别题
BUG-917 死卡与 collect_waiting 不得同屏 完成(缺口态改 unavailable,死卡不再画灰选项)
T2 BUG-916 host 前置挪到会话校验之后 + phase 登记 完成(phase 让步为复用 answer.host_fallback
T3 记录 BUG-912 改 resolved 并补首版缺陷;BUG-915917 新增;BUG-375 复发段补一行;CHANGELOG / 真机清单 / 本进度

1. 做了什么

T1 · 自建探针按 receipt 确定性重建(BUG-915

  • frontend/src/lib/rectification-agentic/v9/varga-distinguish-probe.ts
    • withOwnedDistinguishProbes(state, receipt):按 OWNED_DISTINGUISH_QUESTIONS(七条分层题,<method_id>:<ask_theme>)× receipt.window_scan.transitions(缺失时退到 state.transitions)× state.candidates 重建所有自建探针,同 id / 同 semantic_key 不重复,无变化时原样返回同一对象。不依赖「当前哪个 followup 在问」。
    • 重建探针的 source 是专用的 owned_varga_styledecision-from-dossier.tscontrastPacketFromLatestResult / rectificationFollowupCatalog / contrastPacketFromState 三处像排除 nakshatra_boundary 一样把它排除:答题会把重建后的 state 写回 receipt,若不排除,这些探针会在下一轮变成 contrast / event 候选,等于绕开 (b)。
    • 删掉 withFollowupOwnedProbe(只在盖戳那一刻往内存 state 注入,是本单 P0 的根因)与 attachVargaDistinguishIdentity(风格计分题的生产入口,按 (b) 不再需要)。保留 buildVargaDistinguishFields + conflictProbeFromFollowup 供重建与将来 (a) 使用。
  • 接入点(五处,全部写成源码契约断言):
    1. server-focus.ts expectedAnswerSchemaFor 盖戳;
    2. answer-choice.ts applyRectificationChoice 点选答题;
    3. interview-state.ts choiceCardFromCaseDossier 的 GET 投影(persistedDistinguishCard 用的就是这个 state);
    4. answer-choice.ts persistNextInterviewIfIdleinferenceForStale
    5. src/mastra/rectification-v9-tools.ts rectification-resolve-focus(打字回答同一焦点的路径)。
  • 2 与 5 共用新导出的 inferenceForPersistedAnswer(receipt),测试直接调它,保证断言走的是生产代码而不是测试自己拼的 state。
  • idle 的过期判定抽成导出的 persistedDistinguishFocusStale(inference, focus)(逻辑与原来逐字一致,只是可被测),BUG-559 的 split-hash 判定原样保留。

为什么没有在 previousInferenceFromReceipt 出口统一注入(任务书 §2 首选、§3 让步 1 允许退成逐点包装):那个出口的返回值同时喂给 contrastPacketFromLatestResultrectificationFollowupCatalogeventProbesdecision-from-dossier.ts:226/384)。自建风格探针一旦进那两条投影,就会作为 yearless / contrast 候选重新变成可问的题,等于绕开本单的 (b) 决策,并且会改动与本单无关的决策面。因此按让步 1 逐点包装,并用源码契约测试钉住五处。

§1c 计分口径按 (b) 落地

  • method-followup.ts makeFollowupvarga_observation / precision_stage 的分层判别题(d9_relationship / d10_career / d4_home / d5_education / relatives / d2_finance / d30_health,且自己没有 semantic_key
    • 若引擎在该领域还有带年份事件探针(year > 0choice_kindvarga_style / event_quality、未在 askedProbeKeys / answeredProbes 里),就绑定它:题面是「某年前后有没有…」,semantic_key / expected_outcomes / probe_year 全部取该探针,按该探针计分;探针没有 semantic_key(盖不上戳)时不绑定身份,维持基线行为(落不了库走 BUG-674 耗尽分支,不会错分)。
    • 没有可用事件探针时不出题:不挂 choice_frame,由既有 takeRenderableDistinguishframeless_distinguish 后计划继续往下走(引导窗口 / 跳过线重问 / 未覆盖领域 / 交付)。
  • lagna_framedasha_eventsdomain 为 null)不在这条规则里,行为与基线一致。
  • 实测对照(同一输入,同机):基线 next_followup = d9_relationship / precision_stage / choice_kind=varga_style / 题面「亲密关系里,你更接近哪一种相处方式?」;本单同输入 next_followup = null + dropped_probes 两条 frameless_distinguish:d9_relationship,即 09-17 真机那道题不会再出现。

BUG-917 · 死卡不得与 collect_waiting 同屏

  • rectification-surface-state.tsRectificationQuestionGapInput 新增 unansweredDeadChoice,在 delivered 之后、collect_waiting 之前返回 unavailable(「没有拿到下一个问题」+「接着问」),占位与范围行随之同步。
  • rectification-agentic-chat.tsx:按最新一条 settled 助手消息计算 unansweredDeadChoiceOnMessageskind 为 choice / reverse_verify、无 answer_option、status 为 activesuperseded、且没有活卡)并传进缺口态。
  • rectification-message-entry.tsxunansweredDeadChoice 补上「status superseded 且没有作答」——这类焦点没有答案可展示,原来会画成 disabled 的灰色 A/B/C/D(09-17 手机截图那张)。

T2 · BUG-916

  • agent-run.ts:年月阶段 host 前置(shouldHostReaskYearEntry)挪到 dossier.case.sessionId !== sessionId 校验之后,跨会话消息不再被写成确定性轮;计费位置不变。
  • phases 从未登记的 answer.host_year_entry 改成既有 answer.host_fallback(任务书 §3 让步 2),公开快照的 answer_origin: "host_fallback" 语义也对得上。

2. 门禁数字

基线 c32f81e7 本单
./node_modules/.bin/tsc --noEmit 0 错 0 错
npm run lint 0 error / 116 warning 0 error / 116 warning
npm test tests 3414 · pass 3368 · fail 31 · skipped 15 tests 3421 · pass 3375 · fail 31 · skipped 15
npm test 失败清单 31 条无 Docker 既有缺口 diff 逐条一致(见下)
next build --webpack / 渲染标记 ○ Static ○ Static
产物 JS gzip 合计(.next/static/**/*.js 1,496,339 字节 1,496,408 字节(+0.005%
pytest tests/test_rectification_refinement_packet.py tests/test_rectification_event_probes.py tests/test_rectification_engine_memoization.py 63 绿 63 绿
run_quality_gate.py --profile quick 卡在末尾 npm test31 条无 Docker 失败) 同:pytest 段 798 passed / 1 skipped,末尾 npm test 步骤 exit 1(同样的 31 条)

gzip 口径:本仓没有首屏 gzip 测量脚本,Next 16 的路由表也不再输出 Size 列;取 .next/static 下全部 js chunk 的 gzip 字节合计,基线与本单同机同命令(next build --webpack)各测一次。

失败清单比对:diff 基线与本单的 not ok 名单为空输出。31 条全部是无 Docker 的既有环境缺口(数据库 / 迁移 / 部署 / 缓存 appendix / ephemeris 等),与 BLOCKED.md 一致。

测试总数 3414 → 3421:本单测试文件由 4 条改写为 11 条(见下)。没有弱化任何既有断言。

改过的既有断言

文件 原值 新值 原因
frontend/tests/rectification-dead-d9-choice-20260916.test.ts GET projects the persisted distinguish copy when its probe is still live:手工把自建探针塞进 buildInferenceState({probes:[D24, 自建]}) 再断言有卡 删除该 fixture;改成 choiceCardFromCaseDossier{inference_state: 只含 D24, window_scan} 的落库形状 任务书 §1:手塞探针的 state 不是生产形状,正是它让首版「测试全绿、生产报错」
同上 year-stage D9 followup does not borrow the D24 contrast probe:答题用 withFollowupOwnedProbe(inference, followup) 拆成两条:盖戳断言 + 用生产的 inferenceForPersistedAnswer(receipt) 答题,并先断言未重建时确实 stale_probe withFollowupOwnedProbe 已删除;答题必须走生产读 state 的那一段

其余既有断言一字未改。

本单测试(frontend/tests/rectification-dead-d9-choice-20260916.test.ts11 条全绿)

  1. 盖戳按 receipt 重建自建探针、不借 D24(并断言落库 state 里确实没有该探针)。
  2. 生产答题路径:同一 schema 在未重建的 state 上 stale_probe,在 inferenceForPersistedAnswerappliedanswered_probes 末位 semantic_key=d9_relationship:relationship_style,不含 D24。
  3. choiceCardFromCaseDossier 对该落库焦点返回卡,question_id 与焦点一致、四个选项。
  4. persistedDistinguishFocusStale:未重建判 stale(这就是误伤),重建后不 stale;候选集变化后仍 staleBUG-559 保留)。
  5. 五处接入点的源码契约。
  6. (b) 决策:无带年份事件探针时不出风格题(题面不含「更接近哪一种」,dropped_probesframeless_distinguish:d9_relationship)。
  7. (b) 决策的另一半:引擎给出 2020 年感情事件探针时该线照问,semantic_key 为该探针、choice_kind=existence、题面含年份。 7b. 重建出来的探针不得变成可问的题:rectificationFollowupCatalogeventProbescontrastPacket.probes 里都不含该 key。
  8. BUG-917collect_waiting + 死卡 → unavailablechat 与 message-entry 的源码契约。
  9. BUG-913 closed_by_design 复核(原样保留)。
  10. BUG-914 两极不得同时进 shared_traits(原样保留)。

3. 环境缺口

  • 无 Docker:31 条失败与基线逐条一致;run_quality_gate.py --profile quick 因此仍停在末尾 npm test
  • 无登录态 / 无 Chrome / 无模型凭据:浏览器级验收写成 docs/testing/rectification-dead-d9-choice-fix-20260916.md,未声称通过。
  • staging 部署核对(/api/healthdeployment.gitCommit)不在本单能力范围:只推分支,未推 staging
  • 事发 Case 的 04:48–04:59 仍建立在错分答案上,不要采用(与上一单结论相同)。