Files
Jyotisha/docs/tasks/TASK-rectification-precision-gate-guided-collect-fix-20260916.md
T

12 KiB
Raw Blame History

验收修复单 · 出卡精度门槛 + 引导式补经历(2026-09-16

0. 基线与验收对象

  • 验收对象:cfb41daf(已合入 origin/staging,任务书 TASK-rectification-precision-gate-guided-collect-20260916.md)。
  • 代码基线:317e9f18cfb41daf 的父提交)。
  • staging 部署:/api/healthdeployment.gitCommit = 317e9f18cfb41daf 未部署。门禁跑的是 CORE_PYTEST_TARGETS,其中 tests/test_rectification_*.py 含本单弄红的一条(§2 F1),与未部署的现象一致。
  • 分支:codex/rectification-precision-gate-guided-collect-fix-20260916,基于最新 origin/staging
  • BUG 段:BUG-744 起(基线最大 BUG-743)。

1. 验收结论(逐条)

验收环境:本机 Linuxnode_modules 软链,next build --webpack;无 Docker、无登录态、无 Chrome。

结论 证据
tsc --noEmit 通过 0 错
npm run lint 通过 0 error / 118 warning(基线同类)
npm test 与基线逐条比对 未通过 基线 317e9f183372 条、31 红;cfb41daf3391 条、47 红。新增 16 红,全部是校正套件既有断言,一个文件都没改(§2 F2)。基线的 31 红是无 Docker / [eval] 路径别名那一组,两边相同
next build / Static 通过 ○ /
首屏 gzip 通过 575,108 → 577,616+0.44%
page.tsx 不增长 勉强 1837 → 1838+1 行 birthDate prop
Python 定向(4 文件) 通过 56 passed
Python tests/test_rectification_*.py 全量 未通过 test_rectification_engine_memoization.py::test_score_candidates_matches_baseline_golden 红:decision_receipt 多了 guided_collect_windows 键;基线同文件 14 全绿(§2 F1)
T1 出卡门槛 通过(带 F3 deliveryMaxWidthMinutes 只在 policy JSON 定义一处;decideFromDossierdecideAfterInferenceChange 有 state 分支都经 narrowingExhaustionprecisionGateMet / guidedCollectExhausted;新单测 8 条绿
T2 引导题源 通过(带 F4、F5 guided_collect_windows 挂进 packet / receiptdeclined_domains 进合同校验;不改 MIN_BOUNDARY_DAYS
T3 引导问法与录入 通过(带 F6 题型顺序、录入卡、选择器年份范围、门槛未达无自由文本邀请,新单测绿;RANGE_DELIVERY_OPEN_COLLECT_* 已删净
T4 / T4b 通过 跳过重问一次、拒绝不重问、七条线整领域题干、时间点负答案不关领域、七领域遍历断言
T5 Skill 10.0.27 通过 registry + versions 目录 + hash;历史会话可开只有合同测试,浏览器级留清单第 6 条
T6 通过 BUG-742 已修;BUG-743 investigating 且没编根因
T7 记录 通过 BUG-740743、CHANGELOG、DESIGN、VOICE、真机清单

总结论:未通过。 两条门禁级红(F1、F2)必须先修;F3~F6 一并在本单做。

2. 未通过项与修法

F1P0,门禁红)decision_receipt 新键让 memoization golden 红

  • 实证:scripts/rectification/decision_policy.pyguided_collect_windows 写进 receipttests/test_rectification_engine_memoization.py::_assert_tiered_equal 对 receipt 做键集合严格相等,左边多出 guided_collect_windows。该文件在 run_quality_gate.pyCORE_PYTEST_TARGETStests/test_rectification_*.py)里。
  • 修法:这是形状变化,不是 BUG-733 禁止的"重建 golden 掩盖浮点漂移"。按 BUG-733 的口径,同进程差分仍是主证据;golden 只允许追加 guided_collect_windows 一个键,进度记录里贴 golden 的 diff,必须只有这一处。不得放宽 _assert_tiered_equal 的键集合比较。docs/BUG_HISTORY.md 的 BUG-733 条目补一句"2026-09-16 因 receipt 新增键重生成,diff 仅此一键"。
  • 验收:.venv/bin/python -m pytest tests/test_rectification_*.py 全绿;golden diff 只含新键。

F2(P0,红线 §7.3)16 条既有前端断言红,进度记录写成"已按三栏改"

新红分布(基线全绿):

文件 条数 性质
rectification-collect-direction-20260904.test.ts 3 门槛语义(原"分开即停采集")
rectification-adopt-flow-fix-20260903.test.ts 2 门槛语义 / 题干
rectification-adopt-narration-20260904.test.ts 2 门槛语义 / 提示句
rectification-targeted-collect-cards-20260913.test.ts 2 旧题干「结过婚或订过婚吗」、旧邀请 /不限领域/
rectification-answer-choice.test.ts 1 选项文案
rectification-choice-card.test.ts 1 题干
rectification-convergence-budget.test.ts 1 helper 路径 stopReasonF3
rectification-decide-next-action.test.ts 1 helper 路径(F3
rectification-delivery-vs-collect-20260914.test.ts 1 门槛语义(GET 并列不再交付)
rectification-superseded-focus.test.ts 1 七条线全轮到后"最后一问"变了
rectification-tied-first-fix-20260914.test.ts 1 helper 路径吞掉 BUG-654 规则(F3
  • 修法:逐条二选一——属 D1~D5 预期变化的,改断言并写「原值 / 新值 / 原因」三栏;属 F3 的,先修代码再看断言。不得删测试、不得 skip。 进度记录列 16 条各自的处置。
  • 验收:npm test 红数与基线逐条一致(31 条同名),总数 ≥ 3391。

F3P1mayDeliverOnPrecision 缺省即放行,把 BUG-654 规则一起短路

  • 实证:core/rectification-decision.ts mayDeliverOnPrecisionprecisionGateMetguidedCollectExhausted 都缺省时返回 truedecideRectification 用它把 stillNeedNarrowingdatedMethodCollectOpen 一并绕过。于是任何不传这两个 flag 的调用(decideConversationalSessiondecideAfterInferenceChange 的无 state 分支、全部既有单测)都失去了"刷新与定向线未穷尽不得交付"BUG-654 / 656)。tied-first-fix 那条红就是这个:targetedCollectExhausted: false 也交付了。另外 guidedCollectExhausted() 只收 targetedCollectExhausted,不含 refreshExhausted:引导池空、刷新还没试过,也会直接出卡。
  • 修法:
    1. flag 缺省 → 门槛不参与stillNeedNarrowing / datedMethodCollectOpen 按原逻辑生效(helper 路径行为回到 317e9f18)。
    2. flag 传入 → 出卡条件 = userStopped || (precisionGateMet && guidedCollectExhausted && refreshExhausted && targetedCollectExhausted)precisionGateMet 为假时,即使所有线问完,也只进 collect(用户说「没有了」除外,那时按现行规则出卡)。
    3. 按 D6precisionGateMet 不短路任何采集线;cfb41daf 里 stillNeedNarrowing(input) && !mayDeliverOnPrecision(input)datedMethodCollectOpen ... && !mayDeliverOnPrecision(input) 两处改回原判断,只在最终交付分支加门槛。
  • 验收:rectification-tied-first-fix / decide-next-action / convergence-budget / collect-direction 那几条应大部分恢复绿(原语义"线没问完不出卡"回来了);新增单测「引导池空但 refreshExhausted=false → 不交付」「门槛达标但定向线未问完 → 继续问」「所有线问完但门槛未达 → 继续引导(无题则按 D2 出卡)」。

F4(P1)引导窗口的领域是轮询分配的,一个窗口只问一个随机领域

  • 实证:event_probes.py guided_collect_windowsVimshottari / 那罗延本命轨道的边界与领域无关,代码用 fallback[unlayered_index % len(fallback)] 轮流贴一个领域;只有 nara:d9 / nara:d10 两条轨道有固定领域。前端 windowAlreadyAsked(年, 月区间, 领域) 为键,用户答「这段没有」只关这一组合,但引擎每个边界只产一行,同一窗口不会再以别的领域出现。结果是"2018 年 3 到 5 月有没有换工作"答没有,用户那段时间的搬家或恋爱永远问不到。
  • 修法(表驱动,七条线一样):无领域轨道的窗口题不指定单一领域,题干「YYYY 年 M 到 M 月之间,有没有什么事,比如<开放领域口语 2~3 个>?」,domain 字段留 null"any"A 之后录入卡的类型芯片由用户选;d9 / d10 轨道保持固定领域。windowAlreadyAsked 键去掉领域。declined_domains 只用于剔除例子。
  • 验收:Python 单测「无领域轨道窗口 domain 为 any」;前端单测「any 窗口题干列 ≤3 个开放领域口语、A 后芯片无默认或默认第一开放领域」。

F5(P1,研究口径)离线回放没按任务书注入真值方向事件,"0/20"不能作数

  • 实证:scripts/research/guided_collect_holdout_replay.py synthetic_event 一律取 month_lo,与真值候选的边界月无关;领域用轮询值。任务书要求"注入真值方向的带年月事件"。所以进度记录里"没有一例靠引导件达标"只说明合成事件不在真值边界上,不说明引导题源无效。
  • 修法:对每个窗口,取 starts_by_time[true_time] 同一轨道、同一 index 的日期作为事件月(真值方向);再跑一组"反方向"(取离真值最远的候选)作对照;三档半径都跑。报「达标件数中位」「达标例数」「反方向达标例数」。仍不是合入门槛。
  • 验收:docs/research/guided_collect_holdout_2026_09_16.json 更新并附口径说明;进度记录改数字。

F6(P2)录入卡提交的是选项列表拼成的句子,走模型轮落库

  • 实证:event-date-entry-card.tsx formatEventDateEntryMessage 生成「2019 年 3 月,开始认真关系、分手或结婚」发给 send("message"),经分类器与模型再写账本。账本 summary 是三选一列表,不是用户说的事;每次录入多一轮模型调用与延迟。
  • 修法:文本改为「YYYY 年 M 月(D 日),<领域标签>方面有一件事」;或直接走 set-focus / batch 的结构化写入不经模型。账本 summary 不得含「或」列表。
  • 验收:单测断言提交文本;账本 summary 断言。

F7P3,记录即可)

  • page.tsx 1837 → 1838birthDate 可从 conversational-birth-time-rectification.tsx 内部取档案,把那一行挪出去。
  • tests/test_event_probes_guided_windows.py 用手造 static contexts(沿用 test_rectification_event_probes.py 既有模式);test_declined_domains_are_removed 在空窗口时 skipTest。至少一条用真实引擎 golden 的 20 分钟窗口断言窗口非空、不许 skip。

3. 决策记录

  • D1D5 沿用主单。
  • D6(产品负责人 2026-09-16 拍板:问完再出)precisionGateMet 只是出卡的必要条件,不是充分条件。交付仍要等自动题、引导窗口题、跳过线重问、未覆盖领域题全部问完(或用户说「没有了」)。也就是说 cfb41daf 里"门槛达标就短路剩余采集线"的写法要撤回:stillNeedNarrowing / datedMethodCollectOpen 不再被 mayDeliverOnPrecision 绕过。Skill §流程保留"所有线问完或用户说没有了后才交付"原句,只在其前加"且目前范围 ≤10 分钟、头名不并列"。

4. 硬红线

主单 §4 全部沿用。另加:不得放宽 _assert_tiered_equal;不得删或 skip 任何既有测试;F4 不得按领域写 if。

5. 开工前置

git fetch origin --prune && git status -sb
git worktree add -b codex/rectification-precision-gate-guided-collect-fix-20260916 \
  .worktrees/rectification-precision-gate-guided-collect-fix-20260916 origin/staging
grep -o "^## BUG-[0-9]*" docs/BUG_HISTORY.md | sort -t- -k2 -n | tail -1   # 应为 BUG-743
cd frontend && npm test 2>&1 | grep -E "^# (tests|pass|fail)"                  # 记基线 3391 / 47
cd .. && .venv/bin/python -m pytest tests/test_rectification_*.py -q | tail -3

Bug 检索:BUG-733、BUG-654、BUG-656、BUG-740743。

6. 验收口径

  • F1、F2 是合入门槛:Python tests/test_rectification_*.py 全绿;npm test 红清单与基线逐条一致。
  • F3~F6 各带单测;F5 只看数字与口径。
  • 合入后 /api/healthgitCommit 必须等于修复提交;仍是 317e9f18 视为未部署。
  • 真机清单 docs/testing/rectification-guided-collect-20260916.md 六条留给产品负责人。