修卡(效果端口与掷格分件批·godot-master终审折入): v1.0→v1.1——阻断三处(①rider_tier两键三方矛盾:契约2闭面改五键声明三when事实+两rider运行态读数,biz_fact规格补答两rider键,V3阴性对照守卫不变;②S7_B1_SET漏登reward_roller.gd:T2步与§7结构图补齐,轴戊S7/S8随动两新件全登;③variant_placement白盒指址申报漏计:四处→economy内部符号族全量约17行21处五符号(_rollable_rows/_pool_weight/_biased_cell_rows/board_skew/luck_bias_query),F12/§3.1/§7/T2/§9申报表五处同改)+建议五处(复位五拍/T3套件清单补四件/V1期望输出补订阅锚与读数面/V3合成行effect段逐字抄铁壁行零推表/combat_segment等价论证换唯一写点时序)+README:87能力行代表用例补V2三函数并列; 审裁决=修后即可开工; grilling三轮零待裁点

This commit is contained in:
DKChan committed 2026-11-02 04:58:09 +08:00
1 parent 937ba1075f
commit 29295bd75e
2 files changed
+18 -16

No files matched your search

+1 -1
View File
@@ -84,7 +84,7 @@ tests/
|---|---|---|---|
| 伤害公式与掷点乘区 | 战斗结算 | `DamageMath.pool_damage()`/`roll_discount_percent()`/`rolled_component()` | `damage_math_test.gd:_test_pool_damage`;`damage_math_test.gd:_test_roll_discount_percent`;`roll_multiplier_test.gd:_smoke_armor_roll` |
| 伤害单据编排与回放 | 战斗结算 | `DamageController.submit()`/`open_ticket()`/`flush()` | `damage_controller_test.gd:_run_recipe_sequence`;`damage_controller_test.gd:_test_five_recipes_and_replay` |
| 效果发射端口单点 | 战斗结算 | `BattleOrchestrator.begin_battle()`(真实装配链端口接线+合成行闭面守卫经 `DamageController.flush_submit()` 承伤路径) | `effect_port_test.gd:_smoke_effect_port_wiring`;`effect_port_test.gd:_test_biz_fact_face_closed`〔计划用例——锚定 `reports/需求卡片-效果端口与掷格分件批-2026-11-02.md`〕〔效果端口与掷格分件批 V1/V3〕 |
| 效果发射端口单点 | 战斗结算 | `BattleOrchestrator.begin_battle()`(真实装配链端口接线+合成行闭面守卫经 `DamageController.flush_submit()` 承伤路径) | `effect_port_test.gd:_smoke_effect_port_wiring`;`effect_port_test.gd:_test_business_classes_engine_free`;`effect_port_test.gd:_test_biz_fact_face_closed`〔计划用例——锚定 `reports/需求卡片-效果端口与掷格分件批-2026-11-02.md`〕〔效果端口与掷格分件批 V1/V2/V3〕 |
| 轮内流与结算五段全序 | 战斗结算 | `BattleOrchestrator.begin_battle()` | `round_flow_skill_pool_test.gd:_smoke_full_battle`;`round_flow_skill_pool_test.gd:_smoke_settlement_segments` |
| 技能池数据面与回放独立 | 敌人系统 | `BattleOrchestrator.begin_battle()`(技能池抽号/校验) | `round_flow_skill_pool_test.gd:_smoke_skill_pool_data`;`round_flow_skill_pool_test.gd:_smoke_skill_pool_replay_independence` |
| Boss 半血解锁技门控 | 敌人系统 | `BattleOrchestrator.begin_battle()`(Boss 行) | `skill_unlock_test.gd:_test_gate_predicate`;`skill_unlock_test.gd:_test_latch_irreversible` |
@@ -1,4 +1,6 @@
# 需求卡片 · 效果端口与掷格分件批(v1.0)
# 需求卡片 · 效果端口与掷格分件批(v1.1)
> **版本**:v1.1(2026-11-02)——godot-master 终审阻断三处+建议五处全折入(裁决=修后即可开工);grilling 三轮在案、零待裁点。
> **用途**:迭代实现批的实施合同。战斗域结构深拆第二波(轴戊):效果发射端口件+掷格件两新件建件,业务类零引擎直连、受理账面留守。**行为零变化结构重构**——游戏可观测行为逐位不变。
> **来源**:待办清单「战斗域结构深拆批——效果发射端口+掷格/受理分件(2026-10-31 重立,来源=批次移交)」条目(状态=文档设计已闭合→已写卡)。
@@ -43,7 +45,7 @@
| F9 | 事实词汇封闭(引擎 FACTS 19 项)与取值序(payload 同名字段优先→facts 回调);宿主事实链现状=charge_layers 自答+facts_cb 转宿主五事实(nest_count/round_index/battle_index/seals_count/hp_ratio);伤害侧发射事实闭面=damage `_fx_fact` 三答 | effect_engine.gd:58-62、:365-377;battle_fx_host.gd:168-171;battle_orchestrator.gd:114-133;damage_controller.gd:89-98 |
| F10 | 🔴 posture_bulwark 树规则现行形态:on=damage_taken、when battle_index==1 且 round_index==1;伤害侧发射的事实回调不答 battle_index/round_index → when 恒不通过=**现行行为是「每关首轮承伤不加盾不触发」**;事实面若扩成宿主全链(答 battle_index)该行即翻活=行为变化 | effect_engine.gd:150-155(_push_tree_rule posture_bulwark);damage_controller.gd:89-98 |
| F11 | 经济域三事件(dig_paid/dig_stop/charge_full)订阅行 when 词汇实测:feats/treasure 两表全部订阅行 when ∈ {counter}∪∅,ap_left/ap_cost 均载荷携带;skills.tres/status_effects.tres 零 triggers;树规则合成行六键订阅面=round_start/damage_taken/mine_hit(无经济域三事件)——订阅行全域与 facts 回调无交集,统一闭面对经济域发射无观测差 | feats.tres/treasure_items.tres/skills.tres/status_effects.tres+effect_engine.gd:147-161(2026-11-02 写卡段全量实测) |
| F12 | 测试与工具消费面:damage_controller_test 41 检查(夹具直构四参、不接 fx——零扰动路径);round_flow_skill_pool_test 100 检查(EV 锚+全战斗事件流逐位回放 ×2 关);roll_multiplier_test 42;variant_placement_test 43 检查(economy 直构+board_skew 写+luck_bias_query 断言+白盒 `_pool_weight`/`_biased_cell_rows` 四处);board_gen_reward_test(economy 直构+受理面全 API);seal_polarity_test/refresh_feats_test(受理面);minesweeper_rules_test(A3 直构单参);system_contracts_test(damage 直构四参);sim/reward_expectation(economy 直构+roll_board/is_mapped/query_identity) | damage_controller_test.gd:51-63;round_flow_skill_pool_test.gd(:60、:690-726 逐位);roll_multiplier_test.gd:245;variant_placement_test.gd:223、:227-242、:259、:264-265;board_gen_reward_test.gd:86-121;seal_polarity_test.gd:362、:729-748;refresh_feats_test.gd:113、:473;minesweeper_rules_test.gd:35;system_contracts_test.gd:99;reward_expectation.gd:18-58 |
| F12 | 测试与工具消费面:damage_controller_test 41 检查(夹具直构四参、不接 fx——零扰动路径);round_flow_skill_pool_test 100 检查(EV 锚+全战斗事件流逐位回放 ×2 关);roll_multiplier_test 42;variant_placement_test 43 检查(economy 直构+board_skew 写+luck_bias_query 断言+economy 内部符号族白盒指址——_rollable_rows/_pool_weight/_biased_cell_rows/board_skew/luck_bias_query 约 17 行 21 处);board_gen_reward_test(economy 直构+受理面全 API);seal_polarity_test/refresh_feats_test(受理面);minesweeper_rules_test(A3 直构单参);system_contracts_test(damage 直构四参);sim/reward_expectation(economy 直构+roll_board/is_mapped/query_identity) | damage_controller_test.gd:51-63;round_flow_skill_pool_test.gd(:60、:690-726 逐位);roll_multiplier_test.gd:245;variant_placement_test.gd:223-265(符号族全量、T0 实数登记);board_gen_reward_test.gd:86-121;seal_polarity_test.gd:362、:729-748;refresh_feats_test.gd:113、:473;minesweeper_rules_test.gd:35;system_contracts_test.gd:99;reward_expectation.gd:18-58 |
| F13 | gdstyle 基线无本批触面键:`tools/gdstyle_baseline.txt` 仅 5 键(adventure_runner/level_runner/board_view)——三宿主与新件零基线登记(无「迁移即拆函数」义务源);S13 判定=基线外新违规红 | tools/gdstyle_baseline.txt:9-13 |
| F14 | 静态扫描两清单现状 12 文件(S8 await 集=S7 B1 域文件集同款清单独立两处);B4 符号清单 9 符号含 weighted_pick | game/tests/static_scan.sh:317、:361、S7 符号交替式段 |
@@ -54,7 +56,7 @@
| damage_controller_test(41 检查) | 夹具不接 fx(_fx=null 路径)→ 本卡签名不变(bind_port 单独注入)→ **零改动照跑** |
| round_flow_skill_pool_test(100 检查,EV 锚+逐位回放) | 全链真实流 → **零改动照跑=行为零变化主证** |
| roll_multiplier_test(42 检查) | 消费 damage 公开面(clamped_pool/defender_ac)→ 零改动 |
| variant_placement_test(43 检查) | **四处白盒指址换代**(:227-242 board_skew→roller、:259 luck_bias_query→roller、:264-265 `_pool_weight`/`_biased_cell_rows`→roller)——判据零删改零放宽,申报为指址随件改写(非削弱,见 §9 申报表) |
| variant_placement_test(43 检查) | **economy 内部符号族全量白盒指址换代**(_rollable_rows/_pool_weight/_biased_cell_rows/board_skew/luck_bias_query → `eco.roller.*`,实测约 17 行 21 处、T0 实数登记)——判据零删改零放宽,申报为指址随件改写(非削弱,见 §9 申报表) |
| board_gen_reward_test/seal_polarity_test/refresh_feats_test/reward_expectation.gd | 全受理面(economy 保留 API、直构签名不变)→ 零改动照跑 |
| run_time_fatal_test | BoardInstaller 装配传 economy 引用(:75)+economy `_fatal` 直调(:83)——两触面均零变化 → 零改动照跑 |
| minesweeper_rules_test/system_contracts_test | A3/damage 直构签名不变 → 零改动 |
@@ -63,7 +65,7 @@
## 4. 设计契约(违反即返工;标 🔴 触碰=L3 停机呈报)
1. 🔴 **行为零变化=本批最高契约**:RNG 消费序零漂移(roll_board 流 scope=每次 install 批不变、流开时点与取号序取号次数零变化;armor_roll/fx_reveal 流不动);命中序=声明序;明细行协议词表零触碰;`_ineffective_row` 载荷 deferred 自描述键观测面不回退;EV 锚同种子逐位一致。任何签名改动的观测面等价论证随对照表行在案。
2. 🔴 **业务发射事实面闭集**:经端口发射的事实回调=闭集 {charge_layers, dig_streak, hp_ratio}(charge_layers=action_economy 现值活读、dig_streak=宿主侧轮内槽、hp_ratio=主角血量比例算式逐字),其余事实一律 null。battle_index/round_index/seals_count/nest_count **禁进**业务发射事实面——posture_bulwark 每关首轮窗在 damage_taken 的现行形态=事实未答不触发(F10),闭面守此现状;扩面=行为变化=L3。
2. 🔴 **业务发射事实面闭集**:经端口发射的事实回调=闭集**五键**——三 when 事实 {charge_layers, dig_streak, hp_ratio}(charge_layers=action_economy 现值活读、dig_streak=宿主侧轮内槽、hp_ratio=主角血量比例算式逐字)∪ 两 rider 运行态读数 {rider_tier_bonus, rider_tier_crit}(宿主 facts_inject 现值——redeem 两读经 `_fact_or` 路由的载体,行为与现行 inject 缺省逐位同),其余事实一律 null。battle_index/round_index/seals_count/nest_count **禁进**业务发射事实面——posture_bulwark 每关首轮窗在 damage_taken 的现行形态=事实未答不触发(F10),闭面守此现状(V3 阴性对照钉死);扩面=行为变化=L3。
3. 🔴 **业务类零引擎直连零账面触碰**:damage_controller/action_economy 两文件禁出现 EffectEngine/EffectContext 引用与计数器静态调用;账面唯一宿主侧(counters_ctx 归 BattleFxHost),业务类读写经端口路由。
4. 🔴 **跨件单向接线**:端口经注入 Callable 路由宿主,**禁持 BattleFxHost 对象引用**(宿主 bundle 已持两业务类——对象引用即成 host→damage→port→host 强引用环,两件均随每战新建=泄漏);掷格件零回指 economy(fatal 经构造注入 Callable 链);注入 Callable 同单向纪律,禁为省参数改持对象引用。
5. **端口单实现零接口抽象**:无基类无多态(GDScript 无 interface,本案不需要——轴戊条款)。
@@ -108,8 +110,8 @@
| game/core/settlement/combat_segment.gd | 修改 | dig_streak 写/读改 `oc.fx_host().facts_inject`;charge_layers 注入行退役(事实面活读) | 非行为改动(行 2/8) |
| game/tests/unit/battle_settlement/effect_port_test.gd | **新增** | 端口验收断言三函数(对照表行 1/2/3 载体) | 验收载体(行 1/2/3) |
| game/tests/run_all.gd | 修改 | manifest 登记 effect_port_test(expect 0)——套件可达性 | 非行为改动(行 9——闸 manifest 字面值随动) |
| game/tests/unit/battle_settlement/variant_placement_test.gd | 修改 | 四处白盒指址换代(判据零删改零放宽) | 非行为改动(行 5/7,§9 申报) |
| game/tests/static_scan.sh | 修改 | S7_B1_SET 增 effect_port.gd;S8_FILES 增 effect_port.gd+reward_roller.gd;B4 符号清单 weighted_pick 行随符号退役删除 | 非行为改动(行 9——闸清单字面值随动,判断依据=闸清单与架构文档同批对齐、判据口径不变) |
| game/tests/unit/battle_settlement/variant_placement_test.gd | 修改 | economy 内部符号族全量白盒指址换代(约 17 行 21 处,判据零删改零放宽) | 非行为改动(行 5/7,§9 申报) |
| game/tests/static_scan.sh | 修改 | S7_B1_SET 增 effect_port.gd+reward_roller.gd(T1/T2 各增一件——轴戊「S7/S8 随动」两新件全登);S8_FILES 增 effect_port.gd+reward_roller.gd;B4 符号清单 weighted_pick 行随符号退役删除 | 非行为改动(行 9——闸清单字面值随动,判断依据=闸清单与架构文档同批对齐、判据口径不变) |
| game/tests/README.md | 修改 | 能力行增行「效果发射端口单点」(**已随本卡提交**——写卡段义务) | 非行为改动(义务清单维护) |
(结构图与 §12 落位预检互指:两新增件均标「新增」,理由与唯一落点见 §12。)
@@ -152,16 +154,16 @@ func reveal_effect() -> void # 未绑→n
func apply_ac(h: Dictionary) -> void # 未绑→no-op
```
- [ ] `battle_fx_host.gd`:增 `var facts_inject := {"dig_streak": 0, "rider_tier_bonus": 0, "rider_tier_crit": false}`(类变量 14→15,贴顶);增四法——`fx_emit_biz(event, payload) -> Array`(`engine.dispatch(counters_ctx, event, payload, _biz_fact)`)、`counter_read(counter_id) -> int`(`EffectEngine.counter(counters_ctx, counter_id)`)、`counter_add(counter_id, value, set_form) -> void`(`EffectEngine.counter_add(counters_ctx, …)`)、`biz_fact(name) -> Variant` 闭面(charge_layers→`action_economy.charge_layers`;dig_streak→`int(facts_inject["dig_streak"])`;hp_ratio→逐字承 damage_controller.gd:96-97 算式读 `damage.units.hero_unit()`;余→null);`_init` 尾段=建 `EffectPort.new({六 Callable 绑 self 对应方法})` 后 `bundle.damage.bind_port(port)`+`bundle.action_economy.bind_port(port)`(端口不落 host 成员——单向接线,host 零回持);头注换代(封闭面 14→15、端口组建与转注、facts_inject 键集封闭声明)
- [ ] `battle_fx_host.gd`:增 `var facts_inject := {"dig_streak": 0, "rider_tier_bonus": 0, "rider_tier_crit": false}`(类变量 14→15,贴顶);增四法——`fx_emit_biz(event, payload) -> Array`(`engine.dispatch(counters_ctx, event, payload, _biz_fact)`)、`counter_read(counter_id) -> int`(`EffectEngine.counter(counters_ctx, counter_id)`)、`counter_add(counter_id, value, set_form) -> void`(`EffectEngine.counter_add(counters_ctx, …)`)、`biz_fact(name) -> Variant` 闭面五键(charge_layers→`action_economy.charge_layers`;dig_streak→`int(facts_inject["dig_streak"])`;hp_ratio→逐字承 damage_controller.gd:96-97 算式读 `damage.units.hero_unit()`;rider_tier_bonus→`int(facts_inject["rider_tier_bonus"])`;rider_tier_crit→`bool(facts_inject["rider_tier_crit"])`;余→null);`_init` 尾段=建 `EffectPort.new({六 Callable 绑 self 对应方法})` 后 `bundle.damage.bind_port(port)`+`bundle.action_economy.bind_port(port)`(端口不落 host 成员——单向接线,host 零回持);头注换代(封闭面 14→15、端口组建与转注、facts_inject 键集封闭声明)
- [ ] `damage_controller.gd`:删 `_fx`/`_ctx`/`set_fx`/`inject`/`fx_calls`/`_fx_fact`;增 `var _port: EffectPort`(**可空成员,缺省 null=未绑**——与原 `_fx == null` 零扰动同语义;不用成员初始化器建端口——`_init(bundle)` 必传参,无参可建不成立)与 `func bind_port(port: EffectPort) -> void`(组装期单点注入,BattleFxHost._init 调用);守卫写法:`_fx_hits` 漏斗体 `if _port == null: return []` 后 `return _port.emit(event, payload)`;未绑 fact 直读统一经私有 `_fact_or(name: String) -> Variant`(`return null if _port == null else _port.fact(name)`——调用点 `int(…)`/`bool(…)` 包裹,`int(null)=0`/`bool(null)=false` 与现行 inject 缺省值逐位同);计数器三处(:364/:370/:533)改 `_port.counter_read(…)`/`_port.counter_add(…)`(均在 hits 非空循环内——`_port` 已由漏斗守卫保证非空);open_ticket dig_streak 载荷键与 `_per_value` 回落、redeem rider 两读(:400/:408)改 `_fact_or` 路由;kill 揭示回调与 damage_taken ac 回调改 `_port.reveal_effect()`/`_port.apply_ac(h)`(均在 hits 非空分支内);头注换代(**头注含注释全域零 `EffectEngine`/`EffectContext` 英文类名 token**——V2 源码锚扫描面为全文)
- [ ] `action_economy.gd`:删 `_fx`/`_ctx`/`set_fx`;增 `var _port: EffectPort`(可空,同上语义)与 `bind_port`;三发射点(:120/:178/:205)外层守卫原位换写 `if _port != null:`(原 `if _fx != null`),体内改 `_port.emit(…)`;计数器三处(:125/:128/:183)改 `_port.counter_add(…)`(守卫外层已保证);头注换代(同零 token 口径)
- [ ] `battle_orchestrator.gd`::229-230 set_fx 两行与 :343-344 fx_calls 两行删除(host._init 组装期接线取代,先于任何发射——时序等价论证见 §12);:348-349 rider 复位改 `_fx_host.facts_inject["rider_tier_bonus"] = 0`/`["rider_tier_crit"] = false`;头注发射点指址换代(词表不动,指址须真)
- [ ] `combat_segment.gd`::34 改 `oc.fx_host().facts_inject["dig_streak"] = 0 if round_mine else round_safe`;:37 charge_layers 注入行删除(事实面活读等价——写值同一时刻读自同源);:64 改读 `int(oc.fx_host().facts_inject["dig_streak"])`
- [ ] `combat_segment.gd`::34 改 `oc.fx_host().facts_inject["dig_streak"] = 0 if round_mine else round_safe`;:37 charge_layers 注入行删除(活读等价论证=唯一写点时序:finish_round_stop 于上轮授予段尾写→:37 拷贝点→本轮授予段 :197 发射前无第二写点;ActionEconomy 随战新建〔编排器 :214〕首战活读恒 0=旧缺省——同种子逐位回放为等价证明载体);:64 改读 `int(oc.fx_host().facts_inject["dig_streak"])`
- [ ] `static_scan.sh`:S7_B1_SET 与 S8_FILES 两清单各增 `game/core/effect_port.gd`
- [ ] 新建 `game/tests/unit/battle_settlement/effect_port_test.gd`(extends SceneTree+Kit 收口样板,三函数):
- `_smoke_effect_port_wiring`〔效果端口与掷格分件批 V1〕:真实 `BattleOrchestrator.new(db, rng)` 装配链——①断言 `damage._port` 与 `action_economy._port` 同一实例(内部字段直查=受控例外,对照表行 1 注明理由:端口同一性无公开查询面);②attack_commit 经真实战斗流(begin_battle+自动玩家最小回合)取回命中批可观测(订阅效果命中在事件流/明细行可见);
- `_smoke_effect_port_wiring`〔效果端口与掷格分件批 V1〕:真实 `BattleOrchestrator.new(db, rng)` 装配链——①断言 `damage._port` 与 `action_economy._port` 同一实例(内部字段直查=受控例外,对照表行 1 注明理由:端口同一性无公开查询面);②attack_commit 经真实战斗流(begin_battle+自动玩家最小回合)取回命中批可观测——期望输出=命中批非空且订阅 attack_commit 的专长行命中效果在事件流载荷可见(订阅锚=整备军备 attack_bonus per charge_layers 族,F3 载荷键在案;断言读数面=事件流该行 delta 读数非零);
- `_test_business_classes_engine_free`〔效果端口与掷格分件批 V2〕:源码契约锚(theme_projection_test 源码锚惯例)——读两业务文件全文,断言零 `EffectEngine`/`EffectContext` token(扫描面>0 前置:两文件读出非空字数);
- `_test_biz_fact_face_closed`〔效果端口与掷格分件批 V3〕:begin_battle 后直改 `db.crossrun.feats.rows` 追加两条合成行(调试注入两层·数据面粒度:行键 feat_id+triggers,on=damage_taken;rebuild 走持有 id 过滤真实表行——合成行经直改表行进入装载域)→ `rebuild_subscriptions([两条合成 feat_id], [], [])`(订阅面换代为合成行独占,确定性)→ 真实 `flush_submit` board_flat_hero 单据(base_value 足量避开伤害下限保底,shield 减量可观测):合成行 (a) when hp_ratio lte 100(载荷携带恒真——阳性对照,证 harness 能见命中)→ 断言承伤减量恰 1;合成行 (b) when battle_index eq 1 → 同路径断言零减量(闭面——F10 现状形态守卫)
- `_test_biz_fact_face_closed`〔效果端口与掷格分件批 V3〕:begin_battle 后直改 `db.crossrun.feats.rows` 追加两条合成行(调试注入两层·数据面粒度:行键 feat_id+triggers,on=damage_taken;effect 段 kind/value 逐字抄现役铁壁 shield per 表行——行构造零推表;rebuild 走持有 id 过滤真实表行——合成行经直改表行进入装载域)→ `rebuild_subscriptions([两条合成 feat_id], [], [])`(订阅面换代为合成行独占,确定性)→ 真实 `flush_submit` board_flat_hero 单据(base_value 足量避开伤害下限保底,shield 减量可观测):合成行 (a) when hp_ratio lte 100(载荷携带恒真——阳性对照,证 harness 能见命中)→ 断言承伤减量恰 1;合成行 (b) when battle_index eq 1 → 同路径断言零减量(闭面——F10 现状形态守卫)
- [ ] `run_all.gd` manifest 登记 effect_port_test(expect 0)
- [ ] 孤立验证:先跑 `--editor --quit` 刷新全局类名缓存(新 class_name 两枚——`--headless --script` 模式解析在册坑),再 effect_port_test 单套直跑绿(新断言先行,禁以全套闸首跑充当首验)
@@ -197,18 +199,18 @@ func set_treasure_unlocked(ids: Dictionary) -> void
# 通道),返回值由 reward_id 换为表行,调用侧取 row.reward_id;取号算术与次数零变化
```
- [ ] `reward_economy.gd` 收纳:增 `var roller: RewardRoller`(_init 内自建自接线——`roller = RewardRoller.new({"db": db_ref, "rng": rng_ref, "fatal_cb": fatal_handler})`,BoardInstaller.generator 先例);`fatal_handler` 改属性 setter(赋值同拍转注——`set(v): fatal_handler = v; if roller != null: roller.fatal_cb = v`,构造序防:economy._init 不触该成员、编排器 :220 注入晚于 _init);`roll_board(eligible)` 保留签名改装配序=相位守卫(E5,先行——违约路径零取号不变)→ 空清单 fatal(E1)→ 受理面复位四拍(逐字)→ `var batch := roller.roll(eligible)` → 收纳 `_mapping = batch.mapping`/`_power_roll = batch.power_roll`/`_clear_identity = batch.clear_identity`/`_clear_power = batch.clear_power` → `_phase = "ready"`;退役清单=rng/_eligible/luck_bias_query/board_skew/_treasure_unlocked 五成员+set_treasure_unlocked/weighted_pick/drops_total/_pool_weight/_pool_rows/_rollable_rows/_normalize_eligible/_biased_cell_rows/_skewed_cell_rows/_skew_families/_apply_skew/_largest_remainder_split 十二函数+三常量(类变量 15→11、公开方法 12→9);保留面=_phase/_mapping/_power_roll/_clear_power/_granted/_clear_identity/_clear_granted/_clear_snapshot/is_mapped/query_identity/query_full_snapshot/grant/grant_clear/retire_mapping/polarity_row/phase/_row_by_id/_snapshot/_diag/_fatal;头注换代(两职责分家声明+衔接面批束键集)
- [ ] `reward_economy.gd` 收纳:增 `var roller: RewardRoller`(_init 内自建自接线——`roller = RewardRoller.new({"db": db_ref, "rng": rng_ref, "fatal_cb": fatal_handler})`,BoardInstaller.generator 先例);`fatal_handler` 改属性 setter(赋值同拍转注——`set(v): fatal_handler = v; if roller != null: roller.fatal_cb = v`,构造序防:economy._init 不触该成员、编排器 :220 注入晚于 _init);`roll_board(eligible)` 保留签名改装配序=相位守卫(E5,先行——违约路径零取号不变)→ 空清单 fatal(E1)→ 受理面复位五拍(逐字)→ `var batch := roller.roll(eligible)` → 收纳 `_mapping = batch.mapping`/`_power_roll = batch.power_roll`/`_clear_identity = batch.clear_identity`/`_clear_power = batch.clear_power` → `_phase = "ready"`;退役清单=rng/_eligible/luck_bias_query/board_skew/_treasure_unlocked 五成员+set_treasure_unlocked/weighted_pick/drops_total/_pool_weight/_pool_rows/_rollable_rows/_normalize_eligible/_biased_cell_rows/_skewed_cell_rows/_skew_families/_apply_skew/_largest_remainder_split 十二函数+三常量(类变量 15→11、公开方法 12→9);保留面=_phase/_mapping/_power_roll/_clear_power/_granted/_clear_identity/_clear_granted/_clear_snapshot/is_mapped/query_identity/query_full_snapshot/grant/grant_clear/retire_mapping/polarity_row/phase/_row_by_id/_snapshot/_diag/_fatal;头注换代(两职责分家声明+衔接面批束键集)
- [ ] `battle_orchestrator.gd`::231 改 `_reward_economy.roller.luck_bias_query = _fx_host.luck_bias_for`;:501-502 `set_treasure_unlocked` 内部改 `_reward_economy.roller.set_treasure_unlocked(ids)`(编排器公开面不动——level_runner :522 零改址)
- [ ] `board_installer.gd`::80/:86 两行改 `reward_economy.roller.board_skew = …`(写时序在 roll_board 前——不变)
- [ ] `variant_placement_test.gd` 四处指址换代::227-242 board_skew 写改 `eco.roller.board_skew`;:259 luck_bias_query 断言改 `eco.roller.luck_bias_query`;:264-265 `_pool_weight`/`_biased_cell_rows` 改 `eco.roller.…`——判据零删改零放宽
- [ ] `static_scan.sh`:S8_FILES 增 `game/core/reward_roller.gd`;S7 B4 符号清单 weighted_pick 行随符号退役删除(注释同步——符号全库零调用后清单与架构文档对齐)
- [ ] `variant_placement_test.gd` economy 内部符号族全量指址换代:_rollable_rows/_pool_weight/_biased_cell_rows/board_skew/luck_bias_query 五符号全仓消费行改 `eco.roller.…`(:224/:225/:227-242/:228/:231/:232/:238/:240/:243/:244/:259/:260/:262/:264-265,约 17 行 21 处、T0 实数登记)——判据零删改零放宽
- [ ] `static_scan.sh`:S7_B1_SET 增 `game/core/reward_roller.gd`(轴戊 S7/S8 随动——两新件全登);S8_FILES 增 `game/core/reward_roller.gd`;S7 B4 符号清单 weighted_pick 行随符号退役删除(注释同步——符号全库零调用后清单与架构文档对齐)
- [ ] 孤立验证:board_gen_reward_test+variant_placement_test 单套直跑绿
### 8.4 T3——受影响面窄测与逐位主证(估时 0.25–0.5 个工作日)
**取用**:T1/T2 产出。 **产出**:触碰面套件全绿记录+S13 改后对读数。
- [ ] 本批触碰面套件跑绿(卡内只测自己改的——选择依据=§3.1 受影响面并集):effect_port_test/board_gen_reward_test/variant_placement_test/round_flow_skill_pool_test(EV 锚+事件流逐位=行为零变化主证)/damage_controller_test/refresh_feats_test(计数器行为与受理链)/seal_polarity_test(受理面+reward_ineffective 观测面)/minesweeper_rules_test(A3 dig 路径端口换线)/settle_segments_test(段件链)/run_time_fatal_test(装板装配与 economy fatal 通道)
- [ ] 本批触碰面套件跑绿(卡内只测自己改的——选择依据=§3.1 受影响面并集):effect_port_test/board_gen_reward_test/variant_placement_test/round_flow_skill_pool_test(EV 锚+事件流逐位=行为零变化主证)/damage_controller_test/refresh_feats_test(计数器行为与受理链)/seal_polarity_test(受理面+reward_ineffective 观测面)/minesweeper_rules_test(A3 dig 路径端口换线)/settle_segments_test(段件链)/run_time_fatal_test(装板装配与 economy fatal 通道)/effect_words_t1_test(词表接线——R4 点名)/adventure_feats_test(fx_host 直发面——R4 点名)/roll_multiplier_test(damage 公开面消费)/system_contracts_test(damage 直构签名)
- [ ] 改后 gdstyle 读数对照:五存量文件 S13 键较 T0 读数只降不升(reward_economy 15→11、damage 15→12、action_economy 11→10、battle_fx_host 14→15、orchestrator 15→15 持平)——超预期即停卡呈报
- [ ] 里程碑步(T3)出口跑一次代码域闸(按本批改动域选档);波内修复只做出红最小单元的最窄确认;新增测试先行孤立验证再并入全套
@@ -238,7 +240,7 @@ func set_treasure_unlocked(ids: Dictionary) -> void
| R8 行为零变化总证 | 全域(本批 diff 全部非行为改动——逐面等价论证见本表各行) | 既有 round_flow_skill_pool_test 照跑:EV 锚+全战斗事件流逐位回放 ×2 关(含 roll_board 取号序/armor_roll 流/命中序/明细行词表/_ineffective_row deferred 键观测面的链路覆盖) | 单元 | `BattleOrchestrator.begin_battle()` 全战斗回放 | 事件流 JSON 逐位等值(既有断言原样执行即判据) | 既有:轮内流与结算五段全序;既有:伤害单据编排与回放 |
| R9 闸清单随动与能力行维护 | static_scan.sh S7_B1_SET/S8_FILES/B4 符号清单三处;README 能力行增行(随卡提交);run_all.gd manifest 登记 | 代码域闸全绿(清单缺位自检通过=S7/S8 文件集存在性判据)+批末收尾核对能力行名与落地名一致 | 闸 | 静态扫描域闸(随代码域闸跑批执行) | 清单缺位判红面+manifest 套件可达性 | 新增:效果发射端口单点 |
**测试削弱申报**:本卡允许的旧断言改动=variant_placement_test 四处**指址换代**(:227-242/:259/:264-265,economy→economy.roller——拆件随件改写,判据表达式与阈值零删改零放宽);此外零申报——实现期任何未申报的删断言/放宽阈值/缩小扫描面/换下层直调由独立验收审计判红。
**测试削弱申报**:本卡允许的旧断言改动=variant_placement_test **economy 内部符号族全量指址换代**(_rollable_rows/_pool_weight/_biased_cell_rows/board_skew/luck_bias_query → `eco.roller.*`,实测约 17 行 21 处、T0 实数登记——拆件随件改写,判据表达式与阈值零删改零放宽);此外零申报——实现期任何未申报的删断言/放宽阈值/缩小扫描面/换下层直调由独立验收审计判红。
## 10. 异常分级