From 331a0ba688893a93c9452344fd5811945f698b87 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Thu, 22 Oct 2026 21:16:24 +0800 Subject: [PATCH] =?UTF-8?q?docs(=E6=8F=8F=E8=BF=B0=E6=80=A7=E6=96=87?= =?UTF-8?q?=E6=A1=A3):=20=E6=89=B9=20A=20=E6=9C=AF=E8=AF=AD=E4=B8=8E?= =?UTF-8?q?=E8=AF=BB=E6=95=B0=E6=8D=A2=E4=BB=A3=E2=80=94=E2=80=94tests/REA?= =?UTF-8?q?DME=20=E6=9C=AF=E8=AF=AD=E8=A1=A8=E6=94=B9=E6=A0=87=E5=87=86?= =?UTF-8?q?=E5=9B=9B=E7=BA=A7=EF=BC=88=E5=86=92=E7=83=9F/=E5=8D=95?= =?UTF-8?q?=E5=85=83/=E9=9B=86=E6=88=90/=E7=B3=BB=E7=BB=9F+UI=20=E6=8E=A2?= =?UTF-8?q?=E9=92=88=E4=B8=8E=20golden=EF=BC=89=E5=B9=B6=E5=88=A0=E3=80=8C?= =?UTF-8?q?=E5=88=AB=E8=A2=AB=E5=AD=97=E9=9D=A2=E9=AA=97=E4=BA=86=E3=80=8D?= =?UTF-8?q?=E8=AD=A6=E5=91=8A=E8=8A=82=EF=BC=88=E5=90=8D=E5=AD=97=E5=AF=B9?= =?UTF-8?q?=E4=BA=86=E8=AD=A6=E5=91=8A=E5=B0=B1=E6=AD=BB=EF=BC=89=EF=BC=8C?= =?UTF-8?q?=E6=BB=9E=E5=90=8E=E8=AF=BB=E6=95=B0=E8=AE=A2=E6=AD=A3=EF=BC=88?= =?UTF-8?q?35=20=E5=A5=97=E4=BB=B6=E2=86=92manifest=2040=20=E6=9D=A1?= =?UTF-8?q?=E5=AE=9E=E6=95=B0=E5=8F=A3=E5=BE=84=E3=80=81=E6=8C=A5=E5=8F=91?= =?UTF-8?q?=E8=AE=A1=E6=95=B0=E6=94=B9=E5=8F=A3=E5=BE=84=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=E3=80=81=E6=B8=B8=E7=A6=BB=E5=8F=B0=E8=B4=A6=E5=8E=BB=E7=A1=AC?= =?UTF-8?q?=E8=AE=A1=E6=95=B0=E5=B9=B6=E6=94=B6=20probe=5Fui=5Futil=20?= =?UTF-8?q?=E5=85=A5=E8=A1=A8=EF=BC=89=EF=BC=9Barchitecture.md=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=9F=9F=E8=8A=82=E6=8D=A2=20run=5Fsmoke/run=5Fsystem?= =?UTF-8?q?=5Ftest/run=5Fall=20=E4=B8=89=E8=A1=8C=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=99=BB=E8=AE=B0+=E5=AE=9E=E8=AF=81=E8=A1=8C=E5=8E=BB?= =?UTF-8?q?=E6=BB=9E=E5=90=8E=E8=AF=BB=E6=95=B0+=E4=B8=89=E5=B1=82?= =?UTF-8?q?=E6=89=BF=E8=BD=BD=E8=A1=A8=E6=94=B9=E5=9B=9B=E7=BA=A7+?= =?UTF-8?q?=E6=94=B9=E5=8A=A8=E5=9F=9F=E4=B8=8E=E4=B8=89=E7=A1=AC=E9=97=B8?= =?UTF-8?q?=E8=A1=A8=E5=90=8C=E6=AD=A5=EF=BC=9B=E4=BD=93=E9=AA=8C=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E6=B8=85=E5=8D=95/=E5=BE=85=E5=8A=9E=E6=B8=85?= =?UTF-8?q?=E5=8D=95/probe=5Fgate.sh/golden=5Fcompare.py=E3=80=8C=E8=BD=BB?= =?UTF-8?q?=E9=97=B8/=E9=87=8D=E9=97=B8=E3=80=8D=E8=AF=8D=E9=9D=A2?= =?UTF-8?q?=E6=B8=85=E6=89=AB=EF=BC=88=E6=8E=A2=E9=92=88=E9=97=B8=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E6=AE=B5/=E5=85=A8=E9=87=8F=E5=9F=9F=E9=97=B8?= =?UTF-8?q?=E5=8F=A3=E5=BE=84=EF=BC=89=EF=BC=9B5=20=E4=B8=AA=E5=8D=95?= =?UTF-8?q?=E6=B5=8B=E5=A4=B4=E6=B3=A8=E9=87=8A=E3=80=8C=E5=8D=95=E5=85=83?= =?UTF-8?q?=E5=A5=97=E4=BB=B6=E3=80=8D=E6=8D=A2=E3=80=8C=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E3=80=8D=EF=BC=88=E4=BB=85=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=EF=BC=8C=E9=97=B8=2002=20=E8=BD=BD=E4=BD=93=E9=9B=B6=E6=94=B9?= =?UTF-8?q?=E5=8A=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../engineering/architecture/architecture.md | 31 ++++--- game/tests/README.md | 85 +++++++++++-------- .../unit/core/save_boot_no_activate_test.gd | 2 +- .../unit/core/save_slot_isolation_test.gd | 2 +- .../tests/unit/core/save_slot_summary_test.gd | 2 +- .../unit/core/tree_requires_level_test.gd | 2 +- game/tests/unit/core/tree_rules_v11_test.gd | 2 +- tools/golden_compare.py | 2 +- tools/probe_gate.sh | 8 +- 体验验证清单.md | 8 +- 待办清单.md | 2 +- 11 files changed, 82 insertions(+), 64 deletions(-) diff --git a/design/engineering/architecture/architecture.md b/design/engineering/architecture/architecture.md index 14c641cc..6423d481 100644 --- a/design/engineering/architecture/architecture.md +++ b/design/engineering/architecture/architecture.md @@ -8,7 +8,7 @@ - 评审模式:solo(`review-mode.txt`,2026-09-12 用户拍板——TD/LP 门禁按 solo 跳过,用户授权架构产出自主推进、迭代期回调) - 覆盖 GDD:现行全集——`design/gdd/` 顶层 00–08 共 9 件 + `systems/` 五域 25 件(排雷循环 4 / 战斗循环 5 / 冒险循环 5 / 跨局循环 5 / 表现与支撑 6)+ `ux/` 8 件;原英文命名 12 份系统 GDD 已全部迁入中文域目录,原「未设计」预留(A5 扫雷秘宝 / C1–C4 / B5 / D3–D6)均已立项成文 - 事实源地位:本文是架构事实的唯一来源——分层/所有权/数据流/API 边界/测试与质量域均以本文为准;历史决策沿革查 git commit log,不另设决策文档体系 -- 实现实证:`game/`(2026-09-10 起开发,**现为完整游戏开发版本,已非垂直切片**;迭代 29 已入库合并——冒烟 1127/0、单元 33 套(含 1 集成)、重闸按改动域分跑在役)——本文档的分层/所有权/数据流均经实现验证,非纯纸面设计 +- 实现实证:`game/`(2026-09-10 起开发,**现为完整游戏开发版本,已非垂直切片**;测试层级=冒烟/单元/集成/系统四级+UI 探针与渲染 golden(2026-10-22 用户裁,测试术语与层级重构批),闸机按改动域分跑在役)——本文档的分层/所有权/数据流均经实现验证,非纯纸面设计 - 技术总监签核:2026-09-12 — 有条件通过(原条件「决策记录须于正式工程骨架前定稿」已满足:骨架已成、沿革在 git 历史可查;`.claude/docs/director-gates.md` 缺失已登记技术债) - 主程序可行性:跳过 — solo 模式 @@ -185,9 +185,10 @@ | 模块 | 载体 | 独占职责 | 对外暴露 | 判定口径 | |---|---|---|---|---| -| **run_smoke**(系统级断言面) | `tools/run_smoke.py` | 冒烟总入口与判定权:headless 跑 `res://smoke/smoke_all.gd` 全量(轻闸首项,迭代 28 后在册 1125 项检查)+ 三闸判定 | 进程退出码(0=PASS / 1=FAIL) | 三闸齐备、判定无洞:①退出码 = 0;②摘要行「冒烟完成:N 项检查,0 项失败」在案且失败数为 0;③输出扫 `SCRIPT ERROR` / `Parse Error` 标记行。外部 wall-clock 超时在位(超时 = FAIL) | -| **run_all**(单元套件+L2 集成) | `game/tests/run_all.gd` | 33 套统一入口(32 单元/集成 + 见下):套件 manifest 逐条「套件 → 期望退出码」,致命类 expect=1 独立子进程实跑;`negative` 字段标负面套件(仅豁免错误扫描一闸) | 逐套件结果 + 汇总退出码(任一套件失败 quit(1)) | 套件级退出码协议在位;三硬闸齐备(入口内扫子套件输出 + 编排层扫 `unit.log`;`Kit.finish` 零断言返回专用码 2)。子进程输出须经系统命令解释器重定向落盘再读——`OS.execute` 的输出管道在本机恒空 | -| **probe_gate**(场景探针) | `tools/probe_gate.sh` | headless 场景探针段编排:默认段 ui_input_probe / ui_struct_probe / layout_probe / ui_contrast_probe / **screen_flow_probe**(L3 屏壳切换),`--click` 加跑 ui_click_probe,`--full` 加跑 ui_probe / ui_tune_probe / t17_defer_probe / fx_probe;孤儿 Godot 预清 + 批末残留清点(必须 0) | 段级 FAIL 输出 + 汇总退出码 | 每段 timeout 守卫在位(挂死 124/137 = FAIL,不静默);判定统一走 `verdict` 函数——退出码为唯一权威判据,PASS 串降级为附加判据,逐段跑 `tools/error_scan.py` | +| **run_smoke**(冒烟测试运行器) | `tools/run_smoke.py` | 冒烟测试总入口与判定权:headless 以项目运行目标拉起 `res://tests/smoke_boot.tscn`(极小启动场景——autoload 在位、配置表全载、主屏实例化,零逻辑判定)+ 三闸判定 | 进程退出码(0=PASS / 1=FAIL) | 三闸齐备、判定无洞:①退出码 = 0;②摘要行「[smoke] 摘要:N 项检查,0 项失败」在案且失败数为 0;③输出扫 `SCRIPT ERROR` / `Parse Error` 标记行。外部 wall-clock 超时在位(超时 = FAIL) | +| **run_system_test**(系统测试运行器) | `tools/run_system_test.py` | 系统测试总入口:headless 跑真实玩法路径断言面(过渡期宿主=`res://smoke/smoke_all.gd`,断言迁移批后=`res://tests/system_test.gd`)+ 三闸判定(口径同 run_smoke,摘要行兼容两种宿主格式) | 进程退出码(0=PASS / 1=FAIL) | 三闸齐备(同 run_smoke:退出码/摘要失败数/脚本错误扫描;wall-clock 超时在位) | +| **run_all**(单元与集成测试) | `game/tests/run_all.gd` | 40 套统一入口(39 单元+1 集成,2026-10-22 实数):套件 manifest 逐条「套件 → 期望退出码」,致命类 expect=1 独立子进程实跑;`negative` 字段标负面套件(仅豁免错误扫描一闸) | 逐套件结果 + 汇总退出码(任一套件失败 quit(1)) | 套件级退出码协议在位;三硬闸齐备(入口内扫子套件输出 + 编排层扫 `unit.log`;`Kit.finish` 零断言返回专用码 2)。子进程输出须经系统命令解释器重定向落盘再读——`OS.execute` 的输出管道在本机恒空 | +| **probe_gate**(场景探针) | `tools/probe_gate.sh` | headless 场景探针段编排:默认段 ui_input_probe / ui_struct_probe / layout_probe / ui_contrast_probe / **screen_flow_probe**(屏壳切换),`--click` 加跑 ui_click_probe,`--full` 加跑 ui_probe / ui_tune_probe / t17_defer_probe / fx_probe;孤儿 Godot 预清 + 批末残留清点(必须 0) | 段级 FAIL 输出 + 汇总退出码 | 每段 timeout 守卫在位(挂死 124/137 = FAIL,不静默);判定统一走 `verdict` 函数——退出码为唯一权威判据,PASS 串降级为附加判据,逐段跑 `tools/error_scan.py` | | **render_gate**(视觉 golden) | `tools/render_gate.sh` | 窗口化渲染闸:①shot_probe 七屏截图 → shot_checker A-D 语义断言;②fx_shot_probe / board_shot_probe 取证(超时/零字节/文件缺失任一 = FAIL);③棋盘 golden 逐像素容差比对(差 ≤16 / changed ≤2%,provisional)+ 菜单 golden;④批末孤儿残留清零 | 段级 FAIL 输出 + 汇总退出码 | 查退出码在位(每段 timeout 守卫 + 退出码/取证文件双检);**SCRIPT ERROR 扫描缺失**;断言数下限不适用(判定主体 = golden 容差与取证文件存在性,非断言计数) | | **static_scan**(静态扫描域) | `game/tests/static_scan.sh` | 静态扫描域 S1–S10 可执行载体(grep 规则 + `tools/audit_type_hints.py`〔S9〕+ `tools/audit_file_headers.py`〔S10〕) | fail 聚合退出码(任一硬失败规则命中即 exit 1) | 查退出码在位;SCRIPT ERROR 扫描与断言数下限不适用(逐规则命中即红,无运行时错误面与断言计数);S3 现为 warning 级、不参与 fail 判定(见下表) | @@ -195,7 +196,7 @@ | 域 | 闸 | 本批改动落在这些路径即属该域 | |---|---|---| -| **代码域** | run_smoke、run_all、probe_gate `--click`、probe_gate `--full`、render_gate、static_scan、回放确定性对拍(条件件) | **唯一触发=`game/`**(游戏工程代码、测试、探针、场景、数据表)。2026-10-18 用户裁「跨领域执法禁绝」(提交 `7bdf53e3`)后,原 `tools/`、`artwork/` 两前缀不再把改动拉进代码域 | +| **代码域** | run_smoke、run_system_test、run_all、probe_gate `--click`、probe_gate `--full`、render_gate、static_scan、回放确定性对拍(条件件) | **唯一触发=`game/`**(游戏工程代码、测试、探针、场景、数据表)。2026-10-18 用户裁「跨领域执法禁绝」(提交 `7bdf53e3`)后,原 `tools/`、`artwork/` 两前缀不再把改动拉进代码域 | | **文档域** | gdd_lint、doc_link_audit、doc_term_audit、流水线闸 | 一切非 `game/` 的路径:`design/`、根规则文件(`AGENTS.md`/`roadmap.md`/`待办清单.md`/`体验验证清单.md`)、`tools/` 工具脚本、`.zcode/` 配置、`sim-data/`、`reports/` | | **两域都含** | repo_structure_audit(熵增闸:顶层目录/根文件/图像白名单/退役路径/design 子目录五查——文档批新增或挪动 `.md` 同样会绊它;纯文本扫描秒级) | — | @@ -203,11 +204,11 @@ ### 三条判定硬闸现行状态 -| 硬闸 | run_smoke | run_all | probe_gate | render_gate | static_scan | -|---|---|---|---|---|---| -| **退出码优先** | 已落实(闸①) | 已落实(套件 manifest expect 协议) | 已落实(`verdict` 函数逐段判退出码,PASS 串不作判据) | 已落实(段级退出码 + 取证双检) | 已落实(fail 聚合退出) | -| **SCRIPT ERROR 扫描** | 已落实(闸③,外部扫描——GDScript 无脚本错误陷阱 API,闸设在外部包装) | 已落实(入口内扫子套件输出;子进程的 stdout/stderr 须先落盘——`OS.execute` 管道在本机恒空) | 已落实(逐探针段 `tools/error_scan.py`,标记与 run_smoke 同源) | **缺失**(本批未收——判定主体是 golden 容差与取证存在性;登记待办清单) | 不适用(静态域无运行时错误面) | -| **断言数下限** | 无独立下限(闸②摘要行必须在案,防「跑一半仍 PASS」;脚本错误吞断言路径由闸③兜住) | 已落实(`Kit.finish` 零断言返回专用退出码 2——与「断言失败=1」区分,防致命类套件的 expect 协议放过) | 已落实(退出码 0 但无该探针签名标记亦判红——防静默退出) | 不适用(golden 容差 + 取证存在性判定) | 不适用(逐规则命中判定;另有扫描面为空即判红的零命中自检) | +| 硬闸 | run_smoke | run_system_test | run_all | probe_gate | render_gate | static_scan | +|---|---|---|---|---|---|---| +| **退出码优先** | 已落实(闸①) | 已落实(闸①,口径同 run_smoke) | 已落实(套件 manifest expect 协议) | 已落实(`verdict` 函数逐段判退出码,PASS 串不作判据) | 已落实(段级退出码 + 取证双检) | 已落实(fail 聚合退出) | +| **SCRIPT ERROR 扫描** | 已落实(闸③,外部扫描——GDScript 无脚本错误陷阱 API,闸设在外部包装) | 已落实(闸③,标记清单与 run_smoke 同源) | 已落实(入口内扫子套件输出;子进程的 stdout/stderr 须先落盘——`OS.execute` 管道在本机恒空) | 已落实(逐探针段 `tools/error_scan.py`,标记与 run_smoke 同源) | **缺失**(本批未收——判定主体是 golden 容差与取证存在性;登记待办清单) | 不适用(静态域无运行时错误面) | +| **断言数下限** | 无独立下限(闸②摘要行必须在案,防「跑一半仍 PASS」;脚本错误吞断言路径由闸③兜住) | 同 run_smoke(过渡期宿主摘要行必须在案) | 已落实(`Kit.finish` 零断言返回专用退出码 2——与「断言失败=1」区分,防致命类套件的 expect 协议放过) | 已落实(退出码 0 但无该探针签名标记亦判红——防静默退出) | 不适用(golden 容差 + 取证存在性判定) | 不适用(逐规则命中判定;另有扫描面为空即判红的零命中自检) | 背景事实(run_smoke 头注在案):GDScript 运行时脚本错误不改进程退出码、不计入冒烟失败数、且静默中断所在函数剩余断言——2026-09-19 状态记录 83 实证「309 项 0 失败」实为断言未跑全。这是 SCRIPT ERROR 扫描必须存在于判定层的原因。 @@ -230,13 +231,15 @@ ### 三层承载与判定原则 -测试分三层,各有在册承载与入闸方式(细目见 `game/tests/README.md`): +测试分标准四级+UI 探针(2026-10-22 用户裁,测试术语与层级重构批),各有在册承载与入闸方式(细目见 `game/tests/README.md`): | 层 | 判什么 | 承载 | 入闸 | |---|---|---|---| -| L1 单元 | 最单元的逻辑代码对不对 | `game/tests/unit/` | `run_all.gd`(闸 02) | -| L2 集成 | 跨系统闭环对不对 | `game/tests/integration/` | 同上(同一入口同一协议) | -| L3 系统/场景 | 跑起来之后 UI 与场景切换对不对 | `game/smoke/*.tscn` 探针 | `probe_gate.sh`(闸 03/04) | +| 冒烟测试 | 这版活着吗(零逻辑判定) | `game/tests/smoke_boot.tscn/.gd` | `run_smoke.py`(闸 00) | +| 单元测试 | 最单元的逻辑代码对不对 | `game/tests/unit/` | `run_all.gd`(闸 02) | +| 集成测试 | 跨系统闭环对不对 | `game/tests/integration/` | 同上(同一入口同一协议) | +| 系统测试 | 真实玩法路径断言面 | 过渡期 `game/smoke/smoke_all.gd`(迁移后 `game/tests/system_test.gd`) | `run_system_test.py`(闸 01) | +| UI 探针+渲染 golden | 场景切换与观感 | `game/smoke/*.tscn` 探针 | `probe_gate.sh`(闸 03/04)、`render_gate.sh`(闸 05) | **判定原则**:验收不能只是「跑一遍没崩」。三硬闸(退出码/脚本错误扫描/断言数下限)是判定的底线, 缺一即判定有洞;「日志里写着 PASS」不作判据。另有两类静默失效必须机制性防范——规则扫描面为空 diff --git a/game/tests/README.md b/game/tests/README.md index d2d6abb2..dd91fb24 100644 --- a/game/tests/README.md +++ b/game/tests/README.md @@ -1,65 +1,79 @@ # Test Infrastructure — Dungeon Sweeper **Engine**: Godot 4.7.2-stable(GDScript 单语言) -**Test Framework**: 双载体——`--headless --script` 单元套件 + headless 场景探针;gdUnit4 **deferred**(D2 OQ1 关闭形态——本仓库不引第三方测试框架,需求见下「门禁」节) -**门禁**: 本地闸——`tools/run_all_gates.sh`(收口唯一验收入口,按改动域选档 `--code`/`--docs`,跨两域或改闸件本身不带参数=全量;`--quick` 为中段自检)。本仓库**无远端、无 CI**(`.github/workflows/` 已删除,不建不留不参考);闸清单与域划分只在本脚本定义,文档不另列 +**Test Framework**: 标准四级测试层(冒烟/单元/集成/系统)+ headless 场景探针与渲染 golden;gdUnit4 **deferred**(D2 OQ1 关闭形态——本仓库不引第三方测试框架,需求见下「门禁」节) +**门禁**: 本地闸——`tools/run_all_gates.sh`(收口唯一验收入口,按改动域选档 `--code`/`--docs`,跨两域或改闸件本身不带参数=全量)。本仓库**无远端、无 CI**(`.github/workflows/` 已删除,不建不留不参考);闸清单与域划分只在本脚本定义,文档不另列 **Setup date**: 2026-09-12(2026-09-27 `7f24b640` 三层收敛:根 tests/ 并入 `game/tests/`、docs 并入 `design/engineering/`、production 解散) -**参考实现**: `game/smoke/`(冒烟断言 + 场景探针族——正式工程的活样板;断言计数双口径见下节) +**参考实现**: `game/smoke/`(场景探针族——正式工程的活样板;系统测试断言面过渡宿主 `smoke_all.gd` 亦在此,迁移归断言迁移批) ## Directory Layout ``` tests/ - unit/ # 隔离单元测试(公式/状态机/纯逻辑)——extends SceneTree + quit(code) - integration/ # L2 集成用例(跨系统闭环——与 unit/ 的隔离断言面分工,见下「分层」节) - smoke/ # critical-paths.md——人工 gate 清单(/smoke-check 读取) - evidence/ # 截图与人工签核记录 - run_all.gd # 单元套件统一入口(套件 manifest 协议,D2 AC-2b) - static_scan.sh # 静态扫描域 S1–S12 可执行子集 + unit/ # 单元测试(公式/状态机/纯逻辑,隔离断言)——extends SceneTree + quit(code) + integration/ # 集成测试(跨系统闭环——与 unit/ 同一入口同一协议,见下「测试层级」节) + smoke_boot.tscn/.gd # 冒烟测试——极小启动场景(真实开机路径存活检查,闸 00) + system_test.gd # 系统测试宿主薄壳(SceneTree+构造注入;断言迁移批后承接 smoke_all.gd,闸 01) + smoke/ # critical-paths.md——人工 gate 清单(/smoke-check 读取) + evidence/ # 截图与人工签核记录 + run_all.gd # 单元与集成测试统一入口(套件 manifest 协议,D2 AC-2b) + static_scan.sh # 静态扫描域 S1–S12 可执行子集 ``` -## 测试三层与承载目录 +## 测试层级(标准四级+UI 探针) | 层 | 判什么 | 承载 | 入闸方式 | |---|---|---|---| -| **L1 单元** | 最单元的逻辑代码对不对(公式/状态机/纯逻辑) | `tests/unit/` | `run_all.gd` manifest(闸 02) | -| **L2 集成** | 跨系统的闭环对不对 | `tests/integration/` | 同上(与单元套件同一入口、同一协议) | -| **L3 系统/场景** | 跑起来之后 UI 与场景切换对不对 | `game/smoke/*.tscn` 探针 | `probe_gate.sh`(闸 03/04) | +| **冒烟测试** | 这版活着吗——能否启动、能否加载表、能否进主屏。零逻辑判定 | `tests/smoke_boot.tscn/.gd`(以项目运行目标拉起=真实开机路径) | `run_smoke.py`(闸 00) | +| **单元测试** | 最单元的逻辑代码对不对(公式/状态机/纯逻辑) | `tests/unit/` | `run_all.gd` manifest(闸 02) | +| **集成测试** | 跨系统的闭环对不对 | `tests/integration/` | 同上(与单元测试同一入口、同一协议) | +| **系统测试** | 启动真实工程走真实玩法路径 | 过渡期 `game/smoke/smoke_all.gd`(断言迁移批后=`tests/system_test.gd`) | `run_system_test.py`(闸 01) | +| **UI 探针+渲染 golden** | 跑起来之后 UI 与场景切换对不对;画面基线 | `game/smoke/*.tscn` 探针 | `probe_gate.sh`(闸 03/04)、`render_gate.sh`(闸 05) | > **integration/ 已建承载**(迭代 29 T3——此前只有章程句无实体,空壳曾于迭代 19 卡① T4 删过一次)。 > 首个集成用例 = `save_rng_roundtrip_test.gd`(存档⇄RNG 快照往返)。章程登记的四类集成用例里, -> 回放确定性已由 `tools/parity_anchor.py` 承担并收编为重闸 11 号条件闸件; +> 回放确定性已由 `tools/parity_anchor.py` 承担并收编为全量域闸 11 号条件闸件; > Core-Core 与 A1×A2 端到端仍随属主批增量登记;各 GDD 登记的 deferral 集成用例以对侧登记为生效条件。 > -> **分层判据**:能不能在 `--script` 模式下不加载 autoload 就验完?能 = L1; -> 需要两个以上真实系统协同并走落盘/落库 = L2;需要完整项目引导与场景树 = L3。 +> **分层判据**:能不能在 `--script` 模式下不加载 autoload 就验完?能 = 单元测试; +> 需要两个以上真实系统协同并走落盘/落库 = 集成测试;需要完整项目引导与场景树 = 冒烟/系统/UI 探针。 ## 断言计数双口径(2026-10-08 迭代 19 卡① T4 订正) 历史 README 写死「752+ 断言/十五套件」必然漂移,改为两种口径并注明语义: -- **运行时口径**:`python tools/run_smoke.py` 打印的通过项数(迭代 19 卡①分支基点实测 **840 项 0 失败**,2026-10-08;master 合并卡②④后为 846——随断言面增减浮动,以当次跑测打印为准)——循环内断言按执行次数计。 -- **静态口径**:`game/smoke/` 源码 `check(` 调用点计数(2026-10-08 本分支实测 **764 处**)——按源码位置计,一处在循环内即多次执行,故两口径数值不同且各自浮动,禁互相换算或写死。 +- **运行时口径**:系统测试运行器(`tools/run_system_test.py`)打印的通过项数——循环内断言按执行次数计,随断言面增减浮动,以当次跑测打印为准。 +- **静态口径**:断言面源码 `check(` 调用点计数——按源码位置计,一处在循环内即多次执行。 + +两口径数值不同且各自浮动,禁互相换算或写死。 ## Running Tests ```bash -# 单元套件(双载体之一)——Windows 须用 console 版 exe 取 stdout;工作目录 = game/(含 project.godot 那一层) +# 冒烟测试(闸 00)——极小启动场景,秒级 +python tools/run_smoke.py + +# 单元与集成测试(闸 02)——Windows 须用 console 版 exe 取 stdout;工作目录 = game/(含 project.godot 那一层) godot --headless --script tests/run_all.gd # 统一入口(本地闸 02 闸调用点) godot --headless --script tests/unit/example_test.gd -# 场景探针(双载体之二)——以探针场景为运行目标,走完整项目引导(autoload 在位) +# 系统测试(闸 01)——过渡期宿主 smoke_all.gd;分钟级 +python tools/run_system_test.py + +# 场景探针(UI 探针层)——以探针场景为运行目标,走完整项目引导(autoload 在位) godot --headless --path . res://smoke/.tscn # 本地闸必须配外部 wall-clock 超时(引擎无死锁探测;run_all_gates.sh 已内置) timeout 120s godot --headless --script tests/run_all.gd ``` -> 单元/契约套件状态(单元测试与契约层批 2026-09-28 立基,后随迭代 7/8 增补):`_suites()` manifest 已登记实跑 **35 套件**(2026-10-21 迭代 32 卡② 后现况,含 `counter_prefix_fatal_test` 负面套件;上一次计数变动为 2026-10-16 迭代 29 T1:样板件 `example_test.gd` 退出生产 manifest,27 → 26;`fog_lantern_wiring_test` 随提灯载体退役摘除在前)——data_config(ConfigDB 加载/进度坐标 ceil 解析/fatal 退出码/统一随机流)、core(秘宝结构冻结/断点续玩/提灯装配链)、battle_settlement(DamageMath 纯函数/DamageController 伤害单据五配方)、contracts(系统间接口契约断言)、presentation(节奏队列/奖励浮层/字体资产/地形瓦片集);致命类独立进程 `expect=1`。期望值一律由表读值推导(零玩法数值字面量)、随机只经 RngService 统一流。 +> 套件 manifest 现况:`_suites()` 登记 **40 条**(39 单元+1 集成,2026-10-22 实数;口径=manifest 条目数, +> 随套件增减浮动)——data_config(ConfigDB 加载/进度坐标 ceil 解析/fatal 退出码/统一随机流)、core(秘宝结构冻结/断点续玩/存档槽位/结算五段等)、battle_settlement(DamageMath 纯函数/DamageController 伤害单据五配方)、contracts(系统间接口契约断言)、presentation(节奏队列/奖励浮层/字体资产/地形瓦片集/模态编排等);集成 1 条存 `tests/integration/`。致命类独立进程 `expect=1`;负面套件 `negative=true` 只豁免错误扫描一闸。期望值一律由表读值推导(零玩法数值字面量)、随机只经 RngService 统一流。 **协议要点**(D2 冒烟协议): -- 冒烟脚本 `extends SceneTree` 以获得 `quit(code)`;成功 `quit(0)`、失败 `quit(1)` +- `--script` 载体 `extends SceneTree` 以获得 `quit(code)`;成功 `quit(0)`、失败 `quit(1)` - `--script` 模式**不加载 autoload 且不注册全局标识符**(裸引用 `ConfigDB` = parse error)——ConfigDB/RngService 由脚本显式实例化,core 一律构造注入 +- 冒烟测试走项目运行目标(场景拉起),autoload 全在位——与 `--script` 载体互补 - 致命类用例独立进程,runner manifest 登记"调用 → 期望退出码" - 每用例先 `reset_diagnostics()`(诊断收集器协议) - 期望值由表读值推导,不写死玩法数值 @@ -78,23 +92,24 @@ timeout 120s godot --headless --script tests/run_all.gd | Integration | Integration test OR playtest doc | 落地时随批建 `tests/integration/[system]/`(目录已删空壳,章程见上) | | Visual/Feel | Screenshot + lead sign-off | `tests/evidence/` | | UI | Manual walkthrough OR interaction test(场景探针为首选) | `tests/evidence/` | -| Config/Data | Smoke check pass | 跑批回执 `game/tests/.gates/receipt.json`(不入库)+结论随批记 `reports/` | +| Config/Data | Smoke test pass | 跑批回执 `game/tests/.gates/receipt.json`(不入库)+结论随批记 `reports/` | ## game/smoke/ 游离脚本台账(迭代 29 T7 逐个定性) -`game/smoke/` 有 29 个 `.gd`,其中 14 个在闸(探针有 `.tscn` 宿主、被 `probe_gate.sh`/`run_smoke.py` 调用), -其余 15 个**不带 `.tscn`、不入任何闸**——它们是手动入口的测量/诊断件。逐个定性如下, +`game/smoke/` 里的脚本两分:**在闸件**(探针有 `.tscn` 宿主、被 `probe_gate.sh`/`render_gate.sh` 调用; +`smoke_all.gd` 被系统测试运行器调用——过渡期宿主)与**不入闸的手动测量/诊断件**(下表)。 判据 = 头注自陈用途 + 有无外部引用(工具脚本/产品码/文档)。**存疑一律按在役登记并标注**(不退役)。 | 脚本 | 定性 | 用途与触发方式 | 结果由谁判 | |---|---|---|---| -| `adventure_monte_carlo.gd` | **在役**(`AGENTS.md` 在册入口) | 全冒险期望 MC;`tools/parity_anchor.py` 的对拍载体 | 工具脚本逐位比对 | +| `adventure_monte_carlo.gd` | **在役**(`AGENTS.md` 在册入口) | 全冒险期望 MC;`tools/parity_anchor.py` 的比对载体 | 工具脚本逐位比对 | | `verifier_batch.gd` | **在役**(`AGENTS.md` 在册入口) | `tools/balance_verifier.py` 的采集端(数值验证) | 报告与原始数据落 `sim-data/` | | `reward_expectation.gd` | **在役**(`AGENTS.md` 在册入口) | 奖励期望测量(非封锁格分布/资源类型频次) | 人工读 stdout | | `monte_carlo.gd` | 在役 | 战斗期望 MC(胜轮数/承伤/清图/开格分布) | 人工读 stdout | | `auto_player.gd` | 在役(库件) | 自动玩家策略单一实现——被 `battle_driver` preload | 宿主脚本 | | `battle_driver.gd` | 在役(库件) | 战斗驱动;被 `damage_probe` preload | 宿主脚本 | | `level_autoplay.gd` | 在役(库件) | 被 `presentation/tune_sim_controller.gd` load | 宿主脚本 | +| `probe_ui_util.gd` | 在役(库件) | 探针公共工具(存档隔离/句柄解析)——被各探针 preload | 宿主探针 | | `board_stat_probe.gd` | 在役 | `tools/clear_dominance_audit.py` 的取数端 | 工具脚本 | | `calibrate_boards.gd` | 在役 | 生成器真值分母;`sim-data/analyze.py` 引用 | 工具脚本 | | `damage_probe.gd` | 在役(诊断件) | 零承伤根因分割探针(头注自陈:非验收件·不入闸) | 人工读 stdout | @@ -114,7 +129,7 @@ timeout 120s godot --headless --script tests/run_all.gd `tests/static_scan.sh` 头注**——迭代 32 两卡是代码实现批,按流水线红线禁写 `design/`,故这两段未进架构文档。 新违规案例必须**先补扫描规则再修码**。 -S11 代码注释禁用词:扫描 game/ 全域 .gd/.tres/.md/.json/.sh 的注释面;禁词全部由 `design/gdd/术语表.md`「退役词对照」第一列解析供给(脚本内不内联任何词面——单一事实源);解析结果再按「巢穴」「旗」「flag_*」三条整行排除;三层豁免(闸门自指/显式留档 4 处/非注释面天然不取);两道自检(扫描面文件数非空+词表解析非空)。 +S11 代码注释禁用词:扫描 game/ 全域 .gd/.tres/.md/.json/.sh 的注释面;禁词全部由 `design/gdd/术语表.md`「退役词对照」第一列解析供给(脚本内不内联任何词面——单一事实源);解析结果再按「巢穴」「旗」「flag_*」三条整行排除,另设同形异义豁免「去重闸」(封印重判去重的机制判别器,与某闸档退役词后二字同形——行内含「去重闸」整行跳过);三层豁免(闸门自指/显式留档 4 处/非注释面天然不取);两道自检(扫描面文件数非空+词表解析非空)。 S12 效果引擎无状态化旧件零残留:扫描 game/ 全域 `.gd` 与 `game/data/tables/*.tres`,禁已删符号(`counters_export`/`counters_seed`/`_reset_tree_limited_counters` 等)与带引号的旧计数器 id 字面(引号锚定,不误伤 `adv_`/`round_` 前缀新名);扫描面文件数为零即判红。 @@ -132,17 +147,17 @@ S12 效果引擎无状态化旧件零残留:扫描 game/ 全域 `.gd` 与 `gam ③**断言数为零即红**——`test_kit.gd` 的 `finish()` 在零断言时返回专用退出码 2(与「断言失败=1」 区分,避免致命类套件的 expect 协议把它当成预期结果放过)。 -**gdUnit4 维持 deferred**:不引入第三方测试框架,本仓库在既有双载体(`--script` 套件 + 探针场景)内解决。 +**gdUnit4 维持 deferred**:不引入第三方测试框架,本仓库在既有载体(`--script` 套件+场景运行目标)内解决。 ## 术语(本项目内的唯一语义) +标准四级测试层(2026-10-22 用户裁,权威对照=design/gdd/术语表.md「退役词对照」): + | 词 | 语义 | 载体 | |---|---|---| -| **冒烟(smoke)** | **秒级存活检查**——能否启动、能否加载表、能否进主屏。判断「这版还活着吗」,不判断「这版对不对」 | `game/smoke/` 的存活面 | -| **单元套件** | 隔离的公式/状态机/纯逻辑断言面,逐套件独立进程实跑 | `game/tests/unit/`、`run_all.gd` | +| **冒烟测试** | 秒级存活检查——能否启动、能否加载表、能否进主屏。判断「这版还活着吗」,不判断「这版对不对」 | `tests/smoke_boot.tscn/.gd`、`tools/run_smoke.py` | +| **单元测试** | 隔离的公式/状态机/纯逻辑断言面,逐套件独立进程实跑 | `game/tests/unit/`、`run_all.gd` | +| **集成测试** | 跨系统闭环断言面(存档⇄RNG 往返、回放确定性等) | `game/tests/integration/`、`run_all.gd` | +| **系统测试** | 启动真实工程走真实玩法路径的断言面 | 过渡期 `game/smoke/smoke_all.gd`+`tools/run_system_test.py`;迁移后 `tests/system_test.gd` | | **场景探针** | 以探针场景为运行目标、走完整项目引导(autoload 在位)的 UI/场景断言 | `game/smoke/*.tscn`、`probe_gate.sh` | -| **集成用例** | 跨系统测试(Core-Core/回放确定性/存档⇄RNG 往返/端到端) | `game/tests/integration/` | - -> ⚠ **两处历史遗留命名,读代码时别被字面骗了**:`tools/run_smoke.py` 之名来自冒烟,但**它承载的是 -> 系统级断言面**(1127 项检查,远不止存活检查);`game/smoke/` 之名同理,里面既有存活面也有 -> 系统级断言与场景探针。判定「是不是冒烟」看语义,不看文件名。 +| **渲染 golden** | 窗口化截图与基线图的逐像素容差比对 | `render_gate.sh`、`tests/golden/` | diff --git a/game/tests/unit/core/save_boot_no_activate_test.gd b/game/tests/unit/core/save_boot_no_activate_test.gd index 19afb2d4..27674ed7 100644 --- a/game/tests/unit/core/save_boot_no_activate_test.gd +++ b/game/tests/unit/core/save_boot_no_activate_test.gd @@ -4,7 +4,7 @@ # 生产链无激活触达写盘被拒、访问器 fail-safe 合法空值、越界槽号拒绝。 # 真实 autoload boot 链断言在冒烟 V2-s1..s4 段——--script 模式无 autoload,本套件为类级 # 白盒面+生产链对照,双落点在实现报告实际对照表对账。) -# 载体:run_all 单元套件(显式实例化夹具——--script 模式无 autoload)。 +# 载体:run_all 单元测试(显式实例化夹具——--script 模式无 autoload)。 extends SceneTree const SaveManagerScript := preload("res://core/save_manager.gd") diff --git a/game/tests/unit/core/save_slot_isolation_test.gd b/game/tests/unit/core/save_slot_isolation_test.gd index ca49a91f..20cb7260 100644 --- a/game/tests/unit/core/save_slot_isolation_test.gd +++ b/game/tests/unit/core/save_slot_isolation_test.gd @@ -2,7 +2,7 @@ # (三槽往返异值逐域读回、改 A 槽 B 槽字节不动、高版本槽进档失败恢复切换前三态、 # MetaProgression 宿主链切槽前后引用同一且写盘落当前槽、attach_slot_template 注入语义。 # 测试隔离:槽模板注入 user://smoke_slotiso_%d.json 命名空间——绝不碰真实槽与 user://save.json。) -# 载体:run_all 单元套件(显式实例化夹具——--script 模式无 autoload)。 +# 载体:run_all 单元测试(显式实例化夹具——--script 模式无 autoload)。 extends SceneTree const SaveManagerScript := preload("res://core/save_manager.gd") diff --git a/game/tests/unit/core/save_slot_summary_test.gd b/game/tests/unit/core/save_slot_summary_test.gd index 4d83a816..ba9592e7 100644 --- a/game/tests/unit/core/save_slot_summary_test.gd +++ b/game/tests/unit/core/save_slot_summary_test.gd @@ -2,7 +2,7 @@ # (三槽异值逐槽三项正确、空槽零值合法态、坏槽/高版本/缺版本字段=显式损坏标记、 # 窥视零副作用(字节+mtime 双比对+零 .corrupt.bak 改名+不激活不写内存)、路径注入口 peek_summary_at。 # 测试隔离:模板注入 user://smoke_slotsum_%d.json 命名空间——绝不碰真实槽与 user://save.json。) -# 载体:run_all 单元套件(显式实例化夹具——--script 模式无 autoload)。 +# 载体:run_all 单元测试(显式实例化夹具——--script 模式无 autoload)。 extends SceneTree const SaveManagerScript := preload("res://core/save_manager.gd") diff --git a/game/tests/unit/core/tree_requires_level_test.gd b/game/tests/unit/core/tree_requires_level_test.gd index 516073d2..46d143b5 100644 --- a/game/tests/unit/core/tree_requires_level_test.gd +++ b/game/tests/unit/core/tree_requires_level_test.gd @@ -2,7 +2,7 @@ # (schema requires_level Dictionary{node_id: n} + validate 悬空/阈值 <1/类型错即拒 + # tree_can_buy 等级阈值判定 + 行板 gate_text 门槛文案投影。 # 现役 meta_tree.tres 12 行零配置=机制位空转,判定面经本夹具直构行承载。) -# 载体:run_all 单元套件(显式实例化夹具——--script 模式无 autoload)。 +# 载体:run_all 单元测试(显式实例化夹具——--script 模式无 autoload)。 extends SceneTree diff --git a/game/tests/unit/core/tree_rules_v11_test.gd b/game/tests/unit/core/tree_rules_v11_test.gd index cdda06cf..ad233a8b 100644 --- a/game/tests/unit/core/tree_rules_v11_test.gd +++ b/game/tests/unit/core/tree_rules_v11_test.gd @@ -7,7 +7,7 @@ # rebuild 树规则段对两键零合成行——此状态在此被显式断言(旧版对两键取空夹具静默跳过=假绿): # rebuild 实装面行数对照——四键逐键与夹具同行数(rebuild 分支被删/漂移即转红), # 两键断零行(将来被加合成行即转红,提示先复核效果闭集再同步夹具与实现)。 -# 载体:run_all 单元套件(显式实例化夹具——--script 模式无 autoload)。) +# 载体:run_all 单元测试(显式实例化夹具——--script 模式无 autoload)。) extends SceneTree diff --git a/tools/golden_compare.py b/tools/golden_compare.py index af8bbea6..c98701df 100644 --- a/tools/golden_compare.py +++ b/tools/golden_compare.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -r"""golden_compare.py — C 档黄金图对比(呈现层测试分级·周期重闸件)。 +r"""golden_compare.py — C 档黄金图对比(呈现层测试分级·渲染闸周期件)。 用法: python tools/golden_compare.py <黄金图目录> <新截图目录> [屏清单...] 默认屏清单 = shot_menu.png shot_camp.png shot_level.png shot_battle.png。 diff --git a/tools/probe_gate.sh b/tools/probe_gate.sh index d4f46866..fb8a0e2c 100644 --- a/tools/probe_gate.sh +++ b/tools/probe_gate.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# 轻闸探针正主——headless 安全段固化,禁再手拼同款 Godot 命令。 +# 探针闸正主——headless 安全段固化,禁再手拼同款 Godot 命令。 # 三闸:①每段 timeout 守卫(挂死=FAIL 不静默)②段前预清+段后清本 game 目录孤儿 Godot # (按命令行精确匹配本 game 路径,不误杀其他 worktree 会话的合法进程)③末尾残留清点=批末闸(必须 0)。 # 段:ui_input_probe=默认必跑;ui_struct_probe=默认必跑( @@ -12,8 +12,8 @@ # 永远为真的假断言,禁用)。 # 判定口径(三硬闸):每段走 verdict 函数——①退出码为唯一权威判据(PASS 串降级为日志) # ②脚本错误扫描命中即红(tools/error_scan.py,标记与 run_smoke.py 同源)③PASS 串保留为附加判据。 -# --full 段(重闸件):ui_probe/ui_tune_probe/t17_defer_probe/fx_probe -# 四件带断言探针;轻闸默认段断言面零删减零改动(--full 只增不改)。 +# --full 段(扩展段):ui_probe/ui_tune_probe/t17_defer_probe/fx_probe +# 四件带断言探针;默认段断言面零删减零改动(--full 只增不改)。 # 渲染面归 render_gate.sh 正主(窗口化:shot_probe 七屏+fx_shot/board_shot 取证+棋盘 golden 逐像素)。 # 用法: tools/probe_gate.sh [game目录] [--click] [--full] 缺省 game 目录=脚本所在仓库的 game/(worktree 内自动指向该 worktree) # 依赖: Git Bash(coreutils timeout/taskkill) + PowerShell;Godot 路径可用环境变量 GODOT_BIN 覆盖。 @@ -34,7 +34,7 @@ TMP="$(mktemp -d /tmp/probegate_XXXXXX)" trap 'rm -rf "$TMP"' EXIT _rc=0 -# 错误扫描闸件用纯标准库 python(不依赖 PIL),优先带 PIL 的 gamedev venv 以保持与重闸同解释器。 +# 错误扫描闸件用纯标准库 python(不依赖 PIL),优先带 PIL 的 gamedev venv 以保持与全量域闸同解释器。 PY="" for cand in "/d/dev/python/envs/gamedev/Scripts/python.exe" "python" "python3"; do if "$cand" -c "import sys" >/dev/null 2>&1; then PY="$cand"; break; fi diff --git a/体验验证清单.md b/体验验证清单.md index 055f5e77..1f490520 100644 --- a/体验验证清单.md +++ b/体验验证清单.md @@ -118,7 +118,7 @@ > **✅ 转记(2026-10-16 用户令屏蔽——前提失效,无需验)**:贫矿脉行已从 `variation.tres` 删除(用户裁「地形变异特异关卡后续重新做设计,先屏蔽」;Boss/精英图全局池空→region 布置兜底,玩家面零横幅零角标零资源偏斜)。下列六项判据全部失效,不验;重建设计立项时随新需求卡重立目检项。改动面=shielddebug-20261016 批(variation.tres 删行+冒烟断言换代)。 -> **改排状态注记(2026-10-05 ui-struct-probe 批)**:UI 优化批(UI 落码 11 卡+SR-1)已全入库;期间实机目检抓到标题屏双列叠放缺陷,已随 ui-struct-probe 批修复,并新增全 UI 屏结构探针入轻闸(按钮互压/等宽/出屏/零尺寸——机械可判项自此探针代管,见 §⑯)。本条统一验证窗口仍开,等用户实机目检时点;下列六项均为体感/呈现面,保留人闸。 +> **改排状态注记(2026-10-05 ui-struct-probe 批)**:UI 优化批(UI 落码 11 卡+SR-1)已全入库;期间实机目检抓到标题屏双列叠放缺陷,已随 ui-struct-probe 批修复,并新增全 UI 屏结构探针入探针闸默认段(tools/probe_gate.sh,按钮互压/等宽/出屏/零尺寸——机械可判项自此探针代管,见 §⑯)。本条统一验证窗口仍开,等用户实机目检时点;下列六项均为体感/呈现面,保留人闸。 **怎么玩**:打一局正常打到第 2 关的 Boss 图(想快可以在开始界面开调试入口直接进第 2 关);顺手的话再看一眼第 1 关 Boss 和任意普通战斗图,前后共约十分钟。本条目在 UI 优化批落地后执行——届时以改完后的最终界面为准,下述文案若随 UI 批调整以当批登记为准。 @@ -162,11 +162,11 @@ ### ⑮ 秘宝词族换词全 UI 面目检 + 渲染像素闸补跑(2026-10-05 秘宝代码词族对齐批顺带登记——迭代15③号卡) - **实机目检(「秘宝」字样全 UI 面,约 3 分钟)**:①C 区槽位标题应显示「秘宝」(原「魔法道具」,hero_info_panel_view 槽位组);②空槽悬停提示应显示「秘宝槽(空)」;③战斗总结三选一叙述行应显示「秘宝 / …」或「秘宝 / 随机获得一件」(原「魔法道具」开头);④换关 toast 应显示「上关剩余 N 件秘宝已随关卡结束清空」。**红灯信号**:任何界面残留「魔法道具」字样。〔**✅ 真人目检半已核销(2026-10-14)**:用户实机目检回报完成,未呈报残留(qa 收尾批)〕 -- **渲染像素类闸补跑挂账**:本批屏蔽五件(golden_compare / shot_probe / fx_probe / ui_layout_probe / board_shot——组 A 文案变更使像素断言合法失效,按 goal 令弃断);**补跑时点=迭代 14 全入库后的表现批周期闸一次性补跑**,与本批无独立补跑义务;届时若 golden 基线因文案换代失效,按换代流程重锚而非当缺陷修。**分工注记(2026-10-05 ui-struct-probe 批)**:结构类断言(互压/等宽/出屏)已由 `ui_struct_probe` 入轻闸默认必跑段(tools/probe_gate.sh),与像素五闸互补不替代——五闸补跑义务不变。**核销注记(2026-10-08 迭代 19 卡①自动化验收补齐批——机器补跑半顺带核销)**:五件机器半全部归宿已入库并实测绿——shot_probe(扩屏七屏+shot_checker A-D 断言)/board_shot(L1-L3 取证+golden 逐像素 0.00%×3)/golden_compare 三件入 render_gate(窗口化自动闸),fx_probe 入 probe_gate --full(headless 逻辑层断言),ui_layout_probe 补 1280/1920 两档最小断言(首跑 PASS);五闸归宿对账见需求卡片-自动化验收补齐批 §一。**真人目检半(上方「秘宝」字样全 UI 面 3 分钟)仍归用户,不注销**。 +- **渲染像素类闸补跑挂账**:本批屏蔽五件(golden_compare / shot_probe / fx_probe / ui_layout_probe / board_shot——组 A 文案变更使像素断言合法失效,按 goal 令弃断);**补跑时点=迭代 14 全入库后的表现批周期闸一次性补跑**,与本批无独立补跑义务;届时若 golden 基线因文案换代失效,按换代流程重锚而非当缺陷修。**分工注记(2026-10-05 ui-struct-probe 批)**:结构类断言(互压/等宽/出屏)已由 `ui_struct_probe` 入探针闸默认必跑段(tools/probe_gate.sh),与像素五闸互补不替代——五闸补跑义务不变。**核销注记(2026-10-08 迭代 19 卡①自动化验收补齐批——机器补跑半顺带核销)**:五件机器半全部归宿已入库并实测绿——shot_probe(扩屏七屏+shot_checker A-D 断言)/board_shot(L1-L3 取证+golden 逐像素 0.00%×3)/golden_compare 三件入 render_gate(窗口化自动闸),fx_probe 入 probe_gate --full(headless 逻辑层断言),ui_layout_probe 补 1280/1920 两档最小断言(首跑 PASS);五闸归宿对账见需求卡片-自动化验收补齐批 §一。**真人目检半(上方「秘宝」字样全 UI 面 3 分钟)仍归用户,不注销**。 ### ⑯ UI 结构批复验(2026-10-05 ui-struct-probe mini-loop 顺带登记——标题屏双列修复+按钮宽度纪律,约 2 分钟) -- **背景**:实机目检抓到标题屏立绘/帮助牌叠压操作行牌(M2 双列接线缺陷,ui_assembler.gd 右列误挂 MarginContainer)+全站按钮宽度无纪律(UiKit 工厂无宽度约束)。已修复:右列挂回 HBox 第二列;UiKit.button 全站最小宽度基准 216+SIZE_FILL(行板/角标/图标钮 9 处豁免);全 UI 屏结构探针入轻闸。 +- **背景**:实机目检抓到标题屏立绘/帮助牌叠压操作行牌(M2 双列接线缺陷,ui_assembler.gd 右列误挂 MarginContainer)+全站按钮宽度无纪律(UiKit 工厂无宽度约束)。已修复:右列挂回 HBox 第二列;UiKit.button 全站最小宽度基准 216+SIZE_FILL(行板/角标/图标钮 9 处豁免);全 UI 屏结构探针入探针闸默认段。 - **看什么(实机目检约 2 分钟)**:①标题屏应为左右双列——左操作列(冒险/营地与系统两组)与右立绘看台+玩法速览牌并排,互不遮挡,「退出游戏」按钮完整可见;②同组按钮等宽(菜单列/确认弹窗),按钮不再宽窄参差;③营地屏三柜台+底栏不出屏、不重叠。 - **红灯信号**:任何屏出现控件叠压/按钮宽窄不一回潮/内容超出屏幕——结构探针已拦机检面,红灯=探针盲区,报批补断言。 @@ -265,7 +265,7 @@ > 场景拆分+会话物化+战斗域重构+账本完成+模态协议内聚后,玩家可见面唯一验收=观感零变化。登记不阻塞(2026-10-12 真人检测立规)。 > -> **机检承接注记(2026-10-17 用户裁,迭代 29 反哺点③)**:屏壳切换正确性半(六步主流程+两条回退+屏级互斥)已转机检 `screen_flow_probe` 入轻闸必跑段;第 1 项保留——其承载的是机检抓不到的观感半(逐屏演出时序/文案/布局零漂移)。 +> **机检承接注记(2026-10-17 用户裁,迭代 29 反哺点③)**:屏壳切换正确性半(六步主流程+两条回退+屏级互斥)已转机检 `screen_flow_probe` 入探针闸必跑段;第 1 项保留——其承载的是机检抓不到的观感半(逐屏演出时序/文案/布局零漂移)。 | # | 目检项 | 判据 | |---|---|---| diff --git a/待办清单.md b/待办清单.md index 8ea6e620..d2efecfd 100644 --- a/待办清单.md +++ b/待办清单.md @@ -500,7 +500,7 @@ #### 「生涯」界面——冒险中数据回看的唯一承接处(2026-10-04 登记,来源=会话提及,设计面=文档轨) -**现状**:2026-10-04 用户裁决(UI 落码计划 P-C7)——冒险中不给数据回看:settlement_review_overlay(结算明细回看)与结算框「明细回看」钮在冒险中退役,战报带只留单行「上轮」;历史战报/结算明细的查看去向=新「生涯」界面(跨局汇总/历史冒险回看/图鉴承接)。来源=UI 重设计原型 v2 落码计划裁决记录(已退役,git 历史可查 §七)。**设计提纲已成文**:[systems/跨局循环/生涯.md](design/gdd/systems/跨局循环/生涯.md) v0.1 范围草案五节成文〔信息架构/承载内容清单/与既有界面关系/分期实装建议/待裁点〕,P0-P5 六裁点已全裁并写入文档〔P0甲 提纲落现行事实源 / P1甲 主菜单一级入口 / P2甲 每局一行摘要+cap 扩容 N+补用时埋点 / P3甲 敌人图鉴优先 / P4甲 局内不放入口+生涯零跳转 / P5甲 settlement_review 复活承接〕。**期一已落地**(2026-10-07 迭代 18 卡④实装批:生涯页六类九项+主菜单入口+终身计数域存档 v6,批末重闸全绿;实机人闸=体验验证清单⑲ 生涯页目检节)。剩余面=期二(历史回看/图鉴)与期三实装+P6 营地最近冒险条带去留(新发现待裁点,随提纲评审一并裁)。**迭代 19 卡②遗留件(2026-10-08 登记)**:settlement_review_overlay.gd 现态考证=未接线遗留件(全仓零引用,M9 结算框改版「明细回看」钮退役后遗留)——处置两途:①随生涯实装批按 P5 甲复活承接(重接线入生涯);②确认不再需要则正式退役删除文件;二选一归生涯实装批/用户裁。Alpha 出口裁决:期二、期三全部排 Beta——Alpha 只保留期一,不在 Alpha 内做生涯新内容。 +**现状**:2026-10-04 用户裁决(UI 落码计划 P-C7)——冒险中不给数据回看:settlement_review_overlay(结算明细回看)与结算框「明细回看」钮在冒险中退役,战报带只留单行「上轮」;历史战报/结算明细的查看去向=新「生涯」界面(跨局汇总/历史冒险回看/图鉴承接)。来源=UI 重设计原型 v2 落码计划裁决记录(已退役,git 历史可查 §七)。**设计提纲已成文**:[systems/跨局循环/生涯.md](design/gdd/systems/跨局循环/生涯.md) v0.1 范围草案五节成文〔信息架构/承载内容清单/与既有界面关系/分期实装建议/待裁点〕,P0-P5 六裁点已全裁并写入文档〔P0甲 提纲落现行事实源 / P1甲 主菜单一级入口 / P2甲 每局一行摘要+cap 扩容 N+补用时埋点 / P3甲 敌人图鉴优先 / P4甲 局内不放入口+生涯零跳转 / P5甲 settlement_review 复活承接〕。**期一已落地**(2026-10-07 迭代 18 卡④实装批:生涯页六类九项+主菜单入口+终身计数域存档 v6,批末全量域闸全绿;实机人闸=体验验证清单⑲ 生涯页目检节)。剩余面=期二(历史回看/图鉴)与期三实装+P6 营地最近冒险条带去留(新发现待裁点,随提纲评审一并裁)。**迭代 19 卡②遗留件(2026-10-08 登记)**:settlement_review_overlay.gd 现态考证=未接线遗留件(全仓零引用,M9 结算框改版「明细回看」钮退役后遗留)——处置两途:①随生涯实装批按 P5 甲复活承接(重接线入生涯);②确认不再需要则正式退役删除文件;二选一归生涯实装批/用户裁。Alpha 出口裁决:期二、期三全部排 Beta——Alpha 只保留期一,不在 Alpha 内做生涯新内容。 **缺口**:还差提纲用户评审收尾与 P6 营地最近冒险条带去留裁决——裁完才能立期二/期三实装卡(期一已交付,期二/三未预占排期)。本条排 Beta,不阻塞 Alpha;重启时再补 P6 与 N 终值。