diff --git a/reports/需求卡片-效果引擎无状态化批-2026-10-20.md b/reports/需求卡片-效果引擎无状态化批-2026-10-20.md index 7e8fcc1e..8d6449ac 100644 --- a/reports/需求卡片-效果引擎无状态化批-2026-10-20.md +++ b/reports/需求卡片-效果引擎无状态化批-2026-10-20.md @@ -3,8 +3,8 @@ - **日期**:2026-10-20 - **来源**:06-待办清单「计数器寿命域未统一」条目(2026-10-18 登记,来源=会话提及,设计面=文档轨),设计已闭合推进至待写卡(唯一事实源:`design/research/提案-效果引擎无状态化-2026-10-19.md` v1.2——§3 目标形态、§7 N1–N6 定值、§7.7 采集定值、§7.10 缺口闭合定值) - **批性质**:工程直入类·架构重构(restructure)——代码域批,禁写 `design/` 整个目录与根规则文件,产出物只落 `reports/` -- **估时**:≈3.75–5.75d(T0–T8 单卡;超盒切分预案=T7 拆「断言新增/迁移」与「复放比对」两波先后挂,T3 与 T7 测试迁移互不阻塞可并行切) -- **状态**:成文待审——零待裁点,开工门=U0(用户发令) +- **估时**:≈4.5–6.25d(=T0–T8 加总;超盒切分预案=T7 拆「断言新增/迁移」与「复放比对」两波先后挂,T3 与 T7 测试迁移互不阻塞可并行切) +- **状态**:成文待审——零待裁点(对抗复核六项已裁并并入正文,见 §12),开工门=U0(用户发令) --- @@ -18,8 +18,8 @@ 2. 计数器四桶外置:round/battle 桶挂 `BattleOrchestrator`、level 桶挂 `LevelRunner`、adventure 桶挂 `run_ledger.adventure_counters`,桶随宿主生灭; 3. 搬运链整段删除:`counters_export`/`counters_seed`(引擎)与 `counters_seed_snapshot`/`counters_merge_export`(RunLedger)零残留; 4. `adventure_runner.gd:838-841` 硬编码清除名单(`_reset_tree_limited_counters`)删除,按关清零由 LevelRunner 每关 new 天然承载; -5. 计数器归属机器可读且加载期可校验:`.tres` 行(errs→加载致命)与树合成行(`_push_trigger` assert)双落点,写错前缀即致命; -6. 同种子复放比对按分组判读通过(V12):未受影响局 RUNSUM 全字段逐位一致;受影响局输入面字段恒等+`levels_cleared` 分布不得上行——**不声称零漂移**(adventure 桶跨战持久与天赋树每关限次是已定性预期差异,提案 §7.7/§7.8); +5. 计数器归属机器可读且加载期可校验:`.tres` 行(errs→加载致命)与树合成行(`_push_trigger` assert)双落点,写错前缀即致命;另加装载期**引用闭合**——凡被读到的计数器 id(`when` 内 `fact: counter` 的 `id`/`do.value_counter`/`counter_spend` 的 `counter_id`)必须能在声明集(feats 行+秘宝行+树合成行的 `do.counter_id`)里找到同名写入行,找不到即加载致命; +6. 批内同种子复放比对按三组判读通过(V12):**off 组**(`feat_mode=off`)RUNSUM 全字段逐位一致;**formed 组**(`formed`)与 **rules 组**(`rules`+sense 两键)输入面字段与字段集恒等、局数与种子集一致、零崩溃;输出面差异与 `levels_cleared` 方向只记报告不作判据——**不声称零漂移**(三处已定性预期差异见契约 1); 7. `dig_combo` 死读数连同 combo 分支删除,零行为差(§7.10.14); 8. 旧档断点含旧计数键加载不崩(V13,无迁移——2026-10-18 代码术语刷新批先例)。 @@ -28,11 +28,13 @@ `design/research/提案-效果引擎无状态化-2026-10-19.md` **v1.2**(2026-10-20 设计闭合版)——本卡全部设计定值的唯一出处: - 目标形态与四桶表=提案 §3(3.1 宿主定值/3.2 接口定稿/3.3 前缀归属); -- N1–N6 定值=提案 §7;不发起全冒险模拟采集=提案 §7.7(零数值改动=零采集,本批一个数值表值都不改);搬运链缺陷全解=提案 §7.8; +- N1–N6 定值=提案 §7;不发起全冒险模拟采集=提案 §7.7(零数值改动=零采集——本批唯一例外是 V12 的复放采集,见 §12 裁点①);搬运链缺陷全解=提案 §7.8; - 写卡前缺口闭合定值=提案 §7.10(ctx 结构/容器类型与测试接口/前缀校验双落点/写口静态化/施加面补列/改名传播面/保险双账不改面/A1 注入口径/level 桶接口/复放分组判读/mine_hit 构造/dig_combo 删除/H1 补条)。 与 06 条目状态里的闭合凭据一致(v1.2)。 +**与本卡口径不一致的提案节以本卡为准**,三处冲突及裁决见 §12:§7.10.3 前缀判据(封闭词表方案在现实 id 面上误伤全部既有 battle id)、§7.10.12 复放判读(所记 27 字段与实采 33 字段不符,且单一「不得上行」判据把增强组与削弱组混判)、§7.7 零采集定值(现行锚与本批不同构,不可用作复放基线)。实现批禁写 `design/`,三处订正随反哺点清单归文档批。 + ## 3. 现状事实(2026-10-20 代码级实查,开工自行复核) **引擎侧**: @@ -50,27 +52,35 @@ **死路**:`battle_orchestrator.gd:635-647` `_feat_yield_adjust` 读 `dig_combo`(全仓无数据行声明——feats.tres/treasure_items.tres/树合成行三源零命中;feats.tres 唯一 `"cap"` 行属石肤 ac_bonus `:576-583`,yield_mult 带 cap 行零个)⇒ `combo_val` 恒 0、`combo_cap` 恒 ≤0,分支恒短路。 -**数据行**:`feats.tres` 计数 id 共 14 枚+树合成 2 枚=16 枚;`reset_at: round_start` 恰 4 行(`round_blast_spent` :212/`instinct_atk` :494/`round_mines` :518/`round_digs` :723——全带 round_ 前缀或随改名获得前缀,分桶后「整桶清空」与现役名单清零逐键等价);`domain` 键行:`twin_digs` :31/`pain_layers` :377/:535/:628(另 :407 附近 set 行)/`insurance_used` :877(battle)。 +**数据行**:`feats.tres` 计数 id 共 14 枚+树合成 2 枚=16 枚,**14 枚全含下划线**(`round_digs`/`round_mines`/`round_blast_spent` 三枚既有 id 已自带 `round_` 前缀);`reset_at: round_start` 恰 4 行(`round_blast_spent` :212/`instinct_atk` :494/`round_mines` :518/`round_digs` :723——三枚 round_ 前缀行现役已按轮始清零、改名零涉及,分桶后「整桶清空」与现役名单清零逐键等价);`domain` 键行:`twin_digs` :31/`pain_layers` :377/:535/:628(另 :407 附近 set 行)/`insurance_used` :877(battle)。**写点与读点成对同名**:`twin_digs` 写 :29/读 `when.id` :39;`instinct_atk` 写 :492/读 `value_counter` :501——改名必须成对同改,只改写点会把读写拆进两只桶且前缀校验查不出。 + +**残留 `domain` 键两处**:`effect_engine.gd:145/:155` 树合成行与 `tree_rules_v11_test.gd:33/:39` 夹具行仍带 `"domain": "adventure"`——`DO_KEYS` 删键后该夹具在 `validate_trigger_row` 面当场转红,在 T6 一并清除。 + +**过期注释面**:`effect_engine.gd:42`(「feats.tres 首例 domain=battle 行」)/`:70-71`(domain 键说明)/`:118-120`(adventure 域计数器按关重置叙述)、`grant_segment.gd:307`(「限次=合成行 adventure 域计数器闸」——sense 两键改 level 桶后失义)、`run_ledger.gd:7-8` 与 `:12`(export/seed 时点语义、「rebuild 回灌/战末导出回写目标」字段注)、`level_runner.gd:630`(T1 已列)——分桶后字面全部失义,在 T6 一并清除。 + +**采集器与复放数据面(2026-10-20 实跑复核)**:现行锚 `runsum_m3iter9exit_81000_n400_20261003.jsonl` 是 24 字段面、无 `feat_mode`/`attune`/`equipped`/`seal_miss`,与现役采集器的 33 字段面不同构;`sim-data/` 整目录不入 git,批 worktree 内本就没有该文件——两条合起来判锚不可用作本批复放基线,改批内重采(T0/T7)。另查出采集器缺陷:`adventure_monte_carlo.gd:65` 的 `rules` 档用 `String.split()` 产出 `PackedStringArray`,被 `adventure_runner.gd:118/:200` 的 `is Array` 守卫整组静默丢弃——对照实跑为 `ab_a`(字面量 Array)逐局改变轨迹、`rules` 与 `off` 除 `feat_mode` 标签外逐位相同;不修此行则 V12 的 rules 组恒假绿(修复列 T0 首提交)。 + +**静态扫描面**:`static_scan.sh:20-24` 的扫描路径单点只有 `game/core` 与 `game/presentation`,`scan_files` 只数 `.gd`——`feats.tres`、`game/tests/`、`game/smoke/` 全不在面内,新 S 段必须自带扩面(T7),否则「全仓零命中」是空转出来的绿。 **测试面**:`tests/run_all.gd` manifest(负面套件协议 `expect:1`+`negative:true`,先例 `reward_dangling_treasure_test.gd` 夹具注入);`tests/README.md:58` 记套件数 26;`game/presentation/` 全目录 counter_id 与 16 枚 id 字面零命中(呈现层不入改名传播)。 ## 4. 设计契约(违反即返工;🔴 触碰=L3 停机呈报) -1. 🔴 **确定性**:除 V12 已定性的两处行为变更(adventure 桶真跨战持久、天赋树锋/守真每关限 2)外,同种子复放必须逐位一致——读写时序、装载序、命中序零变化;命中序=装载声明序写死不碰。 +1. 🔴 **确定性**:除三处已定性行为变更(①adventure 桶真跨战持久;②天赋树锋/守真每关限 2;③旧档续玩时 `pain_layers` 不再跨战携带——见契约 8)外,同种子复放必须逐位一致——读写时序、装载序、命中序零变化;命中序=装载声明序写死不碰。全新开局不受③影响(缺陷态下本就未跨战,提案 §7.8 自锁)。 2. 🔴 **数值零改动**:本批不改任何数值表值;天赋树锋/守「每战 2→每关 2」是回到设计意图(`effect_engine.gd:137` 注释),不是平衡调整;禁顺手调任何 provisional 值。 3. 🔴 **round 桶就地清空**:轮始清零必须 `.clear()`(或等效就地清空),**禁 `= {}` 重赋值**——重赋值会切断 ctx 持有的引用。 4. 🔴 **搬运链删除后禁留任何副本同步时点**:战末导出/战首回灌/判空守卫三类代码不得以任何形态复活(含「防御性保留」)。 5. 🔴 **保险双账不改面**:`_perfect_forgiven` 与 `insurance_used` 同点双记关系原样保持(§3 现状事实),禁合并、禁迁账、禁改判定段两账相减语义(N6 定值)。 -6. 🔴 **两落点同源**:前缀校验的判定函数唯一(静态单点),`validate_trigger_row` 与 `_push_trigger` 共用;禁两处各写一份前缀表。 +6. 🔴 **两落点同源**:前缀校验的判定函数唯一(静态单点),`validate_trigger_row` 与 `_push_trigger` 共用;禁两处各写一份前缀表。引用闭合校验(T5)只做「读点 id ∈ 声明集」的成员判定,**禁在其中再写一份前缀判据**——前缀归桶的唯一出处仍是 `bucket_of`。 7. 🔴 **容器与接口逐字锁定**:`EffectContext` 四字段名 `round_bucket`/`battle_bucket`/`level_bucket`/`adventure_bucket`;编排器成员 `fx_counters_round`/`fx_counters_battle`/`counters_ctx`;LevelRunner 成员 `level_counters`;enc 下传键 `"level_counters"`;改名 4 枚新 id `adv_twin_digs`/`lvl_tree_sense_sharp_used`/`lvl_tree_sense_guard_used`/`round_instinct_atk`。实现期不得另起名。 -8. 🔴 **pain_layers 归 battle 零语义变更**:只改归属(删 `domain` 键),「攒层→下次攻击附加→用后清零」行为逐位不变(N4)。 +8. 🔴 **pain_layers 归 battle**:只改归属(删 `domain` 键),单战内「攒层→下次攻击附加→用后清零」行为逐位不变(N4)。跨战口径按设计意图统一为「不跨战」:全新开局本就如此(`run_ledger.gd:42` 自锁),旧档续玩经 `adventure_runner.gd:206-208` 回灌后原可跨战携带,改后不再携带——该差异属缺陷消解(契约 1 第③项),不是语义回归,V13 旧档夹具须带 `pain_layers` 旧键。 9. 🔴 **旧档不迁移**:改名后旧档断点旧键成死键属预期(H1 只断言不崩);禁加迁移体、禁动 `SAVE_FORMAT_VERSION`。 -10. 🔴 **实现批禁写 `design/` 与根规则文件**;`sim-data/` 零采集零重跑(§7.7 定值——本批零数值改动,锚与读数不动)。 +10. 🔴 **实现批禁写 `design/` 与根规则文件**;`sim-data/` 只允许 V12 的两次采集(批前三组 40 局基线——已采完留盘,见 T0;批后同种子复放 40 局),二档上限内;现行锚与读数文件一律不动、不改名、不替换;对 `sim-data/README.md` 纪律③⑦的批次例外,依据见 §12 裁点①。 11. 🔴 **同名成员禁遮蔽内建**:ctx 字段名已避讳(`round_bucket` 非 `round`),实现期引入新成员时同规。 ## 5. 范围边界 -**做**:§1 完成定义 1–8 全部;`EffectContext` 新类;引擎接口 ctx 化与写口静态化;7 个施加点迁移;前缀校验双落点;4 枚 id 改名+`domain`/`reset_at` 键删除;`dig_combo` 死路删除;测试新增/迁移/变异验证;同种子复放比对(零局新采集,锚内子集 20 种子);`static_scan.sh` 新增 S 段+`run_all.gd` manifest 登记。 +**做**:§1 完成定义 1–8 全部;`EffectContext` 新类;引擎接口 ctx 化与写口静态化;7 个施加点迁移;前缀校验双落点+装载期引用闭合校验;4 枚 id 改名(写点与读点成对)+`domain`/`reset_at` 键删除(含引擎合成行与测试夹具两处残留);七处过期注释清扫;`dig_combo` 死路删除;采集器 `rules` 注入类型缺陷修复(一行,T0 首提交);测试新增/迁移/变异验证;批内同种子复放比对(三组 40 局基线已采+40 局复放+`tools/parity_compare.py` 判读);`static_scan.sh` 新增 S11 段(扫描面自带扩至 `game/**` 含 `.tres`)+`run_all.gd` manifest 登记。 **不做**(去向已定): @@ -82,7 +92,7 @@ | 天赋树锋/守重新定价 | 独立议题,不进本批(§4.5 B3) | | architecture.md「系统无状态」原则补条 | 已登记 06 新条目(文档轨·待文档设计),归文档批;本批反哺点带依据 | | `roadmap.md` 当前位 stale | 已呈报在案,归并行会话 | -| MC 采集(任何档位) | §7.7 定值:零数值改动=零采集 | +| MC 采集(V12 复放之外的任何档) | §7.7 定值:零数值改动=零采集——本批只跑 V12 的三组 40 局基线(已采完)与 40 局复放,不开任何读数档、不动现行锚与读数文件 | | 元素类型精化(`Dictionary[String, int]`) | S9 现行口径 `Dictionary[Variant, Variant]` 合格,精化另走裁决 | ## 6. 关联文档同步面(实现批禁写本表所列文档——义务全部走 `reports/iter-反哺点-<日期>.md`) @@ -93,7 +103,9 @@ | `game/tests/README.md` | :58 记「manifest 已登记实跑 26 套件」——本批新增 1 负面套件 | 反哺点(机械面文档,批后由文档批更新套件数与段描述) | | `design/gdd/06-待办清单.md` | 上游条目「计数器寿命域未统一」随批完成整条删除(收口=batch-closure 技能);条目现状内引用的行号(:838-841 等)随实现漂移 | 批内不动;收口时整条删除(行号订正随之消亡) | | `design/gdd/07-体验验证清单.md` | 天赋树锋/守第二次后无减免=玩家可感知削弱,需真人目检入册 | 反哺点(登记项见 §9 真人目检面) | -| `design/gdd/术语表.md` | 零关联(本批不造新术语;四桶/前缀均为代码标识符非玩家术语) | 无 | +| `design/research/提案-效果引擎无状态化-2026-10-19.md` | 三处与本卡口径不同(裁决见 §12):①§7.10.3「`meta_` 及其余未知前缀→违例」——14 枚 id 全含下划线,该判据误伤全部既有 battle id,且封闭前缀词表=第二事实源;②§7.10.12 记「RUNSUM 共 27 字段」且「受影响局 `levels_cleared` 不得上行」——实采 33 字段,且双管镐组是增强方向,单一判据把增强组与削弱组混判;③§7.7「零采集」定值所依赖的现行锚与本批不同构,不可作复放基线 | 反哺点清单逐条列出(本卡为准,提案随文档批订正;`design/` 对本批只读) | +| `sim-data/README.md` | 配套纪律③「对拍断言=0 新跑」与⑦「零数值改动=零 MC」在本批开例外(三组 40 局基线+40 局复放,二档上限内);「现行锚」节与本批复放无关 | 反哺点(登记批次例外与判据,不改档位表本身) | +| `design/gdd/术语表.md` | 零关联(本批不造新术语;四桶/前缀均为代码标识符非玩家术语;未新增 `battle_` 前缀词) | 无 | ## 7. 卡前文件结构图 @@ -102,26 +114,29 @@ | 文件 | 职责 | |---|---| | `game/core/effect_context.gd` | `EffectContext`——四桶引用束(RefCounted,四公开类型化字段,零方法) | -| `game/tests/unit/data_config/counter_prefix_fatal_test.gd` | E1 负面套件:坏前缀 counter_id 行 → load_tables 拒绝(expect 1) | +| `game/tests/unit/data_config/counter_prefix_fatal_test.gd` | E1 负面套件:坏前缀 counter_id 行→load_tables 拒绝 + 读点 id 无声明行(引用闭合)→load_tables 拒绝(expect 1,两断言同套件) | +| `tools/parity_compare.py` | V12 复放比对器:解析 `RUNSUM` 行按 seed 配对;`--mode parity`(整条记录逐位相等)与 `--mode input-parity`(输入面八字段+顶层字段集恒等)两判据;`--expect <局数>`;输出差异表与 `levels_cleared` 直方图(后者仅报告)。退出码 0 绿/1 红/2 用法或解析错;字段集从基线文件推导,禁硬编码 | **修改**: | 文件 | 改动 | |---|---| -| `game/core/effect_engine.gd` | 删三状态成员与搬运接口;dispatch/counter 带 ctx;写口改静态纯函数+`bucket_of`;`_push_trigger` 前缀校验;DO_KEYS 删两键;合成行(:136-155)id 改名 | +| `game/core/effect_engine.gd` | 删三状态成员与搬运接口;dispatch/counter 带 ctx;写口改静态纯函数+`bucket_of`;`_push_trigger` 前缀校验;DO_KEYS 删两键;合成行(:136-155)id 改名**并删 :145/:155 两处 `"domain": "adventure"`**;:42/:70-71/:116-122 过期注释改前缀口径 | | `game/core/battle_orchestrator.gd` | 两桶公开成员+ctx 构造挂载;set_fx 扩参;`dig_combo` 死路删除;战末导出两行删除 | | `game/core/action_economy.gd` | set_fx 扩参;3 dispatch 面+3 写口静态化 | | `game/core/damage_controller.gd` | set_fx 扩参;dispatch 汇聚面+1 读 2 写静态化 | | `game/core/reward_economy.gd` | set_fx 扩参;dispatch 面加 ctx | -| `game/core/settlement/grant_segment.gd` | `_apply_counter_hit` 静态化;2 读口 ctx 化;`combo_cap` 收集与实参删除 | -| `game/core/run_ledger.gd` | 删 `counters_seed_snapshot`/`counters_merge_export` | +| `game/core/settlement/grant_segment.gd` | `_apply_counter_hit` 静态化;2 读口 ctx 化;`combo_cap` 收集与实参删除;:307「限次=合成行 adventure 域计数器闸」注释改 level 桶口径 | +| `game/core/run_ledger.gd` | 删 `counters_seed_snapshot`/`counters_merge_export`;:7-8 头注契约句与 :12 字段注(「rebuild 回灌/战末导出回写目标」)改 ctx 直写口径 | | `game/core/adventure_runner.gd` | 删 `_reset_tree_limited_counters` 及调用点 | | `game/core/level_runner.gd` | `level_counters` 公开成员+enc `"level_counters"` 下传 | -| `game/data/tables/feats.tres` | 4 枚 id 改名;`domain`/`reset_at` 键逐行删除 | -| `game/tests/static_scan.sh` | 新增 S 段:旧件符号+带引号旧 id 字面零命中 | +| `game/data/tables/feats.tres` | 4 枚 id 改名(**写点 :29/:492 与读点 :39/:501 成对同改**);`domain`/`reset_at` 键逐行删除 | +| `game/core/config_db_cross_validator.gd` | 新增计数器引用闭合校验(声明集 vs 读点集,违例入 errs→加载致命) | +| `game/smoke/adventure_monte_carlo.gd` | `:65` `rules` 档类型修复(`split` 结果包 `Array(…)`、`split(",", false)`)——T0 首提交,V12 rules 组生效前提 | +| `game/tests/static_scan.sh` | 新增 S11 段:旧件符号+带引号旧 id 字面零命中;**扫描面自带扩至 `game/**/*.gd` 与 `game/data/tables/*.tres`**(现役 :20-24 只覆盖 core/presentation 的 `.gd`,不扩面即空转)+扫描面非空自检 | | `game/tests/run_all.gd` | manifest 登记负面套件 | | `game/tests/unit/core/effect_words_t1_test.gd` | ⑥ export/seed 块、⑦ reset_at/spend 块重写为桶语义;裸 dispatch/写口 ctx 化 | -| `game/tests/unit/core/tree_rules_v11_test.gd` | :29-39 合成行字面 id 随改名 | +| `game/tests/unit/core/tree_rules_v11_test.gd` | :29-39 合成行字面 id 随改名+**删 :33/:39 两处 `"domain": "adventure"`**(不删则 `validate_trigger_row` 面当场转红) | | `game/tests/unit/core/feat_vouchers_test.gd` | V16 键名随前缀;V25 迁移保绿;新增 H1 旧档不崩断言 | | `game/tests/unit/core/settle_segments_test.gd` | `:32-33` 样本 id 换活 id+ctx 形态 | | `game/tests/unit/core/treasure_effects_test.gd` | 裸 dispatch 面 ctx 化(:34/:45/:53-56) | @@ -132,7 +147,9 @@ ### T0 基线(0.25d) - [ ] worktree/分支开工(process-conventions §1);拷主区 `.godot/` 与字体 `.import` 边车 -- [ ] 基线三查记档:冒烟全绿数目、单元 26 套件全绿、探针基线;记录 `sim-data/` 现行锚文件名与 sha1(**只记录不跑**——零数值改动禁采集) +- [ ] **首提交=采集器 `rules` 注入类型缺陷修复**:`adventure_monte_carlo.gd:65` 的 `String(args[7] if args.size() >= 8 else "").split(",")` 改为 `Array(String(args[7] if args.size() >= 8 else "").split(",", false))`(不包则 `PackedStringArray` 被 `adventure_runner.gd:118` 的 `is Array` 整组静默丢弃——实测 `rules` 档与 `off` 逐位相同);单独一个提交,提交信息注明「测试工具类型修复,无玩法改动」 +- [ ] 基线三查记档:冒烟全绿数目、单元 26 套件全绿、探针基线 +- [ ] **V12 批前基线=卡片成文后的准备阶段已采完,批内不重跑**:三组文件(`sim-data/v12_base_off_n20_81000_f75acc1.jsonl` 20 局 81000–81019,sha256 `bf084ef3…`/`sim-data/v12_base_formed_n10_81000_f75acc1.jsonl` 10 局 81000–81009,sha256 `2d3d4247…`/`sim-data/v12_base_rules-sense_n10_81000_f75acc1_p1.jsonl` 10 局 81000–81009,sha256 `0fcbcb26…`,`_p1` 后缀=依赖上一条采集器修复),主区 `sim-data/` 与卡片 worktree `sim-data/` 两处留盘(`sim-data/` 不入 git,实现批新 worktree 内无此目录——开工时从主区拷入本 worktree 的 `sim-data/`,或让比对器按绝对路径读主区文件);采集时工作区无改动,有效码=commit `826e7ea1` 的 `game` 树 `f75acc15…` +本 T0 首提交。**开工先核 `git rev-parse HEAD:game`**:若不等于 `f75acc15…`(master 的 `game/` 在准备阶段之后前进过),留盘基线作废,按 T7 命令重采三组 40 局 - [ ] `git log --oneline -1` 与 `tools/run_all_gates.sh --code` 基线档记档(起点绿态) **产出**:基线档(实现报告 T0 节)。 @@ -182,67 +199,81 @@ - [ ] 确认 battle/level 桶零显式清零代码(宿主 new 天然空);全仓 grep `_reset_on_round_start`/`_adventure_counter_ids`/`_reset_tree_limited_counters` 零命中 - [ ] 孤立验证:round 桶写非零→dispatch("round_start")→整桶空;battle 前缀键同一 dispatch 下不动 -### T5 前缀校验双落点(0.25d) +### T5 前缀校验双落点+引用闭合(0.5d) -**取用**:T2 的 `bucket_of` 前缀规则。**产出**:加载期前缀致命通道(V9)。 +**取用**:T2 的 `bucket_of` 前缀规则。**产出**:加载期前缀致命通道+读点无声明致命通道(V9 两侧)。 -- [ ] 新增静态判定函数 `static func counter_id_legal(counter_id: String) -> bool`(名字锁定;规则=提案 §3.3 字面):`round_`/`lvl_`/`adv_` 前缀=true;`meta_` 前缀=false(引擎不接存档级);其余=true(无前缀落 battle——「其余未知前缀也非法」在语法上不可判定:一切 snake_case id 都以「词_」形态开头,禁自造封闭词表=第二事实源红线;自造新前缀静默落 battle 的残余风险由 V10 扫描与评审承担) +- [ ] 新增静态判定函数 `static func counter_id_legal(counter_id: String) -> bool`(名字锁定;判据见提案 §3.3 与 §12 裁点②):`round_`/`lvl_`/`adv_` 前缀=true;`meta_` 前缀=false(引擎不接存档级);其余=true(无前缀落 battle——「其余未知前缀也非法」在语法上不可判定:`feats.tres` 现有 14 枚 id 全含下划线,除三枚已带 `round_` 前缀外首段全是普通词(`pain_`/`geo_`/`helm_`…),封闭前缀词表会把它们全部判成违例,且自造词表=第二事实源红线)。自造新前缀静默落 battle 的残余风险由下条引用闭合校验(拦写读不一致)与 V10 扫描承接,不再单靠人工评审 +- [ ] **引用闭合校验**落 `config_db_cross_validator.gd`(既有跨表校验面,非单行校验):三源装载完成后收集声明集=各行 `do.counter_id` ∪ 树合成行 `do.counter_id`;再遍历全部读点(`when` 内 `fact: counter` 的 `id`、`do.value_counter`、`counter_spend` 的 `counter_id`),读点 id 不在声明集→errs 追加 `{"category": "enum", ...}` 违例→加载致命。判据只做成员判定,禁在其中再写前缀规则(契约 6)。两判据互补且不可互相替代:`meta_`/自造前缀由 `counter_id_legal` 拦,写点与读点拼错不一致由本条拦(现役 `feats.tres` 的 `:29`/`:39` 与 `:492`/`:501` 就是成对同名形态,改一半即中招) - [ ] `validate_trigger_row` 增分支:`do` 含 `counter_id` 时校验,非法→errs 追加 `{"category": "enum", ...}` 违例 - [ ] `_push_trigger` 增 assert:`do` 含 `counter_id` 时同规则 assert(树合成行路径,写错调试期致命;assert 消息以 `[效果引擎]` 前缀起头,供孤立验证扫描) -- [ ] 孤立验证(V9 assert 侧):`--headless --script` 最小驱动 rebuild 带「counter_id 带非法前缀」的合成行——输出扫到 `[效果引擎]` assert 签名=拦截在位(debug 通道;assert 不改退出码,判据=输出签名扫描) -- [ ] 新建 `game/tests/unit/data_config/counter_prefix_fatal_test.gd`:copy_tables 夹具→向 feats.tres 注入 `"counter_id": "meta_probe"` 行→`load_tables` 必须拒绝→quit(1);违规放行 quit(2)(reward_dangling_treasure_test.gd 同构,退出码协议同款) +- [ ] 孤立验证(V9 assert 侧):`--headless --script` 最小驱动**直调 `_push_trigger`** 塞一条 `"counter_id": "meta_probe"` 行——`rebuild` 只认固定规则键,坏前缀合成行无法经 `rebuild` 从外部注入,故注入点必须是 `_push_trigger`;输出扫到 `[效果引擎]` assert 签名=拦截在位(debug 通道;assert 不改退出码,判据=输出签名扫描) +- [ ] 新建 `game/tests/unit/data_config/counter_prefix_fatal_test.gd`:copy_tables 夹具两断言——①向 feats.tres 注入 `"counter_id": "meta_probe"` 行→`load_tables` 必须拒绝;②把某行 `when` 内 `fact: counter` 的读点 id 改成表内无声明的 `"pain_layer"`(比 `pain_layers` 少一个 s)→`load_tables` 必须拒绝。任一条违规放行即 quit(2)(reward_dangling_treasure_test.gd 同构,退出码协议同款) - [ ] `tests/run_all.gd` manifest 登记:`{"path": "res://tests/unit/data_config/counter_prefix_fatal_test.gd", "expect": 1, "negative": true}` ### T6 数据行与合成行改名(0.25d) **取用**:契约 7 的 4 枚新 id。**产出**:16 枚 id 全部合规(前缀即归属)。 -- [ ] `feats.tres`:`"twin_digs"`→`"adv_twin_digs"`(:29);`"instinct_atk"`→`"round_instinct_atk"`(:492);`pain_layers` 各行删 `"domain": "adventure"` 键(:375-377/:407 邻近/:533-535/:626-628——归 battle,N4);`twin_digs` 行删 domain 键、`insurance_used` 行删 `domain: battle` 键(:875-877);全部 `reset_at` 键删除(:209-212/:490-494/:514-518/:719-723) -- [ ] `effect_engine.gd` 合成行(:136-155):`tree_sense_sharp_used`→`lvl_tree_sense_sharp_used`、`tree_sense_guard_used`→`lvl_tree_sense_guard_used`(when id 与 counter_id 共六处字面:锋/守各 when 两行+do counter_id 一行);:116-122 注释内「domain=adventure」叙述同步改前缀口径 -- [ ] 全仓 grep 确认零 `domain`/`reset_at` 键残留于 feats.tres;`pain_layers`/`geo_types` 等 12 枚无前缀 id 零改名(无前缀=battle 即归属) +- [ ] `feats.tres`:**写点与读点成对改名**——`"twin_digs"`→`"adv_twin_digs"`(写 :29/读 `when.id` :39);`"instinct_atk"`→`"round_instinct_atk"`(写 :492/读 `value_counter` :501);`pain_layers` 各行删 `"domain": "adventure"` 键(:375-377/:407 邻近/:533-535/:626-628——归 battle,N4);`twin_digs` 行删 domain 键、`insurance_used` 行删 `domain: battle` 键(:875-877);全部 `reset_at` 键删除(:209-212/:490-494/:514-518/:719-723) +- [ ] `effect_engine.gd` 合成行(:136-155):`tree_sense_sharp_used`→`lvl_tree_sense_sharp_used`、`tree_sense_guard_used`→`lvl_tree_sense_guard_used`(when id 与 counter_id 共六处字面:锋/守各 when 两行+do counter_id 一行);**删除 :145/:155 两处 `"domain": "adventure"`**(随 DO_KEYS 删键,否则合成行自带出域键);:116-122 注释内「domain=adventure」叙述同步改前缀口径 +- [ ] `tree_rules_v11_test.gd` 夹具行(:33/:39)随合成行同源改 id + **删两处 `"domain": "adventure"`**——该夹具经 `validate_trigger_row` 校验,`DO_KEYS` 删键后不删此处即当场转红 +- [ ] 过期注释清扫(§3 清单逐处):`effect_engine.gd:42/:70-71/:118-120`、`grant_segment.gd:307`、`run_ledger.gd:7-8/:12`、`level_runner.gd:630` +- [ ] 全仓 grep 确认零 `domain`/`reset_at` 键残留(feats.tres+引擎合成行+测试夹具三处都算);`pain_layers`/`geo_types` 等 9 枚无前缀 id 零改名(前缀判据只禁 `meta_`,其余落 battle 即归属——判据理由与残余风险的承接面见 T5 与 §12 裁点②;14 枚去三枚已带 `round_` 前缀、再去本批改名的 `twin_digs`/`instinct_atk` 两枚,落地后无前缀者恰为 9 枚) -### T7 验收断言+旧测试迁移+变异验证+同种子复放比对(1–1.5d) +### T7 验收断言+旧测试迁移+变异验证+同种子复放比对(1.25–1.75d) -**取用**:T1–T6 全部产物;§9 验证闸清单(V1–V14 断言内容以本卡为唯一规格)。**产出**:V1–V13 全绿+变异验证记录表+G1 判读记录。 +**取用**:T1–T6 全部产物;§9 验证闸清单(V1–V14 断言内容以本卡为唯一规格)。**产出**:V1–V13 全绿+变异验证记录表+三组复放判读记录(承载提案 §7.10.12 的 G1 已知差异面条目)。 -- [ ] **新增单测断言**(套件内新增,断言尾缀〔无状态化 Vn〕):V2/V3/V7/V13 落 `feat_vouchers_test.gd`(V16 键名 `pain_layers:3, twin_digs:5`→`adv_twin_digs:5` 等活键改写;V25 全组保绿不改断言内容;新增 H1:bp_legacy 注入 `"tree_sense_sharp_used": 2` 旧键 resume 受理零崩溃);V9 落 `counter_prefix_fatal_test.gd`(T5) -- [ ] **新增冒烟断言段**(`smoke_all.gd` 树规则段近旁):V1(两编排器共享 RunLedger,第 1 战 `request_dig(0, false)` 一次无返还+桶值 1,第 2 战一次返还成立+桶值 2);V4/V5(两编排器经 enc["level_counters"] 共享同字典:第 1 战两次 `_fx.dispatch(counters_ctx, "mine_hit", {})` 命中桶值 2,第 2 战同 dispatch 返回空且桶值不动;新 LevelRunner 新桶为空);V6(第 1 战经 `_judge_seal` 置 `insurance_used=1`,第 2 战读 0);V8(round 桶直写非零→`orch._emit_round_start()`→全零,battle 前缀键不动) +- [ ] **新增单测断言**(套件内新增,断言尾缀〔无状态化 Vn〕):V2/V3/V7/V13 落 `feat_vouchers_test.gd`(V16 键名 `pain_layers:3, twin_digs:5`→`adv_twin_digs:5` 等活键改写;V25 全组保绿不改断言内容;新增 H1:bp_legacy 同时注入 `"tree_sense_sharp_used": 2` 与 `"pain_layers": 3` 两枚旧键,resume 受理零崩溃、旧键留桶成死键——`pain_layers` 旧键即契约 8 的续玩差异面);V9 落 `counter_prefix_fatal_test.gd`(T5 两断言) +- [ ] **新增冒烟断言段**(`smoke_all.gd` 树规则段近旁;每条夹具的 enc 键写死,不留给实现期自由发挥):V1(夹具=同一 `RunLedger` 下两次 `begin_battle`,**两 enc 均带 `"feats": ["twin_pick"]`**——不带则 `adv_twin_digs` 无订阅行,断言空转;第 1 战 `orch1.action_economy.request_dig(0, false)` 一次无返还+桶值 1,第 2 战一次返还成立+桶值 2);V4/V5(夹具=两编排器 enc 均带 `"tree_rules": ["sense_sharp", "sense_guard"]` 且 `"level_counters"` 传同一字典引用:第 1 战两次 `_fx.dispatch(counters_ctx, "mine_hit", {})` 命中桶值 2,第 2 战同 dispatch 返回空且桶值不动;新 LevelRunner 新桶为空);V6(第 1 战经 `_judge_seal` 置 `insurance_used=1`,第 2 战读 0);V8(round 桶直写非零→`orch._emit_round_start()`→全零,battle 前缀键不动) - [ ] **旧断言迁移/摘除**(F2——同批同轮):`effect_words_t1_test.gd` ⑥(:77-94 export/seed 往返)改桶语义断言(共享 adventure 桶字典往返)、⑦(:96-124 reset_at/spend)重写(round 桶轮始清空语义+静态 spend);`smoke_all.gd` `:6076-6083`(counter_add+export/seed 链)改共享桶形态、`:6124-6128`(`_reset_tree_limited_counters` 直呼)删除;`:1419-1602` 裸引擎段 53 处 dispatch+8 处写口改 ctx 形态(逐行扫尽,rig 顶自建 ctx 或取 `orch.counters_ctx`);`tree_rules_v11_test.gd:29-39` 字面随 T6 新 id;`settle_segments_test.gd:32-33` 样本 id `dig_combo`→`geo_types`+ctx 形态;`treasure_effects_test.gd:34/:45/:53-56` dispatch 加 ctx;各 rig enc `"adventure_counters": {}` 死键删除(编排器只读 `enc["ledger"]`,:372 实证) -- [ ] **静态扫描 S 段**(`static_scan.sh`):新增规则组——`counters_export`/`counters_seed`/`counters_merge_export`/`counters_seed_snapshot`/`_reset_tree_limited_counters` 零命中+带引号旧 id 字面 `"tree_sense_sharp_used"`/`"tree_sense_guard_used"`/`"twin_digs"`/`"instinct_atk"` 零命中(带引号匹配防新名误伤);`run_all_gates.sh` 域内已有 static_scan,无闸件清单改动 -- [ ] **变异验证**(§9 变异表 4 条逐条:注入→最窄闸见红→`git checkout -- <文件>` 还原→复跑回绿→记录) -- [ ] **同种子复放比对**(V12):先做**锚新鲜度核对**——用批前基线码(T0 记录的起点 commit)重放种子 81000–81001 两局,与现行锚 `sim-data/runsum_m3iter9exit_81000_n400_20261003.jsonl` 对应行逐位比对,一致方判锚可用(不一致=锚与 HEAD 玩法口径脱钩,L3 停机呈报,禁带病判读);核对通过后重放锚内 normal 档种子 81000–81019(20 局,零局新采集),按 §7.10.12 分组判读(未受影响局=feat_mode off 或装备集不含双管镐/sense 两组→全字段逐位一致;受影响局→输入面恒等+`levels_cleared` 分布不得上行);逐位差异明细记实现报告 +- [ ] **静态扫描 S 段**(`static_scan.sh` 新增 S11):规则=`counters_export`/`counters_seed`/`counters_merge_export`/`counters_seed_snapshot`/`_reset_tree_limited_counters` 零命中+带引号旧 id 字面 `"tree_sense_sharp_used"`/`"tree_sense_guard_used"`/`"twin_digs"`/`"instinct_atk"` 零命中(带引号匹配防新名误伤)。**扫描面必须自带扩面**:现役 `:20-24` 的扫描路径单点只有 `game/core` 与 `game/presentation`、`scan_files` 只数 `.gd`,而改名与旧字面同时存在于 `feats.tres`、`game/tests/`、`game/smoke/` 三处——本段单独声明扫 `game/**/*.gd` 与 `game/data/tables/*.tres`,并沿用 `require_scan_face` 同规做扫描面非空自检(空面判红,防静默空转);`run_all_gates.sh` 域内已有 static_scan,无闸件清单改动 +- [ ] **变异验证**(§9 变异表 7 行逐条:注入→最窄闸见红→`git checkout -- <文件>` 还原→复跑回绿→记录) +- [ ] **批内同种子复放比对**(V12):批后码按 T0 的同一命令行重跑三组 40 局(STATE=post,落 `sim-data/v12_post_*_.jsonl`),再用 `tools/parity_compare.py` 三组各判读一次——off 组 `--mode parity` 要求 20 局整条记录逐位相等;formed 组与 rules 组 `--mode input-parity` 要求输入面八字段(`policy`/`seed`/`atk_pct`/`hp_pct`/`route`/`attune`/`equipped`/`feat_mode`)与顶层字段集恒等、种子集与局数一致;输出面差异打差异表、`levels_cleared` 合计与直方图作报告行(formed 组预期不降、rules 组预期不升;10 局无统计功效,**方向不作判红依据**)。采集命令与分组(normal 档、route=min、attune/shop 显式 off,逐字与 T0 准备阶段一致): + + ```bash + mc () { "$GODOT" --headless --path "$(cygpath -m "$PWD/game")" \ + --script res://smoke/adventure_monte_carlo.gd -- \ + "$1" "$2" normal min off off "$3" "$4" 2>&1 | grep '^RUNSUM ' > "$5"; } + mc 20 81000 off "" sim-data/v12_post_off_n20.jsonl + mc 10 81000 formed "" sim-data/v12_post_formed_n10.jsonl + mc 10 81000 rules "sense_sharp,sense_guard" sim-data/v12_post_rules-sense_n10.jsonl + ``` + + 判红后的归因顺序(先排除环境再查码):在基线码上重放 5 局对哈希——一致=代码漂移(L3),不一致=环境漂移(console exe/导入缓存/种子面),按 process-conventions 环境自救清单处理后再判。逐位差异明细与三组判读结论记实现报告;40 局合计约 1.5 分钟(准备阶段实测 normal 档单局 1.8 秒),禁顺手加档 - [ ] 里程碑步出口:`tools/run_all_gates.sh --code`(代码域闸全绿);波内修复只做出红最小单元的最窄确认 ### T8 产出物落位卡(0.25d) **取用**:T0–T7 全部记录。**产出**:`reports/` 三份齐+06 状态就绪。 -- [ ] 实现报告 `reports/iter-实现报告-<日期>.md`:各 T 卡状态+变异验证记录表(注入点 file:line/跑的闸/红输出摘要/还原结论)+G1 判读记录+实机目检总清单(V14 登记) +- [ ] 实现报告 `reports/iter-实现报告-<日期>.md`:各 T 卡状态+变异验证记录表(注入点 file:line/跑的闸/红输出摘要/还原结论)+三组复放判读记录(基线与复放六个文件的 sha256、T0 首提交 SHA、逐位差异明细、`levels_cleared` 直方图)+实机目检总清单(V14 登记) - [ ] 代拍板追认单 `reports/iter-代拍板-<日期>.md`(§11 白名单内微决策逐条备案) -- [ ] 反哺点清单 `reports/iter-反哺点-<日期>.md`:§6 同步面五条逐条落点(architecture.md 三处改写文案与依据、tests/README 套件数、07 目检入册项) +- [ ] 反哺点清单 `reports/iter-反哺点-<日期>.md`:§6 同步面逐条落点(architecture.md 三处改写文案与依据、tests/README 套件数、07 目检入册项、**提案三处口径订正(§7.10.3/§7.10.12/§7.7,依 §12 裁决以本卡为准)**、`sim-data/README.md` 批次例外登记) - [ ] 轻闸全绿后合并前自查:`game/` 与 `tools/` 改动全在案、`design/` 与根规则文件零触碰(`git diff --stat` 核对) ## 9. 验证闸(逐需求点验收清单) -**闸口径**:批末=代码域闸全绿(冒烟+单元+探针+渲染/静态,`tools/run_all_gates.sh --code`)+同种子复放比对(V12,零局新采集)。里程碑步(波/T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试/工具先行孤立验证再并入全套。本批无数值改动,改前改后读数对比由 V12 分组判读承载。 +**闸口径**:批末=代码域闸全绿(冒烟+单元+探针+渲染/静态,`tools/run_all_gates.sh --code`)+批内同种子复放比对(V12,批后 40 局,二档上限内)。里程碑步(波/T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增测试/工具先行孤立验证再并入全套。本批无数值改动,三处预期行为差异(契约 1)由 V12 三组判读与 V1/V4/V5 确定性断言共同承载——方向性结论只由后者下,V12 只负责「没改坏」。 **逐需求点清单**(断言尾缀〔无状态化 Vn〕;「测什么/怎么测/谁验」三问逐条可答): | # | 需求点 | 测什么 | 怎么测(注入粒度) | 落点 | 谁验 | |---|---|---|---|---|---| -| V1 | adventure 桶跨战持久(`run_ledger.gd:42` 缺陷消解回归钉面) | 第 1 战写入第 2 战可读、读写走真实事件面 | 两编排器共享 `RunLedger`:第 1 战 `orch1.action_economy.request_dig(0, false)`(无返还,`adv_twin_digs`=1)→第 2 战(同 ledger)一次挖返还成立+桶值 2 | 冒烟新段 | 自动 | +| V1 | adventure 桶跨战持久(`run_ledger.gd:42` 缺陷消解回归钉面) | 第 1 战写入第 2 战可读、读写走真实事件面 | 夹具=同一 `RunLedger` 下两次 `begin_battle`、两 enc 均带 `"feats": ["twin_pick"]`:第 1 战 `orch1.action_economy.request_dig(0, false)`(无返还,`adv_twin_digs`=1)→第 2 战一次挖返还成立+桶值 2 | 冒烟新段 | 自动 | | V2 | adventure 桶新冒险置空 | `start_adventure` 后桶空 | `start_adventure(301,...)` 后 `run_ledger.adventure_counters` 为空 | `feat_vouchers_test.gd` | 自动 | | V3 | adventure 桶随断点往返 | 快照/回填逐位相等 | 写桶→`_on_level_finished`→`breakpoint_snapshot`→`resume_adventure`→逐键比对(键随新 id) | `feat_vouchers_test.gd`(V16 改写) | 自动 | | V4 | level 桶同关跨战保持、进关清零 | 共享桶第 2 战保值;新 LevelRunner 新桶空 | 两编排器 enc["level_counters"] 同引用:第 2 战读 `lvl_tree_sense_*` 保持;再 new LevelRunner 断言 `level_counters` 空 | 冒烟新段 | 自动 | -| V5 | tree_sense 每关限 2 次(削弱正面确认) | 第 1 战用满→第 2 战 lt 2 闸关零命中 | V4 同款共享桶:第 1 战两次 dispatch mine_hit 命中(桶值 2)→第 2 战同 dispatch 返回空且桶值不动 | 冒烟新段 | 自动 | +| V5 | tree_sense 每关限 2 次(削弱正面确认) | 第 1 战用满→第 2 战 lt 2 闸关零命中 | 夹具=V4 同款共享 `level_counters` 引用、两 enc 均带 `"tree_rules": ["sense_sharp", "sense_guard"]`:第 1 战两次 dispatch mine_hit 命中(桶值 2)→第 2 战同 dispatch 返回空且桶值不动 | 冒烟新段 | 自动 | | V6 | battle 桶随宿主销毁 | 第 2 战读 `insurance_used`==0 | 第 1 战 `_judge_seal` 置 1→新编排器 begin_battle 后读 0 | 冒烟新段 | 自动 | | V7 | 完美保险每战限次一次(卡② V25 迁移保绿) | V25 全组断言原样绿 | 沿用 V25 注入口径零改写——双账同点双记不改面(契约 5) | `feat_vouchers_test.gd:130-188` | 自动 | | V8 | round 桶轮始清空 | 轮始后 round 桶全零、他桶不动 | round 桶直写非零→`orch._emit_round_start()`→全零;`fx_counters_battle` 内键不动 | 冒烟新段 | 自动 | -| V9 | counter_id 非法前缀加载致命(.tres 行+树合成行双落点) | 坏前缀行 load_tables 拒绝;合成行 assert 触发有据 | 夹具注入 `"counter_id": "meta_probe"`→load_tables false→expect 1;assert 侧=T5 孤立验证(最小驱动 rebuild 坏前缀合成行,输出扫 `[效果引擎]` assert 签名)+变异表第 4 行证明拦截 | `counter_prefix_fatal_test.gd`(expect 1, negative)+T5 孤立验证 | 自动 | -| V10 | 旧件零残留 | 五符号+四带引号旧 id 字面全仓零命中 | static_scan 新 S 段(规则见 T7) | `static_scan.sh` | 自动 | +| V9 | counter_id 非法前缀加载致命(.tres 行+树合成行双落点)+读点无声明加载致命(引用闭合) | 坏前缀行 load_tables 拒绝;合成行 assert 触发有据;读点 id 不在声明集 load_tables 拒绝 | 夹具①注入 `"counter_id": "meta_probe"`→load_tables false;夹具②把某 `when` 读点 id 改成 `"pain_layer"`(无声明)→load_tables false;assert 侧=T5 孤立验证(**直调 `_push_trigger`** 塞坏前缀行,输出扫 `[效果引擎]` assert 签名——`rebuild` 不认外部键,经它注入不可执行)+变异表第 4/5 行证明拦截 | `counter_prefix_fatal_test.gd`(expect 1, negative)+T5 孤立验证 | 自动 | +| V10 | 旧件零残留 | 五符号+四带引号旧 id 字面在 `game/**/*.gd` 与 `game/data/tables/*.tres` 零命中(面含测试与 smoke,不只 core) | static_scan 新增 S11 段(扫描面自带扩面+非空自检,规则见 T7) | `static_scan.sh` | 自动 | | V11 | 旧断言同批摘除 | 冒烟/单测无旧件断言残留 | V10 扫描面覆盖测试文件+F2 清单逐项核对(`effect_words_t1` ⑥⑦/`smoke :6076-6083`/`:6124-6128`/`:1419-1602`) | 冒烟+单测迁移轮 | 自动+实现报告核对表 | -| V12 | 同种子复放分组判读 | 锚新鲜度核对通过后:未受影响局逐位一致;受影响局输入面恒等+`levels_cleared` 不上行 | 批前基线码先重放 81000–81001 两局与现行锚 `runsum_m3iter9exit_81000_n400_20261003.jsonl` 逐位比对(新鲜度闸);通过后批后码重放锚内 normal 档 81000–81019(20 局);分组判读按提案 §7.10.12 | 复放脚本+实现报告判读记录 | 自动+人工判读 | -| V13 | 旧档断点加载不崩 | 旧键 resume 受理零崩溃 | bp_legacy 注入 `"tree_sense_sharp_used": 2`→`resume_adventure` 受理、旧键留桶 | `feat_vouchers_test.gd`(V16 同构) | 自动 | +| V12 | 批内同种子复放三组判读 | off 组整条记录逐位相等;formed 组/rules 组输入面八字段与顶层字段集恒等、种子集与局数一致、零崩溃;输出面差异与 `levels_cleared` 方向只记报告 | T0 留盘基线(三组 40 局,哈希在册)vs 批后同命令同种子复放 40 局;`tools/parity_compare.py` 三组各跑一次(判据与命令见 T7) | 复放比对器+实现报告判读记录 | 自动+人工判读 | +| V13 | 旧档断点加载不崩 | 旧键 resume 受理零崩溃 | bp_legacy 同时注入 `"tree_sense_sharp_used": 2` 与 `"pain_layers": 3`→`resume_adventure` 受理、旧键留桶成死键 | `feat_vouchers_test.gd`(V16 同构) | 自动 | | V14 | 天赋树锋/守削弱玩家可感知(登记不阻塞) | 每关第 3 次起无减免/无返还 | 用户实战目检:同关第 3 次踩雷不再减半、第 3 次标错不再返 AP | 实机目检总清单+07 入册(反哺点) | 用户目检 | **变异验证必做**(无变异红记录只写「已实现,拦截能力未证」): @@ -253,15 +284,17 @@ | V5 | V4/V5 两编排器改传各自新建 level 字典(复刻 :838-841 旧形态跨战不共享) | 跑 `smoke_all.gd` 单脚本,核对 V5 断言红 | V5 红 | | V9(.tres 侧) | 注释 `validate_trigger_row` 前缀校验分支 | `counter_prefix_fatal_test.gd` 单跑 | 套件转「违规放行」态判红 | | V9(合成行侧) | 注释 `_push_trigger` 内前缀 assert 行 | T5 孤立验证驱动单跑(输出签名扫描) | assert 签名消失=拦截失效判红 | -| V10 | 任一源文件临时写回一处 `"counters_export"` 引用 | `static_scan.sh` 单跑该 S 段 | 扫描红 | +| V9(引用闭合侧) | 注释 `config_db_cross_validator.gd` 的闭合校验调用行 | `counter_prefix_fatal_test.gd` 单跑 | 夹具②(读点无声明)违规放行=拦截失效判红 | +| V10 | 任一源文件临时写回一处 `"counters_export"` 引用 | `static_scan.sh` 单跑 S11 段 | 扫描红 | +| V12(采集器侧) | 还原 T0 首提交那一行(`Array(…)` 包裹去掉) | T7 复放 rules 组判读 | rules 组与 off 组逐位相同=分组空转,被 L3「采集器补丁缺位」条款捕获 | **测试方式两分法**:V1–V13 全部=调试接口直改/直调核心层(共享字典注入、`request_dig`/`_judge_seal`/`_emit_round_start` 核心写口直调),不经 UI;V14=用户目检(登记不阻塞,验收时间用户自定)。 ## 10. 异常分级 - **L1 自救**:编译错/笔误级红;孤立验证失败(新类/新函数首跑);复放脚本参数错重跑。 -- **L2 记录继续**:V12 受影响局保守超集判定边界个案(某局归类存疑)——按受影响组从严判读并记录;负面套件在 release 模式 assert 剥离致假绿——换 debug 通道验证并记录(配置问题非代码缺陷)。 -- **L3 停机呈报**:V12 未受影响局出现任何逐位差异(确定性破约);V7/V25 任何一条红(契约 5 破);V13 崩溃;`pain_layers` 行为漂移(契约 8 破);写口语义与现役不一致导致 V1–V8 连带红且无法归因单点。 +- **L2 记录继续**:V12 的 formed/rules 两组输出面差异面过大(半数以上字段位移)——属轮数一变后的 RNG 级联放大,如实记录不判红;负面套件在 release 模式 assert 剥离致假绿——换 debug 通道验证并记录(配置问题非代码缺陷)。 +- **L3 停机呈报**:V12 的 off 组出现任何逐位差异,或 formed/rules 两组输入面八字段/字段集/种子集/局数任一不一致(确定性破约);rules 组复放结果与 off 组逐位相同(采集器补丁缺位,分组空转、数据不可信);V7/V25 任何一条红(契约 5 破);V13 崩溃;`pain_layers` 单战「攒层→附加→清零」行为漂移(契约 8 破);写口语义与现役不一致导致 V1–V8 连带红且无法归因单点。 - 环境故障(worktree 缓存/资源导入/console exe)按 process-conventions 异常分级自救清单处理。 ## 11. 代拍板白名单(超清单必呈报) @@ -272,6 +305,8 @@ 4. `feats.tres` 行内键序调整(键集与值已锁)。 5. `effect_engine.gd` 合成行注释的措辞更新(口径已锁:前缀表述)。 6. 负面套件内诊断文案措辞(退出码协议已锁)。 +7. `tools/parity_compare.py` 的内部实现与输出排版(两判据模式、`--expect`、退出码 0/1/2 与「字段集从基线推导」四条已锁)。 +8. 复放文件名里的码态后缀取值与差异表行数(三组名、种子面、留盘位置已锁)。 ## 12. 待裁点 @@ -279,14 +314,25 @@ 本卡全部设计定值承自提案 §7(N1–N6)、§7.7(零采集)、§7.10(缺口闭合),均为「已生效裁定+硬纪律」推导所得并写明依据;其中以下各项带「可否决」标注(提案 §7.9/§7.10.14 同口径),用户审卡时可单独推翻、推翻即回改卡:§7.9 五条(立项判据载体/domain+reset_at 删键/`adventure_counters` 容器名不改/旧档不迁移/两编译名单删除)与 B6(dig_combo 死路删除)。卡片成立还需 grilling 复核通过(零待裁点+验收三问逐条可答已自查,见下)。 +**owner 已裁六项(对抗复核轮 2026-10-20,已并入正文对应节,非待裁点)**: + +| # | 裁决 | 落点 | 判据 | +|---|---|---|---| +| ① | 复放基线改批内重采(三组 40 局,二档上限内),`sim-data/` 纪律③⑦开批次例外 | §1.6/契约 10/T0/T7/V12 | 现行锚 24 字段面与现采 33 字段面不同构、无 `feat_mode` 分组面,且 `sim-data/` 不入 git 致批 worktree 内根本没有该文件——「锚新鲜度核对」一步必红,原方案不可执行 | +| ② | 前缀判据采「只禁 `meta_`+其余落 battle」的松版,另加装载期引用闭合校验 | §1.5/契约 6/T5/V9 | `feats.tres` 现有 14 枚 id 全含下划线、首段除三枚 `round_` 外全是普通词,严版误伤全部既有 battle id;封闭前缀词表=第二事实源(2026-10-19 registry 注销裁定同类);实测风险形态是写读成对字面改一半,闭合校验正面拦截 | +| ③ | 改名与删键面扩到读点、合成行、测试夹具与过期注释 | T6/§7 文件结构 | `:39`/`:501` 读点、`effect_engine.gd:145/:155` 与 `tree_rules_v11_test.gd:33/:39` 的 `domain` 残留、七处注释——原卡遗漏,其中夹具残留会当场转红 | +| ④ | `pain_layers` 措辞改为「单战零变更+旧档续玩不再跨战」,列第三处预期差异 | 契约 1/8/V13 | 断点回灌路径(`adventure_runner.gd:206-208`→`:1033`)使旧档续玩可跨战携带该键,改后不再携带——属缺陷消解,不得写成「零语义变化」 | +| ⑤ | 验收夹具写死到 enc 键字面;V9 assert 侧注入点改直调 `_push_trigger` | §9 V1/V5/V9/T7 | `rebuild` 只认固定规则键,经它注入坏前缀合成行不可执行;V1/V5 不带 `"feats"`/`"tree_rules"` 则订阅行为空、断言空转 | +| ⑥ | 采集器 `rules` 注入类型缺陷修复入本批(T0 首提交),不单独开非迭代批 | T0/§7 文件结构/V12 | 一行测试工具修复在本批末的代码域闸里一起验,省一次全量闸;不修则 rules 组恒假绿,V12 削弱面失去复放承载 | + --- ## 附:任务卡自审五查记录(2026-10-20 成文自检) -**grilling 复核轮**:成文后经独立会话对抗复核(新会话视角、不带写卡人假设),五项回炉点已全落——①行号群订正(成员块 :83-88/:84-87、`counters_seed` :255、`EffectEngine.new` :271、合成行 :136-155、注释 :137、rebuild :1610、裸引擎段实数 53 处、合成行字面六处);②V12 具名锚+T7 增锚新鲜度核对步(批前基线码两局配对在先);③V9 assert 侧补孤立验证与变异行;④T5 判定函数名落定 `counter_id_legal`;⑤估时头对齐 T 表加总+超盒切分预案。复核结论「定点回炉后可交用户审」已执行。 +**grilling 复核轮**:成文后经独立会话对抗复核(新会话视角、不带写卡人假设),五项回炉点已全落——①行号群订正(成员块 :83-88/:84-87、`counters_seed` :255、`EffectEngine.new` :271、合成行 :136-155、注释 :137、rebuild :1610、裸引擎段实数 53 处、合成行字面六处);②V12 复放基线的取数面与判据(现落定为批内三组 40 局,见 §12 裁点①);③V9 assert 侧补孤立验证与变异行;④T5 判定函数名落定 `counter_id_legal`;⑤估时头对齐 T 表加总+超盒切分预案。复核结论「定点回炉后可交用户审」已执行。 1. **规格覆盖**:完成定义 1–8 ↔ T 卡映射——1→T2;2→T1;3→T2/T3;4→T1;5→T5;6→T7(V12);7→T3;8→T7(V13)。验收 V1–V14 逐条有落点。✓ 2. **步骤扫描**:各步一动作一核查结果;实现体未写(签名+断言已锁);无「TBD/酌情」句式。✓ 3. **类型一致性**:`EffectContext` 字段名、编排器/LevelRunner 成员名、enc 键名、静态函数签名在 T1/T2/T3/T7 引用处逐字一致(契约 7 锁定)。✓ -4. **审查焦点**:规格暗示的失败模式对照——确定性漂移(V12 未受影响组+锚新鲜度闸)、引用切断(契约 3+V1 变异)、限次失效回退(V5 变异)、加载致命旁路(V9 双侧变异)、死路复活(V10)——均有承载;「同轮 dispatch 内 round 桶清空与订阅行命中并存」的时序语义由 V8+现役特判位置保持承载。✓ +4. **审查焦点**:规格暗示的失败模式对照——确定性漂移(V12 off 组逐位判据+判红先归因环境再归因码)、引用切断(契约 3+V1 变异)、限次失效回退(V5 变异)、加载致命旁路(V9 三侧变异:`.tres`/合成行/引用闭合)、死路复活(V10)、复放分组空转(rules 组与 off 组同形即 L3,配采集器侧变异行)——均有承载;「同轮 dispatch 内 round 桶清空与订阅行命中并存」的时序语义由 V8+现役特判位置保持承载。✓ 5. **篇幅比例**:本卡以签名/断言/落点为主,无函数体代码块。✓