test(卡② T7补漏): 补齐 §9 正向验收主力 V1/V2/V6 与全 Vn 标注 + V1/V4 变异验证

前批合回时漏建卡② §9 逐需求点「正向验收主力」断言(四桶机制),仅靠迁移旧测+V12 兜底——
V12 formed/rules 输出面不作判据,adventure 桶跨战持久若坏不会红,核心缺陷消解无回归网。本批补:
- V1 冒烟新段:同一 RunLedger 两 begin_battle 各真实 request_dig,断 adv_twin_digs 经 ctx 就地写宿主字典跨战累积=2
- V2:start_adventure 后 adventure 桶置空(feat_vouchers)
- V6 冒烟新段:次战新编排器 fx_counters_battle 天然空、battle 桶 insurance_used 读 0
- V3/V4/V5/V7/V8/V9/V10 补〔无状态化 Vn〕尾缀使 §9 可追溯
- 变异验证:V1 断 adventure 桶引用改新建空字典→3 条红;V4 断 level 桶不共享→红(证回归网真拦得住)
This commit is contained in:
DKChan committed 2026-10-21 02:45:57 +08:00
1 parent bdb09c5a30
commit 33e1f11057
5 files changed
+62 -7

No files matched your search

+54 -2
View File
@@ -697,6 +697,7 @@ func _run_core_smoke(db, rng) -> void:
_smoke_meta()
_smoke_tree()
_smoke_tree_rules()
_smoke_counter_buckets()
_smoke_node_map_templates()
@@ -6049,6 +6050,57 @@ func _smoke_tree() -> void:
# ---- 天赋树六键行为冒烟(V10:开局姿态窗/危险直觉限次/财富之道聚散/守侧失误补偿;
# 引擎直构 + 编排授予段直注(_grant_seg._judge_seal=结算授予段封印判定单口))----
func _smoke_counter_buckets() -> void:
print("--- 效果引擎无状态化四桶正向验收(卡② §9 V1/V6)---")
# V1——adventure 桶跨战持久:同一 RunLedger 下两战各真实 request_dig 一次,
# 断付费计数经 ctx 就地写入宿主字典并跨战累积(副本搬运态:读不到上一战值→第 2 战仍 1,此断言会红)。
var led = load("res://core/run_ledger.gd").new()
var RigS = load("res://core/battle_orchestrator.gd")
var RngS = load("res://core/rng_service.gd")
var enc1 = {"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {},
"feats": ["twin_pick"], "tree_rules": [], "battle_index": 1,
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "voucher_rap": 0, "voucher_time_sand": 0,
"node_type": "battle", "ledger": led}
var o1 = RigS.new(_db, _fresh_rng(779301))
o1.begin_battle(enc1)
var dig1: bool = o1.action_economy.request_dig(0, false)
check(dig1, "V1 第1战真实 request_dig 受理(begin_battle 后 ACTIVE 相位)〔无状态化 V1〕")
check(int(led.adventure_counters.get("adv_twin_digs", 0)) == 1,
"V1 第1战付费挖格经 ctx 就地写 adventure 桶=1(无 export 搬运时点)〔无状态化 V1〕")
var enc2 = enc1.duplicate(true)
enc2["battle_index"] = 2
var o2 = RigS.new(_db, _fresh_rng(779302))
o2.begin_battle(enc2)
check(EffectEngine.counter(o2.counters_ctx, "adv_twin_digs") == 1,
"V1 第2战 begin_battle 经 ctx 读到上一战持久值=1(读路径走宿主字典引用,非副本种子回灌)〔无状态化 V1〕")
o2.action_economy.request_dig(0, false)
check(int(led.adventure_counters.get("adv_twin_digs", 0)) == 2,
"V1 第2战挖后 adventure 桶跨战累积=2(run_ledger:42 判空早退缺陷消解的回归钉面——旧副本态恒 1)〔无状态化 V1〕")
# V6——battle 桶随宿主销毁:第1战标错落 insurance_used(battle 桶无前缀),次战新编排器读 0。
var encA = {"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "attune_charges": {},
"feats": ["perfect_insurance"], "tree_rules": [], "battle_index": 1,
"bonus_ref": {"atk": 0, "max_hp": 0, "armor_class": 0}, "voucher_rap": 0, "voucher_time_sand": 0,
"node_type": "battle", "ledger": load("res://core/run_ledger.gd").new()}
var oA = RigS.new(_db, _fresh_rng(779401))
oA.begin_battle(encA)
var safe_a: Variant = null
for ca in oA.minesweeper.current_board().all_cells():
if not oA.minesweeper.current_board().is_mine(ca):
safe_a = ca
break
oA._grant_seg._judge_seal({"cell": safe_a, "hit": false}, 1)
check(EffectEngine.counter(oA.counters_ctx, "insurance_used") == 1,
"V6 第1战标错落 battle 桶 insurance_used=1(_apply_counter_hit 静态写口经 ctx)〔无状态化 V6〕")
var encB = encA.duplicate(true)
encB["battle_index"] = 2
var oB = RigS.new(_db, _fresh_rng(779402))
oB.begin_battle(encB)
check(EffectEngine.counter(oB.counters_ctx, "insurance_used") == 0,
"V6 battle 桶随宿主销毁:次战新编排器 fx_counters_battle 天然空,读 insurance_used=0〔无状态化 V6〕")
func _smoke_tree_rules() -> void:
print("--- 天赋树六键行为(V10:窗/限次/聚散/守侧)---")
# 开局姿态组:每关首轮窗(battle_index 事实——引擎直构 + 自建 ctx)
@@ -6078,13 +6130,13 @@ func _smoke_tree_rules() -> void:
got_add = true
check(got_mult and got_add, "锋:踩雷减半命中+限次记账(counter 0<2 闸开——damage_mult+counter_add 双行同预评估态)")
EffectEngine.counter_add(ctx_fz, "lvl_tree_sense_sharp_used", 2) # 直调置数(V10 注入口径——置 level 桶计数后走事件路径)
check((fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array).is_empty(), "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)")
check((fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array).is_empty(), "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)〔无状态化 V5〕")
var fz2 = load("res://core/effect_engine.gd").new(null)
fz2.rebuild([], [], ["sense_sharp"])
var ctx_fz2 := EffectContext.new()
ctx_fz2.level_bucket = lvl_shared # 挂同一 level 桶引用(新形态跨战持久:无 export/seed,同字典即持久)
check((fz2.dispatch(ctx_fz2, "mine_hit", {}, Callable()) as Array).is_empty(),
"锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)")
"锋:限次跨战持久(level 桶共享引用——替代旧副本搬运链)〔无状态化 V4〕")
# 守侧五场景(契约 12):编排 rig——begin_battle 携 sense_guard,授予段 _grant_seg._judge_seal 直注
var orch = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777001))
check(orch.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
+1 -1
View File
@@ -228,7 +228,7 @@ else
echo "$S12_FACE" | tr '\n' '\0' | xargs -0 grep -En "$S12_SYM" 2>/dev/null
fail=1
else
echo "[static_scan] S12 ok(扫描面 $S12_FC 个 .gd/.tres,旧件零残留)"
echo "[static_scan] S12 ok(扫描面 $S12_FC 个 .gd/.tres,旧件零残留)〔无状态化 V10〕"
fi
fi
fi
+1 -1
View File
@@ -113,7 +113,7 @@ func _init() -> void:
"V4:counter_spend 静态写口消耗(用后清零语义承载面)〔专长批 V4〕")
fx5.dispatch(ctx5, "round_start", {"round": 2})
Kit.check_quiet(EffectEngine.counter(ctx5, "round_mines") == 0,
"V4:round_ 前缀计数器随 round_start 整桶清空(旧 reset_at 名单声明制换代为前缀归桶)〔专长批 V4〕")
"V4:round_ 前缀计数器随 round_start 整桶清空(旧 reset_at 名单声明制换代为前缀归桶)〔无状态化 V8〕")
# battle 桶对照:无前缀 helm_layers 不受 round_start 影响(跨 round_start 存活)
var fx6 = EffectEngineScript.new(null)
var ctx6 := EffectContext.new()
+5 -2
View File
@@ -26,6 +26,9 @@ func _init() -> void:
Kit.check_quiet(false, "V9 前置:冒险启动")
quit(Kit.finish("feat_vouchers"))
return
# V2——adventure 桶新冒险置空:start_adventure 后宿主字典为空(寿命随 RunLedger,无残留)
Kit.check_quiet(a.run_ledger.adventure_counters.is_empty(),
"V2:新冒险 adventure 桶置空(start_adventure 后 run_ledger.adventure_counters 为空)〔无状态化 V2〕")
# V9-1 刷新券:入存量账(冒险级)——反例=写进 _attune_refresh_x(X 每停点重置会吞存量)
a._reset_refresh_x() # X 归零基线(模拟新停点)
@@ -136,7 +139,7 @@ func _init() -> void:
Kit.check_quiet(b4.resume_adventure(bp_legacy2, {}),
"V13:旧档 adventure_counters 含改名前旧键续玩受理零崩溃(无迁移——旧键留桶成死键)〔卡② V13/H1〕")
Kit.check_quiet(int(b2.run_ledger.adventure_counters.get("pain_layers", -1)) == 3 and b2.refresh_stock() == 2,
"V16:续玩回填逐项一致(计数恢复——跨战账不丢)〔专长批 V16〕")
"V16:续玩回填逐项一致(adventure 桶随断点往返·计数恢复)〔无状态化 V3〕")
# V25 完美保险专长验收组(R2 全项:装载/限次一次/计数纯净/两账相减/跨战清零/事件不吞)
var fi: Dictionary = db.feats.row_by_id("perfect_insurance")
Kit.check_quiet(not fi.is_empty() and String(fi.get("rarity", "")) == "rare"
@@ -178,7 +181,7 @@ func _init() -> void:
orch._perfect_hit_false = 1
orch._perfect_hit_true = 1
Kit.check_quiet(orch.perfect_sweep_earned(),
"判定相减:误标 1 − 豁免 1 = 有效 0 且标中≥1 → 完美成立〔卡② R2 真值表〕")
"判定相减:误标1−豁免1=有效0且标中≥1→完美成立(保险每战限次一次迁移保绿)〔无状态化 V7〕")
orch._perfect_hit_true = 0
Kit.check_quiet(not orch.perfect_sweep_earned(),
"判定守卫:标中数 0 时零误标也不成立(整图 ≥1 标中条件零松动)〔卡② R2 真值表〕")
@@ -67,5 +67,5 @@ func _init() -> void:
if not _assert_rejected(BAD_REF_DIR, PROBE_REF, "counter_prefix_fatal_test/②悬空读点"):
quit(2)
return
print("[counter_prefix_fatal_test] 双断言均正确拒绝(前缀合法性 + 引用闭合)")
print("[counter_prefix_fatal_test] 双断言均正确拒绝(前缀合法性 + 引用闭合)〔无状态化 V9〕")
quit(1)