Files
Jyotisha/docs/tasks/TASK-rectification-declared-uncertainty-20260907.md
T

18 KiB
Raw Blame History

TASK · 出生时间"有多确定"只问一次:intake 三档、校正读档案里的偏移、吻合率低时一键放宽并写回(2026-09-07)

  • 基线:origin/staging @ 5c644f92(代码头 517df002,含 BUG-565570
  • 分支:codex/rectification-declared-uncertainty-20260907
  • 执行方:coding agent;验收:Claude
  • 涉及文件:frontend/src/lib/birth-time-intake-model.tsfrontend/src/components/birth-time-intake.tsxfrontend/src/lib/account-profile-patch.tsfrontend/src/lib/rectification-agentic/v9/case-service.tsdecision-from-dossier.tscore/rectification-decision.tsmethod-followup.tsanswer-choice.tsblock-scan-answer.ts(重算函数泛化)、tool-service.tsfrontend/supabase/migrations/(新 RPC)、frontend/src/lib/rectification-agentic/user-copy.ts
  • 涉及引擎:scripts/rectification/api_service.py::block_scan(泛化为任意子段)、scripts/rectification/contracts.py(可选 blocks 字段)
  • 不改:探针契约 contracts/probe-question-v1.json、采用/确认门、page.tsxjyotish_api_server.py
  • BUG 编号:BUG-571(声明不确定度被忽略)、BUG-572(吻合率低不提议放宽)、BUG-573(选段后直接进 4~6 小时分钟网格)(开工时 grep -o "^## BUG-5[0-9][0-9]" docs/BUG_HISTORY.md | tail -1 复核)
  • 串行:在 TASK-rectification-capability-fix-20260907.md(已合入)之后;与其他校正单无并行

0. 为什么做这件事

产品负责人 2026-09-07 拍板:出生时间"有多确定"在初始化出生资料时问一次、存进档案,校正每次直接读,不再问;校正过程中只在证据说窗口选错时,由系统出一键卡提议放宽,并把新偏移写回档案。对照上游 yinduzhanxing:它保留用户给的区间,且方法 1 规定吻合率 <60% 要调 ±1~2 小时重验;我们现在两件都没做。

1. 现状实证

# 事实 位置
1 intake 只露出两个来源:"我知道准确出生时间"(family_exact,±0)和"我不确定准确时间"(period_only 自选起止 / unknown)。模型与校验里早有 approximate(±15/30/60)和 hospital_record(固定 ±2),但单选框 birthTimeSourceOptions 只有两项,用户选不到 birth-time-intake-model.ts L9194、L96140account-profile-patch.ts L126145
2 校正开 Case:只要档案有钟点,一律 reportedTime ± 15FRESH_CASE_SEARCH_RADIUS_MINUTES),注释写明"引擎执行边界,不是用户声明的不确定度";档案里的 uncertainty_before/after 被读进 baseline 但不参与窗口 case-service.ts L131146
3 引擎每次重算都产出 event_fit_ratematched / total / percent / band:≥80 high、6080 medium、<60 low),TS 已解析,但只用在采用后的八法报告里;没有"吻合低 → 窗口可能错 → 放宽"的动作 refinement_packet.py::event_fit_raterefinement-packet.ts L761skill-verification-report.ts
4 设置弹窗的星盘资料复用 BirthTimeIntakeFields,所以 intake 改了设置页自动同步 profile-fields.tsx L3
5 改窗口的 RPC 只有 advance_agentic_rectification_case_from_block_scan,要求 stage=block_scan;分钟阶段没有放宽窗口的入口。candidateRangeFingerprint 含起止,窗口一变快照自动失配触发重算 20260906030000_rectification_block_scan_stage.sql L132182tool-service.ts L619
6 既有测试把填报 05:00 → 04:4505:15 写死了三处 rectification-v9-case-service.test.ts L83、L136、L428

2. 决策记录(产品负责人 2026-09-07 拍板)

  1. 只在 intake 问一次,用"你对这个时间有多确定"来问,不用"偏移 / 误差"字眼。三档:
    • 「有出生证或医院记录,精确到分钟」→ hospital_record(校验仍固定 ±2 作无感检查)
    • 「家人记得大概时间」→ approximate + 一排按钮「差不多准 / 前后半小时 / 前后一小时 / 前后两小时」= ±15 / 30 / 60 / 120(校验从 {15,30,60} 扩到 {15,30,60,120}
    • 「只知道大概时段,或完全不知道」→ 现有 period_only / unknown 分支不动 family_exact 不再出现在新用户选项里,存量档案仍合法,语义等同"差不多准"(±15)。
  2. 校正窗口读档案。 有钟点时:hospital_record / family_exact → ±15(引擎边界,记录常按 5 或 15 分钟取整);approximate → ±max(15, 声明值),最大 ±120。窗口宽度 > 120 分钟一律先进子段扫描(决策 7),≤ 120 分钟才进分钟网格。 period_only 四~六小时的时段窗口同样先切子段;unknown 仍先走五时段全日扫描,赢的时段再按决策 7 继续切。
  3. 校正中唯一碰这个值的时机是证据说窗口错了:训练门已开、event_fit_rate.band = lowtotal ≥ 3、代表分钟落在窗口边缘 3 分钟内、stage=minute、未采用、有钟点、本证据指纹下没拒过。满足即出服务端一键卡(不是模型问句):"按你说的经历,出生时间可能比家人记的偏得更多。放宽后再比一次?" A「放宽到前后 {下一档}」 B「放宽到前后 {再下一档}」 C「不放宽,按现在的范围继续」 D「说不好」。A/B 改窗口、重算、并把新偏移写回档案(来源改为 approximate);C/D 记 declined_at_fingerprint,同一批证据不再问。已是 ±120 的不出卡。
  4. 放宽卡优先级高于区分卡:窗口都可能错的时候,先别在错窗口里出题。
  5. 放宽不写证据账本、不进推断层;已答探针按 semantic_key 在新候选集上重放(buildInferenceState 现有行为),测试锁定。
  6. 不动采用门、确认门、MIN_SEPARATION_LEAD_relative_support;不动 adopted_credible_range(那是采用后的产物)。
  7. 窗口 > 120 分钟先切三段,迭代到 ≤ 120 分钟再进分钟。block_scan 泛化:请求可带 blocks: [{label,start_time,end_time}],不带则用五个声明时段;子段 = 当前窗口等分三段(宽度不足 3 的取整到分钟),步长按窗口宽自适应(>360 分钟 10、>180 分钟 5、其余 2),支持度仍是"段内均值减窗口最低分"归一。选 A/B/C 把窗口改成该子段并再评估:仍 > 120 → 再出一张子段卡;≤ 120 → stage=minute 重算。D「说不好」= 再收一件经历后重比。每个 Case 最多连问 3 张子段卡,超过即按当前窗口进分钟阶段并在旁白写明"时段分不开,直接按分钟比"。
  8. 事件可靠度只问一次、只问日级事件。 用户说到精确到日的经历时,随后的采集确认里多一句服务端口述:"这个日子是查过记录,还是凭记忆?"——答"查过"写 date_reliability=high"凭记忆"写 medium,不答留空;月级、年级不问。这只影响 date_quality 门与精度权重,不改任何其他门。
  9. 脚本化手测场景进 docs/testing/:范围(家人说两点到四点)、时段(只知道傍晚)、完全未知、以及"必须停下"的反例(日期地点经历都不确定)四个脚本,每个写期望观察点,供产品负责人在 staging 照着走。

3. 硬红线

  • approximate 校验扩到 120 只在 account-profile-patch.tsisBirthTimeDraftReady 两处同改;hospital_record 固定 ±2 不改。
  • 放宽只能扩大且必须包含旧窗口;新窗口 ≤ 241 分钟;只在 stage=minute 允许;RPC 内校验,不信客户端。
  • 放宽后 candidate_range 改写是合法的(这是搜索窗口,不是采用产物);但 adopted_credible_range 不得被动。
  • 既有 04:4505:15 断言只对 family_exact / hospital_record 保留;approximate 用例另写,三栏说明。
  • 文案对照 frontend/docs/VOICE.md;不得出现"偏移 / 误差 / 置信度 / 概率";一键卡四选项契约与其他卡一致(TS 内部 choice_kind: "widen_window",不进 Python 契约,比照 block_choice)。
  • 迁移需 Docker test:db;无 Docker 写 BLOCKED.md
  • 测试总数 ≥ 1521(口径同 BUG-569 验收);tsc 0 错;lint 0 error。

4. 任务分解

D1 intake 三档

  • birthTimeSourceOptions 改三项(决策 1),approximate 分支渲染四个按钮写 uncertaintyBefore/Afterhospital_record 保留"无感检查前后 2 分钟"说明;family_exact 从选项移除、读档时映射为"差不多准"显示。
  • 校验:isBirthTimeDraftReadyaccount-profile-patch.ts 的 approximate 集合 → {15,30,60,120};错误文案同步。
  • 验收:birth-time-intake.test.ts 新增三档渲染与 120 校验;account-profile-patch 测试 120 合法、90 非法;设置页(profile-fields)源扫描仍复用 BirthTimeIntakeFields

D2 校正窗口读档案(BUG-571:声明的不确定度被忽略)

  • case-service.ts::deriveRectificationOpenPlan 增加 uncertaintyBefore/After 入参:approximatereportedTime max(15,before) reportedTime + max(15,after)(各自独立,允许不对称),上限 120;其他钟点来源 ±15。
  • 验收:rectification-v9-case-service.test.ts 新增 approximate ±60 → 04:0006:00、±120 → 03:0007:00;既有三处 04:4505:15 保留并注明来源为 family_exact(三栏)。

D3 放宽窗口一键卡(BUG-572:吻合率低不提议放宽)

  • 迁移:widen_agentic_rectification_case_window(p_user_id, p_case_id, p_start_time, p_end_time):校验 stage='minute'、新窗口包含旧窗口、宽度 ≤ 241、status ∉ terminal、未采用;写 candidate_range,返回新范围;同一迁移给 agentic_rectification_caseswiden_declined_at_fingerprint text null
  • 决策层:decideRectification 新入参 windowWidenSuggested(由 decision-from-dossier 按决策 3 条件计算,边缘判定用 separation.representativeTimecase.candidateRange),为真时返回 nextAction="ask_window_widen",排在 ask_candidate_discriminator 之前;sessionOutcome="widen_window"can_adopt=false
  • 计划层:method-followup.ts 比照 blockChoiceFollowup 生成 widen_window 卡,选项文案服务端生成,档位从当前半径推:15 → A ±30 / B ±6030 → A ±60 / B ±12060 → A ±120 / B「先补一件带月份的经历再说」(answer_class no,四选项契约要求四项且不重复)。
  • 应用:answer-choice.ts 比照 mutateCaseForBlockChoice:A/B → RPC 改窗口 → 按 D5 的 stageForWindow(width) 决定去向(新窗口 ≤ 120 分钟:泛化 rescoreMinuteAfterBlockAdvancerescoreMinuteAfterWindowChange 重算;> 120 分钟:切 block_scan 出三子段卡) → 调用现有账户资料 patch 路径把 uncertainty_before/after 写为新半径、birth_time_sourceapproximatehospital_record 例外:档案不改,只改窗口,因为其校验固定 ±2);C/D → 写 widen_declined_at_fingerprint
  • 验收:新文件 rectification-window-widen-20260907.test.ts——(1) fit low + 代表分钟在边缘 → nextAction=ask_window_widen,卡四项齐;(2) fit low 但代表分钟在中间 → 不出卡;(3) 答 A → RPC 收到包含旧窗口的新范围、重算被触发、profile patch 收到新半径、已答探针数不变;(4) 答 C → 记指纹,同指纹下再评估不出卡,新证据到来后可再出;(5) 已 ±120 不出卡;(6) hospital_record 答 A 不改档案来源。rectification-v9-database.test.ts 加 RPC 用例(Docker)。

D5 子段扫描(决策 7;BUG-573:选定时段后直接进 4~6 小时分钟网格)

  • 引擎:contracts.py 接收可选 blocks(1~5 段,每段起止 HH:MM,必须落在请求窗口内且不重叠);api_service.block_scan 在有 blocks 时按其聚合,否则沿用五时段;步长按窗口宽自适应(决策 7),minute_step 显式传入时以传入为准。jyotish_api_server.py 不改(同一端点)。
  • TSblock-scan.tsBlockScanBlock.period 放宽为 label(五时段标签或 sub_1/sub_2/sub_3),选项文案用起止时间("04:00—05:59");deriveRectificationOpenPlan 与放宽卡(D3)落地新窗口后统一走 stageForWindow(width)> 120 → block_scan + 三等分 blocks;≤ 120 → minuteadvance_agentic_rectification_case_from_block_scan RPC 放开"必须是 00:0023:59"的限制,改为"新窗口必须是当前窗口的子区间";block_scan 列记 rounds 计数,≥ 3 直接切 minute
  • 验收:test_rectification_v5_services.py——三段 blocks 支持度和 100、等分网格三段各 33.3/33.3/33.4、越界或重叠段报错;TS rectification-block-scan-*.test.ts——±120 approximate 开 Case 为 block_scan 三子段;period_only 傍晚(18:0022:59)先出三子段卡;选 A 后窗口 ≤ 120 切 minute;连问 3 张后强制 minute 并有旁白;unknown 五时段 → 赢段再切三段。

D6 事件可靠度一问 + 脚本化手测场景(决策 8、9)

  • method-followup.ts / collect-prompt.ts:账本新增 datePrecision=daydate_reliability 为空的事件时,下一轮口述题固定为服务端句"刚才那个日子是查过记录,还是凭记忆?",答案由意图分类映射 high / medium,写回该条证据(用现有证据修订链,不新造 RPC);每条事件只问一次,用户不答则跳过。
  • docs/testing/rectification-scenarios-20260907.md:四个脚本(范围 / 时段 / 完全未知 / 必须停下),每个含开场输入、3~5 条虚构经历、期望看到的卡与旁白、不得出现的内容(如全日窗口上出现分钟或采用卡)。
  • 验收:rectification-collect-*.test.ts 新增日级事件后下一问是可靠度句、月级不问、答"凭记忆"写 mediumdocs/testing 文件存在且四场景齐。

D4 记录

  • docs/BUG_HISTORY.md BUG-571、BUG-572、BUG-573CHANGELOG.mdPROGRESS-rectification-declared-uncertainty-20260907.mddocs/testing/rectification-declared-uncertainty-20260907.md(真实环境:intake 选"家人记得大概时间 · 前后一小时"→ 进校正后顶部范围是 2 小时;说 3 件与该窗口明显不合的经历 → 出放宽卡 → 点 A 后范围变宽且设置页里的选项跟着变);frontend/DESIGN.mdfrontend/docs/VOICE.md

5. 让步顺序

D1 + D2 + D5 是一组(窗口读档案与 >120 分钟切子段互相依赖),不可拆,先做;D3 其次(若做不完,先落 RPC + 决策层 + 卡,写回档案可后置并写明);D6 最后,可拆到同分支第三次提交;D4 不可省。

6. 开工前置命令

git fetch origin --prune
git worktree add -b codex/rectification-declared-uncertainty-20260907 .worktrees/rectification-declared-uncertainty-20260907 origin/staging
cd .worktrees/rectification-declared-uncertainty-20260907
ln -s /workspace/Jyotisha/frontend/node_modules frontend/node_modules
cd frontend && ls tests/rectification-*.test.ts tests/consultation-*.test.ts tests/report-*.test.ts tests/personal-report-*.test.ts tests/birth-time-*.test.ts tests/account-profile*.test.ts tests/agent-voice-copy-contract.test.ts 2>/dev/null | grep -v database | xargs npx tsx --test 2>&1 | grep -E "^# (tests|pass|fail)"
grep -o "^## BUG-5[0-9][0-9]" ../docs/BUG_HISTORY.md | tail -1

验收(Claude2026-09-07origin/staging @ 7cf7705a,实现 8e31680b

结果
tsc / lint 0 错 / 0 error83 warning,既有类型)
前端 rectification + consultation + report + personal-report + birth-time + account-api + voice(非 DB 1899 / 0
Python v5_services / event_probes / engine_convergence / growth contract / flexible engine / input_contract 全绿
minute_step=1 字节级不变(虚构 7 件事新旧引擎复跑) candidate_scores / result_id / spec_hash 全等
子段扫描(18:00–22:59 三等分,3 件事) 0.8 s,步长 5,三段各 20 个候选,支持 48.7 / 41.4 / 9.9,和 100
Docker test:db 执行方 37/0(含 widen 扩大 / 缩小拒绝 / 权限;08:0011:59 选定后仍 block_scan08:0009:00 进 minute);本机无 Docker,取其数字
结论
D1 intake 三档 通过。三档文案"你对这个时间有多确定"approximate 四个范围按钮,family_exact 读档映射"差不多准",校验集合 {15,30,60,120} 两处同改
D2 窗口读档案(BUG-571 通过。deriveDeclaredSearchWindow:医院/存量准确 → ±15;approximate → 前后各自 clamp(15..120);既有 04:4505:15 断言只保留给 family_exact,三栏齐
D5 子段扫描(BUG-573 通过。blocks 进请求契约(1~5 段、须落窗内、除共享端点不重叠);步长随窗宽 10/5/2;advance_* 改为"新窗须是当前窗子区间"span>120 且 rounds<3 留 block_scan;最多 3 轮后强制 minute 并有旁白;全零均分改 100/n(执行方偏差合理:写死 20 在三段只到 60)
D3 放宽卡(BUG-572 通过。触发条件六项齐;RPC 校验含旧窗、inclusive ≤241、stage=minute、未采用;放宽后按 stageForClockWindow 决定去向;档案写回 approximate + 新半径,hospital_record 只改窗口;C/D 记指纹
D6 可靠度一问 + 脚本 通过。只对本回合刚写入的日级事件问一次,答案正则映射 high/medium,薄 RPC 原地写(执行方偏差合理:修订链会改指纹误伤训练门);docs/testing/rectification-scenarios-20260907.md 四场景齐
D4 记录 通过;四处书面偏差都在进度记录里写明
P3 classifyDateReliabilityUtterance 用「记得」判 medium,用户在该焦点下改说新经历("我记得 2018 年入职")会被顺带记成凭记忆;影响只到 date_quality 权重,可接受但建议把"记得"从正则里去掉,只留"凭记忆 / 印象 / 大概"
P3 patchV9BirthUncertainty 直接 from("profiles").update,绕开 account-profile-patch 的校验层,档案由此多了第二条写路径;值是常量所以当前无害,建议改走同一 patch 函数
P2(沿用) test_block_scan_seven_events_finishes_within_fifteen_seconds 壁钟断言仍在,门禁可能间歇红

部署:staging 当前 72dd5e9d(迁移到 20260907010000),本轮迁移 20260907020000 未应用,部署前先 Migrate Staging Database。真实环境走查按 docs/testing/rectification-scenarios-20260907.md 四个脚本。