diff --git a/game/presentation/views/ap_bar_view.gd b/game/presentation/views/ap_bar_view.gd index 4045fbf0..8f7b86a6 100644 --- a/game/presentation/views/ap_bar_view.gd +++ b/game/presentation/views/ap_bar_view.gd @@ -189,20 +189,10 @@ func is_refresh_ready() -> bool: return not _btn_refresh.disabled -# 探针接缝(合成输入走真实翻译出口口径):刷新按钮点击 → 确认 chips 全链路 -func press_refresh_for_probe() -> void: - _on_refresh_pressed() - - func confirm_visible() -> bool: return _confirm_row.visible -func press_confirm_for_probe() -> void: - if _arm_timer.is_armed(): - _disarm_and_fire() - - func refresh_remaining_text() -> String: return _refresh_sub.text diff --git a/game/presentation/views/battle_end_overlay.gd b/game/presentation/views/battle_end_overlay.gd index 3c9c3ba6..dba79c75 100644 --- a/game/presentation/views/battle_end_overlay.gd +++ b/game/presentation/views/battle_end_overlay.gd @@ -191,10 +191,11 @@ func close() -> void: st.seal.visible = false st.seal.modulate = Color.WHITE st.cele.visible = false - var tw: Tween = get_meta(&"celebrate_tw", null) # 循环句柄随节点存(跨 open/close,close 必杀) - if tw != null and tw.is_valid(): - tw.kill() - remove_meta(&"celebrate_tw") + if has_meta(&"celebrate_tw"): # 循环句柄随节点存(跨 open/close:有则必杀、无则无事——缺 meta 跳过) + var tw: Tween = get_meta(&"celebrate_tw") + if tw.is_valid(): + tw.kill() + remove_meta(&"celebrate_tw") _clear_crest_bricks() @@ -328,9 +329,10 @@ func _spawn_dust_mote(i: int, base: Vector2) -> void: func _play_celebrate() -> void: if UiKit.motion_scale < 1.0: return - var prev: Tween = get_meta(&"celebrate_tw", null) - if prev != null and prev.is_valid(): - prev.kill() + if has_meta(&"celebrate_tw"): # 有句柄才取并 kill(缺 meta 静默跳过——败走后首次胜利同款容错) + var prev: Tween = get_meta(&"celebrate_tw") + if prev.is_valid(): + prev.kill() var tw := st.cele.create_tween() set_meta(&"celebrate_tw", tw) tw.set_loops() diff --git a/game/presentation/views/curio_pick_overlay.gd b/game/presentation/views/curio_pick_overlay.gd index 9d187fb7..a2ca37af 100644 --- a/game/presentation/views/curio_pick_overlay.gd +++ b/game/presentation/views/curio_pick_overlay.gd @@ -153,26 +153,6 @@ func _on_confirm_pressed() -> void: pick_confirmed.emit(_selected) -# 探针/测试复用:以真实按钮路径触发点选、确认与继续(树序遮挡纪律——断言走真实信号路径) -func press_card(index: int) -> void: - # M7:卡载体 Button→bcard(gui_input 点选)——真实路径=同一 _select_card 处理器,探针语义不变 - if index >= 0 and index < _card_meta.size(): - _select_card(String(_card_meta[index].get("id", ""))) - - -func press_confirm() -> void: - _confirm_btn.pressed.emit() - - -func press_continue() -> void: - _continue_btn.pressed.emit() - - -# 探针/测试复用:以真实按钮路径触发刷新意图(信号外发——受理归组合根) -func press_refresh() -> void: - _refresh_btn.pressed.emit() - - # 兑付反馈态(curio_resolved 到达——结果行追加在头部之后;唯一出口 = 继续) func show_result(text: String) -> void: _label.text = _header + "\n" + text diff --git a/game/presentation/views/debug_overlay.gd b/game/presentation/views/debug_overlay.gd index 922b33cd..d0117d8d 100644 --- a/game/presentation/views/debug_overlay.gd +++ b/game/presentation/views/debug_overlay.gd @@ -123,20 +123,6 @@ func output_text() -> String: return st.sim_out.get_parsed_text() -# ---- 探针接缝(真实翻译出口——按钮 pressed 信号路径,非直调组合根)---- - -func press_apply_for_probe() -> void: - _apply_btn_pressed() - - -func press_reset_for_probe() -> void: - _reset_btn_pressed() - - -func press_simulate_for_probe() -> void: - _sim_btn.pressed.emit() - - # ---- 内部(信号 → 外发)---- func _apply_btn_pressed() -> void: diff --git a/game/presentation/views/level_map_screen.gd b/game/presentation/views/level_map_screen.gd index c7ad7bf6..f4deee69 100644 --- a/game/presentation/views/level_map_screen.gd +++ b/game/presentation/views/level_map_screen.gd @@ -465,18 +465,20 @@ func _submit_choice(nid: int) -> void: route_chosen.emit(nid) -# 探针复用:按候选序号走图上命中同路径(候选节点中心坐标 → 命中检测 → 提交——布局决策 2 探针面改造) -func press_candidate(index: int) -> void: +# 探针面命中矩形读口:候选节点的图上命中矩形(全局坐标——真输入 push_click_at 落点)。 +# 命中面与 _candidate_at 同源(节点方块+外扩容差);index 越界或候选未就绪返回空 Rect2(调用方跳过)。 +func candidate_rect(index: int) -> Rect2: if index < 0 or index >= _cand_ids.size() or _map.runner == null: - return + return Rect2() var nodes: Array[Variant] = _map.runner.nodes() var node := _node_by_id(int(_cand_ids[index]), nodes) if node.is_empty(): - return + return Rect2() var pos: Vector2 = _layout(nodes, _map.canvas)[node] - var nid := _candidate_at(pos) # 节点中心必命中自身(命中面含节点方块) - if nid >= 0: - _submit_choice(nid) + var half := NODE_HALF + Vector2(HIT_PAD, HIT_PAD) + var local := Rect2(pos - half, half * 2.0) + var xform := _map.canvas.get_global_transform() + return Rect2(xform * local.position, local.size) # ---- 视口锚点(布局决策 5:当前节点居中;入口停点尚无当前节点=锚入口组包围盒居中) ---- diff --git a/game/presentation/views/node_overlay.gd b/game/presentation/views/node_overlay.gd index cabfcdf5..19954500 100644 --- a/game/presentation/views/node_overlay.gd +++ b/game/presentation/views/node_overlay.gd @@ -29,10 +29,6 @@ func show_rest(healed: int, hp_after: int, max_hp: int) -> void: _btn.text = "休息完毕,继续" visible = true -# 探针复用:以真实按钮路径触发继续 -func press_continue() -> void: - _btn.pressed.emit() - func _emit_continue_pressed() -> void: continue_pressed.emit() diff --git a/game/probe/fx_shot_probe.gd b/game/probe/fx_shot_probe.gd index b7e15910..71c2daa7 100644 --- a/game/probe/fx_shot_probe.gd +++ b/game/probe/fx_shot_probe.gd @@ -45,7 +45,7 @@ func _ready() -> void: return for i in 5: await get_tree().process_frame - main.ui.modals.run.level_screen.press_candidate(0) + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center()) for i in 20: await get_tree().process_frame var st = main.run_scope.settlement diff --git a/game/probe/layout_probe.gd b/game/probe/layout_probe.gd index cb68fd60..c9fbc57d 100644 --- a/game/probe/layout_probe.gd +++ b/game/probe/layout_probe.gd @@ -30,7 +30,7 @@ func _ready() -> void: for i in range(8): await get_tree().process_frame # 进战斗屏——三区(B 战斗画面/C 角色信息/D 扫雷图)同屏常驻,菜单屏不构成断言面 - main.ui.modals.run.level_screen.press_candidate(0) + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center()) for i in range(20): await get_tree().process_frame for vp in VIEWPORTS: diff --git a/game/probe/shot_probe.gd b/game/probe/shot_probe.gd index eb66e9bd..43faf8e8 100644 --- a/game/probe/shot_probe.gd +++ b/game/probe/shot_probe.gd @@ -6,7 +6,7 @@ # 流程 = 真实按钮信号路径驱动(ui_click_probe 同款;存档/设置/元进度三面换注隔离档——禁碰真实档, # 坑册「断点写盘随 save_manager 引用走」教训同款覆盖):菜单(启动态)→ 设置面(settingsbtn # 真实信号路径)→ 生涯页(函数直调中间态——卡② mn10)→ 营地(进档开层新路径——mn9)→ 关卡画面(函数直调中间态→停点出图)→ -# 战斗(level_screen.press_candidate(0) 真实选路提交)→ 状态面板(地图屏状态角标真实信号路径)。 +# 战斗(level_screen candidate_rect 命中矩形真点选路提交)→ 状态面板(地图屏状态角标真实信号路径)。 # 每屏双帧 + frame_post_draw 同步后抓 get_viewport().get_texture(),PNG 落输出目录; # 最小自检 = 七件存在且非零字节(像素方差断言归 tools/shot_checker.py——B 档骨架分工)。 # 商店房不纳屏(真实路径口径):真实路径必经 L1 打穿→L2 商店节点 @@ -149,10 +149,10 @@ func _ready() -> void: var expect := "◈ %d" % int(main.ui.modals.run.level_screen.get("_runner").gp) if gp_label.text != expect: _problems.append("GP 徽章读数不符: %s != %s" % [gp_label.text, expect]) - # ④ 战斗(节点一——真实选路提交路径 press_candidate,ui_click_probe :199 同款) + # ④ 战斗(节点一——candidate_rect 命中矩形真点选路提交,ui_click_probe 同款) # oracle: design/gdd/ux/battle.md:122 B 区构建:站桩/明细带/轮次指示(战斗出图与几何锚组) if main.ui.modals.run.level_screen != null and main.ui.modals.run.level_screen.visible: - main.ui.modals.run.level_screen.press_candidate(0) + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center()) for i in 10: await get_tree().process_frame await _snap("battle") diff --git a/game/probe/t17_defer_probe.gd b/game/probe/t17_defer_probe.gd index a535e93f..c0edf5b3 100644 --- a/game/probe/t17_defer_probe.gd +++ b/game/probe/t17_defer_probe.gd @@ -37,7 +37,7 @@ func _ready() -> void: return for i in 5: await get_tree().process_frame - main.ui.modals.run.level_screen.press_candidate(0) + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center()) for i in 20: await get_tree().process_frame if main.run_scope.settlement == null: diff --git a/game/probe/ui_click_probe.gd b/game/probe/ui_click_probe.gd index a405d128..2b420b62 100644 --- a/game/probe/ui_click_probe.gd +++ b/game/probe/ui_click_probe.gd @@ -40,6 +40,8 @@ var _route_clicks := 0 # 选路覆盖层点击计数(岔路使用 var _locktest_state := 0 # T1 D1 锁期/重放断言状态机(0=待注入 1=等重放 2=完结)——每 run 一次 var _locktest_cell := Vector2i(-9, -9) var _locktest_frames := 0 +var _stoptest_state := 0 # 停手钮定型化覆盖段状态机(0=待命 1=已点击待核效 2=完结)——每 run 一次 +var _stoptest_frames := 0 func _ready() -> void: @@ -421,13 +423,13 @@ func _ready() -> void: var wc3_txt: String = main.ui.battle.log_view._log.get_parsed_text() if not wc3_txt.contains("种子 42") or not wc3_txt.contains("(含面板加成)"): _problems.append("开场行回归失守:局外加成(pct>0)局的开场行应带「(含面板加成)」(wc3)") - # 关卡开始 = 入口选路停点——press_candidate 图上命中路径选入口候选进首战 + # 关卡开始 = 入口选路停点——candidate_rect 命中矩形中心真点选入口候选进首战 #(首格可点 AC 口径随之含入口选路一步:菜单点击 → 停点呈现 → pick → 棋盘安装) if main.ui.modals.run.level_screen.visible: # 独立性断言:独立屏激活期战斗屏宿主整体隐藏 if main.ui.screens.battle_root.visible: _problems.append("独立屏激活期战斗屏宿主未隐藏(零透出契约失守)") - main.ui.modals.run.level_screen.press_candidate(0) + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center()) await get_tree().process_frame await get_tree().process_frame var t_entry := Time.get_ticks_msec() - t1 @@ -449,7 +451,7 @@ func _ready() -> void: action_budget -= 1 await get_tree().process_frame if main.ui.modals.run.node_overlay.visible: - main.ui.modals.run.node_overlay.press_continue() + ProbeUiUtil.push_click(main, main.ui.modals.run.node_overlay._btn) _seals_placed.clear() continue if main.ui.modals.run.curio_overlay.visible: @@ -474,15 +476,17 @@ func _ready() -> void: if not main.ui.flow.locks_input(): _problems.append("三选一停点打开时输入未锁定") if main.ui.modals.run.curio_overlay._continue_btn.visible: - main.ui.modals.run.curio_overlay.press_continue() + ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._continue_btn) elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty(): - main.ui.modals.run.curio_overlay.press_card(0) # 奖励节点待选态:点首件+确认(真实按钮信号路径——与关卡完成停点同形) - main.ui.modals.run.curio_overlay.press_confirm() + ProbeUiUtil.push_click_at(main, (main.ui.modals.run.curio_overlay._bcards[0] as Control).get_global_rect().get_center()) # 奖励节点待选态:真点首卡+确认(gui_input 点选——与关卡完成停点同形) + await get_tree().process_frame # 选卡当帧确认钮才现形、容器重排在途——排版帧后矩形成形再真点 + ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._confirm_btn) elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._pending.is_empty(): - main.ui.modals.run.curio_overlay.press_card(0) - main.ui.modals.run.curio_overlay.press_confirm() + ProbeUiUtil.push_click_at(main, (main.ui.modals.run.curio_overlay._bcards[0] as Control).get_global_rect().get_center()) + await get_tree().process_frame # 同上:确认钮排版帧 + ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._confirm_btn) else: - main.ui.modals.run.curio_overlay.press_continue() # 停点已消费的残面——继续关屏 + ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._continue_btn) # 停点已消费的残面——继续关屏 _seals_placed.clear() continue if main.ui.modals.menu.shop_overlay.visible: @@ -537,7 +541,7 @@ func _ready() -> void: if String(nd.get("type", "")) == "reward" and main.ui.modals.run.level_screen._cand_ids.has(nd_id): pick = main.ui.modals.run.level_screen._cand_ids.find(nd_id) break - main.ui.modals.run.level_screen.press_candidate(pick) + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(pick).get_center()) _route_clicks += 1 _seals_placed.clear() continue @@ -626,11 +630,13 @@ func _ready() -> void: _ap_spent_this_round = 0 _last_state = st _locktest_tick() + _stoptest_tick() if main.run_scope.settlement.battle_result == "" and main.run_scope.settlement.action_economy.phase == "Active": # T1 D1——锁测在飞期(state 1)冻结探针动作面:排空帧 autoplay 的免费 move 会抢在 # 帧末 deferred 重放之前改变英雄位(seal 拒收 not_adjacent 实证)——「注入→重放」 # 窗口内局面零变化正是 v3.1 被测语义(演播期冻结),动作面恢复待断言收口。 - if _locktest_state != 1: + # 停手定型化段同款冻结:核效窗口内零挖掘提交,排除 AP/盘面漂移对收轮判据的混淆。 + if _locktest_state != 1 and _stoptest_state != 1: _run_modal_probes_once() _play_one_action() # ---- 预算耗尽收口(T17 硬锁批适配):选件变强后 probe 可能活过预算不再战败——断点段前提 @@ -651,14 +657,15 @@ func _ready() -> void: break elif main.ui.modals.run.curio_overlay.visible: if main.ui.modals.run.curio_overlay._continue_btn.visible: - main.ui.modals.run.curio_overlay.press_continue() + ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._continue_btn) elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._pending.is_empty(): - main.ui.modals.run.curio_overlay.press_card(0) - main.ui.modals.run.curio_overlay.press_confirm() + ProbeUiUtil.push_click_at(main, (main.ui.modals.run.curio_overlay._bcards[0] as Control).get_global_rect().get_center()) + await get_tree().process_frame # 选卡当帧确认钮才现形、容器重排在途——排版帧后矩形成形再真点 + ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._confirm_btn) else: - main.ui.modals.run.curio_overlay.press_continue() + ProbeUiUtil.push_click(main, main.ui.modals.run.curio_overlay._continue_btn) elif main.ui.modals.run.node_overlay.visible: - main.ui.modals.run.node_overlay.press_continue() + ProbeUiUtil.push_click(main, main.ui.modals.run.node_overlay._btn) elif main.ui.modals.menu.shop_overlay.visible: var shop_leave2: Button = ProbeUiUtil.find_button(main.ui.modals.menu.shop_overlay, "离开商店") if shop_leave2 != null: @@ -978,10 +985,13 @@ func _run_modal_probes_once() -> void: _tested_refresh_chain = true var rap_before: int = int(main.run_scope.settlement.action_economy.rap) var board_before = main.run_scope.settlement.minesweeper.current_board() - main.ui.battle.board_zone.ap_bar.press_refresh_for_probe() + ProbeUiUtil.push_click(main, main.ui.battle.board_zone.ap_bar._btn_refresh) # 真点弃图刷新(disabled 守卫=真实拒绝路径) if not main.ui.battle.board_zone.ap_bar.confirm_visible(): _problems.append("弃图刷新确认 chips 未出现(异位确认缺失)") - main.ui.battle.board_zone.ap_bar.press_confirm_for_probe() + for c in main.ui.battle.board_zone.ap_bar._confirm_row.get_children(): + if c is Button: + ProbeUiUtil.push_click(main, c) # 真点确认 chips 真钮(异位确认——_confirm_row 内 Button 子件) + break if int(main.run_scope.settlement.action_economy.rap) != rap_before - 8: _problems.append("弃图刷新未扣 RAP8(%d→%d)" % [rap_before, int(main.run_scope.settlement.action_economy.rap)]) if main.run_scope.settlement.action_economy.phase != "Active": @@ -1155,6 +1165,56 @@ func _locktest_pick_cell(minesweeper) -> Vector2i: return Vector2i(-1, -1) +# ---- 停手钮定型化覆盖段(B1——每 run 一次:使能窗内真点停手钮,钉命中登记与收轮落效) ---- +# 触发条件=战局挖掘期(相位 Active+停手钮使能+盘面有挖可走+本 run 未测过);触发帧冻结 +# 自动玩家其余随机行为,仅提交停手(驱动与 _click_stop 同型——AP 条真钮 push_input)。 +# 断言两钉:①覆盖登记件停手键在场(零命中=驱动空转假绿,即红);②相位离开 Active(停手 +# 收轮)。前提消失守卫:点击后战局收场(终结演播竞态)→ 静默弃测不计 problem(正常局不触发)。 +func _stoptest_tick() -> void: +# oracle: design/gdd/systems/排雷循环/排雷玩法.md:167 (停手=玩家主动收轮——判定上报通道) + if _stoptest_state == 2: + return + var settlement = main.run_scope.settlement + if settlement == null or settlement.minesweeper == null or settlement.minesweeper.current_board() == null: + return + if _stoptest_state == 0: + if String(settlement.battle_result) != "" or main.ui.modals.run.end_overlay.is_open(): + return # 战局已收场——本 run 无停手窗(静置) + if not main.ui.screens.row_pacer.pending_rows().is_empty(): + return # 演播期不出手:点击会进锁期缓存被吞(同 _locktest_tick 前提) + var ae = settlement.action_economy + if String(ae.phase) != "Active" or int(ae.ap) < 1: + return + var stop_btn: Button = main.ui.battle.board_zone.ap_bar._btn_stop + if stop_btn == null or stop_btn.disabled: + return # 停手钮未使能——下帧重试 + if AutoPlayer.choose_dig(settlement.minesweeper).is_empty(): + return # 盘面已无可挖——不算挖掘期触发窗(防清图混淆) + _stoptest_state = 1 + _stoptest_frames = 0 + _click_stop() + elif _stoptest_state == 1: + _stoptest_frames += 1 + if _stoptest_frames > 120: + _problems.append("停手定型化段:点击后 120 帧相位仍 Active(收轮落效失守)〔QA收口 V2〕") + _stoptest_state = 2 + return + if String(settlement.battle_result) != "" or main.ui.modals.run.end_overlay.is_open(): + _stoptest_state = 2 # 前提消失守卫:终结演播竞态——静默弃测 + return + if String(settlement.action_economy.phase) != "Active": + _stoptest_assert_hit() + _stoptest_state = 2 + + +func _stoptest_assert_hit() -> void: +# oracle: design/gdd/systems/排雷循环/排雷玩法.md:167 (停手钮真输入命中登记——判定上报通道) + var stop_btn: Button = main.ui.battle.board_zone.ap_bar._btn_stop + var stop_key := String(_recorder._node_keys.get(stop_btn.get_instance_id(), "")) + if stop_key == "" or not _recorder._hits.has(stop_key): + _problems.append("停手定型化段:停手钮真点零命中登记(驱动空转假绿)〔QA收口 V2〕") + + func _report_stall(st: Dictionary) -> void: # oracle: design/engineering/test-conventions.md:30 (判定上报通道——验证函数组) var why := "" diff --git a/game/probe/ui_probe.gd b/game/probe/ui_probe.gd index 8a37e1d3..1e2bfbab 100644 --- a/game/probe/ui_probe.gd +++ b/game/probe/ui_probe.gd @@ -75,7 +75,7 @@ func _ready() -> void: await get_tree().process_frame await get_tree().process_frame # ---- 入口选路停点(关卡开始即出节点图——level_started 后、首战前弹选路层; - # press_candidate 图上命中路径 pick 入口单候选进首战)---- + # candidate_rect 命中矩形真点 pick 入口单候选进首战)---- # oracle: design/gdd/ux/level-map.md:11 入口停点候选数=入口个数(入口单候选断言) # ---- 独立性断言(独立屏激活期战斗屏宿主整体隐藏,零透出)---- if not main.ui.modals.run.level_screen.visible: @@ -86,7 +86,7 @@ func _ready() -> void: or main.run_scope.runner.next_candidate_ids().size() != 1: problems.append("入口停点状态失守(选路态 + 入口单候选)") else: - main.ui.modals.run.level_screen.press_candidate(0) + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center()) await get_tree().process_frame await get_tree().process_frame if not main.ui.screens.battle_root.visible or main.ui.modals.run.level_screen.visible: diff --git a/game/probe/ui_struct_probe.gd b/game/probe/ui_struct_probe.gd index d15fe3f5..0b343c18 100644 --- a/game/probe/ui_struct_probe.gd +++ b/game/probe/ui_struct_probe.gd @@ -272,7 +272,7 @@ func _ready() -> void: if not main.ui.modals.run.node_overlay.visible: problems.append("[node] 节点结算层未打开(rest 兑付路径中断)") await _audit("node_overlay", [main.ui.modals.run.node_overlay]) - main.ui.modals.run.node_overlay.press_continue() + ProbeUiUtil.push_click(main, main.ui.modals.run.node_overlay._btn) _settle() if main.ui.modals.run.node_overlay.visible: problems.append("[node] 关闭后层未隐藏(continue_pressed 路径中断)") diff --git a/game/probe/ui_tune_probe.gd b/game/probe/ui_tune_probe.gd index 3c910611..e7509d2d 100644 --- a/game/probe/ui_tune_probe.gd +++ b/game/probe/ui_tune_probe.gd @@ -58,16 +58,47 @@ func _ready() -> void: problems.append("生效预览未联动(%s)" % main.ui.modals.menu.debug_overlay.effect_text()) # ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照 # oracle: design/engineering/test-conventions.md:49 调试面加成链路:滑条联动/应用/重置、hero 表运行期只读(②③④⑦组) - main.ui.modals.menu.debug_overlay.press_apply_for_probe() + var apply_btn: Button = ProbeUiUtil.find_button(main.ui.modals.menu.debug_overlay, "应用并关闭") + if apply_btn == null: + problems.append("调试浮窗「应用并关闭」钮缺失") + else: + ProbeUiUtil.push_click(main, apply_btn) # 真点应用(组合根应用=关浮窗——落点断言由 ④ 重开承载) # ④ 重置(重开浮窗 → 重置按钮真实路径;面板百分比归零——下一场战斗生效值回到基线) main._show_debug_overlay() - main.ui.modals.menu.debug_overlay.press_reset_for_probe() + var reset_btn: Button = ProbeUiUtil.find_button(main.ui.modals.menu.debug_overlay, "重置为 0") + if reset_btn == null: + problems.append("调试浮窗「重置为 0」钮缺失") + else: + ProbeUiUtil.push_click(main, reset_btn) # ⑤ 批量模拟全链路(N=10——走 level_autoplay 与 headless 扫描同口径;表基线由组合根复位; # 菜单态上下文未锁定=按钮可点,is_running 语义=模拟在跑〔18卡⑦ 与对局禁用态拆分〕) # oracle: design/engineering/test-conventions.md:49 调试面批量模拟链路:N=10 输出关卡完成率/剩余 HP/败北节点(⑤组) main.ui.modals.menu.debug_overlay.set_sliders(100, 0) main.ui.modals.menu.debug_overlay.set_sim_count(10) - main.ui.modals.menu.debug_overlay.press_simulate_for_probe() + # 折叠区控件真输入通道(test-conventions §2——先滚轮滚入可视区再点):sim 钮在滚动列表 boot + # 滚位下跨出视口底边(rect y=639 对 scroll 底 641、钮高 40——hit 面被裁),真点必落空; + # 滚轮下探滚入可视区后真点(实证一档入视口、同帧 is_running 翻真) + var sim_btn: Button = main.ui.modals.menu.debug_overlay._sim_btn + var sim_scroll: ScrollContainer = null + var sim_stack: Array = [main.ui.modals.menu.debug_overlay] + while not sim_stack.is_empty(): + var sn: Node = sim_stack.pop_back() + if sn is ScrollContainer: + sim_scroll = sn + break + for sch in sn.get_children(): + sim_stack.append(sch) + if sim_scroll == null: + problems.append("调试浮窗滚动列表缺失(sim 钮滚入通道无载体)") + else: + for i in 12: + if sim_scroll.get_global_rect().encloses(sim_btn.get_global_rect()): + break + ProbeUiUtil.push_wheel(main, sim_scroll.get_global_rect().get_center(), 1) + await get_tree().process_frame + if not sim_scroll.get_global_rect().encloses(sim_btn.get_global_rect()): + problems.append("sim 钮滚轮滚入可视区失败(12 档未入——折叠区真输入通道断)") + ProbeUiUtil.push_click(main, sim_btn) # 真点「运行模拟」(菜单态未锁可点——disabled 守卫=真实拒绝路径) var guard := SIM_GUARD_FRAMES while main.ui.modals.menu.debug_overlay.is_running() and guard > 0: guard -= 1 @@ -97,7 +128,12 @@ func _ready() -> void: # isolate_save 先删后建——上次运行的残留断点会触发互斥确认早退) ProbeUiUtil.isolate_save(main, "smoke_tune_probe_save", true, false, true) # 末参=swap_save(卡②签名换代——断点写盘经 autoload 落隔离档) main.ui.modals.menu.debug_overlay.set_sliders(100, 50) - main.ui.modals.menu.debug_overlay.press_apply_for_probe() + main._show_debug_overlay() # ⑥ 关闭后重开——⑧ 应用真点(应用=组合根关窗,⑧b 显式关浮窗断言照旧承载) + var apply_btn2: Button = ProbeUiUtil.find_button(main.ui.modals.menu.debug_overlay, "应用并关闭") + if apply_btn2 == null: + problems.append("调试浮窗「应用并关闭」钮缺失(⑧)") + else: + ProbeUiUtil.push_click(main, apply_btn2) main.ui.modals.menu.debug_overlay.seed_edit.text = "42" # 种子钉死(18卡⑦审计🔴——空框=非首局随机,显式优先经真实路径断言 master_seed) main._start_adventure_from_menu() # 完整冒险路径(唯一试玩入口;_start_single 按语义清零滑条 = 恒基线,不承载加成断言) await get_tree().process_frame @@ -107,9 +143,9 @@ func _ready() -> void: problems.append("开冒险路径未显式关浮窗(T2 防护失守)") if int(main.run_scope.adventure.master_seed) != 42: problems.append("显式种子未达冒险(master_seed=%s ≠ 42——真实路径显式优先失守)" % str(main.run_scope.adventure.master_seed)) - # 关卡开始 = 入口选路停点——真实按钮路径选入口候选进首战再读呈现快照 + # 关卡开始 = 入口选路停点——candidate_rect 命中矩形真点选入口候选进首战再读呈现快照 if main.ui.modals.run.level_screen.visible: - main.ui.modals.run.level_screen.press_candidate(0) + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center()) await get_tree().process_frame await get_tree().process_frame var hero: Dictionary = main.run_scope.settlement.presentation_snapshot().hero diff --git a/game/sim/journey_player.gd b/game/sim/journey_player.gd index 5e64ae82..a9b04cf1 100644 --- a/game/sim/journey_player.gd +++ b/game/sim/journey_player.gd @@ -10,9 +10,9 @@ # 真实输入-场景热点/真实输入-键盘/方法-地图命中口/方法-棋盘口)。原归因制度残骸=本结构内字段。 # 误报收敛:误报指纹(类+段+归因摘要)落 .gates/coverage/journey_misfire_ledger.json——同一指纹 # 第 2 次起自动登记静默(计数照记、不再进上报清单);连 3 批零真缺陷判据收紧归用户复核轮。 -# 驱动纪律:按钮/场景物件全走真输入(push_input 管线,同探针判据——ProbeUiUtil 单点通道)。 -# 两处方法口如实标注通道:地图候选=press_candidate(自绘布局命中路径,探针族既定入口); -# 棋盘格=click_cell(BoardView 公共点击口,ui_input_probe 先例——棋盘管线面由该探针承载)。 +# 驱动纪律:按钮/场景物件/地图候选全走真输入(push_input 管线,同探针判据——ProbeUiUtil 单点通道; +# 地图候选=LevelMapScreen.candidate_rect 命中矩形中心 push_click_at——方法口已清零)。 +# 棋盘格=click_cell(BoardView 公共点击口,ui_input_probe 先例——棋盘管线面由该探针承载)=余下唯一方法口。 # 停点分发的视图内部读数(_continue_btn/_node_pending 等)沿用探针族直读受控例外(iter53 反哺点 9 同款在册)。 # 运行:Godot_console.exe --headless --path . res://sim/journey_player.tscn extends Node @@ -41,6 +41,10 @@ var _map_status_done := false # 地图屏状态角标一次性 var _legend_done := false # 图例开合一次性(每战斗面一次) var _status_done := false # 战斗屏状态面板一次性 var _refresh_done := false # 刷新确认链一次性 +var _stop_done := false # 停手行为一次性(每战至多一次——挖掘期确定性触发) +var _stop_settlement: Variant = null # 停手基线战局(换战局重置一次性) +var _stop_round := -1 # 停手轮次基线(轮换重置已花计数) +var _digs_this_round := 0 # 本轮已提交挖掘数(停手条件——AP 已花 ≥1) func _ready() -> void: @@ -376,11 +380,11 @@ func _phase_adventure() -> void: _record_screens() var st := _screen_state() if bool(st["节点"]): - _last_action = "节点层「继续」(方法-视图口)" - main.ui.modals.run.node_overlay.press_continue() + _last_action = "真点节点层「继续」钮" + ProbeUiUtil.push_click(main, main.ui.modals.run.node_overlay._btn) continue if bool(st["三选一"]): - _drive_curio() + await _drive_curio() continue if bool(st["商店"]): var lv: Button = ProbeUiUtil.find_button(main.ui.modals.menu.shop_overlay, "离开商店") @@ -437,30 +441,42 @@ func _drive_map_once() -> void: var resumed: Button = ProbeUiUtil.find_button(main.ui.modals.run.status_panel, "继续", "prefix") if resumed != null and main.ui.modals.run.status_panel.visible: ProbeUiUtil.push_click(main, resumed) - _last_action = "地图候选命中(方法-地图命中口)" - main.ui.modals.run.level_screen.press_candidate(0) + _last_action = "地图候选命中(真实输入-命中矩形)" + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center()) func _drive_curio() -> void: - # 三选一停点:待选态点首件+确认;兑付/已消费态点继续(方法-视图口——curio 停点既定驱动面) + # 三选一停点:待选态真点首卡+确认(gui_input 点选——确认钮现形当帧容器重排在途,排版帧后再点); + # 兑付/已消费态真点继续 var co: Control = main.ui.modals.run.curio_overlay - _last_action = "三选一停点分发(方法-视图口)" + _last_action = "三选一停点分发(真输入)" if co._continue_btn.visible: - co.press_continue() + ProbeUiUtil.push_click(main, co._continue_btn) elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty(): - co.press_card(0) - co.press_confirm() + ProbeUiUtil.push_click_at(main, (co._bcards[0] as Control).get_global_rect().get_center()) + await _frames(2) + ProbeUiUtil.push_click(main, co._confirm_btn) elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._pending.is_empty(): - co.press_card(0) - co.press_confirm() + ProbeUiUtil.push_click_at(main, (co._bcards[0] as Control).get_global_rect().get_center()) + await _frames(2) + ProbeUiUtil.push_click(main, co._confirm_btn) else: - co.press_continue() + ProbeUiUtil.push_click(main, co._continue_btn) func _drive_battle_once() -> void: # 战斗单步:挂死检测+功能钮真点(图例/状态/刷新确认链/停手)+棋盘挖格(方法-棋盘口) var settlement = main.run_scope.settlement var diag: Dictionary = settlement.diagnostic_snapshot() + if settlement != _stop_settlement: # 换战局——停手一次性与轮内计数重置 + _stop_settlement = settlement + _stop_done = false + _stop_round = -1 + _digs_this_round = 0 + var round_now := int(diag.round) + if round_now != _stop_round: + _stop_round = round_now + _digs_this_round = 0 var st := { "round": diag.round, "phase": diag.action_economy.phase, "ap": diag.action_economy.ap, "revealed": diag.minesweeper_revealed, @@ -514,6 +530,15 @@ func _drive_battle_once() -> void: # 棋盘动作(方法-棋盘口——BoardView 公共点击口,ui_input_probe 先例;C 的按钮面全走真输入) var minesweeper = settlement.minesweeper var plan: Dictionary = AutoPlayer.choose_dig(minesweeper) + # 停手行为(每战至多一次——挖掘期确定性触发,无随机占比;触发时点微调在代拍板白名单): + # 相位 Active+停手钮使能+AP 已花 ≥1+盘面有挖可走 → 真点停手钮收轮(真输入行为之一) + if not _stop_done and String(diag.action_economy.phase) == "Active" \ + and not ap_bar._btn_stop.disabled and _digs_this_round >= 1 and not plan.is_empty(): + _stop_done = true + _last_action = "真点「停手」钮(挖掘期定型化触发)" + ProbeUiUtil.push_click(main, ap_bar._btn_stop) + await _frames(2) + return if plan.is_empty(): plan = AutoPlayer.gamble_pick(minesweeper) if plan.is_empty(): @@ -525,6 +550,7 @@ func _drive_battle_once() -> void: if plan.hero_cell != minesweeper.hero_pos(): main.ui.battle.board_zone.board_view.click_cell(plan.hero_cell, MOUSE_BUTTON_LEFT) main.ui.battle.board_zone.board_view.click_cell(plan.dig_cell, MOUSE_BUTTON_LEFT) + _digs_this_round += 1 await _frames(1) diff --git a/game/tests/system_test.gd b/game/tests/system_test.gd index c9c951b1..9ccd6677 100644 --- a/game/tests/system_test.gd +++ b/game/tests/system_test.gd @@ -9,6 +9,7 @@ extends SceneTree const AutoPlayer := preload("res://sim/auto_player.gd") # 自动玩家策略单一实现(lifecycle 活战斗驱动;smoke 库件迁移期按现路径 preload) +const ProbeUiUtil := preload("res://probe/probe_ui_util.gd") # 真输入合成单点(tests 面复用共用件——禁复制第二份合成输入件) var _failures: int = 0 var _checks: int = 0 @@ -436,8 +437,8 @@ func _smoke_lifecycle_matrix() -> void: await process_frame await _stage_faces(main, "V3b 开局停点", "alive", "alive", "null", {"mf_start": false, "level_vis": true}) - # 阶段③地图选路→战斗中(真实选路提交 press_candidate) - main.ui.modals.run.level_screen.press_candidate(0) + # 阶段③地图选路→战斗中(真实选路提交——candidate_rect 命中矩形真点) + ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center()) for i in range(10): await process_frame await _stage_faces(main, "V3c 战斗中", "alive", "alive", "alive", {"mf_start": false}) diff --git a/game/tests/unit/presentation/reward_overlay_test.gd b/game/tests/unit/presentation/reward_overlay_test.gd index 51937212..3a442146 100644 --- a/game/tests/unit/presentation/reward_overlay_test.gd +++ b/game/tests/unit/presentation/reward_overlay_test.gd @@ -6,6 +6,7 @@ extends SceneTree # 运行:godot --headless --path game --script res://tests/unit/presentation/reward_overlay_test.gd(expect 0) # 断言走 test_kit 计数(Kit.check)+ 失败即停早退协议(顺序状态机——坏态下后继断言无效)。 const Kit := preload("res://tests/unit/test_kit.gd") +const ProbeUiUtil := preload("res://probe/probe_ui_util.gd") # 真输入合成单点(tests 面复用共用件——禁复制第二份合成输入件) # 断言 + 失败即停(原直写 print FAIL + quit(1) + return 样板单点化——早退语义逐位保持) func _req(desc: String, ok: bool) -> bool: @@ -60,13 +61,15 @@ func _run() -> void: if not _req("付费档余额可付:免费位 0 但钮点亮(对齐商店——GP 档可刷)", ov._refresh_btn.visible and not ov._refresh_btn.disabled): return - ov.press_refresh() - if not _req("press_refresh 未外发 refresh_requested", refreshed.size() == 1): + await process_frame # 刷新钮现形当帧容器重排在途——排版帧后矩形成形再真点 + ProbeUiUtil.push_click(root, ov._refresh_btn) # 真点刷新钮(disabled 守卫=真实拒绝路径) + if not _req("真点刷新未外发 refresh_requested", refreshed.size() == 1): return - # ② 点选→高亮→确认外发(真实按钮路径);确认前改选以最后点选为准 - ov.press_card(0) - ov.press_card(1) - ov.press_confirm() + # ② 点选→高亮→确认外发(真输入管线:bcard gui_input 点选——与 press_card 同一 _select_card 处理器);确认前改选以最后点选为准 + ProbeUiUtil.push_click_at(root, (ov._bcards[0] as Control).get_global_rect().get_center()) + ProbeUiUtil.push_click_at(root, (ov._bcards[1] as Control).get_global_rect().get_center()) + await process_frame # 确认钮现形当帧容器重排在途——排版帧后矩形成形再真点 + ProbeUiUtil.push_click(root, ov._confirm_btn) if not _req("点选确认未外发 pick_confirmed(gp)(%s)" % str(picked), ov._confirm_btn.visible and picked.size() == 1 and String(picked[0]) == "gp"): return @@ -76,7 +79,8 @@ func _run() -> void: if not _req("结果态应点亮继续且收三卡与刷新钮", ov._continue_btn.visible and not ov._choice_box.visible and not ov._refresh_btn.visible): return - ov.press_continue() + await process_frame # 继续钮现形当帧容器重排在途——排版帧后矩形成形再真点 + ProbeUiUtil.push_click(root, ov._continue_btn) if not _req("继续未外发 continue_pressed", continued.size() == 1): return ov.close_pick() diff --git a/reports/iter54-反哺点-2026-11-07.md b/reports/iter54-反哺点-2026-11-07.md new file mode 100644 index 00000000..e072398a --- /dev/null +++ b/reports/iter54-反哺点-2026-11-07.md @@ -0,0 +1,11 @@ +# 迭代 54 反哺点清单(2026-11-07) + +> 本清单逐卡增量落笔;用户在会话中裁决后由文档批写入 `design/` 并把该条清掉(AGENTS 红线:实现批禁写 `design/`,迭代实现报告与反哺点归 `reports/`)。 + +## 卡 1|QA harness 缺陷与覆盖遗漏收口批——申报:零条 + +卡 1 §6 申报「无关联文档」——A2 为容错修复、B1/B2 为测试驱动面,零设计语义变化。核过面:`design/gdd/systems/排雷循环/排雷玩法.md`(停手语义句 :162/:167 与实装一致)、`design/engineering/test-conventions.md`(真输入管线条文 §2 与实装一致)——两处无需改句。 + +## 卡 2|营地交互修复批——(随卡 2 推进补齐) + +## 卡 3|精英专属池——(随卡 3 推进补齐) diff --git a/reports/iter54-实现报告-2026-11-07.md b/reports/iter54-实现报告-2026-11-07.md new file mode 100644 index 00000000..2af4b0df --- /dev/null +++ b/reports/iter54-实现报告-2026-11-07.md @@ -0,0 +1,65 @@ +# 迭代 54 实现报告(2026-11-07) + +> 三卡:QA harness 缺陷与覆盖遗漏收口批 → 营地交互修复批 → 精英专属池。本报告随各卡末卡增量落笔(卡 1 QA 收口段先行,卡 2/卡 3 段随后补齐),总收口前为完整版。 +> 独立验收审计入口预约:`reports/iter54-验收审计-2026-11-07.md`(未参与实现的只读会话/子代理执行,未过不合回)。审计深度:卡 1=中档(对照表逐行全量、最窄复跑省略以远端 CI 现查代证);卡 2=中档;卡 3=高档(全量)。 + +## 卡 1|QA harness 缺陷与覆盖遗漏收口批——状态:已完成 + +### 各任务卡状态 + +| 任务卡 | 状态 | 摘要 | +|---|---|---| +| T1 A2 关闭容错修复 | 完成 | battle_end_overlay.gd close(:194) 与 _play_celebrate(:331) 两处 `get_meta(&"celebrate_tw", null)` 改 `has_meta` 前置判断,语义不变(有则必杀、无则无事)〔提交 57f3cc75〕 | +| T2 B1 探针定型化停手段 | 完成 | ui_click_probe 新增 `_stoptest_tick` 状态机(镜像 `_locktest_tick` 形态)+`_stoptest_assert_hit`,挖掘期触发窗真点停手钮,断言①覆盖登记停手键在场②相位离开 Active〔提交 816aaae5〕 | +| T3 B1 journey 停手+S4c 8 处转换 | 完成 | journey_player 新增停手行为(每战至多一次,挖掘期确定性触发)+8 处真输入转换+头注方法口句换代〔提交 2f5c734e〕 | +| T4 B2 ui_click_probe 16 处+candidate_rect | 完成 | LevelMapScreen 删 press_candidate 增 candidate_rect(index)->Rect2(命中面与 _candidate_at 同源,全局坐标,越界空 Rect2);ui_click_probe 候选 2+节点继续 2+三选一 10+弃图刷新链 2 全转真输入,既有落点断言逐条原样〔提交 afcfbf37〕 | +| T5 B2 其余 7 探针 11 处 | 完成 | 候选 6+停点 1+调试浮窗 4;shot_probe 头注同步〔提交 22903043〕 | +| T6 B2 非探针层 6 处+死接缝清理 | 完成 | system_test 1 处+reward_overlay_test 5 处真输入;死接缝 11 方法删除(ap_bar_view 2+curio_pick_overlay 4+node_overlay 1+debug_overlay 3,level_map_screen 1 随 T4)〔提交 710df6ce〕 | +| T7 产出物落位 | 完成 | 本报告+反哺点清单+批末选档跑闸(读数见下)+审计入口预约 | + +### 实际对照表(R1–R9,补「结果」格) + +| 行 | 验收测试 | 结果 | +|---|---|---| +| R1 终局结算框关闭动线零引擎报错 | fx_probe+screen_flow_probe 直跑 grep「SCRIPT ERROR」「celebrate_tw」 | 绿——两探针各 exit=0、SCRIPT ERROR 0、celebrate_tw 0(提交 57f3cc75 读数) | +| R2 停手钮真输入命中与落效 | ui_click_probe 直跑(_stoptest_tick 断言①②) | 绿——exit=0、问题=0、覆盖落盘停手键命中 1 次(确定性触发实证;提交 816aaae5 读数) | +| R3 journey 停手+方法口清零 | journey 全线跑批 | 绿(读数判据满足)——fatal 0、上报结构不变(2 条=已知缺陷 A1 双指纹,改前基线同源)、覆盖登记停手键 6 次+停点键在场(结算面继续 6/确认 3)、屏到达 16 类含战斗/终局;exit=1 为 A1 基线态(营地交互修复批承接中,本卡不做动作——卡 1 §5 范围边界) | +| R4 停点链真输入化(ui_click_probe 16 处) | ui_click_probe 直跑+既有落点断言+登记键集不回退 | 绿——exit=0、问题=0、覆盖 43 命中/30 控件较改前 39/28 不回退、新增停点链键(curio 确认选择 2/继续 2)、冒险终局在案选路口点击 9 次 | +| R5 到达型候选+停点(7 探针) | 7 探针逐件孤立直跑 | 绿——headless 五件(ui_struct/layout/ui_probe/t17_defer/ui_tune)exit=0+SCRIPT ERROR 0;窗口化两件(shot 七屏 PNG 齐、fx_shot PASS 演出帧 7 张挖格 3)exit=0 | +| R6 调试浮窗面(4 处) | ui_tune_probe 直跑 | 绿——PASS 全链路(含模拟 N=10 输出与种子三分断言) | +| R7 系统测试宿主候选真输入化 | 系统测试直跑 | 绿——69 项检查 0 失败、三闸过(V3c 阶段真实选路提交在跑批中执行) | +| R8 三选一覆盖层单元真输入化 | 单元与集成套件直跑 | 绿——82 套件 all green、exit=0、0 FAIL(reward_verify 断言在册) | +| R9 死接缝同批清理(5 文件 11 方法) | 全仓 .gd 双模式 grep | 绿——定义清 `func press_(candidate\|continue\|card\|confirm\|refresh\|apply\|reset\simulate)` 与调用清 `.press_(…)` 双零命中 | + +### 卡 1 批末选档跑闸读数(T7) + +`bash tools/run_all_gates.sh --code`(游戏码批选档)=**【验收通过】闸况与基线一致,无新增红**。回执六闸全绿:冒烟测试(smoke_boot.log)=绿/单元与集成测试(unit.log)=绿/系统测试(system_test.log)=绿/场景探针(probe.log)=绿/渲染闸(render.log)=绿/静态扫描(static_scan.log)=绿。改前改后对拍:改前基线=master 40a3ac12 远端 CI run 99 绿;改后=本跑批无新增红(闸况与基线一致)。 + +### 实机目检总清单(卡 1) + +| 目检项 | 登记处 | 义务口径 | +|---|---|---| +| 覆盖面板控件下钻停手键与停点链键在场 | 本表(数据面读数:probe_hits_ui_click.js 停手 1 次、停点链键在案) | 真人检测只登记不阻塞(AGENTS 红线)——登记即完成义务 | + +### 遗留读数义务(卡 1) + +- 无 MC/verifier 义务(本卡零数值零随机面)。 +- 远端 CI 现查读数(合回后):审计与追绿按 process-conventions §1 第 3 条按需现查。 + +### 实现决定记录(卡 1,超白名单事项备案) + +1. **reward_overlay_test.gd 五处行号就地校正**:卡面申报 :60/:64/:65/:66/:76,实查为 :63/:67/:68/:69/:79(漂移 +3,内容与描述全一致)——依卡内 T1 开工核对条款就地校正,未呈报(漂移远小于一屏)。 +2. **ui_click_probe 停手段与 system_test 行号就地校正**:停手触发/驱动 :1041-1045/:1063-1065 → 实查 :1051-1055/:1074-1077;system_test :433 → :440。同上依据。 +3. **排版帧插入**:三选一选卡→确认之间、reward_overlay_test 刷新/确认/继续点击前、journey _drive_curio 卡→确认之间插入 1–2 帧等待——确认钮/继续钮现形当帧容器重排在途,矩形未成形真点必落空。属驱动机制细节(白名单「使能窗等待时长、触发条件的具体写法」同族)。 +4. **ui_tune ⑤ sim 真点的折叠区滚轮通道**:初跑发现 sim 钮真点落空——根因=滚动列表 boot 滚位下钮矩形跨出 ScrollContainer 视口底边(hit 面被裁;emit 路径历史性掩盖)。按 test-conventions §2「折叠区控件先以真输入滚轮滚入可视区再点」落滚轮通道(一档滚入实证后定装),并配 12 档上限+滚入失败 problem 上报(正面效力自检)。⑧ 应用前置重开浮窗(同款机制先例=④ 重开浮窗)。属真输入通道的实现形态(卡片白名单「具体写法」族),随提交留痕。 +5. **候选/bcard 覆盖登记键的结构性缺口**:覆盖登记件自动登记通道=BaseButton pressed+营地自绘物件两源;地图画布与 BCard 非 BaseButton、无 pressed 信号,真输入命中不产键(登记件零改动——卡 1 §13 申报)。候选/弃图确认键的对照表括注预期键与登记件能力不符:效力由既有落点断言承载(R4 判据绑定面=既有落点断言逐条+键集不回退,两者均成立)。弃图确认链在 ui_click_probe 受 is_refresh_ready(RAP≥8)门槛四种子未触发——改前同门槛不可达,链面真输入效力由 journey 既有真点链承载。此为观测面局限申报,非缺陷。 +6. **T2 断言①的登记件读取形态**:`_stoptest_assert_hit` 经 `_recorder._node_keys`/`_recorder._hits` 直读登记件命中表(登记件为探针共用面、卡 1 §13 申报零改动——不新增公开读口,探针面读私有沿现役惯例:ap_bar._btn_stop 同型先例)。 +7. **卡 2 T4.4「等用户发令再走收口」与迭代单合并语义的冲突处置**:卡 2 写卡时为独立批(编入迭代 54 前),其末卡收口语义已被编排段换代为迭代级总收口(iter 技能第 5 步:一次合并、五步登记)。本批按迭代级收口执行,不逐卡等发令;此冲突为编排换代遗留,随本报告呈报。 + +## 卡 2|营地交互修复批——状态:进行中 + +(随卡 2 任务卡推进补齐。) + +## 卡 3|精英专属池——状态:待开工 + +(随卡 3 任务卡推进补齐。)