# PROGRESS · 生时校正采集出口 / 探针去重 / prior 校准(2026-09-06) 工作树:`.worktrees/rectification-convergence-exit-20260906` 分支:`codex/rectification-convergence-exit-20260906` 基线:任务书 `2dc454c9`(staging 当时代码头 `e2f4b55c`)。执行期间 `origin/staging` 另有文档提交,本分支未 rebase。 未改 `SKILL.md`(仍 10.0.14)、未改 `scripts/jyotish_api_server.py`、未改 `frontend/src/app/page.tsx`。未改采用门 / 确认门 / `MIN_SEPARATION_LEAD=8` / 熔断常数。未把 `.venv` 加入提交。 本单状态:**部分完成**。A 完成;B1+B3 完成、B2 未做;C1 代码保留、C2 未过门故未启用。 | 任务 | 状态 | BUG | | --- | --- | --- | | A1 穷尽日志 | 完成 | BUG-558 | | A2 穷尽即交付 | 完成 | BUG-558 | | A3 口述停止 + 范围可点 | 完成(同分支,与 A1/A2 同一提交) | BUG-558 | | A4 事故形状回归 | 完成 | BUG-558 | | B1 同域同年去重 | 完成 | BUG-559 | | B2 同年月合并卡 `boundary_pair` | **未做** | BUG-559 | | B3 varga_style 锚点 | 完成 | BUG-559 | | 题干邻近账本年月 | 完成 | BUG-559 | | C1 offset/softmax 实现 | 完成(默认未启用) | BUG-560 | | C2 holdout 校准门 | 未过门 | BUG-560 | | C3 存量自动重算 | 路径仍在;本单未 bump 版本,故不会触发 | BUG-560 | | D 记录 | 完成 | — | ## A · 出口(BUG-558) - `exhaustionSpokenCollectFollowup` 在 dated + occupation 之后返回 `null`,不再落到 `other`。开场仍走 `!dashaCovered` 的 GENERIC 句;`USER_COLLECT_QUESTION.other` 留在文案表,穷尽路径不再问它。 - `persistExhaustionCollect` 每次入口 `console.warn` 结构化 `rectification_exhaustion_collect`(`case_id`、can_adopt、ceiling、training_gate、stop_class、ranked_count、probe_key、budget、next_domain/source)。不含 evidence summary、不含年份。 - 无剩余采集时:`acceptanceAllowed && trainingGate.open && canAdopt` → 采用,`stopReason=probe_pool_exhausted`,文案「能分开候选的问题已经问完,先按现有材料给你结果。」;否则门槛翻译句 + `terminalNote`,`ensureNonTerminalTurnExit` 见到 `terminalNote` 不抛。 - 口述采集:输入框上方 44px `.rectification-collect-stop`,文案 `CHOICE_STOP_LABEL`。`RectificationReadonlyRange` 改为 button,同一 `submitStop`。`ask_about_result` 与 `stop_rectification` 在点选和口述焦点都进停止。点选卡仍隐藏停止项。 - **A2 纠偏(未合入的中间稿):** 曾把 `keep_collecting` 在方法已覆盖时劫持成采用。证据不足、应继续采集的用例会变成 `completed_with_range`。已改回:`keep_collecting` **一律** `collect(...)`。 ### 既有断言改动 | 文件 | 原值 | 新值 | 原因 | | --- | --- | --- | --- | | `rectification-decision-authority` 出牌 | `offer_provisional_range` | `ready_to_adopt` | 探针空、方法覆盖且可采用时穷尽交付 | | `rectification-hidden-e2e` 同上 | 同上 | 同上 | 同上 | | `occupationClosed.next_followup` | `other` | `null` | 穷尽不再问 other | | adopt-flow / spoken-collect / timeline-adapter 源扫描 | 禁止 `rectification-collect-stop` | 要求出现 | A3 口述停止按钮 | | `rectification-answer-choice` 点 A | 旁白含范围、不再问家人 | 下一问家人采集 | B3 丢掉无锚点性格卡后按方法轮转到家人;点选仍不写账本 | ## B · 题型(BUG-559) - Python:`asked_probe_keys` 经 `normalize_rectification_request`(0–200 条、每条 ≤120)。指纹构造 **排除** 该字段。`known_years = 账本年份 ∪ 已问年份`,再进 `_existence_blocked_years`(含 `EXISTENCE_NEARBY_YEARS`)。 - TS:`existenceProbeAsked` 按 `domain.year` 与相邻年匹配。`engine-client` / `rectification-v9-tools` 把已问键传给引擎。 - `nearby_ledger_note` / `nearbyLedgerHint`:只拼领域 + 年月,不写 summary。 - B3:无同域 confirmed 带年月事件的 `varga_style` → `dropped_probes(unanchored_varga_style)`;有则 hint 带年月、`selection_score *= 0.8`。用户可见卡题干仍是「平时做事,你更接近下面哪一种?」(不写年份)。 ### B2 偏离 未实现 `choice_kind: "boundary_pair"`,未改 `contracts/probe-question-v1.json`,未改四选项 yes/weak_yes/no/unsure 语义。原因:让步顺序允许一天内做不完则保留 B1+题干锚点;合并卡要同时改 Python 契约、TS `applyRectificationChoice` 双键写入与联合熵,本单先把死胡同和同年复问堵住。 ## C · 收敛(BUG-560) `decision_policy.py` 增加 `_relative_support_offset` / `_relative_support_softmax`;`build_candidate_decisions(..., support_mode=, temperature=)`;floor = **全部分钟网格**量化分的最低值。默认: - `RELATIVE_SUPPORT_MODE = "proportional"` - `POLICY_VERSION = "rectification-candidate-policy-v3"` - `ALGORITHM_VERSION = "rectification-v5-matrix-scoring-7"` 前端 fixture 里写死的 v3 / matrix-scoring-7 缓存身份断言未改。 ### C2 holdout(20 例,全部 `excluded_from_tuning`,radius 10) 脚本:`scripts/rectification_prior_calibration.py`。每例只 `score_from_matrix` 一次,再按方案重建公开决策。门:覆盖 ≥ 旧方案 − 1 **且** 中位宽度 < 旧方案。 | scheme | coverage | median width | mean | | --- | --- | --- | --- | | proportional(旧 / 仍默认) | 17/20 | 21.0 | 18.55 | | offset | 12/20 | 10.0 | 10.3 | | softmax_0.25 | 10/20 | 6.0 | 7.6 | | softmax_0.5 | 11/20 | 9.0 | 9.55 | | softmax_1 | 12/20 | 14.5 | 13.1 | | softmax_2 | 16/20 | 21.0 | 17.15 | 无方案过门:offset 覆盖 12 < 16;softmax_2 中位 21 不小于 21。**未启用 C1。** 产品负责人确认前不得合入启用。 ### C3 `storedSnapshotIsCurrent` 仍比较 `scoringPolicyVersion`;失配 → `snapshotCurrent=false`。本单未 bump,旧 Case 打开后 **不会** 只因本单重算。测试清单第 5 条按「不会自动重算」验收。 ### C4 虚构 12 簇代表(任务书 §2.3 七件事量级:原始分 10.96–15.52) 单位测试用同一串分,显式调用 offset 时 top-2 领先不低于 proportional;等分时仍拆成 8/9。线上默认仍是 7–9 压平。公开文案「相对支持度 N」未改。 | 方案 | 12 个公开代表(示例) | | --- | --- | | proportional(启用) | 约 7–9,top-2 领先 0 | | offset(未启用) | 拉开,低分可到 0 | | softmax T=0.5(未启用) | 更尖,低分大量 0 | ## 测试 | 命令 | 结果 | | --- | --- | | `npx tsx --test` 13 个先回归文件(含 exhaustion-exit、probe-year-dedupe、answer-choice) | 225 pass / 0 fail | | 非 DB `tests/rectification-*.test.ts` + `agent-voice-copy-contract.test.ts` | **905 pass / 0 fail** | | `.venv/bin/python -m pytest` event_probes + engine_convergence + relative_support + v5_services | 70 pass / 0 fail | | `tests/test_rectification_diagnostics_clusters.py` | 5 pass / 0 fail | | `tests/test_active_rectification_api.py::test_long_real_conversation_reaches_vedastro_after_local_range_is_narrow` | 本机失败(04:16 vs 05:07);**用 HEAD 的 decision_policy/event_probes 复跑同样失败**,非本单引入 | | `./node_modules/.bin/tsc --noEmit` | 0 错 | | `npm run lint` | 0 error(既有 warning 未清) | | `npx next build --webpack`(worktree `node_modules` 符号链接使默认 turbopack 拒掉) | 通过;`/` 仍是 ○ Static | | `.venv/bin/python scripts/run_quality_gate.py --profile quick` | Python 段跑完后 `npm test` 失败:Docker 地址池耗尽导致 `*-database.test.ts` 起 postgres 失败;另有无关的 `onboarding-route` deadline 超时。非本单代码回归。本单相关非 DB 校正套件 905/905。 | 排除 `rectification-*-database.test.ts`(docker compose EPERM)。ERR-011:验收命令用 `python3 -m pytest` / `.venv/bin/python -m pytest`,不用裸 `pytest`。未改 `page.tsx`,首页静态化不应因本单变化。 ## 三次提交 1. A `150d7ef1`:出口、口述停止、A 测试与相关三栏、DESIGN/VOICE;`method-followup` 的 TS 去重/锚点因同文件随 A 2. B `3a9ae736`:引擎 `asked_probe_keys`、去重、邻近 hint、B 测试 3. C+D `62afa521`:prior 实现与校准脚本、相对支持测试、BUG_HISTORY / CHANGELOG / PROGRESS / testing / README 未 push。未改 `page.tsx`,首页静态化不因本单变化。