feat(迭代38卡④ 明细收敛批): 扩展行型收敛——四个专长实名行型(fx_reveal/mine_neutralized/insurance_used/fx_preview_area)收敛为单一 extended 行型+effect_id 载荷(六发射点:orchestrator 三处+grant_segment 三处,display_name/value/highlight 逐字不动);a2 _emit_row 扩展行必载 effect_id 校验(emit 之前,违约 assert 炸零发射);a3 fx_preview_area 来源类别空串违约修复(→fx_warning_beacon,对齐 fx_seal_smith 先例);a4 头注换代两层词表(基础 9 类+扩展行 1 类+四效果身份发射点指引);a5 呈现零改动核验(_format_row fallback 承载、AGG_TYPES 不含扩展行);a6 裁点甲重锚(parity_anchor freeze 改后行为立新锚+复闸三变体双面全同;改前对拍绿基线在案——stash 干净跑批实测);验收=wr2 断言五条(insurance_used〔V6 扩展〕+fx_reveal 兑付/落空〔真实揭示管线〕+mine_neutralized/fx_preview_area〔treasure 套件首次 feats 注入〕)+wr2-np 负面小套件(extended_row_violation_test,expect=1 negative,manifest 登记行)+wr3 并⑤断言;撤销证明三条实测红绿复现;JSONL 读数对拍 PASS(type 迁移计数与 T0 基线对账 264/304/0/0,字段口径外零差异——type/effect_id/source_class/source 四字段,source=logger:133 同值改名副本);改动面封闭(基础 9 类零触碰、豁免集合零变化、数值随机零变化)

This commit is contained in:
DKChan committed 2026-10-26 08:52:14 +08:00
1 parent ce49a8eec2
commit d5a03db1a8
7 files changed
+174 -12

No files matched your search

@@ -0,0 +1,38 @@
# 致命类用例(独立进程,期望退出码 1)——迭代 38 卡④ wr2-np:扩展行必载 effect_id 负路径断言
# 违约直调 _emit_row(type=extended 无 effect_id 载荷)→ a2 行协议校验 assert 炸(debug 实况,
# 输出中的 SCRIPT ERROR 为其测试目标——负面套件全文折叠)→ 零发射;
# 回归态(校验段被移除)= 违约行照发 → 退出码 2 ≠ 期望 1(闸红)。
# 独立进程理由:GDScript assert 失败必产 SCRIPT ERROR 且不可捕获,非负面套件会被 run_all 错误
# 扫描判红(迭代 38 卡④ grilling 第二轮同款结论——负面豁免机制为此存在,counter_prefix 先例)。
# 运行:godot --headless --path game --script res://tests/unit/battle_settlement/extended_row_violation_test.gd
extends SceneTree
const Kit := preload("res://tests/unit/test_kit.gd")
func _init() -> void:
var db = load("res://core/config_db.gd").new()
if not db.load_tables("res://data/tables", false):
push_error("[extended_row_violation_test] 表加载失败")
quit(2)
return
var rng = load("res://core/rng_service.gd").new()
rng.set_master_seed(74100)
var oc = load("res://core/battle_orchestrator.gd").new(db, rng)
var hits: Array = []
oc.step_row.connect(func(row): hits.append(row))
# 违约直调(缺 effect_id 载荷——行协议违约):debug 下 a2 校验 assert 炸中断 _emit_row 本体,
# 控制流回到本函数(headless assert 语义=中断当前函数不中止运行);release 下 return 零发射同效
oc._emit_row("extended", "fx_reveal", Vector2i.ZERO, 1, "负路径", 0, {}, "")
if not hits.is_empty():
push_error("[extended_row_violation_test] 违约行被发射——a2 校验失守(回归)")
quit(2)
return
# 对照组:合法扩展行(带 effect_id)照常发射——校验只拦违约形态
oc._emit_row("extended", "fx_reveal", Vector2i.ZERO, 1, "合法对照", 0, {"effect_id": "fx_reveal"}, "")
if hits.size() != 1 or String(hits[0].get("type", "")) != "extended":
push_error("[extended_row_violation_test] 合法扩展行未发射——校验误拦(回归)")
quit(2)
return
print("[extended_row_violation_test] 违约零发射+合法照发(a2 校验在位)")
quit(1)
@@ -38,6 +38,7 @@ func _init() -> void:
_smoke_settlement_segments()
_smoke_skill_pool()
_smoke_counter_buckets()
_smoke_extended_rows()
quit(Kit.finish("round_flow_skill_pool_test"))
@@ -994,6 +995,8 @@ func _smoke_counter_buckets() -> void:
"node_type": "battle", "ledger": load("res://core/run_ledger.gd").new()}
var oA = RigS.new(_db, _fresh_rng(779401))
oA.begin_battle(encA)
var v6_rows: Array = []
oA.step_row.connect(func(row): v6_rows.append(row)) # 卡④ wr2①——标错行捕获(真实 step_row 发射面)
var safe_a: Variant = null
for ca in oA.minesweeper.current_board().all_cells():
if not oA.minesweeper.current_board().is_mine(ca):
@@ -1001,8 +1004,62 @@ func _smoke_counter_buckets() -> void:
break
oA._grant_seg._judge_seal({"cell": safe_a, "hit": false}, 1)
Kit.check("V6 第1战标错落 battle 桶 insurance_used=1(_apply_counter_hit 静态写口经 ctx)〔无状态化 V6〕", EffectEngine.counter(oA.counters_ctx, "insurance_used") == 1)
# 卡④ wr2①——完美保险标错行=单一扩展行型(原 insurance_used 实名行型收敛)
var v6_ins := {}
for r in v6_rows:
if String(r.get("effect_id", "")) == "insurance_used":
v6_ins = r
Kit.check("卡④ wr2①:完美保险标错行 type=extended 且 effect_id=insurance_used(effect_id 必载)〔明细收敛 V2〕",
not v6_ins.is_empty() and String(v6_ins.get("type", "")) == "extended"
and String(v6_ins.get("source_class", "")) == "seal_miss" and bool(v6_ins.get("forgiven", 0)))
var encB = encA.duplicate(true)
encB["battle_index"] = 2
var oB = RigS.new(_db, _fresh_rng(779402))
oB.begin_battle(encB)
Kit.check("V6 battle 桶随宿主销毁:次战新编排器 fx_counters_battle 天然空,读 insurance_used=0〔无状态化 V6〕", EffectEngine.counter(oB.counters_ctx, "insurance_used") == 0)
# ---- 扩展行收敛(迭代 38 卡④)——fx_reveal 兑付行与落空行经真实揭示管线发射 ----
# 驱动形态:装配揭示类专长(geologist)的编排器上走生产同一入队/兑付管线
# (_fx_enqueue_reveal → _flush_pending_reveals——grant/damage 揭示订阅面的共用出口)。
# V6 形态先例同源:直入真实管线缝(_judge_seal 直调同款口径)。
func _smoke_extended_rows() -> void:
print("--- 扩展行收敛:fx_reveal 兑付/落空 ---")
var RigS = load("res://core/battle_orchestrator.gd") # 局部装配别名(_smoke_counter_buckets 同款——跨函数不共享局部量)
var enc_fx = {"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": ["geologist"], "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()}
# ② 兑付行:有未揭示安全格 → 掷格揭示成功 → 扩展行带真实格与 fx_reveal 身份
var o_fx = RigS.new(_db, _fresh_rng(779501))
o_fx.begin_battle(enc_fx)
var fx_rows: Array = []
o_fx.step_row.connect(func(row): fx_rows.append(row))
o_fx._fx_enqueue_reveal()
o_fx._flush_pending_reveals(1)
var paid := {}
for r in fx_rows:
if String(r.get("effect_id", "")) == "fx_reveal":
paid = r
Kit.check("卡④ wr2②:专长揭示兑付行 type=extended 且 effect_id=fx_reveal 且来源格真实〔明细收敛 V2〕",
not paid.is_empty() and String(paid.get("type", "")) == "extended"
and Vector2i(paid["origin_cell"][0], paid["origin_cell"][1]).x >= 0)
# ③ 落空行:无未揭示安全格态(全安全格先揭)→ 候选空 → 落空归因行(origin_cell 越界哨值)
var o_void = RigS.new(_db, _fresh_rng(779502))
o_void.begin_battle(enc_fx)
var void_rows: Array = []
o_void.step_row.connect(func(row): void_rows.append(row))
for cv in o_void.minesweeper.current_board().all_cells():
if not o_void.minesweeper.current_board().is_mine(cv):
o_void.minesweeper.fx_reveal(cv)
o_void._fx_enqueue_reveal()
o_void._flush_pending_reveals(1)
var missed := {}
for r in void_rows:
if String(r.get("effect_id", "")) == "fx_reveal" and String(r.get("display_name", "")).contains("落空"):
missed = r
Kit.check("卡④ wr2③:揭示落空行 type=extended 且 effect_id=fx_reveal 且 origin_cell=越界哨值〔明细收敛 V2〕",
not missed.is_empty() and String(missed.get("type", "")) == "extended"
and Vector2i(missed["origin_cell"][0], missed["origin_cell"][1]).x == -1)
@@ -300,6 +300,60 @@ func _init() -> void:
and (adr14.runner()._attunement as Array) == snap14,
"装配不可变:战斗运行态装配集与冒险装配集逐位同源(运行零写口——快照对比)")
# ⑮ 扩展行收敛(迭代 38 卡④ wr2④⑤——套件首次 feats 注入,V6 形态自 round_flow_skill_pool_test 移植:
# enc 带 feats 键+ledger,专长触发经 _judge_seal 真实封印判定管线)
var enc_feats = {"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": ["seal_smith"], "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 rng15 = load("res://core/rng_service.gd").new(); rng15.set_master_seed(341)
var ob15 = orch_s.new(db, rng15)
ob15.begin_battle(enc_feats)
var rows15: Array = []
ob15.step_row.connect(func(row): rows15.append(row))
# ④ 封印匠(每第 3 次封印命中→危险格中立移除):三枚不同雷格各封一次
#(_judge_seal 每格去重——_seal_consumed 以危险格实例为键,同格重复命中不计数)
var mines15: Array = []
for cv in ob15.minesweeper.current_board().all_cells():
if ob15.minesweeper.current_board().is_mine(cv):
mines15.append(cv)
if mines15.size() >= 3:
break
Kit.check_quiet(mines15.size() == 3, "封印匠:板上存在三枚危险格(断言前提)")
if mines15.size() == 3:
for mv in mines15:
ob15._grant_seg._judge_seal({"cell": mv, "hit": true}, 1)
var smith_row := {}
for r in rows15:
if String(r.get("effect_id", "")) == "mine_neutralized":
smith_row = r
Kit.check_quiet(not smith_row.is_empty() and String(smith_row.get("type", "")) == "extended"
and String(smith_row.get("source_class", "")) == "fx_seal_smith",
"封印匠第三次命中:危险格中立移除行=扩展行型(type=extended+effect_id=mine_neutralized)〔明细收敛 V2〕")
# ⑤ 示警烟火(封印命中→周围资源类型预告):fx_preview_area 扩展行+来源类别修复断言(wr3)
var enc_beacon = enc_feats.duplicate(true)
enc_beacon["feats"] = ["warning_beacon"]
var rng15b = load("res://core/rng_service.gd").new(); rng15b.set_master_seed(342)
var ob15b = orch_s.new(db, rng15b)
ob15b.begin_battle(enc_beacon)
var rows15b: Array = []
ob15b.step_row.connect(func(row): rows15b.append(row))
var cell15b: Variant = null
for cv in ob15b.minesweeper.current_board().all_cells():
if not ob15b.minesweeper.current_board().is_mine(cv):
cell15b = cv
break
if cell15b != null:
ob15b._grant_seg._judge_seal({"cell": cell15b, "hit": true}, 1)
var beacon_row := {}
for r in rows15b:
if String(r.get("effect_id", "")) == "fx_preview_area":
beacon_row = r
Kit.check_quiet(not beacon_row.is_empty() and String(beacon_row.get("type", "")) == "extended"
and String(beacon_row.get("source_class", "")) == "fx_warning_beacon",
"示警烟火命中行:type=extended+effect_id=fx_preview_area+来源类别=fx_warning_beacon(空串违约修复)〔明细收敛 V2〕")
quit(Kit.finish("treasure_effects_test", "PASS — 新目录 12 件逐件行为断言全绿〔秘宝批 V7 附属表〕"))