From 33e1f1105783589cb86f14d66e25affa0f220155 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Wed, 21 Oct 2026 02:45:57 +0800 Subject: [PATCH 1/2] =?UTF-8?q?test(=E5=8D=A1=E2=91=A1=20T7=E8=A1=A5?= =?UTF-8?q?=E6=BC=8F):=20=E8=A1=A5=E9=BD=90=20=C2=A79=20=E6=AD=A3=E5=90=91?= =?UTF-8?q?=E9=AA=8C=E6=94=B6=E4=B8=BB=E5=8A=9B=20V1/V2/V6=20=E4=B8=8E?= =?UTF-8?q?=E5=85=A8=20Vn=20=E6=A0=87=E6=B3=A8=20+=20V1/V4=20=E5=8F=98?= =?UTF-8?q?=E5=BC=82=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 前批合回时漏建卡② §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 桶不共享→红(证回归网真拦得住) --- game/smoke/smoke_all.gd | 56 ++++++++++++++++++- game/tests/static_scan.sh | 2 +- game/tests/unit/core/effect_words_t1_test.gd | 2 +- game/tests/unit/core/feat_vouchers_test.gd | 7 ++- .../data_config/counter_prefix_fatal_test.gd | 2 +- 5 files changed, 62 insertions(+), 7 deletions(-) diff --git a/game/smoke/smoke_all.gd b/game/smoke/smoke_all.gd index dd8c2b10..7f8cf2e8 100644 --- a/game/smoke/smoke_all.gd +++ b/game/smoke/smoke_all.gd @@ -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, diff --git a/game/tests/static_scan.sh b/game/tests/static_scan.sh index bdabdc7b..42bbdb91 100644 --- a/game/tests/static_scan.sh +++ b/game/tests/static_scan.sh @@ -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 diff --git a/game/tests/unit/core/effect_words_t1_test.gd b/game/tests/unit/core/effect_words_t1_test.gd index d0c44dc2..0979ef5d 100644 --- a/game/tests/unit/core/effect_words_t1_test.gd +++ b/game/tests/unit/core/effect_words_t1_test.gd @@ -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() diff --git a/game/tests/unit/core/feat_vouchers_test.gd b/game/tests/unit/core/feat_vouchers_test.gd index 2bece985..0179721c 100644 --- a/game/tests/unit/core/feat_vouchers_test.gd +++ b/game/tests/unit/core/feat_vouchers_test.gd @@ -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 真值表〕") diff --git a/game/tests/unit/data_config/counter_prefix_fatal_test.gd b/game/tests/unit/data_config/counter_prefix_fatal_test.gd index f5289ec2..6f0e1951 100644 --- a/game/tests/unit/data_config/counter_prefix_fatal_test.gd +++ b/game/tests/unit/data_config/counter_prefix_fatal_test.gd @@ -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) From d8622ee271d3a3a4038afbec73ecaf633a130c2c Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Wed, 21 Oct 2026 02:47:10 +0800 Subject: [PATCH 2/2] =?UTF-8?q?docs(=E5=8D=A1=E2=91=A1=E6=8A=A5=E5=91=8A?= =?UTF-8?q?=E6=9B=B4=E6=96=B0):=20=C2=A79=20=E9=80=90=E9=9C=80=E6=B1=82?= =?UTF-8?q?=E7=82=B9=20V1-V14=20=E5=85=A8=E8=90=BD=E5=9C=B0=E8=A1=A8=20+?= =?UTF-8?q?=20=E8=A1=A5=E6=BC=8F=E8=AE=B0=E5=BD=95=20+=20V1/V4/V9/V10=20?= =?UTF-8?q?=E5=8F=98=E5=BC=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- reports/iter32-卡②实现报告-2026-10-20.md | 26 ++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/reports/iter32-卡②实现报告-2026-10-20.md b/reports/iter32-卡②实现报告-2026-10-20.md index d8cac9e5..a505a923 100644 --- a/reports/iter32-卡②实现报告-2026-10-20.md +++ b/reports/iter32-卡②实现报告-2026-10-20.md @@ -39,8 +39,30 @@ | T4 round 泛化+宿主清零确认 | 并入 T2/T3 | ✅(round 整桶 clear+宿主 new 天然空) | | T5 前缀双落点+引用闭合+负面套件+manifest | d402ea14, 4c5bdbc9 | ✅ | | T6 数据行/合成行改名+domain/reset_at 删 | 7f45f904 | ✅ | -| T7 断言迁移+S12+V12+变异 | b226a292, 003581f4, a9d5ac13, b42463c9 | ✅(见下) | -| T8 产出物 | 本批 reports | 进行中 | +| T7 断言迁移+§9 正向断言+S12+V12+变异 | b226a292, 003581f4, a9d5ac13, b42463c9, 33e1f110 | ✅(§9 见下) | +| T8 产出物 | 本批 reports | ✅ | + +## §9 逐需求点正向验收主力(V1–V14·补漏记录 33e1f110) + +> 首合(d00f24a8)时本节缺 V1/V2/V6——只靠迁移旧测+V12 兜底。V12 formed/rules 输出面不作判据,adventure 桶跨战持久若坏不会红,核心缺陷消解无回归网。补漏批建齐并打〔无状态化 Vn〕尾缀: + +| V | 需求点 | 落点 | 断言 | 变异拦截 | +|---|---|---|---|---| +| V1 | adventure 桶跨战持久(真实事件面) | smoke `_smoke_counter_buckets` | 同一 RunLedger 两 begin_battle 各真实 request_dig→adv_twin_digs 经 ctx 累积=2、第2战读到持久值1 | **变异红**(桶引用改新建空字典→3 断言 FAIL) | +| V2 | adventure 桶新冒险置空 | feat_vouchers | start_adventure 后 run_ledger.adventure_counters 空 | — | +| V3 | adventure 桶随断点往返 | feat_vouchers V16 换代 | 写桶→快照→回读逐键一致 | — | +| V4 | level 桶同关跨战保持 | smoke `_smoke_tree_rules` | 共享 level 字典引用第2战读到限次值 | **变异红**(level 不共享→FAIL) | +| V5 | tree_sense 每关限 2 | smoke `_smoke_tree_rules` | 置 2 后 mine_hit 闸关零命中 | — | +| V6 | battle 桶随宿主销毁 | smoke `_smoke_counter_buckets` | 次战新编排器读 insurance_used=0 | — | +| V7 | 保险每战限次迁移保绿 | feat_vouchers V25 | 组内断言原样绿(双账不改面) | — | +| V8 | round 桶轮始清空 | effect_words_t1 ⑦ | round_ 前缀随 round_start 整桶 clear | — | +| V9 | 前缀致命+引用闭合 | counter_prefix_fatal_test | meta_/悬空读点两断言拒绝 | **变异红**(前缀分支置 false/闭合降 info→quit2) | +| V10 | 旧件零残留 | static_scan.sh S12 | game/**/*.gd+*.tres 旧符号 0 命中 | **变异红**(注入 counters_export→命中) | +| V11 | 旧断言同批摘除 | F2 迁移 | smoke 裸引擎段+4 单测件 ctx 化 | — | +| V12 | 同种子复放三组 | parity_compare.py | off 逐位/formed·rules 输入面恒等 | rules≠off 0 相同=非空转 | +| V13 | 旧档加载不崩 | feat_vouchers | 含改名前旧键 resume 受理零崩 | — | +| V14 | tree_sense 削弱目检 | 实机目检清单 | 每关第3次无减半/无返 AP | 用户实战验(只登记) | + ## V12 同种子复放三组判读(完成定义 6 主力证据)