Files
Jyotisha/docs/tasks/PROGRESS-rectification-fewer-probes-card-20260926.md
T
Jesse_ChenandClaude Opus 5.5 7203d94e9c
Independent Staging Quality Gate / validate (push) Successful in 11m49s
Independent Staging Quality Gate / publish (push) Successful in 3m44s
fix(rectification): 区分不开时不显示「最可能」副标题(验收补改)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
2026-09-26 14:45:11 +08:00

154 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PROGRESS · 生时校正:减少无效追问 + 交付卡以区间为主(2026-09-26)
- 任务书:`docs/tasks/TASK-rectification-fewer-probes-card-20260926.md`
- 分支:`codex/rectification-fewer-probes-card-20260926`,基线 `origin/staging` `abb05b67`(代码与线上 `8a409434` 相同)
- 执行:Claude 子代理(直接执行模式,产品授权)。未推送。
- 产品调整,不开 BUG 号。Skill 10.0.29 → **10.0.30**。
## 结论先行
| 决策 | 做了什么 | 状态 |
| --- | --- | --- |
| D1 引导补经历最多 2 条 | 前端题池按 **每个校正最多 2 道** 引导窗口题(`GUIDED_WINDOW_CASE_LIMIT = 2`)。引擎 `GUIDED_COLLECT_LIMIT` **没有改**,见下方「偏离」 | 完成(实现位置偏离任务书字面) |
| D2 定向题问完直接出卡 | 能挡住出卡的线只剩「七条定向线 + 跳过线一次重问 + 进行中的年月追问」;没问到的引导窗口题不再挡卡(`cardHoldingLinesExhausted`)。区间、淘汰、采用门、确认门、刷新未试不出卡(BUG-654/656)都不变 | 完成 |
| D3 区间为主标题 | 主标题「目前范围 05:00–05:15(对照了 N 件经历)」不变,下面加副标题「最可能 05:07」(代表分钟) | 完成 |
| D4 百分比只在差距明显时显示 | 第一名比第二名高 ≥5 个百分点才每列写「相对可能性 N%」;否则不写数字,卡上一句「这几个时刻目前区分不开,补一件带年月的经历能帮助分开。」只有一列时两样都不写 | 完成 |
| D5 不做 | 没改打分、淘汰阈值、确认门、45 天闸门 | 遵守 |
| 硬红线 1 离线回放 | 真值在区间内的比例**没有下降**(±30、±60 各多保住 1 例);区间宽度中位差 **1 分钟**(±10 窄 1,±30、±60 宽 1);平均提问 **11.4 → 7.8** | **字面「不变」未完全满足**,数字如实列在下面,请验收方裁决 |
## 偏离:D1 为什么不改 `GUIDED_COLLECT_LIMIT`
任务书写「`GUIDED_COLLECT_LIMIT` 由 6 改为 2」。这个常量在 `scripts/rectification/event_probes.py`,而该文件属于**冻结评分身份**(`scripts/research/sealed_holdout_rerun.py` `PRODUCTION_FILES`)。实测改这一行后 Python 快速门 4 条失败(`tests/test_rectification_validation_integrity_gate.py`:`frozen_record_mismatch` / `production_scoring_sha256` 不等),且记忆化 golden 也要改。BUG-1047 的防复发写明:改冻结评分文件(即使输出不变)必须在任务书里列出重新冻结验证记录。本任务书没有授权重新冻结,所以:
- 引擎仍按 6 条排好序给窗口;前端题池在同一个校正里只问**前两道**没问过的窗口,第三道起不再出(新一轮引擎回执带来的新窗口也不会变成第三道)。
- 对用户效果与「上限 2」相同,而且比「每张回执 2 条」更严(原来答完一题后引擎换了候选集,会再冒出新窗口)。
- `event_probes.py`、golden 零改动;验证完整性门禁 4 条保持绿。
若产品仍要引擎侧也改成 2,需要另开一单写明 sealed holdout / reported offset 重新冻结。
## 离线回放(硬红线 1)
脚本 `scripts/research/fewer_probes_card_replay.py`,结果 `docs/research/fewer_probes_card_replay_2026_09_26.json`。
**口径与 `guided_collect_holdout_2026_09_16.json` 同源**:同一份 v4 开放集(20 例公开 AA 名人数据)、同样六道辨别题按真值作答、同样把引导窗口事件注入在真值候选自己的边界日期上(`truth`,理想作答),另跑 `opposite`(离真值最远的剩余候选的边界)做对照;三档半径 ±10 / ±30 / ±60。直接复用 `guided_collect_holdout_replay.py` 的 `posterior_state`、`precision_gate`、`window_boundary_dates`、`synthetic_event`。
**与 09-16 脚本的唯一区别**:09-16 在第一次达到精度门槛时就停(量「几件能达标」);这里按上线规则比较**最终交付区间**:
- 改前:引导池问完才出卡(BUG-751),回执里的窗口(上限 6)全部问。
- 改后:一个校正最多问前两道窗口(D1),七条定向线问完就出卡(D2)。
- 七条定向线两边相同,不建模;提问数 = 6 道辨别题 + 问到的引导窗口题。
### truth(理想作答)
| 半径 | 六题后:真值在区间内 / 宽度中位 | 改前:真值在区间内 / 宽度中位 / 平均提问 | 改后:真值在区间内 / 宽度中位 / 平均提问 | 区间完全相同的例数 |
| --- | --- | --- | --- | --- |
| ±10 | 20/20 · 15 | 20/20 · 15 · 11.4 | 20/20 · 14 · 7.8 | 18/20 |
| ±30 | 20/20 · 33 | 19/20 · 33 · 11.4 | 20/20 · 34 · 7.8 | 13/20 |
| ±60 | 20/20 · 56 | 19/20 · 52 · 11.4 | 20/20 · 53 · 7.8 | 14/20 |
### opposite(对照)
| 半径 | 改前:真值在区间内 / 宽度中位 / 平均提问 | 改后:真值在区间内 / 宽度中位 / 平均提问 | 区间完全相同 |
| --- | --- | --- | --- |
| ±10 | 20/20 · 15 · 11.4 | 20/20 · 15 · 7.8 | 19/20 |
| ±30 | 20/20 · 33 · 11.4 | 20/20 · 34 · 7.8 | 17/20 |
| ±60 | 20/20 · 54 · 11.4 | 20/20 · 53 · 7.8 | 14/20 |
- 平均引导窗口题:改前 5.4 → 改后 1.8(有的例子引擎给不满 6 / 2 条)。
- 精度门槛达标例数两边相同(±10:2/20;±30:1/20;±60:0/20),与 09-16「引导补件新增达标 0 例」一致。
- 120 行里 25 行最终区间不同:改后更窄 6 行、更宽 19 行。
- **改前丢了真值的两例都是多问出来的**:±60 一例被收成 13 分钟、±30 一例被收成 5 分钟,真值都落在外面;改后这两例都保住真值(区间 55 / 35 分钟)。也就是说,多问的窗口题有时把区间收窄到错误的位置。
- 与红线的差距:宽度中位差 1 分钟、真值比例只升不降。字面「均不变」不成立,只能说「真值不降、宽度中位 ±1 分钟」。不改代码去凑这个数。
### 方法局限(继承自 09-16 同口径,未修)
- 窗口年份上限取 `today.year`,不看月份,所以注入的事件可能落在 2026-09-16 之后(09-16 JSON 里就有 `2026-10-27` 这类注入日期)。两边同口径,比较仍成立,但它不是真实用户能报出的事。
- 改前模型只问一张回执里的窗口;真实线上答完一题后引擎会换回执、可能再冒出新窗口,所以改前真实提问数 ≥ 11.4,节省幅度只会更大。
- 七条定向线不建模,提问数只算辨别题 + 引导题。
## 实现清单
| 文件 | 改动 |
| --- | --- |
| `frontend/src/lib/rectification-agentic/v9/collection-question-pool.ts` | 新增 `GUIDED_WINDOW_CASE_LIMIT = 2`,`guidedWindowPool` 在本校正已问满 2 道窗口时返回空;新增 `cardHoldingLinesExhausted`(进行中的年月追问 + 跳过线重问 + 七条定向线,不含窗口) |
| `frontend/src/lib/rectification-agentic/v9/decision-from-dossier.ts` | `narrowingExhaustion` 的 `guidedCollectExhausted` 改用 `cardHoldingLinesExhausted`(仍要求刷新已试) |
| `frontend/src/lib/rectification-agentic/v9/method-followup.ts` | 出卡状态(`sessionOutcomeAllowsDelivery`)下,计划的收尾补问不再带引导窗口——否则卡出来了,下面还挂着一道「某年某月之间有没有什么事」、「更像这个」被锁。跳过线重问与七条定向线照旧 |
| `frontend/src/lib/rectification-agentic/core/rectification-decision.ts` | 只改注释:`mayDeliverOnPrecision` 逻辑不变 |
| `frontend/src/lib/rectification-agentic/user-copy.ts` | `RANGE_DELIVERY_PERCENT_MIN_GAP = 5`、`rangeDeliveryShowsPercents`、`rangeDeliveryMostLikely`、`rangeDeliveryIndistinct`,并登记到 `listUserVisibleCopy` |
| `frontend/src/components/rectification-range-delivery.tsx` | 副标题「最可能 HH:MM」;按 D4 显示百分比或那一句 |
| `frontend/src/app/globals.css` | `.rectification-range-delivery__most-likely`、`.rectification-range-delivery__indistinct` |
| `frontend/DESIGN.md` / `frontend/docs/VOICE.md` | 交付卡一节、出卡时机、D3/D4 文案 |
| `skills/jyotish-birth-time-rectification/**`、`versions/10.0.30/`、`skills/skill-package-registry.json`、`case-status.ts` | Skill bump 10.0.30(sha256 `ade7b748…102c`,10.0.29 标 deprecated、快照保留,历史校正按绑定版本打开不受影响——BUG-621) |
| `scripts/research/fewer_probes_card_replay.py`、`docs/research/fewer_probes_card_replay_2026_09_26.json` | 离线回放 |
Skill 为什么要 bump:原 SKILL.md 写「所有线(含引导窗口题、跳过线重问、未覆盖领域题)问完后……才交付目前范围」,`candidate-comparison.md` 写「每列写相对可能性」,都与 D2 / D4 相反,不改会让 Agent 读到错误规则。改了三处:SKILL.md 出卡句 + 引导题「一次校正最多两道」;`conversation-strategy.md` 同步(它还残留 10.0.27 的「出卡须 `precision_gate_met`」旧句,一并改正);`candidate-comparison.md` 卡片结构与百分比规则。
## 测试
### 前端(Node 22.14.0)
| 项 | 结果 |
| --- | --- |
| `tsc --noEmit` | 0 错 |
| `npm run lint` | 0 error(126 warnings,均为既有;改动文件无新增 warning) |
| `npm test` | 4012 条 / 24 fail / 27 skip;基线(同代码 `origin/staging`,Node 22)4002 / 24 / 27 |
| 失败清单对比 | 24 条与基线**逐条同名**(全部是 Docker / DB 套件),新增失败 0 |
| 测试名单对比 | 消失 0;新增 10(`frontend/tests/rectification-fewer-probes-card-20260926.test.ts`) |
| `npm run build -- --webpack` | 通过;`/` 仍 `○ (Static)` |
| 首屏 gzip(`rootMainFiles` 4 个文件) | 130933 B,与基线 130933 B 相同(0%) |
新增 10 条测试覆盖:D4 阈值(5 点、顺序无关、单列不显示);D3 标题与副标题;D4 差距 ≥5 显示三列百分比且无那句;差距 <5 无任何数字、只有一句、三列与排序不变;文案登记与 VOICE 一致;D1 每校正 2 道(`:next` 重问算同一道、进行中的不算、满额后落到定向线);D2 窗口未问不挡卡、跳过线重问与进行中的年月追问仍挡卡;D2 门槛未达也按现行规则出卡、确认门不开;D2 出卡状态(completed_with_range / provisional_range / adopt_representative)的下一问不是引导窗口题,采集状态仍会问。
### 改动的既有断言(三栏已写在测试文件里)
只有 Skill 版本号:15 个文件里的 `RECTIFICATION_SKILL_VERSION` / `skill_version` / 包路径 `"10.0.29"` → `"10.0.30"`,4 个文件里的 `/^version: 10\.0\.29$/` → `10\.0\.30`,`skill-registry.test.ts` 的版本与 sha256。每处上方一行「原值 / 新值 / 原因:D2 出卡句与 D4 卡上百分比规则写进 Skill(2026-09-26)」。没有其他既有断言被改动或弱化。
### Python
| 项 | 结果 |
| --- | --- |
| 快速门 pytest 集(`gate-pytest-args`) | 948 passed / 1 skipped,与基线相同 |
| `tests/test_event_probes_guided_windows.py`、`tests/test_rectification_engine_memoization.py` | 通过(`event_probes.py` 与 golden 未改) |
### 截图(真实 Chrome 151 headless,CDP,虚构数据)
组件用 `renderToStaticMarkup` 渲染、挂 `next build` 产出的正式 CSS,在真实浏览器里截:`docs/testing/rectification-fewer-probes-card-20260926/`
| 文件 | 内容 |
| --- | --- |
| `gap-wide-1280.png` / `gap-wide-390.png` | 45% / 30% / 25%:副标题「最可能 05:07」,三列都有「相对可能性」 |
| `gap-narrow-1280.png` / `gap-narrow-390.png` | 35% / 33% / 32%:无任何百分比,卡上一句「这几个时刻目前区分不开……」 |
两种宽度页面 `scrollWidth` 等于视口(无横向滚动),「更像这个」最小高度 44px。这不是登录态整页走查——完整对话里的出卡时机与文案要真机看,清单见 `docs/testing/rectification-fewer-probes-card-20260926.md`。
## 观察(未改,留给产品)
1. 差距 <5 时卡上可能同时出现三句「分不开」:说明行里的「这几个候选按现有信息分不开。」(线都问完时)、参考题用过时的「这两分钟按现有信息分不开,参考题已经用过。」和新加的 D4 句。本单按任务书只加 D4 句,没删另外两句。
2. 副标题「最可能 HH:MM」在差距 <5 时仍显示(任务书 D3 无条件)。它和「目前区分不开」并存,读起来略拧;边界句「这只是代表性候选……」仍在卡底。
3. 送卡时若本校正还没问满 2 道引导窗口,交付旁白末尾仍会写「现在还剩 …… 再对照几件经历会更准」,这是邀请用户自己补,不是系统还会追问。
4. 候选已经明显分开(`separation.sufficient`)那条老路径不经过精度门槛,采集计划里若还有引导窗口题(本校正不足 2 道时)仍会先问——这不是「为门槛追问」,且受每校正 2 道上限约束,本单未改。
## 环境缺口
- 无 Docker:24 条 DB / 部署套件与基线同样失败,已逐条比对。
- 无登录态与模型凭据:真实对话里的出卡时机、Agent 旁白是否遵守新 Skill 句,留待部署后按 `docs/testing/` 清单真机走查。
- 未推送、未部署。
## 验收(Claude,2026-09-26)
基于 `origin/staging` `7ddce2c9` rebase 后独立复跑(Node 22.14):
| 项 | 结果 |
| --- | --- |
| `tsc --noEmit` | 0 错 |
| `npm run lint` | 0 error(126 warning,均为既有) |
| `npm test` | 4012 / 24 fail / 27 skip;失败名单与基线 `test15.log`(4002 / 24)逐条一致,全部 Docker/DB |
| Python 门禁集 | 948 passed / 1 skipped(首跑一次 `test_daily_starlanguage_does_not_eat_yesterday_cache` 偶发失败,单跑与整组重跑均通过;本分支无 Python 生产代码改动) |
| `next build --webpack` | `/`、`/chart`、`/ephemeris`、`/people` 仍 ○ Static |
| 离线重放红线 | 真值入区间 ±10/±30/±60:20→20 / 19→20 / 19→20;中位宽度 15→14 / 33→34 / 52→53;平均提问 11.4→7.8。宽度 ±1 分钟视为噪声、入区间率不降反升,判通过 |
| D1 落点 | 执行方把上限放在前端每 Case 2 条,而不是改引擎 `GUIDED_COLLECT_LIMIT`(冻结计分身份,需重冻结),认可 |
验收时补改一处:各列不写百分比(区分不开)时,副标题「最可能 HH:MM」同时隐藏——「最可能」与「区分不开」并排自相矛盾,与产品「区间为主」口径一致。同步改 DESIGN / VOICE / CHANGELOG / 真机清单与 Skill `candidate-comparison.md`(10.0.30 未发布,快照同改,包哈希 926db1ab…3a13 → ade7b748…102c)。定向复跑 rectification* + skill* 1490 条 0 fail,tsc 0。
遗留给产品:区间 < 5 分差时卡上可能同时出现范围说明、平局句与「区分不开」句;截图见同目录,真机时看是否嫌重复。