fix(rectification): use candidate dates for cross-midnight dasha scoring
Add date-isolated caches and regression coverage, align scoring identity, and freeze full research reruns while preserving historical artifacts. Record unresolved cache/receipt identity and end-to-end acceptance gaps for branch review only. Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+8
-1
@@ -1,12 +1,19 @@
|
||||
# BLOCKED
|
||||
|
||||
## 跨午夜修复:既有上下游日期与回执缺口(2026-09-20)
|
||||
|
||||
- BUG-981 本轮只修 `candidate_at` 到 Dasha 辅助评分的日期一致性。独立审查确认两个更上/下游既有缺口,不应扩大“已修复”口径:跨午夜同簇成员按钟点排序会把短簇记成全天宽度(BUG-982);凌晨申报生成跨午夜窗时,生产枚举将起点绑定申报日,中心候选可能错到次日(BUG-983)。都不在本单仅修 helper 日期的授权内,未顺改开窗、枚举、聚类或交付策略。
|
||||
- 默认与后端算法标记同步升级只保证版本接口可达、无旧环境覆盖的分钟模式按既有身份门失效缓存。已有环境覆盖、版本探测失败的回退和 `block_scan` 提前缓存路径可能继续复用旧结果;聚合回执 `max(engine_version)` 也不是实际成功计算身份。不能用 started 新版标记声称旧结果已经重新计算,T4 的全链路可区分性尚未完整验收。
|
||||
- 本机无受控登录态、无授权线上环境变量读取渠道;不借用凭据。逐项补验见 `docs/testing/rectification-cross-midnight-20260920.md`;不把单元测试代替部署或真实历史会话打开。
|
||||
- 本轮前端全量基线 3486 项/79 失败,最终 3493 项/79 失败,失败标题集合完全相同,但套件仍未全绿。Docker 实际可用,不沿用旧轮“无 Docker”结论;存在 Windows symlink 权限、网络地址池等环境失败。两侧 build 均被外部 node_modules junction 拦截,Static/gzip 未验;Python quick 两侧均缺 mcp。完整对照见本轮进度及 `docs/testing/rectification-cross-midnight-frontend-results-20260920.json`。
|
||||
|
||||
## 生时校正验证:独立盲测资格与申报偏差真实分布(2026-09-20)
|
||||
|
||||
- v3 / v4 的案例成绩已曝光。`BUG-427/428` 禁止将已见案例重新算作独立盲测,任务书没有授权推翻;固定实现、重跑或刷新哈希不能恢复未见性。本轮 T2 只能提供固定口径重跑,独立发布验证仍 blocked,确认门保持 `not_ready`。
|
||||
- 真实用户的申报偏差分布目前不可得:需要有独立出生记录的受控用户样本和合规采集授权,目前无受控账号;不读取、猜测或借用账号。本轮 T1 只能回答给定偏差时的敏感性,不能推算总体准确率或现实失败比例。
|
||||
- 本机无项目 `.venv`,Python 3.11.7 的 mcp/hypothesis 缺失;快速门及历史 v2 封存哈希断言的基线对照见 `docs/tasks/PROGRESS-rectification-validation-20260920.md`。不改历史封存、不削弱断言来换通过。
|
||||
- 前端两侧构建均因 Turbopack 不接受指向工作树外的 node_modules junction 失败;Static/gzip 无完整产物。类型检查和 lint 通过不等于构建通过,补验清单见 `docs/testing/rectification-validation-20260920.md`。
|
||||
- 独立审查发现生产矩阵的 transition proximity 仍对跨日候选复用单一出生日期。本轮仅在新增离线评测按日期分组适配并回归,生产实现受本单“不改打分”红线保护,未修;因此本轮跨日结果不是未经改动的生产端到端回放。生产是否另立修复单由产品决定。
|
||||
- 独立审查发现生产矩阵的 transition proximity 对跨日候选复用单一出生日期。验证补缺轮仅在离线评测按日期分组适配,不能称为未经改动的生产端到端回放。产品现已批准独立修复,跟踪 **BUG-981** 与 `docs/tasks/PROGRESS-rectification-cross-midnight-20260920.md`;生产修复、回归与部署须分别验收,未闭环前保留本条。
|
||||
|
||||
|
||||
## ~~私人案例清理:远程 review 分支推送认证失败(2026-09-19)~~
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# 印度占星 Skill 更新日志
|
||||
|
||||
## 2026-09-20 — 修正跨午夜出生范围的候选打分
|
||||
|
||||
跨午夜窗口中的候选改用各自日期计算 Dasha 边界,避免午夜后的候选沿用前一天日期;候选分数与排序可能随之变化,打分权重和确认门不变。后端算法身份与 V9 默认回执版本同步更新;版本接口可达且未被环境覆盖的分钟评分会按既有规则失效旧缓存。时段缓存和聚合回执仍有既有追溯缺口,未宣称所有旧结果都已重新计算。历史回执不重写,Skill 版本不变。部署与受控会话验收状态见 `docs/tasks/PROGRESS-rectification-cross-midnight-20260920.md`。
|
||||
|
||||
## 2026-09-19 — 清理上游私人资料并补齐导入防线
|
||||
|
||||
删除未参与线上加载的旧占星快照、私人研究台账及会话转录;测试改用明确虚构的共享资料,历史文档去除私人参数和机器路径,并同步清理失效证据引用。新增导入路径拦截与仓库隐私扫描,保留证据限制和校正历史包。业务代码与 Skill 版本不变;上游哈希快照待清理后重新导入,部署与未完成验收见本轮进度记录。
|
||||
|
||||
+69
-2
@@ -12873,7 +12873,7 @@
|
||||
|
||||
## BUG-978 | 封存契约当前打分身份与资料审计状态过期
|
||||
|
||||
- 状态:resolved/partial
|
||||
- 状态:resolved
|
||||
- 首次发现:2026-09-20
|
||||
- 最近更新:2026-09-20
|
||||
- 影响面:封存 holdout 契约、当前实现评测身份与离线成绩归属。
|
||||
@@ -12900,7 +12900,8 @@
|
||||
- 验证:完整修正版 900 组合、45 格,排除 0;新增偏差与 freshness/桥接回归 28/28 通过(包含桥接重复收集),真实引擎跨日分组与逐候选重算相同。报告见 `docs/research/reported_offset_2026_09_20.md`,口径与哈希见同名 JSON。resolved 仅指离线缺少偏差维度的缺口,不代表产品分钟准确或生产跨日问题已修。
|
||||
- 防复发:锁定零偏移、窗外、口径字段、非真值破同分与跨日行为;窗口覆盖与排序命中分开记录,不以敏感性代替真实用户准确率。
|
||||
- 独立审查追加:首版偏差评测的矩阵为全窗共用一个日期,跨日时辅助 transition proximity 有偏差。已在离线适配层按候选日期分组计算并与逐候选真实引擎结果对照,不修改生产打分;原初扫统计作废。生产同类日期处理不在本单修复范围,记入 BLOCKED,不能声称生产端到端等价。
|
||||
- 相关记录:BUG-098、BUG-978。
|
||||
- 后续生产问题:候选级 Dasha 日期修复单独跟踪于 BUG-981;本记录 resolved 仍仅限离线评测缺口。
|
||||
- 相关记录:BUG-098、BUG-978、BUG-981。
|
||||
- 复发自:BUG-098;既有防复发聚焦候选公开门与同案稳定性,没有离线与生产窗心可比性的测试。
|
||||
- 修复版本:本轮执行分支,未交付。
|
||||
|
||||
@@ -12919,3 +12920,69 @@
|
||||
- 相关记录:BUG-098、BUG-428、BUG-978、BUG-979。
|
||||
- 复发自:BUG-098;原记录明确延期独立分区与校准,本轮仍无真正独立样本,不能将文档协议写成验证完成。
|
||||
- 修复版本:协议执行分支;数据集未就绪,不标 resolved。
|
||||
|
||||
## BUG-981 | 跨午夜候选共用窗口日期导致 Dasha 邻近度偏移
|
||||
|
||||
- 状态:investigating
|
||||
- 首次发现:2026-09-20
|
||||
- 最近更新:2026-09-20
|
||||
- 影响面:生产事件贡献矩阵 transition proximity、跨午夜候选排序与实现身份追溯。
|
||||
- 用户现象:跨午夜窗口的部分候选按窗口起始日计算 Dasha 边界,结果可能改变分数与排序;此前离线评测用日期分组适配绕开,生产问题仍在。
|
||||
- 触发条件:候选实际日期与请求 `birth_date` 不同,进入 `merge_transition_proximity()`。
|
||||
- 根因:helper 将请求日期用于所有候选的 Vimshottari / Narayana 起始计算及缓存键;候选的完整 `candidate_at` 未用于此层。
|
||||
- 修复:按产品已批准的任务书,先红测再改候选级日期与缓存键,不调权重;冻结重跑和 V9 版本标记同步实施。进度见 `docs/tasks/PROGRESS-rectification-cross-midnight-20260920.md`。
|
||||
- 验证:原实现新增测试 4 failed / 4 passed,修复后 9 项与 quick 桥接重复执行共 18 passed;另 3 项既有版本/枚举回归通过。19 个公开 AA 同日窗口、2299 个候选分数与矩阵字节不变;实跨日窗口全候选等于逐候选日期正确独算。性能配对中位 -0.87%,满足不超 +20%;口径 v3、raman/mean、±60、步长 1,旧 12 文件身份 `b15d9ea15227cd58`、新扩展生产身份 `7fffd1db612af1f2`。独立主会话日期/隐私补验 80 项通过。尚无部署证据,保持 investigating。
|
||||
- 验收边界:已有时段缓存与聚合回执身份导致 T4 未完整通过,见 BUG-984;另发现 BUG-982/983,不把候选级日期一致夸大为所有跨午夜路径端到端正确。
|
||||
- 防复发:新增生产打分层跨午夜回归并接入 quick 收集;候选枚举正确不再被视为下游日期正确的充分证据。保留 BUG-427/428 的实现身份与已曝光边界。
|
||||
- 相关记录:BUG-098、BUG-427、BUG-428、BUG-621、BUG-978、BUG-979。
|
||||
- 复发自:BUG-098 的跨午夜兼容只覆盖逐分钟枚举,未覆盖后加的 transition-proximity 层;BUG-979 已在离线适配发现,但其范围禁止改生产,因而没有消除此生产缺陷。
|
||||
- 补充回归:最终三个校正 Python glob 共 300 项,296 passed / 4 failed,四项失败与基线逐条相同、新增失败 0。算法 -8 的版本断言及真实引擎 golden 两项身份已精确同步,数值与比较器不变;独立定向 25 项通过。900/20 正式重跑的真实源码身份、历史字节与报告汇总经独立审查通过,确认门与独立性边界不变。
|
||||
- 修复版本:`codex/rectification-cross-midnight-20260920` 核心修复完成,T4 未完整通过;按产品要求仅交付独立分支供远程 review,不合入 staging、未部署,推送结果以远端 SHA 核对为准。
|
||||
|
||||
## BUG-982 | 跨午夜短簇按钟点排序被扩成全天跨度
|
||||
|
||||
- 状态:investigating
|
||||
- 首次发现:2026-09-20
|
||||
- 最近更新:2026-09-20
|
||||
- 影响面:`candidate_contrast.py`、`decision_policy.py` 的簇范围与宽度。
|
||||
- 用户现象:跨午夜连续三个分钟候选组成的短簇,被表示为从当天最早钟点到最晚钟点,宽度扩成全天。
|
||||
- 触发条件:同一签名簇跨午夜,钟点字符串排序而没有候选日期或全窗序号。
|
||||
- 根因:签名聚类及 `_cluster_span()` 丢失日期,只用钟点决定首尾;这不是本轮 Dasha 日期修复新引入的行为。
|
||||
- 修复:本轮仅独立诊断,不改聚类或交付策略;另行修复单处理。
|
||||
- 验证:纯虚构最小探针沿 `build_candidate_decisions()` 实测一簇三个连续跨午夜成员被报为 1440 分钟。未涉及真实案例资料,未标 resolved。
|
||||
- 防复发:后续需完整日期/相对窗序号回归贯穿签名聚类、候选决策与交付范围,不能只测试辅助 `_primary_cluster`。
|
||||
- 相关记录:BUG-623、BUG-624、BUG-639、BUG-981。
|
||||
- 复发自:未发现同症状既有记录;BUG-624/639 的范围断言仍在但均是同日样本,跨午夜诊断测试覆盖的是另一条聚类路径,未覆盖本路径。
|
||||
- 修复版本:未实施,见 `docs/tasks/PROGRESS-rectification-cross-midnight-20260920.md`。
|
||||
|
||||
## BUG-983 | 凌晨申报窗口的候选日期锚点可能偏到次日
|
||||
|
||||
- 状态:investigating
|
||||
- 首次发现:2026-09-20
|
||||
- 最近更新:2026-09-20
|
||||
- 影响面:V9 搜索窗、`engineRequestBody()` 与 `_candidate_datetimes()` 的日期传递。
|
||||
- 用户现象:申报在午夜后的范围向前跨日时,生产候选中心可能落到申报日期的次日。
|
||||
- 触发条件:申报日内早凌晨分钟采用左右搜索半径,起始钟点位于前一天,而请求仍只传未调整的申报日期。
|
||||
- 根因:前端只传钟点范围与原申报日期,生产枚举将起始钟点绑定该日、较小终止钟点推到次日;候选日期本身已错误,helper 按候选日期一致计算并不能修正上游锚点。
|
||||
- 修复:本轮仅独立诊断,不越界修改开窗或既有枚举合同;另行立单。
|
||||
- 验证:纯虚构窗口探针证明申报中心候选与申报日期的日期差为 +1 日;不含真实用户资料,未标 resolved。
|
||||
- 防复发:必须从申报日/分钟经实际前端请求构建到后端枚举端到端校验日期,而非只验证钟点范围与“支持跨午夜”。
|
||||
- 相关记录:BUG-098、BUG-198、BUG-979、BUG-981。
|
||||
- 复发自:未发现同症状既有记录;BUG-198 时段开窗与 BUG-098 枚举回归仍在但不校验申报日期中心。离线 holdout 已有前日中心保护,未覆盖生产请求链。
|
||||
- 修复版本:未实施,见 `docs/tasks/PROGRESS-rectification-cross-midnight-20260920.md`。
|
||||
|
||||
## BUG-984 | 时段旧分数缓存不校验算法身份且聚合回执可能显示新版本
|
||||
|
||||
- 状态:investigating
|
||||
- 首次发现:2026-09-20
|
||||
- 最近更新:2026-09-20
|
||||
- 影响面:V9 `block_scan` 缓存、工具活动回执与 turn 级聚合。
|
||||
- 用户现象:升级算法后,同证据的时段模式仍可能直接返回旧分数;开始活动显示新版、成功结果实际属于旧版,聚合字段又可能展示新版。
|
||||
- 触发条件:历史时段缓存证据指纹未变;本次运行默认版本已升,而缓存算法身份仍旧。
|
||||
- 根因:`scoreAndPersistV9Candidates()` 的时段分支在版本探测之前按证据指纹提前返回;成功回执使用旧结果身份,但 SQL 将不同阶段的 `engine_version` 取字符串最大值,而不是成功结果来源。分钟模式另有环境覆盖/探测失败回退边界,不能宣称身份无条件一致。
|
||||
- 修复:未实施,当前 Dasha 单不顺带改缓存政策或 SQL;补单 `docs/tasks/TASK-rectification-cross-midnight-dasha-fix-20260920.md` 待批准后执行。
|
||||
- 验证:独立纯虚构 TS 探针使用替身 RPC/fetch,旧时段缓存返回 `cached:true`、算法尾号 -7、fetch 次数 0;真实工具调用路径记录 started 尾号 -8 / completed 尾号 -7。聚合 SQL 的 `max(engine_version)` 已核源码,未真跑 DB,因此不伪称数据库端到端通过。
|
||||
- 防复发:后续必须分别覆盖分钟/时段缓存、实际版本接口、部分环境覆盖与接口失败,成功回执必须绑定实际结果来源;历史打开与 Skill 绑定不变,不得重标或删除旧结果来掩盖问题。
|
||||
- 相关记录:BUG-427、BUG-621、BUG-981。
|
||||
- 复发自:未发现同症状既有记录;既有分钟算法身份缓存门不覆盖提前返回的时段分支,回执测试未组合新版 started 与旧缓存 completed。
|
||||
- 修复版本:未实施;本轮 T4 仅完成默认及后端身份同步,整体验收未通过。
|
||||
|
||||
@@ -8,7 +8,9 @@ This file is the small index for the current engineering fronts that still drive
|
||||
- [固定口径重跑与契约](sealed_holdout_rerun_2026_09_20.md):已曝光 v3 的当前实现成绩,不是新的独立盲测;确认门关闭。
|
||||
- [v5 独立封存采集协议](sealed_holdout_v5_protocol_2026_09_20.md):本轮只出协议,不采事件。
|
||||
- 前置结论:[分钟分辨率两轮研究](rectification_minute_resolution_closure_2026_09_14.md)。不重走已证伪的加权重与簇合并假设。
|
||||
- 执行状态:[本轮进度](../tasks/PROGRESS-rectification-validation-20260920.md)。
|
||||
- 验证补缺轮:[进度](../tasks/PROGRESS-rectification-validation-20260920.md)。
|
||||
- 后续候选级跨午夜日期修复:[进度与独立验收](../tasks/PROGRESS-rectification-cross-midnight-20260920.md)。当前评测转至 `reported_offset_cross_midnight_2026_09_20.json` / `sealed_holdout_rerun_cross_midnight_2026_09_20.json`;旧 JSON / freeze 原字节留存,旧文档及评测器在 `history/rectification_pre_cross_midnight_2026_09_20/`。
|
||||
- 尚未闭环:BUG-982 跨午夜簇跨度、BUG-983 凌晨日期锚点、BUG-984 时段缓存/回执身份;[验收补单](../tasks/TASK-rectification-cross-midnight-dasha-fix-20260920.md) 不代表批准实施。
|
||||
|
||||
## Shortest-Path Closure Order (2026-06-29)
|
||||
|
||||
|
||||
+12201
File diff suppressed because it is too large
Load Diff
+64
@@ -0,0 +1,64 @@
|
||||
# 申报偏差敏感性评测(2026-09-20)
|
||||
|
||||
## 结论与边界
|
||||
|
||||
**默认半径 ±15 分钟时,申报误差绝对值超过 15 分钟,记录真值就不在候选窗内;按整数分钟,第 16 分钟开始出窗。** 这是搜索窗几何边界,不需要知道真实用户偏差分布。
|
||||
|
||||
本轮完整评测预先固定 15 个偏移 × 3 档半径 × 20 例,共 900 组合。修正版全部运行完成,validator 排除 0 例;实测结果以下列最终 JSON 为准,不能用中途旧版本初扫代替。
|
||||
|
||||
**区间覆盖不等于分钟命中,模拟偏差敏感性不等于真实用户准确率。** 不运行六题真值方向回放,不以用户认可作真值,不调参、不改生产打分、不打开确认门。
|
||||
|
||||
## 口径
|
||||
|
||||
| 项目 | 口径 |
|
||||
| --- | --- |
|
||||
| 数据集 | `references/real_case_calibration/minute_rectification_holdout_v3.json`,20 例公开 AA,每例 3 件事件,已曝光 |
|
||||
| ayanamsa / node mode | `raman` / `mean` |
|
||||
| 申报偏移 | 0、±3、±5、±8、±10、±15、±20、±30 分钟 |
|
||||
| 搜索半径 | ±15 / ±30 / ±60 分钟;±120 仅说明几何边界,未评分 |
|
||||
| 候选步长 | 1 分钟,含两端;不是生产 2 分钟抽样 |
|
||||
| 12 文件历史身份 | `b15d9ea15227cd58`(完整值见 JSON) |
|
||||
| 实际评分路径 | 原生 event contribution matrix;不是 T2 的 shadow fact ranker |
|
||||
| 额外身份 | JSON 的 `production_scoring_sha256` 与 `research_implementation_sha256` 分别绑定扩展评分文件和研究适配文件;显式文件清单不是完整传递依赖锁 |
|
||||
| 头名与排名 | 分数降序,再用既有 `_opaque_winner` 同源 SHA-256 规则排同分;不选择最接近真值的候选 |
|
||||
| 交付区间 | 初始未答题:signature clusters 中落后头名不足 8 分的仍有效簇外包范围;无新回答、无淘汰,不是完整问答链交付效果 |
|
||||
| 跨午夜 | 候选、误差及区间使用完整日期;离线矩阵按候选日期分组,合并后全窗排序/聚类 |
|
||||
| 独立性 | `is_blind_evaluation=false`,已曝光集;评分/区间定稿后才揭示标签,仅证明程序标签隔离 |
|
||||
|
||||
## 实测表
|
||||
|
||||
每格依次为 **真值在窗比例 / 哈希头名命中 / 初始交付区间覆盖**,分母均为 20 例。列为申报偏移分钟,行为搜索半径。统一口径:v3、raman/mean、1 分钟步长;历史打分身份 `b15d9ea15227cd58`,扩展原生评分身份 `115c3fbcffdaff49`,研究适配身份 `8d0c613dc0900adb`。
|
||||
|
||||
| 半径 | -30 | -20 | -15 | -10 | -8 | -5 | -3 | 0 | +3 | +5 | +8 | +10 | +15 | +20 | +30 |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
| ±15 | 0/0/0 | 0/0/0 | 100/10/95 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/95 | 100/15/95 | 0/0/0 | 0/0/0 |
|
||||
| ±30 | 100/10/95 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/95 | 100/15/95 |
|
||||
| ±60 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 | 100/5/100 |
|
||||
|
||||
表内全部数字单位为 %。这只是 20 例公开已曝光案例的条件敏感性,不附会总体置信度。即使真值在候选窗中,±15 的 -15/+10/+15 档及 ±30 的 -30/+20/+30 档仍各有一例未被初始交付范围覆盖;窗口包含与交付覆盖必须分开。±60 的所有采样偏移虽保持覆盖,但真正选中的头名只有 5%,不能用宽区间覆盖声称分钟准确。
|
||||
|
||||
修正版与错误初版的汇总比例恰好相同,不表示初版评分正确;身份、逐候选计算与跨日回归以修正版为准。
|
||||
|
||||
## 放宽能救回什么
|
||||
|
||||
- 真值在候选窗的条件始终是 `abs(offset) <= radius`。本次偏移最大绝对值 30,因此半径 30 / 60 / 120 在几何上都能包含本次所有偏移标签。
|
||||
- 对 ±15 已出窗的 ±20 / ±30 两组偏移,放宽到 ±30 可把**候选窗包含率**从 0 恢复到 100%;±60 / ±120 对这些特定偏移不再增加几何包含率。
|
||||
- 半径 ±30 自第 31 个整数分钟偏移出窗,±60 自第 61 个、±120 自第 121 个。放宽不是无条件保证,更不代表排名命中或交付覆盖同样恢复。
|
||||
- ±120 未跑评分,不报告其头名命中率或交付覆盖率。真实用户申报偏差分布未知,不估算现实用户中有多少人能被放宽救回。
|
||||
|
||||
## 初扫作废与独立复核
|
||||
|
||||
首版正确枚举了跨日候选,但矩阵内 transition proximity 仍共用窗口起日,独立审查发现跨日候选得分偏移。因此旧初扫作废,修正版标记 `replay_revision=candidate_date_grouped_v2`,并登记 `supersedes=initial_sweep_invalidated_cross_midnight_transition_anchor`。
|
||||
|
||||
修复仅在本次新脚本 `score_window()` 做按日期分组适配,未改冻结打分文件。实引擎回归验证跨日窗口的分组计算与逐候选独立重算相同;聚类和交付范围在合并后全窗运行。**生产日期处理未在本单修复,不能称为生产端到端回放。**
|
||||
|
||||
## 可复算产物与验收
|
||||
|
||||
```bash
|
||||
python scripts/research/reported_offset_sweep.py --json
|
||||
python -m pytest tests/test_reported_offset_research.py tests/test_rectification_validation_integrity_gate.py -q
|
||||
```
|
||||
|
||||
`reported_offset_2026_09_20.json` 保留逐例四项必要输出:真值是否在窗、真值排名、头名分钟误差、交付区间覆盖,以及每个格子的分母、全部参数和实现哈希。仅保存公开集序号,不保存出生分钟、坐标或事件正文。
|
||||
|
||||
排名不能与 T2 的竞争排名 top-1/top-3 混比:T2 按旧协议允许并列多个分钟同时算第一;本表把同分完全按哈希打破。最终测试数字与已知基线失败见 [进度](../tasks/PROGRESS-rectification-validation-20260920.md)。真实分布采集与新未曝光样本见 [v5 协议](sealed_holdout_v5_protocol_2026_09_20.md)。
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"record_version": "exposed-v3-fixed-protocol-rerun-v1",
|
||||
"frozen_at_utc": "2026-09-20T03:27:26.670869+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": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"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": "3bf35de5f8a0b9588808178d47ba2457c398cf2fd2d2fcb03c5c71420f998da5",
|
||||
"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"
|
||||
}
|
||||
+626
@@ -0,0 +1,626 @@
|
||||
{
|
||||
"scope": "fixed_protocol_previously_exposed_v3_rerun",
|
||||
"evaluated_on": "2026-09-20",
|
||||
"frozen_record": {
|
||||
"record_version": "exposed-v3-fixed-protocol-rerun-v1",
|
||||
"frozen_at_utc": "2026-09-20T03:27:26.670869+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": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"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": "3bf35de5f8a0b9588808178d47ba2457c398cf2fd2d2fcb03c5c71420f998da5",
|
||||
"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"
|
||||
},
|
||||
"implementation_hash_matches_at_replay": true,
|
||||
"dataset_hash_matches_at_replay": true,
|
||||
"source_audit_status": "corrected_known_date_errors",
|
||||
"validation_status": "blocked_awaiting_public_aa_cases",
|
||||
"valid_public_aa_cases": 20,
|
||||
"excluded_cases": [],
|
||||
"trial_count": 20,
|
||||
"trials": [
|
||||
{
|
||||
"case_ordinal": 1,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 5,
|
||||
"opaque_true_rank": 8,
|
||||
"minute_error": 10,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 2,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 2,
|
||||
"opaque_true_rank": 8,
|
||||
"minute_error": 3,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 3,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 1,
|
||||
"opaque_true_rank": 12,
|
||||
"minute_error": 5,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 4,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 1,
|
||||
"opaque_true_rank": 1,
|
||||
"minute_error": 0,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 5,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 1,
|
||||
"opaque_true_rank": 10,
|
||||
"minute_error": 1,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 6,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 7,
|
||||
"opaque_true_rank": 12,
|
||||
"minute_error": 6,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 7,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 5,
|
||||
"opaque_true_rank": 7,
|
||||
"minute_error": 9,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 8,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 5,
|
||||
"opaque_true_rank": 8,
|
||||
"minute_error": 10,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 9,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 1,
|
||||
"opaque_true_rank": 3,
|
||||
"minute_error": 1,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"missing_mandatory_layers",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"missing_mandatory_layers",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 10,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 1,
|
||||
"opaque_true_rank": 8,
|
||||
"minute_error": 5,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 11,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 6,
|
||||
"opaque_true_rank": 17,
|
||||
"minute_error": 5,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 12,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 1,
|
||||
"opaque_true_rank": 4,
|
||||
"minute_error": 5,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 13,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 5,
|
||||
"opaque_true_rank": 7,
|
||||
"minute_error": 10,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 14,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 17,
|
||||
"opaque_true_rank": 19,
|
||||
"minute_error": 10,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 15,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 1,
|
||||
"opaque_true_rank": 7,
|
||||
"minute_error": 9,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"missing_mandatory_layers",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"missing_mandatory_layers",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 16,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 12,
|
||||
"opaque_true_rank": 12,
|
||||
"minute_error": 10,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 17,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 6,
|
||||
"opaque_true_rank": 6,
|
||||
"minute_error": 6,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 18,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 11,
|
||||
"opaque_true_rank": 14,
|
||||
"minute_error": 10,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 19,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 1,
|
||||
"opaque_true_rank": 6,
|
||||
"minute_error": 4,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
},
|
||||
{
|
||||
"case_ordinal": 20,
|
||||
"candidate_count": 21,
|
||||
"event_count": 3,
|
||||
"true_rank": 1,
|
||||
"opaque_true_rank": 7,
|
||||
"minute_error": 10,
|
||||
"would_confirm": false,
|
||||
"false_confirmation": false,
|
||||
"insufficient_evidence_rejected": true,
|
||||
"full_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
],
|
||||
"sparse_trial_reasons": [
|
||||
"unique_minute_not_found",
|
||||
"insufficient_events",
|
||||
"insufficient_domains",
|
||||
"neighbor_stability_not_passed",
|
||||
"leave_one_event_out_not_passed",
|
||||
"top_candidate_feature_not_unique",
|
||||
"fact_ranker_v4_holdout_not_ready"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metrics": {
|
||||
"top_1_rate": 0.45,
|
||||
"top_3_rate": 0.5,
|
||||
"mean_absolute_minute_error": 6.45,
|
||||
"false_confirmation_rate": 0.0,
|
||||
"correct_insufficient_evidence_rejection_rate": 1.0,
|
||||
"confirmation_coverage_rate": 0.0
|
||||
},
|
||||
"metric_gates_passed": false,
|
||||
"opaque_exact_top_1_rate": 0.05,
|
||||
"opaque_exact_top_3_rate": 0.1,
|
||||
"official_valid_independent_blind": false,
|
||||
"official_blind_trial_count": 0,
|
||||
"is_blind_evaluation": false,
|
||||
"truth_hidden_from_ranker": true,
|
||||
"results_previously_seen": true,
|
||||
"verified_minute_claim_allowed": false,
|
||||
"status": "blocked_independent_blind_evidence",
|
||||
"boundary": "Three-event low-information protocol, not a mathematical accuracy lower bound and not representative of real sessions. Historical v3/v4 exposure cannot be undone by freezing today's scorer. No tuning or release claims."
|
||||
}
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
# 当前冻结实现的 v3 固定口径重跑(2026-09-20)
|
||||
|
||||
## 结论
|
||||
|
||||
**已补上当前实现可归属、可复算的成绩;没有补成新的独立官方盲测。** 既有 v3/v4 人物与成绩已经曝光,重新冻结今天的打分文件不能消除曝光。任务书 T2 中「首次口径干净的官方盲测」在既有 BUG-427 / BUG-428 红线下仍为 **blocked**,不得改名通过。确认门保持关闭。
|
||||
|
||||
这是每例只有 3 件事的低信息量(任务书称「下限」)口径,不代表真实会话;事件少不等于数学上保证成绩更低,因此也不是准确率下界。
|
||||
|
||||
## 固定口径与可审计文件
|
||||
|
||||
| 项目 | 本次实际值 |
|
||||
| --- | --- |
|
||||
| 数据集 | `references/real_case_calibration/minute_rectification_holdout_v3.json` |
|
||||
| 数据集 SHA-256 | `45aa76ab79dfc6c6404b6252083e1aa5c7e2e2b7de811fc11042a8ad2da3f0ea` |
|
||||
| 计算规格 | ayanamsa `raman` / node mode `mean` |
|
||||
| 半径 / 步长 | ±10 分钟 / 1 分钟,含两端;真值为窗心 |
|
||||
| 算法 | `birth-time-event-fact-ranker-v4-shadow`,不是生产 V9 问答链 |
|
||||
| 当前打分身份 | `b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18`(前缀 `b15d9ea15227cd58`) |
|
||||
| 样本 / 事件 | 20 例 / 每例 3 件;validator 排除 0 例 |
|
||||
| 资料审计状态 | `corrected_known_date_errors`,不等同全新独立人工重审 |
|
||||
| 排名标签隔离 | 排序和稀疏证据判定完成后才读取真值标签 |
|
||||
| 独立官方盲测 | `official_valid_independent_blind=false`;有效官方盲测试次仍为 0 |
|
||||
|
||||
冻结记录:`sealed_holdout_rerun_2026_09_20.freeze.json`。先以独占创建模式记录 12 个文件、数据集与评测器字节哈希,再执行评分;评分前验证全部身份,任何漂移即拒绝。历史 v3 `frozen_scoring` 原值 `f41c298dd6cdcebe…` 未改。
|
||||
|
||||
脱敏逐例结果:`sealed_holdout_rerun_2026_09_20.json`,只含公开集序号、排名、误差、门禁原因,不含出生分钟、坐标或事件正文。复算:
|
||||
|
||||
```bash
|
||||
python scripts/research/sealed_holdout_rerun.py --json
|
||||
```
|
||||
|
||||
## 五项预注册指标与门槛
|
||||
|
||||
本表统一口径:v3 / `raman` / `mean` / ±10 / 1 分钟 / `b15d9ea15227cd58`。
|
||||
|
||||
| 指标 | 实测 | 预注册门槛 | 数值对照 |
|
||||
| --- | ---: | ---: | --- |
|
||||
| top-1(旧协议竞争排名,含并列) | 0.45 | ≥0.60 | 未通过 |
|
||||
| top-3(旧协议竞争排名,含并列) | 0.50 | ≥0.85 | 未通过 |
|
||||
| 头名平均绝对分钟误差(哈希打破并列) | 6.45 分钟 | ≤2.00 分钟 | 未通过 |
|
||||
| 误确认率(全样本分母) | 0.00 | ≤0.05 | 通过,但无确认覆盖,不能证明确认精度 |
|
||||
| 信息不足正确拒绝率(每例仅首件事件) | 1.00 | ≥0.90 | 通过 |
|
||||
| 确认覆盖率(补充) | 0.00 | 本单禁止抬高 | 保持关闭 |
|
||||
| 作为独立盲测发布成绩的资格 | 不适用 | 新封存、未曝光、独立审计 | blocked,不可当发布指标 |
|
||||
|
||||
**重要口径纠正:旧 top-1/top-3 不是哈希选中那个分钟的命中率。** 现有评测的 `true_rank = 1 + 严格高于真值分数的候选数` 会把同分的多个分钟全算第一;本次为保证与旧协议可比保留它,同时增加哈希全序的真实选中指标,不暗改预注册定义。
|
||||
|
||||
| 附加指标(同一上述口径) | 实测 |
|
||||
| --- | ---: |
|
||||
| `_opaque_winner` 真正选中的头名命中 | 0.05 |
|
||||
| 同一哈希全序中的 top-3 命中 | 0.10 |
|
||||
|
||||
这两个数字不能与旧 0.45/0.50 混称同一种准确率。没有据此改权重、换半径、换案例或剔除失败例。
|
||||
|
||||
## 契约改了什么、没有改什么
|
||||
|
||||
- `current_tree_scorer` 绑定本次真实哈希及新报告,新增固定口径试次数与成绩;旧 `current_tree_unfrozen_diagnostic` 历史块保留。
|
||||
- `source_audit_status` 对齐数据集;`evaluated_on` 表示最新固定口径重跑日期,并新增作用域说明。历史顶层指标仍有 `metrics_produced_by` 的旧日期与来源,未伪装成本次结果。
|
||||
- `status`、`valid_public_aa_cases`、`required_cases`、`top_1_rate`、`confirmation_coverage_rate`、`sealed_benchmark_id` 六个 runtime 键的值与类型全不变;尤其 `status=not_ready`、确认覆盖率为零。
|
||||
- `official_eval_implementation_hash_matches=false`、`official_eval_trial_count=0` 保留;新增重跑记录不冒充已重新获得独立性。
|
||||
- 未改任何冻结打分文件、生产判定、现有 `_candidate_moments()` 或 `request_from_case()`。
|
||||
|
||||
## 防复发与既有断言变更
|
||||
|
||||
新增 `tests/test_sealed_holdout_contract_freshness.py` 钉死当前树身份、审计状态、冻结记录、报告汇总与独立性声明;冻结身份不符必须在计算前拒绝。`tests/test_rectification_validation_integrity_gate.py` 将其和偏差评测回归纳入现有 `test_rectification_*.py` 快速门,不改 workflow。
|
||||
|
||||
| 原断言 | 新断言 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| Python / 前端:`current_tree_scorer.implementation_sha256` 等于旧 post-audit sidecar 的哈希 | 等于新固定口径报告 `frozen_record.implementation_sha256`,另验证当前树与新报告 | 契约当前树块的含义就是当前实现;继续绑定旧报告会强制过期。新增身份断言更严格,不弱化门禁 |
|
||||
| 官方标志 false、官方试次 0、六键等于历史报告、确认门全套行为断言 | 原值保留,另加固定口径重跑身份/试次和独立盲测 false | 固定口径重跑不是新的独立盲测,不借元数据迁移打开确认门 |
|
||||
|
||||
本机 Python 3.11.7 可跑;`python3` Windows 别名退出 49。该任务定向命令的最终测试清单与基线失败对照由 `PROGRESS-rectification-validation-20260920.md` 统一记录。特别是既有 v2 冻结身份测试不属本次刷新范围,不得为了通过而改其历史封存或断言。
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"scope": "immutable_pre_fix_artifacts_not_current_results",
|
||||
"baseline_commit": "932f2fff",
|
||||
"reason": "BUG-981 production candidate-date fix; prior T1 used offline date grouping and T2 shadow scorer; numerical changes not assumed",
|
||||
"files": [
|
||||
{
|
||||
"source_path": "docs/research/reported_offset_2026_09_20.md",
|
||||
"archive_path": "docs/research/history/rectification_pre_cross_midnight_2026_09_20/docs/research/reported_offset_2026_09_20.md",
|
||||
"sha256": "1033fe68ff4b2802ab321ef2f76e558dc21c3c534486954bf03af8b7f114f6df",
|
||||
"size_bytes": 6018
|
||||
},
|
||||
{
|
||||
"source_path": "docs/research/reported_offset_2026_09_20.json",
|
||||
"archive_path": "docs/research/history/rectification_pre_cross_midnight_2026_09_20/docs/research/reported_offset_2026_09_20.json",
|
||||
"sha256": "9878f2b50c957a470fafcb2ed0a9eb16405c3f6fa422ea50f467e84b0189ace1",
|
||||
"size_bytes": 324672
|
||||
},
|
||||
{
|
||||
"source_path": "docs/research/sealed_holdout_rerun_2026_09_20.md",
|
||||
"archive_path": "docs/research/history/rectification_pre_cross_midnight_2026_09_20/docs/research/sealed_holdout_rerun_2026_09_20.md",
|
||||
"sha256": "dab1bf59748efab481aee394ab25822ee51b0665bf66c769afdfb4d53a492d29",
|
||||
"size_bytes": 5818
|
||||
},
|
||||
{
|
||||
"source_path": "docs/research/sealed_holdout_rerun_2026_09_20.json",
|
||||
"archive_path": "docs/research/history/rectification_pre_cross_midnight_2026_09_20/docs/research/sealed_holdout_rerun_2026_09_20.json",
|
||||
"sha256": "40df220bfcb51a683b31fbd626896e47a5aed7ee4f30ff669543691f7ce53c1e",
|
||||
"size_bytes": 20052
|
||||
},
|
||||
{
|
||||
"source_path": "docs/research/sealed_holdout_rerun_2026_09_20.freeze.json",
|
||||
"archive_path": "docs/research/history/rectification_pre_cross_midnight_2026_09_20/docs/research/sealed_holdout_rerun_2026_09_20.freeze.json",
|
||||
"sha256": "d17651cb50224acdd1af7a4692c777ed169b216a962a0675371254ce561d0921",
|
||||
"size_bytes": 1898
|
||||
},
|
||||
{
|
||||
"source_path": "references/rectification_sealed_holdout.v1.json",
|
||||
"archive_path": "docs/research/history/rectification_pre_cross_midnight_2026_09_20/references/rectification_sealed_holdout.v1.json",
|
||||
"sha256": "863806992ee7162e959bb319fd82bd75f13fadea6218e197dd4e78fd2ffd0279",
|
||||
"size_bytes": 3705
|
||||
},
|
||||
{
|
||||
"source_path": "scripts/research/reported_offset_sweep.py",
|
||||
"archive_path": "docs/research/history/rectification_pre_cross_midnight_2026_09_20/scripts/research/reported_offset_sweep.py.txt",
|
||||
"sha256": "87f87859bb399da1e860ab5e750e68088401a5a82fbba10ba709c17e5266167c",
|
||||
"size_bytes": 11001
|
||||
},
|
||||
{
|
||||
"source_path": "scripts/research/sealed_holdout_rerun.py",
|
||||
"archive_path": "docs/research/history/rectification_pre_cross_midnight_2026_09_20/scripts/research/sealed_holdout_rerun.py.txt",
|
||||
"sha256": "3bf35de5f8a0b9588808178d47ba2457c398cf2fd2d2fcb03c5c71420f998da5",
|
||||
"size_bytes": 7482
|
||||
}
|
||||
]
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"scope": "superseded_preparation_audit",
|
||||
"reason": "Existing report overwrite denied; retained initial freeze and completed 20-case preparation replay unchanged. Stopped 900 sweep before result write. Final report paths changed; evaluator identity re-frozen before new final replay.",
|
||||
"files": [
|
||||
{
|
||||
"path": "docs/research/sealed_holdout_rerun_cross_midnight_2026_09_20.freeze.json",
|
||||
"sha256": "6638e52191fed63ed3a2e99da149ba3269ea4367d8e60b8408f2b05165ca280e",
|
||||
"status": "superseded_preparation_not_final_report"
|
||||
},
|
||||
{
|
||||
"path": "docs/research/reported_offset_cross_midnight_2026_09_20.freeze.json",
|
||||
"sha256": "80004557170595e7ae222ecf538482e2f1f279f3858896e2553ba0d565a45eea",
|
||||
"status": "superseded_preparation_not_final_report"
|
||||
},
|
||||
{
|
||||
"path": "docs/research/sealed_holdout_rerun_2026_09_20.cross_midnight.rerun.json",
|
||||
"sha256": "f4b0ed8a8ad17604940f9022339854571501d95af863820f58501a4981d4e4e1",
|
||||
"status": "superseded_preparation_not_final_report"
|
||||
}
|
||||
]
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"sealed_benchmark_id": "minute_rectification_fact_ranker_v4_holdout_v3",
|
||||
"status": "not_ready",
|
||||
"valid_public_aa_cases": 20,
|
||||
"required_cases": 20,
|
||||
"top_1_rate": 0.15,
|
||||
"confirmation_coverage_rate": 0.0,
|
||||
"previous_pilot_id": "minute_rectification_holdout_v2",
|
||||
"dataset_path": "references/real_case_calibration/minute_rectification_holdout_v3.json",
|
||||
"report_path": "references/real_case_calibration/minute_rectification_holdout_v3_report.json",
|
||||
"source_audit_status": "corrected_known_date_errors",
|
||||
"evaluated_on": "2026-09-20",
|
||||
"evaluated_on_scope": "latest_fixed_protocol_rerun_not_historical_runtime_metrics",
|
||||
"historical_source_audit_status": "invalidated_after_replay",
|
||||
"report_status": "invalidated_after_source_audit",
|
||||
"trial_count": 20,
|
||||
"top_3_rate": 0.25,
|
||||
"mean_absolute_minute_error": 6.95,
|
||||
"metrics_produced_by": {
|
||||
"implementation_sha256": "f41c298dd6cdcebe7a93e632f7191954be7987f6012ca2a34cecb6e447fbf196",
|
||||
"algorithm_version": "birth-time-event-fact-ranker-v4-shadow",
|
||||
"implementation_hash_matches_at_replay": true,
|
||||
"source_report": "references/real_case_calibration/minute_rectification_holdout_v3_report.json",
|
||||
"evaluated_on": "2026-07-21"
|
||||
},
|
||||
"current_tree_scorer": {
|
||||
"implementation_sha256": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"matches_metrics_scorer": false,
|
||||
"official_eval_implementation_hash_matches": false,
|
||||
"official_eval_trial_count": 0,
|
||||
"source_report": "docs/research/sealed_holdout_rerun_2026_09_20.json",
|
||||
"fixed_protocol_rerun_trial_count": 20,
|
||||
"fixed_protocol_rerun_hash_matches": true,
|
||||
"metrics": {
|
||||
"top_1_rate": 0.45,
|
||||
"top_3_rate": 0.5,
|
||||
"mean_absolute_minute_error": 6.45,
|
||||
"false_confirmation_rate": 0.0,
|
||||
"correct_insufficient_evidence_rejection_rate": 1.0,
|
||||
"confirmation_coverage_rate": 0.0
|
||||
}
|
||||
},
|
||||
"current_tree_fixed_protocol_rerun": {
|
||||
"evaluated_on": "2026-09-20",
|
||||
"report_path": "docs/research/sealed_holdout_rerun_2026_09_20.json",
|
||||
"freeze_record_path": "docs/research/sealed_holdout_rerun_2026_09_20.freeze.json",
|
||||
"implementation_sha256": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"scorer_frozen_before_rerun": true,
|
||||
"source_audit_status": "corrected_known_date_errors",
|
||||
"trial_count": 20,
|
||||
"official_valid_independent_blind": false,
|
||||
"is_blind_evaluation": false,
|
||||
"truth_hidden_from_ranker": true,
|
||||
"results_previously_seen": true,
|
||||
"must_not_claim_as_release_metrics": true,
|
||||
"must_not_use_for_tuning": true,
|
||||
"verified_minute_claim_allowed": false,
|
||||
"metric_gates_passed": false,
|
||||
"events_per_case": 3,
|
||||
"boundary": "First current-hash fixed-protocol rerun in this task, not a first independent official blind evaluation. Three-event low-information protocol is not representative of real sessions and is not a mathematical accuracy lower bound. Historical v3/v4 score exposure requires a fresh sealed set."
|
||||
},
|
||||
"current_tree_unfrozen_diagnostic": {
|
||||
"is_blind_evaluation": false,
|
||||
"results_already_seen": true,
|
||||
"scorer_frozen": false,
|
||||
"source_audit_passed": false,
|
||||
"must_not_claim_as_release_metrics": true,
|
||||
"must_not_use_for_tuning": true,
|
||||
"report_path": "references/real_case_calibration/minute_rectification_holdout_v3_post_audit_diagnostic_report.json",
|
||||
"implementation_sha256": "99730c84c6434f52669a05e4d9a4a87df0a218e3237b5436b933252f2028384e",
|
||||
"trial_count": 20,
|
||||
"top_1_rate": 0.45,
|
||||
"top_3_rate": 0.5,
|
||||
"mean_absolute_minute_error": 5.8,
|
||||
"confirmation_coverage_rate": 0.0,
|
||||
"metric_gates_passed": false,
|
||||
"verified_minute_claim_allowed": false
|
||||
}
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Reported-centre sensitivity on exposed public cases, without oracle answers."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from scripts.active_rectification_event_engine import (
|
||||
AYANAMSA, NODE_MODE, compute_candidate_static_contexts,
|
||||
)
|
||||
from scripts.minute_rectification_blind_eval import _opaque_winner, implementation_sha256
|
||||
from scripts.minute_rectification_holdout_validator import validate
|
||||
from scripts.rectification.candidate_contrast import select_signature_representatives
|
||||
from scripts.rectification.scoring_service import build_event_contribution_matrix, score_from_matrix
|
||||
from scripts.research.cluster_width_lib import SEPARATION_LEAD, still_valid_public
|
||||
from scripts.research.probe_supply_after_six import request_from_case
|
||||
from scripts.research.sealed_holdout_rerun import DATASET, file_sha256, opaque_order
|
||||
|
||||
OFFSETS = (-30, -20, -15, -10, -8, -5, -3, 0, 3, 5, 8, 10, 15, 20, 30)
|
||||
RADII = (15, 30, 60)
|
||||
MINUTE_STEP = 1
|
||||
PRODUCTION_FILES = [
|
||||
"scripts/rectification/scoring_service.py",
|
||||
"scripts/rectification/dasha_transition_proximity.py",
|
||||
"scripts/rectification/candidate_contrast.py",
|
||||
"scripts/rectification/case_holdout.py",
|
||||
"scripts/rectification/contracts.py",
|
||||
"scripts/rectification/event_probes.py",
|
||||
]
|
||||
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",
|
||||
]
|
||||
|
||||
|
||||
def shifted_window(case: dict[str, Any], offset: int, radius: int) -> tuple[dict[str, Any], list[datetime]]:
|
||||
if radius not in (*RADII, 120):
|
||||
raise ValueError("unsupported_radius")
|
||||
true_at = datetime.fromisoformat(f"{case['birth']['date']}T{case['birth']['time']}:00")
|
||||
reported_at = true_at + timedelta(minutes=offset)
|
||||
start = reported_at - timedelta(minutes=radius)
|
||||
end = reported_at + timedelta(minutes=radius)
|
||||
candidates = [start + timedelta(minutes=i) for i in range(0, radius * 2 + 1, MINUTE_STEP)]
|
||||
# Reuse only the established event normalization, then replace the window.
|
||||
# The ranker sees neither the truth label nor the simulated offset.
|
||||
request = request_from_case(case)
|
||||
request.update({
|
||||
"birth_date": start.date().isoformat(),
|
||||
"start_time": start.strftime("%H:%M"),
|
||||
"end_time": end.strftime("%H:%M"),
|
||||
"minute_step": MINUTE_STEP,
|
||||
"ayanamsa": AYANAMSA,
|
||||
"node_mode": NODE_MODE,
|
||||
})
|
||||
return request, candidates
|
||||
|
||||
|
||||
def score_window(request: dict[str, Any], contexts: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""Keep auxiliary transition anchors on each candidate's actual date.
|
||||
|
||||
The production matrix's transition-proximity helper accepts one birth date
|
||||
per call, unlike the static chart layer which reads candidate_at. Grouping
|
||||
is only an offline adapter; it does not change the production scorer.
|
||||
"""
|
||||
by_date: dict[str, list[dict[str, Any]]] = {}
|
||||
for context in contexts:
|
||||
by_date.setdefault(context["candidate_at"].date().isoformat(), []).append(context)
|
||||
by_time = {}
|
||||
for candidate_date, group in by_date.items():
|
||||
dated_request = {**request, "birth_date": candidate_date}
|
||||
built = build_event_contribution_matrix(dated_request, static_contexts=group)
|
||||
by_time.update({row["time"]: row for row in score_from_matrix(dated_request, built)})
|
||||
return [by_time[context["candidate_at"].strftime("%H:%M")] for context in contexts]
|
||||
|
||||
|
||||
def delivery_moments(public: list[dict[str, Any]], candidates: list[datetime]) -> list[datetime]:
|
||||
"""Initial delivery envelope in date-aware order; no truth-based narrowing."""
|
||||
valid = still_valid_public(public, {}, lead=SEPARATION_LEAD)
|
||||
clocks = {str(value)[:5] for row in valid for value in (row.get("cluster_times") or [row["time"]])}
|
||||
return [candidate for candidate in candidates if candidate.strftime("%H:%M") in clocks]
|
||||
|
||||
|
||||
def reveal_metrics(
|
||||
rows: list[dict[str, Any]], candidates: list[datetime], delivery: list[datetime],
|
||||
truth: datetime, benchmark_id: str, case_id: str,
|
||||
) -> dict[str, Any]:
|
||||
ordered = opaque_order(benchmark_id, case_id, rows)
|
||||
predicted_clock = _opaque_winner(benchmark_id, case_id, rows)
|
||||
by_clock = {candidate.strftime("%H:%M"): candidate for candidate in candidates}
|
||||
if len(by_clock) != len(candidates):
|
||||
raise ValueError("ambiguous_candidate_clock")
|
||||
predicted = by_clock[predicted_clock]
|
||||
within = min(candidates) <= truth <= max(candidates)
|
||||
in_candidates = truth in candidates
|
||||
rank = next((i for i, row in enumerate(ordered, 1) if by_clock[row["time"]] == truth), None)
|
||||
covered = bool(delivery) and min(delivery) <= truth <= max(delivery)
|
||||
return {
|
||||
"truth_in_window": within,
|
||||
"truth_in_candidates": in_candidates,
|
||||
"true_rank": rank,
|
||||
"top_1_hit": predicted == truth,
|
||||
"top_1_minute_error": abs((predicted - truth).total_seconds()) / 60,
|
||||
"delivery_covers_truth": covered,
|
||||
"candidate_count": len(candidates),
|
||||
"delivery_width_minutes": int((max(delivery) - min(delivery)).total_seconds() / 60) + 1 if delivery else 0,
|
||||
}
|
||||
|
||||
|
||||
def summarize(trials: list[dict[str, Any]], radii: tuple[int, ...], offsets: tuple[int, ...]) -> list[dict[str, Any]]:
|
||||
result = []
|
||||
for radius in radii:
|
||||
for offset in offsets:
|
||||
group = [row for row in trials if row["radius_minutes"] == radius and row["offset_minutes"] == offset]
|
||||
count = len(group)
|
||||
result.append({
|
||||
"radius_minutes": radius, "offset_minutes": offset, "trial_count": count,
|
||||
**{key: round(sum(row[source] for row in group) / count, 4) if count else None for key, source in (
|
||||
("truth_in_window_rate", "truth_in_window"),
|
||||
("top_1_rate", "top_1_hit"),
|
||||
("delivery_coverage_rate", "delivery_covers_truth"),
|
||||
("mean_absolute_minute_error", "top_1_minute_error"),
|
||||
)},
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
def run(dataset: Path = DATASET, radii: tuple[int, ...] = RADII, offsets: tuple[int, ...] = OFFSETS) -> dict[str, Any]:
|
||||
manifest = json.loads(dataset.read_text(encoding="utf-8"))
|
||||
frozen_files = manifest["frozen_scoring"]["files"]
|
||||
scoring_files = sorted(set(frozen_files + PRODUCTION_FILES))
|
||||
starting_hash = implementation_sha256(scoring_files)
|
||||
validation = validate(dataset)
|
||||
invalid = validation["invalid_cases"]
|
||||
trials = []
|
||||
for index, case in enumerate(manifest["cases"], 1):
|
||||
if case["case_id"] in invalid:
|
||||
continue
|
||||
# Reuse static chart calculations, not window-dependent scores or ranks.
|
||||
contexts_by_moment: dict[datetime, dict[str, Any]] = {}
|
||||
for radius in radii:
|
||||
for offset in offsets:
|
||||
request, candidates = shifted_window(case, offset, radius)
|
||||
missing = [moment for moment in candidates if moment not in contexts_by_moment]
|
||||
if missing:
|
||||
contexts_by_moment.update(zip(missing, compute_candidate_static_contexts(request, candidates=missing), strict=True))
|
||||
contexts = [contexts_by_moment[moment] for moment in candidates]
|
||||
rows = score_window(request, contexts)
|
||||
public = select_signature_representatives(rows, contexts)
|
||||
delivery = delivery_moments(public, candidates)
|
||||
# All scores and the delivery envelope are locked before reveal.
|
||||
truth = datetime.fromisoformat(f"{case['birth']['date']}T{case['birth']['time']}:00")
|
||||
trials.append({
|
||||
"case_ordinal": index, "offset_minutes": offset, "radius_minutes": radius,
|
||||
**reveal_metrics(rows, candidates, delivery, truth, manifest["benchmark_id"], case["case_id"]),
|
||||
})
|
||||
if implementation_sha256(scoring_files) != starting_hash:
|
||||
raise ValueError("scorer_changed_during_sweep")
|
||||
return {
|
||||
"scope": "reported_offset_sensitivity_not_product_accuracy",
|
||||
"specification": {
|
||||
"ayanamsa": AYANAMSA, "node_mode": NODE_MODE,
|
||||
"radii_minutes": list(radii), "offsets_minutes": list(offsets), "minute_step": MINUTE_STEP,
|
||||
"dataset": dataset.relative_to(ROOT).as_posix(), "dataset_sha256": file_sha256(dataset),
|
||||
"dataset_benchmark_id": manifest["benchmark_id"],
|
||||
"implementation_sha256": implementation_sha256(frozen_files),
|
||||
"implementation_sha256_prefix": implementation_sha256(frozen_files)[:16],
|
||||
"production_scoring_files": scoring_files, "production_scoring_sha256": starting_hash,
|
||||
"research_files": RESEARCH_FILES,
|
||||
"research_implementation_sha256": implementation_sha256(RESEARCH_FILES),
|
||||
"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_matrix_grouped_by_candidate_date",
|
||||
"evaluator_sha256": file_sha256(Path(__file__)),
|
||||
"replay_revision": "candidate_date_grouped_v2",
|
||||
"supersedes": "initial_sweep_invalidated_cross_midnight_transition_anchor",
|
||||
"is_blind_evaluation": False, "truth_hidden_from_ranker": True,
|
||||
"results_previously_seen": True, "must_not_use_for_tuning": True,
|
||||
},
|
||||
"excluded_cases": invalid, "case_count": validation["valid_public_aa_cases"],
|
||||
"trial_count": len(trials), "trials": trials,
|
||||
"summary": summarize(trials, radii, offsets),
|
||||
"widening_geometry": {
|
||||
"scored_radii": list(radii),
|
||||
"radius_120_scored": 120 in radii,
|
||||
"truth_in_window_condition": "abs(reported_offset_minutes) <= radius_minutes",
|
||||
"radius_15_first_integer_minute_outside": 16,
|
||||
"all_tested_offsets_within_radius_30_60_120": max(map(abs, offsets)) <= 30,
|
||||
"boundary": "Geometry rescues candidate inclusion only, not ranking or delivery coverage; no population frequency without a reported-offset distribution.",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--json", action="store_true")
|
||||
args = parser.parse_args()
|
||||
print(json.dumps(run(), ensure_ascii=False, indent=2))
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Freeze and replay the exposed v3 corpus; never claim a fresh blind holdout."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from scripts.active_rectification_event_engine import AYANAMSA, NODE_MODE
|
||||
from scripts.minute_rectification_blind_eval import (
|
||||
_candidate_moments, _clock_distance, _opaque_winner, _request,
|
||||
implementation_sha256, summarize_trials,
|
||||
)
|
||||
from scripts.minute_rectification_fact_blind_eval_v4 import _would_confirm
|
||||
from scripts.minute_rectification_fact_ranker_v4 import (
|
||||
ALGORITHM_VERSION, rank_fact_rows, score_fact_ranker_v4,
|
||||
)
|
||||
from scripts.minute_rectification_feature_facts_v4 import build_feature_fact_rows
|
||||
from scripts.minute_rectification_holdout_validator import validate
|
||||
|
||||
DATASET = ROOT / "references/real_case_calibration/minute_rectification_holdout_v3.json"
|
||||
FREEZE = ROOT / "docs/research/sealed_holdout_rerun_2026_09_20.freeze.json"
|
||||
REPORT = ROOT / "docs/research/sealed_holdout_rerun_2026_09_20.json"
|
||||
|
||||
|
||||
def file_sha256(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def opaque_order(benchmark_id: str, case_id: str, rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""Extend the existing opaque winner to a total, truth-independent ranking."""
|
||||
return sorted(rows, key=lambda row: (
|
||||
-row["score"],
|
||||
hashlib.sha256(f"{benchmark_id}:{case_id}:{row['time']}".encode()).hexdigest(),
|
||||
))
|
||||
|
||||
|
||||
def freeze_record(dataset: Path = DATASET) -> dict[str, Any]:
|
||||
manifest = json.loads(dataset.read_text(encoding="utf-8"))
|
||||
files = manifest["frozen_scoring"]["files"]
|
||||
return {
|
||||
"record_version": "exposed-v3-fixed-protocol-rerun-v1",
|
||||
"frozen_at_utc": datetime.now(timezone.utc).isoformat(),
|
||||
"dataset_path": dataset.relative_to(ROOT).as_posix(),
|
||||
"dataset_sha256": file_sha256(dataset),
|
||||
"algorithm_version": ALGORITHM_VERSION,
|
||||
"implementation_sha256": implementation_sha256(files),
|
||||
"files": files,
|
||||
"historical_frozen_sha256": manifest["frozen_scoring"]["implementation_sha256"],
|
||||
"evaluator_sha256": file_sha256(Path(__file__)),
|
||||
"ayanamsa": AYANAMSA,
|
||||
"node_mode": NODE_MODE,
|
||||
"candidate_radius_minutes": sorted({case["candidate_radius_minutes"] for case in manifest["cases"]}),
|
||||
"minute_step": 1,
|
||||
"release_metrics": manifest["release_metrics"],
|
||||
"results_previously_seen": True,
|
||||
"official_valid_independent_blind": False,
|
||||
"must_not_use_for_tuning": True,
|
||||
"tie_breaker": manifest["frozen_scoring"]["tie_breaker"],
|
||||
"metric_rank_definition": "competition_rank_1_plus_strictly_higher_scores_legacy_protocol",
|
||||
"extra_metric_rank_definition": "score_desc_then_existing_opaque_sha256_total_order",
|
||||
}
|
||||
|
||||
|
||||
def run(freeze_path: Path = FREEZE, dataset: Path = DATASET) -> dict[str, Any]:
|
||||
frozen = json.loads(freeze_path.read_text(encoding="utf-8"))
|
||||
actual = freeze_record(dataset)
|
||||
for key in actual:
|
||||
if key != "frozen_at_utc" and actual[key] != frozen.get(key):
|
||||
raise ValueError(f"frozen_record_mismatch:{key}")
|
||||
manifest = json.loads(dataset.read_text(encoding="utf-8"))
|
||||
validation = validate(dataset)
|
||||
invalid = validation["invalid_cases"]
|
||||
trials = []
|
||||
for index, case in enumerate(manifest["cases"], 1):
|
||||
if case["case_id"] in invalid:
|
||||
continue
|
||||
request = _request(case, case["events"])
|
||||
candidates = _candidate_moments(case)
|
||||
facts = build_feature_fact_rows(request, candidates=candidates)
|
||||
rows, _ = rank_fact_rows(facts, request["events"])
|
||||
result = score_fact_ranker_v4(facts, request["events"])
|
||||
predicted = _opaque_winner(manifest["benchmark_id"], case["case_id"], rows)
|
||||
ordered = opaque_order(manifest["benchmark_id"], case["case_id"], rows)
|
||||
sparse_request = _request(case, case["events"][:1])
|
||||
sparse_facts = build_feature_fact_rows(sparse_request, candidates=candidates)
|
||||
sparse_result = score_fact_ranker_v4(sparse_facts, sparse_request["events"])
|
||||
# Truth is revealed only after both full and sparse ranking/decisions.
|
||||
truth = case["birth"]["time"]
|
||||
truth_score = next(row["score"] for row in rows if row["time"] == truth)
|
||||
would_confirm = _would_confirm(result)
|
||||
trials.append({
|
||||
"case_ordinal": index,
|
||||
"candidate_count": len(rows),
|
||||
"event_count": len(request["events"]),
|
||||
"true_rank": 1 + sum(row["score"] > truth_score for row in rows),
|
||||
"opaque_true_rank": next(i for i, row in enumerate(ordered, 1) if row["time"] == truth),
|
||||
"minute_error": _clock_distance(predicted, truth),
|
||||
"would_confirm": would_confirm,
|
||||
"false_confirmation": would_confirm and predicted != truth,
|
||||
"insufficient_evidence_rejected": not _would_confirm(sparse_result),
|
||||
"full_trial_reasons": result["reasons"],
|
||||
"sparse_trial_reasons": sparse_result["reasons"],
|
||||
})
|
||||
aggregate = summarize_trials(trials, manifest["release_metrics"])
|
||||
count = len(trials)
|
||||
return {
|
||||
"scope": "fixed_protocol_previously_exposed_v3_rerun",
|
||||
"evaluated_on": datetime.now(timezone.utc).date().isoformat(),
|
||||
"frozen_record": frozen,
|
||||
"implementation_hash_matches_at_replay": True,
|
||||
"dataset_hash_matches_at_replay": True,
|
||||
"source_audit_status": manifest["source_audit_status"],
|
||||
"validation_status": validation["status"],
|
||||
"valid_public_aa_cases": validation["valid_public_aa_cases"],
|
||||
"excluded_cases": invalid,
|
||||
"trial_count": count,
|
||||
"trials": trials,
|
||||
**aggregate,
|
||||
"opaque_exact_top_1_rate": sum(row["opaque_true_rank"] == 1 for row in trials) / count if count else None,
|
||||
"opaque_exact_top_3_rate": sum(row["opaque_true_rank"] <= 3 for row in trials) / count if count else None,
|
||||
"official_valid_independent_blind": False,
|
||||
"official_blind_trial_count": 0,
|
||||
"is_blind_evaluation": False,
|
||||
"truth_hidden_from_ranker": True,
|
||||
"results_previously_seen": True,
|
||||
"verified_minute_claim_allowed": False,
|
||||
"status": "blocked_independent_blind_evidence",
|
||||
"boundary": "Three-event low-information protocol, not a mathematical accuracy lower bound and not representative of real sessions. Historical v3/v4 exposure cannot be undone by freezing today's scorer. No tuning or release claims.",
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--freeze", action="store_true", help="Create a new record before replay; never overwrite an existing record")
|
||||
parser.add_argument("--freeze-path", type=Path, default=FREEZE)
|
||||
parser.add_argument("--json", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if args.freeze:
|
||||
with args.freeze_path.open("x", encoding="utf-8", newline="\n") as handle:
|
||||
json.dump(freeze_record(), handle, ensure_ascii=False, indent=2)
|
||||
handle.write("\n")
|
||||
print(args.freeze_path.relative_to(ROOT).as_posix())
|
||||
else:
|
||||
print(json.dumps(run(args.freeze_path), ensure_ascii=False, indent=2))
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
Purpose: read this file before substantial project work. It exists to stop repeat mistakes caused by multiple Codex windows, WorkBuddy mirrors, local drafts, backup folders, and partial cloud-git visibility.
|
||||
|
||||
## 2026-09-20 · 跨午夜生产修复的身份与本机预检
|
||||
|
||||
- 任务书的“V9 `engine_version` 全仓只写不比”不完整:成功回执来自实际 `algorithmVersion`,已有 score cache 通过版本接口/环境覆盖比较算法身份。只 bump 前端默认串不能保证成功结果与旧缓存可区分;本单同步 bump 既有后端算法身份,不新增历史会话打开门,不重标旧缓存。
|
||||
- 新工作树预检仍为远端 verified、适配器/碎片检查成功、focused 镜像路径断言失败;快速门基线与本轮均在 source inventory 因缺 `mcp` 失败。BUG-089 的 `mcp>=1.0,<2` 约束两处仍在,本机问题是未安装,不是再次升到 MCP 2。
|
||||
- 广域校正 Python 基线已有 4 个失败,必须逐条对照,不借机修改历史冻结值或削弱旧断言。验收记录见 `docs/tasks/PROGRESS-rectification-cross-midnight-20260920.md`。
|
||||
|
||||
## 2026-09-20 · 校正验证补缺的本机验收复现
|
||||
|
||||
- 基线与实现工作树均使用 Python 3.11.7(无项目 `.venv`;`python3` launcher 退出 49)。开工预检远端 verified,但同一碎片镜像路径断言失败;不得把同步成功写成预检通过。
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# 申报偏差敏感性评测(2026-09-20)
|
||||
# 申报偏差敏感性评测(2026-09-20,BUG-981 后原生单矩阵重跑)
|
||||
|
||||
> 当前正式结果:`reported_offset_cross_midnight_2026_09_20.json`,冻结:`reported_offset_cross_midnight_2026_09_20.final.freeze.json`。旧 `reported_offset_2026_09_20.json` 原字节保留,不再充当当前身份报告。修订前本 Markdown、旧 JSON/冻结/契约/评测器逐字节保留在 `history/rectification_pre_cross_midnight_2026_09_20/`,由 manifest 固定哈希。
|
||||
|
||||
## 结论与边界
|
||||
|
||||
@@ -6,7 +8,7 @@
|
||||
|
||||
本轮完整评测预先固定 15 个偏移 × 3 档半径 × 20 例,共 900 组合。修正版全部运行完成,validator 排除 0 例;实测结果以下列最终 JSON 为准,不能用中途旧版本初扫代替。
|
||||
|
||||
**区间覆盖不等于分钟命中,模拟偏差敏感性不等于真实用户准确率。** 不运行六题真值方向回放,不以用户认可作真值,不调参、不改生产打分、不打开确认门。
|
||||
**区间覆盖不等于分钟命中,模拟偏差敏感性不等于真实用户准确率。** 不运行六题真值方向回放,不以用户认可作真值,不调参、不换案例、不打开确认门。本轮生产代理单独修正候选日期与版本标记;本研究使用修复后的原生路径,不增加评分规则。
|
||||
|
||||
## 口径
|
||||
|
||||
@@ -22,12 +24,12 @@
|
||||
| 额外身份 | JSON 的 `production_scoring_sha256` 与 `research_implementation_sha256` 分别绑定扩展评分文件和研究适配文件;显式文件清单不是完整传递依赖锁 |
|
||||
| 头名与排名 | 分数降序,再用既有 `_opaque_winner` 同源 SHA-256 规则排同分;不选择最接近真值的候选 |
|
||||
| 交付区间 | 初始未答题:signature clusters 中落后头名不足 8 分的仍有效簇外包范围;无新回答、无淘汰,不是完整问答链交付效果 |
|
||||
| 跨午夜 | 候选、误差及区间使用完整日期;离线矩阵按候选日期分组,合并后全窗排序/聚类 |
|
||||
| 跨午夜 | 候选、误差及区间使用完整日期;修复后的原生单矩阵内按候选日期计分,全窗排序/聚类;不再运行日期分组适配 |
|
||||
| 独立性 | `is_blind_evaluation=false`,已曝光集;评分/区间定稿后才揭示标签,仅证明程序标签隔离 |
|
||||
|
||||
## 实测表
|
||||
|
||||
每格依次为 **真值在窗比例 / 哈希头名命中 / 初始交付区间覆盖**,分母均为 20 例。列为申报偏移分钟,行为搜索半径。统一口径:v3、raman/mean、1 分钟步长;历史打分身份 `b15d9ea15227cd58`,扩展原生评分身份 `115c3fbcffdaff49`,研究适配身份 `8d0c613dc0900adb`。
|
||||
每格依次为 **真值在窗比例 / 哈希头名命中 / 初始交付区间覆盖**,分母均为 20 例。列为申报偏移分钟,行为搜索半径。统一口径:v3、raman/mean、1 分钟步长;历史 12 文件打分身份 `b15d9ea15227cd58`,扩展原生评分身份 `7fffd1db612af1f2`,研究评测身份 `2931d4661bf53dde`。
|
||||
|
||||
| 半径 | -30 | -20 | -15 | -10 | -8 | -5 | -3 | 0 | +3 | +5 | +8 | +10 | +15 | +20 | +30 |
|
||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||
@@ -46,11 +48,25 @@
|
||||
- 半径 ±30 自第 31 个整数分钟偏移出窗,±60 自第 61 个、±120 自第 121 个。放宽不是无条件保证,更不代表排名命中或交付覆盖同样恢复。
|
||||
- ±120 未跑评分,不报告其头名命中率或交付覆盖率。真实用户申报偏差分布未知,不估算现实用户中有多少人能被放宽救回。
|
||||
|
||||
## 初扫作废与独立复核
|
||||
## 历史初扫、日期分组版与本轮原生重跑
|
||||
|
||||
首版正确枚举了跨日候选,但矩阵内 transition proximity 仍共用窗口起日,独立审查发现跨日候选得分偏移。因此旧初扫作废,修正版标记 `replay_revision=candidate_date_grouped_v2`,并登记 `supersedes=initial_sweep_invalidated_cross_midnight_transition_anchor`。
|
||||
历史初扫正确枚举跨日候选,但矩阵内 transition proximity 共用窗口起日,因此初扫作废。其后已修成 `candidate_date_grouped_v2` 离线日期分组版,旧同名 JSON 是这个修正版,不是错误初扫。修正版旧身份:扩展原生评分 `115c3fbcffdaff49`,研究适配 `8d0c613dc0900adb`,旧数值不能被误说成必然错误。
|
||||
|
||||
修复仅在本次新脚本 `score_window()` 做按日期分组适配,未改冻结打分文件。实引擎回归验证跨日窗口的分组计算与逐候选独立重算相同;聚类和交付范围在合并后全窗运行。**生产日期处理未在本单修复,不能称为生产端到端回放。**
|
||||
本轮 `replay_revision=native_candidate_date_v3`:生产 helper 已修,`score_window()` 改为一次原生矩阵调用;回归同时锁定调用次数为 1、与逐候选独立真实引擎重算相等。不再保留运行中的日期分组替代路径。旧数字因实现/路径身份更新不再代表当前报告,是否变化只由完整比对决定,不能预设数字必变。
|
||||
|
||||
**这里只保证 matrix scoring path 是修复后的原生单次调用,不是完整生产端到端回放。** `shifted_window()` 使用完整日期,`delivery_moments()` 按完整日期定义研究交付外包区间;它们仍是离线协议。协调独立审查另报 BUG-982/983:生产跨午夜簇/span 的钟点排序与前端早凌晨申报窗日期锚点仍有独立缺陷。本研究不覆盖这些路径,不能凭区间覆盖数字声称线上所有跨日问题已修。
|
||||
|
||||
## 本轮预冻结身份
|
||||
|
||||
| 项目 | 值 |
|
||||
| --- | --- |
|
||||
| 冻结时间(先于评分) | `2026-09-20T04:56:55.260470+00:00` |
|
||||
| 原 12 文件 | `b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18` |
|
||||
| 扩展生产 18 文件 | `7fffd1db612af1f244a8b5d5eac2f1a3f5a8e9e138c09ac6b3743b98f9a47a51` |
|
||||
| 研究/校验 7 文件 | `2931d4661bf53dde5c57f6492de65be4c00877293b7277353279a05eda5af215` |
|
||||
| 冻结文件 SHA-256 | `1f281b6c3e9b5a7dc2de91fb2ae1fd94da38f9b8a7bb1f930f670e9a39865074` |
|
||||
|
||||
原 12 文件未变,但实际变化的 `scoring_service.py` 与 `dasha_transition_proximity.py` 都进入扩展身份。冻结亦绑定数据集、所有组合/参数、每文件字节哈希、归档 manifest 和评测器;评分前后核对,漂移即拒绝,不能事后刷新冻结。准备阶段曾因旧 JSON 覆盖被安全工具拒绝而迁移新报告路径,准备产物及弃用原因由历史目录 `preparation_manifest.json` 保留;最终路径评测器另行预冻结,没有追认旧冻结。
|
||||
|
||||
## 可复算产物与验收
|
||||
|
||||
@@ -59,6 +75,19 @@ python scripts/research/reported_offset_sweep.py --json
|
||||
python -m pytest tests/test_reported_offset_research.py tests/test_rectification_validation_integrity_gate.py -q
|
||||
```
|
||||
|
||||
`reported_offset_2026_09_20.json` 保留逐例四项必要输出:真值是否在窗、真值排名、头名分钟误差、交付区间覆盖,以及每个格子的分母、全部参数和实现哈希。仅保存公开集序号,不保存出生分钟、坐标或事件正文。
|
||||
`reported_offset_cross_midnight_2026_09_20.json` 保留逐例四项必要输出:真值是否在窗、真值排名、头名分钟误差、交付区间覆盖,以及每个格子的分母、全部参数和实现哈希。仅保存公开集序号,不保存出生分钟、坐标或事件正文。
|
||||
|
||||
**最终全量 900/900 组合完成,用时 399.902 秒;排除 0 例。与旧日期分组版逐例全部字段相同,改变 0/900,45/45 汇总格一致。** JSON 的 `historical_comparison.trials` 保留所有 before/after 与 changed_fields(含未变化行)。这符合旧最终版已经做日期分组修正的事实,不表示生产旧 helper 没有 Bug。本轮没有调参、删失败样本、换半径或把已曝光集包装成新独立盲测。
|
||||
|
||||
### 既有测试断言变更
|
||||
|
||||
| 原值 / 原断言 | 新值 / 新断言 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| 跨日实引擎 `grouped == expected` 且生产 `old_rows != expected` | 原生单矩阵 `native == expected` 且 `native_rows == expected`,并锁一次矩阵调用 | 原测试第二条锁住的是已知生产缺陷;修复后必须相等。独立逐候选真实引擎 oracle 原样保留并强化,不是删掉差异验证 |
|
||||
| `replay_revision=candidate_date_grouped_v2` | `native_candidate_date_v3` | 原生路径已修,不再离线分组 |
|
||||
| 记录测试读取旧 JSON | 读取脚本新 `REPORT` 路径,全部数据集/评测器/生产/研究哈希断言保留 | 旧产物原字节留档,新报告独立创建 |
|
||||
| 仅运行后记录参数身份 | 预冻结完整身份,评分前后核对;新增漂移提前拒绝、时间先后、完整旧新对比断言 | 不能事后伪造冻结或只比较有利样本 |
|
||||
|
||||
本轮定向验证:`test_reported_offset_research.py`、`test_sealed_holdout_contract_freshness.py`、`test_rectification_validation_integrity_gate.py`、`test_rectification_confirmation_and.py` 合计 **52/52 通过**(含桥接重复收集)。独立逐行比较新旧 900 trials、45 summary 全相同;当前报告 SHA-256 为 `26c3d4a6e5e96ffaacdd00c64b5f3ed08fb9eacb18ef6fffc4b01c15159207b4`。旧产物与准备产物 manifest 的全部字节校验通过。预检因既有 Windows 镜像路径断言失败(23 通过 / 1 失败),不是预检通过。完整环境/广域验收由 `../tasks/PROGRESS-rectification-cross-midnight-20260920.md` 记录。
|
||||
|
||||
排名不能与 T2 的竞争排名 top-1/top-3 混比:T2 按旧协议允许并列多个分钟同时算第一;本表把同分完全按哈希打破。最终测试数字与已知基线失败见 [进度](../tasks/PROGRESS-rectification-validation-20260920.md)。真实分布采集与新未曝光样本见 [v5 协议](sealed_holdout_v5_protocol_2026_09_20.md)。
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"record_version": "reported-offset-native-candidate-date-v3",
|
||||
"frozen_at_utc": "2026-09-20T04:56:55.260470+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": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"implementation_sha256_prefix": "b15d9ea15227cd58",
|
||||
"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/jaimini.py",
|
||||
"scripts/minute_rectification_fact_ranker_v4.py",
|
||||
"scripts/minute_rectification_feature_facts_v4.py",
|
||||
"scripts/narayana_dasha.py",
|
||||
"scripts/rectification/candidate_contrast.py",
|
||||
"scripts/rectification/case_holdout.py",
|
||||
"scripts/rectification/contracts.py",
|
||||
"scripts/rectification/dasha_transition_proximity.py",
|
||||
"scripts/rectification/event_probes.py",
|
||||
"scripts/rectification/scoring_service.py",
|
||||
"scripts/shadbala.py",
|
||||
"scripts/varga.py"
|
||||
],
|
||||
"production_scoring_sha256": "7fffd1db612af1f244a8b5d5eac2f1a3f5a8e9e138c09ac6b3743b98f9a47a51",
|
||||
"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": "2931d4661bf53dde5c57f6492de65be4c00877293b7277353279a05eda5af215",
|
||||
"file_sha256": {
|
||||
"scripts/active_rectification_event_engine.py": "edbe93ef3c8bd65f986139dba6b95b96d9b0fa9920341fbe677cd6f53e4db84f",
|
||||
"scripts/active_rectification_events.py": "03e333ccf1103caf685dfe50fe890ab1793e945037085f50ce834a4a3b232222",
|
||||
"scripts/ashtakavarga.py": "cc32776674cf60713634a360a6f09774b3af0ec5d0f650687f52b79597b0dfbe",
|
||||
"scripts/dasha_analyzer.py": "5e53b1b4d4c7414c08dd0e7716f120a63c70ec3428ec93547f64d55c580da0e1",
|
||||
"scripts/divisional_charts_extended.py": "73bd50a83ac444830940184d0988cea621855f91db76a3f1a232014b992eacea",
|
||||
"scripts/domain_calculation_service.py": "9ee955874eea806c7b56f4246d90009c02ee8318b8f3dfe1d7132b1c0b4262e1",
|
||||
"scripts/jaimini.py": "b98bf965603dd2e3cc871fe40f3c8184731a701875ca50fef3ca68e9682eda81",
|
||||
"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": "3d899a2cdd4825b8e32062443d487edcd2173a4ea55a36eb9f3abe587c0faf0f",
|
||||
"scripts/minute_rectification_holdout_validator.py": "b60b1c752e58672beaee8b36eb3b2348169726cda070ec969db0f13e7ed715af",
|
||||
"scripts/narayana_dasha.py": "7ff2c3238cd113b11b815e14b41967bd8e486ff62e36650e55613b9c79d1bfe3",
|
||||
"scripts/rectification/candidate_contrast.py": "10a360586359e72846593ef6d7c15a2cea79358245a34c288851406fe9e47ca8",
|
||||
"scripts/rectification/case_holdout.py": "fe0e699b617d9e216acdfeff87af4221c1d6f4c101bf33e1dbf14e2bcd75a8b5",
|
||||
"scripts/rectification/contracts.py": "fdd1a47b2e3dac8579e870a282ed74c68518b39f6c0576b27df766a4b8021143",
|
||||
"scripts/rectification/dasha_transition_proximity.py": "0afba494aa12899621173c3cc941c6c1d0be9cec0bdf4c4a1c9bcd004ab22de8",
|
||||
"scripts/rectification/event_probes.py": "45478ee3fdc36dc36e1a602bee5dd0627a5464275e370e64adb5826fe2aa58f5",
|
||||
"scripts/rectification/scoring_service.py": "0adf5700bb22dd8c4c3147027251210e21ec66a2c9f9a91217edba3ad9e5d1f5",
|
||||
"scripts/research/cluster_width_lib.py": "1d8f2ffa7795d62069deebb2de8e16749317037d4ed3f24a2c0092676e5cfcc6",
|
||||
"scripts/research/probe_supply_after_six.py": "098a5b4398d6f8d996917d809c2fc922ad64031d3b70660f8ef7c876aba92438",
|
||||
"scripts/research/reported_offset_sweep.py": "f81c681fd0fe257e90a761dfab370f623e5f8a881b62cc6b1c596cbd58800f8f",
|
||||
"scripts/research/sealed_holdout_rerun.py": "34a6724530fbecdecff9e5e07fa64980b8e8aff1fac2e000f4268debe1ee96e1",
|
||||
"scripts/shadbala.py": "912e0e6d169c2172aab85f71e4347e3e39193020777b43825b775d906400956b",
|
||||
"scripts/varga.py": "4331de5a25ea08729af91aae863943c4223183937f419d52dc63fd6fe7f27be6"
|
||||
},
|
||||
"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": "f81c681fd0fe257e90a761dfab370f623e5f8a881b62cc6b1c596cbd58800f8f",
|
||||
"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
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"record_version": "reported-offset-native-candidate-date-v3",
|
||||
"frozen_at_utc": "2026-09-20T04:50:20.160896+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": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"implementation_sha256_prefix": "b15d9ea15227cd58",
|
||||
"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/jaimini.py",
|
||||
"scripts/minute_rectification_fact_ranker_v4.py",
|
||||
"scripts/minute_rectification_feature_facts_v4.py",
|
||||
"scripts/narayana_dasha.py",
|
||||
"scripts/rectification/candidate_contrast.py",
|
||||
"scripts/rectification/case_holdout.py",
|
||||
"scripts/rectification/contracts.py",
|
||||
"scripts/rectification/dasha_transition_proximity.py",
|
||||
"scripts/rectification/event_probes.py",
|
||||
"scripts/rectification/scoring_service.py",
|
||||
"scripts/shadbala.py",
|
||||
"scripts/varga.py"
|
||||
],
|
||||
"production_scoring_sha256": "7fffd1db612af1f244a8b5d5eac2f1a3f5a8e9e138c09ac6b3743b98f9a47a51",
|
||||
"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": "61dbb47eb46f9a01c78096677ac6238e3a65e9674ce4f2514b18e1ca50c0b954",
|
||||
"file_sha256": {
|
||||
"scripts/active_rectification_event_engine.py": "edbe93ef3c8bd65f986139dba6b95b96d9b0fa9920341fbe677cd6f53e4db84f",
|
||||
"scripts/active_rectification_events.py": "03e333ccf1103caf685dfe50fe890ab1793e945037085f50ce834a4a3b232222",
|
||||
"scripts/ashtakavarga.py": "cc32776674cf60713634a360a6f09774b3af0ec5d0f650687f52b79597b0dfbe",
|
||||
"scripts/dasha_analyzer.py": "5e53b1b4d4c7414c08dd0e7716f120a63c70ec3428ec93547f64d55c580da0e1",
|
||||
"scripts/divisional_charts_extended.py": "73bd50a83ac444830940184d0988cea621855f91db76a3f1a232014b992eacea",
|
||||
"scripts/domain_calculation_service.py": "9ee955874eea806c7b56f4246d90009c02ee8318b8f3dfe1d7132b1c0b4262e1",
|
||||
"scripts/jaimini.py": "b98bf965603dd2e3cc871fe40f3c8184731a701875ca50fef3ca68e9682eda81",
|
||||
"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": "3d899a2cdd4825b8e32062443d487edcd2173a4ea55a36eb9f3abe587c0faf0f",
|
||||
"scripts/minute_rectification_holdout_validator.py": "b60b1c752e58672beaee8b36eb3b2348169726cda070ec969db0f13e7ed715af",
|
||||
"scripts/narayana_dasha.py": "7ff2c3238cd113b11b815e14b41967bd8e486ff62e36650e55613b9c79d1bfe3",
|
||||
"scripts/rectification/candidate_contrast.py": "10a360586359e72846593ef6d7c15a2cea79358245a34c288851406fe9e47ca8",
|
||||
"scripts/rectification/case_holdout.py": "fe0e699b617d9e216acdfeff87af4221c1d6f4c101bf33e1dbf14e2bcd75a8b5",
|
||||
"scripts/rectification/contracts.py": "fdd1a47b2e3dac8579e870a282ed74c68518b39f6c0576b27df766a4b8021143",
|
||||
"scripts/rectification/dasha_transition_proximity.py": "0afba494aa12899621173c3cc941c6c1d0be9cec0bdf4c4a1c9bcd004ab22de8",
|
||||
"scripts/rectification/event_probes.py": "45478ee3fdc36dc36e1a602bee5dd0627a5464275e370e64adb5826fe2aa58f5",
|
||||
"scripts/rectification/scoring_service.py": "0adf5700bb22dd8c4c3147027251210e21ec66a2c9f9a91217edba3ad9e5d1f5",
|
||||
"scripts/research/cluster_width_lib.py": "1d8f2ffa7795d62069deebb2de8e16749317037d4ed3f24a2c0092676e5cfcc6",
|
||||
"scripts/research/probe_supply_after_six.py": "098a5b4398d6f8d996917d809c2fc922ad64031d3b70660f8ef7c876aba92438",
|
||||
"scripts/research/reported_offset_sweep.py": "cdcd7b583d9ecee8a313c25cac795923da2f8af45875d572f0a3efbc25f5d071",
|
||||
"scripts/research/sealed_holdout_rerun.py": "33c294b21db0631a68068dcf135b70db7aae6a1a3e66414bb72959a448883465",
|
||||
"scripts/shadbala.py": "912e0e6d169c2172aab85f71e4347e3e39193020777b43825b775d906400956b",
|
||||
"scripts/varga.py": "4331de5a25ea08729af91aae863943c4223183937f419d52dc63fd6fe7f27be6"
|
||||
},
|
||||
"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": "cdcd7b583d9ecee8a313c25cac795923da2f8af45875d572f0a3efbc25f5d071",
|
||||
"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
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
||||
# 当前冻结实现的 v3 固定口径重跑(2026-09-20)
|
||||
# 当前冻结实现的 v3 固定口径重跑(2026-09-20,BUG-981 后重新冻结)
|
||||
|
||||
> 当前正式结果已迁到 `sealed_holdout_rerun_cross_midnight_2026_09_20.json`;旧同名 JSON 与旧 `.freeze.json` 原字节保留,不代表本轮身份。本文修订前的 Markdown、JSON、冻结记录、契约和评测器均逐字节归档在 `history/rectification_pre_cross_midnight_2026_09_20/`,由 `manifest.json` 校验。历史冻结不刷新、不追认。
|
||||
|
||||
## 结论
|
||||
|
||||
@@ -21,9 +23,18 @@
|
||||
| 排名标签隔离 | 排序和稀疏证据判定完成后才读取真值标签 |
|
||||
| 独立官方盲测 | `official_valid_independent_blind=false`;有效官方盲测试次仍为 0 |
|
||||
|
||||
冻结记录:`sealed_holdout_rerun_2026_09_20.freeze.json`。先以独占创建模式记录 12 个文件、数据集与评测器字节哈希,再执行评分;评分前验证全部身份,任何漂移即拒绝。历史 v3 `frozen_scoring` 原值 `f41c298dd6cdcebe…` 未改。
|
||||
本轮冻结记录:`sealed_holdout_rerun_cross_midnight_2026_09_20.final.freeze.json`。在 **2026-09-20T04:56:54.914584+00:00** 以独占创建模式预冻结,然后重跑。12 文件身份继续为 `b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18`;实际修复在这 12 文件之外,故增加以下身份,不能拿旧 12 文件哈希不变冒充生产未变:
|
||||
|
||||
脱敏逐例结果:`sealed_holdout_rerun_2026_09_20.json`,只含公开集序号、排名、误差、门禁原因,不含出生分钟、坐标或事件正文。复算:
|
||||
| 身份 | SHA-256 |
|
||||
| --- | --- |
|
||||
| 扩展生产评分(18 文件,含 `scoring_service.py` 与 `dasha_transition_proximity.py`) | `7fffd1db612af1f244a8b5d5eac2f1a3f5a8e9e138c09ac6b3743b98f9a47a51` |
|
||||
| 研究/校验(7 文件) | `2931d4661bf53dde5c57f6492de65be4c00877293b7277353279a05eda5af215` |
|
||||
| 本轮冻结文件 | `b74f70d21011f98b78f141535b1bfa49b20701418812a3ad97f9350cc793c67d` |
|
||||
| 旧产物归档 manifest | `6102a26a840be207b5858b3a4c0509274468ae9071e86d308cbbac7371d6864e` |
|
||||
|
||||
每个文件的字节哈希与有序文件集身份均在冻结记录内;评分前后都核对数据集、评测器与扩展实现。文件集为显式范围,不冒称完整传递依赖或环境锁。历史 v3 `frozen_scoring` 原值 `f41c298dd6cdcebe…` 未改。
|
||||
|
||||
脱敏逐例结果:`sealed_holdout_rerun_cross_midnight_2026_09_20.json`,只含公开集序号、排名、误差、门禁原因,不含出生分钟、坐标或事件正文。复算:
|
||||
|
||||
```bash
|
||||
python scripts/research/sealed_holdout_rerun.py --json
|
||||
@@ -58,7 +69,15 @@ python scripts/research/sealed_holdout_rerun.py --json
|
||||
- `source_audit_status` 对齐数据集;`evaluated_on` 表示最新固定口径重跑日期,并新增作用域说明。历史顶层指标仍有 `metrics_produced_by` 的旧日期与来源,未伪装成本次结果。
|
||||
- `status`、`valid_public_aa_cases`、`required_cases`、`top_1_rate`、`confirmation_coverage_rate`、`sealed_benchmark_id` 六个 runtime 键的值与类型全不变;尤其 `status=not_ready`、确认覆盖率为零。
|
||||
- `official_eval_implementation_hash_matches=false`、`official_eval_trial_count=0` 保留;新增重跑记录不冒充已重新获得独立性。
|
||||
- 未改任何冻结打分文件、生产判定、现有 `_candidate_moments()` 或 `request_from_case()`。
|
||||
- 本轮生产代理修复 transition-proximity 候选日期并将矩阵算法身份升为 `rectification-v5-matrix-scoring-8`,落在历史 12 文件之外。本研究未改权重、候选/事件/半径,未改 `_candidate_moments()` 或 `request_from_case()`。
|
||||
|
||||
## BUG-981 前后逐例对比
|
||||
|
||||
**20/20 例全部字段相同,改变 0 例;五项指标和附加 opaque 指标均不变。** 当前 JSON 的 `historical_comparison.trials` 保留全部 20 例 before/after 与 changed_fields,包括未变化行,并校验旧报告 SHA-256。不是只保留变化样本。
|
||||
|
||||
原因边界:本协议是 shadow fact ranker,`build_feature_fact_rows()` 原先已使用 `candidate_at` 日期,不调用被修复的 transition-proximity helper。扩展生产身份用于标识同一工作树上下文,不得写成该 shadow 路径执行了生产 helper。旧成绩因身份更新不再充当当前报告,但不能把数值本身说成错误;重新冻结也不恢复独立盲测资格。
|
||||
|
||||
准备阶段曾生成两份 `*.freeze.json` 和一个 `*.cross_midnight.rerun.json`。因安全工具拒绝覆盖旧 JSON,改用新的正式报告路径,停止未完成的 900 组合准备运行,评测器最终路径修改后重新冻结再完整重跑。所有准备产物保留并由历史目录的 `preparation_manifest.json` 标为 `superseded_preparation_not_final_report`;不得当最终结果。
|
||||
|
||||
## 防复发与既有断言变更
|
||||
|
||||
@@ -69,4 +88,16 @@ python scripts/research/sealed_holdout_rerun.py --json
|
||||
| Python / 前端:`current_tree_scorer.implementation_sha256` 等于旧 post-audit sidecar 的哈希 | 等于新固定口径报告 `frozen_record.implementation_sha256`,另验证当前树与新报告 | 契约当前树块的含义就是当前实现;继续绑定旧报告会强制过期。新增身份断言更严格,不弱化门禁 |
|
||||
| 官方标志 false、官方试次 0、六键等于历史报告、确认门全套行为断言 | 原值保留,另加固定口径重跑身份/试次和独立盲测 false | 固定口径重跑不是新的独立盲测,不借元数据迁移打开确认门 |
|
||||
|
||||
本机 Python 3.11.7 可跑;`python3` Windows 别名退出 49。该任务定向命令的最终测试清单与基线失败对照由 `PROGRESS-rectification-validation-20260920.md` 统一记录。特别是既有 v2 冻结身份测试不属本次刷新范围,不得为了通过而改其历史封存或断言。
|
||||
本轮既有测试调整三栏(此前 BUG-978 的三栏保留在上表):
|
||||
|
||||
| 原值 / 原断言 | 新值 / 新断言 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| `FREEZE` 指向旧 `sealed_holdout_rerun_2026_09_20.freeze.json`,当前 `REPORT` 指向旧同名 JSON | 指向新 `sealed_holdout_rerun_cross_midnight_2026_09_20.final.freeze.json` 与新 JSON;保留原完整身份相等断言 | 历史冻结不可刷新,新实现/评测器必须重新预冻结;旧文件继续逐字节校验 |
|
||||
| 当前树身份只核对 12 文件 | 保留 12 文件相等与数量断言,另核对生产 18 文件、研究 7 文件、每文件哈希、归档 manifest、契约及报告 | 修复位于旧 12 文件之外;新增身份漂移拒绝,不弱化原断言 |
|
||||
| 当前报告与冻结身份一致 | 原断言全部保留,另核对冻结时间早于评分开始、评分前后身份一致、全部旧新逐例对比、六键类型与值不变 | 防止事后追认、选样与误开确认门 |
|
||||
| Python 确认合同仅比较 legacy 实现身份 | 原断言保留,新增扩展身份相等与 helper 在文件集内 | 原 12 文件不变不足以证明当前修复已被绑定 |
|
||||
| 前端确认测试读取旧固定口径 JSON | 由 T4 协调改为新正式 JSON,原门禁与身份断言保留 | 报告地址迁移,不改变确认行为 |
|
||||
|
||||
本轮定向四文件合计 **52/52 通过**(含 quick 桥接重复收集),身份/历史字节/全部 before-after 独立复核通过。20 例重跑用时 **3.158 秒**;当前报告 SHA-256:`0122d3cd1545416e1de9029b912d665d130049e9ea768a8972d18bb917331d38`。
|
||||
|
||||
本机 Python 3.11.7 可跑;`python3` Windows 别名退出 49。开工预检 remote verified,但 23 通过 / 1 既有 Windows 镜像路径断言失败,不冒写预检通过。本轮测试清单与环境/广域基线对照由 `../tasks/PROGRESS-rectification-cross-midnight-20260920.md` 统一记录;前轮记录仍在 `../tasks/PROGRESS-rectification-validation-20260920.md`。既有 v2 冻结身份测试不属刷新范围,不得为了通过而改历史封存或断言。
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"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/jaimini.py",
|
||||
"scripts/minute_rectification_fact_ranker_v4.py",
|
||||
"scripts/minute_rectification_feature_facts_v4.py",
|
||||
"scripts/narayana_dasha.py",
|
||||
"scripts/rectification/candidate_contrast.py",
|
||||
"scripts/rectification/case_holdout.py",
|
||||
"scripts/rectification/contracts.py",
|
||||
"scripts/rectification/dasha_transition_proximity.py",
|
||||
"scripts/rectification/event_probes.py",
|
||||
"scripts/rectification/scoring_service.py",
|
||||
"scripts/shadbala.py",
|
||||
"scripts/varga.py"
|
||||
],
|
||||
"production_scoring_sha256": "7fffd1db612af1f244a8b5d5eac2f1a3f5a8e9e138c09ac6b3743b98f9a47a51",
|
||||
"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": "2931d4661bf53dde5c57f6492de65be4c00877293b7277353279a05eda5af215",
|
||||
"file_sha256": {
|
||||
"scripts/active_rectification_event_engine.py": "edbe93ef3c8bd65f986139dba6b95b96d9b0fa9920341fbe677cd6f53e4db84f",
|
||||
"scripts/active_rectification_events.py": "03e333ccf1103caf685dfe50fe890ab1793e945037085f50ce834a4a3b232222",
|
||||
"scripts/ashtakavarga.py": "cc32776674cf60713634a360a6f09774b3af0ec5d0f650687f52b79597b0dfbe",
|
||||
"scripts/dasha_analyzer.py": "5e53b1b4d4c7414c08dd0e7716f120a63c70ec3428ec93547f64d55c580da0e1",
|
||||
"scripts/divisional_charts_extended.py": "73bd50a83ac444830940184d0988cea621855f91db76a3f1a232014b992eacea",
|
||||
"scripts/domain_calculation_service.py": "9ee955874eea806c7b56f4246d90009c02ee8318b8f3dfe1d7132b1c0b4262e1",
|
||||
"scripts/jaimini.py": "b98bf965603dd2e3cc871fe40f3c8184731a701875ca50fef3ca68e9682eda81",
|
||||
"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": "3d899a2cdd4825b8e32062443d487edcd2173a4ea55a36eb9f3abe587c0faf0f",
|
||||
"scripts/minute_rectification_holdout_validator.py": "b60b1c752e58672beaee8b36eb3b2348169726cda070ec969db0f13e7ed715af",
|
||||
"scripts/narayana_dasha.py": "7ff2c3238cd113b11b815e14b41967bd8e486ff62e36650e55613b9c79d1bfe3",
|
||||
"scripts/rectification/candidate_contrast.py": "10a360586359e72846593ef6d7c15a2cea79358245a34c288851406fe9e47ca8",
|
||||
"scripts/rectification/case_holdout.py": "fe0e699b617d9e216acdfeff87af4221c1d6f4c101bf33e1dbf14e2bcd75a8b5",
|
||||
"scripts/rectification/contracts.py": "fdd1a47b2e3dac8579e870a282ed74c68518b39f6c0576b27df766a4b8021143",
|
||||
"scripts/rectification/dasha_transition_proximity.py": "0afba494aa12899621173c3cc941c6c1d0be9cec0bdf4c4a1c9bcd004ab22de8",
|
||||
"scripts/rectification/event_probes.py": "45478ee3fdc36dc36e1a602bee5dd0627a5464275e370e64adb5826fe2aa58f5",
|
||||
"scripts/rectification/scoring_service.py": "0adf5700bb22dd8c4c3147027251210e21ec66a2c9f9a91217edba3ad9e5d1f5",
|
||||
"scripts/research/cluster_width_lib.py": "1d8f2ffa7795d62069deebb2de8e16749317037d4ed3f24a2c0092676e5cfcc6",
|
||||
"scripts/research/probe_supply_after_six.py": "098a5b4398d6f8d996917d809c2fc922ad64031d3b70660f8ef7c876aba92438",
|
||||
"scripts/research/reported_offset_sweep.py": "f81c681fd0fe257e90a761dfab370f623e5f8a881b62cc6b1c596cbd58800f8f",
|
||||
"scripts/research/sealed_holdout_rerun.py": "34a6724530fbecdecff9e5e07fa64980b8e8aff1fac2e000f4268debe1ee96e1",
|
||||
"scripts/shadbala.py": "912e0e6d169c2172aab85f71e4347e3e39193020777b43825b775d906400956b",
|
||||
"scripts/varga.py": "4331de5a25ea08729af91aae863943c4223183937f419d52dc63fd6fe7f27be6"
|
||||
},
|
||||
"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-09-20T04:56:54.914584+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": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"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": "34a6724530fbecdecff9e5e07fa64980b8e8aff1fac2e000f4268debe1ee96e1",
|
||||
"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"
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"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/jaimini.py",
|
||||
"scripts/minute_rectification_fact_ranker_v4.py",
|
||||
"scripts/minute_rectification_feature_facts_v4.py",
|
||||
"scripts/narayana_dasha.py",
|
||||
"scripts/rectification/candidate_contrast.py",
|
||||
"scripts/rectification/case_holdout.py",
|
||||
"scripts/rectification/contracts.py",
|
||||
"scripts/rectification/dasha_transition_proximity.py",
|
||||
"scripts/rectification/event_probes.py",
|
||||
"scripts/rectification/scoring_service.py",
|
||||
"scripts/shadbala.py",
|
||||
"scripts/varga.py"
|
||||
],
|
||||
"production_scoring_sha256": "7fffd1db612af1f244a8b5d5eac2f1a3f5a8e9e138c09ac6b3743b98f9a47a51",
|
||||
"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": "61dbb47eb46f9a01c78096677ac6238e3a65e9674ce4f2514b18e1ca50c0b954",
|
||||
"file_sha256": {
|
||||
"scripts/active_rectification_event_engine.py": "edbe93ef3c8bd65f986139dba6b95b96d9b0fa9920341fbe677cd6f53e4db84f",
|
||||
"scripts/active_rectification_events.py": "03e333ccf1103caf685dfe50fe890ab1793e945037085f50ce834a4a3b232222",
|
||||
"scripts/ashtakavarga.py": "cc32776674cf60713634a360a6f09774b3af0ec5d0f650687f52b79597b0dfbe",
|
||||
"scripts/dasha_analyzer.py": "5e53b1b4d4c7414c08dd0e7716f120a63c70ec3428ec93547f64d55c580da0e1",
|
||||
"scripts/divisional_charts_extended.py": "73bd50a83ac444830940184d0988cea621855f91db76a3f1a232014b992eacea",
|
||||
"scripts/domain_calculation_service.py": "9ee955874eea806c7b56f4246d90009c02ee8318b8f3dfe1d7132b1c0b4262e1",
|
||||
"scripts/jaimini.py": "b98bf965603dd2e3cc871fe40f3c8184731a701875ca50fef3ca68e9682eda81",
|
||||
"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": "3d899a2cdd4825b8e32062443d487edcd2173a4ea55a36eb9f3abe587c0faf0f",
|
||||
"scripts/minute_rectification_holdout_validator.py": "b60b1c752e58672beaee8b36eb3b2348169726cda070ec969db0f13e7ed715af",
|
||||
"scripts/narayana_dasha.py": "7ff2c3238cd113b11b815e14b41967bd8e486ff62e36650e55613b9c79d1bfe3",
|
||||
"scripts/rectification/candidate_contrast.py": "10a360586359e72846593ef6d7c15a2cea79358245a34c288851406fe9e47ca8",
|
||||
"scripts/rectification/case_holdout.py": "fe0e699b617d9e216acdfeff87af4221c1d6f4c101bf33e1dbf14e2bcd75a8b5",
|
||||
"scripts/rectification/contracts.py": "fdd1a47b2e3dac8579e870a282ed74c68518b39f6c0576b27df766a4b8021143",
|
||||
"scripts/rectification/dasha_transition_proximity.py": "0afba494aa12899621173c3cc941c6c1d0be9cec0bdf4c4a1c9bcd004ab22de8",
|
||||
"scripts/rectification/event_probes.py": "45478ee3fdc36dc36e1a602bee5dd0627a5464275e370e64adb5826fe2aa58f5",
|
||||
"scripts/rectification/scoring_service.py": "0adf5700bb22dd8c4c3147027251210e21ec66a2c9f9a91217edba3ad9e5d1f5",
|
||||
"scripts/research/cluster_width_lib.py": "1d8f2ffa7795d62069deebb2de8e16749317037d4ed3f24a2c0092676e5cfcc6",
|
||||
"scripts/research/probe_supply_after_six.py": "098a5b4398d6f8d996917d809c2fc922ad64031d3b70660f8ef7c876aba92438",
|
||||
"scripts/research/reported_offset_sweep.py": "cdcd7b583d9ecee8a313c25cac795923da2f8af45875d572f0a3efbc25f5d071",
|
||||
"scripts/research/sealed_holdout_rerun.py": "33c294b21db0631a68068dcf135b70db7aae6a1a3e66414bb72959a448883465",
|
||||
"scripts/shadbala.py": "912e0e6d169c2172aab85f71e4347e3e39193020777b43825b775d906400956b",
|
||||
"scripts/varga.py": "4331de5a25ea08729af91aae863943c4223183937f419d52dc63fd6fe7f27be6"
|
||||
},
|
||||
"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-09-20T04:50:19.815357+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": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"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": "33c294b21db0631a68068dcf135b70db7aae6a1a3e66414bb72959a448883465",
|
||||
"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,137 @@
|
||||
# PROGRESS · 跨午夜候选 Dasha 日期修复(2026-09-20)
|
||||
|
||||
## 基线、授权与边界
|
||||
|
||||
- 用户要求执行远端 `dacd4044` 的任务;fetch 时远端已追加产品决策补充 `03cba4780a220b132cf4f06d22e14608aaf9ad3c`,本轮以该后代为基线。
|
||||
- 任务:`TASK-rectification-cross-midnight-dasha-20260920.md`。产品决策 A/B/C 已放行:修复候选级日期、重新冻结重跑、通过 V9 `engine_version` 区分结果。
|
||||
- 工作树 `.worktrees/rectification-cross-midnight-20260920`;分支 `codex/rectification-cross-midnight-20260920`。上一轮已提交的独立工作树只作基线读取与测试,不更改其源码。
|
||||
- 开工核对 Bug 最大号为 980;本单使用 BUG-981。已与任务书会话确认其未修改相关生产文件、未占用编号。本会话各实现任务文件所有权分开。
|
||||
- 不改打分权重、确认门、Skill 版本、V4 `INPUT_CONTRACT_VERSION`、数据库结构、workflow、依赖、DNS 或 main。
|
||||
- 可选回执实现哈希增强不做;采用默认 `engine_version` 与既有后端 `ALGORITHM_VERSION` 同步 bump,保留原环境覆盖语义。线上环境是否覆盖另行验收,不借用凭据。
|
||||
|
||||
## 执行中纠正的任务书事实
|
||||
|
||||
任务书 §1.4 的 `BUG-3315` 实为历史文档行号,对应记录是 BUG-198;相关开窗记录另有 BUG-114 / BUG-353,不新增不存在的编号。候选枚举与时段映射回归仍在,未覆盖后来加入的日期辅助项。
|
||||
|
||||
T4 追踪发现:`v9EngineVersion()` 默认值主要用于 started/failed 回执;成功 compare / diagnostics 回执使用实际 `algorithmVersion`。`liveEngineScoringIdentityFromEnv()` 还会把非粗版本的环境覆盖视为算法身份,`cachedEngineScoreIsReusable()` 已有算法身份相等比较,`score-persist.ts` 在身份与输入相同时可直接复用旧分数。因而任务书“全仓只写不比”并不准确,只改默认串不足以落实产品 C,甚至可能继续使用错误的缓存分数。
|
||||
|
||||
最小实现选择:在本单允许的 `scoring_service.py` 将既有 `ALGORITHM_VERSION` 从 `rectification-v5-matrix-scoring-7` 提升为 `rectification-v5-matrix-scoring-8`,前端默认标记同步;复用既有失效逻辑,不新增按版本拒绝打开历史会话的门,不修改 Skill、V4 input contract、SQL 或旧回执。成功结果保留实际来源身份,不能把旧缓存强行重新标成新版。环境若覆盖旧算法身份,部署验收仍需核实;没有受控访问时列为缺口。
|
||||
|
||||
## 必读与预检
|
||||
|
||||
已读错误台账、分钟分辨率研究结论、前轮偏差报告、任务书和 BUG-098 / 427 / 428 / 621 / 978 / 979 / 980。旧 BUG-098 的跨午夜保护针对候选枚举,不足以覆盖后来加入的 transition-proximity 日期参数;BUG-427/428 的实现归属与曝光边界继续有效。
|
||||
|
||||
本机运行 `python scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45`:实际 Python 3.11.7,有 pytest;无本工作树 `.venv`。远端 verified、碎片与适配器检查成功;focused tests 因既有 `test_preflight_fragment_scan_reports_authority_layers_and_risk_buckets` 的 `.workbuddy` 镜像路径断言失败。与前轮台账一致,不冒写预检全绿。
|
||||
|
||||
## 执行与验收结论
|
||||
|
||||
核心日期修复、重新冻结及完整重跑完成;T4 缓存/回执身份仍未闭环,因此整单未通过完整验收。产品随后要求“按分支推送,我远程 review”,本轮仅向独立分支 `codex/rectification-cross-midnight-20260920` 交付供评审,推送结果以远端 SHA 核对为准;不合入 staging,不部署,不以其他会话的 staging 部署证明本轮已发布。保留原测试基线 `03cba478`,不混入同期其他任务的提交。
|
||||
|
||||
| 项目 | 状态 | 证据/下一步 |
|
||||
| --- | --- | --- |
|
||||
| T1 修复前红测 | 通过 | 原实现 4 failed / 4 passed,包含跨年、闰日、一般午夜日期/缓存及真实引擎回归;同日 golden 原来即通过 |
|
||||
| T2 最小生产修复 | 核心日期修复通过;部署未验收 | 19 个同日窗口共 2299 候选分数与矩阵字节相同;真实跨日全 121 候选等于日期正确独立计算;性能中位 -0.87%;独立生产选择性测试 4 项通过 |
|
||||
| T3 冻结及重跑 | 完成,独立校验通过 | 900 组合与 20 例均完整重跑,旧新逐例变化均为 0;独立复算真实源码身份、历史字节、汇总与 Markdown 45 格全部一致 |
|
||||
| T4 回执版本 | 部分实现,整体验收未通过 | 默认与后端身份同步;minute正常版本探测失效旧cache、新Case成功回执/历史打开定向通过;旧block_scan缓存仍绕过版本检查,BUG-984另立补单 |
|
||||
| T5 记录、独立验收 | 本地记录与独立复核完成 | BUG-981~984、BLOCKED、CHANGELOG、测试清单与验收补单已记录;Python 广域 300 项/4 个基线失败,新增失败 0;T4/构建/部署缺口不写通过 |
|
||||
|
||||
所有报告保留已曝光数据边界,官方独立有效试次仍为 0;修复后数字是否变化以逐例复算为准,不能从实现改动推断数值必变。
|
||||
|
||||
## 红测与同窗对照证据
|
||||
|
||||
修复前输出(去掉所有案例参数,只保留测试标识):
|
||||
|
||||
```text
|
||||
FFF....F [100%]
|
||||
FAILED test_every_candidate_uses_own_date_and_caches_do_not_cross_dates[2000-01-01]
|
||||
FAILED test_every_candidate_uses_own_date_and_caches_do_not_cross_dates[2000-02-29]
|
||||
FAILED test_every_candidate_uses_own_date_and_caches_do_not_cross_dates[2000-12-31]
|
||||
FAILED test_real_cross_midnight_all_candidates_match_independent_dated_calculation
|
||||
4 failed, 4 passed in 7.32s
|
||||
```
|
||||
|
||||
日期为纯合成测试日期,不是案例出生资料。同日真实引擎 golden 修复前即绿。
|
||||
|
||||
性能与分数比较口径:公开已曝光 v3,raman / mean,半径 ±60、步长 1;20 个窗口各 121 候选。历史 12 文件打分身份 `b15d9ea15227cd58` 不变;新扩展生产身份 `7fffd1db612af1f2`。性能从原生静态上下文到矩阵及得分,5 对交替执行,不能与任务书另一环境的 8.6 秒直接比。
|
||||
|
||||
| 项 | 基线 | 修复后 |
|
||||
| --- | --- | --- |
|
||||
| 5 次耗时(秒) | 27.479 / 21.570 / 20.713 / 21.362 / 21.438 | 21.151 / 21.252 / 20.739 / 21.429 / 21.526 |
|
||||
| 耗时中位(秒) | 21.438497 | 21.252369(-0.8682%) |
|
||||
| 非跨日分数字节比较 | 19 个窗口,2299 候选 | 全部相同,矩阵字节亦同 |
|
||||
| 真实跨日窗口对日期正确独算 | 121 候选中 16 个跨日候选不同,同日 0 不同 | 全 rows / matrix 相同,差异 0 |
|
||||
| 该跨日窗口最大绝对分差 | 0.0267 | 0 |
|
||||
|
||||
这里是该公开样本的实测,不把算术上界或这个幅度推广到所有真实会话。可提交脱敏证据见 `docs/testing/rectification-cross-midnight-scoring-evidence-20260920.json`。
|
||||
|
||||
## 独立验收:明确未通过与另行问题
|
||||
|
||||
1. **BUG-984 / T4 未完整通过**:独立 TS 探针在无环境覆盖的 `block_scan` 历史缓存上实测 `cached:true`、旧算法 -7、HTTP 调用 0;真实工具路径写 started -8 / completed -7。SQL `max(engine_version)` 的源码核验显示聚合可能取 -8,不能据此证明重新评分。未真跑 SQL,因此数据库部分只报源码证据。补单 `TASK-rectification-cross-midnight-dasha-fix-20260920.md` 待批准,不擅改缓存政策/迁移。
|
||||
2. **BUG-982 / 既有聚类跨度**:纯虚构三分钟跨午夜簇沿 `build_candidate_decisions()` 输出 1440 分钟宽度;旧 BUG-624/639 的同日范围保护仍在,但不覆盖该跨日调用链。没有顺改候选淘汰或范围策略。
|
||||
3. **BUG-983 / 既有凌晨锚点**:前端凌晨申报的跨日钟点窗仍配原申报日期,生产枚举将起点绑当天、中心移到次日。helper 本轮只修与 `candidate_at` 一致,不能声称上游候选日期也已正确。
|
||||
|
||||
因此本轮可以分别验收“候选级日期修复”与“指定条件下版本身份”,不能说所有跨午夜路径端到端已闭环。BUG-981 未有部署证据前仍 investigating;BUG-982~984均 investigating,不冒标 resolved。
|
||||
|
||||
## 重新冻结与完整重跑
|
||||
|
||||
两份正式 freeze 均先于各自评分开始;评分结束再次核对真实当前文件身份。旧 8 份产物归档逐字节保留并有 manifest,原路径的两份旧 JSON 与旧 freeze 也未改;准备阶段冻结/试跑另标 superseded,不充当最终证据。
|
||||
|
||||
| 项目 | 结果 |
|
||||
| --- | --- |
|
||||
| 历史 12 文件打分身份 | `b15d9ea15227cd58`(不变) |
|
||||
| 新扩展生产 18 文件身份 | `7fffd1db612af1f2` |
|
||||
| 最终研究 7 文件身份 | `2931d4661bf53dde` |
|
||||
| 申报偏差重跑 | 900/900 组合完成、45 格、排除 0;旧新逐例所有保存字段 0 变化,耗时 399.902 秒 |
|
||||
| 固定口径 shadow 重跑 | 20/20 例完成、排除 0;逐例 0 变化,耗时 3.158 秒 |
|
||||
| T3 定向四文件 | 52/52 通过(包含 quick 桥接重复收集,不能算 52 个独立新增用例) |
|
||||
|
||||
数字未变不等于生产缺陷不存在:旧 T1 最终版已有离线按日期分组适配,新版矩阵走修复后的原生单调用;T2 的 shadow fact-ranker 本来按候选日期构事实,不走此次 helper。两者仍非完整生产问答/交付回放,也不是新的独立盲测。
|
||||
|
||||
独立只读审查已从真实文件复算 12/18/7 文件集及逐文件 SHA,核对历史 8 份归档与 Git 基线原字节、3 份 superseded 准备产物,以及两份正式冻结/报告/契约。900 组合完整、每格分母 20,summary 独立算术复算一致,Markdown 全部 45 格逐格吻合;20 例汇总与六项指标表亦同。独立审查另执行生产轻量 4 项、T3 选择性 5 项通过,未重复 900 评分;不把执行方的 52/52 冒称独立复跑。上表 399.902 秒为执行方外层耗时,JSON 内部 replay 区间为 399.819 秒,计时边界不同。
|
||||
|
||||
T2 数值口径:公开已曝光 v3、raman/mean、±10、步长 1、12文件打分身份 `b15d9ea15227cd58`,扩展生产上下文身份 `7fffd1db612af1f2`。竞争 top-1/top-3 0.45/0.50,哈希头名平均误差 6.45 分钟,三项仍未过原门槛;误确认 0、稀疏证据拒绝 1、确认覆盖 0;实际哈希头名命中 0.05。不得拿含并列 top-1 当唯一头名命中率,不因重新冻结增加官方盲测试次。
|
||||
|
||||
## 前端与确认门验收
|
||||
|
||||
基线测试工作树为已提交的 `932f2fff`;与开工 `03cba478` 之间只有两次任务文档提交,业务与测试代码相同。前端对照实际核对 1500 文件;没有安装/升级依赖或修改旧源码。以下最终轮覆盖报告路径更新,早期候选 77 失败只是阶段数据,不替代最终结果。
|
||||
|
||||
| 检查 | 基线 | 最终候选 |
|
||||
| --- | --- | --- |
|
||||
| TypeScript | 0 error | 0 error |
|
||||
| lint | 0 error / 120 warning | 0 error / 120 warning |
|
||||
| 全量前端 | 3486 总 / 3407 pass / 79 fail | 3493 总 / 3414 pass / 79 fail |
|
||||
| 新增版本与确认门定向 | — | 14/14 通过 |
|
||||
| BUG-621 + convergence + 新增版本 | 52 总 / 48 pass / 4 fail | 59 总 / 55 pass / 4 fail |
|
||||
| build | 外部 node_modules junction 超出 Turbopack filesystem root | 同原因失败;Static/gzip 未验收 |
|
||||
|
||||
全量 79 个失败标题与基线集合完全一致,新增失败 0;BUG-621 组合的 4 个失败标题也完全一致,为 Windows symlink EPERM。真实历史绑定 Skill 打开用例通过,但不是线上登录/DB E2E。Docker可用,部分全量失败涉及网络地址池/环境,不能写成无Docker。详见 `docs/testing/rectification-cross-midnight-frontend-results-20260920.json`。
|
||||
|
||||
主会话独立运行新增日期回归、桥接与隐私守卫共 80 项通过;17 个受保护文件(12冻结评分文件及确认门、历史枚举/请求实现、主API)与基线逐字节相同。实际 `holdout_passed()` 仍 False,runtime 六键值及类型全部相同。最终文档阶段再跑真实冻结身份、确认门及三组隐私守卫,86 passed / 0 failed(13.17 秒);14 份本轮 JSON 解析成功,`git diff --check` 通过。
|
||||
|
||||
## Python 广域回归收尾
|
||||
|
||||
基线三个校正 glob 共 284 项,280 passed / 4 failed。当前首轮共 300 项,294 passed / 6 failed;原四项失败标识及实际断言值均未改变,新增两项为版本身份相关断言:`test_score_candidates_matches_baseline_golden` 的决策代表候选 ID 不同,以及 `test_holdout_gate_keeps_proportional_default` 仍要求旧算法 -7。已保留该轮失败证据,不把它算作通过。真实引擎同进程旧/新身份 A/B 已确认仅两项身份变化,精确迁移后主会话核对所有其他 golden 叶与基线相同;未整份重写 golden、未改变分数或比较器。原进程出现的三个静态特征 hash 差异在另外两个新进程复测消失,旧身份独立复测与旧 golden 全字段相等,未覆盖这些历史 hash。修正后三文件定向(memoization、relative-support、freshness)25 passed / 0 failed(2.12 秒);两个 final.freeze 所绑定的 12/18/7 文件集合均不含这三项测试/快照文件,真实字节身份验证仍绿。主会话独立复跑上述 25 项亦全部通过(2.04 秒)。广域全套另起日志重跑,不覆盖首轮失败证据,最终 4 failed / 296 passed / 300 total(609.52 秒、exit 1);四个失败标识与实际断言值均同基线,新增失败 0,收集数增加 16(含桥接),但不宣称全套全绿。
|
||||
|
||||
| Python 广域失败项 | 基线与最终对照 |
|
||||
| --- | --- |
|
||||
| `test_long_real_conversation_reaches_vedastro_after_local_range_is_narrow` | 既有 winning_segment 不符,实际/期望字段值未变 |
|
||||
| `test_selector_changes_do_not_change_legacy_or_v5_score_bytes` | 既有 V5 分数 hash 不符,最终实际仍 `e20199f79f4283da`,未改断言 |
|
||||
| `test_v3_development_result_stays_shadow_only` | 两侧均 `v3_improved_case_count` 实际 1 / 期望 0 |
|
||||
| `test_frozen_implementation_hash_matches_manifest` | 既有历史 v2 hash 不符,最终实际仍 `f642651ef4dec9b5`,未刷新历史冻结 |
|
||||
|
||||
脱敏计数、失败标识与中间轮证据见 `docs/testing/rectification-cross-midnight-20260920-results.json`。本机 quick 仍因缺 mcp 阻断;前端 build / Static / gzip、受控登录态、数据库回执聚合与部署验收仍缺,按补验清单处理。
|
||||
|
||||
## 既有断言调整三栏
|
||||
|
||||
| 原值 / 原断言 | 新值 / 新断言 | 原因 |
|
||||
| --- | --- | --- |
|
||||
| 研究跨日测试要求原生生产 rows 不等于日期正确结果 | 要求原生 rows 等于独立日期正确结果,并锁定单矩阵调用 | 原断言描述旧缺陷;本单修复后新增正确性保障,不再锁住已知错误 |
|
||||
| 当前研究/确认测试引用旧 JSON / freeze | 引用新 cross_midnight JSON / final.freeze;旧文件保留字节验证 | 原报告留作历史,新实现重新预冻结;门禁值与独立性断言不变 |
|
||||
| 当前树身份只包含历史 12 文件 | 保留原断言,新增 18 文件生产与 7 文件研究身份及漂移拒绝 | 本次修复在 12 文件之外,不能靠旧hash没变宣称当前身份一致 |
|
||||
| 前端 currentTreeRerun 来源为旧同名 JSON | 新 `sealed_holdout_rerun_cross_midnight_2026_09_20.json` | 只迁来源;所有确认行为断言保留 |
|
||||
| relative-support 测试要求算法身份 `rectification-v5-matrix-scoring-7` | 要求 `rectification-v5-matrix-scoring-8` | 本单已授权同步算法身份;同一测试的 proportional 默认与 policy-v3 断言保留 |
|
||||
| memoization golden 代表候选 UUID `85ca5490-07ab-54b7-acf0-59385f705015`、snapshot 算法 -7 | 真实引擎输出 UUID `e8c11277-022c-519c-95e9-6a0a04e8bf23`、snapshot 算法 -8 | 版本身份参与确定性 UUID;同进程旧/新身份 A/B 仅这两叶变化。精确更新两叶,不整份刷新;主会话独立逐叶核对所有其他值与 Git 基线相同,数值、历史特征 hash、比较器均保留 |
|
||||
|
||||
## 报告保存与权限边界
|
||||
|
||||
原两份 JSON 的 shell 覆盖被拒后,没有换执行者覆盖它们;改用独占新建的正式报告 `reported_offset_cross_midnight_2026_09_20.json` 与 `sealed_holdout_rerun_cross_midnight_2026_09_20.json`。旧 JSON / 旧 freeze 原字节保留;准备阶段产物与最终身份分开,修改 evaluator 路径后重新冻结再重跑,不拿之前的冻结追认新脚本。当前契约与测试读取新正式路径,原数字留作历史,不因新实现自动判旧数字算错。
|
||||
@@ -291,7 +291,13 @@
|
||||
| `TASK-owner-case-purge-20260919.md` | `PROGRESS-owner-case-purge-20260919.md` | **上游库主案例与本机路径残留清除(只含本仓)**:镜像同步带进库主本人案例(敏感案例标识与本机路径)并被 `origin/staging` 命中,涉及无引用的 `versions/` 三快照、前端 fixture、Python 测试、整机扫描台账、会话转录及上游 SKILL 快照。运行时无特判不用动。产品拍板整体删除不留匿名版;fixture 统一虚构常量;`import_yinduzhanxing.py` 加隐私排除项 + 新增仓库级隐私守卫测试;上游 SKILL 快照等库主清完再重导入(BLOCKED 记录)。上游仓的清理指令另见 `UPSTREAM-INSTRUCTION-owner-case-purge-20260919.md`(交给库主,不在本仓执行)。BUG-972/973 | **已验收通过(2026-09-20,`497798ac`);未合入 staging,等产品放行** | 两轮:首轮未通过(1 个隐私守卫冲突:新增路径规则与答案键守卫冲突,该守卫在快速门 glob 内,合入会让门禁红)→ 修复单 `TASK-owner-case-purge-fix2-20260919.md` → `497798ac` 通过。Claude 在 Linux 全依赖环境独立复验:快速门 Python 步 859 passed / 0 failed(上一轮就是这步红),Python 全量 63 红与基线逐条相同、0 新红,收集数与删除清单已记录;tsc 0 / lint 0 error、120 warning 同基线 / `npm test` 失败清单为基线子集(少 1 条工作流 YAML,非回归)/ `○ /` Static、首屏 gzip 与基线字节相同(前端自首轮提交起零改动)。上游指令文件已逐字节还原为基线原文;12 个已删测试名与计数已落进度记录;BLOCKED 两条已划掉。校正 Skill 哈希包、注册表、上游快照、`frontend/src`、主 API 全程 0 改动;库主本机用户名 0 命中。遗留 P3:进度记录和 BUG 状态文字待后续对账修正 |
|
||||
| `TASK-consult-smalltalk-fastpath-20260920.md` | — | **普通对话寒暄轮快速通道**:真机一句「你好」触发完整窗口排盘(活动面板「已完成 4 步」)+ `## 先回答你的问题` + 400 字判词 + 扣 1 点。三层叠加:`index.ts` 两处「every turn 必调排盘」(BUG-922)与 `contractReady()` 的 `requireTool` 把排盘变成硬合同;`product-voice.ts` OPENER SHAPE 标「三种模式共用」;唯一的 chit-chat 豁免句只在 `natalSpokenReportContract` 里、只拼进本命 Agent(BUG-977)。计费侧「写回复」与「扣点」绑在 `complete_consultation_response` 同一次调用,`cancel` 只退款不写消息,所以今天没有「不扣点但保留对话」的通道。**产品拍板 a:不扣点、不排盘、回一句白话**;**分流不得用正则/关键词/长度阈值**,改为进 Agent 之前一次极短的结构化模型调用(复用本轮已选模型),fail-open 一律落回完整路径;BUG-922/923 的三处合同一个字不改;新增 `complete_consultation_free` 迁移。BUG-976/977 | 待领取 | — |
|
||||
| `TASK-rectification-validation-integrity-20260920.md` | `PROGRESS-rectification-validation-20260920.md` | **生时校正验证体系补缺(纯离线评测,不改打分不改产品)**:会议要求把「推断真实出生时间」与「用户认可的参考盘」分开证明。核对结论——**产品口径侧四条已落地**(`accepted`≠`confirmed` 两条写入路径、确认门 fail-closed 且 `holdout` 为 `not_ready` 使 `confirmation_allowed` 不可能为真、采用不写 `reported_birth_time`、无任何把采用率当准确率的指标;运行时也无按生日走捷径的分支);**缺口全在评测本身**。三条:① 封存契约 `rectification_sealed_holdout.v1.json` 的三个打分哈希互不相同(封存 `f41c298d` / 契约记录 `99730c84` / 基线实测 `b15d9ea1`),`official_eval_trial_count: 0`——当前实现**从未产出过一次有效官方盲测**,唯一跑过那次已被资料审计作废(top-1 `0.15`,发布门要 `0.60`),可见的 `0.45` 自带「不得当发布指标」标记(BUG-978);② 全部离线评测的候选窗**以真值为圆心**(`_candidate_moments()`、`request_from_case()` 的 `true_time`),生产以申报时间为圆心(`ENGINE_SEARCH_RADIUS_MINUTES = 15`)——「真值掉出窗外」这一失败模式从不可见(BUG-979);③ v3 封存但每例仅 3 事件、v4 有 7+ 事件却已被看过并用于调参,**无口径干净又贴近真实会话的封存集**;六题回放的 `0.80/0.55/0.35` 是「真值方向最优答」的上帝视角上界,±30/±60 仍低于发布门(BUG-980)。T1 申报偏差敏感性 sweep、T2 有效重跑 + 契约对齐 + **防复发新测试**、T3 只出 v5 采集协议、T4 记录。**硬红线:不得改 12 个打分文件、不得用封存集调参、不得把 `status` 改 `ready`。** 家庭信息(父母职业/兄弟姐妹)在拿到基线数字前不开工——现有七领域全是带日期事件,静态属性没有输入口。前置:owner-case-purge 三提交仍未合入 staging。BUG-978~980 | **已验收通过(2026-09-20,Claude 独立复算)**;独立盲测仍 blocked;门禁/部署待核验 | `932f2fff`。Claude 独立复核:T1 完整重跑 900 组合**逐例 8 字段 0 差异**、summary/specification 全等,另从 900 条逐例重算 45 格表与发布表逐位相同;T2 独立复跑 0.45/0.50/6.45/0/1.0/0.0 与哈希全序 0.05/0.10 全部一致;12 个打分文件字节未动、`status=not_ready`、`confirmation_coverage_rate=0`、实跑 `holdout_passed()`=False;新增/触及 Python 测试本机 38 条全绿;freshness 测试比对时排除 `frozen_at_utc`(BUG-693/694 教训已用上)。**执行方正确推翻任务书 T2.3**:「首次口径干净的官方盲测」与 BUG-428 防复发(已看过结果的案例不得再计入盲测)冲突,已核 BUG-428 原文,是任务书写错。环境缺口:本机 frontend 无 node_modules,tsc/lint/npm test/build 未能复核,采信执行方自报。遗留 → `TASK-rectification-cross-midnight-dasha-20260920.md`(BUG-981) |
|
||||
| `TASK-rectification-cross-midnight-dasha-20260920.md` | — | **跨午夜候选的 Dasha 边界错一天(生产打分)**:`scoring_service.py` 调 `merge_transition_proximity()` 只传**一个** `birth_date`,该函数用它算全部候选的 Vimshottari / Narayana 起始日期,候选之间只靠 `_context_time()` 的 `HH:MM` 区分、**日期被丢掉**。窗口跨午夜时午夜后候选的 dasha 边界整体错一天。Claude 验收时在生产调用链独立复现:窗口 `23:50→00:10`、21 个候选,**恰好那 11 个跨日候选分数错、10 个同日候选逐位相同**(幅度 +0.0267 / −0.0133,本例头名未变)。算术上界 = `cap/kernel_width`,day 精度 0.067 分/件,18 件可累计约 1.2 分,而随分钟变化项总量仅约 2.1 分 —— **上界是推的不是实测,真实幅度本单必须实测**。踩中路径:`late_night` 时段 `23:00–03:59`、`unknown` `00:00–23:59`、23:45 后或 00:15 前申报的 ±15 窗。连带发现:`calculation_spec()` 不含打分实现身份,修复后同一 spec hash 对应不同分数,历史 Case 静默失去可复现性(同 BUG-427 类型)。**产品 2026-09-20 已就三点拍板:A 修、B 修完重新冻结并重跑 T1/T2、C 让新旧结果可区分。** C 的做法经查证已修正:`calculation_spec_hash` 全在 **V4** 链路、**V9 零引用**,原提案 bump `INPUT_CONTRACT_VERSION` 对真实历史无效已作废;改为随修复 bump `engine_version`(`v9EngineVersion()` 缺省串,全仓只写不比、无相等性门控),**不得动 `skill_version`**(BUG-621:open RPC 要求绑定 Skill 等于当前版本,bump 会让历史校正打不开)。硬红线:只改「按候选日期取 dasha 起始」,不得动 kernel/cap/share 任一常数;确认门不变。BUG-981 | **待领取**(产品已放行) | — |
|
||||
| `TASK-rectification-cross-midnight-dasha-20260920.md` | `PROGRESS-rectification-cross-midnight-20260920.md` | **跨午夜候选的 Dasha 边界错一天(生产打分)**:`scoring_service.py` 调 `merge_transition_proximity()` 只传**一个** `birth_date`,该函数用它算全部候选的 Vimshottari / Narayana 起始日期,候选之间只靠 `_context_time()` 的 `HH:MM` 区分、**日期被丢掉**。窗口跨午夜时午夜后候选的 dasha 边界整体错一天。Claude 验收时在生产调用链独立复现:窗口 `23:50→00:10`、21 个候选,**恰好那 11 个跨日候选分数错、10 个同日候选逐位相同**(幅度 +0.0267 / −0.0133,本例头名未变)。算术上界 = `cap/kernel_width`,day 精度 0.067 分/件,18 件可累计约 1.2 分,而随分钟变化项总量仅约 2.1 分 —— **上界是推的不是实测,真实幅度本单必须实测**。踩中路径:`late_night` 时段 `23:00–03:59`、`unknown` `00:00–23:59`、23:45 后或 00:15 前申报的 ±15 窗。连带发现:`calculation_spec()` 不含打分实现身份,修复后同一 spec hash 对应不同分数,历史 Case 静默失去可复现性(同 BUG-427 类型)。**产品 2026-09-20 已就三点拍板:A 修、B 修完重新冻结并重跑 T1/T2、C 让新旧结果可区分。** C 的做法经查证已修正:`calculation_spec_hash` 全在 **V4** 链路、**V9 零引用**,原提案 bump `INPUT_CONTRACT_VERSION` 对真实历史无效已作废;改为随修复 bump `engine_version`(执行查证:默认串主要写 started/failed;成功回执及已有 minute 缓存门实际使用后端算法身份,故同步 `ALGORITHM_VERSION`,不新增历史打开门;旧“全仓只写不比”假设作废),**不得动 `skill_version`**(BUG-621:open RPC 要求绑定 Skill 等于当前版本,bump 会让历史校正打不开)。硬红线:只改「按候选日期取 dasha 起始」,不得动 kernel/cap/share 任一常数;确认门不变。BUG-981 | **已实现核心修复,整体验收未通过;独立分支交付供远程 review,不合入 staging** | 基线 `03cba478`;日期红绿、同日逐位、性能与900/20重跑完成;成功回执/缓存实际使用算法身份,已同步后端与前端标记,但独立审查发现BUG-984时段旧缓存与聚合来源未闭环,T4不能全通过。BUG-982/983另登记既有跨日缺口;补单与环境对照见进度。 |
|
||||
|
||||
### 跨午夜执行轮独立验收补单
|
||||
|
||||
| 任务书 | 状态 | 范围 |
|
||||
| --- | --- | --- |
|
||||
| `TASK-rectification-cross-midnight-dasha-fix-20260920.md` | 待产品确认后实施 | BUG-984:时段旧缓存不做算法身份失效、聚合回执可能取开始阶段新版而掩盖旧成功结果;原任务 T4 不判全通过。BUG-982/983 为另行登记的簇跨度与凌晨日期锚点问题,不在此补单顺改。 |
|
||||
|
||||
## 命名与归档
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
# TASK · 跨午夜修复验收补单:缓存与结果身份(2026-09-20)
|
||||
|
||||
> 状态:待产品确认后实施;本文件仅记录验收未通过项,不代表批准扩大当前生产修复范围。原任务的候选级 Dasha 日期修复与评测继续完成,不能将本补单缺口冒写已通过。
|
||||
|
||||
## 0. 基线与串行依赖
|
||||
|
||||
- 原任务基线 `origin/staging = 03cba478`,前轮代码 `932f2fff`;待原任务最终提交后以其 SHA 为本单执行基线,不以未经确认的部署状态推导基线。
|
||||
- 依赖 `TASK-rectification-cross-midnight-dasha-20260920.md` 完成后串行执行。将涉及 `frontend/src/lib/rectification-agentic/v9/score-persist.ts`、回执查询及测试;不得与原任务 `engine-client.ts` 版本更新并行改同一文件。
|
||||
- 建议 worktree `.worktrees/rectification-cross-midnight-fix-20260920`,分支 `codex/rectification-cross-midnight-fix-20260920`。
|
||||
|
||||
## 1. 事故实证(按符号定位)
|
||||
|
||||
1. `scoreAndPersistV9Candidates()` 的 `block_scan` 分支先比较 `evidenceLedgerFingerprint`,命中即返回 `cached:true` 与缓存原 `algorithmVersion`;该返回发生在 `readV9EngineScoringIdentity()` 之前。跨午夜 late-night 时段可进入此分支,后端真实重算也确实会走本轮修复的 helper。故即使版本接口正常、后端已升级,旧时段分数仍可能被复用。
|
||||
2. `rectification-v9-tools.ts` 的 compare started 回执使用默认 engineVersion,成功回执使用实际 `persisted.algorithmVersion`。现有 `20260902020000_rectification_tool_activity_timing.sql` 的回执聚合用 `max(tr.engine_version)`,不是成功结果的身份。新版开始标记与旧缓存成功标记并存时,聚合可显示新版,不能证明新版重新算过。
|
||||
3. minute 分支已有算法身份相等缓存门,但 `readV9EngineScoringIdentity()` 可优先环境覆盖;仅有部分配置或版本接口失败时,不能保证实际旧缓存失效。不得把这些既有降级条件写成无条件安全。
|
||||
|
||||
## 2. 根因
|
||||
|
||||
- 时段与分钟评分缓存的身份条件不一致。
|
||||
- 聚合将运行阶段版本与计算结果来源版本混为同一可取字符串最大值的字段。
|
||||
- 任务书最初“全仓只写不比”的假设不成立;同步 bump 后端算法身份是必要但非充分条件。
|
||||
|
||||
## 3. 决策记录
|
||||
|
||||
产品原授权 C 要求新旧结果可区分,并禁止改 Skill 版本、V4 input contract 和新增历史打开相等门。本轮已同步既有后端算法身份和前端默认值,没有改缓存政策或数据库。
|
||||
|
||||
本补单建议:**所有评分缓存复用必须有实际计算身份;成功回执展示成功结果来源,不用开始阶段覆盖;无法取得可信当前身份时,不把旧缓存标为当前已验证结果。** 涉及版本接口失败时是否允许旧结果只读显示、是否新增向后兼容 SQL 迁移,需在本节由产品/架构明确后再实施,不能自行删历史结果或放宽门。
|
||||
|
||||
## 4. 硬红线
|
||||
|
||||
- 不重标旧结果为新算法,不删除历史回执/缓存来假装完成迁移。
|
||||
- 不 bump Skill,不改 V4 input contract,不引入按 engineVersion 拒绝打开历史会话。
|
||||
- 不调打分常数,不改确认门,不把已曝光重跑当独立盲测。
|
||||
- 如新增 SQL 迁移必须向后兼容当前已部署版本,真跑标准 DB 测试;不得原地改已应用迁移。
|
||||
- 不顺带修聚类跨度/凌晨日期锚点;它们分别为 BUG-982/983,需独立明确范围。
|
||||
|
||||
## 5. 任务分解与验收
|
||||
|
||||
### F1 · 先红测时段旧缓存
|
||||
|
||||
使用真实引擎脱敏 golden 构造旧算法时段缓存,同证据指纹、当前版本接口返回新身份。旧实现必须复现无网络重算仍返回旧结果;新实现不得把旧缓存作为当前分数复用。minute、block_scan 两条都覆盖,当前身份相同的正确缓存仍可命中。
|
||||
|
||||
### F2 · 成功回执身份
|
||||
|
||||
覆盖 started 新版本 / succeeded 旧缓存版本、diagnostics、失败重试与历史回执。用户看到的成功结果身份必须来自该成功结果,不能由字符串 `max` 决定。历史回执保持原值且旧会话仍可打开。若动 SQL,新增兼容迁移并真跑 `npm run test:db`。
|
||||
|
||||
### F3 · 失败与覆盖边界
|
||||
|
||||
分别测试无环境覆盖、完整覆盖、部分覆盖、版本接口超时/错误。按已批准策略说明何时重算、何时只读旧结果、何时拒绝复用;每种状态不得混淆当前身份与来源身份。
|
||||
|
||||
### F4 · 回归、记录与部署
|
||||
|
||||
前端 tsc/lint/相关与全量测试、build Static/gzip;数据库按需;BUG-621 历史打开回归必跑。更新 BUG-981 及缓存身份独立记录、BLOCKED、进度与测试清单。staging 受控会话分别验证 minute 与 late-night block_scan,不用匿名健康检查替代结果身份验收。
|
||||
|
||||
## 6. 让步顺序
|
||||
|
||||
先保证不伪造结果身份,其次保证缓存只复用兼容结果,再考虑缓存命中率;界面附加信息可延期,成功回执来源正确性不可砍。
|
||||
|
||||
## 7. 开工前置命令
|
||||
|
||||
先 `git status -sb`,再 fetch 并确认原任务最终 SHA;使用独立 worktree。读取 `AGENTS.md`、错误台账、前端三份规范、BUG-621/981 及本轮缓存身份记录;运行预检。未批准前不得执行迁移或推送生产。
|
||||
|
||||
## 8. BUG 编号
|
||||
|
||||
原日期修复为 BUG-981,附带既有范围/锚点问题为 BUG-982/983。本单缓存与聚合身份问题拟用 BUG-984;开工重核最大号与并行占用,按最终 Bug History 为准。
|
||||
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"scope": "cross_midnight_regression_acceptance",
|
||||
"baseline_commit": "03cba4780a220b132cf4f06d22e14608aaf9ad3c",
|
||||
"baseline_test_tree": "932f2fffba820a47fa4e1a7b825d6b38c310a4d6",
|
||||
"baseline_test_tree_reason": "The two later commits contain task documentation only; baseline executable sources are unchanged.",
|
||||
"python_globs": ["test_rectification_*.py", "test_active_rectification_*.py", "test_minute_rectification_*.py"],
|
||||
"baseline": {
|
||||
"collected": 284,
|
||||
"passed": 280,
|
||||
"failed": 4,
|
||||
"failure_ids": [
|
||||
"tests/test_active_rectification_api.py::test_long_real_conversation_reaches_vedastro_after_local_range_is_narrow",
|
||||
"tests/test_active_rectification_selector.py::test_selector_changes_do_not_change_legacy_or_v5_score_bytes",
|
||||
"tests/test_minute_rectification_development.py::test_v3_development_result_stays_shadow_only",
|
||||
"tests/test_minute_rectification_holdout_validator.py::test_frozen_implementation_hash_matches_manifest"
|
||||
]
|
||||
},
|
||||
"current": {
|
||||
"collected": 300,
|
||||
"passed": 296,
|
||||
"failed": 4,
|
||||
"seconds": 609.52,
|
||||
"exit_code": 1,
|
||||
"failure_ids": [
|
||||
"tests/test_active_rectification_api.py::test_long_real_conversation_reaches_vedastro_after_local_range_is_narrow",
|
||||
"tests/test_active_rectification_selector.py::test_selector_changes_do_not_change_legacy_or_v5_score_bytes",
|
||||
"tests/test_minute_rectification_development.py::test_v3_development_result_stays_shadow_only",
|
||||
"tests/test_minute_rectification_holdout_validator.py::test_frozen_implementation_hash_matches_manifest"
|
||||
],
|
||||
"baseline_only_failures": [],
|
||||
"current_only_failures": [],
|
||||
"baseline_failure_assertion_values_unchanged": true,
|
||||
"full_suite_passed": false,
|
||||
"zero_new_failures": true
|
||||
},
|
||||
"metadata_alignment_independent_targeted": {
|
||||
"passed": 25,
|
||||
"failed": 0,
|
||||
"seconds": 2.04
|
||||
},
|
||||
"pre_metadata_alignment_run": {
|
||||
"collected": 300,
|
||||
"passed": 294,
|
||||
"failed": 6,
|
||||
"baseline_failures_unchanged": true,
|
||||
"new_failure_ids": [
|
||||
"tests/test_rectification_engine_memoization.py::test_score_candidates_matches_baseline_golden",
|
||||
"tests/test_rectification_relative_support.py::RelativeSupportScaleTest::test_holdout_gate_keeps_proportional_default"
|
||||
],
|
||||
"observed_new_failures": [
|
||||
"decision_receipt.representative_candidate_id differs from the existing golden; root cause under verification",
|
||||
"ALGORITHM_VERSION is rectification-v5-matrix-scoring-8; existing assertion expects rectification-v5-matrix-scoring-7"
|
||||
]
|
||||
},
|
||||
"metadata_alignment": {
|
||||
"reason": "Authorized algorithm identity bump from rectification-v5-matrix-scoring-7 to rectification-v5-matrix-scoring-8",
|
||||
"golden_updated_paths": [
|
||||
"decision_receipt.representative_candidate_id",
|
||||
"candidate_feature_snapshot.algorithm_version"
|
||||
],
|
||||
"golden_source": "Two identity leaves extracted from the real engine after old/new identity A/B comparison",
|
||||
"all_other_golden_leaves_equal_to_git_baseline": true,
|
||||
"comparison_logic_unchanged": true,
|
||||
"relative_support_and_policy_assertions_unchanged": true,
|
||||
"version_literal_assertion_updated": true
|
||||
},
|
||||
"final_integrity_and_privacy_targeted": {
|
||||
"passed": 86,
|
||||
"failed": 0,
|
||||
"seconds": 13.17
|
||||
},
|
||||
"independent_research_audit": {
|
||||
"passed": true,
|
||||
"scope": "Real source hashes, archived bytes, 900/20 coverage and arithmetic summaries, 45 Markdown cells and final report references; not a second scoring rerun or end-to-end deployment test"
|
||||
},
|
||||
"preflight": {
|
||||
"remote_verified": true,
|
||||
"fragment_scan_ok": true,
|
||||
"external_engine_adapters_ok": true,
|
||||
"passed": false,
|
||||
"failed_test": "test_preflight_fragment_scan_reports_authority_layers_and_risk_buckets"
|
||||
},
|
||||
"quick": {
|
||||
"baseline_passed": false,
|
||||
"current_passed": false,
|
||||
"same_blocker": "interpretation_source_inventory_gate.py: ModuleNotFoundError: No module named 'mcp'"
|
||||
},
|
||||
"deployment_verified": false
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# 跨午夜修复 · 部署与受控环境验收(2026-09-20)
|
||||
|
||||
> 本清单不是通过证明。执行记录见 `../tasks/PROGRESS-rectification-cross-midnight-20260920.md`。不使用或索取真实用户出生资料,不借用账号或读取密钥。
|
||||
|
||||
## 发布身份
|
||||
|
||||
- [ ] Gitea 本轮含门禁路径提交的质量门成功;镜像 digest 与 deploy-staging 运行绑定同一提交。
|
||||
- [ ] staging `/api/health` 的 `deployment.gitCommit` 与 `apiGitCommit` 等于该提交;如 head 后续仅纯文档,执行 docs-only-range 检查。
|
||||
- [ ] `/login` 正常、匿名 `/api/account` 返回 401;在授权的内部健康检查中 `swisseph_available=true`。不得发布 API 内网端口。
|
||||
- [ ] 由授权运维只读核实 `RECTIFICATION_ENGINE_VERSION` 没有锁定修复前算法身份;不得输出环境文件。环境覆盖可能绕过默认串并使客户端版本探测优先使用旧身份。
|
||||
- [ ] 版本接口与新评分响应实际算法标记均为 `rectification-v5-matrix-scoring-8`,成功回执同源;不把仅 started 回执新版当作成功结果新版。
|
||||
|
||||
## 历史与缓存(受控测试账号、明确虚构数据)
|
||||
|
||||
- [ ] 打开既有校正会话:仍按其绑定 Skill 包身份运行,旧回执不被重新标成新版,不出现 `skill_identity_mismatch`。
|
||||
- [ ] 使用已有旧算法分数缓存的测试会话再次计算同一输入:分别覆盖 minute 与 block_scan。minute 在版本探测成功且无旧环境覆盖时不得把旧缓存当新版;**block_scan 当前仍仅检查证据指纹,未做身份失效,属于已确认未通过项,不能勾选通过**。
|
||||
- [ ] 分别检查成功回执原始来源与聚合展示,不能只看 started 的新版标记;现有 SQL `max(engine_version)` 可能覆盖成功阶段的旧版本,不得把聚合标签当作已重新计算的证据。
|
||||
- [ ] 创建新测试会话并完成评分:started 与 succeeded 回执均有实际版本,最终展示可区分新旧结果;不改变历史数据或 SQL。
|
||||
- [ ] 跨午夜申报窗口与 late-night 时段不被缩小或改映射;结果只作为候选参考,确认门仍关闭。
|
||||
|
||||
## 本机已知缺口的补验
|
||||
|
||||
- [ ] 在具备 mcp 等既有项目依赖的 Python 环境运行 `scripts/run_quality_gate.py --profile quick`;不得修改历史冻结哈希/弱化断言来求通过。
|
||||
- [ ] 使用完整依赖与有效 symlink 的前端环境运行 tsc、lint、相关及全量测试、build;首页 `/` 保持 Static,首屏 gzip 与本轮基线相比在 ±2%。
|
||||
- [ ] 若测试环境无 DB/浏览器/受控登录态,分别记为缺口;测试替身证明路由与身份传递,不替代线上数据库和真人开会话验收。
|
||||
@@ -0,0 +1,255 @@
|
||||
{
|
||||
"phase": "Final rerun after confirmation-gate report-path update; earlier candidate run retained separately",
|
||||
"baseline": {
|
||||
"tests": 3486,
|
||||
"pass": 3407,
|
||||
"fail": 79,
|
||||
"skipped": 0,
|
||||
"failures": [
|
||||
"admin code functions reject immutable codes, revoked redemption, and roll back on audit failure",
|
||||
"operation-level admin email reauthentication is removed from routes and UI",
|
||||
"rectification agent maps setup failures without calling the rest of the handler",
|
||||
"public code never imports the internal dynamic choice contract",
|
||||
"tests\\\\birth-time-guide-agent.test.ts",
|
||||
"candidate scores stay out of the specified client ownership boundary",
|
||||
"personal Agent exposes the Jyotish Skill and named server tool",
|
||||
"general agent runtime has no Jyotish skill package and no personal chart tool",
|
||||
"the route's own strict checklist reaches the answer, not just the package listing",
|
||||
"a route the skill declares no checklist for is reported, not filled in with another route's",
|
||||
"health uses the skill's health-timing-strict checklist",
|
||||
"wealth uses finance-timing-strict as the live checklist, with the wealth alias in the heading",
|
||||
"a multi-domain plan carries every executed route's checklist once",
|
||||
"no plan can spend the answer's context on method",
|
||||
"the widest legal plan still fits the budget",
|
||||
"further reading offers the references the skill names, and only ones that exist",
|
||||
"the delivered method is quoted from the live skill tree",
|
||||
"tests\\\\consultation-workflow-request.test.ts",
|
||||
"admin customer reset clears only rebuildable application state",
|
||||
"service and restricted admin database identities stay separated",
|
||||
"Owner recovery grants only one currently loginable synced identity admin",
|
||||
"staging backups are encrypted, atomic, private, and retain the newest three",
|
||||
"rejects destructive backup directory aliases and symlink components before mutation",
|
||||
"rejects unsafe writable backup parents before creating the target",
|
||||
"rejects a direct canonical sticky shared backup directory before chmod",
|
||||
"creates every absent backup path component privately despite a permissive caller umask",
|
||||
"same-second backups publish once without overwriting the completed archive",
|
||||
"find enumeration failures preserve existing backups and do not report completion",
|
||||
"refuses full disks and removes a failed-pipeline partial file",
|
||||
"billing order adjustments and redemption reasons are atomic and audited",
|
||||
"billing, subscriptions, usage authorization, RBAC, and model publication remain transactional",
|
||||
"saving a published product forks a draft and delete retires or removes it",
|
||||
"append_consultation_question ignores thinking fields and enforces the physical JSON cap",
|
||||
"database env validator accepts punctuated literal required secrets",
|
||||
"database env validator rejects symlinks and unsafe modes",
|
||||
"database env validator enforces an explicit staging owner uid without printing values",
|
||||
"database env validator accepts a private valid file without printing values",
|
||||
"local PostgreSQL applies the reviewed business schema and serves authenticated business calls",
|
||||
"personal_reports.card_summary is nullable, owner-read, service-written, and length-capped",
|
||||
"personal report job migration is atomic, lease-bound, recoverable, and owner read-only",
|
||||
"longform appendices are owner-read, service-written, and never change report status",
|
||||
"personal report sections enforce owner-read RLS and service-owned durable transitions",
|
||||
"block_scan RPCs are service_role-only and advance a declared period",
|
||||
"redeem security: case-sensitive hashing, rate limiting, idempotency and order ownership",
|
||||
"read_report_candidate_range is service_role-only, returns only the window, and stays empty without rows",
|
||||
"self-hosted identity migration creates Better Auth tables with least privilege",
|
||||
"database roles have no cluster privileges",
|
||||
"every color token used as a Tailwind utility is exposed through @theme",
|
||||
"staging model provider env preparation removes legacy settings and keeps one stable key",
|
||||
"staging env validator rejects selector drift, duplicates, and unsafe permissions",
|
||||
"production env validators accept only self-hosted production selectors and role URLs",
|
||||
"Better Auth supports shared user OTP/password sessions for admins",
|
||||
"database drops secret refs, generates immutable codes, and invalidates evidence after key rotation",
|
||||
"job migration mirror is exact and preserves personal_reports as the document projection",
|
||||
"document v2 and durable job migrations are exact db/supabase mirrors",
|
||||
"document v2 migration preserves v1 reads and adds explicit product depth",
|
||||
"durable job migration defines the complete state, lease, retry, and identity contract",
|
||||
"durable job migration atomically enqueues reports and exposes lease RPCs only to service_role",
|
||||
"ingest P0: education kinds, batch confirm, opening focus reuse, precision lock",
|
||||
"PR-4 candidate decisions use server UUIDs, receipt-derived gates and separate acceptance/confirmation",
|
||||
"tests\\\\rectification-v9-agent.test.ts",
|
||||
"v9 migration applies on a fresh database and re-applies idempotently",
|
||||
"v9 open is atomic, idempotent and allows separate homepage cases",
|
||||
"v9 enforces profile gating, ownership and terminal read-only",
|
||||
"v9 evidence lifecycle: quote grounding, idempotency, confirm and revision lineage",
|
||||
"v9 legacy backfill maps statuses, keeps one resumable per user and is idempotent",
|
||||
"v9 agent api migration applies, seeds the runtime flag and guards consent",
|
||||
"v9 ignores a historical active minute and allows reselection within the live result",
|
||||
"tests\\\\skill-binding.test.ts",
|
||||
"immutable Skill migration enforces RPC-only identity, legacy adoption, and cascade-safe receipts",
|
||||
"checked-in registry verifies hashed product packages and leaves consult on the live skill",
|
||||
"path traversal and symlink escape fail closed",
|
||||
"symbolic links are rejected even when their target stays inside the project root",
|
||||
"live consult skill reads a hand-updated tree without a registry hash",
|
||||
"changed staging workflows are syntactically valid YAML",
|
||||
"live staging sync preserves env, state, incoming files, and encrypted backups",
|
||||
"live staging sync repairs nested deploy-tree drift without preserving foreign ownership",
|
||||
"first immutable deployment rolls back to validated local image IDs",
|
||||
"is-docs-only-range.sh decides from local history and refuses non-ancestor ranges"
|
||||
]
|
||||
},
|
||||
"final": {
|
||||
"tests": 3493,
|
||||
"pass": 3414,
|
||||
"fail": 79,
|
||||
"skipped": 0,
|
||||
"failures": [
|
||||
"admin code functions reject immutable codes, revoked redemption, and roll back on audit failure",
|
||||
"operation-level admin email reauthentication is removed from routes and UI",
|
||||
"rectification agent maps setup failures without calling the rest of the handler",
|
||||
"public code never imports the internal dynamic choice contract",
|
||||
"tests\\\\birth-time-guide-agent.test.ts",
|
||||
"candidate scores stay out of the specified client ownership boundary",
|
||||
"personal Agent exposes the Jyotish Skill and named server tool",
|
||||
"general agent runtime has no Jyotish skill package and no personal chart tool",
|
||||
"the route's own strict checklist reaches the answer, not just the package listing",
|
||||
"a route the skill declares no checklist for is reported, not filled in with another route's",
|
||||
"health uses the skill's health-timing-strict checklist",
|
||||
"wealth uses finance-timing-strict as the live checklist, with the wealth alias in the heading",
|
||||
"a multi-domain plan carries every executed route's checklist once",
|
||||
"no plan can spend the answer's context on method",
|
||||
"the widest legal plan still fits the budget",
|
||||
"further reading offers the references the skill names, and only ones that exist",
|
||||
"the delivered method is quoted from the live skill tree",
|
||||
"tests\\\\consultation-workflow-request.test.ts",
|
||||
"admin customer reset clears only rebuildable application state",
|
||||
"service and restricted admin database identities stay separated",
|
||||
"Owner recovery grants only one currently loginable synced identity admin",
|
||||
"staging backups are encrypted, atomic, private, and retain the newest three",
|
||||
"rejects destructive backup directory aliases and symlink components before mutation",
|
||||
"rejects unsafe writable backup parents before creating the target",
|
||||
"rejects a direct canonical sticky shared backup directory before chmod",
|
||||
"creates every absent backup path component privately despite a permissive caller umask",
|
||||
"same-second backups publish once without overwriting the completed archive",
|
||||
"find enumeration failures preserve existing backups and do not report completion",
|
||||
"refuses full disks and removes a failed-pipeline partial file",
|
||||
"billing order adjustments and redemption reasons are atomic and audited",
|
||||
"billing, subscriptions, usage authorization, RBAC, and model publication remain transactional",
|
||||
"saving a published product forks a draft and delete retires or removes it",
|
||||
"append_consultation_question ignores thinking fields and enforces the physical JSON cap",
|
||||
"database env validator accepts punctuated literal required secrets",
|
||||
"database env validator rejects symlinks and unsafe modes",
|
||||
"database env validator enforces an explicit staging owner uid without printing values",
|
||||
"database env validator accepts a private valid file without printing values",
|
||||
"local PostgreSQL applies the reviewed business schema and serves authenticated business calls",
|
||||
"personal_reports.card_summary is nullable, owner-read, service-written, and length-capped",
|
||||
"personal report job migration is atomic, lease-bound, recoverable, and owner read-only",
|
||||
"longform appendices are owner-read, service-written, and never change report status",
|
||||
"personal report sections enforce owner-read RLS and service-owned durable transitions",
|
||||
"block_scan RPCs are service_role-only and advance a declared period",
|
||||
"redeem security: case-sensitive hashing, rate limiting, idempotency and order ownership",
|
||||
"read_report_candidate_range is service_role-only, returns only the window, and stays empty without rows",
|
||||
"self-hosted identity migration creates Better Auth tables with least privilege",
|
||||
"database roles have no cluster privileges",
|
||||
"every color token used as a Tailwind utility is exposed through @theme",
|
||||
"staging model provider env preparation removes legacy settings and keeps one stable key",
|
||||
"staging env validator rejects selector drift, duplicates, and unsafe permissions",
|
||||
"production env validators accept only self-hosted production selectors and role URLs",
|
||||
"Better Auth supports shared user OTP/password sessions for admins",
|
||||
"database drops secret refs, generates immutable codes, and invalidates evidence after key rotation",
|
||||
"job migration mirror is exact and preserves personal_reports as the document projection",
|
||||
"document v2 and durable job migrations are exact db/supabase mirrors",
|
||||
"document v2 migration preserves v1 reads and adds explicit product depth",
|
||||
"durable job migration defines the complete state, lease, retry, and identity contract",
|
||||
"durable job migration atomically enqueues reports and exposes lease RPCs only to service_role",
|
||||
"ingest P0: education kinds, batch confirm, opening focus reuse, precision lock",
|
||||
"PR-4 candidate decisions use server UUIDs, receipt-derived gates and separate acceptance/confirmation",
|
||||
"tests\\\\rectification-v9-agent.test.ts",
|
||||
"v9 migration applies on a fresh database and re-applies idempotently",
|
||||
"v9 open is atomic, idempotent and allows separate homepage cases",
|
||||
"v9 enforces profile gating, ownership and terminal read-only",
|
||||
"v9 evidence lifecycle: quote grounding, idempotency, confirm and revision lineage",
|
||||
"v9 legacy backfill maps statuses, keeps one resumable per user and is idempotent",
|
||||
"v9 agent api migration applies, seeds the runtime flag and guards consent",
|
||||
"v9 ignores a historical active minute and allows reselection within the live result",
|
||||
"tests\\\\skill-binding.test.ts",
|
||||
"immutable Skill migration enforces RPC-only identity, legacy adoption, and cascade-safe receipts",
|
||||
"checked-in registry verifies hashed product packages and leaves consult on the live skill",
|
||||
"path traversal and symlink escape fail closed",
|
||||
"symbolic links are rejected even when their target stays inside the project root",
|
||||
"live consult skill reads a hand-updated tree without a registry hash",
|
||||
"changed staging workflows are syntactically valid YAML",
|
||||
"live staging sync preserves env, state, incoming files, and encrypted backups",
|
||||
"live staging sync repairs nested deploy-tree drift without preserving foreign ownership",
|
||||
"first immutable deployment rolls back to validated local image IDs",
|
||||
"is-docs-only-range.sh decides from local history and refuses non-ancestor ranges"
|
||||
]
|
||||
},
|
||||
"baseline_only": [],
|
||||
"final_only": [],
|
||||
"commands": {
|
||||
"baseline": [
|
||||
{
|
||||
"command": "tsc",
|
||||
"exit": 0,
|
||||
"seconds": 44.09
|
||||
},
|
||||
{
|
||||
"command": "lint",
|
||||
"exit": 0,
|
||||
"seconds": 32.7
|
||||
},
|
||||
{
|
||||
"command": "test",
|
||||
"exit": 1,
|
||||
"seconds": 149.2
|
||||
},
|
||||
{
|
||||
"command": "build",
|
||||
"exit": 1,
|
||||
"seconds": 2.66
|
||||
}
|
||||
],
|
||||
"final": [
|
||||
{
|
||||
"command": "tsc",
|
||||
"exit": 0,
|
||||
"seconds": 38.28
|
||||
},
|
||||
{
|
||||
"command": "lint",
|
||||
"exit": 0,
|
||||
"seconds": 25.39
|
||||
},
|
||||
{
|
||||
"command": "test",
|
||||
"exit": 1,
|
||||
"seconds": 135.52
|
||||
},
|
||||
{
|
||||
"command": "build",
|
||||
"exit": 1,
|
||||
"seconds": 2.39
|
||||
}
|
||||
]
|
||||
},
|
||||
"metadata_and_version_targeted": {
|
||||
"tests": 14,
|
||||
"pass": 14,
|
||||
"fail": 0,
|
||||
"skipped": 0,
|
||||
"failures": []
|
||||
},
|
||||
"bug621_plus_convergence_and_version": {
|
||||
"tests": 59,
|
||||
"pass": 55,
|
||||
"fail": 4,
|
||||
"skipped": 0,
|
||||
"failures": [
|
||||
"checked-in registry verifies hashed product packages and leaves consult on the live skill",
|
||||
"path traversal and symlink escape fail closed",
|
||||
"symbolic links are rejected even when their target stays inside the project root",
|
||||
"live consult skill reads a hand-updated tree without a registry hash"
|
||||
]
|
||||
},
|
||||
"lint": {
|
||||
"baseline_errors": 0,
|
||||
"baseline_warnings": 120,
|
||||
"final_errors": 0,
|
||||
"final_warnings": 120
|
||||
},
|
||||
"build_limitation": "Both builds fail because external node_modules junction is outside the Turbopack filesystem root; Static/gzip not measured",
|
||||
"new_version_tests": 7,
|
||||
"baseline_frontend_files_compared": 1500,
|
||||
"baseline_source_modified": false,
|
||||
"installed_or_upgraded_dependencies": false
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"scope": "candidate_local_date_fix_not_product_accuracy_or_independent_blind_validation",
|
||||
"baseline_commit": "03cba4780a220b132cf4f06d22e14608aaf9ad3c",
|
||||
"baseline_source_sha256": {
|
||||
"scripts/rectification/dasha_transition_proximity.py": "190c615d9733f4ca0cd2a903f67ecaa45afc1db3eaa0f72a6884d9fe5582b57b",
|
||||
"scripts/rectification/scoring_service.py": "e0b3dbefbf501c20b1cd0d7353bdc3314ec98982c04164af003fa747295a09db"
|
||||
},
|
||||
"current_source_sha256": {
|
||||
"scripts/rectification/dasha_transition_proximity.py": "0afba494aa12899621173c3cc941c6c1d0be9cec0bdf4c4a1c9bcd004ab22de8",
|
||||
"scripts/rectification/scoring_service.py": "0adf5700bb22dd8c4c3147027251210e21ec66a2c9f9a91217edba3ad9e5d1f5"
|
||||
},
|
||||
"dataset_sha256": "45aa76ab79dfc6c6404b6252083e1aa5c7e2e2b7de811fc11042a8ad2da3f0ea",
|
||||
"parameters": {
|
||||
"dataset": "references/real_case_calibration/minute_rectification_holdout_v3.json",
|
||||
"public_aa_case_count": 20,
|
||||
"radius_minutes": 60,
|
||||
"offset_minutes": 0,
|
||||
"minute_step": 1,
|
||||
"candidates_per_window": 121,
|
||||
"ayanamsa": "raman",
|
||||
"node_mode": "mean",
|
||||
"timed_scope": "compute_candidate_static_contexts + build_event_contribution_matrix + score_from_matrix",
|
||||
"timing_method": "same process, five alternating baseline/fixed pairs; baseline helper preserved before modification; all other scoring paths identical",
|
||||
"score_serialization": "json.dumps(scores, sort_keys=True, separators=(\",\", \":\")).encode()",
|
||||
"matrix_comparison": "same canonical JSON serialization then SHA-256",
|
||||
"timing_pair_orders": [
|
||||
"baseline,fixed",
|
||||
"fixed,baseline",
|
||||
"baseline,fixed",
|
||||
"fixed,baseline",
|
||||
"baseline,fixed"
|
||||
]
|
||||
},
|
||||
"timing": {
|
||||
"pre_fix_samples_seconds": [
|
||||
22.780348100000992,
|
||||
21.738976600026945,
|
||||
20.851636399980634
|
||||
],
|
||||
"paired_alternating_samples_seconds": {
|
||||
"baseline": [
|
||||
27.478544100013096,
|
||||
21.569872399995802,
|
||||
20.71335249999538,
|
||||
21.362332899996545,
|
||||
21.438496799994027
|
||||
],
|
||||
"fixed": [
|
||||
21.150953400006983,
|
||||
21.252369000023464,
|
||||
20.739417499979027,
|
||||
21.42927980000968,
|
||||
21.525520600000164
|
||||
]
|
||||
},
|
||||
"paired_median_seconds": {
|
||||
"baseline": 21.438496799994027,
|
||||
"fixed": 21.252369000023464
|
||||
},
|
||||
"median_change_percent": -0.868194266169886,
|
||||
"paired_change_percent": [
|
||||
-23.027387029588287,
|
||||
-1.4719762550491455,
|
||||
0.1258367035642971,
|
||||
0.3133875889236082,
|
||||
0.40592304963360526
|
||||
],
|
||||
"maximum_allowed_regression_percent": 20
|
||||
},
|
||||
"same_day": {
|
||||
"case_count": 19,
|
||||
"candidate_count": 2299,
|
||||
"comparisons": [
|
||||
{
|
||||
"case_ordinal": 1,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "2aabdda6bb56baf6a9d0b119964ee1ab8023fede9ea8703f6d265207c490bec2"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 2,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "6585d895aeb79e26257b1002696c16b5e252f9a702f2a5702f929fd80486abb9"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 3,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "246296903d915e4886229530fa554428d7f65fb686f70cc369711347ebaf2610"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 4,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "79126128e3a9e861c076569428729cd490f6a122aa35e33975066533e1cc08f0"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 5,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "3f891b16ae23d499a676610829e240ded987cc32570c81ac9e2f24f09ab5cae1"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 7,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "d211367fac590c19d6b68e7717fa605ab70fa0c2f9c7adda2477f211a909de77"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 8,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "1e5a0d58160f7bea190cff621acfaf7e94f82a8b64c2ab2a7b16d574fa72545f"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 9,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "8673a519a9729194e44305bfa329092f0e5fec2b21c5cea8c0271136a610c22c"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 10,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "3525cfcee8c541ac707c83b7badcb1c1c990ffa93c220d17ad95df79fd272ffb"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 11,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "3224140f8f1d5bfa76727a77b17859751a384c3ee45dbff07f9b3e073431c488"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 12,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "4ad82e9d8d9af710078906deb0c82c2d11cccb7b85051d769db48c952c633ef1"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 13,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "ae7e7a6282f6130d8769d52dbf419b16434ac6b27c166ebacab8cb2ae03ae905"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 14,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "02319206460876de38170308930896142171595636ffffef9bfe652b3760c089"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 15,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "d29d0098f63db390f8de05abba77d88b5f5f2d3d63deebf12f1edecad1935e3d"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 16,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "a9ef128d8535171bfd55ebb30ee7a6f5137f40f3a38218de691f2259d4ee8e1f"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 17,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "675f400eb0ef8840092fb512de62ca29eb0482065178ccb0f8d1020b38f4b168"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 18,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "d17e6197a5ef668130e72d6513ebacb7ce357d27b7569d6be29be8dcd3f52dff"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 19,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "a0bb50c9e7a6b48c9206a486e0962271129489ff53a2ee76ff827fcba52a480b"
|
||||
},
|
||||
{
|
||||
"case_ordinal": 20,
|
||||
"candidate_count": 121,
|
||||
"scores_bytes_equal": true,
|
||||
"matrix_bytes_equal": true,
|
||||
"scores_sha256": "183324931666a285b04624facd50cc1adc82f7a50ba41f3cb1765695469507c0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"cross_day": {
|
||||
"case_ordinal": 6,
|
||||
"method": "natural cross-midnight window, independent single-candidate matrices each anchored to its own local date, compare all rows and cells",
|
||||
"baseline": {
|
||||
"candidate_count": 121,
|
||||
"cross_date_candidates": 16,
|
||||
"different_candidate_count": 16,
|
||||
"same_day_different_count": 0,
|
||||
"max_absolute_score_delta": 0.0267,
|
||||
"full_rows_equal": false,
|
||||
"full_matrix_equal": false
|
||||
},
|
||||
"fixed": {
|
||||
"candidate_count": 121,
|
||||
"cross_date_candidates": 16,
|
||||
"different_candidate_count": 0,
|
||||
"same_day_different_count": 0,
|
||||
"max_absolute_score_delta": 0.0,
|
||||
"full_rows_equal": true,
|
||||
"full_matrix_equal": true
|
||||
}
|
||||
},
|
||||
"tests": {
|
||||
"pre_fix": {
|
||||
"failed": 4,
|
||||
"passed": 4,
|
||||
"elapsed_seconds": 7.32
|
||||
},
|
||||
"post_fix_with_quick_bridge": {
|
||||
"failed": 0,
|
||||
"passed": 18,
|
||||
"elapsed_seconds": 14.71,
|
||||
"unique_test_count": 9
|
||||
},
|
||||
"existing_endpoint_registry_enumeration": {
|
||||
"failed": 0,
|
||||
"passed": 3,
|
||||
"elapsed_seconds": 0.69
|
||||
},
|
||||
"quick_bridge_collected": 9,
|
||||
"notes": "Version identity regression added after red run; bridge repeats the same nine regressions. Full quick and broad baseline comparisons are coordinator-owned."
|
||||
},
|
||||
"scope_verification": {
|
||||
"frozen_files_count": 12,
|
||||
"frozen_files_unchanged": true,
|
||||
"all_proximity_constants_unchanged": true
|
||||
},
|
||||
"privacy": "Only public dataset anonymous ordinals, score hashes and aggregates; no birth times, dates, names, coordinates, case IDs or raw scores."
|
||||
}
|
||||
@@ -949,8 +949,10 @@ export async function runV9Diagnostics(input: {
|
||||
};
|
||||
}
|
||||
|
||||
// Keep the receipt default aligned with Python's ALGORITHM_VERSION; persisted
|
||||
// receipts retain their original identity (this is not a Skill/open gate).
|
||||
export const v9EngineVersion = (): string =>
|
||||
process.env.RECTIFICATION_ENGINE_VERSION?.trim() || "rectification-v5";
|
||||
process.env.RECTIFICATION_ENGINE_VERSION?.trim() || "rectification-v5-matrix-scoring-8";
|
||||
|
||||
export type V9RangeReading = Readonly<{
|
||||
stableThemes: readonly string[];
|
||||
|
||||
+1428
File diff suppressed because it is too large
Load Diff
@@ -81,8 +81,11 @@ const diagnosticReport = JSON.parse(readFileSync(
|
||||
verified_minute_claim_allowed: boolean;
|
||||
};
|
||||
};
|
||||
// 原值:docs/research/sealed_holdout_rerun_2026_09_20.json
|
||||
// 新值:docs/research/sealed_holdout_rerun_cross_midnight_2026_09_20.json
|
||||
// 原因:BUG-981 修复后重跑另存报告;旧报告保留,确认门行为与阈值不变。
|
||||
const currentTreeRerun = JSON.parse(readFileSync(
|
||||
new URL("../../docs/research/sealed_holdout_rerun_2026_09_20.json", import.meta.url),
|
||||
new URL("../../docs/research/sealed_holdout_rerun_cross_midnight_2026_09_20.json", import.meta.url),
|
||||
"utf8",
|
||||
)) as {
|
||||
frozen_record: { implementation_sha256: string };
|
||||
@@ -204,7 +207,7 @@ test("sealed holdout aggregates match the v3 report and still block confirmation
|
||||
);
|
||||
assert.equal(
|
||||
productHoldout.current_tree_scorer.source_report,
|
||||
"docs/research/sealed_holdout_rerun_2026_09_20.json",
|
||||
"docs/research/sealed_holdout_rerun_cross_midnight_2026_09_20.json",
|
||||
);
|
||||
assert.equal(currentTreeRerun.official_valid_independent_blind, false);
|
||||
assert.equal(productHoldout.current_tree_scorer.matches_metrics_scorer, false);
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
|
||||
import {
|
||||
cachedEngineScoreIsReusable,
|
||||
liveEngineScoringIdentityFromEnv,
|
||||
readV9EngineScoringIdentity,
|
||||
v9EngineVersion,
|
||||
} from "../src/lib/rectification-agentic/v9/engine-client.ts";
|
||||
import { openRectificationCase } from "../src/lib/rectification-agentic/v9/case-service.ts";
|
||||
import { loadV9TurnReceipt } from "../src/lib/rectification-agentic/v9/tool-service.ts";
|
||||
import { resolveSkillPackageVersion } from "../src/lib/skill-package-registry.ts";
|
||||
import { createRectificationV9Tools } from "../src/mastra/rectification-v9-tools.ts";
|
||||
import {
|
||||
CASE_ID, SESSION_ID, TURN_ID, USER_ID, FOCUS_ID,
|
||||
computeFixture, dossierFixture, fakeAccounting, receiptHandlers,
|
||||
} from "./rectification-v9-test-support.ts";
|
||||
|
||||
// Real native-engine response, using explicitly fictional input, not a hand-built contract.
|
||||
const golden = JSON.parse(readFileSync(new URL(
|
||||
"./fixtures/rectification-engine-version-cross-midnight-golden.json", import.meta.url,
|
||||
), "utf8"));
|
||||
const CURRENT = "rectification-v5-matrix-scoring-8";
|
||||
const PREVIOUS = "rectification-v5-matrix-scoring-7";
|
||||
const envKeys = [
|
||||
"RECTIFICATION_ENGINE_VERSION", "RECTIFICATION_ALGORITHM_VERSION",
|
||||
"RECTIFICATION_DECISION_POLICY_VERSION",
|
||||
] as const;
|
||||
|
||||
function isolateIdentityEnv(t: test.TestContext) {
|
||||
const saved = envKeys.map((key) => [key, process.env[key]] as const);
|
||||
for (const key of envKeys) delete process.env[key];
|
||||
t.after(() => {
|
||||
for (const [key, value] of saved) {
|
||||
if (value === undefined) delete process.env[key];
|
||||
else process.env[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
test("receipt default matches the cross-midnight native scoring version without changing Skill identity", (t) => {
|
||||
isolateIdentityEnv(t);
|
||||
assert.equal(v9EngineVersion(), CURRENT);
|
||||
for (const blank of ["", " "]) {
|
||||
process.env.RECTIFICATION_ENGINE_VERSION = blank;
|
||||
assert.equal(v9EngineVersion(), CURRENT);
|
||||
}
|
||||
assert.equal(golden.score.algorithm_version, CURRENT);
|
||||
assert.equal(golden.versions.algorithm_version, CURRENT);
|
||||
const python = readFileSync(new URL("../../scripts/rectification/scoring_service.py", import.meta.url), "utf8");
|
||||
assert.match(python, /ALGORITHM_VERSION = "rectification-v5-matrix-scoring-8"/);
|
||||
// A receipt default must not become an environment override for cache identity.
|
||||
assert.deepEqual(liveEngineScoringIdentityFromEnv({}), { algorithmVersion: null, policyVersion: null });
|
||||
});
|
||||
|
||||
test("explicit engine override still wins and remains a deployment prerequisite, not silently rewritten", (t) => {
|
||||
isolateIdentityEnv(t);
|
||||
process.env.RECTIFICATION_ENGINE_VERSION = " rectification-v5 ";
|
||||
assert.equal(v9EngineVersion(), "rectification-v5");
|
||||
assert.equal(liveEngineScoringIdentityFromEnv().algorithmVersion, null);
|
||||
process.env.RECTIFICATION_ENGINE_VERSION = ` ${PREVIOUS} `;
|
||||
assert.equal(v9EngineVersion(), PREVIOUS);
|
||||
// This existing cache-identity fallback predates this change; no new open gate is introduced.
|
||||
assert.equal(liveEngineScoringIdentityFromEnv().algorithmVersion, PREVIOUS);
|
||||
});
|
||||
|
||||
test("native versions invalidate matching-input old scores while same-version scores remain reusable", async (t) => {
|
||||
isolateIdentityEnv(t);
|
||||
t.mock.method(globalThis, "fetch", async (url: unknown) => {
|
||||
assert.ok(String(url).endsWith("/api/rectification/v5/versions"));
|
||||
return Response.json(golden.versions);
|
||||
});
|
||||
const live = await readV9EngineScoringIdentity();
|
||||
assert.equal(live.algorithmVersion, CURRENT);
|
||||
const fingerprints = { evidenceLedgerFingerprint: "e".repeat(64), candidateRangeFingerprint: "c".repeat(64) };
|
||||
const previous = { ...fingerprints, algorithmVersion: PREVIOUS, policyVersion: golden.versions.decision_policy_version };
|
||||
assert.equal(cachedEngineScoreIsReusable(previous, fingerprints, live), false);
|
||||
assert.equal(cachedEngineScoreIsReusable({ ...previous, algorithmVersion: CURRENT }, fingerprints, live), true);
|
||||
assert.equal(previous.algorithmVersion, PREVIOUS);
|
||||
});
|
||||
|
||||
test("policy-only or stale algorithm overrides bypass native versions under the existing cache policy", async (t) => {
|
||||
isolateIdentityEnv(t);
|
||||
let fetches = 0;
|
||||
t.mock.method(globalThis, "fetch", async () => {
|
||||
fetches += 1;
|
||||
return Response.json(golden.versions);
|
||||
});
|
||||
const fingerprints = { evidenceLedgerFingerprint: "e".repeat(64), candidateRangeFingerprint: "c".repeat(64) };
|
||||
const stored = { ...fingerprints, algorithmVersion: PREVIOUS, policyVersion: golden.versions.decision_policy_version };
|
||||
process.env.RECTIFICATION_DECISION_POLICY_VERSION = golden.versions.decision_policy_version;
|
||||
const policyOnly = await readV9EngineScoringIdentity();
|
||||
assert.equal(policyOnly.algorithmVersion, null);
|
||||
assert.equal(cachedEngineScoreIsReusable(stored, fingerprints, policyOnly), true);
|
||||
process.env.RECTIFICATION_ALGORITHM_VERSION = PREVIOUS;
|
||||
const staleOverride = await readV9EngineScoringIdentity();
|
||||
assert.equal(staleOverride.algorithmVersion, PREVIOUS);
|
||||
assert.equal(cachedEngineScoreIsReusable(stored, fingerprints, staleOverride), true);
|
||||
assert.equal(fetches, 0);
|
||||
});
|
||||
|
||||
test("unreachable versions retain the existing unknown-identity cache fallback, not proof of rescore", async (t) => {
|
||||
isolateIdentityEnv(t);
|
||||
t.mock.method(globalThis, "fetch", async () => { throw new Error("fixture versions unavailable"); });
|
||||
const live = await readV9EngineScoringIdentity();
|
||||
assert.deepEqual(live, { algorithmVersion: null, policyVersion: null });
|
||||
const fingerprints = { evidenceLedgerFingerprint: "e".repeat(64), candidateRangeFingerprint: "c".repeat(64) };
|
||||
assert.equal(cachedEngineScoreIsReusable({ ...fingerprints, algorithmVersion: PREVIOUS }, fingerprints, live), true);
|
||||
});
|
||||
|
||||
test("new Case scoring writes the new version on both started and completed receipts", async (t) => {
|
||||
isolateIdentityEnv(t);
|
||||
const request = golden.request;
|
||||
const candidateRange = { start_time: request.start_time, end_time: request.end_time };
|
||||
const evidence = request.events.map((event: Record<string, string>) => ({
|
||||
id: event.id, source_turn_id: TURN_ID, subject: "self", event_kind: event.event_kind,
|
||||
domain: event.domain, occurred_from: event.date_start, occurred_to: event.date_end,
|
||||
date_precision: event.precision, summary: event.summary, status: "confirmed",
|
||||
}));
|
||||
const calls: string[] = [];
|
||||
t.mock.method(globalThis, "fetch", async (url: unknown) => {
|
||||
const path = new URL(String(url)).pathname;
|
||||
calls.push(path);
|
||||
if (path.endsWith("/versions")) return Response.json(golden.versions);
|
||||
if (path.endsWith("/score") || path.endsWith("/diagnostics")) return Response.json(golden.score);
|
||||
throw new Error(`unexpected engine request ${path}`);
|
||||
});
|
||||
const accounting = fakeAccounting({
|
||||
...receiptHandlers,
|
||||
get_agentic_rectification_case_dossier: () => dossierFixture({ candidateRange, evidence, latestResult: null }),
|
||||
get_agentic_rectification_case_compute: () => ({
|
||||
...computeFixture({ baselineBirthSnapshot: {
|
||||
birth_date: request.birth_date, latitude: request.lat, longitude: request.lon,
|
||||
timezone_offset: request.tz, timezone_id: "Asia/Shanghai", birth_time_source: "approximate",
|
||||
} }), candidate_range: candidateRange,
|
||||
}),
|
||||
persist_agentic_rectification_candidate_v2: (_fn, args) => ({
|
||||
result_id: args.p_engine_result_id, cached: false, candidates: args.p_candidates,
|
||||
algorithm_version: args.p_algorithm_version, event_contract_version: args.p_event_contract_version,
|
||||
decision_policy_version: args.p_decision_policy_version, decision_receipt: args.p_decision_receipt,
|
||||
execution_ledger: args.p_execution_ledger, overall_confidence: golden.score.decision_receipt.overall_confidence,
|
||||
selection_allowed: golden.score.decision_receipt.selection_allowed, confirmation_allowed: false,
|
||||
representative_time: golden.score.decision_receipt.representative_time,
|
||||
}),
|
||||
set_agentic_rectification_conversation_focus: (_fn, args) => ({
|
||||
focus: { id: FOCUS_ID, case_id: CASE_ID, question_id: args.p_question_id,
|
||||
intent: args.p_intent, target_evidence_id: args.p_target_evidence_id,
|
||||
target_domain: args.p_target_domain, target_kind: args.p_target_kind,
|
||||
expected_answer_schema: args.p_expected_answer_schema, status: "active" }, idempotent: false,
|
||||
}),
|
||||
});
|
||||
const tools = createRectificationV9Tools({ userId: USER_ID, caseId: CASE_ID, turnId: TURN_ID, accounting: accounting.client as never });
|
||||
for (const name of ["rectification-compare-candidates", "rectification-read-diagnostics"] as const) {
|
||||
await (tools[name] as unknown as { execute(input: unknown): Promise<unknown> }).execute({ caseId: CASE_ID });
|
||||
const receipts = accounting.calls.filter((call) => call.fn === "insert_agentic_rectification_tool_receipt" && call.args.p_tool_name === name);
|
||||
assert.deepEqual(receipts.map((call) => [call.args.p_status, call.args.p_engine_version]), [
|
||||
["started", CURRENT], ["completed", CURRENT],
|
||||
]);
|
||||
}
|
||||
assert.ok(calls.includes("/api/rectification/v5/score"));
|
||||
const persisted = accounting.calls.find((call) => call.fn === "persist_agentic_rectification_candidate_v2");
|
||||
assert.equal(persisted?.args.p_algorithm_version, CURRENT);
|
||||
});
|
||||
|
||||
test("history opens with its bound Skill and reads old engine receipt values unchanged", async (t) => {
|
||||
isolateIdentityEnv(t);
|
||||
const bound = resolveSkillPackageVersion("jyotish-birth-time-rectification", "10.0.17");
|
||||
for (const oldVersion of ["rectification-v5", PREVIOUS]) {
|
||||
const historical = { turn_id: TURN_ID, engine_version: oldVersion, skill_name: bound.name, skill_version: bound.version, status: "completed" };
|
||||
const before = structuredClone(historical);
|
||||
const open = { disposition: "readonly", case_id: CASE_ID, session_id: SESSION_ID,
|
||||
status: "closed", should_start_opening: false, skill_version: bound.version };
|
||||
const accounting = fakeAccounting({
|
||||
open_agentic_rectification_case: () => open,
|
||||
get_agentic_rectification_skill_identity: () => ({ skill_name: bound.name, skill_version: bound.version, skill_sha256: bound.sha256, skill_source_commit: bound.sourceCommit }),
|
||||
open_agentic_rectification_case_v2: (_fn, args) => {
|
||||
assert.equal(args.p_skill_version, bound.version);
|
||||
assert.equal(args.p_skill_sha256, bound.sha256);
|
||||
assert.equal("p_engine_version" in args, false);
|
||||
return open;
|
||||
},
|
||||
get_agentic_rectification_turn_receipt: () => historical,
|
||||
});
|
||||
const opened = await openRectificationCase(accounting.client as never, USER_ID, {
|
||||
intent: "session", requestId: "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee", sessionId: SESSION_ID,
|
||||
});
|
||||
assert.equal(opened.disposition, "readonly");
|
||||
const receipt = await loadV9TurnReceipt(accounting.client as never, USER_ID, CASE_ID, TURN_ID);
|
||||
assert.equal(v9EngineVersion(), CURRENT);
|
||||
assert.equal(receipt?.engineVersion, oldVersion);
|
||||
assert.deepEqual(historical, before);
|
||||
assert.equal(accounting.calls.some((call) => /insert|persist|upgrade/.test(call.fn)), false);
|
||||
}
|
||||
});
|
||||
@@ -25,10 +25,72 @@
|
||||
},
|
||||
"current_tree_scorer": {
|
||||
"implementation_sha256": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"extended_identity": {
|
||||
"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/jaimini.py",
|
||||
"scripts/minute_rectification_fact_ranker_v4.py",
|
||||
"scripts/minute_rectification_feature_facts_v4.py",
|
||||
"scripts/narayana_dasha.py",
|
||||
"scripts/rectification/candidate_contrast.py",
|
||||
"scripts/rectification/case_holdout.py",
|
||||
"scripts/rectification/contracts.py",
|
||||
"scripts/rectification/dasha_transition_proximity.py",
|
||||
"scripts/rectification/event_probes.py",
|
||||
"scripts/rectification/scoring_service.py",
|
||||
"scripts/shadbala.py",
|
||||
"scripts/varga.py"
|
||||
],
|
||||
"production_scoring_sha256": "7fffd1db612af1f244a8b5d5eac2f1a3f5a8e9e138c09ac6b3743b98f9a47a51",
|
||||
"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": "2931d4661bf53dde5c57f6492de65be4c00877293b7277353279a05eda5af215",
|
||||
"historical_artifacts_manifest_sha256": "6102a26a840be207b5858b3a4c0509274468ae9071e86d308cbbac7371d6864e",
|
||||
"file_sha256": {
|
||||
"scripts/active_rectification_event_engine.py": "edbe93ef3c8bd65f986139dba6b95b96d9b0fa9920341fbe677cd6f53e4db84f",
|
||||
"scripts/active_rectification_events.py": "03e333ccf1103caf685dfe50fe890ab1793e945037085f50ce834a4a3b232222",
|
||||
"scripts/ashtakavarga.py": "cc32776674cf60713634a360a6f09774b3af0ec5d0f650687f52b79597b0dfbe",
|
||||
"scripts/dasha_analyzer.py": "5e53b1b4d4c7414c08dd0e7716f120a63c70ec3428ec93547f64d55c580da0e1",
|
||||
"scripts/divisional_charts_extended.py": "73bd50a83ac444830940184d0988cea621855f91db76a3f1a232014b992eacea",
|
||||
"scripts/domain_calculation_service.py": "9ee955874eea806c7b56f4246d90009c02ee8318b8f3dfe1d7132b1c0b4262e1",
|
||||
"scripts/jaimini.py": "b98bf965603dd2e3cc871fe40f3c8184731a701875ca50fef3ca68e9682eda81",
|
||||
"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": "3d899a2cdd4825b8e32062443d487edcd2173a4ea55a36eb9f3abe587c0faf0f",
|
||||
"scripts/minute_rectification_holdout_validator.py": "b60b1c752e58672beaee8b36eb3b2348169726cda070ec969db0f13e7ed715af",
|
||||
"scripts/narayana_dasha.py": "7ff2c3238cd113b11b815e14b41967bd8e486ff62e36650e55613b9c79d1bfe3",
|
||||
"scripts/rectification/candidate_contrast.py": "10a360586359e72846593ef6d7c15a2cea79358245a34c288851406fe9e47ca8",
|
||||
"scripts/rectification/case_holdout.py": "fe0e699b617d9e216acdfeff87af4221c1d6f4c101bf33e1dbf14e2bcd75a8b5",
|
||||
"scripts/rectification/contracts.py": "fdd1a47b2e3dac8579e870a282ed74c68518b39f6c0576b27df766a4b8021143",
|
||||
"scripts/rectification/dasha_transition_proximity.py": "0afba494aa12899621173c3cc941c6c1d0be9cec0bdf4c4a1c9bcd004ab22de8",
|
||||
"scripts/rectification/event_probes.py": "45478ee3fdc36dc36e1a602bee5dd0627a5464275e370e64adb5826fe2aa58f5",
|
||||
"scripts/rectification/scoring_service.py": "0adf5700bb22dd8c4c3147027251210e21ec66a2c9f9a91217edba3ad9e5d1f5",
|
||||
"scripts/research/cluster_width_lib.py": "1d8f2ffa7795d62069deebb2de8e16749317037d4ed3f24a2c0092676e5cfcc6",
|
||||
"scripts/research/probe_supply_after_six.py": "098a5b4398d6f8d996917d809c2fc922ad64031d3b70660f8ef7c876aba92438",
|
||||
"scripts/research/reported_offset_sweep.py": "f81c681fd0fe257e90a761dfab370f623e5f8a881b62cc6b1c596cbd58800f8f",
|
||||
"scripts/research/sealed_holdout_rerun.py": "34a6724530fbecdecff9e5e07fa64980b8e8aff1fac2e000f4268debe1ee96e1",
|
||||
"scripts/shadbala.py": "912e0e6d169c2172aab85f71e4347e3e39193020777b43825b775d906400956b",
|
||||
"scripts/varga.py": "4331de5a25ea08729af91aae863943c4223183937f419d52dc63fd6fe7f27be6"
|
||||
},
|
||||
"hash_scope": "explicit_identity_file_sets_not_a_transitive_dependency_lock"
|
||||
},
|
||||
"matches_metrics_scorer": false,
|
||||
"official_eval_implementation_hash_matches": false,
|
||||
"official_eval_trial_count": 0,
|
||||
"source_report": "docs/research/sealed_holdout_rerun_2026_09_20.json",
|
||||
"source_report": "docs/research/sealed_holdout_rerun_cross_midnight_2026_09_20.json",
|
||||
"fixed_protocol_rerun_trial_count": 20,
|
||||
"fixed_protocol_rerun_hash_matches": true,
|
||||
"metrics": {
|
||||
@@ -42,9 +104,71 @@
|
||||
},
|
||||
"current_tree_fixed_protocol_rerun": {
|
||||
"evaluated_on": "2026-09-20",
|
||||
"report_path": "docs/research/sealed_holdout_rerun_2026_09_20.json",
|
||||
"freeze_record_path": "docs/research/sealed_holdout_rerun_2026_09_20.freeze.json",
|
||||
"report_path": "docs/research/sealed_holdout_rerun_cross_midnight_2026_09_20.json",
|
||||
"freeze_record_path": "docs/research/sealed_holdout_rerun_cross_midnight_2026_09_20.final.freeze.json",
|
||||
"implementation_sha256": "b15d9ea15227cd58b3097555537fa7c37fb5a993905ada4f5aa04f52de1f8f18",
|
||||
"extended_identity": {
|
||||
"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/jaimini.py",
|
||||
"scripts/minute_rectification_fact_ranker_v4.py",
|
||||
"scripts/minute_rectification_feature_facts_v4.py",
|
||||
"scripts/narayana_dasha.py",
|
||||
"scripts/rectification/candidate_contrast.py",
|
||||
"scripts/rectification/case_holdout.py",
|
||||
"scripts/rectification/contracts.py",
|
||||
"scripts/rectification/dasha_transition_proximity.py",
|
||||
"scripts/rectification/event_probes.py",
|
||||
"scripts/rectification/scoring_service.py",
|
||||
"scripts/shadbala.py",
|
||||
"scripts/varga.py"
|
||||
],
|
||||
"production_scoring_sha256": "7fffd1db612af1f244a8b5d5eac2f1a3f5a8e9e138c09ac6b3743b98f9a47a51",
|
||||
"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": "2931d4661bf53dde5c57f6492de65be4c00877293b7277353279a05eda5af215",
|
||||
"historical_artifacts_manifest_sha256": "6102a26a840be207b5858b3a4c0509274468ae9071e86d308cbbac7371d6864e",
|
||||
"file_sha256": {
|
||||
"scripts/active_rectification_event_engine.py": "edbe93ef3c8bd65f986139dba6b95b96d9b0fa9920341fbe677cd6f53e4db84f",
|
||||
"scripts/active_rectification_events.py": "03e333ccf1103caf685dfe50fe890ab1793e945037085f50ce834a4a3b232222",
|
||||
"scripts/ashtakavarga.py": "cc32776674cf60713634a360a6f09774b3af0ec5d0f650687f52b79597b0dfbe",
|
||||
"scripts/dasha_analyzer.py": "5e53b1b4d4c7414c08dd0e7716f120a63c70ec3428ec93547f64d55c580da0e1",
|
||||
"scripts/divisional_charts_extended.py": "73bd50a83ac444830940184d0988cea621855f91db76a3f1a232014b992eacea",
|
||||
"scripts/domain_calculation_service.py": "9ee955874eea806c7b56f4246d90009c02ee8318b8f3dfe1d7132b1c0b4262e1",
|
||||
"scripts/jaimini.py": "b98bf965603dd2e3cc871fe40f3c8184731a701875ca50fef3ca68e9682eda81",
|
||||
"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": "3d899a2cdd4825b8e32062443d487edcd2173a4ea55a36eb9f3abe587c0faf0f",
|
||||
"scripts/minute_rectification_holdout_validator.py": "b60b1c752e58672beaee8b36eb3b2348169726cda070ec969db0f13e7ed715af",
|
||||
"scripts/narayana_dasha.py": "7ff2c3238cd113b11b815e14b41967bd8e486ff62e36650e55613b9c79d1bfe3",
|
||||
"scripts/rectification/candidate_contrast.py": "10a360586359e72846593ef6d7c15a2cea79358245a34c288851406fe9e47ca8",
|
||||
"scripts/rectification/case_holdout.py": "fe0e699b617d9e216acdfeff87af4221c1d6f4c101bf33e1dbf14e2bcd75a8b5",
|
||||
"scripts/rectification/contracts.py": "fdd1a47b2e3dac8579e870a282ed74c68518b39f6c0576b27df766a4b8021143",
|
||||
"scripts/rectification/dasha_transition_proximity.py": "0afba494aa12899621173c3cc941c6c1d0be9cec0bdf4c4a1c9bcd004ab22de8",
|
||||
"scripts/rectification/event_probes.py": "45478ee3fdc36dc36e1a602bee5dd0627a5464275e370e64adb5826fe2aa58f5",
|
||||
"scripts/rectification/scoring_service.py": "0adf5700bb22dd8c4c3147027251210e21ec66a2c9f9a91217edba3ad9e5d1f5",
|
||||
"scripts/research/cluster_width_lib.py": "1d8f2ffa7795d62069deebb2de8e16749317037d4ed3f24a2c0092676e5cfcc6",
|
||||
"scripts/research/probe_supply_after_six.py": "098a5b4398d6f8d996917d809c2fc922ad64031d3b70660f8ef7c876aba92438",
|
||||
"scripts/research/reported_offset_sweep.py": "f81c681fd0fe257e90a761dfab370f623e5f8a881b62cc6b1c596cbd58800f8f",
|
||||
"scripts/research/sealed_holdout_rerun.py": "34a6724530fbecdecff9e5e07fa64980b8e8aff1fac2e000f4268debe1ee96e1",
|
||||
"scripts/shadbala.py": "912e0e6d169c2172aab85f71e4347e3e39193020777b43825b775d906400956b",
|
||||
"scripts/varga.py": "4331de5a25ea08729af91aae863943c4223183937f419d52dc63fd6fe7f27be6"
|
||||
},
|
||||
"hash_scope": "explicit_identity_file_sets_not_a_transitive_dependency_lock"
|
||||
},
|
||||
"scorer_frozen_before_rerun": true,
|
||||
"source_audit_status": "corrected_known_date_errors",
|
||||
"trial_count": 20,
|
||||
@@ -57,7 +181,12 @@
|
||||
"verified_minute_claim_allowed": false,
|
||||
"metric_gates_passed": false,
|
||||
"events_per_case": 3,
|
||||
"boundary": "First current-hash fixed-protocol rerun in this task, not a first independent official blind evaluation. Three-event low-information protocol is not representative of real sessions and is not a mathematical accuracy lower bound. Historical v3/v4 score exposure requires a fresh sealed set."
|
||||
"boundary": "Previously exposed v3 shadow fixed-protocol rerun after BUG-981. Production identity is contextual: shadow scorer does not call transition proximity. Prior shadow metrics are not presumed incorrect. Three-event low-information protocol is neither representative of real sessions nor a mathematical accuracy lower bound. No independent blind evidence or release claim."
|
||||
},
|
||||
"previous_fixed_protocol_rerun": {
|
||||
"report_path": "docs/research/sealed_holdout_rerun_2026_09_20.json",
|
||||
"freeze_record_path": "docs/research/sealed_holdout_rerun_2026_09_20.freeze.json",
|
||||
"superseded_reason": "New production and evaluator identity; prior shadow report and freeze preserved byte-for-byte, not relabelled as new results."
|
||||
},
|
||||
"current_tree_unfrozen_diagnostic": {
|
||||
"is_blind_evaluation": false,
|
||||
|
||||
@@ -8,7 +8,7 @@ bounded auxiliary signal, never larger than one day-level event body.
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable, Sequence
|
||||
from datetime import date
|
||||
from datetime import date, datetime
|
||||
from typing import Any
|
||||
|
||||
from scripts.rectification.event_probes import _narayana_start_dates, _vim_start_dates
|
||||
@@ -152,11 +152,15 @@ def merge_transition_proximity(
|
||||
moon = (context.get("planet_longitudes") or {}).get("Moon")
|
||||
if not isinstance(moon, (int, float)):
|
||||
continue
|
||||
vim_key = (birth_date, round(float(moon), 6), lo, hi)
|
||||
# Native contexts retain the local date even when the window crosses midnight.
|
||||
# Legacy time-only contexts still use the request's date.
|
||||
candidate_at = context.get("candidate_at")
|
||||
candidate_date = candidate_at.date().isoformat() if isinstance(candidate_at, datetime) else birth_date
|
||||
vim_key = (candidate_date, round(float(moon), 6), lo, hi)
|
||||
if vim_key not in vim_cache:
|
||||
vim_cache[vim_key] = _vim_start_dates(birth_date, float(moon), lo, hi)
|
||||
vim_cache[vim_key] = _vim_start_dates(candidate_date, float(moon), lo, hi)
|
||||
pd_cache[vim_key] = _vim_start_dates(
|
||||
birth_date,
|
||||
candidate_date,
|
||||
float(moon),
|
||||
lo,
|
||||
hi,
|
||||
@@ -165,7 +169,7 @@ def merge_transition_proximity(
|
||||
planets = context.get("planet_longitudes") or {}
|
||||
asc = context.get("ascendant_index")
|
||||
narayana_key = (
|
||||
birth_date,
|
||||
candidate_date,
|
||||
int(asc) if isinstance(asc, int) else None,
|
||||
lo,
|
||||
hi,
|
||||
@@ -173,7 +177,7 @@ def merge_transition_proximity(
|
||||
)
|
||||
if narayana_key not in narayana_cache:
|
||||
narayana_cache[narayana_key] = (
|
||||
_narayana_start_dates(int(asc), planets, birth_date, lo, hi)
|
||||
_narayana_start_dates(int(asc), planets, candidate_date, lo, hi)
|
||||
if isinstance(asc, int) and isinstance(planets, dict)
|
||||
else None
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ from scripts.rectification.dasha_transition_proximity import merge_transition_pr
|
||||
from scripts.rectification.contracts import LifeEvent, RectificationRequest, is_scoreable_event
|
||||
from scripts.rectification.case_holdout import holdout_event_ids
|
||||
|
||||
ALGORITHM_VERSION = "rectification-v5-matrix-scoring-7"
|
||||
ALGORITHM_VERSION = "rectification-v5-matrix-scoring-8"
|
||||
INPUT_CONTRACT_VERSION = "rectification-calculation-spec-v4"
|
||||
PRECISION_WEIGHTS = {
|
||||
"day": 1.0,
|
||||
|
||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -22,26 +22,17 @@ from scripts.rectification.candidate_contrast import select_signature_representa
|
||||
from scripts.rectification.scoring_service import build_event_contribution_matrix, score_from_matrix
|
||||
from scripts.research.cluster_width_lib import SEPARATION_LEAD, still_valid_public
|
||||
from scripts.research.probe_supply_after_six import request_from_case
|
||||
from scripts.research.sealed_holdout_rerun import DATASET, file_sha256, opaque_order
|
||||
from scripts.research.sealed_holdout_rerun import (
|
||||
DATASET, file_sha256, historical_comparison, implementation_identity,
|
||||
opaque_order, verify_frozen_record,
|
||||
)
|
||||
|
||||
OFFSETS = (-30, -20, -15, -10, -8, -5, -3, 0, 3, 5, 8, 10, 15, 20, 30)
|
||||
RADII = (15, 30, 60)
|
||||
MINUTE_STEP = 1
|
||||
PRODUCTION_FILES = [
|
||||
"scripts/rectification/scoring_service.py",
|
||||
"scripts/rectification/dasha_transition_proximity.py",
|
||||
"scripts/rectification/candidate_contrast.py",
|
||||
"scripts/rectification/case_holdout.py",
|
||||
"scripts/rectification/contracts.py",
|
||||
"scripts/rectification/event_probes.py",
|
||||
]
|
||||
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",
|
||||
]
|
||||
FREEZE = ROOT / "docs/research/reported_offset_cross_midnight_2026_09_20.final.freeze.json"
|
||||
REPORT = ROOT / "docs/research/reported_offset_cross_midnight_2026_09_20.json"
|
||||
LEGACY_REPORT = ROOT / "docs/research/reported_offset_2026_09_20.json"
|
||||
|
||||
|
||||
def shifted_window(case: dict[str, Any], offset: int, radius: int) -> tuple[dict[str, Any], list[datetime]]:
|
||||
@@ -67,21 +58,13 @@ def shifted_window(case: dict[str, Any], offset: int, radius: int) -> tuple[dict
|
||||
|
||||
|
||||
def score_window(request: dict[str, Any], contexts: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""Keep auxiliary transition anchors on each candidate's actual date.
|
||||
"""Use the repaired native single matrix, with candidate dates preserved.
|
||||
|
||||
The production matrix's transition-proximity helper accepts one birth date
|
||||
per call, unlike the static chart layer which reads candidate_at. Grouping
|
||||
is only an offline adapter; it does not change the production scorer.
|
||||
The historical grouped adapter is archived, not a second active scoring
|
||||
path. This is a native scoring replay, still not a production Q&A replay.
|
||||
"""
|
||||
by_date: dict[str, list[dict[str, Any]]] = {}
|
||||
for context in contexts:
|
||||
by_date.setdefault(context["candidate_at"].date().isoformat(), []).append(context)
|
||||
by_time = {}
|
||||
for candidate_date, group in by_date.items():
|
||||
dated_request = {**request, "birth_date": candidate_date}
|
||||
built = build_event_contribution_matrix(dated_request, static_contexts=group)
|
||||
by_time.update({row["time"]: row for row in score_from_matrix(dated_request, built)})
|
||||
return [by_time[context["candidate_at"].strftime("%H:%M")] for context in contexts]
|
||||
built = build_event_contribution_matrix(request, static_contexts=contexts)
|
||||
return score_from_matrix(request, built)
|
||||
|
||||
|
||||
def delivery_moments(public: list[dict[str, Any]], candidates: list[datetime]) -> list[datetime]:
|
||||
@@ -135,11 +118,39 @@ def summarize(trials: list[dict[str, Any]], radii: tuple[int, ...], offsets: tup
|
||||
return result
|
||||
|
||||
|
||||
def run(dataset: Path = DATASET, radii: tuple[int, ...] = RADII, offsets: tuple[int, ...] = OFFSETS) -> dict[str, Any]:
|
||||
def freeze_record(dataset: Path = DATASET, radii: tuple[int, ...] = RADII, offsets: tuple[int, ...] = OFFSETS) -> dict[str, Any]:
|
||||
manifest = json.loads(dataset.read_text(encoding="utf-8"))
|
||||
legacy_hash = implementation_sha256(manifest["frozen_scoring"]["files"])
|
||||
return {
|
||||
"record_version": "reported-offset-native-candidate-date-v3",
|
||||
"frozen_at_utc": datetime.now(timezone.utc).isoformat(),
|
||||
"ayanamsa": AYANAMSA, "node_mode": NODE_MODE,
|
||||
"radii_minutes": list(radii), "offsets_minutes": list(offsets), "minute_step": MINUTE_STEP,
|
||||
"dataset": dataset.relative_to(ROOT).as_posix(), "dataset_sha256": file_sha256(dataset),
|
||||
"dataset_benchmark_id": manifest["benchmark_id"],
|
||||
"implementation_sha256": legacy_hash,
|
||||
"implementation_sha256_prefix": legacy_hash[:16],
|
||||
**implementation_identity(dataset),
|
||||
"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": file_sha256(Path(__file__)),
|
||||
"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,
|
||||
}
|
||||
|
||||
|
||||
def run(dataset: Path = DATASET, radii: tuple[int, ...] = RADII, offsets: tuple[int, ...] = OFFSETS,
|
||||
freeze_path: Path = FREEZE) -> dict[str, Any]:
|
||||
frozen = json.loads(freeze_path.read_text(encoding="utf-8"))
|
||||
verify_frozen_record(frozen, freeze_record(dataset, radii, offsets))
|
||||
replay_started_at = datetime.now(timezone.utc).isoformat()
|
||||
manifest = json.loads(dataset.read_text(encoding="utf-8"))
|
||||
frozen_files = manifest["frozen_scoring"]["files"]
|
||||
scoring_files = sorted(set(frozen_files + PRODUCTION_FILES))
|
||||
starting_hash = implementation_sha256(scoring_files)
|
||||
validation = validate(dataset)
|
||||
invalid = validation["invalid_cases"]
|
||||
trials = []
|
||||
@@ -164,32 +175,17 @@ def run(dataset: Path = DATASET, radii: tuple[int, ...] = RADII, offsets: tuple[
|
||||
"case_ordinal": index, "offset_minutes": offset, "radius_minutes": radius,
|
||||
**reveal_metrics(rows, candidates, delivery, truth, manifest["benchmark_id"], case["case_id"]),
|
||||
})
|
||||
if implementation_sha256(scoring_files) != starting_hash:
|
||||
raise ValueError("scorer_changed_during_sweep")
|
||||
verify_frozen_record(frozen, freeze_record(dataset, radii, offsets))
|
||||
return {
|
||||
"scope": "reported_offset_sensitivity_not_product_accuracy",
|
||||
"specification": {
|
||||
"ayanamsa": AYANAMSA, "node_mode": NODE_MODE,
|
||||
"radii_minutes": list(radii), "offsets_minutes": list(offsets), "minute_step": MINUTE_STEP,
|
||||
"dataset": dataset.relative_to(ROOT).as_posix(), "dataset_sha256": file_sha256(dataset),
|
||||
"dataset_benchmark_id": manifest["benchmark_id"],
|
||||
"implementation_sha256": implementation_sha256(frozen_files),
|
||||
"implementation_sha256_prefix": implementation_sha256(frozen_files)[:16],
|
||||
"production_scoring_files": scoring_files, "production_scoring_sha256": starting_hash,
|
||||
"research_files": RESEARCH_FILES,
|
||||
"research_implementation_sha256": implementation_sha256(RESEARCH_FILES),
|
||||
"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_matrix_grouped_by_candidate_date",
|
||||
"evaluator_sha256": file_sha256(Path(__file__)),
|
||||
"replay_revision": "candidate_date_grouped_v2",
|
||||
"supersedes": "initial_sweep_invalidated_cross_midnight_transition_anchor",
|
||||
"is_blind_evaluation": False, "truth_hidden_from_ranker": True,
|
||||
"results_previously_seen": True, "must_not_use_for_tuning": True,
|
||||
},
|
||||
"replay_started_at_utc": replay_started_at,
|
||||
"replay_finished_at_utc": datetime.now(timezone.utc).isoformat(),
|
||||
"specification": frozen,
|
||||
"frozen_record": frozen,
|
||||
"freeze_record_path": freeze_path.relative_to(ROOT).as_posix(),
|
||||
"implementation_hash_matches_at_replay": True,
|
||||
"dataset_hash_matches_at_replay": True,
|
||||
"historical_comparison": historical_comparison(LEGACY_REPORT, trials, ("case_ordinal", "radius_minutes", "offset_minutes")),
|
||||
"excluded_cases": invalid, "case_count": validation["valid_public_aa_cases"],
|
||||
"trial_count": len(trials), "trials": trials,
|
||||
"summary": summarize(trials, radii, offsets),
|
||||
@@ -207,5 +203,13 @@ def run(dataset: Path = DATASET, radii: tuple[int, ...] = RADII, offsets: tuple[
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--json", action="store_true")
|
||||
parser.add_argument("--freeze", action="store_true", help="Exclusively create the record before replay")
|
||||
parser.add_argument("--freeze-path", type=Path, default=FREEZE)
|
||||
args = parser.parse_args()
|
||||
print(json.dumps(run(), ensure_ascii=False, indent=2))
|
||||
if args.freeze:
|
||||
with args.freeze_path.open("x", encoding="utf-8", newline="\n") as handle:
|
||||
json.dump(freeze_record(), handle, ensure_ascii=False, indent=2)
|
||||
handle.write("\n")
|
||||
print(args.freeze_path.relative_to(ROOT).as_posix())
|
||||
else:
|
||||
print(json.dumps(run(freeze_path=args.freeze_path), ensure_ascii=False, indent=2))
|
||||
|
||||
@@ -27,8 +27,27 @@ from scripts.minute_rectification_feature_facts_v4 import build_feature_fact_row
|
||||
from scripts.minute_rectification_holdout_validator import validate
|
||||
|
||||
DATASET = ROOT / "references/real_case_calibration/minute_rectification_holdout_v3.json"
|
||||
FREEZE = ROOT / "docs/research/sealed_holdout_rerun_2026_09_20.freeze.json"
|
||||
REPORT = ROOT / "docs/research/sealed_holdout_rerun_2026_09_20.json"
|
||||
FREEZE = ROOT / "docs/research/sealed_holdout_rerun_cross_midnight_2026_09_20.final.freeze.json"
|
||||
REPORT = ROOT / "docs/research/sealed_holdout_rerun_cross_midnight_2026_09_20.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 = [
|
||||
"scripts/rectification/scoring_service.py",
|
||||
"scripts/rectification/dasha_transition_proximity.py",
|
||||
"scripts/rectification/candidate_contrast.py",
|
||||
"scripts/rectification/case_holdout.py",
|
||||
"scripts/rectification/contracts.py",
|
||||
"scripts/rectification/event_probes.py",
|
||||
]
|
||||
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",
|
||||
]
|
||||
|
||||
|
||||
def file_sha256(path: Path) -> str:
|
||||
@@ -43,11 +62,70 @@ def opaque_order(benchmark_id: str, case_id: str, rows: list[dict[str, Any]]) ->
|
||||
))
|
||||
|
||||
|
||||
def implementation_identity(dataset: Path = DATASET) -> dict[str, Any]:
|
||||
"""Bind legacy scorer, changed production files and both replay adapters.
|
||||
|
||||
The production identity is contextual for the shadow rerun, not a claim
|
||||
that the shadow scorer executes transition proximity. These are explicit
|
||||
audited file sets, not a transitive dependency or environment lock.
|
||||
"""
|
||||
legacy_files = json.loads(dataset.read_text(encoding="utf-8"))["frozen_scoring"]["files"]
|
||||
production_files = sorted(set(legacy_files + PRODUCTION_FILES))
|
||||
return {
|
||||
"historical_artifacts_manifest_sha256": file_sha256(ARCHIVE / "manifest.json"),
|
||||
"production_scoring_files": production_files,
|
||||
"production_scoring_sha256": implementation_sha256(production_files),
|
||||
"research_files": RESEARCH_FILES,
|
||||
"research_implementation_sha256": implementation_sha256(RESEARCH_FILES),
|
||||
"file_sha256": {path: file_sha256(ROOT / path) for path in sorted(set(production_files + RESEARCH_FILES))},
|
||||
"hash_scope": "explicit_identity_file_sets_not_a_transitive_dependency_lock",
|
||||
}
|
||||
|
||||
|
||||
def verify_frozen_record(frozen: dict[str, Any], actual: dict[str, Any]) -> None:
|
||||
for key in actual:
|
||||
if key != "frozen_at_utc" and actual[key] != frozen.get(key):
|
||||
raise ValueError(f"frozen_record_mismatch:{key}")
|
||||
if set(frozen) != set(actual):
|
||||
raise ValueError("frozen_record_mismatch:keys")
|
||||
|
||||
|
||||
def historical_comparison(report_path: Path, trials: list[dict[str, Any]], keys: tuple[str, ...]) -> dict[str, Any]:
|
||||
"""Keep every before/after row, including unchanged rows and failures."""
|
||||
history_path = ARCHIVE / report_path.relative_to(ROOT)
|
||||
archive_manifest = json.loads((ARCHIVE / "manifest.json").read_text(encoding="utf-8"))
|
||||
entry = next(item for item in archive_manifest["files"] if item["source_path"] == report_path.relative_to(ROOT).as_posix())
|
||||
if file_sha256(history_path) != entry["sha256"]:
|
||||
raise ValueError("historical_report_bytes_changed")
|
||||
previous = json.loads(history_path.read_text(encoding="utf-8"))
|
||||
identity = lambda row: tuple(row[key] for key in keys)
|
||||
old = {identity(row): row for row in previous["trials"]}
|
||||
new = {identity(row): row for row in trials}
|
||||
if len(old) != len(previous["trials"]) or len(new) != len(trials) or old.keys() != new.keys():
|
||||
raise ValueError("historical_comparison_trial_identity_mismatch")
|
||||
comparisons = [{
|
||||
**{key: row[key] for key in keys},
|
||||
"before": old[identity(row)], "after": row,
|
||||
"changed_fields": sorted(key for key in set(row) | set(old[identity(row)])
|
||||
if row.get(key) != old[identity(row)].get(key)),
|
||||
} for row in trials]
|
||||
return {
|
||||
"source_report": history_path.relative_to(ROOT).as_posix(),
|
||||
"source_report_sha256": file_sha256(history_path),
|
||||
"reason": "BUG-981 new production identity and native single-matrix sweep; prior grouped sweep/shadow metrics not presumed incorrect",
|
||||
"trial_count": len(comparisons),
|
||||
"changed_trial_count": sum(bool(row["changed_fields"]) for row in comparisons),
|
||||
"trials": comparisons,
|
||||
}
|
||||
|
||||
|
||||
def freeze_record(dataset: Path = DATASET) -> dict[str, Any]:
|
||||
manifest = json.loads(dataset.read_text(encoding="utf-8"))
|
||||
files = manifest["frozen_scoring"]["files"]
|
||||
return {
|
||||
"record_version": "exposed-v3-fixed-protocol-rerun-v1",
|
||||
"record_version": "exposed-v3-fixed-protocol-cross-midnight-rerun-v2",
|
||||
"extended_identity": implementation_identity(dataset),
|
||||
"production_identity_scope": "context_only_shadow_scorer_does_not_call_transition_proximity",
|
||||
"frozen_at_utc": datetime.now(timezone.utc).isoformat(),
|
||||
"dataset_path": dataset.relative_to(ROOT).as_posix(),
|
||||
"dataset_sha256": file_sha256(dataset),
|
||||
@@ -73,9 +151,8 @@ def freeze_record(dataset: Path = DATASET) -> dict[str, Any]:
|
||||
def run(freeze_path: Path = FREEZE, dataset: Path = DATASET) -> dict[str, Any]:
|
||||
frozen = json.loads(freeze_path.read_text(encoding="utf-8"))
|
||||
actual = freeze_record(dataset)
|
||||
for key in actual:
|
||||
if key != "frozen_at_utc" and actual[key] != frozen.get(key):
|
||||
raise ValueError(f"frozen_record_mismatch:{key}")
|
||||
verify_frozen_record(frozen, actual)
|
||||
replay_started_at = datetime.now(timezone.utc).isoformat()
|
||||
manifest = json.loads(dataset.read_text(encoding="utf-8"))
|
||||
validation = validate(dataset)
|
||||
invalid = validation["invalid_cases"]
|
||||
@@ -110,10 +187,14 @@ def run(freeze_path: Path = FREEZE, dataset: Path = DATASET) -> dict[str, Any]:
|
||||
"full_trial_reasons": result["reasons"],
|
||||
"sparse_trial_reasons": sparse_result["reasons"],
|
||||
})
|
||||
verify_frozen_record(frozen, freeze_record(dataset))
|
||||
aggregate = summarize_trials(trials, manifest["release_metrics"])
|
||||
count = len(trials)
|
||||
return {
|
||||
"scope": "fixed_protocol_previously_exposed_v3_rerun",
|
||||
"replay_started_at_utc": replay_started_at,
|
||||
"replay_finished_at_utc": datetime.now(timezone.utc).isoformat(),
|
||||
"historical_comparison": historical_comparison(LEGACY_REPORT, trials, ("case_ordinal",)),
|
||||
"evaluated_on": datetime.now(timezone.utc).date().isoformat(),
|
||||
"frozen_record": frozen,
|
||||
"implementation_hash_matches_at_replay": True,
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"confirmation_allowed": false,
|
||||
"accept_allowed": false,
|
||||
"confirm_allowed": false,
|
||||
"representative_candidate_id": "85ca5490-07ab-54b7-acf0-59385f705015",
|
||||
"representative_candidate_id": "e8c11277-022c-519c-95e9-6a0a04e8bf23",
|
||||
"representative_time": "12:00",
|
||||
"overall_confidence": "low",
|
||||
"margin_percent": 5.3006,
|
||||
@@ -1150,7 +1150,7 @@
|
||||
},
|
||||
"candidate_feature_snapshot": {
|
||||
"calculation_spec_hash": "e03dfc555a247e78549768cc0b487cb427414633982212debd9cd6e60bb67292",
|
||||
"algorithm_version": "rectification-v5-matrix-scoring-7",
|
||||
"algorithm_version": "rectification-v5-matrix-scoring-8",
|
||||
"candidate_count": 3,
|
||||
"feature_hash": "36190982db75d3c9fe7c584607f25342531049c3d751060075519dbbd68033db",
|
||||
"features": [
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
"""Candidate-local dasha dates; public AA replay and synthetic cache boundaries."""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from scripts.active_rectification_event_engine import compute_candidate_static_contexts
|
||||
from scripts.rectification import dasha_transition_proximity as proximity
|
||||
from scripts.rectification.scoring_service import (
|
||||
build_event_contribution_matrix,
|
||||
public_technique_layers,
|
||||
score_from_matrix,
|
||||
)
|
||||
from scripts.research.reported_offset_sweep import shifted_window
|
||||
from scripts.research.sealed_holdout_rerun import DATASET
|
||||
|
||||
|
||||
def _canonical(value):
|
||||
return json.dumps(value, sort_keys=True, separators=(",", ":")).encode()
|
||||
|
||||
|
||||
def _cases():
|
||||
return json.loads(DATASET.read_text(encoding="utf-8"))["cases"]
|
||||
|
||||
|
||||
def test_fixed_scoring_identity_is_exposed_without_changing_input_contract():
|
||||
from scripts.rectification.api_service import engine_scoring_versions
|
||||
from scripts.rectification.scoring_service import ALGORITHM_VERSION, INPUT_CONTRACT_VERSION
|
||||
|
||||
assert engine_scoring_versions()["algorithm_version"] == ALGORITHM_VERSION == "rectification-v5-matrix-scoring-8"
|
||||
assert INPUT_CONTRACT_VERSION == "rectification-calculation-spec-v4"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("start", ["2000-01-01", "2000-02-29", "2000-12-31"])
|
||||
def test_every_candidate_uses_own_date_and_caches_do_not_cross_dates(monkeypatch, start):
|
||||
# Intentionally identical synthetic chart values: only date distinguishes caches.
|
||||
anchor = date.fromisoformat(start)
|
||||
moments = [datetime.combine(anchor, datetime.min.time()) + timedelta(hours=23, minutes=50+i)
|
||||
for i in range(21)]
|
||||
contexts = [{"candidate_at": at, "feature": {"time": at.strftime("%H:%M")},
|
||||
"planet_longitudes": {"Moon": 42.0}, "ascendant_index": 1}
|
||||
for at in moments]
|
||||
event_date = date(2020, 1, 10)
|
||||
calls = {"ad": [], "pd": [], "narayana": []}
|
||||
|
||||
def vim(birth_date, moon, lo, hi, *, include_pratyantar=False):
|
||||
calls["pd" if include_pratyantar else "ad"].append(birth_date)
|
||||
delta = (date.fromisoformat(birth_date) - anchor).days
|
||||
return [event_date + timedelta(days=delta + (3 if include_pratyantar else 2))]
|
||||
|
||||
def narayana(asc, planets, birth_date, lo, hi):
|
||||
calls["narayana"].append(birth_date)
|
||||
delta = (date.fromisoformat(birth_date) - anchor).days
|
||||
return [event_date + timedelta(days=delta + 4)]
|
||||
|
||||
monkeypatch.setattr(proximity, "_vim_start_dates", vim)
|
||||
monkeypatch.setattr(proximity, "_narayana_start_dates", narayana)
|
||||
# Two events sharing the year band exercise cache reuse, not merely a new call.
|
||||
events = [{"id": precision, "domain": "career", "precision": precision,
|
||||
"date_start": event_date.isoformat(), "date_end": event_date.isoformat()}
|
||||
for precision in ("day", "month")]
|
||||
matrix = {event["id"]: {at.strftime("%H:%M"): {"points": 2.0, "rule_ids": []}
|
||||
for at in moments} for event in events}
|
||||
proximity.merge_transition_proximity(matrix, events, contexts, start,
|
||||
public_technique_layers=public_technique_layers)
|
||||
for at in moments:
|
||||
delta = (at.date() - anchor).days
|
||||
for precision in ("day", "month"):
|
||||
expected = proximity.score_transition_proximity(
|
||||
event_date=event_date, precision=precision,
|
||||
vim_starts=[event_date + timedelta(days=delta + 2)],
|
||||
vim_pd_starts=[event_date + timedelta(days=delta + 3)],
|
||||
narayana_starts=[event_date + timedelta(days=delta + 4)],
|
||||
)
|
||||
actual = matrix[precision][at.strftime("%H:%M")]
|
||||
assert actual["points"] == round(2.0 + expected["points"], 4)
|
||||
assert actual["rule_ids"] == sorted(expected["rule_ids"])
|
||||
expected_dates = [anchor.isoformat(), (anchor + timedelta(days=1)).isoformat()]
|
||||
assert calls == {kind: expected_dates for kind in calls}
|
||||
|
||||
|
||||
def test_legacy_context_without_candidate_at_retains_request_date(monkeypatch):
|
||||
calls = []
|
||||
|
||||
def vim(birth_date, moon, lo, hi, **kwargs):
|
||||
calls.append(birth_date)
|
||||
return []
|
||||
|
||||
monkeypatch.setattr(proximity, "_vim_start_dates", vim)
|
||||
monkeypatch.setattr(proximity, "_narayana_start_dates", lambda *args: [])
|
||||
proximity.merge_transition_proximity(
|
||||
{"event": {"12:00": {"points": 2.0}}},
|
||||
[{"id": "event", "precision": "day", "date": "2020-01-10"}],
|
||||
[{"feature": {"time": "12:00"}, "planet_longitudes": {"Moon": 42.0}}],
|
||||
"2000-01-01", public_technique_layers=public_technique_layers,
|
||||
)
|
||||
assert calls == ["2000-01-01", "2000-01-01"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ordinal,score_sha256", [
|
||||
(1, "2aabdda6bb56baf6a9d0b119964ee1ab8023fede9ea8703f6d265207c490bec2"),
|
||||
(2, "6585d895aeb79e26257b1002696c16b5e252f9a702f2a5702f929fd80486abb9"),
|
||||
(3, "246296903d915e4886229530fa554428d7f65fb686f70cc369711347ebaf2610"),
|
||||
])
|
||||
def test_same_day_public_aa_scores_keep_pre_fix_bytes(ordinal, score_sha256):
|
||||
# Golden hashes captured from the unmodified production path, 121 minutes each.
|
||||
# Only ordinal and score bytes are retained; no birth data or coordinates.
|
||||
request, moments = shifted_window(_cases()[ordinal - 1], 0, 60)
|
||||
assert len({moment.date() for moment in moments}) == 1
|
||||
built = build_event_contribution_matrix(request)
|
||||
scores = [row["score"] for row in score_from_matrix(request, built)]
|
||||
assert len(scores) == 121
|
||||
assert hashlib.sha256(_canonical(scores)).hexdigest() == score_sha256
|
||||
|
||||
|
||||
def test_real_cross_midnight_all_candidates_match_independent_dated_calculation(monkeypatch):
|
||||
# Existing public AA case naturally crosses midnight at radius 60; no birth mutation.
|
||||
request, moments = shifted_window(_cases()[5], 0, 60)
|
||||
contexts = compute_candidate_static_contexts(request)
|
||||
assert [context["candidate_at"] for context in contexts] == moments
|
||||
assert len({moment.date() for moment in moments}) == 2
|
||||
original_vim, original_narayana = proximity._vim_start_dates, proximity._narayana_start_dates
|
||||
seen_vim, seen_narayana = set(), set()
|
||||
|
||||
def vim(birth_date, moon, lo, hi, *, include_pratyantar=False):
|
||||
seen_vim.add((birth_date, round(moon, 6), include_pratyantar))
|
||||
return original_vim(birth_date, moon, lo, hi, include_pratyantar=include_pratyantar)
|
||||
|
||||
def narayana(asc, planets, birth_date, lo, hi):
|
||||
seen_narayana.add((birth_date, asc, round(planets["Moon"], 6)))
|
||||
return original_narayana(asc, planets, birth_date, lo, hi)
|
||||
|
||||
with monkeypatch.context() as capture:
|
||||
capture.setattr(proximity, "_vim_start_dates", vim)
|
||||
capture.setattr(proximity, "_narayana_start_dates", narayana)
|
||||
actual = build_event_contribution_matrix(request, static_contexts=contexts)
|
||||
expected_rows, expected_matrix = [], {}
|
||||
for context in contexts:
|
||||
candidate_date = context["candidate_at"].date().isoformat()
|
||||
dated_request = {**request, "birth_date": candidate_date}
|
||||
# One correctly dated candidate per independent matrix, with fresh local caches.
|
||||
built = build_event_contribution_matrix(dated_request, static_contexts=[context])
|
||||
expected_rows.extend(score_from_matrix(dated_request, built))
|
||||
for event_id, cells in built["matrix"].items():
|
||||
expected_matrix.setdefault(event_id, {}).update(cells)
|
||||
assert _canonical(actual["matrix"]) == _canonical(expected_matrix)
|
||||
assert _canonical(score_from_matrix(request, actual)) == _canonical(expected_rows)
|
||||
for context in contexts:
|
||||
candidate_date = context["candidate_at"].date().isoformat()
|
||||
moon = round(context["planet_longitudes"]["Moon"], 6)
|
||||
assert (candidate_date, moon, False) in seen_vim
|
||||
assert (candidate_date, moon, True) in seen_vim
|
||||
assert (candidate_date, context["ascendant_index"], moon) in seen_narayana
|
||||
@@ -117,6 +117,9 @@ def test_sealed_holdout_contract_matches_v3_report_and_stays_closed() -> None:
|
||||
assert produced_by["implementation_hash_matches_at_replay"] is True
|
||||
assert produced_by["source_report"] == "references/real_case_calibration/minute_rectification_holdout_v3_report.json"
|
||||
assert current_tree["implementation_sha256"] == rerun["frozen_record"]["implementation_sha256"]
|
||||
assert current_tree["extended_identity"] == rerun["frozen_record"]["extended_identity"]
|
||||
assert "scripts/rectification/dasha_transition_proximity.py" in current_tree["extended_identity"]["production_scoring_files"]
|
||||
assert product["current_tree_fixed_protocol_rerun"]["extended_identity"] == current_tree["extended_identity"]
|
||||
assert current_tree["fixed_protocol_rerun_hash_matches"] is rerun["implementation_hash_matches_at_replay"] is True
|
||||
assert current_tree["fixed_protocol_rerun_trial_count"] == rerun["trial_count"] == 20
|
||||
assert rerun["official_valid_independent_blind"] is False
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
"""Collect candidate-date regressions through quick's test_rectification_*.py glob."""
|
||||
|
||||
from tests.test_dasha_transition_proximity_cross_midnight import ( # noqa: F401
|
||||
test_every_candidate_uses_own_date_and_caches_do_not_cross_dates,
|
||||
test_fixed_scoring_identity_is_exposed_without_changing_input_contract,
|
||||
test_legacy_context_without_candidate_at_retains_request_date,
|
||||
test_real_cross_midnight_all_candidates_match_independent_dated_calculation,
|
||||
test_same_day_public_aa_scores_keep_pre_fix_bytes,
|
||||
)
|
||||
@@ -2,6 +2,12 @@
|
||||
|
||||
Golden payload in tests/golden/rectification_engine_memoization_v1.json was
|
||||
produced from origin/staging @ a8d29d1b before any memoization landed.
|
||||
On 2026-09-20 only two identity leaves were refreshed from the real engine:
|
||||
representative_candidate_id 85ca5490-07ab-54b7-acf0-59385f705015 ->
|
||||
e8c11277-022c-519c-95e9-6a0a04e8bf23, and snapshot algorithm_version
|
||||
rectification-v5-matrix-scoring-7 -> rectification-v5-matrix-scoring-8.
|
||||
The authorized cross-midnight version bump changes UUID identity, not this
|
||||
same-day fixture's scores. Historical numeric values and feature hashes remain.
|
||||
|
||||
Do not compare that payload with a whole-structure ``==``. Cross-machine
|
||||
libm / pyswisseph rounding already drifted ``margin_percent`` by 1.1e-3
|
||||
|
||||
@@ -19,7 +19,8 @@ def _row(time: str, score: float) -> dict:
|
||||
class RelativeSupportScaleTest(unittest.TestCase):
|
||||
def test_holdout_gate_keeps_proportional_default(self) -> None:
|
||||
self.assertEqual(POLICY_VERSION, "rectification-candidate-policy-v3")
|
||||
self.assertEqual(ALGORITHM_VERSION, "rectification-v5-matrix-scoring-7")
|
||||
# Cross-midnight date fix: scoring identity -7 -> -8, not a prior/policy change.
|
||||
self.assertEqual(ALGORITHM_VERSION, "rectification-v5-matrix-scoring-8")
|
||||
self.assertEqual(RELATIVE_SUPPORT_MODE, "proportional")
|
||||
|
||||
def test_offset_top_two_lead_is_at_least_proportional(self) -> None:
|
||||
|
||||
@@ -7,10 +7,13 @@ from tests.test_reported_offset_research import ( # noqa: F401
|
||||
test_recorded_specification_and_all_prespecified_cells,
|
||||
test_shifted_window_preserves_dates_across_midnight,
|
||||
test_zero_offset_centres_on_truth_and_has_complete_grid,
|
||||
test_sweep_changed_frozen_identity_fails_before_scoring,
|
||||
)
|
||||
from tests.test_sealed_holdout_contract_freshness import ( # noqa: F401
|
||||
test_changed_frozen_identity_fails_before_any_replay,
|
||||
test_contract_tracks_actual_current_scorer_and_dataset_audit,
|
||||
test_fixed_protocol_rerun_is_auditable_but_never_independent_blind,
|
||||
test_frozen_record_matches_dataset_scorer_and_evaluator_bytes,
|
||||
test_extended_identity_drift_rejected_even_when_legacy_hash_unchanged,
|
||||
test_historical_artifacts_are_byte_preserved_not_refreshed,
|
||||
)
|
||||
|
||||
@@ -68,25 +68,33 @@ def test_shifted_window_preserves_dates_across_midnight(clock, offset):
|
||||
assert metrics["delivery_width_minutes"] == 31
|
||||
|
||||
|
||||
def test_cross_midnight_real_engine_scores_match_candidate_date_replay():
|
||||
def test_cross_midnight_real_engine_scores_match_candidate_date_replay(monkeypatch):
|
||||
case = json.loads(DATASET.read_text(encoding="utf-8"))["cases"][5]
|
||||
request, candidates = sweep.shifted_window(case, 0, 60)
|
||||
assert len({candidate.date() for candidate in candidates}) == 2
|
||||
contexts = sweep.compute_candidate_static_contexts(request, candidates=candidates)
|
||||
grouped = sweep.score_window(request, contexts)
|
||||
calls = []
|
||||
build = sweep.build_event_contribution_matrix
|
||||
def traced(request, **kwargs):
|
||||
calls.append(len(kwargs["static_contexts"]))
|
||||
return build(request, **kwargs)
|
||||
with monkeypatch.context() as patch:
|
||||
patch.setattr(sweep, "build_event_contribution_matrix", traced)
|
||||
native = sweep.score_window(request, contexts)
|
||||
assert calls == [len(contexts)]
|
||||
expected = []
|
||||
for context in contexts:
|
||||
dated = {**request, "birth_date": context["candidate_at"].date().isoformat()}
|
||||
built = sweep.build_event_contribution_matrix(dated, static_contexts=[context])
|
||||
expected.extend(sweep.score_from_matrix(dated, built))
|
||||
assert grouped == expected
|
||||
old_matrix = sweep.build_event_contribution_matrix(request, static_contexts=contexts)
|
||||
old_rows = sweep.score_from_matrix(request, old_matrix)
|
||||
assert old_rows != expected
|
||||
assert native == expected
|
||||
native_matrix = sweep.build_event_contribution_matrix(request, static_contexts=contexts)
|
||||
native_rows = sweep.score_from_matrix(request, native_matrix)
|
||||
assert native_rows == expected
|
||||
|
||||
|
||||
def test_recorded_specification_and_all_prespecified_cells():
|
||||
report = json.loads((sweep.ROOT / "docs/research/reported_offset_2026_09_20.json").read_text(encoding="utf-8"))
|
||||
report = json.loads(sweep.REPORT.read_text(encoding="utf-8"))
|
||||
spec = report["specification"]
|
||||
assert spec["ayanamsa"] == "raman"
|
||||
assert spec["node_mode"] == "mean"
|
||||
@@ -98,7 +106,17 @@ def test_recorded_specification_and_all_prespecified_cells():
|
||||
assert spec["evaluator_sha256"] == sweep.file_sha256(sweep.ROOT / "scripts/research/reported_offset_sweep.py")
|
||||
assert spec["production_scoring_sha256"] == sweep.implementation_sha256(spec["production_scoring_files"])
|
||||
assert spec["research_implementation_sha256"] == sweep.implementation_sha256(spec["research_files"])
|
||||
assert spec["replay_revision"] == "candidate_date_grouped_v2"
|
||||
assert spec["replay_revision"] == "native_candidate_date_v3"
|
||||
frozen = json.loads(sweep.FREEZE.read_text(encoding="utf-8"))
|
||||
assert report["frozen_record"] == spec == frozen
|
||||
sweep.verify_frozen_record(frozen, sweep.freeze_record())
|
||||
assert report["implementation_hash_matches_at_replay"] is True
|
||||
assert report["dataset_hash_matches_at_replay"] is True
|
||||
assert frozen["frozen_at_utc"] <= report["replay_started_at_utc"] <= report["replay_finished_at_utc"]
|
||||
assert spec["official_valid_independent_blind"] is False
|
||||
assert spec["official_blind_trial_count"] == 0
|
||||
assert spec["results_previously_seen"] is True
|
||||
assert spec["must_not_use_for_tuning"] is True
|
||||
assert spec["truth_hidden_from_ranker"] is True
|
||||
assert spec["is_blind_evaluation"] is False
|
||||
assert report["trial_count"] == 20 * len(sweep.RADII) * len(sweep.OFFSETS)
|
||||
@@ -115,3 +133,19 @@ def test_recorded_specification_and_all_prespecified_cells():
|
||||
assert row["truth_in_window_rate"] == expected
|
||||
assert row["delivery_coverage_rate"] <= expected
|
||||
assert row["top_1_rate"] <= expected
|
||||
assert report["historical_comparison"] == sweep.historical_comparison(
|
||||
sweep.LEGACY_REPORT, report["trials"], ("case_ordinal", "radius_minutes", "offset_minutes"),
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("key", ["dataset_sha256", "production_scoring_sha256", "research_implementation_sha256", "evaluator_sha256"])
|
||||
def test_sweep_changed_frozen_identity_fails_before_scoring(tmp_path, monkeypatch, key):
|
||||
frozen = sweep.freeze_record()
|
||||
frozen[key] = "0" * 64
|
||||
path = tmp_path / "bad-freeze.json"
|
||||
path.write_text(json.dumps(frozen), encoding="utf-8")
|
||||
def unexpected(*args, **kwargs):
|
||||
raise AssertionError("must reject identity before scoring")
|
||||
monkeypatch.setattr(sweep, "compute_candidate_static_contexts", unexpected)
|
||||
with pytest.raises(ValueError, match=f"frozen_record_mismatch:{key}"):
|
||||
sweep.run(freeze_path=path)
|
||||
|
||||
@@ -7,7 +7,10 @@ import pytest
|
||||
|
||||
from scripts.minute_rectification_blind_eval import implementation_sha256, summarize_trials
|
||||
from scripts.rectification.sealed_holdout import holdout_passed, load_sealed_minute_holdout
|
||||
from scripts.research.sealed_holdout_rerun import DATASET, FREEZE, REPORT, file_sha256, freeze_record, run
|
||||
from scripts.research.sealed_holdout_rerun import (
|
||||
ARCHIVE, DATASET, FREEZE, REPORT, LEGACY_REPORT, PRODUCTION_FILES, file_sha256,
|
||||
freeze_record, historical_comparison, implementation_identity, run,
|
||||
)
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
@@ -21,6 +24,12 @@ def test_contract_tracks_actual_current_scorer_and_dataset_audit():
|
||||
contract = read(ROOT / "references/rectification_sealed_holdout.v1.json")
|
||||
actual_hash = implementation_sha256(dataset["frozen_scoring"]["files"])
|
||||
assert contract["current_tree_scorer"]["implementation_sha256"] == actual_hash
|
||||
assert contract["current_tree_scorer"]["extended_identity"] == implementation_identity()
|
||||
historical_contract = read(ARCHIVE / "references/rectification_sealed_holdout.v1.json")
|
||||
runtime_keys = ("status", "valid_public_aa_cases", "required_cases", "top_1_rate", "confirmation_coverage_rate", "sealed_benchmark_id")
|
||||
for key in runtime_keys:
|
||||
assert type(contract[key]) is type(historical_contract[key])
|
||||
assert contract[key] == historical_contract[key]
|
||||
assert contract["source_audit_status"] == dataset["source_audit_status"]
|
||||
assert contract["evaluated_on"] == read(REPORT)["evaluated_on"]
|
||||
assert contract["status"] == "not_ready"
|
||||
@@ -37,6 +46,11 @@ def test_frozen_record_matches_dataset_scorer_and_evaluator_bytes():
|
||||
assert frozen["dataset_sha256"] == file_sha256(DATASET)
|
||||
assert len(frozen["files"]) == 12
|
||||
assert read(DATASET)["frozen_scoring"]["implementation_sha256"] == frozen["historical_frozen_sha256"]
|
||||
assert frozen["extended_identity"] == implementation_identity()
|
||||
assert set(PRODUCTION_FILES) <= set(frozen["extended_identity"]["production_scoring_files"])
|
||||
assert {"scripts/rectification/scoring_service.py", "scripts/rectification/dasha_transition_proximity.py"} <= set(PRODUCTION_FILES)
|
||||
for path, digest in frozen["extended_identity"]["file_sha256"].items():
|
||||
assert digest == file_sha256(ROOT / path)
|
||||
|
||||
|
||||
def test_fixed_protocol_rerun_is_auditable_but_never_independent_blind():
|
||||
@@ -45,6 +59,11 @@ def test_fixed_protocol_rerun_is_auditable_but_never_independent_blind():
|
||||
scorer = contract["current_tree_scorer"]
|
||||
rerun = contract["current_tree_fixed_protocol_rerun"]
|
||||
assert report["frozen_record"] == read(FREEZE)
|
||||
assert report["frozen_record"]["frozen_at_utc"] <= report["replay_started_at_utc"] <= report["replay_finished_at_utc"]
|
||||
assert scorer["extended_identity"] == report["frozen_record"]["extended_identity"]
|
||||
assert rerun["extended_identity"] == scorer["extended_identity"]
|
||||
assert rerun["freeze_record_path"] == FREEZE.relative_to(ROOT).as_posix()
|
||||
assert report["historical_comparison"] == historical_comparison(LEGACY_REPORT, report["trials"], ("case_ordinal",))
|
||||
assert report["trial_count"] == len(report["trials"]) == 20
|
||||
assert report["excluded_cases"] == []
|
||||
aggregate = summarize_trials(report["trials"], read(DATASET)["release_metrics"])
|
||||
@@ -79,3 +98,42 @@ def test_changed_frozen_identity_fails_before_any_replay(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr("scripts.research.sealed_holdout_rerun.build_feature_fact_rows", unexpected)
|
||||
with pytest.raises(ValueError, match="frozen_record_mismatch:implementation_sha256"):
|
||||
run(path)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", ["scripts/rectification/scoring_service.py", "scripts/rectification/dasha_transition_proximity.py"])
|
||||
def test_extended_identity_drift_rejected_even_when_legacy_hash_unchanged(tmp_path, monkeypatch, path):
|
||||
from scripts.research import sealed_holdout_rerun as replay
|
||||
frozen = replay.freeze_record()
|
||||
freeze_path = tmp_path / "extended-freeze.json"
|
||||
freeze_path.write_text(json.dumps(frozen), encoding="utf-8")
|
||||
original = replay.implementation_identity
|
||||
def changed(dataset=DATASET):
|
||||
actual = original(dataset)
|
||||
actual["file_sha256"][path] = "0" * 64
|
||||
return actual
|
||||
monkeypatch.setattr(replay, "implementation_identity", changed)
|
||||
def unexpected(*args, **kwargs):
|
||||
raise AssertionError("extended drift must reject before shadow scoring")
|
||||
monkeypatch.setattr(replay, "build_feature_fact_rows", unexpected)
|
||||
assert frozen["implementation_sha256"] == replay.freeze_record()["implementation_sha256"]
|
||||
with pytest.raises(ValueError, match="frozen_record_mismatch:extended_identity"):
|
||||
replay.run(freeze_path)
|
||||
|
||||
|
||||
def test_historical_artifacts_are_byte_preserved_not_refreshed():
|
||||
manifest = read(ARCHIVE / "manifest.json")
|
||||
assert file_sha256(ARCHIVE / "manifest.json") == "6102a26a840be207b5858b3a4c0509274468ae9071e86d308cbbac7371d6864e"
|
||||
assert len(manifest["files"]) == 8
|
||||
for record in manifest["files"]:
|
||||
archived = ROOT / record["archive_path"]
|
||||
assert archived.stat().st_size == record["size_bytes"]
|
||||
assert file_sha256(archived) == record["sha256"]
|
||||
old_freeze = ROOT / "docs/research/sealed_holdout_rerun_2026_09_20.freeze.json"
|
||||
assert old_freeze.read_bytes() == (ARCHIVE / old_freeze.relative_to(ROOT)).read_bytes()
|
||||
assert file_sha256(old_freeze) == "d17651cb50224acdd1af7a4692c777ed169b216a962a0675371254ce561d0921"
|
||||
assert FREEZE != old_freeze
|
||||
for name, digest in (
|
||||
("reported_offset_2026_09_20.json", "9878f2b50c957a470fafcb2ed0a9eb16405c3f6fa422ea50f467e84b0189ace1"),
|
||||
("sealed_holdout_rerun_2026_09_20.json", "40df220bfcb51a683b31fbd626896e47a5aed7ee4f30ff669543691f7ce53c1e"),
|
||||
):
|
||||
assert file_sha256(ARCHIVE / "docs/research" / name) == digest
|
||||
|
||||
Reference in New Issue
Block a user