Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JUei7K13cYxLHE3Axe4A45
12 KiB
12 KiB
验收修复单 · 出卡精度门槛 + 引导式补经历(2026-09-16)
0. 基线与验收对象
- 验收对象:
cfb41daf(已合入origin/staging,任务书TASK-rectification-precision-gate-guided-collect-20260916.md)。 - 代码基线:
317e9f18(cfb41daf 的父提交)。 - staging 部署:
/api/health的deployment.gitCommit = 317e9f18,cfb41daf未部署。门禁跑的是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. 验收结论(逐条)
验收环境:本机 Linux,node_modules 软链,next build --webpack;无 Docker、无登录态、无 Chrome。
| 项 | 结论 | 证据 |
|---|---|---|
tsc --noEmit |
通过 | 0 错 |
npm run lint |
通过 | 0 error / 118 warning(基线同类) |
npm test 与基线逐条比对 |
未通过 | 基线 317e9f18:3372 条、31 红;cfb41daf:3391 条、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 定义一处;decideFromDossier 与 decideAfterInferenceChange 有 state 分支都经 narrowingExhaustion 传 precisionGateMet / guidedCollectExhausted;新单测 8 条绿 |
| T2 引导题源 | 通过(带 F4、F5) | guided_collect_windows 挂进 packet / receipt;declined_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-740~743、CHANGELOG、DESIGN、VOICE、真机清单 |
总结论:未通过。 两条门禁级红(F1、F2)必须先修;F3~F6 一并在本单做。
2. 未通过项与修法
F1(P0,门禁红)decision_receipt 新键让 memoization golden 红
- 实证:
scripts/rectification/decision_policy.py把guided_collect_windows写进 receipt;tests/test_rectification_engine_memoization.py::_assert_tiered_equal对 receipt 做键集合严格相等,左边多出guided_collect_windows。该文件在run_quality_gate.py的CORE_PYTEST_TARGETS(tests/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 路径 stopReason(F3) |
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。
F3(P1)mayDeliverOnPrecision 缺省即放行,把 BUG-654 规则一起短路
- 实证:
core/rectification-decision.tsmayDeliverOnPrecision:precisionGateMet与guidedCollectExhausted都缺省时返回true;decideRectification用它把stillNeedNarrowing、datedMethodCollectOpen一并绕过。于是任何不传这两个 flag 的调用(decideConversationalSession、decideAfterInferenceChange的无 state 分支、全部既有单测)都失去了"刷新与定向线未穷尽不得交付"(BUG-654 / 656)。tied-first-fix那条红就是这个:targetedCollectExhausted: false也交付了。另外guidedCollectExhausted()只收targetedCollectExhausted,不含refreshExhausted:引导池空、刷新还没试过,也会直接出卡。 - 修法:
- flag 缺省 → 门槛不参与,
stillNeedNarrowing/datedMethodCollectOpen按原逻辑生效(helper 路径行为回到 317e9f18)。 - flag 传入 → 出卡条件 =
userStopped || (precisionGateMet && guidedCollectExhausted && refreshExhausted && targetedCollectExhausted);precisionGateMet为假时,即使所有线问完,也只进collect(用户说「没有了」除外,那时按现行规则出卡)。 - 按 D6,
precisionGateMet不短路任何采集线;cfb41daf 里stillNeedNarrowing(input) && !mayDeliverOnPrecision(input)与datedMethodCollectOpen ... && !mayDeliverOnPrecision(input)两处改回原判断,只在最终交付分支加门槛。
- flag 缺省 → 门槛不参与,
- 验收:
rectification-tied-first-fix/decide-next-action/convergence-budget/collect-direction那几条应大部分恢复绿(原语义"线没问完不出卡"回来了);新增单测「引导池空但 refreshExhausted=false → 不交付」「门槛达标但定向线未问完 → 继续问」「所有线问完但门槛未达 → 继续引导(无题则按 D2 出卡)」。
F4(P1)引导窗口的领域是轮询分配的,一个窗口只问一个随机领域
- 实证:
event_probes.pyguided_collect_windows:Vimshottari / 那罗延本命轨道的边界与领域无关,代码用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.pysynthetic_event一律取month_lo,与真值候选的边界月无关;领域用轮询值。任务书要求"注入真值方向的带年月事件"。所以进度记录里"没有一例靠引导件达标"只说明合成事件不在真值边界上,不说明引导题源无效。 - 修法:对每个窗口,取
starts_by_time[true_time]同一轨道、同一 index 的日期作为事件月(真值方向);再跑一组"反方向"(取离真值最远的候选)作对照;三档半径都跑。报「达标件数中位」「达标例数」「反方向达标例数」。仍不是合入门槛。 - 验收:
docs/research/guided_collect_holdout_2026_09_16.json更新并附口径说明;进度记录改数字。
F6(P2)录入卡提交的是选项列表拼成的句子,走模型轮落库
- 实证:
event-date-entry-card.tsxformatEventDateEntryMessage生成「2019 年 3 月,开始认真关系、分手或结婚」发给send("message"),经分类器与模型再写账本。账本 summary 是三选一列表,不是用户说的事;每次录入多一轮模型调用与延迟。 - 修法:文本改为「YYYY 年 M 月(D 日),<领域标签>方面有一件事」;或直接走
set-focus/batch的结构化写入不经模型。账本 summary 不得含「或」列表。 - 验收:单测断言提交文本;账本 summary 断言。
F7(P3,记录即可)
page.tsx1837 → 1838:birthDate可从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. 决策记录
- D1~D5 沿用主单。
- 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-740~743。
6. 验收口径
- F1、F2 是合入门槛:Python
tests/test_rectification_*.py全绿;npm test红清单与基线逐条一致。 - F3~F6 各带单测;F5 只看数字与口径。
- 合入后
/api/health的gitCommit必须等于修复提交;仍是317e9f18视为未部署。 - 真机清单
docs/testing/rectification-guided-collect-20260916.md六条留给产品负责人。