fix(rectification): honor declared birth-time uncertainty and split windows over two hours (BUG-571–573)

Intake stores how sure the user is; rectification now searches that range, offers a one-click widen when event fit is low at the edge, and trisects windows longer than two hours before the minute grid.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jesse_Chen
2026-09-07 12:17:04 +08:00
parent b2a8d3a005
commit 8e31680b45
49 changed files with 3047 additions and 142 deletions
+48
View File
@@ -8838,6 +8838,54 @@
- 复发自:unknown-time `block_scan``814c924e`)按时段长度偏置
- 修复版本:`517df002`
## BUG-571 | 档案里声明的不确定度被忽略,有钟点一律 ±15
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:`frontend/src/lib/birth-time-intake-model.ts``birth-time-intake.tsx``account-profile-patch.ts``case-service.ts::deriveRectificationOpenPlan`
- 用户现象:家人记得大概时间时,intake 选不到前后半小时 / 一小时 / 两小时;校正开工窗口仍是填报钟点 ±15,档案里的 `uncertainty_before/after` 不参与搜索。
- 触发条件:资料有填报钟点;来源本应是 `approximate` 或医院记录。
- 根因:intake 单选只露出 `family_exact` / `period_only`(含 unknown)。开 Case 用固定 `FRESH_CASE_SEARCH_RADIUS_MINUTES = 15`,注释写明那是引擎边界、不是用户声明。
- 修复:intake 改三档——医院记录(固定 ±2 无感检查)、家人大概时间(±15/30/60/120)、时段或完全不知道。`family_exact` 从新选项移除,读档映射成「差不多准」。有钟点时:`hospital_record` / `family_exact` → ±15`approximate` → ±max(15, 声明值),上限 120,前后独立。窗口 exclusive span >120 先进 `block_scan`BUG-573)。
- 验证:`birth-time-intake.test.ts` 三档与 120 校验;`account-api.test.ts` 120 合法、90 非法;`rectification-v9-case-service.test.ts` 既有三处 `family_exact` 04:4505:15 保留,新增 approximate ±60 → 04:0006:00、±120 → 03:0007:00。
- 防复发:有钟点开 Case 不得再写死 ±15;`approximate` 校验集合必须与 intake 按钮同为 {15,30,60,120}。
- 相关记录:BUG-572、BUG-573
- 复发自:无
- 修复版本:待合入 `origin/staging``codex/rectification-declared-uncertainty-20260907`
## BUG-572 | 事件吻合率低且贴窗口边缘时不提议放宽搜索窗
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:`decision-from-dossier.ts``window-widen.ts``method-followup.ts``widen_agentic_rectification_case_window``set_agentic_rectification_widen_declined`
- 用户现象:经历已经明显对不上家人记的时间,校正仍只在原来的 ±15/±30 里出区分卡,没有一键放宽。
- 触发条件:训练门已开、`event_fit_rate.band=low``total≥3`、代表分钟距窗口边缘 ≤3 分钟、`stage=minute`、未采用、有填报钟点、当前证据指纹未拒过、当前半径 <120。
- 根因:`event_fit_rate` 只进采用后八法报告。分钟阶段没有改 `candidate_range` 的 RPC;唯一改窗 RPC 要求 `stage=block_scan`
- 修复:服务端四选卡 `choice_kind: widen_window`(不进 Python 探针契约),排在区分卡之前。A/B 扩大窗口(必须包含旧窗、inclusive 宽 ≤241)、重算,并把档案写成 `approximate` + 新半径;`hospital_record` 只改窗口不改档案来源。C/D 记 `widen_declined_at_fingerprint`。档位 15→±30/±6030→±60/±120,60→±120 / 「先补一件带月份的经历再说」。D 文案「说不好 / 先这样」(`clippedCopy` 最短 4 字)。不动采用/确认门、`MIN_SEPARATION_LEAD`、分钟级 `_relative_support``adopted_credible_range`
- 验证:`rectification-window-widen-20260907.test.ts` 六条;迁移合同锁 widen RPC 与不得改 `adopted_credible_range`。Docker `test:db``database-rectification-block-scan.test.ts` 加 widen 扩大/缩小与权限。
- 防复发:放宽卡必须服务端生成;不得让模型问「偏移 / 误差」;widen RPC 只许扩大且包含旧窗。
- 相关记录:BUG-571、BUG-573
- 复发自:无
- 修复版本:待合入 `origin/staging``codex/rectification-declared-uncertainty-20260907`
## BUG-573 | 选定时段或 ±120 窗口后直接进 4~6 小时分钟网格
- 状态:resolved
- 首次发现:2026-09-07
- 最近更新:2026-09-07
- 影响面:`scripts/rectification/api_service.py::block_scan``contracts.py` 可选 `blocks``advance_agentic_rectification_case_from_block_scan``search-window.ts``block-scan.ts`
- 用户现象:只知道傍晚或家人说前后两小时时,选定后马上在 4~6 小时上按分钟比,网格过密、题也问不清。
- 触发条件:exclusive span >120(例如 18:0022:59、03:0007:00、下午 12:0017:59)。
- 根因:`block_scan` 只服务全日五时段;`advance_*` 要求原窗 00:00–23:59,选定后一律 `stage=minute``set_stage` 也曾要求全日窗。
- 修复:请求可带 15 段 `blocks`(须落在窗口内、除共享端点外不重叠)。无 `blocks` 时仍用五声明时段;全日默认步长 10。子窗三等分、无共享分钟;步长 >360→10、>180→5、其余 2。`advance_*` 改为新窗须为当前窗子区间;span>120 且 rounds<3 仍 `block_scan`,否则 `minute`。每 Case 最多 3 张子段卡,超过旁白「时段分不开,直接按分钟比」。unknown 仍先五时段,赢段再切三段。全零相对支持按 100/n 均分(三段 33.3/33.3/33.4),不再写死五段各 20。不改分钟级 `_relative_support`
- 验证:`test_rectification_v5_services.py` 自定义三段和为 100、等分 33.3/33.3/33.4、越界/重叠报错;`rectification-block-scan-20260906.test.ts` ±120 开 Case 为 block_scan、傍晚三子段、选下午后仍 block_scanDocker 上午 08:0011:59 选定后仍 block_scan08:0009:00 进 minute。
- 防复发:exclusive span >120 不得直接 `stage=minute``block_scan` 全零均分必须按段数,不得写死 20%。
- 相关记录:BUG-570、BUG-571
- 复发自:unknown-time 选段后一律进分钟(`814c924e`
- 修复版本:待合入 `origin/staging``codex/rectification-declared-uncertainty-20260907`
## BUG-574 | 报告列表 JSON 路径 select 在 staging PostgREST 上 500
- 状态:resolved
@@ -0,0 +1,56 @@
# PROGRESS · 出生时间「有多确定」只问一次(2026-09-07)
工作树:`.worktrees/rectification-declared-uncertainty-20260907`
分支:`codex/rectification-declared-uncertainty-20260907`(跟踪 `origin/staging`
基线:任务书落地 commit `ad9283d1`(含 BUG-565570);合入前 rebase 到当时 `origin/staging`(含 BUG-574 `b2a8d3a0`)。
未改 `SKILL.md`(仍 10.0.14)、未改采用门 / 确认门 / `MIN_SEPARATION_LEAD` / 分钟级 `_relative_support`、未改 `page.tsx`、未改 `jyotish_api_server.py`、未改 `contracts/probe-question-v1.json`。迁移文件为 `20260907020000_rectification_declared_uncertainty.sql``20260907010000` 已被 `personal_report_card_summary` 占用)。
本单状态:**待验收**。BUG-571 / 572 / 573 已实现。
| 任务 | 状态 | BUG |
| --- | --- | --- |
| D1 intake 三档 + ±120 | 完成 | — |
| D2 校正窗口读档案 | 完成 | BUG-571 |
| D5 子段扫描 | 完成 | BUG-573 |
| D3 放宽窗口一键卡 | 完成 | BUG-572 |
| D6 日级可靠度 + 手测脚本 | 完成 | — |
| D4 记录 | 完成 | 本文件 / BUG_HISTORY / CHANGELOG / DESIGN / VOICE / testing |
## 实现要点
- 窗口阶段看 **exclusive span**04:0006:00 = 120 → minute03:0007:00 = 240 → block_scan)。放宽上限看 **inclusive width ≤ 241**
- 子段是当前窗三等分、无共享分钟。unknown 仍先扫五个声明时段(步长 10),赢段再切三段。
- 放宽卡优先级高于区分卡。`hospital_record` 答 A 只改窗口。
- 日级可靠度只在**本回合刚写入**的 day 事件上问(`source_turn_id === askedTurnId`),不拦采用门、不改历史账本上的旧日事件采集顺序。
### 书面偏差
| 项 | 任务书 | 实际 | 原因 |
| --- | --- | --- | --- |
| D6 写回 | 「用现有证据修订链,不新造 RPC」 | 薄 RPC `set_agentic_rectification_evidence_date_reliability`(原地 high/medium | revise/supersede 会新开证据行、改指纹,误伤训练门 |
| 放宽 D 文案 | 「说不好」 | 「说不好 / 先这样」 | `clippedCopy` 最短 4 字 |
| `block_scan` 全零均分 | 五段各 20% | 100/n(三段 33.3/33.3/33.4 | 自定义 blocks 时写死 20 只给到 60 |
| 迁移时间戳 | `20260907010000_...` | `20260907020000_...` | staging 已占用 `20260907010000_personal_report_card_summary.sql` |
### 既有断言改动
| 文件 | 原值 | 新值 | 原因 |
| --- | --- | --- | --- |
| `rectification-v9-case-service` 04:4505:15 | 有钟点一律 | 仅 `family_exact` / 医院记录;approximate 另写 | BUG-571 |
| `database-rectification-block-scan` 08:0011:59 | 选定后 `minute` | 选定后仍 `block_scan` | span 239>120 |
| `rectification-block-scan` 选下午 | 进分钟 | 仍 `block_scan` | 12:0017:59 span 359 |
| `birth-time-intake` `/前后两小时/` | 扫组件源码 | 扫 `approximateUncertaintyOptions` | 文案在 model |
## 测试
| 命令 | 结果 |
| --- | --- |
| 非 DB `rectification-*` + `consultation-*` + `report-*` + `personal-report-*` + `birth-time-*` + `account-api` + `agent-voice-copy-contract` | **1913 pass / 0 fail**(≥ 1521 |
| `.venv/bin/python -m pytest tests/test_rectification_v5_services.py -k 'not seven_events'` | **33 pass / 0 fail**(含自定义三段 100 与 33.3/33.3/33.4 |
| `test_block_scan_seven_events_finishes_within_fifteen_seconds` | 本机连跑偶发超 15s,与 BUG-570 同一 P2,未改断言 |
| `npx tsc --noEmit` | 0 错 |
| `./node_modules/.bin/eslint --quiet src tests` | 0 error |
| `frontend npm run test:db` | **37 pass / 0 fail**547.7s;含 `database-rectification-block-scan`08:0011:59 选定后仍 `block_scan`08:0009:00 进 `minute`,widen 扩大成功 / 缩小失败 / service_role-only |
排除 `*-database.test.ts` 于非 DB 套件。ERR-011Python 用 `.venv/bin/python -m pytest`。未改 `page.tsx`。未改分钟级 `_relative_support`。无 Docker 缺口,未写 `BLOCKED.md`
+1 -1
View File
@@ -67,7 +67,7 @@
| `TASK-rectification-range-reading-20260906.md` | `PROGRESS-rectification-range-reading-20260906.md` | 可信区间成为一等公民:采用时落库 `adopted_credible_range`,报告 `read_report_candidate_range` 与聊天 `verified_chart(accepted)` 都改读它并接同一份 `birth_time_sensitivity`(现在报告读的是开工窗口,BUG-568);引擎 >15 分钟只取 3 样本改为 ≤31 逐分钟;采用旁白加「稳定 / 随分钟变」两句;`declared_birth_window` 复用 | 已验收通过(test:db 37/0 为执行方数字,本机无 Docker) | `814c924e`(BUG-568);两条迁移待应用 |
| `TASK-rectification-unknown-time-20260906.md` | `PROGRESS-rectification-unknown-time-20260906.md` | 完全不知道出生时间的两段式路线:`stage=block_scan` 以 10 分钟步长扫 24 小时只做事件计分、出五时段四选卡(不写账本不采用),选定后进现有分钟流程;引擎加 `minute_step`;开场读 `birth_time_clue`;删 intake 劝退文案 | 已验收(带修复单:时段支持度按段长偏置,下午段先天 25%,P1) | `814c924e`;修复单 `TASK-rectification-capability-fix-20260907.md`BUG-570 |
| `TASK-rectification-capability-fix-20260907.md` | `PROGRESS-rectification-capability-fix-20260907.md` | 能力补齐修复单:答后旁白把 `range_start/range_end`(搜索窗口)当范围比较,每题都说「范围没变」;`block_scan` 五段支持度按段内原始分求和,长时段先天占优(24/24/36/30/30 个候选);`TRACK_LABEL` 音译与产品 Vimshottari/Narayana 口径不一;BLK-001 写到 `docs/BLOCKED.md` 应回根目录 | 已验收通过(P2block_scan 15 s 壁钟断言在门禁里可能间歇红) | `517df002`BUG-569570);staging 未部署,需先 Migrate Staging Database |
| `TASK-rectification-declared-uncertainty-20260907.md` | `PROGRESS-rectification-declared-uncertainty-20260907.md` | 出生时间「有多确定」只在 intake 问一次:三档(医院记录 / 家人大概 ±15·30·60·120 / 时段或未知),校正窗口读档案(现在有钟点一律 ±15,声明值被忽略,BUG-571);吻合率 <60% 且代表分钟贴窗口边缘时出服务端一键放宽卡,放宽后重算并写回档案(BUG-572);窗口 >120 分钟先切三子段迭代到 ≤120 再进分钟(BUG-573);日级事件问一次可靠度;四个脚本化手测场景进 docs/testing | 待领取 | `codex/rectification-declared-uncertainty-20260907` |
| `TASK-rectification-declared-uncertainty-20260907.md` | `PROGRESS-rectification-declared-uncertainty-20260907.md` | 出生时间「有多确定」只在 intake 问一次:三档(医院记录 / 家人大概 ±15·30·60·120 / 时段或未知),校正窗口读档案(现在有钟点一律 ±15,声明值被忽略,BUG-571);吻合率 <60% 且代表分钟贴窗口边缘时出服务端一键放宽卡,放宽后重算并写回档案(BUG-572);窗口 >120 分钟先切三子段迭代到 ≤120 再进分钟(BUG-573);日级事件问一次可靠度;四个脚本化手测场景进 docs/testing | 待验收 | `codex/rectification-declared-uncertainty-20260907` |
| `TASK-api-not-configured-mislabel-20260904.md` | `PROGRESS-api-not-configured-mislabel-20260904.md` | 16 处路由把数据库瞬断(部署切换窗口)兜底翻译成 503「服务尚未配置」;改为仅配置错误用该文案,其余 `service_unavailable`,收敛为共享 helper | 已验收 | `5483649b`(BUG-542);2 条子进程测试留 CI Node 22 复核 |
| `TASK-rectification-ux-20260902.md` | `PROGRESS-rectification-ux-20260903.md` | 会话面空白假死与交互摩擦 | 已验收 | `d159f08e`(09-03 在新基线重做后合入,BUG-505509 |
@@ -0,0 +1,37 @@
# Staging 人肉复核 · 出生时间有多确定只问一次(2026-09-07)
给产品负责人。不要把真实案例里的年份、事件或候选分钟写进任何记录。
对应 `TASK-rectification-declared-uncertainty-20260907.md` / BUG-571~573。测之前先做第 0 条。四个虚构脚本见 `rectification-scenarios-20260907.md`
## 0. 确认测的是新版本
浏览器打开 staging 的 `/api/health`,看 `deployment.gitCommit` 前 8 位是否等于本单合入 staging 后的提交。不一致 = 先别测。本迁移必须先 `Migrate Staging Database`
## 1. intake 三档,家人一小时进校正是两小时窗(P0)
设置 → 星盘资料。出生时间选「家人记得大概时间」,钟点填一个整点,范围点「前后一小时」,保存后开生时校正。
- ✅ 预期:顶部目前范围是该钟点前后各一小时(共两小时)。没有「偏移 / 误差 / 置信度 / 概率」。校正过程不再问你有多确定。
- ❌ 失败:范围仍是 ±15;或校正开场又问不确定度。
## 2. 吻合低且贴边时出放宽卡,点 A 后设置页跟着变(P0)
用上一档资料。口述三件带年月、不同领域、和这个两小时窗明显对不上的经历(不要用真实资料原句)。等到出现放宽卡。
- ✅ 预期:卡在区分卡之前。A 是放宽到前后两小时(或下一档),没有采用。点 A 后顶部范围变宽;再打开设置,家人范围按钮停在更大的档。
- ❌ 失败:没有放宽卡、先出了分钟区分卡;或点 A 后设置页还是前后一小时。
## 3. 医院记录放宽不改档案来源(P1)
另建资料,选「有出生证或医院记录」,走完能出放宽卡的条件后点 A。
- ✅ 预期:窗口变宽,设置页来源仍是医院记录,没有变成「家人记得大概时间」。
- ❌ 失败:来源被改成 approximate。
## 4. 超过两小时先切三段(P0)
资料选「只知道大概时段」填傍晚,或家人「前后两小时」。开校正,说够带年月经历。
- ✅ 预期:先出三段钟点卡,不是分钟或采用。选一段后若仍超过两小时,再出子段卡。
- ❌ 失败:一上来分钟卡;或选傍晚后立刻按分钟比。
@@ -0,0 +1,64 @@
# 生时校正手测脚本(2026-09-07
虚构走查。不要填真实姓名、出生资料或真实经历。对应 `TASK-rectification-declared-uncertainty-20260907.md` 决策 9。
测之前先看 `rectification-declared-uncertainty-20260907.md` 第 0 条。
## 1. 范围:家人说两点到四点
资料:家人记得大概时间,钟点 15:00,范围「前后一小时」。地点任意公开城市。
开场后依次说(均可改年份,保持不同领域):
1. 2016 年 9 月去外地上学
2. 2018 年 7 月入职第一份工作
3. 2023 年夏天搬家
期望:
- 顶部范围 14:0016:00,不是 14:4515:15
- 若三件和这个窗对不上且代表分钟贴边:出现放宽卡,选项是前后两小时 / 不放宽 / 说不好
- 点 A 后范围变成 13:00–17:00,设置页停在「前后两小时」
- 不得出现:偏移、误差、置信度、概率、采用卡(在放宽之前)
## 2. 时段:只知道傍晚
资料:只知道大概时段,开始 18:00 结束 22:59。
经历同上三件,领域仍要两个以上。
期望:
- 第一张卡是三段钟点(例如 18:00—19:xx),不是「更像 20 点」、不是采用
- 选 A/B/C 后范围收到该子段;若仍大于两小时,再出一张子段卡
- 输入框上方是比较时段 / 子段,不是分钟
- 不得出现:全日 00:00–23:59 上的分钟卡
## 3. 完全未知
资料:完全不清楚。
经历三件同上。
期望:
- 先出清晨 / 上午 / 下午 / 傍晚或夜里这类五时段卡(实际露出支持最高的三段)
- 选定后若该时段超过两小时,再切子段,然后才分钟
- 家人线索如果填了,只出现在旁白,窗口仍是全日直到点选
- 不得出现:劝退「以后再做」;不得在全日窗上采用
## 4. 必须停下
资料:家人记得大概时间,钟点任意,范围「差不多准」。
经历故意说不清:
1. 「大概哪年有过事,具体不记得」
2. 「地点也说不准」
3. 连续几道选择题都点「说不好 / 先这样」
期望:
- 到不确定度停止规则时停下,旁白说明再问也分不开
- 不采用、不编唯一出生分钟
- 不得把「先这样,先看当前范围」当成已经选定时间
@@ -22,12 +22,14 @@
- ✅ 预期:出现四选卡,问哪一段更像出生时段(清晨 / 上午 / 下午 / 傍晚或夜里这类)。没有「更像 X 点」,没有采用。输入框上方是「第 1 步·比较时段」。
- ❌ 失败:仍在问经历却已经给出分钟或采用卡;或时段卡上写了代表分钟。
## 3. 选定一段后进入分钟流程P0
## 3. 选定一段后若仍超过两小时,先切子段P0
在时段卡上选 A、B 或 C 中的一段。
在时段卡上选 A、B 或 C 中的一段(例如下午 12:0017:59
- ✅ 预期:旁白说明已选定出生时段,接下来只在这一段里按分钟比较。目前范围变成该时段(不超过大约 6 小时),不再是 00:00–23:59。之后才可能出现分钟区分题
- ❌ 失败:范围仍是全日;选定后立刻采用某个分钟
- ✅ 预期:范围变成该时段,不再是 00:00–23:59。若这一段超过大约两小时,下一张仍是三段钟点卡,不是分钟或采用。收到大约两小时及以内之后,才按分钟比较
- ❌ 失败:范围仍是全日;选定下午后立刻出现分钟或采用卡
更完整的子段与放宽走查见 `docs/testing/rectification-declared-uncertainty-20260907.md`
## 4. 说不好会再收经历,不会结束校正(P1)