docs/current/07-质量与裁定/脚本埋雷验证记录.md
GitHub ↗
当前有效

脚本埋雷验证记录(J9 台账)

5767 字·约 15 分钟 阅读 2026-10-10 17:07

判据 J9(裁定 §13.2):判定型脚本的埋雷记录 = 0 时,其"全绿"不得作为任何 结论的依据。每个判定型脚本必须至少留有一条"注入必然违反 → 必须变红"的 实证记录,纳入批次验收(路线图 U0#8 / W0-1)。

记录义务:①注入内容(雷是什么)②红证据(输出与退出码)③还原证据(复绿) ④复现命令。发现脚本自身的坑(雷没炸出来 = 脚本缺陷)同等重要,必须记录并修复。

本台账建册时覆盖三个判定型脚本:shadow_verify(唯一硬门禁)> ci_three_tier_check (失败台账对账核心)> serve_smoke(出口 3 协议冒烟);其后随批增补至 facts / precompile_bytecode_libc,并新增两条 2026-09-29 记录(serve_smoke 超长行、facts 真值越界自检)。

覆盖面演化注记(2026-09-29;2026-10-04 复核补录 S8 批新闸):MoonBit 迁移期新增的判定型脚本与闸 (gen_diag / gen_host_route / gen_stubs / gen_protocol_fields / gen_protocol_ts / gen_svg -check / mbti_sync / libc_single_source / moonbit_surface / pkg_deps / testcount / host_route_coverage / source_hygiene / perf_budget / vendor_drift / clang_direct / vm_diff / toolchain_probe / replay --selftest / diff_ledger --selftest 五路 / teaching_annotation_diff 双路 / protocol_frames 双模式等)的 J9 埋雷 记录以各脚本头注与对应批次提交信息为权威源(见 脚本总清单与必跑防线 入册行与 CI 步骤注释中的证红留痕)——本台账不再逐份转抄;判据不变:任何判定型脚本 埋雷记录 = 0 时,其全绿不得作为结论依据。


shadow_verify(Go 主驱动,C 影子验证硬门禁)

记录 #1 —— 引擎语义突变(tpl_add 加→减)〔2026-09-13〕

  • 注入:native/crates/vitro_vm/src/jit_templates.rs 的 tpl_add 中 wrapping_add → wrapping_sub(JIT 路径算术语义突变,产生静默错值)。
  • 红证据:go run ./scripts/shadow_verify → exit 1,9 例非预期差异 (jit_nested_counting_loop / _longlong / jit_single_hot_loop 三条 JIT 专项 + lc_139/lc_3/lc_322/lc_36/lc_49 五例 runtime_gap + lc_401 一例 output_gap——热循环 >100 次的 LeetCode 用例连带拦截)。
  • 还原证据:还原后 666 用例 / match 646 / 门禁通过(exit 0)。
  • 定位:JIT P0 修复批次(commit 5955cb9)的 J10 突变验证环节;同时为 cargo 侧提供 11 红(jit_path_parity 7 + jit_unit_test 2 + vm_bench 2), 合计 margin=20。
  • 复现:改 tpl_add 一行 → go run ./scripts/shadow_verify(DLL 会按 产物新鲜度门禁自动重建)→ 观察 exit 1 → git checkout 还原。

状态:有牙 ✓


ci_three_tier_check(防线 5 一致性监控)

记录 #1 —— 假 KNOWN_FAILURE 条目(文档与测试结果矛盾)〔2026-09-13〕

  • 注入:向 native/tests/FUZZ_FAILURES.md 临时插入 ## 已知失败(KNOWN_FAILURE) section + 假条目 ### fake_j9_landmine_case (该用例不存在,tier 测试实际全过——"文档声明已知失败但测试已通过"的 确定性矛盾场景)。
  • 红证据:跑 ci_three_tier_check(当时为 Python 版;2026-09-18 起 Go 版 go run ./scripts/ci_three_tier_check,重放见下)(必须在仓库根跑, 见下"顺带发现")→ exit 1,hard 报错精确点名: [ERROR] Tests all passed, but FUZZ_FAILURES.md still has 1 un-fixed KNOWN entries: fake_j9_landmine_case(KNOWN_FAILURE 已通过,请更新文档标记为已修复)
    • [FAILED] 一致性检查存在 hard 问题。
  • 还原证据:删除插入段落后 → exit 0 [SUCCESS] All three tier tests passed!。
  • 复现:见本文件 git 历史(2026-09-13 W0-1 批次)或按上述注入重做。

状态:有牙 ✓


记录补充 —— Go 版接替重放 + 顺带抓出复刻 bug(D5 后续批次第三站)〔2026-09-18〕

  • 红证据重放:向 HOST_CONTRACT_FAILURES.md 注入 ## KNOWN_FAILURE + 假条目 ### test_j9_fake_known_entry → hard [ERROR] / exit 1;复原 → exit 0。
  • 埋雷抓真 bug:首轮埋雷显示 hard 判定正确但条目名显示为 ###—— Go 版把标题正则写成无捕获组的前缀形态(^###\s+),title 取了全匹配。 绿路径双轨对账完全不可见(KNOWN 条目为 0 时不显示 title)——判定面 的输出细节只有埋雷路径能验证。修复后重放:title 正确显示 + exit 1。

serve_smoke(出口 3 JSON-lines 协议冒烟)

记录 #1 —— seek 越界 panic 回归(撤 W0-2 钳位)〔2026-09-13〕

  • 前置:U0#8 验收要求"serve_smoke 补边界/负值/极值样例后对已知 2 个 panic 必须红"。本批次先给脚本补了边界批(EDGE_LINES:5 次 step.next 产生 检查点后 seek(50000) 越程 / seek(-7) 负值 / payload.get 负 end/start / 非法 JSON 行 / 缺 params / 存活探针)。
  • 注入:撤掉 engine.rs finish_replay_window 的 discard.min(len) 钳位 (重引入 R-2026-09-01 的 split_off panic)。
  • 红证据:VITRO_CLI=<埋雷 release 二进制> 跑 serve_smoke(当时为 Python 版,2026-09-18 起 Go 版接替并重放证红,见记录 #4) → exit 1,3 项 FAIL:合法边界请求 id 全部关联(进程存活)missing=[103]、 seek 边界(103) 返回完整帧、非法 JSON 行 → protocol 错误帧。注意 panic 被 serve 主循环 catch_unwind 接住(进程存活),id 关联断言层抓住语义破坏—— 若 catch_unwind 同时失效,"边界批逐行响应"断言也会红(双保险)。
  • 还原证据:还原钳位 → exit 0 serve 冒烟全部通过。
  • 边界批触发的必要前置:step.begin 后必须先 step.next 数次产生检查点, 否则 seek 在"没有可用的检查点"提前返回、走不到窗口路径(首版边界批 因此漏触发,已修正)。

顺带发现并修复(脚本自身的坑)

  1. resolve_exe() debug 优先 → 陈旧二进制假绿:埋雷注入 release 后 smoke 仍全绿——它一直在跑旧的 debug 产物(无埋雷)。J9 埋雷反过来 抓住了脚本自己的产物新鲜度缺陷。已修:取 debug/release 中 mtime 较新者。
  2. cwd 敏感:从 native/ 目录跑 ci_three_tier_check(无论 Py/Go 版)直接 NotADirectoryError(相对路径基于 cwd 解析)——记录为已知约束:判定型 脚本一律从仓库根运行(CI 即如此,暂不改为基于 __file__ 解析)。

状态:有牙 ✓(含一次脚本自身缺陷的发现与修复)


记录 #4 —— Go 版接替后的证红重放(D5 后续批次第一站)〔2026-09-18〕

  • 背景:serve_smoke.py → scripts/serve_smoke(Go)。J9 义务随语言迁移 转移:Python 版的埋雷形态(seek panic 回归 3 FAIL)是引擎缺陷注入,Go 版 以等价双通道重放"会红"义务。
  • 红证据(双通道): ① VITRO_RSS_BUDGET_MB=5 go run ./scripts/serve_smoke → 56/57,FAIL "RSS 护栏:提交峰值在预算内"(peak≈24MB > 5MB)/ exit 1(护栏有牙); ② VITRO_CLI=<输出垃圾的桩 exe> → 51 项 FAIL / exit 1(判定面整体 有牙:坏产物不可能全绿)。
  • 双轨对账(切换 CI 前提):Py/Go 同产物并跑——exit 0/0、断言数: 57 (PASS 57 / FAIL 0) 双侧一致、PASS/FAIL 标签逐行一致、RSS 峰值同为 24MB。
  • 口径迁移:断言数: 自报行是 facts 采集锚(字节级一致);产物选择 mtime 较新者(Python 版 J9 实测教训原样保留);响应解析走 UseNumber 复刻 Python int/float 区分;RSS 断言值取最后一次 seek 后采样(Python 版 凭 Popen 句柄在进程退出后再采——峰值单调,两口径数值一致)。

precompile_bytecode_libc(Bytecode Libc 产物同步门禁,--check)

记录 #1 —— 篡改源文件 → digest 失配(D5 后续批次第二站 Go 版重放)〔2026-09-18〕

  • 背景:--check 以 source_digest(源文件内容 SHA-256,CRLF→LF 归一) 判定产物是否与 runtime_libc 同步,不依赖 mtime(CI 干净检出误报事故, 2026-09-11 修复);fd0c1cf 事故(产物被文本替换而非重生成 → digest 冻结 → --check 必红)是其防线价值实证。Python 版迁 Go 时 J9 义务随之转移。
  • 注入:向 native/runtime_libc/src/string.c 追加一行注释 /* J9 mine */。
  • 红证据:go run ./scripts/precompile_bytecode_libc --check → recorded/current digest 双显不一致 + ERROR: out-of-date + exit 1; git checkout 复原 → Precompiled artifacts are up-to-date. + exit 0。
  • 对账前提:Go 版 digest 与 Python 版冻结在产物里的值逐字节一致 (--check 双侧同绿);双侧真实重生成产物 JSON 逐字节一致、.rs 仅头注 指向 Go 命令(fd0c1cf 口径:真实重跑 vitro_cli export,零语义漂移)。

facts(文档测试数字对账门禁,scripts/facts)

记录 #1 —— CURRENT 文档注入区间内假数字〔2026-09-13〕

  • 注入:临时文档写入 C 影子防线覆盖 888 个用例,serve 冒烟包含 99 项 断言。(888 在 shadow_c_cases 规则 Lo/Hi=400~900 区间内、99 在 serve 规则 10~150 区间内,均为 CURRENT 层:无日期、无 as-of 词)。
  • 红证据:go run ./scripts/facts check → exit 1,漂移计数按注入 数量精确增长(基线上各 +1,雷在汇总中可见)。sync --yes 可将雷数字 改回真值(实测回写正确)。
  • 方法学要点(雷没炸出来的教训):第一轮埋雷用 999 / 500,双双被 规则 Lo/Hi 区间静默过滤(999 > 900、500 > 250),红来自存量——因果 未成立。埋雷必须先确认雷落在判定口径区间内,"注入后 exit 1"不等于 "雷触发了 exit 1"(对照存量基线看增量)。这本身是"口径错"形态的又一 实例:真值增长穿越 Lo/Hi 边界后规则静默失配(漏报方向),区间上限需 随真值增长定期复核。

记录 #2 —— 坏引用判定(新判定面的"先证会红")〔2026-09-13〕

  • 背景:spec 冻结掩盖坏引用(用户复审发现)——STEP_PAYLOAD_SCHEMA_V0_1.md 写着已退役的 shadow_verify.py 等三个 Python 驱动路径,数字冻住后失效 执行路径永远没人发现。新增 scanBrokenRefs:CURRENT 文档引用的脚本 路径不存在即坏引用(as-of/归档豁免——勘误注记本身会引用旧路径名)。
  • 注入:临时文档写入两条故意不存在的雷路径 python scripts/definitely_not_exist_j9.py 与 go run ./scripts/ghost_probe.go(覆盖 .py 直引与 .go 包路径两分支)。注:雷路径所在行即"描述雷本身"形态——命令语境 + 死路径,靠同行"不存在"豁免词放行,是行级豁免的已知边界(跨行措辞会把豁免词与路径拆开)。
  • 红证据:坏引用 16 → 18(两颗雷各 +1,报告与汇总点名);check 退出条件含 len(Broken) > 0 → exit 1。
  • 还原证据:删除雷文件 → 回落 16。当前 16 处为真实坏引用(前端 切割删除的 build_flutter.py 等三个构建脚本、D5 退役的 shadow_verify.py/vitro_output.py/extract_shadow_cases.py/ replay_s1_s5.py、早期 test_templates.py/feature_matrix.py), 待文档逐一处置(修正或标注勘误)。
  • 首版误报教训:scripts/shadow_verify.go 被误判坏引用——Go 程序按 目录组织(磁盘是 scripts/shadow_verify/main.go),文档写包路径形态 是合法的。存在性改三级判定:字面存在 / 去 .go 后是含 .go 文件的 目录 / 其余即坏。新增判定面首跑必须人工核对命中清单,误报与漏报 同等重要。
  • 复现:按上述注入重建 → go run ./scripts/facts report(对照基线 看坏引用增量)→ 删除复原。

两轮审阅修复实录(2026-09-13,AI 首修 + 用户复审 + AI 二修)

  1. flag 顺序陷阱:facts sync --yes 的 --yes 被 Go flag 静默丢弃 (实测空转、已改 0)——残留 flag 形态参数现在 fatal 拦截。
  2. --yes 破坏面:曾把警告条目(分解式/实测数字/同行多规则)一并 自动改写(实测三处方改:分解式 617+16+3≠671、实测样本量 632 被伪造 为 671、同行交叉污染)——现 --yes 跳过警告条目,--force 显式逃生。
  3. applyHit 偏移失真(用户复审 P0):同行多命中时复用 audit 时刻的 字节偏移,前一条替换变长度后偏移失真切错位置(实测 78 → 781)—— 替换前基于当前行重算候选区间 + 规则匹配安全网(行不再匹配即拒改), offsets_test.go 三条契约测试固化。
  4. check 假绿(用户复审):--max-age 1ns 曾使真值全超龄 → 漂移 0 → exit 0(--strict-age opt-in 形同虚设)——超龄现在无条件红, --allow-stale 仅为本地调试逃生门(实测 1ns → exit 1)。
  5. demoteStale 写盘污染(用户复审):降级结果(value:null)曾写回 facts.json,prev-fallback 真值不可逆丢失——改先写盘原始值、再内存降级。
  6. 目录计数 as_of(用户复审):目录 mtime 曾致 leetcode 真值被误判 超龄(目录内容不变 ≠ 真值陈旧)——改采集时刻。
  7. spec 坏引用勘误:验收表后加注退役驱动 → 现行 Go 命令;验收数字 保持签字快照。
  8. 其余:collectCaseDirs 按扩展名计数(.in/.h 伴生曾计入)、docs/spec/ 整体 AS-OF、报告区分 ⏰超龄 与 待采集、删 NowOffset 死字段。

记录 #2 补充 —— 语境判据复审修正(用户复审驱动,2026-09-13 同日)

  • 文件级豁免漏活指引(用户独立全量扫描实锤:35 处路径不存在、工具报 0): 工程债务维护方案的"执行步骤"第 1 步已更新 Go 版、第 2 步仍是退役的 shadow_verify_cpp.py——同段半更新恰是最需要防线抓的,却因文件名 含"维护方案"被整体豁免;下游回执验收清单 3 条命令 2 条退役同理。 修正:判据从文件级改为行级命令语境(路径紧跟 python / go run / cargo 等命令动词 = 祈使指引,任何文件层级都扫);非命令语境的 路径提及维持"CURRENT 才扫"(历史叙述)。J9 新覆盖面验证:AS-OF 文件 注入命令语境死路径 → 检出 +1(实测);4 处真活指引全部处置(改现行 Go 命令 / spec 表行加退役标记)。
  • 豁免词表误豁免(用户受控实验实锤):探针 B"已用 nonexistent.py 取代旧流程"被"取代"放过。修正:删"取代/接替"出词表(它们标记的 是新路径存活,方向性错误);接替句改由"同句活路径则死路径 豁免"规则覆盖(影子验证框架"Go 接替 py"行实测不误报)。复验: 探针 A/B 检出 ✓;探针 C("含'删除'字样"自指涉)仍豁免——行级词表 无法语义区分"描述这个词本身",已知边界(漏报方向,本行即例)。
  • 顺带发现:豁免词跨行措辞会把词与路径拆开("故意 不存在"无效), 雷描述必须单行成文。

状态:有牙 ✓(check 判定路径 ×2 记录 + 语境复审补充);sync 写路径由 警告跳过 + 交互确认 + 偏移重算安全网三闸收敛。存量已清零(2026-09-13 同日):14 处漂移逐条人工处置(分解式整行重写、实测记录标 as-of 不改数)、 16 处坏引用(12 叙述性豁免 + 4 指令性改现行路径)+ 语境修正后新抓 4 处 活指引(工程债务半更新 1 + 回执验收清单 3)+ 4 处分解式行归"人工维护" 新类——facts check 现绿(exit 0),可接 CI

记录 #3 —— 常量对账判定面(ABI 版本,M13 细化落地)〔2026-09-18〕

  • 背景:§7A 复核 M13 实锤——ABI 版本代码 2.1.0 时,多份 CURRENT 文档 仍写 1.2.0 / 2.0.0(数字对账规则只抓区间整数,x.y.z 版本串零覆盖)。 新增 auditConst 常量对账:真值唯一来源 first_batch.rs 的 VITRO_ABI_VERSION 常量;判据为"陈述现值的句子 == 真值、历史事件句 (迁移箭头 → / 首批 / 曾 / 当时 / 追加 / 条目 等)豁免"。
  • 红证据(两条独立路径): ① 单元测试先红:const_audit_test.go 8 例,夹具全部取自真实漂移 行(路线图 = 1.2.0、更名记录 返回 "2.0.0"、迁移箭头句、历史注释句、 AGENTS C ABI(2.1.0)、多版本串同行、缺真值不判、采集器形态变更 fail loud)——go test ./scripts/facts/ 全绿即锚定判定口径。 ② 实战红:规则上线首轮 facts check 抓出 4 处真实 ABI 漂移 (架构设计:69 / 更名记录:35 / 路线图:15 / 构建指南:49,此前全部不可 见),漂移计数 2 → 6,exit 1——护栏先红,修文档后复绿,红→绿闭环。
  • 顺带落地:--cargo-log 采集器(CI 的 cargo test 步骤 tee 日志后 facts 解析 passed/套件真值,不重跑 30 分钟测试;日志缺失 → unavailable 不兜底,单测锚定)+ ci.yml 接线 facts check(M15:此前工具自称 CI 门禁但 workflow 无此步骤——对账工具自己的口径先漂移;现置于 replay 之后,shadow 真值读当轮产物、replay/serve_smoke --run 秒级刷新、 cargo 读日志,CI 每轮真值新鲜,无超龄红)。
  • 已知边界(漏报方向,人工核补):① 豁免是行级的——"首批 1.1.0, 当前 1.2.0"整行因"首批"豁免,行尾的错现值不红(本次 README:53 该 形态由人工修复);② Context=vitro_abi_version|ABI,引擎版本等其他 版本化常量未纳入规则,按需扩容。
  • 复现:go test ./scripts/facts/(夹具即雷);或临时文档写入 "vitro_abi_version() = 1.2.0"(CURRENT 层)→ facts check exit 1。

记录 #4 —— 真值越界自检(b49d4ff 治本批)〔2026-09-29〕

  • 背景:模块审阅 09 号 P2-2 实锤静默盲区——真值越出规则 [Lo,Hi] 区间时 候选数字被滤空、整行不进任何桶:文档与真值一起错也判绿(E11 实证 383 越下界时文档同写 383 判绿;此前一批的 508 越上限假红同机制, b28736a 只放宽带宽没治本)。
  • 注入锚:TestTruthInRangeSelfCheck(上下界两向越界必须判红 + 在界 不误报),随 truthsInRange 治本落位(go test ./scripts/facts/)。
  • 实战红:自检首跑即抓出 cargo_test_suites cached 坏值 1(与 passed=1027 不自洽,真值键内部矛盾此前不可见)——已清,CI --cargo-log 完整日志重采权威值。
  • 复现:go test ./scripts/facts/ -run TestTruthInRangeSelfCheck。

serve_smoke 补充记录

记录 #5 —— 超长行批双臂锚(b49d4ff)〔2026-09-29〕

  • 背景:模块审阅 06 号 P1-1 坐实——cmd/serve 的 fgets 定长缓冲行内容 ≥65535 字符返回 -2 时静默 break(stdout 0 字节、rc=0),与 shutdown 正常退出不可区分,违反协议演化纪律②(沉默是调用方唯一无法处置的回应)。
  • 红证据:serve_smoke 新增 long_line 批(双宿主双臂共跑、不进豁免表, 共同不变量「超长行非静默(≥1 响应行)+ 响应是帧」)——修复前 MoonBit 臂 responses=0 FAIL;断言总数 57→59。
  • 还原证据:修复后双臂 PASS(Rust 59/59、MoonBit 33 PASS + 12 豁免); oracle 侧 BufRead::lines 无行上限回 pong——形态差登记 cmd/serve main.mbt 注释(不照搬)。
  • 复现:go run ./scripts/serve_smoke -moonbit(及默认 Rust 臂)。

汇总

脚本 埋雷记录数 最近验证 J9 状态
shadow_verify 1(引擎语义突变,9 例非预期差异 / exit 1) 2026-09-13 有牙
ci_three_tier_check 2(假 KNOWN 条目,hard / exit 1;Go 接替重放同判 + 顺带抓出 title 提取缺捕获组复刻 bug) 2026-09-18 有牙
serve_smoke 3(seek panic 回归,3 FAIL / exit 1;Go 接替重放:RSS 预算 5MB→1 FAIL + 桩 exe→51 FAIL;超长行双臂锚 2026-09-29,修复前 MoonBit 臂 FAIL) 2026-09-29 有牙
facts 4(区间内假数字漂移 +1 / exit 1;坏引用两分支各 +1 / exit 1;ABI 常量漂移 ×4 实战红 + 单测 8 例锚定 / exit 1;真值越界自检 2026-09-29——首跑抓出 cargo_test_suites 坏值);存量漂移/坏引用已清零,check 现绿 2026-09-29 有牙
precompile_bytecode_libc 1(篡改 runtime_libc 源文件 → digest 失配 / exit 1;Go 版 2026-09-18 重放) 2026-09-18 有牙
其余判定型脚本与闸 各自头注 + 批次提交留痕(覆盖面演化注记) 随批 义务随闸

上表五脚本 J9 义务满足;MoonBit 迁移期新增闸的证红留痕以头注与提交为权威源。 后续新增判定型脚本或为既有脚本新增判定面时,先埋雷后上线。