会议要求把「推断真实出生时间」与「用户认可的参考盘」分开证明。逐条核对 后:产品口径侧四条已落地(accepted≠confirmed 两条写入路径、确认门 fail-closed、采用不写 reported_birth_time、无把采用率当准确率的指标), 缺口全在评测本身。 三条实证:封存契约里三个打分哈希互不相同且 official_eval_trial_count 为 0,当前实现从未产出过有效官方盲测;全部离线评测的候选窗以真值为圆心而 生产以申报时间为圆心,真值掉出窗外这一失败模式从不可见;v3 封存但每例 仅 3 事件、v4 事件够却已被看过并用于调参,六题回放的高分是真值方向最优 答的上界。 任务分解:申报偏差敏感性 sweep、封存盲测有效重跑 + 契约对齐 + 防复发 测试、v5 采集协议(只出协议)、记录。硬红线:不改 12 个打分文件、不用 封存集调参、不得把 status 改 ready。家庭信息在拿到基线数字前不开工。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0199rbQDTsUbCVw84wc8BTFe
18 KiB
TASK · 生时校正验证体系补缺:申报偏差评测与封存盲测有效重跑(2026-09-20)
状态:待领取。纯离线评测与契约对齐,不改任何打分逻辑、不改产品行为、不动确认门。 起因:产品会议对「我们究竟在证明什么」提出边界要求(真实出生时间 vs 用户认可的参考盘必须分开证明)。Claude 2026-09-20 对代码、评测数据与线上口径做了逐条核对,结论见 §1。 产品口径侧的四条要求(accepted≠confirmed、不承诺精确分钟、不覆盖原始资料、不拿用户认可当验证)核对下来已经落地,本单不碰那一层;缺口全部在评测本身。
0. 基线与交付
- 基线:
origin/staging=fcad0637(2026-09-20 实测核对)。 - worktree
.worktrees/rectification-validation-20260920,分支codex/rectification-validation-20260920。 - 改动落点:
scripts/research/**、tests/**、references/rectification_sealed_holdout.v1.json、docs/**。前三项在deploy/gated-paths.txt内,推 staging 会触发门禁并重新发布镜像。 - 前置依赖(不在本单,不阻塞开工):
TASK-owner-case-purge-20260919的三个提交fe9489e6/36c443bf/497798ac已验收通过但尚未合入staging(已核:三者均不是origin/staging的祖先)。那条分支带着仓库级隐私守卫tests/test_repo_privacy_markers.py。本单不依赖它即可执行,但在它合入之前,任何「拿自己的案例试一下」的结果都不具备独立性,不得当作验证证据。放行由产品负责人决定。
1. 事故实证
基线 fcad0637,行号按符号定位。
1.1 三个打分实现哈希互不相同,任何成绩都无法归属(BUG-978)
references/real_case_calibration/minute_rectification_holdout_v3.json 的 frozen_scoring 对 12 个打分文件做 SHA-256 封存。三处哈希实测:
| 来源 | 哈希前缀 |
|---|---|
封存值 frozen_scoring.implementation_sha256 |
f41c298dd6cdcebe |
契约文件记录的当前树 current_tree_scorer.implementation_sha256 |
99730c84c6434f52 |
基线树实测(implementation_sha256(frozen_scoring.files)) |
b15d9ea15227cd58 |
三者两两不等——契约文件 references/rectification_sealed_holdout.v1.json 里的 current_tree_scorer 块本身已经过期。同一文件另有两处与现实不符:source_audit_status 仍写 invalidated_after_replay,而 v3 数据集在 2026-09-14 已改为 corrected_known_date_errors;evaluated_on 仍停在 2026-07-21。
后果记在该文件自己的字段里:official_eval_implementation_hash_matches: false、official_eval_trial_count: 0——当前打分实现从未在封存集上产出过一次有效的官方盲测成绩。
- 唯一跑过的那次(2026-07-21):top-1
0.15、top-30.25、MAE6.95分钟、确认覆盖率0.0,报告状态invalidated_after_source_audit(资料审计发现两处错年,已在docs/research/holdout_v4_build_2026_09_14.md更正)。 - 现在唯一能看到的较新数字(top-1
0.45/ top-30.50/ MAE5.8)来自current_tree_unfrozen_diagnostic块,该块自带is_blind_evaluation: false、results_already_seen: true、must_not_claim_as_release_metrics: true、must_not_use_for_tuning: true。
预注册发布门(minute_rectification_holdout_v3.json 的 release_metrics):top-1 ≥ 0.60、top-3 ≥ 0.85、MAE ≤ 2.0 分钟、误确认率 ≤ 0.05、信息不足正确拒绝率 ≥ 0.90。
计算成本不是障碍:基线树实测,20 例全量打分 ±10 档 1.6 s、±60 档 8.6 s。
1.2 所有离线评测的候选窗以真值为圆心,生产以申报时间为圆心(BUG-979)
scripts/minute_rectification_blind_eval.py的_candidate_moments()以case["birth"]["time"](Astro-Databank AA 记录的真实分钟)为中心,向两侧各取candidate_radius_minutes分钟。scripts/minute_rectification_fact_blind_eval_v4.py直接 import 复用同一函数。scripts/research/probe_supply_after_six.py的request_from_case()同样以真值为心,局部变量名就叫true_time,start_time/end_time由它 ± 半径得到;scripts/research/minute_resolution_sweep.py的scoring_request_for()调的就是它。
生产不是这样:frontend/src/lib/rectification-agentic/v9/search-window.ts 的 ENGINE_SEARCH_RADIUS_MINUTES = 15,窗以用户申报时间为中心(frontend/src/lib/rectification-agentic/birth-time-provenance.ts 文件头注释明写 "Scoring still uses reportedBirthTime as the search-window centre"),可放宽到 30 / 60 / 120。
后果:离线评测里真值永远在窗正中心、永远在候选集里。「申报偏差大于半径 → 真值根本不在候选里」这一失败模式在现有全部评测中不可见,其发生率从未被测量。全仓检索偏移窗相关键在评测脚本内 0 命中。
1.3 现无「口径干净且事件数贴近真实会话」的封存集(BUG-980)
| 数据集 | 是否封存 | 每例事件 | 每例领域 | 可否当发布门 |
|---|---|---|---|---|
minute_rectification_holdout_v3.json |
是(truth_hidden_from_ranker: true) |
3 | 2–3 | 可,但事件数远低于真实会话 |
minute_rectification_holdout_v4.json |
否(truth_hidden_from_ranker: false) |
≥7 | ≥4 | 不可,结果已被看过且用于 R1–R5 调参 |
docs/research/holdout_v4_build_2026_09_14.md 自己写明 v4「是开放评价集,用来看尺度,不是第二份封存盲测」;真实会话每例 5–18 件事。
另:docs/research/minute_resolution_2026_09_14.md 记载六题回放用「真值方向最优答」。因此 0.80 / 0.55 / 0.35(±10 / ±30 / ±60)是上帝视角上界,不是产品表现;其中 ±30 与 ±60 两档仍低于 0.60 的发布门,而生产默认半径 ±15 正落在两档之间。
1.4 产品口径侧已经达标(不构成 BUG,记此以免重复排查)
accepted与confirmed是两个状态、两条写入路径(frontend/supabase/migrations/20260813010000_agentic_rectification_v9_agent_api.sql的accept_agentic_rectification_candidate_for_case与 confirm 函数),selection_kind区分user_accepted/engine_confirmed。- 采用候选写
active_birth_time/birth_time,不写reported_birth_time——原始申报保留,且仍是搜索窗圆心。 - 确认门 fail-closed:
scripts/rectification/decision_policy.py的apply_confirmation_decision()要求四闸同时通过,其一是scripts/rectification/sealed_holdout.py的holdout_passed()。封存契约status: not_ready且confirmation_coverage_rate: 0.0,因此confirmation_allowed今天在任何情况下都不可能为真。 - 交付卡自带标签
candidate_range_not_birth_time_truth与「采用只会把代表性时间写入当前排盘,不是已确认的唯一出生分钟」。 - 全仓无「把采用率 / 满意度当准确率」的指标;无任何按特定生日走捷径的运行时分支(上游
PL9_BAV_CELL_OVERRIDES、_known_case_regression_hint在本仓 0 命中)。
关联记录:BUG-098(公开候选门禁未明确独立 holdout 边界)。其防复发条写明「任何 holdout 完成声明必须先有稳定分区持久化与校准验收证据」。本单三条与之同源:那条措施仍然有效,但从未被执行到底,且没有任何测试守着它。
2. 根因
- 封存契约文件
references/rectification_sealed_holdout.v1.json是人工维护的(scripts/rectification/sealed_holdout.py文件头注释:「A human updates ... after a valid passing evaluation」),没有任何测试断言它与数据集、与当前树哈希一致 —— 打分代码每改一次,它就更旧一次,而没有人会被提醒。 - 评测脚本是从「给定真实出生时间,能否把它排到第一」出发写的;产品解决的是「给定用户申报时间,真实时间在哪」。两者差一个偏移量,建评测时没有人把这个差异写成一项指标。
- v4 建立时就定性为开放评价集,此后没有人补上对应的封存孪生集,于是「事件数真实」与「口径干净」只能二选一。
3. 决策记录
- 2026-09-20 产品授权出本单,覆盖核对结论中的两条行动:补申报偏差评测、重跑有效封存盲测;外加一条只出协议的封存集规划。
- 本单只产出数字与结论,不改打分逻辑、不调参、不改产品行为。 即便重跑成绩过了发布门,执行方不得把
status改成ready、不得让确认门打开 —— 过门是产品决策,另立单。 - 家庭信息(父母职业、父母出生年份、兄弟姐妹)在本单产出基线数字之前不开工。 核对已确认:现有七个领域全部是带日期事件,打分输入只认
{id, domain, date, precision}四个字段(见scripts/minute_rectification_blind_eval.py的_request()),静态家庭属性没有输入口 —— 加它不是多问一道题,是新开一条打分通道。若产品另有决定,请在领取前更新本段。 - 上游清除分支的放行是产品动作,不在本单(§0)。
4. 硬红线
- 不得修改
frozen_scoring.files里那 12 个打分文件中的任何一个,也不得修改scripts/rectification/decision_policy.py、scripts/rectification/sealed_holdout.py的判定逻辑。本单是量,不是改。 - 不得把封存集 v3 用于任何调参;不得因成绩难看而更换数据集、更换半径或剔除案例。剔除只允许发生在
minute_rectification_holdout_validator.validate()判定为invalid_cases的情况下,且必须列出被剔案例与理由。 - 不得让
references/rectification_sealed_holdout.v1.json的status变成ready,不得抬高confirmation_coverage_rate。 - 新增的偏差评测必须与现有评测并存;不得改写
_candidate_moments()与request_from_case()的现有行为(改了会让两轮历史结论不可比)。 - 任何数字进文档必须带口径:ayanamsa / node mode / 半径 / 步长 / 数据集 / 打分实现哈希前 16 位。不得把非盲诊断写成发布指标。
- 不得写入真实用户资料;案例只用已登记的公开 AA 名人;不得在任何文档里写出生分钟或坐标。
- 不顺手升级依赖、不修不在本单内的 warning。
frontend/src/app/page.tsx与scripts/jyotish_api_server.py均不得增长(本单本不该碰前端与主 API,碰了说明跑偏)。
5. 任务分解
T1 · 申报偏差评测(BUG-979)
新增 scripts/research/reported_offset_sweep.py:在现有取候选方式之外,增加一个「模拟申报时间」的圆心。
- 对每个案例,把窗心从真值平移
offset ∈ {0, ±3, ±5, ±8, ±10, ±15, ±20, ±30}分钟,半径取产品档位{15, 30, 60}。 - 每个(案例, 偏移, 半径)组合输出四项:真值是否落在窗内、真值在候选中的排名、头名与真值的分钟距离、交付区间是否覆盖真值。
- 汇总成一张表:横轴偏移、纵轴半径,格子里是「真值在窗内的比例 / 头名命中 / 区间覆盖」。
- 真值仍然排完名之后才揭晓;并列沿用现有哈希打破方式(
_opaque_winner),不得按接近真值挑选。
验收标准:
python3 scripts/research/reported_offset_sweep.py --json在基线树跑通,输出含上述四项与完整口径块(含打分实现哈希前 16 位)。- 新增
tests/test_reported_offset_research.py,至少断言:(a) 偏移为 0 时窗心等于真值且「真值在窗内」为1.0;(b) 偏移绝对值大于半径时「真值在窗内」为0;(c) 口径块字段齐全;(d) 并列打破不依赖真值。 - 结论写进
docs/research/reported_offset_2026_09_20.md,必须明确回答一句:在半径 ±15 下,申报偏差多大时真值开始掉出窗外;以及现有放宽路径(30 / 60 / 120)能把这个比例救回多少。 - 本任务不需要知道真实用户的申报偏差分布;它给的是「如果偏差是 X,会怎样」的敏感性曲线。真实分布怎么拿,进 T4 的遗留项。
T2 · 封存盲测有效重跑与契约对齐(BUG-978)
- 冻结当前打分实现:把基线树实测哈希(
b15d9ea15227cd58…,执行方自己重算确认)连同 12 个文件清单写进一次新的评测记录;不改 v3 的frozen_scoring原值(那是历史封存值,要留着做对照)。 - 在 v3 封存集上跑一次完整盲测,产出五项预注册指标:top-1、top-3、MAE、误确认率、信息不足正确拒绝率。
- 把
references/rectification_sealed_holdout.v1.json更新到与现实一致:source_audit_status取 v3 数据集当前值;current_tree_scorer刷新到本次实测哈希与成绩;新增一个块记录本次重跑,注明它是首次口径干净的官方盲测,以及「v3 每例仅 3 件事,是下限口径,不代表真实会话」。status保持not_ready(§4.3)。 - 新增
tests/test_sealed_holdout_contract_freshness.py:断言契约文件的current_tree_scorer.implementation_sha256等于当前树实测值、source_audit_status等于 v3 数据集里的值。这条测试就是防复发 —— 它一红就说明打分代码改了而契约没跟上。
验收标准:
.venv/bin/python -m pytest tests/test_sealed_holdout_contract_freshness.py tests/test_minute_rectification_holdout_validator.py tests/test_minute_rectification_fact_blind_eval_v4.py -q通过。- 契约文件三处过期字段全部对齐,且
status仍为not_ready、confirmation_coverage_rate仍为0.0。 - 五项指标与发布门逐条对照写进
docs/research/sealed_holdout_rerun_2026_09_20.md,每项标「通过 / 未通过 / 不适用」。 - 确认门行为零变化:跑
frontend/tests/rectification-confirmation-gate.test.ts与相关 pytest,结果与基线逐条相同。 - 快速门
python3 scripts/run_quality_gate.py --profile quick与基线逐条比对。
T3 · 封存集 v5 采集协议(BUG-980,只出协议、不采集)
写 docs/research/sealed_holdout_v5_protocol_2026_09_20.md:
- v5 目标:封存 + 每例 ≥7 件带年月事件 + ≥4 个领域,即 v4 的口径、v3 的纪律。
- 人物名单必须与 v3 / v4 的 20 例不重叠(那 20 例成绩已被看过)。列出 ≥25 个候选公开 AA 人物作为备选池,只写人物与资料来源类型,不写出生分钟或坐标。
- 标注协议沿用
holdout_v4_build_2026_09_14.md的四条(AA 记录、事件来自公开页且independent_of_birth_source=true、工作日期须核实、三档半径写在数据集上),并补一条:v5 建成后,在产出第一份成绩之前不得被任何人读取事件内容做调参。 - 工作量估算:按每例 7 件事、每件需一处可核实公开来源计,给出人工小时数区间。
- 明确写出:「在 v5 就绪之前,任何对外的分钟级准确率说法都没有依据」,并指明 T2 的数字是下限口径、T1 的数字是敏感性而非准确率。
验收标准:文档含上述全部要素;候选池 ≥25 人且与现有 20 例无交集;不含任何出生分钟、坐标或真实用户资料。
T4 · 记录
docs/BUG_HISTORY.md新增BUG-978/BUG-979/BUG-980,状态按实际填(T1 / T2 完成的可写resolved并附证据;BUG-980只出协议,保持investigating直到 v5 就绪)。三条都必须关联BUG-098,并说明那条防复发措施为何没拦住。BLOCKED.md记一条:真实用户的申报偏差分布目前拿不到(需要有独立出生记录的真实用户样本,无受控账号),T1 只能给敏感性曲线。docs/research/ACTIVE_FRONTS.md加一节索引本单三份文档,并指回rectification_minute_resolution_closure_2026_09_14.md。docs/tasks/README.md状态板加一行;实现合入 staging 的同一次推送里改状态。CHANGELOG.md不写(无用户可感知行为变化)。
6. 让步顺序
时间不够时按此顺序砍,砍掉的写进进度记录:
- 最先保 T2 —— 它最便宜(±10 档全量 1.6 s),且产出的是第一份能对着发布门看的数字。
- 其次保 T1 的 ±15 一档与
{0, ±5, ±10, ±15, ±20}五个偏移;其余半径与偏移可后补。 - T3 候选池可从 25 人降到 15 人,但协议正文与那句「在 v5 就绪之前没有依据」不得删。
- 不得为赶工跳过 T2.4 那条防复发测试 —— 没有它,这三条明天会重新长出来(
BUG-098就是这么回来的)。
7. 开工前置命令
git fetch origin --prune
git worktree add -b codex/rectification-validation-20260920 \
.worktrees/rectification-validation-20260920 origin/staging
cd .worktrees/rectification-validation-20260920
git log --oneline -1 # 必须是 fcad0637 或其后代
python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45
本单属 AGENTS.md §9 的引擎 / 评测类任务,pre_work_check.py 必跑,并先读 docs/research/pre_work_error_ledger.md。
另必读 docs/research/rectification_minute_resolution_closure_2026_09_14.md —— 它已经证伪了两条假设(加权重能拉开候选、簇合并把候选并没了),本单不得重走;它的第 6 节三条提醒(先确认指标口径、不要从 docstring 推断行为、真值覆盖率优先于区间宽度)对本单全部适用。
环境备忘:核对机上无 .venv,系统 python3(3.13)可直接 import swisseph;若执行方环境有 .venv,按 AGENTS.md §10 用 .venv/bin/python。
8. BUG 编号起点
docs/BUG_HISTORY.md 当前最大号 975;TASK-consult-smalltalk-fastpath-20260920.md 已占用 976 / 977。本单从 BUG-978 起,连续三条。开工时重新核对最大号,若已被占用则顺延并在进度记录里说明。