Files
Jyotisha/docs/tasks/TASK-rectification-narrow-before-deliver-20260911.md
T

9.3 KiB
Raw Blame History

TASK · 带年月题问完不等于收敛:交付前先按剩余候选再出题、再定向补事(2026-09-11)

  • 基线:origin/staging @ 9941c34d(已部署;代码 f870d3d7,含 BUG-651/652
  • 分支:codex/rectification-narrow-before-deliver-20260911,基于 origin/staging
  • 执行方:coding agent;验收:Claude
  • BUG 编号起点:BUG-653(开工时 grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1 核对)
  • 不改确认门、不改 SCORE_DELTA、不改 MIN_ACCEPTANCE_*;Skill 若改用户可见流程升 10.0.24

1. 事故实证(2026-09-11 staging 真机,代码 f870d3d7

同一序列(两件学业 + 实习入职/离职 + 六道带年月选择题 C/C/C/B/A/C)跑完后,系统按 BUG-651 直接出了三列区间卡:范围 04:48–05:07(20 分钟,5 个簇),头名 04:53 只有 29%,第二名 05:06 22%,第三名 04:50 18%。产品负责人原话:「没到出卡的时候就开始出卡了」「范围没缩短多少,这咋能算结束」。

回执事实:

字段 含义
latest_result.createdAt 08:10:54(记完实习那一轮) 之后六道题都是 TS 侧推理,引擎没有再跑
inference_state.probes 7 道带年月(事业 3、感情 2、迁居 1、学业质量 1)+ 7 道无年月 + 月宿 引擎一次性生成,MAX_PROBES=8、每域 ≤3、每域每年 1 道
家人 / 财务 / 健康三条线 0 道带年月题;oos_blind_prompts 列出感情、家人、财务「校时还没用过」 ("family","existence") 先验 yes=0.85 > DOMINANT_ANSWER_PRIOR=0.8,家人存在题全部被 dominant_answer_prior 丢弃;财务无有效换运边界
剩余 5 簇的结构差异 04:52 D9 换升;05:00 D10 / D4 / D12 / D24 / D2 同时换升;05:06 D2405:07 D11 能切开 04:4804:59 与 05:0005:07 的是事业 / 迁居 / 家人 / 学业 / 财务 这些线上的带年月事件,不是性格题
收敛门 lead = 16 vs 15top share 29% 远未收敛;rounds 六轮全 informative,无平台期

2. 根因

  1. 探针池是静态的event_probes.py 的区分探针只在引擎跑(账本变化 → rescoreStaleMinuteSnapshotIfNeededrescoreMinuteAfterWindowChange)时按初始 9 簇生成一次;选择题答完后 TS 侧只更新候选分数,不会拿剩余候选去引擎再生成一批。六道题一问完池就空,BUG-651 于是交付。
  2. 生成上限与先验把可用题掐掉了MAX_PROBES=8MAX_PROBES_PER_DOMAIN=3_best_probe_per_year 每域每年只留一道;家人存在题被 0.85 的先验整体丢弃(没有任何家人题曾出现过)。
  3. 交付前没有定向补事:重设计单 S1 的锚定追问在训练门开后就停了;S2 池空直接 S3。可是回执里 oos_blind_prompts 已经写明感情 / 家人 / 财务三条线没用过,而这三条线正是 05:00 换升边界所需要的。
  4. 卡片文案把「当前范围」写成「结束」:「这次给出的范围 04:48–05:07 · 对照了 4 件经历」+ 三个「更像这个」按钮,没有一句「还能再收窄:…」。BUG-651 单 T5 的「再补什么」句在真机卡片上没有出现(待 T0 核实是没落还是被裁)。

3. 决策记录(产品负责人 2026-09-11)

  1. 带年月题池空 ≠ 结束。池空后先做两步再交付:(a) 拿剩余活跃候选去引擎刷新一批带年月探针(保持候选集与已答题,keepAnswers);(b) 刷新后仍无题,则按剩余候选的换升层做定向补事(只问能切开剩余候选的那几条线,带具体例子,不带推算年份)。用户给了新事 → 重算 → 回到 S2;用户说「没有了 / 就这些」→ S3 交付。
  2. 交付条件改为:收敛门开,或(刷新后带年月池空 定向补事被拒/池空),或用户主动停。BUG-651 的「池空即交付」收窄为「刷新与定向补事都用尽后交付」。「永远给结果」不变:任何时候用户说停都出卡。
  3. 家人存在题的先验丢弃只对无年月 / 宽窗题生效:带具体年月的家人换运边界题(dasha_boundary,月级)不再因 0.85 先验被整体丢弃;先验只用于排序。
  4. 卡片文案:标题「目前范围 04:48–05:07(对照了 4 件经历)」;卡下必有一句「还能再收窄:如果记得 …(来自定向补事池的首条)」;「更像这个」按钮保留。禁用「这次给出」「结束」「最终」。
  5. 不承诺分钟级:4 件月精度经历本身分辨力有限,本单目标是把「引擎已生成的 8 道题问完」变成「剩余候选能问的都问完、能补的都补完」,不是保证 5 分钟。

4. 硬红线

  • 探针刷新不得改变 candidate_set_id / 已答题;重算必须走现有 rescoreMinuteAfterWindowChange(keepAnswers: true) 的回放路径(BUG-587 / BUG-594 防复发)。
  • 刷新时 asked_probe_keys 必须带上全部已答键,同域同年去重(BUG-559)照旧。
  • 定向补事题干不得带出生年推算年份(重设计单 §4);例子只从「能切开剩余候选的层」对应的领域取。
  • 性格题仍按 BUG-651 决策 2:只作卡后可选,不进本单的刷新池。
  • 不得静默返回空载体(BUG-652 不变量)。
  • 既有测试总数不降;改断言写原值 / 新值 / 原因。

5. 任务分解

  • T0 核实:回放 §1 序列,打印交付时 range_delivery 与 host 正文,确认 BUG-651 T5 的「再补什么」句是否落下;未落则并入 T4 修。
  • T1 探针刷新(BUG-653:新增 refreshDiscriminatorProbes(caseId):带年月池空且未收敛时,用 inference_state.candidates[status=active]candidate_times、全部 answered_probesasked_probe_keys 调引擎的探针生成(build_discriminating_event_probes_remaining_contexts 分支),把新探针并入 inference_state.probes(不动 candidates / rounds / answered)。触发点:persistNextInterviewIfIdlepersistNextInterviewAfterChoicediscriminatorProbe=null 时先刷新一次再决策;每个 candidate_set_id 最多刷新 2 次(写进 inference_state.refresh_count)。验收:§1 序列第六题答完后先出现新的带年月题(预期落在家人 / 财务 / 迁居 2015–2026 的换运边界),rounds 继续为 informative;刷新不改 candidate_set_id
  • T2 引擎侧放开(BUG-653event_probes.py (a) 剩余候选 ≤5 时 MAX_PROBES_PER_DOMAIN 提到 4、MAX_PROBES 提到 12(只在 candidate_times 给定的刷新调用生效,首轮不变);(b) _dominant_existence_priorsource=dasha_boundary 且带月份的家人题不丢弃,只降序。验收:tests/test_candidate_discriminator_contract.py 新增剩余 5 簇夹具,家人月级边界题出现在 public 里。
  • T3 定向补事(BUG-654:刷新后仍无带年月题 → collection-question-pool.ts 新增 targetedCollectPool(remainingLayers, evidence, declined):按 LAYER_DOMAIN 把剩余候选的换升层映射到领域,排除已拒答 / 已覆盖,生成一条带 ≥2 个具体例子的口述题(例如「还有两条线能把 04:50 和 05:06 分开:家里哪年添丁或长辈住院、哪年收入明显变过。记得哪件说哪件,年月大概就行。」),焦点 collect:targeted:<domain>;答「没有了」→ 移除并进 S3。验收:§1 序列在刷新池空后先出这条题;答一件家人事 → 引擎重算、回到 S2;答「没有了」→ 出卡。
  • T4 交付条件与卡片文案(BUG-654)decideRectification!separation.sufficient && !probe 分支在 refreshExhausted && targetedCollectExhausted 之前不得走 completeWithRange / finish;卡片标题改「目前范围」,卡下「还能再收窄」句取 targetedCollectPool 首条;agent-voice-copy-contract 加禁词「这次给出」「最终」。
  • T5 记录BUG-653/654 入历史(关联 BUG-651、BUG-629、BUG-559、BUG-587);CHANGELOGPROGRESSdocs/testing/rectification-scenarios-20260907.md 加「六道题问完必须先刷新再补事再出卡」。

6. 让步顺序

T2(a) 的上限放宽可先只做刷新(T1)看真机效果;T2(b)、T3、T4 不可省。

7. 开工前置命令

git fetch origin --prune
git worktree add -b codex/rectification-narrow-before-deliver-20260911 .worktrees/rectification-narrow-before-deliver-20260911 origin/staging
grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1   # 应为 652
cd frontend && npm test -- tests/rectification-probe-pool-exhausted-20260911.test.ts tests/rectification-replay-20260911.test.ts tests/rectification-probe-replay-loss-20260908.test.ts tests/rectification-collection-question-pool.test.ts
cd .. && .venv/bin/python -m pytest tests/test_candidate_discriminator_contract.py -q

8. 验收口径

  • 前端 tsc --noEmit 0 错、npm run lint 0 error、相关套件 fail=0、测试总数 ≥ 基线;next build/ 仍 Static、首屏 gzip ±2%。
  • Pythontests/test_candidate_discriminator_contract.pytests/test_event_probes*.pyrun_quality_gate.py --profile quick
  • 部署后真机:按 §1 序列走,第六题答完不得直接出卡;先出新的带年月题或定向补事题;说「没有了」后出卡,卡片标题为「目前范围」且带「还能再收窄」句。