创建人员:dev.developer.project.secondary / infodev-2
事项:DEV-ANA-STOCK-VALUATION-PIPELINE-V2-20260801-001
性质:仅关闭实现复审 F5-R1 的 receipt 自指时序阻断;V003+V004+V005+V006 与已关闭 F1、其余实现修复合同全部继承。
当前状态:PENDING_RECEIPT_TIMING_DELTA_REVIEW;本文件 PASS 前不修改 F5 receipt/force 实现。
V004 要求同一 OUT.commit-receipt.json 同时包含 commit_finished_at/task_wall_seconds/process_wall_seconds,且这些值代表该 receipt 自身最终原子写入与持久化完成之后的时刻。任何有限次实现都无法满足:先取时刻再写入,时刻早于这次写;写完再取时刻并回写,又产生一次更晚的最终写。当前实现以第二次写入前的时刻冒充写入完成,reviewer 对最终写单独延迟 0.25 秒后复现:实际成功返回 0.617s,receipt/stdout process_wall_seconds=0.359s,遗漏 0.258s。
因此本修订不再让 receipt 自报其自身写后时刻。receipt 负责不可变地绑定 manifest 和提交身份;stdout 在 receipt 已原子写入、重新读取并复算 hash/bytes、完成 backup cleanup 尝试后记录最终墙钟。这样不存在自指,并能证明最终墙钟不早于 receipt 写入。
本节仅替换 V004 第 3.3 节及第 4 节中“receipt 包含最终双墙钟、stdout 双墙钟逐值匹配 receipt”的条款。正式文件集合、receipt 路径、ticker stdout 精确字段、失败包和全部其他状态矩阵不变。
OUT.commit-receipt.json 升级为 schema 2,精确字段固定为:
{
"schema_version": 2,
"run_id": "string",
"status": "COMPLETE|COMPLETE_WITH_GAPS|DATA_READY_NEEDS_JUDGMENT",
"output_path": "absolute-string",
"manifest_path": "absolute-string",
"manifest_sha256": "64-lowercase-hex",
"manifest_bytes": 0,
"commit_ready_at": "timezone-aware ISO-8601",
"receipt_write_started_at": "timezone-aware ISO-8601",
"task_start": "timezone-aware ISO-8601",
"task_start_source": "arg|process_start",
"process_start": "timezone-aware ISO-8601",
"wall_scope": "through_receipt_write_started"
}
schema 2 receipt 不含 commit_finished_at/task_wall_seconds/process_wall_seconds,也不增加会再次自引用的 receipt hash。receipt_write_started_at 只是写入开始下界,不得被文档或调用者解释为提交完成时刻。
ticker stdout 继续逐字遵循 V004 第 3.2 节的 schema 1 和 null 规则;成功 stdout 的 receipt_path/receipt_sha256/receipt_bytes 必须与最终 schema 2 receipt 实物一致。成功 stdout 的 terminal_at/task_wall_seconds/process_wall_seconds 改为唯一权威最终墙钟,terminal_at 必须在 receipt 原子写入返回、最终实物 hash/bytes 复算和 backup cleanup 尝试之后捕获。包内 runtime_metrics.json 继续是 through_commit_ready,其双墙钟不得大于 stdout 最终双墙钟。
成功路径顺序固定如下,不得调整:
commit_ready_at;receipt_write_started_at,构造一次且仅一次的 schema 2 receipt canonical bytes;write -> flush -> fsync -> os.replace,临时文件/replace 任一点失败都算真正提交失败;receipt_sha256/receipt_bytes,并逐值确认 run/status/output/manifest 指纹;读取、解析或复算失败仍算真正提交失败;terminal_at,计算 stdout 的最终 task/process wall,随后序列化并输出唯一一行 JSON;成功路径此后不得再写 output、receipt、cache 或 baseline。不存在第二次“补写最终墙钟”的 receipt 写入。正式 receipt bytes 一旦第 5 步成功便不可变;stdout 只引用第 6 步复算的最终 hash/bytes。
在第 5 或第 6 步发生 BaseException,均属于 receipt 未完成验证:
failed-<run_id> 归属,删除本次不完整 receipt/临时文件,生成 FAILED/BLOCKED 失败包,禁止成功 stdout;rollback_errors,不得替换原 KeyboardInterrupt/SystemExit;旧正式树和旧 receipt hash 必须与提交前完全一致;receipt 临时文件、正式 replace、最终重新读取/hash/stat、restore、cleanup 都是独立 fault point。路径词法预检和 reparse 规则继续继承 V003 与已完成 F5 修复,不在本修订重开。
新增实际入口测试,不以局部函数或静态字段断言替代:
0.25s:成功实际返回、stdout process_wall_seconds 均包含该延迟,允许计时器 50ms 容差;schema 2 receipt 不出现三个被废止的自指字段;KeyboardInterrupt 和 SystemExit;absent 不留伪成功,force 恢复旧 output/receipt 全树 hash;0.25s 和 cleanup 失败:stdout 最终墙钟包含 cleanup 尝试;失败时新 output/receipt 仍为权威、backup 保留、warning 可消费;task_start<=process_start<=commit_ready_at<=receipt_write_started_at<=terminal_at,task_wall>=process_wall>=0,runtime 双墙钟不大于 stdout;--input 0/2/3 完全不受影响。本限定修订不要求真实网络 smoke;通过 fixture 和受控延迟/故障桩即可复验。实现后应同步工具说明、实现证据和执行日志,明确“receipt 绑定提交身份,stdout 是最终墙钟权威”,不得继续声称 receipt 自报写后时刻。
ana-dev、V1 公式、provider、缓存 freshness、A1、报告、QA 或交易边界;NOT_AUTHORIZED_WHILE_HOLD;dev.reviewer.project 对本 V007 PASS 后才实施第 2—5 节;F2-R1/F3-R1/F4-R1/F6-R1 继续按既有组合方案离线闭环,无需借本修订扩范围。