Compare commits

...
Author SHA1 Message Date
ClaudeandClaude Opus 5.5 c032823f9e research(rectification): reseal round7_integration_2026_11_09 on staging b453505d (BUG-1809)
Independent Staging Quality Gate / validate-frontend (push) Successful in 5m18s
Independent Staging Quality Gate / validate-python (push) Successful in 6m37s
Independent Staging Quality Gate / publish (push) Successful in 2m27s
Product owner approved 2026-11-09 ("agree new result"). Same protocol as
BUG-1720 / BUG-1759: c1a69445^ replay tree + float_split + round5 overlays +
b453505d scripts/references + a small replay overlay (new tag paths, retired
scorer zero-row adapter, RuntimeError guards). Product default strength scope
seed_only_v0, comparability policy common_evidence_coverage_v1.

- sealed 20: top-1 0.35 -> 0.30, top-3 0.45 -> 0.45, mean error 6.80 -> 6.75
- offset 900: top-1 0.0156 -> 0.0100, coverage 0.900 -> 0.883 (164/900 vs
  round5 tag; 57/900 over 4 cases vs pre-integration staging)
- live v5 231: best = truth 8 -> 8; 124 changes vs round5 tag, 115 vs
  pre-integration staging
- leaders: sealed 40/40 comparable; offset 863 comparable / 35 provisional /
  2 no single leader; live 226 / 5

Trial-for-trial identical to the integration agent's seed_only runs.
Sensitivity only, not accuracy (event scoring near chance, BUG-1644).
Older tags unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyn7Hj3LCW3BUs5bZ8
2026-11-09 07:28:04 +08:00
9 changed files with 63226 additions and 0 deletions
+19
View File
@@ -24397,3 +24397,22 @@
- 判断:她的生成器修复(§12)在本仓没有可移植对象;快照不重生成、不改。若以后移植她的生成器,需要一起带她的 4 个新用例。
- 关联记录:无
- 修复版本:无
## BUG-1809 | 第七轮整合上 staging 后,生时校正三组测试集仍封存在 round5 标签:重封 round7_integration_2026_11_09
- 状态:fixed-pending-verify(本地分支 `codex/rect-reseal-r7-1109`,基于 staging b453505d,未推送;产品负责人 2026-11-09 批准「agree new result」)
- 首次发现 / 最近更新:2026-11-09(第七轮整合 b453505d 合入 staging 后,封存记录仍是 `round5_pvr_narayana_2026_11_08`)/ 2026-11-09(产品负责人批准,重封)
- 影响面:只加研究记录与测试,产品代码不改。新文件 `docs/research/sealed_holdout_rerun_round7_integration_2026_11_09.{freeze.json,json}`、`docs/research/reported_offset_round7_integration_2026_11_09.{freeze.json,json}`、`docs/research/live_astrologer_v5_round7_integration_2026_11_09.json`(只存相对真值的分钟数,不含日期)、`docs/research/round7_integration_2026_11_09_comparability.json`、`docs/research/round7_integration_2026_11_09_replay_overlay.patch`;`tests/test_rectification_reseal_round7_bug1809.py`。
- 现象:b453505d 带来 R1(小运方向默认 p50)、R2 局部传播(默认强弱范围 `seed_only_v0`,BUG-1784)、可比领先(`common_evidence_coverage_v1`,BUG-1801)等改动,回放结果已和 round5 标签不同,但标签没有更新。
- 重跑(与 BUG-1720 / BUG-1759 同一协议):回放树 = `git archive c1a69445^` + `float_split_2026_11_08_replay_overlay.patch` + `round5_pvr_narayana_2026_11_08_replay_overlay.patch` + b453505d 的 `scripts/`、`references/` 整体覆盖(退役文件保留回放树版本)+ 新补丁 `round7_integration_2026_11_09_replay_overlay.patch`。新补丁只有三处:两份回放脚本的 FREEZE / REPORT 指向新标签;退役打分器 Narayana 无证据行时按现行引擎「不计分」补 0 分(偏移回放触发 3351 / 1316340 行,封存回放不加载该文件);回放副本里 `birth_at` 守卫改成 `RuntimeError`。回放实际加载的模块里,仍在 b453505d 的 57 个除两个守卫文件外与 b453505d 逐字节相同。按补丁头部步骤从零重建,`scripts/`、`references/` 与运行用的树完全相同。脚本 `--freeze` 新建记录后回放(`PYTHONHASHSEED=0`)。现行阶段用 `scripts/research/live_astrologer_v5_sensitivity.py`(多记 `leader_coverage`、`duplicate_event_ids`),直接跑 b453505d 工作树。`round5_pvr_narayana_2026_11_08`、`float_split_2026_11_08`、`astrologer_166d_2026_11_05` 及更早标签原样保留。
- 与整合代理 seed_only 列核对:封存 20/20、偏移 900/900、现行 231/231 逐条相同,覆盖统计两份逐字节相同,没有差异。整合代理报的「偏移 57 次 / 4 例」「现行 115 次」都是对整合前的 staging(未封存)比的,不是对 round5 标签比的;对 round5 标签见下。整合前的 staging 本来就和 round5 标签不完全相同:偏移头名平均误差 20.65 对 20.49,现行 9/231 次不同,来自两次重封之间合入 staging 的其他改动。
- 结果(旧 `round5_pvr_narayana_2026_11_08` → 新 `round7_integration_2026_11_09`):
- 封存 20 例:top-1 0.35→0.30、top-3 0.45→0.45、平均分钟误差 6.80→6.75;2/20 例变:1 例失去头名(true_rank 1→11,分钟误差 9→8),1 例 true_rank 10→3;误确认 0→0。
- 报时偏移 900 次:top-1 0.0156→0.0100、top-3 0.113→0.106、头名平均误差 20.49→20.63 分、交付覆盖率 0.900→0.883;164/900 次变(7/20 例、44/45 格):true_rank 变好 107 / 变差 39,头名误差变 57 次,头名命中变 5 次,交付覆盖变 15 次,交付宽度变 18 次。对整合前的 staging:57/900 次变(4 例),true_rank 变差 39 / 变好 0。
- 现行占星师阶段(holdout v5 77 × 3 = 231):最佳 = 真值 8→8、真值在前三 19→18、平均最佳误差 10.38→10.29 分、最佳 = 报时 17→16;124/231 次变(49/77 例):前十 105、最佳分钟 31、匹配档 13、报告置信度 13、引擎置信度 77、头部簇 38。对整合前的 staging:115/231 次变(45 例)。
- 可比性(`common_evidence_coverage_v1`):封存 40 次评分(20 全量 + 20 稀疏)全部 comparable;偏移 900 次 comparable 863、暂列(`provisional_unequal_coverage`)35(全在 1 例)、无单一领先 2(覆盖相同但并列,无可比领先),不可拆 0;现行 231 次 comparable 226、暂列 5(3 例);重复事件 0。偏移样本同一例重复,不按 900 次独立计。
- 判断:这些只是敏感度,不是准确率。事件打分接近随机水平(BUG-1644),三组结果早被看过,升降不能当变好或变差,也不能当产品指标。重封理由是记录与产品口径一致。
- 验证:新测试 `tests/test_rectification_reseal_round7_bug1809.py`:新标签封存 / 偏移 / 现行数字、可比性策略与领先计数、旧标签数字不变(带原值 / 新值 / 原因)。之前没有测试按标签钉封存结果,所以没有要改写的旧测试。`test_active_rectification_api.py::test_long_real_conversation_reaches_vedastro_after_local_range_is_narrow` 已知失败(见 BUG-1720),与本单无关。
- 防复发:上述测试;以后重封必须新建标签,不覆盖旧标签。
- 关联记录:BUG-1720、BUG-1759、BUG-1784、BUG-1801、BUG-1644
- 修复版本:本地分支 `codex/rect-reseal-r7-1109`
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
{
"record_version": "reported-offset-native-candidate-date-v3",
"frozen_at_utc": "2026-11-08T23:15:54.895716+00:00",
"ayanamsa": "raman",
"node_mode": "mean",
"radii_minutes": [
15,
30,
60
],
"offsets_minutes": [
-30,
-20,
-15,
-10,
-8,
-5,
-3,
0,
3,
5,
8,
10,
15,
20,
30
],
"minute_step": 1,
"dataset": "references/real_case_calibration/minute_rectification_holdout_v3.json",
"dataset_sha256": "45aa76ab79dfc6c6404b6252083e1aa5c7e2e2b7de811fc11042a8ad2da3f0ea",
"dataset_benchmark_id": "minute_rectification_fact_ranker_v4_holdout_v3",
"implementation_sha256": "b32140f91dd8c65a4380ad31a29d77902b2ec2cd06495b9ce387eb7e60b68825",
"implementation_sha256_prefix": "b32140f91dd8c65a",
"historical_artifacts_manifest_sha256": "6102a26a840be207b5858b3a4c0509274468ae9071e86d308cbbac7371d6864e",
"production_scoring_files": [
"scripts/active_rectification_event_engine.py",
"scripts/active_rectification_events.py",
"scripts/ashtakavarga.py",
"scripts/dasha_analyzer.py",
"scripts/divisional_charts_extended.py",
"scripts/domain_calculation_service.py",
"scripts/functional_benefics.py",
"scripts/jaimini.py",
"scripts/minute_rectification_fact_ranker_v4.py",
"scripts/minute_rectification_feature_facts_v4.py",
"scripts/narayana_dasha.py",
"scripts/rectification/api_service.py",
"scripts/rectification/candidate_contrast.py",
"scripts/rectification/candidate_window.py",
"scripts/rectification/case_holdout.py",
"scripts/rectification/contracts.py",
"scripts/rectification/dasha_transition_proximity.py",
"scripts/rectification/decision_policy.py",
"scripts/rectification/event_probes.py",
"scripts/rectification/refinement_packet.py",
"scripts/rectification/scoring_service.py",
"scripts/shadbala.py",
"scripts/varga.py"
],
"production_scoring_sha256": "9575adabfc825fe515d571ea2a8e8550b85ccbf089f9e4b185d330821d37ace8",
"research_files": [
"scripts/research/reported_offset_sweep.py",
"scripts/research/cluster_width_lib.py",
"scripts/research/probe_supply_after_six.py",
"scripts/research/sealed_holdout_rerun.py",
"scripts/minute_rectification_blind_eval.py",
"scripts/minute_rectification_fact_blind_eval_v4.py",
"scripts/minute_rectification_holdout_validator.py"
],
"research_implementation_sha256": "b7130fd8a9dd0a734d1c10aa0a4900a19702ecf75044ad1740e596f20cada0b5",
"file_sha256": {
"scripts/active_rectification_event_engine.py": "3bda6fcf8855bec90dd62e64fae167ce3e6afe5956549bbd26587ea59eba3d94",
"scripts/active_rectification_events.py": "3d8a625d24aac6f7f05a3265a23b488153daa03b5b81c6b993160011cc07eb1c",
"scripts/ashtakavarga.py": "c524da3f54b2ed9c080b78b33854854a0ca3d5d79b7e536bb1c3bc2be3e02852",
"scripts/dasha_analyzer.py": "3949b96813217fe624899856afec9f0b787c4cb0fea3a9a0d961990dbd11943a",
"scripts/divisional_charts_extended.py": "b13ad68c45685568d6eb17f87017624a9288e31fad74662735cd6d6564f7755d",
"scripts/domain_calculation_service.py": "66923a2bc69daf65c965d2973c4b9234edbec5147909eaf3609b6b377ce3c7ca",
"scripts/functional_benefics.py": "d34dd7745ba9db0a61e3f7a2aa1e6acd1f819961442cb3412b5dbe22c02ef032",
"scripts/jaimini.py": "e474b3a1d5c50de87fafdbdcc3df9bd724de004aa2495463763f5b2688b8f5e2",
"scripts/minute_rectification_blind_eval.py": "07aa30d8bad3278669495d5fea8be937aaf6367c8af980ff4f2fe9aa84ccc6ff",
"scripts/minute_rectification_fact_blind_eval_v4.py": "bdf460e3467208e8b87345c5beb2128f3e22de7eeffd68f72b281b3ad9affc92",
"scripts/minute_rectification_fact_ranker_v4.py": "f1a0d8d2482cd754d2e7f253a9606b0373519435a2bf58cfc3753e2b9ae0fb76",
"scripts/minute_rectification_feature_facts_v4.py": "4661d725d4f04935169d9d8a86ce8d0ef4061915a3a985c4912b93a6e035689e",
"scripts/minute_rectification_holdout_validator.py": "b60b1c752e58672beaee8b36eb3b2348169726cda070ec969db0f13e7ed715af",
"scripts/narayana_dasha.py": "5a924f06f76426ab587163fd8c0b5e32a6a8c6917e8670029bd84a2eb6a6172e",
"scripts/rectification/api_service.py": "089730097d3872a4f7b5d7dd5574bb7e9ab4d2e246d4a380800bc824d7979b95",
"scripts/rectification/candidate_contrast.py": "eee7da71c0574a08876404b78ca111153fa95fb83ace82b9fdca141d1992e273",
"scripts/rectification/candidate_window.py": "2fbc5058b12049b15fca375cdc125b8283e19f9f3e4bedcc291e344501891bef",
"scripts/rectification/case_holdout.py": "fe0e699b617d9e216acdfeff87af4221c1d6f4c101bf33e1dbf14e2bcd75a8b5",
"scripts/rectification/contracts.py": "c6dc6bd230e38f9f2126c224aae9ddbd6415304c15340f8c7cd3f06f6ef3160f",
"scripts/rectification/dasha_transition_proximity.py": "66d29c0b182309d17264dd2645b5cb426e28b4066d6c7004fa66f3334286753f",
"scripts/rectification/decision_policy.py": "80e8de4680d19a052213bb821d2a9c94143a2962235aeb27f6ad81374d2e60d5",
"scripts/rectification/event_probes.py": "283ac525efa5ba5b12e804d9b87e6725741ede3365659002224fffe61722bc3f",
"scripts/rectification/refinement_packet.py": "dbeea5aa780889785ce850f3881e8b7f67bb44594e9bc382c65b2e730884a2f4",
"scripts/rectification/scoring_service.py": "d1530ea3569f86747c4e6d7bccf18639e50c870526c06702bc1d9b3fec07ed9b",
"scripts/research/cluster_width_lib.py": "1d8f2ffa7795d62069deebb2de8e16749317037d4ed3f24a2c0092676e5cfcc6",
"scripts/research/probe_supply_after_six.py": "098a5b4398d6f8d996917d809c2fc922ad64031d3b70660f8ef7c876aba92438",
"scripts/research/reported_offset_sweep.py": "1ef4645a954da5d8374efc807aeeb2daf6e3431db771ed1e346b46ebbe83de62",
"scripts/research/sealed_holdout_rerun.py": "388b8af89fb89da9c3f2ce0e2bb6351ed20fd54fa19b8be34438540eed128617",
"scripts/shadbala.py": "cf1127cb60ce9aedc01050e7e391257d1c354b9ca735c7a2293baefb7d57e343",
"scripts/varga.py": "c1c8521320176e31b1f897807e50322f147654251c323731666482fc98f6df0a"
},
"hash_scope": "explicit_identity_file_sets_not_a_transitive_dependency_lock",
"scorer": "native_event_contribution_matrix_not_shadow_fact_ranker",
"delivery": "initial_signature_clusters_peak_gap_lt_8_envelope_no_answers_no_elimination",
"rank": "score_desc_then_sha256(benchmark_id:case_id:candidate_time)",
"grid": "every_minute_inclusive_not_production_two_minute_sampling",
"cross_midnight": "date_aware_candidates_distance_and_native_single_matrix",
"evaluator_sha256": "1ef4645a954da5d8374efc807aeeb2daf6e3431db771ed1e346b46ebbe83de62",
"replay_revision": "native_candidate_date_v3",
"supersedes": "candidate_date_grouped_v2_identity_and_path_not_assumed_numerically_wrong",
"is_blind_evaluation": false,
"truth_hidden_from_ranker": true,
"official_valid_independent_blind": false,
"official_blind_trial_count": 0,
"results_previously_seen": true,
"must_not_use_for_tuning": true
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,42 @@
{
"tag": "round7_integration_2026_11_09",
"source_commit": "b453505d50f8d9c5ff4a472eb225a0c60a68e607",
"policy": "common_evidence_coverage_v1",
"policy_summary": "evidence unit = (event, layer); equal coverage -> comparable (comparable_leader = provisional_leader = winning_segment); unequal -> provisional_unequal_coverage, re-rank on units every candidate has; merged layers that cannot be split -> not_reducible (BUG-1801)",
"narayana_strength_scope": "seed_only_v0",
"scope": "coverage counts only, not accuracy (BUG-1644)",
"probe": "product adjudicator compute_event_candidate_result on the replay script's own requests (sealed: full + sparse per case; offset: every shifted window); live: chart_choice astrologer leader_coverage",
"sealed_20": {
"evaluations": 40,
"leader_status": {
"comparable": 40
},
"coverage_comparison": {
"comparable/raw_totals": 40
}
},
"reported_offset_900": {
"evaluations": 900,
"leader_status": {
"comparable": 863,
"provisional_unequal_coverage": 35,
"no_single_leader": 2
},
"coverage_comparison": {
"comparable/raw_totals": 865,
"provisional_unequal_coverage/common_coverage": 35
},
"not_reducible": 0,
"provisional_cases": 1,
"no_comparable_leader": 2
},
"live_v5_231": {
"evaluations": 231,
"leader_status": {
"comparable": 226,
"provisional_unequal_coverage": 5
},
"provisional_cases": 3,
"duplicate_event_runs": 0
}
}
@@ -0,0 +1,128 @@
# BUG-1809 回放树:round7_integration_2026_11_09(staging b453505d)
#
# 构建步骤(与 BUG-1720 / BUG-1759 同一协议):
# 1. git archive c1a69445^ (两份回放脚本与退役打分器随 BUG-1644 删除,从这里取)
# 2. patch -p1 < docs/research/float_split_2026_11_08_replay_overlay.patch
# 3. patch -p1 < docs/research/round5_pvr_narayana_2026_11_08_replay_overlay.patch
# 4. git archive b453505d scripts references | tar -x (staging 的 scripts/、references/ 整体覆盖;
# 仓库已删的退役文件保留回放树版本。两份回放实际加载的 67 个模块里,仍在 b453505d 的 57 个除 (c) 两个守卫文件外逐字节相同)
# 5. 本补丁(三部分):
# (a) 两份回放脚本 FREEZE / REPORT 指向新标签(r5tree → r7tree);
# (b) 退役打分器 scoring_service.py 的回放适配:Narayana 定不出、该候选该事件无证据行时按现行引擎「不计分」补 0 分
# (BUG-1769 已记录的 IndexError 路径;本次偏移回放 3351 / 1316340 行触发,封存回放不加载该文件);
# (c) 回放副本里 birth_at 守卫由 TypeError 改为 RuntimeError(REPLAY_GUARD),避免任何调用方 except TypeError 吞掉日期字符串;
# staging → r7guard。
# 6. python scripts/research/sealed_holdout_rerun.py --freeze;reported_offset_sweep.py --freeze;再各自回放(PYTHONHASHSEED=0)。
diff -ruN r5tree/scripts/rectification/scoring_service.py r7tree/scripts/rectification/scoring_service.py
--- r5tree/scripts/rectification/scoring_service.py 2026-11-09 07:17:58.857906592 +0800
+++ r7tree/scripts/rectification/scoring_service.py 2026-11-09 07:17:58.861906532 +0800
@@ -254,6 +254,17 @@
return {**request, "events": [event for event in request["events"] if is_scoreable_event(event)]}
+import atexit as _atexit, sys as _sys
+_REPLAY_EMPTY = [0]
+_REPLAY_TOTAL = [0]
+_atexit.register(lambda: print(f"REPLAY_ADAPTER empty_evidence_rows={_REPLAY_EMPTY[0]} of {_REPLAY_TOTAL[0]}", file=_sys.stderr))
+
+
+def _replay_empty(event):
+ _REPLAY_EMPTY[0] += 1
+ return {"event_id": event["id"], "points": 0.0, "rule_ids": []}
+
+
def build_event_contribution_matrix(
request: RectificationRequest,
row_provider: Callable[[dict[str, Any]], Sequence[CandidateScoreRow]] | None = None,
@@ -277,10 +288,14 @@
sample_rows = []
for sampled in samples:
rows = list(provider(_legacy_request(scoring_request, event, sampled)))
+ _REPLAY_TOTAL[0] += len(rows)
sample_rows.append([
{**row, "score": adjusted["points"], "evidence": [adjusted]}
for row in rows
- for adjusted in [_kind_adjusted_evidence(event, row["evidence"][0])]
+ # REPLAY ADAPTER (round9 integration, 2026-11-09): a candidate whose mandatory layer is missing for this
+ # event (e.g. Narayana blocked) has no evidence row; the product adds nothing for it, so it counts 0 here.
+ for adjusted in [_kind_adjusted_evidence(event, row["evidence"][0]) if row["evidence"]
+ else _replay_empty(event)]
])
grids = [[row["time"] for row in rows] for rows in sample_rows]
if any(grid != grids[0] for grid in grids[1:]) or (candidate_grid is not None and grids[0] != candidate_grid):
diff -ruN r5tree/scripts/research/reported_offset_sweep.py r7tree/scripts/research/reported_offset_sweep.py
--- r5tree/scripts/research/reported_offset_sweep.py 2026-11-09 07:17:58.857906592 +0800
+++ r7tree/scripts/research/reported_offset_sweep.py 2026-11-09 07:17:58.861906532 +0800
@@ -66,8 +66,11 @@
# 原值: docs/research/reported_offset_float_split_2026_11_08.{freeze.json,json}
# 新值: docs/research/reported_offset_round5_pvr_narayana_2026_11_08.{freeze.json,json}
# 原因: 产品 2026-11-08 批准;占星师第五轮裁定 1、3;与报告/卡口径一致(BUG-1759:校正功能吉凶改用默认 PVR 表 30,Narayana 改用来源版 narayana_rath、出生时刻起算;回放结果变了,另存新标签)
-FREEZE = ROOT / "docs/research/reported_offset_round5_pvr_narayana_2026_11_08.freeze.json"
-REPORT = ROOT / "docs/research/reported_offset_round5_pvr_narayana_2026_11_08.json"
+# 原值: docs/research/reported_offset_round5_pvr_narayana_2026_11_08.{freeze.json,json}
+# 新值: docs/research/reported_offset_round7_integration_2026_11_09.{freeze.json,json}
+# 原因: 产品负责人 2026-11-09 批准;第七轮整合(R1 默认 p50、R2 局部传播 seed_only、可比领先);BUG-1809(staging b453505d 全部 scripts/references 叠到回放树,回放结果变了,另存新标签)
+FREEZE = ROOT / "docs/research/reported_offset_round7_integration_2026_11_09.freeze.json"
+REPORT = ROOT / "docs/research/reported_offset_round7_integration_2026_11_09.json"
LEGACY_REPORT = ROOT / "docs/research/reported_offset_2026_09_20.json"
diff -ruN r5tree/scripts/research/sealed_holdout_rerun.py r7tree/scripts/research/sealed_holdout_rerun.py
--- r5tree/scripts/research/sealed_holdout_rerun.py 2026-11-09 07:17:58.857906592 +0800
+++ r7tree/scripts/research/sealed_holdout_rerun.py 2026-11-09 07:17:58.861906532 +0800
@@ -63,8 +63,11 @@
# 原值: docs/research/sealed_holdout_rerun_float_split_2026_11_08.{freeze.json,json}
# 新值: docs/research/sealed_holdout_rerun_round5_pvr_narayana_2026_11_08.{freeze.json,json}
# 原因: 产品 2026-11-08 批准;占星师第五轮裁定 1、3;与报告/卡口径一致(BUG-1759:校正功能吉凶改用默认 PVR 表 30,Narayana 改用来源版 narayana_rath、出生时刻起算;回放结果变了,另存新标签)
-FREEZE = ROOT / "docs/research/sealed_holdout_rerun_round5_pvr_narayana_2026_11_08.freeze.json"
-REPORT = ROOT / "docs/research/sealed_holdout_rerun_round5_pvr_narayana_2026_11_08.json"
+# 原值: docs/research/sealed_holdout_rerun_round5_pvr_narayana_2026_11_08.{freeze.json,json}
+# 新值: docs/research/sealed_holdout_rerun_round7_integration_2026_11_09.{freeze.json,json}
+# 原因: 产品负责人 2026-11-09 批准;第七轮整合(R1 默认 p50、R2 局部传播 seed_only、可比领先);BUG-1809(staging b453505d 全部 scripts/references 叠到回放树,回放结果变了,另存新标签)
+FREEZE = ROOT / "docs/research/sealed_holdout_rerun_round7_integration_2026_11_09.freeze.json"
+REPORT = ROOT / "docs/research/sealed_holdout_rerun_round7_integration_2026_11_09.json"
LEGACY_REPORT = ROOT / "docs/research/sealed_holdout_rerun_2026_09_20.json"
ARCHIVE = ROOT / "docs/research/history/rectification_pre_cross_midnight_2026_09_20"
PRODUCTION_FILES = [
diff -ruN stg/scripts/active_rectification_event_engine.py r7g/scripts/active_rectification_event_engine.py
--- staging/scripts/active_rectification_event_engine.py 2026-11-09 07:17:58.869906413 +0800
+++ r7guard/scripts/active_rectification_event_engine.py 2026-11-09 07:17:58.869906413 +0800
@@ -107,7 +107,7 @@
and started every period from midnight of that day.
"""
if not isinstance(birth_at, datetime):
- raise TypeError("birth_at must be the candidate birth datetime")
+ raise RuntimeError("REPLAY_GUARD birth_at must be the candidate birth datetime")
timeline, _, _, _ = birth_moment.mahadashas(birth_at, float(moon_longitude))
return timeline
@@ -125,7 +125,7 @@
with ``birth_moment.split_period`` at both levels.
"""
if not isinstance(birth_at, datetime):
- raise TypeError("birth_at must be the candidate birth datetime")
+ raise RuntimeError("REPLAY_GUARD birth_at must be the candidate birth datetime")
if timeline is None:
timeline = _vimshottari_timeline(birth_at, moon_longitude)
_, major = dasha_analyzer.find_current(timeline, event_at)
@@ -200,7 +200,7 @@
step; there is no fallback to the old algorithm.
"""
if not isinstance(birth_at, datetime):
- raise TypeError("birth_at must be the candidate birth datetime")
+ raise RuntimeError("REPLAY_GUARD birth_at must be the candidate birth datetime")
if periods is None:
periods = _narayana_periods(ascendant_index, planet_longitudes)
if not periods:
diff -ruN stg/scripts/rectification/event_probes.py r7g/scripts/rectification/event_probes.py
--- staging/scripts/rectification/event_probes.py 2026-11-09 07:17:58.869906413 +0800
+++ r7guard/scripts/rectification/event_probes.py 2026-11-09 07:17:58.873906354 +0800
@@ -509,7 +509,7 @@
if birth_at is None:
return None
if not isinstance(birth_at, datetime):
- raise TypeError("birth_at must be the candidate birth datetime") # a date string would silently drop the track
+ raise RuntimeError("REPLAY_GUARD birth_at must be the candidate birth datetime") # a date string would silently drop the track
periods = _narayana_periods(ascendant_index, planet_longitudes)
if not periods:
return None
@@ -0,0 +1,116 @@
{
"record_version": "exposed-v3-fixed-protocol-cross-midnight-rerun-v2",
"extended_identity": {
"historical_artifacts_manifest_sha256": "6102a26a840be207b5858b3a4c0509274468ae9071e86d308cbbac7371d6864e",
"production_scoring_files": [
"scripts/active_rectification_event_engine.py",
"scripts/active_rectification_events.py",
"scripts/ashtakavarga.py",
"scripts/dasha_analyzer.py",
"scripts/divisional_charts_extended.py",
"scripts/domain_calculation_service.py",
"scripts/functional_benefics.py",
"scripts/jaimini.py",
"scripts/minute_rectification_fact_ranker_v4.py",
"scripts/minute_rectification_feature_facts_v4.py",
"scripts/narayana_dasha.py",
"scripts/rectification/api_service.py",
"scripts/rectification/candidate_contrast.py",
"scripts/rectification/candidate_window.py",
"scripts/rectification/case_holdout.py",
"scripts/rectification/contracts.py",
"scripts/rectification/dasha_transition_proximity.py",
"scripts/rectification/decision_policy.py",
"scripts/rectification/event_probes.py",
"scripts/rectification/refinement_packet.py",
"scripts/rectification/scoring_service.py",
"scripts/shadbala.py",
"scripts/varga.py"
],
"production_scoring_sha256": "9575adabfc825fe515d571ea2a8e8550b85ccbf089f9e4b185d330821d37ace8",
"research_files": [
"scripts/research/reported_offset_sweep.py",
"scripts/research/cluster_width_lib.py",
"scripts/research/probe_supply_after_six.py",
"scripts/research/sealed_holdout_rerun.py",
"scripts/minute_rectification_blind_eval.py",
"scripts/minute_rectification_fact_blind_eval_v4.py",
"scripts/minute_rectification_holdout_validator.py"
],
"research_implementation_sha256": "b7130fd8a9dd0a734d1c10aa0a4900a19702ecf75044ad1740e596f20cada0b5",
"file_sha256": {
"scripts/active_rectification_event_engine.py": "3bda6fcf8855bec90dd62e64fae167ce3e6afe5956549bbd26587ea59eba3d94",
"scripts/active_rectification_events.py": "3d8a625d24aac6f7f05a3265a23b488153daa03b5b81c6b993160011cc07eb1c",
"scripts/ashtakavarga.py": "c524da3f54b2ed9c080b78b33854854a0ca3d5d79b7e536bb1c3bc2be3e02852",
"scripts/dasha_analyzer.py": "3949b96813217fe624899856afec9f0b787c4cb0fea3a9a0d961990dbd11943a",
"scripts/divisional_charts_extended.py": "b13ad68c45685568d6eb17f87017624a9288e31fad74662735cd6d6564f7755d",
"scripts/domain_calculation_service.py": "66923a2bc69daf65c965d2973c4b9234edbec5147909eaf3609b6b377ce3c7ca",
"scripts/functional_benefics.py": "d34dd7745ba9db0a61e3f7a2aa1e6acd1f819961442cb3412b5dbe22c02ef032",
"scripts/jaimini.py": "e474b3a1d5c50de87fafdbdcc3df9bd724de004aa2495463763f5b2688b8f5e2",
"scripts/minute_rectification_blind_eval.py": "07aa30d8bad3278669495d5fea8be937aaf6367c8af980ff4f2fe9aa84ccc6ff",
"scripts/minute_rectification_fact_blind_eval_v4.py": "bdf460e3467208e8b87345c5beb2128f3e22de7eeffd68f72b281b3ad9affc92",
"scripts/minute_rectification_fact_ranker_v4.py": "f1a0d8d2482cd754d2e7f253a9606b0373519435a2bf58cfc3753e2b9ae0fb76",
"scripts/minute_rectification_feature_facts_v4.py": "4661d725d4f04935169d9d8a86ce8d0ef4061915a3a985c4912b93a6e035689e",
"scripts/minute_rectification_holdout_validator.py": "b60b1c752e58672beaee8b36eb3b2348169726cda070ec969db0f13e7ed715af",
"scripts/narayana_dasha.py": "5a924f06f76426ab587163fd8c0b5e32a6a8c6917e8670029bd84a2eb6a6172e",
"scripts/rectification/api_service.py": "089730097d3872a4f7b5d7dd5574bb7e9ab4d2e246d4a380800bc824d7979b95",
"scripts/rectification/candidate_contrast.py": "eee7da71c0574a08876404b78ca111153fa95fb83ace82b9fdca141d1992e273",
"scripts/rectification/candidate_window.py": "2fbc5058b12049b15fca375cdc125b8283e19f9f3e4bedcc291e344501891bef",
"scripts/rectification/case_holdout.py": "fe0e699b617d9e216acdfeff87af4221c1d6f4c101bf33e1dbf14e2bcd75a8b5",
"scripts/rectification/contracts.py": "c6dc6bd230e38f9f2126c224aae9ddbd6415304c15340f8c7cd3f06f6ef3160f",
"scripts/rectification/dasha_transition_proximity.py": "66d29c0b182309d17264dd2645b5cb426e28b4066d6c7004fa66f3334286753f",
"scripts/rectification/decision_policy.py": "80e8de4680d19a052213bb821d2a9c94143a2962235aeb27f6ad81374d2e60d5",
"scripts/rectification/event_probes.py": "283ac525efa5ba5b12e804d9b87e6725741ede3365659002224fffe61722bc3f",
"scripts/rectification/refinement_packet.py": "dbeea5aa780889785ce850f3881e8b7f67bb44594e9bc382c65b2e730884a2f4",
"scripts/rectification/scoring_service.py": "d1530ea3569f86747c4e6d7bccf18639e50c870526c06702bc1d9b3fec07ed9b",
"scripts/research/cluster_width_lib.py": "1d8f2ffa7795d62069deebb2de8e16749317037d4ed3f24a2c0092676e5cfcc6",
"scripts/research/probe_supply_after_six.py": "098a5b4398d6f8d996917d809c2fc922ad64031d3b70660f8ef7c876aba92438",
"scripts/research/reported_offset_sweep.py": "1ef4645a954da5d8374efc807aeeb2daf6e3431db771ed1e346b46ebbe83de62",
"scripts/research/sealed_holdout_rerun.py": "388b8af89fb89da9c3f2ce0e2bb6351ed20fd54fa19b8be34438540eed128617",
"scripts/shadbala.py": "cf1127cb60ce9aedc01050e7e391257d1c354b9ca735c7a2293baefb7d57e343",
"scripts/varga.py": "c1c8521320176e31b1f897807e50322f147654251c323731666482fc98f6df0a"
},
"hash_scope": "explicit_identity_file_sets_not_a_transitive_dependency_lock"
},
"production_identity_scope": "context_only_shadow_scorer_does_not_call_transition_proximity",
"frozen_at_utc": "2026-11-08T23:15:54.758056+00:00",
"dataset_path": "references/real_case_calibration/minute_rectification_holdout_v3.json",
"dataset_sha256": "45aa76ab79dfc6c6404b6252083e1aa5c7e2e2b7de811fc11042a8ad2da3f0ea",
"algorithm_version": "birth-time-event-fact-ranker-v4-shadow",
"implementation_sha256": "b32140f91dd8c65a4380ad31a29d77902b2ec2cd06495b9ce387eb7e60b68825",
"files": [
"scripts/active_rectification_event_engine.py",
"scripts/active_rectification_events.py",
"scripts/ashtakavarga.py",
"scripts/dasha_analyzer.py",
"scripts/divisional_charts_extended.py",
"scripts/domain_calculation_service.py",
"scripts/jaimini.py",
"scripts/minute_rectification_fact_ranker_v4.py",
"scripts/minute_rectification_feature_facts_v4.py",
"scripts/narayana_dasha.py",
"scripts/shadbala.py",
"scripts/varga.py"
],
"historical_frozen_sha256": "f41c298dd6cdcebe7a93e632f7191954be7987f6012ca2a34cecb6e447fbf196",
"evaluator_sha256": "388b8af89fb89da9c3f2ce0e2bb6351ed20fd54fa19b8be34438540eed128617",
"ayanamsa": "raman",
"node_mode": "mean",
"candidate_radius_minutes": [
10
],
"minute_step": 1,
"release_metrics": {
"top_1_rate_minimum": 0.6,
"top_3_rate_minimum": 0.85,
"mean_absolute_minute_error_maximum": 2.0,
"false_confirmation_rate_maximum": 0.05,
"correct_insufficient_evidence_rejection_rate_minimum": 0.9
},
"results_previously_seen": true,
"official_valid_independent_blind": false,
"must_not_use_for_tuning": true,
"tie_breaker": "sha256(benchmark_id:case_id:candidate_time); published minute is never passed to the ranker",
"metric_rank_definition": "competition_rank_1_plus_strictly_higher_scores_legacy_protocol",
"extra_metric_rank_definition": "score_desc_then_existing_opaque_sha256_total_order"
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,80 @@
"""BUG-1809: rectification test sets resealed on the round-7 integration (staging b453505d).
The tag pins sensitivity measures, not accuracy (event scoring is near chance, BUG-1644).
Older tags must stay byte-for-byte what they were; this file only adds a new tag.
"""
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
RESEARCH = ROOT / "docs/research"
TAG = "round7_integration_2026_11_09"
OLD = "round5_pvr_narayana_2026_11_08"
REASON = "产品负责人 2026-11-09 批准;第七轮整合(R1 默认 p50、R2 局部传播 seed_only、可比领先)"
def _load(name: str) -> dict:
return json.loads((RESEARCH / name).read_text(encoding="utf-8"))
def test_sealed_20_pins_the_new_tag() -> None:
# 原值:sealed_holdout_rerun_round5_pvr_narayana_2026_11_08 top-1 0.35、top-3 0.45、平均分钟误差 6.80
# 新值:sealed_holdout_rerun_round7_integration_2026_11_09 top-1 0.30、top-3 0.45、平均分钟误差 6.75
# 原因:产品负责人 2026-11-09 批准;第七轮整合(R1 默认 p50、R2 局部传播 seed_only、可比领先)
report = _load(f"sealed_holdout_rerun_{TAG}.json")
assert report["trial_count"] == 20 and report["implementation_hash_matches_at_replay"] is True
assert report["metrics"]["top_1_rate"] == 0.3
assert report["metrics"]["top_3_rate"] == 0.45
assert report["metrics"]["mean_absolute_minute_error"] == 6.75
assert report["metrics"]["false_confirmation_rate"] == 0.0
assert report["verified_minute_claim_allowed"] is False and report["results_previously_seen"] is True
assert report["status"] == "blocked_independent_blind_evidence"
def test_reported_offset_900_pins_the_new_tag() -> None:
# 原值:reported_offset_round5_pvr_narayana_2026_11_08 top-1 0.0156、交付覆盖率 0.900
# 新值:reported_offset_round7_integration_2026_11_09 top-1 0.0100、交付覆盖率 0.883(164/900 次、7 例变)
# 原因:产品负责人 2026-11-09 批准;第七轮整合(R1 默认 p50、R2 局部传播 seed_only、可比领先)
report = _load(f"reported_offset_{TAG}.json")
trials = report["trials"]
assert len(trials) == 900 and report["implementation_hash_matches_at_replay"] is True
assert report["freeze_record_path"] == f"docs/research/reported_offset_{TAG}.freeze.json"
assert round(sum(t["top_1_hit"] for t in trials) / 900, 4) == 0.01
assert round(sum(t["delivery_covers_truth"] for t in trials) / 900, 4) == 0.8833
old = {(t["case_ordinal"], t["offset_minutes"], t["radius_minutes"]): t for t in _load(f"reported_offset_{OLD}.json")["trials"]}
changed = [t for t in trials if old[(t["case_ordinal"], t["offset_minutes"], t["radius_minutes"])] != t]
assert len(changed) == 164 and len({t["case_ordinal"] for t in changed}) == 7
def test_live_v5_and_comparability_are_recorded_in_the_tag() -> None:
# 原值:live_astrologer_v5_round5_pvr_narayana_2026_11_08 最佳 = 真值 8、真值在前三 19
# 新值:live_astrologer_v5_round7_integration_2026_11_09 最佳 = 真值 8、真值在前三 18;
# 可比性策略 common_evidence_coverage_v1,暂列 / 可比领先计数写进标签
# 原因:产品负责人 2026-11-09 批准;第七轮整合(R1 默认 p50、R2 局部传播 seed_only、可比领先)
live = _load(f"live_astrologer_v5_{TAG}.json")
assert live["tag"] == TAG and live["baseline_tag"] == OLD
assert len(live["trials"]) == 231 and live["summary"]["new"]["best_is_truth"] == 8
assert live["summary"]["changed"]["any"] == 124
assert live["summary"]["pre_integration_staging"]["new_vs_it"]["any"] == 115
assert live["narayana_strength_scope"].startswith("seed_only_v0")
assert live["comparability_policy"].startswith("common_evidence_coverage_v1")
assert live["leader_coverage_status"] == {"comparable": 226, "provisional_unequal_coverage": 5}
comparability = _load(f"{TAG}_comparability.json")
assert comparability["policy"] == "common_evidence_coverage_v1"
assert comparability["sealed_20"]["leader_status"] == {"comparable": 40}
assert comparability["reported_offset_900"]["leader_status"] == {
"comparable": 863, "provisional_unequal_coverage": 35, "no_single_leader": 2}
assert comparability["live_v5_231"]["leader_status"] == live["leader_coverage_status"]
def test_older_tags_stay_intact() -> None:
sealed = _load(f"sealed_holdout_rerun_{OLD}.json")["metrics"]
assert (sealed["top_1_rate"], sealed["top_3_rate"], sealed["mean_absolute_minute_error"]) == (0.35, 0.45, 6.8)
offset = _load(f"reported_offset_{OLD}.json")["trials"]
assert round(sum(t["top_1_hit"] for t in offset) / 900, 4) == 0.0156
assert _load(f"live_astrologer_v5_{OLD}.json")["summary"]["new"]["best_is_truth"] == 8
for old_tag in ("float_split_2026_11_08", "astrologer_166d_2026_11_05", OLD):
for kind in ("sealed_holdout_rerun", "reported_offset"):
assert (RESEARCH / f"{kind}_{old_tag}.json").exists()
assert (RESEARCH / f"{kind}_{old_tag}.freeze.json").exists()
assert REASON in Path(__file__).read_text(encoding="utf-8")