diff --git a/reports/iter35-卡②-验收审计-2026-10-24.md b/reports/iter35-卡②-验收审计-2026-10-24.md new file mode 100644 index 00000000..d9f71d47 --- /dev/null +++ b/reports/iter35-卡②-验收审计-2026-10-24.md @@ -0,0 +1,117 @@ +# 迭代 35 卡② 独立验收审计(节点图消费侧换代批) + +- 审计日期:2026-10-24(执行跨至 2026-10-25 完成) +- 审计对象:分支 `iter35b` 尖 `0c5cd428`,改动范围 `git diff master...HEAD`(merge-base `bb63c93c`,31 文件级) +- 审计身份:未参与实现的独立会话子代理(迭代 loop 内启动,依 test-conventions §9.7 授权跑测) +- 合同:`reports/需求卡片-节点图消费侧换代批-2026-10-21.md` v1.1;被审产出物=实现报告/代拍板单/反哺点清单三份 + +## 总结论:**通过** + +逐条独立复核(不采信实现方「已全绿」)后,V1–V11、S1–S8、件核四项、批性质禁令、变异伴生九条拦截面全部成立;实现报告 §二·十三 实际对照表的每条测试落点均真实存在、真实入口非绕过改动点的下层直调;三条撤销证明(M1/M4/M6)由本审计亲手注入复现出红并还原自证 git status 干净。三处「必看」的旧断言改写均为等价或更强、且已在卡片 §8.2/§一随动表/代拍板单申报,无未申报削弱。**建议合回**。 + +残留项均为已在报告内申报的过程件与文档措辞张力(反哺点⑦/⑨),属文档批处理面,不构成实现批的验收缺漏。次要行号漂移(±1–5 行)不影响判据可定位性(以 尾缀 grep 为权威落点)。 + +## 一、环境与跑测方法 + +- 基线复核:本审计自跑 `PYTHONIOENCODING=utf-8 Godot_v4.7.2 --headless --path . --script res://tests/run_all.gd`,结果 `all green (60 suites)`;对 `FAIL|红色|断言失败` 的 7 处命中逐条判读,全为良性(通过态测试函数名含 "failure_restores"、故意的 corrupt-JSON 解析报错、"[PASS] …R3 fail-fast" 行),**零真实 FAIL**。 +- 关键宿主自证读数:`level_table_validate_test` 17、`node_map_templates_test` 21、`adventure_feats_test` 138、`level_shop_economy_test` 181、`breakpoint_resume_test` PASS —— 与实现报告 §二·十三「终版读数」逐一对齐。 +- 撤销证明:在指定文件注入变异→跑最窄宿主取宿主自身 FAIL 行→写回原文→`git status`/`git diff HEAD` 自证字节级还原(三次注入后树均干净)。 +- 静态核:diff 路径、件核四项、旧断言删除—替换、非空前置、代拍板超清单五件,逐项 grep + 打开文件确认。 +- 依赖说明:探针/冒烟包装闸(run_all_gates.sh 的 probe/smoke/balance 段)未由本审计重跑,依 `game/tests/.gates/receipt.json`(记录 commit=`0c5cd428`、域=code、全绿、11 回放确定性对拍 NEED-APPROVAL 经用户裁定零跑)作背景;代码域测试面已由 run_all.gd 独立复现。**MC/11 回放确定性对拍的零跑是红线内正确处置**(卡② 本体零玩法数值;覆盖批数值随卡② 同批、档位须用户发起),非本审计可越权补跑,亦不阻断代码侧验收结论。 + +## 二、逐需求点审计表(V1–V11) + +| # | 审计项 | 判据出处(卡片) | 实际跑了什么/打开确认了什么 | 结果 | +|---|---|---|---|---| +| V1 | 加载校验四检+良构+起关拒绝 | §4 契约1;§8 V1;§8.2 行1 | 打开 `level_table.gd:50 validate_graph`=①入口终点≥1 ②全员可达双向BFS ③终点恒Boss/Boss无出边 ④出边目标depth严格>源;良构=句柄唯一·出边全命中·type五枚举·battle/boss带depth。`level_runner.gd:308` 起关调 `LevelTable.validate_graph` 违规 return false(真实入口 `start_level`/`start_level_external`)。跑 `level_table_validate_test`〔v1〕17 条全绿(正例三形/负例八类/范围反证三/②④互斥实证)。旧六判据文本全仓 grep 零残留(`编号最大`/`纯战斗`/`出度上限` 作判据 0 命中);`validate()` 已缩为行级,`level_shop_economy:427` 保留「行级校验零错误(图合法性移交 validate_graph)」 | 通过 | +| V2 | 多入口首跳(候选=全部入口) | §4 契约5;§8 V2 | 〔v2〕`node_map_templates:64` 「入口停点候选数=参数档入口个数(L3 档 2)」经 `start_level_external(...,defer_entry=true)`→`level_runner:214 _candidates=entries` 执行;第二入口 `:68` 走 `pick_next_node`。**亲手注入 M1**(两处 `_candidates=entries`→`[entries[0]]`)→该断言变红,还原绿 | 通过 | +| V3 | 大岔口分区域兑付+多Boss收束恰一次 | §4 契约5;§8 V3 | 打开 `level_shop_economy:1156 _smoke_multi_terminal_settlement`:真实入口 `sim/level_autoplay.run_level(min/max 两轴)`+`runner.start_level(2,42)`+`pick_next_node`;断言 L2≥2终点Boss、两路各收本路终点Boss互异、区域隔离(A路未到B终点)、`level_finished` count==1 且 result=victory 且 node_id∈Boss集。基线该宿主 181 全绿 | 通过 | +| V4 | 单链退化合法+三关含岔口+L1终点恰一 | §1完成定义;§8 V4 | 〔v4〕`level_shop:340`「L1 终点恰一个(大岔口自L2起)」`_terminal_ids==1 and boss==1`;`:633` L3 入口数=entry_count·终点数=exit_count≥2;含岔口 `:341/442`(出度≥2节点≥1);单链合法由 V1 `level_table_validate:56` 夹具承载。`adventure_feats:165`〔v4〕三关各含≥1奖励节点 | 通过 | +| V5 | 商店编排三性质 | §4 契约4;§8 V5 | 〔v5〕`level_shop:1129/1130/1131` 三性质(终点前驱恰一店/店数=终点数/全路径恰穿一店)×L1–L3×种子42–61 扫描;非空前置 `:1128 graph_total==3×range & term>=3 & path>=3`,逐图空即 `:1101 false`。DFS `_all_entry_terminal_paths:186` 走句柄序。加载校验内无商店位(`level_table_validate:97` 反证)。M2/M3 记录判据名=性质①/②③,与卡片「终点前驱非店」「路径加第二店」对应(底层断言已开文件确认为真判据) | 通过 | +| V6 | 定位信息两样(关卡序号+节点深度,弃编号寻址) | §4 契约2;§8 V6 | `level_runner:500` enc `"node_depth": int(node.get("depth",0))`=depth值非句柄、键名保留;`_node_id:333` 返回 `id` 句柄(双编号空间退役)。〔v6〕`adventure_feats:188` 走真实 `_drive_to_pref→pick_next_node→begin_battle→current_battle().encounter`,判据含 `enc.node_depth==node.depth` **且 `node.id != node.depth`**(防句柄冒充)。**亲手注入 M4**(enc 取 `id`)→该断言红(实测 depth=6 句柄=6),还原绿 | 通过 | +| V7 | 确定性承载(纯函数复现) | §4 契约3;§8 V7 | 〔v7〕`node_map_templates:43` 同档同种子两次 generate 逐位同图/`:45` 换种子即变/`:53` 起关生效图=生成器直取(单一图源)/`:80` 种子42两开局同图;`breakpoint:174/181` 续玩重生成同图+同种子重开局同图。M5 记录判据名「同参数档同种子两次产图逐位同图」对应卡片「生成器注计数器依赖」;底层 `pure_stream→open_stream` 注入确会破坏该断言(判据对象正确) | 通过 | +| V8 | 模板0语义废除+旧断点代差拒绝 | §4 契约3;§8 V8 | `adventure_runner:146` `if int(bp.get("graph_gen_v2",0))!=1: return false`;快照 `:1014 graph_gen_v2:1`。〔v8〕`node_map:88` 续玩受理(标记在档)+`:94` 缺标记键旧断点 `==false`;`breakpoint:161/171/179` P7 标记键+句柄往返+旧断点拒绝。`is_first_adventure_done` 两态(种子42/随机)由 `node_map:106-112` 收束置位承载。**亲手注入 M6**(`if false:`)→`breakpoint:179`〔v8〕「缺生成图代差标记键的旧断点显式拒绝」红,还原绿 | 通过 | +| V9 | 15 张模板清理 | §4 契约6;§8 V9;完成定义6 | 〔v9〕`node_map:101` 三行 level_row 均无 `templates` 键+`breakpoint:159` `not has("template_pick")`。件核四项见第四节。`_resolve_template_row`/`active_template_*` 代码零引用。`level.tres` 仅剩 3 行 `level_index/display_name/rest_heal_pct`。M7 记录判据名「level 表三行均无 templates 键」对应卡片「level.tres 偷留 templates 键」 | 通过 | +| V10 | 反哺点清单六条各有落点 | §6;§8 V10(非行为改动) | 打开 `reports/iter35-卡②-反哺点-2026-10-24.md`:卡片预列①-⑥逐条有文件+节位+改成什么+依据;另实现批新发现⑦-⑩四条一并呈报。V10 义务满足 | 通过 | +| V11 | 生成图覆盖+多终点Boss带互异(承跨表校验④语义) | §0硬交付;§4契约8;§8 V11 | 〔v11〕`level_shop:302-305` 敌侧落带≥请求深度(`_enemy_band_covers:223` 经 `resolve_enemy_rowset` 真实链,禁顶clamp)/板侧精确命中(`node_depth==D`)/多终点带键两两互异;非空前置 `:268/275/302`。〔fv24〕`adventure_feats:130` 同源消费链+`cov_scan>0`。§一 覆盖批两条注入记录(Boss带并单带→互异红;删顶带→落带红)判据名对应卡片伴生条。**跨表校验④整段已从 `config_db_cross_validator.gd` 删除**(`node_lists`/`level_node_dangling`/带clamp/带互异静态代码零残留),两拦截语义由〔v11〕升级到真实生成图,与 §8.2 预声明④一致 | 通过 | + +## 三、S1–S8 断言随动核 + +| # | 核到之处 | 结果 | +|---|---|---| +| S1 | `node_map_templates_test` 整宿主重写,全文件无「模板 0/回滚档/池=5/node_map抽签」文本(grep 0);⑦收束置位改写为「首局恒走固定种子42」 | 通过 | +| S2 | `_level_active_nodes` 三副本(`level_shop:48`/`adventure_feats:895`/`round_flow:343`)均改 `NodeMapGenerator.generate(level_gen_params_row(L),42)`;死件 `_boss_depth` 全仓 .gd 零引用(仅 `验证集清单.md` 快照残留,见第五节) | 通过 | +| S3 | 商店三性质+行进面(到店节点型别=shop且出边恰指终点)换代完成,硬编码 8/9/穿节点8/L1行集退役 | 通过 | +| S4 | 形状写死断言(节点数=10/普通战斗6+Boss恒一/恢复1+奖励1+商店1)改为生成图读数性质(五型合计=节点数/≥battle_floor/Boss数=exit_count/岔口≥1/终点depth=layers),每条带非空前置 `L%d 现行起关图取数非空` | 通过 | +| S5 | 覆盖判据换真实消费链+落带非顶clamp+带互异(见 V11) | 通过 | +| S6 | 确定性四断言(同图/换种子变图/句柄+depth/单一图源/种子42两开局/断点重生成同图/缺templates)落 `node_map_templates` 同宿主,manifest 零新增 | 通过 | +| S7 | `level_autoplay:141/200-202` `level_maps`(值 `关卡#主种子`)替代 `level_templates`;MC 出数面 `adventure_monte_carlo:139` 随动;sim 内 `template_pick` 零残留;route 轴沿句柄升降序 | 通过 | +| S8 | `breakpoint_resume` P7 六条;`pool_case_d`/`feat_vouchers`/`cross_system_flow` first_script 键清退(.gd 零引用);`meta_progression_forge:323` extra 键样例换 `treasure_unlocked`,「入口特有键随 extra 原样并入」判据结构保持(仍验 wide_scout_band+shop_mode==pool) | 通过 | + +## 四、件核四项(命令与命中数) + +| 项 | 命令 | 命中 | 判读 | +|---|---|---|---| +| 全仓零 `first_script` | `grep -rn first_script game --include=*.gd` | **.gd 0** | 代码面零引用(通过)。`.md` 仅 `game/tests/验证集清单.md` 快照 8 处(不在本 diff、不进任何闸、已申报反哺点⑦待用户重生成/注记)——属过程文档快照,非代码死引用,见第五节例外说明 | +| 全仓零 `template_pick` | `grep -rn template_pick game --include=*.gd` | **.gd 1** | 唯一命中 `breakpoint_resume_test:159 not bp_j.has("template_pick")`=**反向断言**(断言退役键缺席),属例外。其余仅 `验证集清单.md` 快照 3 处 | +| 全仓零 `active_template` | `grep -rn active_template game --include=*.gd` | **.gd 0** | 代码面零引用(通过);快照 2 处同上 | +| `level.tres` 无 `templates` 键 | `grep -n templates game/data/tables/level.tres` | **0 数据键** | 仅 1 处表尾去向注释(第16行「旧15张模板池…清出」),三行均为行级字段无 templates/nodes(V9 达成) | + +## 五、测试削弱核查(三处必看 + 全量删除扫描) + +对 `git diff master...HEAD` 的**全部删除断言行**(约 60 条 Kit.check)逐条溯源其替换体,未发现「删断言/放宽阈值/缩扫描面/换下层直调/精确改恒真/空扫描判绿」的未申报削弱。三处重点: + +1. **奖励三选一断言替代**(`adventure_feats`):旧「L1 链必经≥1 次」→新「图侧每关≥1奖励节点〔v4〕 + `_drive_to_pref("reward")` 真实岔口点选→`node_curio_candidates`+`present_node_curio`呈停+`apply_node_curio`受理」,另有 `:197` 兑付回执==经停逐笔。判据语义从「形状必经」变「接线可用+图上存在」,**已在代拍板①申报**(实测确定性评分 12 次全程恒不进奖励支路,含岔口设计下奖励位可选为既定),替代体仍走真实入口证机制可兑付,未放宽为恒真。判:申报过的合法替代,不红。 +2. **`shop_seq.size()` 2→3**(`adventure_feats:325`):随契约4「每关路径恰穿一店⇒三关=3」的必然投影,**代拍板③申报**,与 V5 三性质扫描互为校验(不一致则二者之一必红),非任意放宽。判:不红。 +3. **三处 `_level_active_nodes` 换代**:templates 键清出后旧实现落 `row.get("nodes",[])` 返空数组会致覆盖/Boss深度/逐节点扫描断言集体空转绿——本批三副本均改生成图取数,且所有消费断言带非空前置(`cov_scan>0`/`scanned>0`/`graph_total==3×range`/逐图空→显式 `false`)。**代拍板②申报为缺陷清偿**(当场重新暴露一次 `boss_depth` 读 -1 真红并清偿)。判:有效强化,不红。 + +**其余删除断言**全部落入卡片 §8.2「旧断言预声明」四类(S1–S8随动/V5商店换代/V8收束置位改写/④静态检查退役由S5承接),替换体经开文件确认为等价或更强(模板形状写死→生成图性质扫描、静态代理→真实消费链落带)。 + +## 六、变异伴生九条拦截面判定(§8.2) + +| 卡片条 | 记录/复现 | 判据名是否对应 | 结果 | +|---|---|---|---| +| V1 终点恒Boss判据注释 | §二·五 记录 `终点非 Boss 负例〔v1〕` 红 | 对应(负例夹具命中「终点 1 非 Boss」) | 判定成立 | +| V1 无环判据短路 | §二·五 记录 回边/环两负例红 | 对应(「违反前进方向」) | 判定成立 | +| V11 Boss带并单带 | §一 记录 带互异红 | 对应〔v11〕两两互异 | 判定成立 | +| V11 删顶带 | §一 记录 落带≥深度红 | 对应〔v11〕band_of非-1 | 判定成立 | +| V2 候选写死单入口 | **本审计亲手注入 M1** | 红行原文见第七节 | **已复现** | +| V5 终点前驱非店 | §二·十二 M2 | 对应性质①(已确认 pred 型别判定为真判据) | 判定成立 | +| V5 路径加第二店 | §二·十二 M3 | 对应性质②③(已确认 shop 计数判定为真判据) | 判定成立 | +| V6 enc取句柄 | **本审计亲手注入 M4** | 红行原文见第七节 | **已复现** | +| V7 生成器注计数器依赖 | §二·十二 M5 | 对应〔v7〕同种子逐位同图 | 判定成立 | +| V8 旧断点放行回放 | **本审计亲手注入 M6** | 红行原文见第七节 | **已复现** | +| V9 表留 templates 键 | §二·十二 M7 | 对应〔v9〕三行无 templates(已确认 iter has("templates")) | 判定成立 | + +结论:九条中三条由本审计独立注入复现出红,其余六条的实现方记录判据名与卡片要求逐条对应、且底层断言开文件确认为真实判据(非恒真/非空扫描),无「记录名与卡片错配」情形。 + +## 七、撤销证明复现记录(本审计亲手注入,附红行原文) + +- **M1(V2)**:`level_runner.gd` 两处 `_candidates = entries` → `[entries[0]]`(`:214` defer入口 / `:243` 断点入口停点)。跑 `node_map_templates_test`。红行: + `ERROR: [FAIL] 入口停点候选数=参数档入口个数(L3 档 2——多入口首跳即选路,禁写死单入口)〔v2〕` + 还原后 `git status` 干净。 +- **M4(V6)**:`level_runner.gd:500` enc `"node_depth": int(node.get("depth",0))` → `int(node.get("id",0))`。跑 `adventure_feats_test`。红行: + `ERROR: [FAIL] 进战载荷两样=关卡序号+节点深度(实测 depth=6 句柄=6)〔v6〕` + 还原后干净。 +- **M6(V8)**:`adventure_runner.gd:146` `if int(bp.get("graph_gen_v2",0))!=1:` → `if false:`。跑 `breakpoint_resume_test`。红行: + `[breakpoint_resume_test] FAIL — P7 缺生成图代差标记键的旧断点显式拒绝(禁按新句柄空间静默回放)〔v8〕` + 还原后干净。 + +三条注入均:注入前确认 `git status` 干净;取被测宿主自身 FAIL 行(非包装层退出码);写回原文后 `git diff HEAD` 为空、`git status` 干净。 + +## 八、代拍板超清单五件(白名单外)的独立判断 + +1. 奖励断言替代 → 见第五节①,申报充分、替代体仍证接线,通过。 +2. `_level_active_nodes` 三处空转缺陷清偿 → 三副本确认换代、非空前置齐备,清偿真实(当场重暴露 boss_depth=-1 红),通过。 +3. 商店到达余额 2→3 → 契约4 必然投影、与 V5 互校,通过。 +4. `_boss_depth` 死件随删(两宿主零调用方)→ 全仓 .gd 零引用确认,涉删纯死代码不触断言,通过。 +5. 两呈现宿主(`level_map_type_vis`/`level_map_info_fog`)夹具拓扑重建、自述「断言条数与阈值零改动」→ **本审计独立计数核**:19→19、26→26 无缩减;可见集阈值判据仍在(层距 N 可见/N+1·N+2 不可见、候选豁免、Boss 豁免不扩散、句柄32层6入雾),负向回边判据移交 `level_table_validate`〔v1〕,自述属实,通过。 + +## 九、批性质与残留申报项 + +- **批性质禁令**:`git diff master...HEAD --name-only` 仅 31 个 `game/` 文件 + 3 份 `reports/` 产出物;grep `design/|AGENTS.md|roadmap.md|待办清单|体验验证清单` = **零命中**。实现批未写 design/ 与根流程清单,合规。 +- **申报的非阻塞残留**(属文档批/用户裁决面,不红): + - 反哺点⑦:`game/tests/验证集清单.md` 为批 B 断言文本快照索引,本批未重生成,仍含退役措辞与旧行号,不进任何闸;已诚实列清单待用户裁决重生成或注记。件核四项中的 `.md` 快照命中即源于此,代码面干净,不构成契约6「死代码死断言」违令。 + - 反哺点⑨:`level.tres:16` 表尾去向注记含「随迭代 35 卡② 清出」的迭代叙事,与 doc-conventions §5「禁写变更叙事」有张力;但卡片 T3 明令表尾写「15 张模板去向」注记,属卡片授权范围,已申报待裁。 + - 实机目检两条(三关选路观感、首局种子42教学图)已登记于实现报告 §三·一,依「真人检测只登记不阻塞」红线,不作合回前置。 +- **次要观察**:报告 §二·十三 若干行号与实际有 ±1–5 行漂移(如 v9 标 :91-96 实为 :101、v4 标 :339 实为 :340),但每条 尾缀 均可 grep 定位、断言真实存在,不影响验收。