合并(写卡批·模态句柄分域·用户令): 需求卡片-模态句柄分域批-2026-11-02 v1.0 落 master——UiModals 平铺12句柄按域抽 run/menu 两子容器(营地六件先例不动)+ModalFlow 升格 UiRegistry 直驻+register_layer 注册面随迁, 行为零变化纯句柄搬家(类变量 14→3, 旧寻址形全仓零命中, 换代面529行钉file:line); grilling四轮零待裁点(开工门=U0)+代拍板白名单5项在卡; pipeline_gate PASS(44条目/48总表行/卡片6张)+capability_audit零红; README能力行新增「模态句柄分域寻址面」4行+既有「模态覆盖层编排与输入锁」2行换代+覆盖域表HUD框架行补名; 待办清单条目推进已写卡; 合回后post-merge钩子自动跑CI全量域闸, 回执后台挂ci_watch

This commit is contained in:
DKChan committed 2026-11-02 01:49:04 +08:00
commit 26f00af7a8
3 files changed
+258 -4

No files matched your search

+3 -2
View File
@@ -127,6 +127,7 @@ tests/
| 拒绝反馈分面可辨与未知兜底文案 | HUD框架 | `HudPresenter.outcome_text()` | `hud_presenter_test.gd:_test_reject_text_by_reason`;`hud_presenter_test.gd:_test_unknown_reason_fallback`;`hud_presenter_test.gd:_test_accepted_text` |
| 结算明细带逐行呈现与排播节奏 | HUD框架 | `BattleLogView.append_row()`/`RowPacer.pace_delay_for()` | `theme_projection_test.gd:_smoke_battle_log_view`;`player_copy_sweep_test.gd:_sweep_source_names`;`pacing_queue_test.gd:_init` |
| 模态覆盖层编排与输入锁 | HUD框架 | `ModalFlow.locks_input()`/`ModalFlow.open_after_playback()` | `modal_flow_test.gd:_init`;`reward_overlay_test.gd:_run` |
| 模态句柄分域寻址面 | HUD框架 | 局内/菜单/营地三子容器句柄经注册面合同寻址(`UiRegistry.node()` 逐键取层)+模态编排口开合(`ModalFlow.open()`/`ModalFlow.close()`) | `ui_struct_probe.gd:_modal_registry_contract`(registry 合同序段+分域句柄段)〔句柄分域批 V1〕 |
| 未开格类型微光(8 邻作用集/五组色相/零玩法写入) | HUD框架 | `BoardView.type_glow_probe()`/`UiKit.glow_group_for_cell_type()` | `theme_projection_test.gd:_smoke_type_glow`;`type_glow_test.gd:_test_grouping_closed`;`type_glow_test.gd:_test_frozen_hue_face` |
| 棋盘悬停反馈(三语义光标/目标格边框/路径预览点迹) | HUD框架 | `ui_input_probe` 合成鼠标事件→`BoardView._gui_input()`;读面 `BoardView.hover_probe()` | `ui_input_probe.gd:_ready`(case①到达②禁入③路径预览三语义) |
| UI 全局主题与按钮五态(含对比度锚) | HUD框架 | `UiKit.button()`/`UiKit.primary_button()` | `theme_projection_test.gd:_smoke_global_theme`;`theme_projection_test.gd:_smoke_button_five_states`;`theme_projection_test.gd:_smoke_contrast_inks` |
@@ -147,7 +148,7 @@ tests/
| 营地篝火回主菜单与笔记本改挂动线 | 营地交互流 | 笔记本命中区 → `CareerOverlay.open()`;篝火命中区 → ConfirmDialog 二次确认 → 主菜单 | `ui_struct_probe.gd:_ready`(career 段)〔营地图批 V5〕;`screen_flow_probe.gd:_ready`(篝火/开合段)〔营地图批 V7〕 |
| 营地图鉴占位挂起态与反参照结构纪律 | 营地场景视图 | 布告板命中区 → `CampSceneView._gui_input()`(object_pressed 信号) → 图鉴占位视图;营地根态场景树审计 | `ui_struct_probe.gd:_ready`(占位禁词/禁行段)〔营地图批 V6〕;`ui_struct_probe.gd:_ready`(结构禁词段)〔营地图批 V9〕 |
| 营地刷新单口与场景根 HUD 同步 | 营地刷新总线 | 写口尾 → `CampRefreshBus.refresh()` → 现开界面刷新+场景根 HUD 刷新 | `ui_struct_probe.gd:_ready`(顶栏位段)〔营地图批 V8〕;`ui_click_probe.gd:_ready`(写口尾刷新段) |
| Esc 浮窗唤起与三出口动线 | 营地 Esc 流 | 营地场景根态合成 Esc 键事件→ `main_esc_flow.on_esc_input()` → `ui.modals.flow.open/close`;浮窗三钮 `pressed` → 三出口 | `screen_flow_probe.gd:_ready`(Esc 唤起/子界面开态不唤起/再按关窗/非营地不唤起段)〔ESC批 V1〕;`screen_flow_probe.gd:_ready`(返回动线/设置双路径段)〔ESC批 V3/V4〕 |
| Esc 浮窗唤起与三出口动线 | 营地 Esc 流 | 营地场景根态合成 Esc 键事件→ `main_esc_flow.on_esc_input()` → `ui.flow.open/close`;浮窗三钮 `pressed` → 三出口 | `screen_flow_probe.gd:_ready`(Esc 唤起/子界面开态不唤起/再按关窗/非营地不唤起段)〔ESC批 V1〕;`screen_flow_probe.gd:_ready`(返回动线/设置双路径段)〔ESC批 V3/V4〕 |
| 营地顶栏 HUD 钥石读数与刷新单口同步 | 营地 HUD 栏 | 隔离存档实例 `meta_state._add_keystone()`→ 显式触发 `CampRefreshBus.refresh(camp_data)` → `camp_hud_band.refresh()` → `keystone_badge` 读数 | `screen_flow_probe.gd:_ready`(HUD 余额读数/刷新段)〔ESC批 V6〕;`ui_struct_probe.gd:_ready`(顶栏位结构段) |
**旅程段七:数据配置与跨系统契约**
@@ -184,7 +185,7 @@ tests/
| 排雷循环 | 排雷玩法.md | 装板与开局免费展开(封锁格无资源、计入清图);挖/封印合法性与可辨拒绝(拒绝零副作用);封印不可逆与挖开封印格放行(兑付改判四情况) | 已覆盖 | — |
| 排雷循环 | 棋盘生成.md | 棋盘按参数行出图、数字恒真;同参数同种子逐位复现、异种子不退化;可推理率与清图可达(合法性先于质量分)、重试有界、降级诚实 | 已覆盖 | — |
| 排雷循环 | 行动经济.md | AP 扣费、末点耗尽即收轮与战斗收束清零;停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励);下轮行动点修正排队与兑现 | 已覆盖 | — |
| 表现与支撑 | HUD框架.md | 拒绝反馈分面可辨与未知兜底文案;结算明细带逐行呈现与排播节奏;模态覆盖层编排与输入锁;未开格类型微光(8 邻作用集/五组色相/零玩法写入);UI 全局主题与按钮五态(含对比度锚);中文字体随包与字形覆盖;地形瓦片套与格面数字可读;战斗叙述与终局文案(数值带入/胜负互异);玩家面文案与危险格格面字形;战斗屏三区几何与棋盘格边长(分辨率两档×四档板型);B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役) | 已覆盖 | — |
| 表现与支撑 | HUD框架.md | 拒绝反馈分面可辨与未知兜底文案;结算明细带逐行呈现与排播节奏;模态覆盖层编排与输入锁;未开格类型微光(8 邻作用集/五组色相/零玩法写入);UI 全局主题与按钮五态(含对比度锚);中文字体随包与字形覆盖;地形瓦片套与格面数字可读;战斗叙述与终局文案(数值带入/胜负互异);玩家面文案与危险格格面字形;战斗屏三区几何与棋盘格边长(分辨率两档×四档板型);B 区舞台背景三关变体与连续地面层(按 board_type 选图/地面层高度读布局表/四柱光柱退役);模态句柄分域寻址面 | 已覆盖 | — |
| 表现与支撑 | 存档序列化.md | 存档写盘⇄读回往返保真;三槽位隔离与激活失败回滚;槽位摘要窥视与坏槽三态;启动态无激活不写盘;旧档迁移链与断点续玩零跳号(集成);存档⇄RNG 快照往返保真(集成) | 已覆盖 | — |
| 表现与支撑 | 教学引导.md | — | 未覆盖 | 教学形态与首遇时机待设计;见待办清单「教学形态未定」条 |
| 表现与支撑 | 数据配置.md | 全表装载、就绪态与表清单目录对账;进度坐标查行:精确命中/向上取整/封顶/警告去重;运行期越域致命与进程非 0 退出;加载期违规表拒绝:跨表悬空/计数器前缀/真视窄域;表结构守卫:域校验/行序键/跨字段互锁/配额锚 | 已覆盖 | — |
@@ -0,0 +1,253 @@
# 需求卡片——模态句柄分域批
> v1.0 · 2026-11-02 · 零待裁点(开工门=U0)
> 来源=待办清单条目「[模态句柄册分域重构](../待办清单.md)(2026-11-01 登记,来源=会话提及)」,状态已到「待写卡」
> 批性质=重构类需求性工程条目(行为零变化、纯句柄搬家);改动面=游戏代码
## 1. 目标与完成定义
**做什么**:把模态句柄册(`UiModals`)里按技术角色平铺的 12 个模态句柄按域分组抽成两只子容器——局内冒险域六件、菜单与跨语境域六件(营地六件已随迭代 47 卡⑥先例落 `UiCampModals`,不动);`ModalFlow` 编排件自 `UiModals` 升格为注册面(`UiRegistry`)直驻字段;模态登记口 `register_layer` 随登记职责迁驻注册面。全仓探针深员寻址、`ui_registry.gd node()` 合同寻址与 `main_ui_build.gd` 装配面随迁。
**什么状态算完**(逐条可核):
1. `UiModals` 类变量面=`run`/`menu`/`camp` 三只子容器,类变量 14→3,零平铺句柄残留;
2. `ModalFlow` 实例由 `UiRegistry` 构造并直驻(`ui.flow`),`UiModals` 无 `flow` 成员;`register_layer` 在 `UiRegistry`(函数体逐字迁移);
3. 全仓旧寻址形(`modals.flow.*` 与 `modals.<12 个平铺名>`)grep 零命中;营地二段式 `modals.camp.*` 与 `LAYER_CONTRACT` 合同表逐字不变;
4. 行为零变化成立——既有模态开合、输入锁、登记合同序断言全部保留原判据,仅寻址字面换代,改址后照跑全绿;
5. 新结构断言〔句柄分域批 V1〕落码(`ui_struct_probe` registry 段扩展)并在当轮真实跑批执行且通过;
6. `game/tests/README.md` 能力行两处落位(存量行入口字面换代+新增分域寻址行);
7. 实现报告与反哺点清单落 `reports/`(末卡 T2);合回 master 后由本地 CI 按改动域跑闸判定(非卡内人手义务)。
## 2. 设计事实源
三选一之第③种——跳过设计线的证据指认(重构类需求性工程条目,凭代码证据+在册条目):
1. **在册条目**:`待办清单.md`「模态句柄册分域重构(2026-11-01 登记,来源=会话提及)」,状态段「待写卡」,改动面=游戏代码;目标形态已在条目实况定稿(按域分组抽容器+ModalFlow 升格独立持有+行为零变化)。
2. **代码证据(2026-11-02 实测复核)**:`game/presentation/ui_modals.gd:7-20`——类变量 14(`flow`+12 平铺句柄+`camp` 子容器),与条目所记一致;迭代 47 卡⑥后营地域六句柄已迁 `game/presentation/ui_camp_modals.gd:4-12`,平铺面余 12 件。
3. **工程债事件沿革**:迭代 47 卡④⑤平铺句柄合计 19 越过 S13 类变量上限 15(停机呈报件已按「吸收即死」退役,`reports/` 现无此件;事件沿革见待办条目实况与 git 提交 a5c89d67)。阈值顶格只作发现信号,本卡的拆分动机是域归组(见 §5 契约 3)。
4. **拆分判据权威**:`design/engineering/code-conventions.md` §1.1 拆分判据;容器先例=`ui_camp_modals.gd`(迭代 47 卡⑥);注册面归属=`design/engineering/architecture/architecture.md:168`(ui_registry 行,v1.28)。
## 3. 现状事实(代码级实测,2026-11-02)
| # | 事实 | 证据(file:line) |
|---|---|---|
| 1 | `UiModals` 类变量 14:`flow`+12 平铺句柄+`camp` 子容器;`_init` 建 `flow` 与 `camp` | `ui_modals.gd:7-20,23-25` |
| 2 | 模态登记唯一口 `register_layer` 在 `UiModals`(锚预设撑满+`flow.register`) | `ui_modals.gd:28-33` |
| 3 | 营地域子容器先例:`UiCampModals` 纯字段六句柄;头注带「禁平铺回 ui_modals」分域纪律 | `ui_camp_modals.gd:1-12` |
| 4 | 注册面持三域容器(screens/battle/modals),`_init` 逐只构造 | `ui_registry.gd:42-50` |
| 5 | `node()` 合同寻址 22 键:12 平铺键直指 `modals.<名>`,营地六键两段式 `modals.camp.*` | `ui_registry.gd:54-80`(平铺 59-64,72-78;营地 66-71) |
| 6 | `apply_layer_contract` 三处调 `modals.register_layer`;`LAYER_CONTRACT` 合同表 24 项(含 tags/host/shell 特例) | `ui_registry.gd:15-40,86-111`(调用点 97/106/111) |
| 7 | `ModalFlow` 纯逻辑类:依赖仅 ModalStack+查询 Callable,零节点构建零信号连接;类本体与本批无关 | `modal_flow.gd:1-15` |
| 8 | 装配面寻址:12 平铺句柄命中 50 行+`flow` 注入 14 行(Callable 注入段与 early 取 `flow.pump_ended`);营地二段式 28 行不动 | `main_ui_build.gd:49,60,89-92,99-158,168-174,189-218,232-253,269-286` |
| 9 | 组合根与四拆件寻址(命中行含注释):main 27;四拆件 38/43/13/8 | `main.gd:94,107-765`;`main_adventure_flow.gd`/`main_overlay_flow.gd`/`main_debug_flow.gd`/`main_esc_flow.gd` |
| 10 | 全仓 `modals.` 寻址 25 文件 595 行:营地二段式 116 行(本批不动);`flow` 87 行;12 平铺句柄 442 行(两者去重唯一行 482) | 复核命令与分文件计数表见 §8 结构图注(口径=`grep -c` 命中行数,含注释行) |
| 11 | 测试与探针消费面 15 件:探针 11+系统测试+冒烟 2 行+单元 2 行;registry 合同断言段钉登记册序 | `ui_struct_probe.gd:562-580,584-606`;`system_test.gd:108-122,316,384-387,491`;`smoke_boot.gd:74-75`;`pacing_queue_test.gd:61`;`theme_projection_test.gd:719` |
| 12 | S13 类变量默认上限 15;基线文件 12 行无本批触碰文件键(14 与 3 均未越限) | `tools/gdstyle.toml`(阈值取默认);`tools/gdstyle_baseline.txt:1-12` |
## 4. 目标结构与域归组
```
main.ui(UiRegistry,类变量 3→4)
├─ screens: UiScreens(9,不动)
├─ battle: UiBattle(5,不动)
├─ modals: UiModals(类变量 14→3,转纯字段容器)
│ ├─ run: UiRunModals(新件,6 句柄=局内冒险域)
│ ├─ menu: UiMenuModals(新件,6 句柄=菜单与跨语境域)
│ └─ camp: UiCampModals(6,不动)
└─ flow: ModalFlow(升格直驻注册面;register_layer 随迁)
```
12 只平铺句柄的域归属与判据(每句柄以真实入口证据归域,非按数量凑袋):
| 句柄 | 现寻址(平铺) | 目标寻址 | 域归属判据(真实入口) |
|---|---|---|---|
| legend_overlay | `modals.legend_overlay` | `modals.run.legend_overlay` | 局内——战斗中图例,棋盘角标触发(`main_ui_build.gd:77` → `main_overlay_flow.gd:184-187`) |
| node_overlay | `modals.node_overlay` | `modals.run.node_overlay` | 局内——节点结算窗(`main_adventure_flow.gd:73-74`) |
| level_screen | `modals.level_screen` | `modals.run.level_screen` | 局内——关卡地图屏(`main_adventure_flow.gd:206`;tags=screen_state `ui_registry.gd:21`) |
| curio_overlay | `modals.curio_overlay` | `modals.run.curio_overlay` | 局内——奖励三选一(`main_ui_build.gd:157,172-174`) |
| end_overlay | `modals.end_overlay` | `modals.run.end_overlay` | 局内——终局面板(`main.gd:290-299`) |
| status_panel | `modals.status_panel` | `modals.run.status_panel` | 局内——状态面板,地图/棋盘两角标入口(`main_ui_build.gd:126`、`main_overlay_flow.gd:32`) |
| shop_overlay | `modals.shop_overlay` | `modals.menu.shop_overlay` | 跨语境——冒险商店节点(`main_adventure_flow.gd:80-87`)+营地商店(`camp_shop_presenter.gd:10`) |
| settings_overlay | `modals.settings_overlay` | `modals.menu.settings_overlay` | 跨语境——开始层(`main_overlay_flow.gd:9`)+营地 Esc 双路径(`main_esc_flow.gd:51`) |
| career_overlay | `modals.career_overlay` | `modals.menu.career_overlay` | 跨语境——开始层笔记本+营地语境(`main_overlay_flow.gd:42,47`) |
| save_select_overlay | `modals.save_select_overlay` | `modals.menu.save_select_overlay` | 跨语境——开始层+营地大门流(`main_overlay_flow.gd:73,96,128`) |
| confirm_dialog | `modals.confirm_dialog` | `modals.menu.confirm_dialog` | 全语境——确认框(`main.gd:129,147,156`、`main_overlay_flow.gd:73,79,83,128`) |
| debug_overlay | `modals.debug_overlay` | `modals.menu.debug_overlay` | 全语境——调试浮窗(non_locking,`ui_registry.gd:37`;`main_debug_flow.gd` 全件) |
寻址合同换代三形:`ui.modals.flow.X` → `ui.flow.X`;`ui.modals.<平铺名>` → `ui.modals.run.<名>` 或 `ui.modals.menu.<名>`;`ui.modals.camp.*` 不变。
## 5. 设计契约(红线条款,违反即返工;🔴=触碰即 L3 停机呈报)
1. 🔴 **行为零变化**:装配序、登记序、树序、模态开合语义、输入锁谓词、遮挡记账、T17 演播后排窗挂起逐位不变;`LAYER_CONTRACT` 合同表零改动;`ModalFlow`/`ModalStack` 类本体零改动(搬家不改性)。判据=§10 对照表 R4/R5 行既有断言原判据照跑全绿。
2. 🔴 **分域纪律册级化**:任何模态句柄禁平铺回 `UiModals`(营地先例条款 `ui_camp_modals.gd:3` 推广为全册纪律);`UiModals` 终态=三子容器纯字段,与 `UiScreens`/`UiBattle` 同型。
3. 🔴 **拆分边界纪律**(条款在卡,判据权威=`code-conventions.md` §1.1 拆分判据,不复制定文):①每个拆出单元须有可陈述的职责语句与单一变化原因(本卡两新件的职责语句见 §8 结构图,实现报告逐件一行承载);②拆出单元须有集体身份——命不出名字、只能说「剩下的成员」的打包=凑计数袋,禁;③「过阈值」(S13 类变量 14 顶格 15)只许作为发现信号,作为拆分动机即违规——本卡动机=域归组(营地先例的册级推广),阈值顶格是发现信号;④按纪律拆不出内聚单元→停卡呈报走豁免/还债通道,禁硬拆凑绿(进 §11 L3)。
4. 🔴 **Core 层零 UI 依赖不因新容器破坏**:两新件=RefCounted 纯句柄容器,不挂树、零信号连接、零核心写调用(与既有四容器同型);`--headless --script` 全链不受影响。
5. **构造时序不变**:`flow` 改由 `UiRegistry._init` 构造——注册面构造点 `main.gd:94` 先于 `_build_ui`,装配早期注入点(`main_ui_build.gd:60` 取 `flow.pump_ended`)依赖其先在座的时序前提逐位保持。
6. **数值纪律**:本卡零数值面;禁数值字面量进代码方案。
7. **命名随仓库法**:新件类名对齐 `UiCampModals` 命名法;子容器成员名沿用原平铺名逐字(12 个句柄零改名——纯句柄搬家)。
## 6. 范围边界
**做**:§4 目标结构全部;全仓寻址换代(探针 11 件+测试 4 件+产品码 8 件);新结构断言落码;README 能力行两处。
**不做(明示去向)**:
| 不收件 | 去向 |
|---|---|
| CAMP_HEADER_BAND 令牌退役 | 另一待办条目(归 ui_kit 令牌批顺带),本卡不收 |
| 任何行为变化(开合时序/遮挡/锁语义/登记序/树序/文案/交互) | 本卡范围外;发现即 L3 停机呈报 |
| `UiCampModals` 再拆分或营地六句柄改名 | 先例已立且职责单一,不动 |
| `ModalFlow`/`ModalStack` 职责重划或类体改写 | 本卡只搬家;重划属新需求 |
| 测量件(`game/sim/`)适配 | 实测零 `modals.` 寻址(§3 表#10 复核命令可验),零触碰 |
| `architecture.md` ui_registry/UiCampModals/模态编排三处记述换代 | 反哺点清单(§7),文档批落文 |
## 7. 关联文档同步面
| 文档 | 关联点(本批实现会改变它的哪条陈述) | 同步义务 |
|---|---|---|
| design/engineering/architecture/architecture.md | :168 ui_registry 行——「三域句柄容器…+ModalFlow」「四件类变量 3/9/5/14,ModalFlow 随 UiModals 容器构造即建」→ 换代为三域容器+两新子容器+flow 直驻注册面+类变量实数(UiRegistry 3→4、UiModals 14→3);:169 UiCampModals 行——「ui_modals 平铺面收窄」可现状化为平铺面清零;:200——「ModalFlow 实例随 UiModals 容器构造建立」→ 随 UiRegistry 构造建立 | 列反哺点清单由文档批落文(实现批禁写 design/) |
| game/tests/README.md | 能力行两处:存量行「Esc 浮窗唤起与三出口动线」真实入口字面 `ui.modals.flow.open/close`→`ui.flow.open/close`;新增「模态句柄分域寻址面」行+覆盖域表 HUD框架 行随补 | 写卡段同批落位(本卡随批写入,非实现批义务) |
| design/gdd/术语表.md | 无新术语——「分域」「子容器」「注册面」均为仓库既有说法 | 无关联文档 |
## 8. 文件结构图(卡前)
| 文件 | 新建/修改 | 职责(职责语句) | 行为改动 |
|---|---|---|---|
| game/presentation/ui_run_modals.gd | 新建 | 局内冒险域六句柄唯一容器——冒险会话内开合的模态呈现面句柄册(纯字段 RefCounted) | 行为改动(寻址合同换代)→对照表 R1 |
| game/presentation/ui_menu_modals.gd | 新建 | 菜单与跨语境域六句柄唯一容器——菜单层与多语境共用模态的句柄册(纯字段 RefCounted) | 行为改动(寻址合同换代)→对照表 R2 |
| game/presentation/ui_modals.gd | 修改 | 模态层域句柄册转纯字段容器:三子容器 run/menu/camp;`register_layer` 与 `flow` 成员迁出 | 行为改动→对照表 R3 |
| game/presentation/ui_registry.gd | 修改 | 注册面增持 `flow` 编排件与 `register_layer` 登记口;`node()` 十二键换代两段式 | 行为改动→对照表 R3/R5 |
| game/presentation/main_ui_build.gd | 修改 | 装配面寻址换代(句柄赋值/接线两段式 50 行;`flow` 取 `ui.flow` 14 行);装配序零变化 | 行为改动→对照表 R4 |
| game/presentation/main.gd | 修改 | 组合根寻址换代(27 行,含头注) | 行为改动→对照表 R4 |
| game/presentation/main_adventure_flow.gd / main_overlay_flow.gd / main_esc_flow.gd / main_debug_flow.gd | 修改 | 四拆件寻址换代(38/43/8/13 行) | 行为改动→对照表 R4 |
| game/tests/smoke_boot.gd | 修改 | 装配检查两行寻址换代 | 非行为改动(测试面同步)——由 R4 覆盖 |
| game/tests/system_test.gd / unit/presentation/pacing_queue_test.gd / unit/presentation/theme_projection_test.gd | 修改 | 测试面寻址换代;theme_projection 源码字面断言期望串换代(判据语义不变) | 非行为改动(测试面同步)——由 R4 覆盖,字面换代申报见 §10 |
| game/probe/ 下 11 件(fx_shot/layout/probe_ui_util/screen_flow/shot/t17_defer/ui_click/ui_input/ui_probe/ui_struct/ui_tune) | 修改 | 探针深员寻址换代;ui_struct_probe registry 段增结构断言 | 非行为改动+结构断言新增→对照表 R1/R2/R3/R5 |
| game/tests/README.md | 修改 | 能力行两处落位(存量行换代+新增行+覆盖域表 HUD框架 行补名) | 非行为改动(写卡段义务,随本卡入库) |
分文件寻址计数表(2026-11-02 实测,`grep -c` 命中行数、含注释行;实现复核用同一命令):main.gd flow=10 flat=17;main_adventure_flow flow=9 flat=29;main_debug_flow flow=4 flat=9;main_esc_flow flow=5 flat=3;main_overlay_flow flow=16 flat=27;main_ui_build flow=14 flat=50;ui_registry flat=12(node() 键行);ui_modals flow=1(头注);探针 11 件 flat=271 flow=27;测试 4 件 flat=24 flow=1。合计换代命中行=flat 442+flow 87=529(唯一行 482)。
## 9. 任务卡 T0–T2
估时:T0 0.5–1.0 工作日;T1 0.5–1.0 工作日;T2 0.25–0.5 工作日;合计中值 ≈1.9 个工作日(编排段汇总用)。
### T0 抽容器与注册面换代(产品码+冒烟测试件)
**取用**:无(本卡首卡)。**产出**(T1 起引用,名字逐字):寻址合同三形 `ui.flow.<编排口>`(open/close/open_after_playback/locks_input/badge_greyed_map/badge_greyed_battle/register 入 stack 不变)、`ui.modals.run.<六名>`、`ui.modals.menu.<六名>`;新类名 `UiRunModals`/`UiMenuModals`。
- [ ] 新建 `game/presentation/ui_run_modals.gd`:`class_name UiRunModals extends RefCounted`,纯字段六枚全类型标注 `var legend_overlay: LegendOverlay`/`var node_overlay: NodeOverlay`/`var level_screen: LevelMapScreen`/`var curio_overlay: CurioPickOverlay`/`var end_overlay: BattleEndOverlay`/`var status_panel: StatusPanel`;头注=职责语句(局内冒险域六句柄唯一容器——与 §8 结构图逐字同句)
- [ ] 新建 `game/presentation/ui_menu_modals.gd`:`class_name UiMenuModals extends RefCounted`,纯字段六枚全类型标注 `var shop_overlay: ShopOverlay`/`var settings_overlay: SettingsOverlay`/`var career_overlay: CareerOverlay`/`var save_select_overlay: SaveSelectOverlay`/`var confirm_dialog: ConfirmDialog`/`var debug_overlay: DebugOverlay`;头注=职责语句(菜单与跨语境域六句柄唯一容器)
- [ ] 改 `game/presentation/ui_modals.gd`:删 `flow` 与 12 平铺句柄成员,增 `var run: UiRunModals`/`var menu: UiMenuModals`(`camp` 保留);`_init` 改建 run/menu/camp 三容器;`register_layer` 函数体(含锚预设纪律注释)迁出;头注换代为纯字段容器+三子容器+分域纪律全册化句
- [ ] 改 `game/presentation/ui_registry.gd`:增 `var flow: ModalFlow`,`_init` 增建(构造时点与今等价——注册面先于装配);`register_layer` 自 `ui_modals.gd:28-33` 逐字迁入(含注释);`apply_layer_contract` 三处 `modals.register_layer` 改直调;`node()` 十二平铺键换代 `modals.run.*`/`modals.menu.*`(营地六键不动);头注「三域容器」句换代
- [ ] 改 `game/presentation/main_ui_build.gd`:句柄赋值与接线换代两段式(计数表 flat=50);`flow` 注入与取用改 `root.ui.flow.*`(flow=14::60、:168-169、:201、:269-286);:273 注释「实例随 ui.modals 容器构造建立」句随迁
- [ ] 改 `game/presentation/main.gd`:寻址换代(计数表 flat 17+flow 10,含 :4 头注「模态开合只走 ui.modals.flow」句改 `ui.flow`);:46 持有面注释核对(注册面单点宿主句仍成立,不改)
- [ ] 改四拆件 `main_adventure_flow.gd`/`main_overlay_flow.gd`/`main_esc_flow.gd`/`main_debug_flow.gd`:寻址换代 38/43/8/13 行(flat+flow 合计,逐件 grep 该文件旧形零命中)
- [ ] 改 `game/tests/smoke_boot.gd:74-75`:`modals.legend_overlay`→`modals.run.legend_overlay`;`modals.status_panel`→`modals.run.status_panel`
- [ ] 验证①(旧形零残留):`grep -rnE "modals\.flow|modals\.(legend_overlay|node_overlay|level_screen|curio_overlay|end_overlay|shop_overlay|settings_overlay|status_panel|career_overlay|save_select_overlay|confirm_dialog|debug_overlay)" --include="*.gd" game` =零命中;正检三形在用:`grep -rc "modals\.run\.\|modals\.menu\.\|ui\.flow\."` 各 >0
- [ ] 验证②(真实开机路径):`python tools/run_smoke.py` PASS(真实装配走新寻址——闸 00 最窄确认)
- [ ] 验证③(静态纪律):`bash game/tests/static_scan.sh` 零 FAIL(S9 新件全类型标注;S13 无新键——预期 `ui_modals` 类变量 14→3、`ui_registry` 3→4 均在上限内不产键);跑闸前先 `--editor --quit` 刷类名缓存(新 class_name 生效前提);新件 `.uid` 边车随扫描生成、随批入库(存量 80 件 presentation `.uid` 均在 git,同法)
### T1 测试与探针面换代+结构断言落码
**取用**:T0 的寻址合同三形与 12 句柄归属表(§4)。**产出**:结构断言〔句柄分域批 V1〕;测试面全量新寻址。
- [ ] 探针 11 件机械改址(分文件计数表:fx_shot flat=3;layout flat=2;probe_ui_util flat=1;screen_flow flat=19 flow=0;shot flat=20;t17_defer flat=6 flow=1;ui_click flat=109 flow=14;ui_input flat=13 flow=4;ui_probe flat=12 flow=1;ui_struct flat=54 flow=2;ui_tune flat=32 flow=5)——判据零改动,只换代址字面
- [ ] `game/tests/system_test.gd` 改址(flat=20 flow=1::108-122 句柄清单数组、:316 `var mf = main.ui.flow`、:384-387、:491 终局弹起等);`pacing_queue_test.gd:61` 改址
- [ ] `game/tests/unit/presentation/theme_projection_test.gd` 改址+:719 源码字面断言期望串换代:`"ui.modals.debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)"` → `"ui.menu.debug_overlay.diagnose_requested.connect(_on_diagnose_pressed)"`(判据语义不变——申报见 §10)
- [ ] `ui_struct_probe.gd` `_modal_registry_contract` 段尾增结构断言块,尾缀〔句柄分域批 V1〕,三条判据:**V1a 分域在座**——run 六件经 `main.ui.modals.run.<名>` 逐件非 null、menu 六件经 `main.ui.modals.menu.<名>` 逐件非 null(12 检);**V1b 平铺面清零**——`main.ui.modals.get(<旧平铺名>)` 12 件逐件为 null(旧寻址成员不存在);**V1c 编排口独立**——`main.ui.flow` 非 null 且 `main.ui.modals.get("flow")` 为 null
- [ ] 验证①(探针层):`godot --headless --path game res://probe/ui_struct_probe.tscn` 单跑零 problems(先孤立验证新断言);`bash tools/probe_gate.sh --full` 探针族绿
- [ ] 验证②(系统层):`python tools/run_system_test.py` PASS
- [ ] 验证③(单元层):工作目录 game/ 跑 `godot --headless --script tests/run_all.gd -- --only=presentation` 绿(开发期窄测形态,不产闸回执)
- [ ] 里程碑步出口跑一次代码域闸(`bash tools/run_all_gates.sh --code`);波内修复只做出红最小单元的最窄确认
### T2 产出物落位卡
**取用**:T0/T1 全部落地事实。**产出**:批收口两件+收尾核对。
- [ ] 写 `reports/iter<N>-实现报告-<日期>.md`:各卡状态+实机目检总清单(本批行为零变化,零新增目检项——登记「模态开合走查由既有探针族覆盖」一句)+实现决定记录(零拍板或逐条:决了什么/为什么/代价/撤销方式)+实际改动—验收对照表(逐行补测试文件:行)
- [ ] 写 `reports/iter<N>-反哺点清单-<日期>.md`:§7 同步面所列 architecture.md 三处(:168/:169/:200)逐条写清哪一节、改成什么、依据是什么
- [ ] 收尾核对:`game/tests/README.md` 能力行名字与实际落地一致(新增行代表用例 `ui_struct_probe.gd:_modal_registry_contract` 实名核对;改名则回表)
- [ ] 合回后由 post-merge 钩子自动跑本地 CI 按域闸(非卡内人手义务);红项走修复 worktree 追绿
## 10. 验证闸(改动—验收对照表)
判据口径:每行断言从真实入口进入、绑被测改动特有的可观测提交面、在当轮真实跑批中执行并通过;禁变异类测试与撤销类取证。断言尾缀新增统一〔句柄分域批 V1〕;既有断言尾缀保留原样(装配生命批 V1/ESC批 V1/注册面 V2 等)。
| # | 需求点 | 改动(文件:行/行为) | 验收测试(名称+落点+尾缀) | 层级 | 真实入口 | 判据绑定面 | 能力行 |
|---|---|---|---|---|---|---|---|
| R1 | 局内冒险域六件抽容器 | `ui_run_modals.gd` 新建;`ui_modals.gd:7-20` 六件成员迁出;`ui_registry.gd:59-64,72-78` 中六键换代;装配面六件赋值换代 | `ui_struct_probe.gd:_modal_registry_contract` V1a run 六件逐件非 null〔句柄分域批 V1〕 | 探针 | 真实实例化主场景 `_ready` 装配→registry 合同段 | 注册面新寻址取值面(`modals.run.*` 六句柄在座) | 新增:模态句柄分域寻址面 |
| R2 | 菜单与跨语境域六件抽容器 | `ui_menu_modals.gd` 新建;`ui_modals.gd` 六件成员迁出;`ui_registry.gd` 六键换代;装配面六件赋值/接线换代 | 同段 V1a menu 六件逐件非 null〔句柄分域批 V1〕 | 探针 | 同上 | 注册面新寻址取值面(`modals.menu.*` 六句柄在座) | 新增:模态句柄分域寻址面 |
| R3 | 平铺面清零+编排口升格独立持有+登记口迁驻 | `ui_modals.gd:7,23-33`(flow/register_layer 迁出);`ui_registry.gd:42-50`(增 flow+_init 建)、:28-33 迁入 register_layer | 同段 V1b 12 件 get() 逐件 null+V1c flow 在注册面且 modals 无 flow〔句柄分域批 V1〕;S13 静态判据(类变量 14→3 无新键,`bash game/tests/static_scan.sh`) | 探针+静态 | 同上 | 旧平铺成员缺位+`ui.flow` 在座(被测改动的特有提交面) | 新增:模态句柄分域寻址面 |
| R4 | 全仓寻址换代行为零变化 | 装配面 `main_ui_build.gd`(flat=50/flow=14)+`main.gd` 27 行+四拆件 102 行+`ui_registry.gd node()` 12 键;测试探针 15 件改址 | 既有断言群改址照跑(原判据):`system_test.gd` V3 终局面板弹起(:491)与模态锁段(:316 `locks_input` 矩阵);`ui_struct_probe.gd` node_overlay 开合段(:251-262);`screen_flow_probe.gd` Esc 三动线段;`ui_click_probe.gd` 各模态交互段;`ui_tune_probe.gd` 调试浮窗段;`smoke_boot.gd:74-75` 装配两检 | 探针+系统+冒烟 | 真实开机路径→真实输入/信号路径(模态经 `ui.flow.open/close` 开合) | 模态可见性与信号开合面(既有断言原样保留,见保留声明) | 既有:模态覆盖层编排与输入锁 |
| R5 | 登记册合同序不破 | `ui_registry.gd:86-111` apply_layer_contract 直调 register_layer;登记序/树序数据零改动 | `ui_struct_probe.gd:_modal_registry_contract` ①②③④段(登记册序==合同序逐位、树序邻接对、宿住/同槽特例)改址照跑〔装配生命批 V1〕 | 探针 | 同上 | 登记册栈序==LAYER_CONTRACT 序逐位 | 新增:模态句柄分域寻址面 |
| R6 | 诊断转储入口接线断言字面换代 | `theme_projection_test.gd:719` 期望串换代(`ui.modals.debug_overlay.`→`ui.menu.debug_overlay.`) | 该断言照跑绿(V6 段,`run_all` presentation 套件;由 R4 之系统与单元跑批同轮执行) | 单元 | 套件直跑(`tests/run_all.gd`) | 组合根接线源码字面(既有源码锚定断言形态) | 既有:模态覆盖层编排与输入锁 |
非行为改动覆盖注记:各文件头注/注释随址换代、README 能力行两处=非行为改动,其装配与开合后果由 R4 行断言从真实入口承载;R6 为测试侧字面换代(判据语义不变),单独列行申报。合同表零改动(红线 1)的判据为静态——`LAYER_CONTRACT` 表常量无运行期可观测面(登记册序断言以表为参照系,表随登记同动时不互证),由独立验收审计核对 diff 中该数据块零行变更(观测面局限申报,非撤销类取证)。
**测试削弱申报**(允许的旧断言改动,全部为字面换代、零判据削弱):①`theme_projection_test.gd:719` 期望串换代(R6);②全部既有模态断言的寻址字面换代(R4/R5 所列段)——判据、阈值、真实入口全部保留;零删除断言、零放宽阈值。除上述外任何断言削弱=上游缺陷,停卡退回写卡段。
**闸口径**:本批改动含 `game/` 行为载体→代码域闸;合回后本地 CI 为唯一强制验证点(判读归 CI 回执,卡内不重复人手跑)。回归措辞按模板:里程碑步(T 段)出口跑一次全套闸;波内修复只做出红最小单元的最窄确认;新增断言先行孤立验证再并入全套(T1 验证①已含)。
**测试方式两分法**:本批全部归能力验证(自动化断言);零新体验面,用户目检零新增项(T2 登记句即了结义务,登记不阻塞)。
**独立验收审计**:合回前由未参与实现的会话或子代理执行,报告落 `reports/iter<N>-验收审计-<日期>.md`;审计至少核对对照表六行两端对上、真实入口未绕过改动点、判据绑定面钉在寻址合同与开合面上;未过不得合并。
## 11. 异常分级
- **L1 自救**:改址漏点(编译/寻址报错指名文件行)——按 §8 计数表补点后重跑该件所在最窄验证。
- **L2 记录继续**:注释/头注换代引发的 S11 注释禁词误报——按扫描面豁免通道核对后记录实现报告;探针偶发超时——单件重跑一次并记录。
- **L3 停机写报告等用户**(逐条):①行为零变化破口(任何开合/锁/序差异);②拆不出内聚单元(契约 3④);③**机检预算互斥=上游缺陷停卡退回写卡段**——S13/S9 键与卡片契约互斥、或需保留无效成员才能过闸时,禁自造第三条路;④判据绑不上(R3 结构断言若造不出可观测面)=静态判据+评审注记申报观测面局限,禁自造注入破坏或回退 hunk 取证。
## 12. 代拍板白名单(授权 agent 实现期自行拍板的微决策)
1. 分组命名:子容器字段名 `run`/`menu`,类名 `UiRunModals`/`UiMenuModals`(对齐 `UiCampModals` 命名法;「局内冒险域」「菜单与跨语境域」两职责语句随件入头注)。
2. 子容器成员名沿用原平铺名逐字(12 个句柄零改名)。
3. `ModalFlow` 独立持有的落点=`UiRegistry` 直驻字段 `flow`:注册面是寻址与登记的单点宿主,构造时点与今等价;备选「main 直持」被否——破坏 `main.ui.<域容器>.<句柄>` 单点寻址面与注册面门面定位。
4. `register_layer` 随登记职责迁驻 `UiRegistry`(`UiModals` 转纯字段容器,与 `UiScreens`/`UiBattle` 同型;登记口语义逐字不变)。
5. 注释与头注随寻址换代等长改写(零语义新增,禁变更叙事)。
超白名单事项记入实现报告「实现决定记录」节备案,不另立追认单。
## 13. 待裁点
**零待裁点,开工门=U0。** 目标形态(分组抽容器+flow 升格独立持有+行为零变化)已在待办条目实况定稿;命名与落点微决策已授权(§12);本卡不触碰用户设计权保留面(无玩法、无文案、无数值、无美术)。
## 14. 落位与可复用性预检
**新增文件两枚**(`ui_run_modals.gd`/`ui_menu_modals.gd`):
- **复用面**:grep 全仓无同职责件——`UiCampModals` 只收营地域六句柄(句柄集不相交,`ui_camp_modals.gd:1-12`),`UiScreens` 收屏壳与全屏态、`UiBattle` 收战区视图(`ui_screens.gd`/`ui_battle.gd` 头注自陈域界)——把 run/menu 域句柄塞进任何一只既有容器=域错位,直接违反分域纪律(§5 契约 2);确需新增的理由成立。
- **唯一面**:类名 `UiRunModals`/`UiMenuModals` 与字段名 `run`/`menu` 全仓 grep 零命中(盘上无第二处同名/同义件);12 只句柄各自唯一落点=自 `ui_modals.gd` 迁入而非复制(迁移后旧成员已删,`get()` 判 null 有断言钉住——R3)。
- **分层与交互面**:两新件被 `UiModals._init` 构造、被 `main_ui_build` 装配赋值、被 `ui_registry.node()` 寻址——调用路径与既有三容器同型(`UiRegistry._init` → 容器构造;装配写句柄;寻址读句柄);RefCounted 不挂树、零信号、零核心触碰,Core 层零 UI 依赖与 RngService/ConfigDB 红线零接触(本批零随机、零数值)。
**新增函数一件**:`UiRegistry.register_layer`——自存量逐字迁移(非新逻辑),对账见 §15。
**新增数据件**:无。
## 15. 实现规范预算
对照 `tools/gdstyle.toml` 默认口径(gdstyle v0.4.0:公开方法 ≤20、类变量 ≤15、单函数分支 ≤8、返回 ≤6;基线=`tools/gdstyle_baseline.txt`):
**新件三查**:
| 新件 | 公开方法 | 类变量 | 迁移函数对账 |
|---|---|---|---|
| ui_run_modals.gd | 0(纯字段,≤20 ✓) | 6(≤15 ✓) | 无迁入函数 |
| ui_menu_modals.gd | 0(纯字段,≤20 ✓) | 6(≤15 ✓) | 无迁入函数 |
**存量触面一查**(预期与手段):
| 存量件 | 键现状 | 预期 | 手段 |
|---|---|---|---|
| ui_modals.gd | 基线无键(14 未越限) | 类变量 14→3、公开方法 1→0——只降不升 | 成员迁出后即删,禁留别名(§16) |
| ui_registry.gd | 基线无键(3 未越限) | 类变量 3→4、公开方法 2→3——均在上限内不产新键 | register_layer 迁入为唯一增量 |
| main_ui_build.gd | 基线无键 | 行数 ±0(改址等长)、函数面零增 | 只换代址字面与随迁注释 |
| main.gd 与四拆件 | 基线无键 | 零新成员、零新函数 | 只换代址字面 |
**迁移函数对账**:`register_layer`(源=`ui_modals.gd:28-33`,函数体 5 行、零分支、单返回)——`gdstyle_baseline.txt` 无源函数键,branches/returns 均在默认值内,迁移即原样、无需拆函数。
**指标例外申报**:无。
## 16. 禁为机检文本保留无效成员(重构类卡必填条款)
禁止为迎合机检消息文本(含计数)而保留或新增无效成员/别名——`UiModals` 迁出的 13 个成员(12 句柄+flow)不设转发属性、不设 get 兜底、不保留空壳 var;`ui_registry.node()` 十二键直接指向新两段式寻址,不留旧键双轨。机检键与卡片契约互斥时,实现段一律停卡呈报(§11 L3③),不得自造第三条路。
+2 -2
View File
@@ -439,8 +439,8 @@
#### 模态句柄册分域重构(2026-11-01 登记,来源=会话提及)
**实况**:重构证据=`game/presentation/ui_modals.gd:7-20`(类变量 14 实测)+停机呈报-迭代47机检预算互斥-2026-11-01.md(卡④⑤ 平铺合计 19>S13 上限 15)——按技术角色平铺 13 模态句柄+flow,迭代 47 营地域新增六句柄顶格 S13 类变量上限 15(2026-11-01 用户裁:最小改动先走——营地六句柄落 `ui_camp_modals.gd` 子容器,随迭代 47 卡⑥/卡④/卡⑤ 落地,本条只登记**全量分家**)。目标形态=存量句柄按域分组抽容器(局内 run 六件/菜单与跨语境归组)+ModalFlow 自 UiModals 升格独立持有;行为零变化、纯句柄搬家;返工面=全仓探针深员寻址+`ui_registry.gd node()` 寻址+`main_ui_build.gd` 装配。拆分判据权威=code-conventions §1.1(沿职责轴;营地子容器先例=`ui_camp_modals.gd`,随迭代 47 落地后为准)。
**实况**:重构证据=`game/presentation/ui_modals.gd:7-20`(类变量 14 实测)+停机呈报-迭代47机检预算互斥-2026-11-01.md(卡④⑤ 平铺合计 19>S13 上限 15)——按技术角色平铺 13 模态句柄+flow,迭代 47 营地域新增六句柄顶格 S13 类变量上限 15(2026-11-01 用户裁:最小改动先走——营地六句柄落 `ui_camp_modals.gd` 子容器,随迭代 47 卡⑥/卡④/卡⑤ 落地,本条只登记**全量分家**)。目标形态=存量句柄按域分组抽容器(局内 run 六件/菜单与跨语境归组)+ModalFlow 自 UiModals 升格独立持有;行为零变化、纯句柄搬家;返工面=全仓探针深员寻址+`ui_registry.gd node()` 寻址+`main_ui_build.gd` 装配。拆分判据权威=code-conventions §1.1(沿职责轴;营地子容器先例=`ui_camp_modals.gd`,随迭代 47 落地后为准)。需求卡片:reports/需求卡片-模态句柄分域批-2026-11-02.md(2026-11-02 写卡会话落位,零待裁点,开工门=U0)。
**改动面**:游戏代码
**状态**:待写卡
**状态**:已写卡