docs(roadmap): 迭代 29 编入测试分层与门禁语义修正批(2026-10-16 用户令)

roadmap:迭代 29 行改写为「测试分层与门禁语义修正批」——需求卡片=design/research/需求卡片-测试分层与门禁语义修正批-2026-10-15.md(v1.4,T0-T8≈5.5-7d,开工门=U0〔Q3-Q7 裁点+卡片 v2.0 重核+/iter 发令〕);新增「迭代 30+ 位空」行——原 M4 数值批已废(2026-10-16 用户删卡令「它就不是一个应该存在的东西」),禁止再以该名义立批/规划/引用已删卡,四项数值欠账归 06 在册、重新归属待用户裁;当前位置节迭代进度与近期主战场两处换代(下一前沿=迭代 29);迭代 23+ 行(M4 原始泊位)追加作废注,仅作排期史保留。

需求卡片 v1.5:排期行与 §5 roadmap 同步行更新;登记 v2.0 重核遗留项(残留 CI 措辞四漏网点+事实基线换代:冒烟 1086→1125/0、门禁五闸→run_all_gates.sh 编排、architecture.md 测试域节已成文、Q3 的 CI timeout 前提消失);订正 v1.3 版本条目尾部重复段。文档三闸全绿(gdd_lint 0 不通过/死链 0/疑似现行 0)。
This commit is contained in:
DKChan committed 2026-10-16 03:09:34 +08:00
1 parent e8ceb27243
commit 21c83f5736
2 files changed
+295 -5

No files matched your search

@@ -0,0 +1,289 @@
# 需求卡片:测试分层与门禁语义修正批(本地闸三硬闸 · L2 集成层建承载 · L3 屏壳切换探针 · 覆盖补洞 · 术语订正 · 台账清理)
- 版本:v1.5(2026-10-15 成文 v1.0 → v1.1 → v1.2 → v1.3 → v1.4 → **v1.5**)
- v1.1:①**CI 从未执行**(`git remote -v` 无输出)⇒ T1 主体由 CI 改为本地闸;②**用户立规:历史决策文档只是变更记录、不是架构文档** ⇒ 🔴 契约 5 重写为「历史决策文档不回改」,Q1 由「是否改决策文档」改为「在役架构事实如何**迁出**」,新增 Q7;③修正「smoke_all 不被执行」的错误表述(它本就在本地轻闸首项)。
- v1.2(用户令后执行态):①**用户裁「CI 没有用、后面也不用、不留死配置」** ⇒ `.github/workflows/` 全数删除(分支 `ci-removal-20261015`),CLAUDE.md 硬性红线立「不建/不留/不参考 CI 配置,改门禁只改本地闸」;本卡 T1④「CI 同步对齐」、T2「CI job 改名」、Q2「是否并入 CI」、§5 同步面 CI 行**全部删除**;②**架构事实源纪律已成文**:CLAUDE.md 硬性红线 + `design/dev/doc-conventions.md`(架构事实唯一源=`design/dev/architecture/architecture.md`);③排期订正:迭代 28 位已被「启动装配与生命周期验收补全批」占用。
- **v1.3(2026-10-15 用户令「放到迭代 29」)**:①排期由「迭代号待定」定为 **迭代 29**;②新增 **§0 前置依赖与并行性**——裁决**串行**(须等 iter28 收口),四条依据(CLAUDE.md 汇聚文件、`game/smoke/` 是 iter28 主战场、基线读数会作废、08-Alpha 测试基建四行会自相矛盾),并标明**例外**:CI 清除 4 文件不与 iter28 触碰面重叠,**建议先行合并**;③T0-③/T0-④ 加注「开工日重采基线 + 复核 iter28 是否已改过相关行」。
- **v1.4(2026-10-15 用户令「决策记录归 git commit log、不另存决策文档」)**:①用户裁历史决策文档体系整体废除——七份决策文件删除(沿革归 git 历史),在效事实已由 10-15 文档批迁入 `design/dev/architecture/architecture.md`;②🔴 契约 5 改写为「架构事实唯一源」口径;③Q1(迁出去向)闭环、§5 同步面改写为 architecture.md;④全文引用清除。
- **v1.5(2026-10-16 roadmap 编入笔,用户令「把需求卡片写进 roadmap 的迭代 29」)**:①roadmap 迭代 29 行已改写为本批、新增「迭代 30+ 位空」行(原 M4 数值批已废记录)、当前位置节两处换代;②§5 `roadmap.md` 同步行与排期行随之更新;③**遗留待 v2.0 重核项登记在案**:卡片残留 CI 措辞(§1 完成定义第 3/4 条、T4「入轻 CI」、V4「CI job 名」、V7「入轻 CI」——v1.2 删 CI 任务时的漏网点)+事实基线换代(冒烟 1086→1125/0、门禁五闸→`tools/run_all_gates.sh` 编排、architecture.md「测试与质量域」节已成文、Q3 的 CI timeout 120s 前提消失)——U0 开工门前一并重核升版;④版本史订正:v1.3 条目尾部重复段(上次修 v1.2 覆盖时误留的 v1.2 内容,致同一条目内「排期定迭代 29」与「顺延 29+」自相矛盾)已删除,v1.3 条目恢复为三事=排期定迭代 29+§0 串行裁决+T0-③/T0-④ 加注。
- 来源:[测试审计 — 单测覆盖与验收门禁标准 v1.1](../qa/测试审计-单测与验收门禁标准-2026-10-15.md)(`design/qa/`,2026-10-15;加载 `godot-builder` + `godot-auditor` 双技能审计产出)
- 归属:**测试基建批**(req-card §3:缺陷修复/工程债/**测试基建**/重构类工程需求凭 bug 证据或 06 在册条目直接写卡,**无设计定稿前置要求**——本批凭据即审计报告 A 类证据)
- 排期:**迭代 29**(2026-10-15 用户令:迭代 28 进行中,本批顺延至 29;**2026-10-16 已写入 roadmap 迭代 29 行**——原「M4 顺延 29+」行随 M4 数值批废除改写为「迭代 30+ 位空」记录)。开工门=U0,须用户 `/iter` 发令。
- **并行性裁决:串行,须等迭代 28 收口**(依据见下 §0,2026-10-15 实查)。已完成的 CI 清除部分(4 文件小批)不与 iter28 逻辑耦合,**建议先行合并**
### 0. 前置依赖与并行性(2026-10-15 裁决)
**不能与迭代 28「启动装配与生命周期验收补全批」并行**,四条依据:
| # | 冲突面 | 具体情况 |
|---|---|---|
| 1 | **`CLAUDE.md` 汇聚文件** | iter28 T6 要在「批次工作流」第 7/8 条(:102-103)旁加硬规则;本批已在「硬性红线」节(:115-116)加两条、后续可能改批末四闸口径句(:106/:116)。同一汇聚文件两批改,顺序一乱即互相覆盖 |
| 2 | **`game/smoke/` 是 iter28 主战场** | iter28 正在改 `smoke_all.gd` / `ui_struct_probe.gd` / `main.gd`(worktree 脏件实证);本批 T4 要在同目录新建 `screen_flow_probe.gd/.tscn`,T7 还要给 15 个游离脚本定性 |
| 3 | **基线读数会作废** | 本批 T0-③ 冻结的基线(冒烟 1086 / run_all 27 套 / 探针 11)在 iter28 收口加断言后全部失效——T0 白冻结,V11「改前改后读数对拍」的改前锚也失去意义 |
| 4 | **`08-Alpha版本标准` 会自相矛盾** | iter28 卡片 §5 明确写「测试基建四行状态不变——本批只加断言不加新设施类」;**本批恰恰就是动测试设施本身**(建 `integration/`、改判定脚本、加新探针)。两批并行则该文档无法陈述真话 |
⇒ **串行。本批在 iter28 收口合入 master 后开工,且 T0-③ 基线以 iter28 收口后的读数重新冻结**(卡片现有 1086/27 数字作废,开工日重采)。
**例外(可先行)**:已完成的 CI 清除 4 文件改动(删 `.github/` + CLAUDE.md 两条红线 + doc-conventions §9.1 + README 门禁节)**不与 iter28 触碰面重叠**(iter28 碰的是 `main.gd`/`smoke_all.gd`/`ui_struct_probe.gd`/code-conventions/roadmap/06/07/active),且两条红线是"规则"性质,越早生效越好。⇒ **建议先提交并合入 master**,不等 iter28。
- 用户原始诉求(本批要兑现的三条):①最单元的逻辑代码有它的单元测试;②整体的模块有它的接口测试;③整体的系统在执行之后,也有它 UI 方面的和场景切换相关的测试。**并且验收不能只是"冒烟"——可以是冒烟,但更重要的是逻辑正确性**
---
## 1. 目标与完成定义
把验收语义从「**跑一遍没崩 = 冒烟通过**」扭转为「**三层完备 + 判定可靠**」。四件事:
1. **门禁判定语义修正(本批第一优先)**:CI 判定从「无超时 + 日志含 PASS 串」改为**三硬闸**——①退出码为唯一权威判据(PASS 串降级为日志);②`SCRIPT ERROR` / `Parse Error` 扫描命中即红;③断言数为零即红。
2. **L2 接口层从零建承载**:重建 `game/tests/integration/`,把现成但游离的 `parity_anchor` 确定性对拍收编入闸表;补「存档 ⇄ RNG 快照往返」集成用例(README 章程已登记四类之一)。
3. **L3 补上屏壳切换**:新建屏切换探针(菜单→营地→冒险→战斗→结算→回营地),断言屏壳 `visible` 互斥与流程状态,入轻 CI。
4. **L1 补洞 + 术语订正 + 台账清理**:4 个零覆盖 core 模块补单测;「冒烟」一词回归其本义(秒级存活检查),不再作为验收主词;`game/smoke/` 游离脚本定性、golden 基线时效判定、补 S5 扫描规则。
**完成定义**:
- 三硬闸在位且**有反例实证**(造必然失败的套件/探针,CI 判定必红——不是推断,是跑出来的证据,留档);
- 三层各有在册承载(L1 `unit/` 27+ 套、L2 `integration/` 从 0 到 ≥1 套、`smoke/` 屏切换探针入 CI);
- **本地闸**判定三硬闸在位且有反例实证(`smoke_all` 已在本地轻闸跑,本批不解决"跑不跑",只解决"判得对不对");CI 侧同步对齐(Q2 裁决后执行);
- 术语订正落到机械面(CI job 名/脚本注释/README 索引/CLAUDE.md 口径句);
- 批末四闸全绿;06 登记销号;反哺点列明待裁。
---
## 2. 现状事实(代码级,2026-10-15 实查;开工自行复核行号)
### 2.1 门禁判定面(A 类,代码直读)
> ✅ **CI 已删除(2026-10-15 用户令,本卡 v1.2 据此修订)**:用户裁「CI 是没有用的,后面也不打算去用它,把 CI 相关的东西都清除掉,不要留着死代码」⇒ `.github/workflows/` **全数删除**(分支 `ci-removal-20261015`),CLAUDE.md 硬性红线同步立规「**不建、不留、不参考任何 CI 配置;改门禁只改本地闸**」。
> ⇒ **验收门禁 = 本地闸**,唯一一套(`run_smoke.py` / `run_all.gd` / `probe_gate.sh` / `render_gate.sh` / `static_scan.sh`),由 CLAUDE.md 纪律驱动人工执行。
> ⇒ **本卡所有改造对象均为本地闸**;原 T1 第④项「CI 同步对齐」、T2「CI job 改名」、Q2「是否并入 CI」**全部作废删除**。
| # | 事实 | file:line |
|---|---|---|
| F1 | 单元 runner 只判「退出码 ≠ 期望」,**无断言数校验、无脚本错误扫描** | `game/tests/run_all.gd:60-75`(`_run_suite`) |
| F2 | 断言出口在 `failures==0` 时即 `all green (%d checks)` 并 `quit(0)`——**checks 为 0 同样绿** | `game/tests/unit/test_kit.gd:41-49` |
| F3 | 四件入 CI 探针判定 =「超时(124/137) → FAIL;否则 `grep -q "PASS"` → 绿」,**退出码完全不参与**;全库仅 `ui_click_probe` 段查退出码 | `tools/probe_gate.sh` ui_input/ui_struct/ui_contrast 各段;`.github/workflows/tests.yml:59-75`(`run_probe`) |
| F4 | `run_smoke.py` 有**三闸**(退出码 + 摘要失败数 + `SCRIPT ERROR`/`Parse Error` 扫描),但**只跑本地,CI 零调用** | `tools/run_smoke.py:22-30`(RECOMMEND 头注 + 三闸实现) |
| F5 | CI 只跑 `run_all.gd`,`smoke_all.gd`(6128 行 / 967 处 check / 运行期 1086 项)**完全不在 CI** | `.github/workflows/tests.yml` smoke job;`game/smoke/smoke_all.gd` |
| F6 | CI workflow 注释 L52/L55 写「轻闸**三**探针」,实际 L72-75 跑**四个** | `.github/workflows/tests.yml:52,55,72-75` |
| F7 | `example_test.gd`(26 行 / 2 断言,样板件)登记在生产 manifest 里实跑 | `game/tests/run_all.gd:29` |
### 2.2 灾害先例(B 类,仓库登记可查)
| # | 事实 | 出处 |
|---|---|---|
| F8 | 「GDScript 运行时脚本错误不改变退出码、不计失败数、静默中断所在函数剩余断言——首跑『309 项 0 失败』实为断言未跑全」 | `CLAUDE.md:21`、`tools/run_smoke.py` 头注 |
| F9 | 迭代 21 基线冒烟 864/0,但 `run_smoke` 判 FAIL(SCRIPT ERROR 51 行) | `active.md` 476 条 |
| F10 | `--script` 模式编译器不解析裸 autoload 标识符 → **`pacing_queue_test` 实证 Parse Error** | `active.md` 494 条坑册② |
⇒ F10 尤其要紧:**Parse Error 就发生在 CI 会跑到的套件里**。有 `run_smoke` ③闸时当场抓住;无③闸的 CI 上只表现为「断言少跑一截、退出码仍是 0」。
### 2.3 三层承载面(A 类)
| 层 | 现状 | 出处 |
|---|---|---|
| L1 单元 | 27 套件 / 412 处断言 / manifest 全登记零孤儿;**core 层 4 模块零直接指名**、呈现层 49 件中约 38 件零指名 | `game/tests/unit/`;审计 §2.3 / §2.4 |
| L2 接口 | `integration/` 空壳已删,**零承载**;契约面仅 `system_contracts_test.gd` 15 断言(单元级,非集成) | `game/tests/README.md` 章程段 |
| L2 现成零件 | `tools/parity_anchor.py` + `game/tests/evidence/parity/anchor.json`(三变体×RUNSUM+JSONL 双面、字节级逐位一致)——**不入任何闸表、README 未登记** | `tools/parity_anchor.py:1-25` |
| L3 屏切换 | 全库 `change_scene` **零命中**;项目改走三屏壳 + `visible` 互斥。**CI 上屏切换断言 = 0**;`ui_click_probe` 有 `_exit_level_screen` 走查但归 `--click` 不入 CI | `game/presentation/main.gd:51-53`;`game/smoke/ui_click_probe.gd` |
### 2.4 台账与扫描域(A/B 类)
| # | 事实 | file:line |
|---|---|---|
| F11 | `game/smoke/` 38 个 `.gd` 中 **17 个无 `.tscn` 宿主**(除 `smoke_all`/`probe_ui_util` 定位清楚外,15 个游离:MC 模拟、defense_scan、rhythm_batch、calibrate_boards、verifier_batch 等),无闸无登记 | `game/smoke/` 目录实查 |
| F12 | golden 视觉基线冻结于 **2026-09-30**,经历 iter25(全局主题)/iter26(按钮五态、Nearest 过滤、换墨、光柱)两次呈现层换代,仍记「0.00%(登记即结未重制)」 | `tools/golden_compare.py:11`;`active.md` 492 条 |
| F13 | 登记八条扫描域,**S5/S6/S7/S8 四条从未实现**(TODO 段);其中 S5 恰是 F10 踩过的坑 | `game/tests/static_scan.sh` 末 TODO 段 |
| F14 | S9 类型闸**显式豁免 smoke/tests**,测量面 untyped 容器 157 处无闸管(tests 24 + smoke 133) | `tools/audit_type_hints.py:5` 注释、`:20` SCAN_DIRS |
| F15 | 术语:「冒烟」一词同时指代最强面(`smoke_all`)、单元面(`run_all`)、CI job 名;「逻辑冒烟」标题即其术语表,全文无单元/集成/系统三层概念 | `design/dev/architecture/architecture.md` 测试与质量域节 |
---
## 3. 设计契约(红线,违反=L3 停机呈报)
🔴 **契约 1 · 零玩法行为变更**:本批只动测试、门禁、注释、文档、脚本判定。**任何「必须改被测代码才能转绿」的断言 = L3 停机呈报**(唯一例外:断言本身写错且与现行设计冲突,走 §9 白名单 7)。
🔴 **契约 2 · 三硬闸不可绕过**:退出码是唯一权威判据,PASS 串只作日志不作判据;脚本错误扫描命中即红;断言数为零即红。**不得为了让某件历史探针转绿而给它开豁免口**(需豁免=L3 呈报)。
🔴 **契约 3 · 现有断言面只增不减**:27 套件 + `smoke_all` 1086 项零删减。删任何一条断言须呈报并给依据。
🔴 **契约 4 · 不引入第三方测试框架**:gdUnit4 维持 deferred 状态。本批在既有双载体(`-script` + 探针场景)内解决。
🔴 **契约 5 · 架构事实唯一源(用户 2026-10-15 立规;v1.4 随决策文档体系废除改写口径)**:架构事实只认 `design/dev/architecture/architecture.md`。本批不得回改任何历史文档、不得另立决策文档体系;涉及历史沿革只作引用。仍在生效的架构约束如需变更,**改架构文档**。术语订正只动机械面(CI job 名 / 本地闸脚本注释 / README 索引 / CLAUDE.md 口径句);`game/smoke/` 文件名改名见 §10 Q7。
🔴 **契约 6 · 探针契约零破坏**:现存 11 个探针的挂载面与断言语义不改(T1 只改判定,不改断言内容)。
🔴 **契约 7 · 测试执行两入口铁规不破**:本批跑测属「迭代 loop 开发」入口(开工后合法);**MC/对拍类跑批仍按 MC 触发细则**——无数值改动零跑,`parity_anchor` 只作收编验证用,不得借机重锚。
---
## 4. 范围边界
**做**(对应审计 §六 A/B/C/D/F 级):
- A 级全部(CI 三硬闸、术语订正、CI 卫生两件、`smoke_all` 入 CI 或明示不进)
- B 级(L2 建承载 + parity 收编 + 首个集成用例)
- C 级(屏壳切换探针)
- D 级(4 个 core 零覆盖模块补单测;呈现层按风险排前三位)
- F 级(游离脚本定性、golden 时效判定、补 S5 规则)
**不做**(明示去向):
- **不重写 `smoke_all.gd`**(它是对的,只是名字错+不在 CI)——只做入 CI 与改名裁点,不拆不重构。拆分区归后续批,登记 06。
- **不做行覆盖率度量**(Godot 无内建;引入第三方工具属新基建,登记 06 另议)。本批的覆盖补洞靠「零指名清单 + 人工判定」。
- **架构事实以 `design/dev/architecture/architecture.md` 为准**(历史决策文档已废,正文不回改)。
- **不清理 E 级卫生件**(test_kit static var、4 套件自建断言出口、157 处 untyped 容器、76 处 lambda)——**登记 06 归后续批**;本批只统一新写断言的出口。
- **不修 07 人闸**(实机目检六批)——不归本批。
---
## 5. 关联文档同步面
| 文档 | 关联点(本批会改变它的哪条陈述) | 同步义务 |
|---|---|---|
| ~~`.github/workflows/tests.yml`~~ | —— | **已删除(2026-10-15 用户令,分支 `ci-removal-20261015`);本卡不再涉及** |
| `tools/probe_gate.sh` | 四件探针判定从「PASS 串」改为「退出码 + 错误扫描 + 断言下限」 | **机械面·随批更新** |
| `game/tests/run_all.gd` | manifest 条目数(example_test 去留);`_run_suite` 增断言数校验 | **机械面·随批更新** |
| `game/tests/unit/test_kit.gd` | `finish()` 零断言判红 | **机械面·随批更新** |
| `game/tests/README.md` | 目录布局(增 `integration/`);「逻辑冒烟」措辞;断言计数双口径 | **机械面·随批更新** |
| `CLAUDE.md` | :21 冒烟入口描述;:106 批末四闸口径;:116 轻闸/重闸定义(若 `smoke_all` 入 CI 则轻闸定义变) | **机械面·随批更新** |
| `roadmap.md` | **迭代 29 行已编入(2026-10-16 用户令)**+「迭代 30+ 位空·M4 数值批已废」记录行新增;原「M4 顺延 29+」表述作废 | **已完成(编入笔=v1.5)**;批内 T8 仍负 roadmap 同步义务(估时/状态/切分变化时) |
| `design/gdd/06-待办清单.md` | 本卡登记条(写卡后登记,收口销号);E 级卫生件 + 覆盖率度量 + smoke_all 拆分 三条新登记 | **机械面·随批更新** |
| `active.md` | 状态快照与台账 | **机械面·随批更新** |
| `game/tests/static_scan.sh` | 增 S5 规则 | **机械面·随批更新** |
| `design/dev/architecture/architecture.md`「测试与质量域」节 | **已承接**(v1.4 前由 10-15 文档批完成):双载体边界、S1–S8 扫描域、外部 wall-clock 超时、gdUnit4 deferred 现以架构文档为唯一记载 | **反哺点一已闭环** |
| `design/dev/architecture/architecture.md` | **反哺点二已闭环**(v1.4):「测试与质量域」节已成文为测试域事实源,文档状态立「事实源地位」条(架构唯一事实源);原文档漂移句随两节删除清除 | —— |
| `design/dev/code-conventions.md` | 测试纪律节(提交前全量、探针标配) | **设计权威文档·不随批更新** → **反哺点三** |
| `design/gdd/07-体验验证清单.md` | 若屏切换探针把部分目检项转成机检,相关条目需改写 | **反哺点四**(待裁:哪些人闸项可转机检) |
---
## 6. 任务卡
> 估时按 Claude 工作日口径;每卡出口自跑轻闸,全批出口跑重闸(CLAUDE.md:116)。
### T0 证据坐实与基线冻结(≈0.5–1d,**必先做**)
审计报告的核心结论目前是**推断(C 类)**。本卡的第一步是把它们坐实成证据,否则后面所有改动都建立在推断上。
- **T0-① 反例实测(关键)**:造两个必然失败的靶子——(a) 单元套件:断言恒假但退出码走 0 的路径(模拟脚本错误中断场景);(b) 探针:先打印 `PASS` 再 `quit(1)`。分别跑 `run_all.gd` 判定与 `probe_gate.sh` 判定,**记录是否判绿**。判绿 = 缺陷坐实,留档作为 T1 的验收基准;判红 = 我读错了,立即回报改结论。
- **T0-② 时长预算实测**:实测 `run_all` 全套(27 子进程)与 `smoke_all` 的墙钟时长,对照 CI 的 `timeout 120s` 与 `--import 300s` 余量。结论进 Q2/Q3 裁决依据。
- **T0-③ 冻结基线读数**:冒烟项数(预期 1086)、run_all 套件数(27)、探针数(11 / 本地轻闸 4)、static_scan 域状态。⚠ **开工日须重新採样**——上述数字是 iter28 收口前的旧读数,若本批在 iter28 之后开工,一律以 master 当期读数为准(见 §0)。
- **T0-④ 复核行号**:§2 全部 file:line 开工日复核一遍,漂移即订正卡片(审计会话的行号未经二次复核)。**外加**:复核时须确认 iter28 是否已改过 `game/smoke/` 与 `CLAUDE.md` 相关行。
### T1 本地闸判定三硬闸(≈0.5–0.75d,**本批改造成效最大的一卡**)
> **主体是本地闸**(CI 未通电,见 §2.1 前提)。CI 侧只在第 ④ 项做同义对齐,不单独开工——只改 CI 等于给一台没插电的机器换零件。
- **① `probe_gate.sh` 探针判定**(**现役缺陷**,每天在跑):四件探针判定从「超时 + `grep -q "PASS"`」改为「**退出码优先** + PASS 串降级为日志」,全部对齐 `ui_click_probe` 段现有口径。
- **② 脚本错误扫描闸**(**现役缺陷**):抽成一个**闸件脚本**(复用 `tools/run_smoke.py` 的 `ERROR_MARKERS` 逻辑,不复制代码),同时作用于 `run_all.gd` 各子套件输出与 `probe_gate` 各探针日志。
- **③ 断言数下限闸**(**现役缺陷**):`Kit.finish` 在 `checks == 0` 时判红;`run_all` 汇总校验实跑套件数 == manifest 条目数。
- ~~**④ CI 同步对齐**~~ —— **v1.2 删除**:CI 已全数移除,无对象可对齐。
- **④ `example_test.gd` 退出生产 manifest**(T0-④ 确认无其他依赖后删条目,文件保留作样板)。
> **注意**:`run_smoke.py` 三闸**已完整**,不在 T1 改动范围——它是本批的**参照样板**,②的闸件逻辑从它抽,不是重写它。
### T2 术语订正(机械面)(≈0.25d)
> 前提:历史决策文档不回改(🔴 契约 5)。本节只改机械面文本;架构事实以 `design/dev/architecture/architecture.md` 为准。
- `run_all.gd` / `README.md` / `CLAUDE.md:21` 中「逻辑冒烟」→「单元套件」。
- 定义「冒烟(smoke)」一词在项目内的**唯一保留语义**:**秒级存活检查**(能否启动、能否加载表、能否进主屏)。写进 README 术语小节。
- (CI job 改名项随 CI 删除而取消;`game/tests/README.md` 的门禁节已改为本地闸口径,由 `ci-removal` 批执行)
- 在 README 术语小节同步写明:`run_smoke.py` 之名是历史遗留,**其承载的是系统级断言面,不是冒烟**(改名与否走 Q7)。
### T3 L2 接口层建承载(≈1–1.5d)
- 建 `game/tests/integration/`(README 章程早有此规划,一直空壳)。
- 把 `tools/parity_anchor.py` 收编进闸表(重闸级,周期执行),README 登记为「回放确定性」集成用例。
- 新增首个集成用例:**存档 ⇄ RNG 快照往返**(README 章程登记四类之一)——存→读→同种子续跑,输出与未存照常跑的那一路逐位一致。
- `integration/` 纳入 `run_all` manifest 或独立 runner(走 Q4 裁决)。
### T4 L3 屏壳切换探针(≈1–1.5d)
- 新建 `game/smoke/screen_flow_probe.gd/.tscn`:走 menu→camp→adventure→战斗→结算→回营地 主流程,**断言屏壳 `visible` 互斥**(任意时刻三屏壳最多一屏可见)+ 每步流程状态位 + 回退路径(放弃二击→终局→回营地)。
- 入轻 CI(与 ui_struct/ui_contrast 同段)。
- 断言源:沿 `ui_click_probe` 已实证的 `_exit_level_screen` 路径与 `smoke_all:_smoke_scene_split()`(:147)现有断言面,不另起炉灶。
### T5 L1 覆盖补洞(≈1–1.5d)
- 4 个 core 零指名模块补直接单测:`minesweeper_rules`(揭示/扩散判定)、`status_container`(状态生命周期)、`config_db_resolver`、`config_db_cross_validator`。
- 呈现层按风险取前三:`hud_presenter`、三屏壳(`adventure_screen`/`camp_screen`/`menu_screen`)、`battle_narrator`。
- 全部进 `run_all` manifest。
### T6 扫描域补 S5(≈0.5d,视余量带 S6)
- 补 S5 规则(core 禁裸引用 autoload 全局标识符)——按 code-conventions「新违规案例必须先补扫描规则再修码」纪律,F10 已是真实反面案例却无规则。
- S6 视 T6 余量决定;S7/S8 仍留 TODO 并登记 06。
### T7 台账与时效清理(≈0.5d)
- `game/smoke/` 15 个游离脚本逐个定性:在役的写进 README 台账(用途 + 触发方式 + 结果由谁判);失效的退役(走 doc-conventions §10 退役规则)。
- golden 基线时效判定:跑一次 render_gate 判别「三代换代面是否落在裁剪区」——判有效则留并加注,判失效则显式重制并呈报(**不擅自重制**)。
### T8 文档同步闸与全闸(≈0.5d,**末卡**)
- §5 全部**机械面**文档更新完毕(roadmap 编入须用户令)。
- **反哺点**逐条列明进实现报告(测试术语、code-conventions 测试纪律、07 人闸项转机检)。
- 06 登记销号 + 新登记三条(E 级卫生件 / 覆盖率度量 / `smoke_all` 拆分)。
- 批末四闸全绿 + 重闸周期件全绿。
**估时合计 T0–T8 ≈ 5.5–7d**;天然切分点:**T1 末**(门禁语义已修,可单独交付见效)、**T4 末**(三层齐备)。软盒 7d,超盒按切分点拆两批。
---
## 7. 验证闸(逐需求点验收清单)
**能力验证(自动化;谁验=批末四闸闸机,全绿才合并)**
| # | 需求点 | 测什么 | 落点 |
|---|---|---|---|
| V1 | 三硬闸-退出码 | **反例实证**:探针打印 PASS 后 `quit(1)` → 闸判红 | T0-① 留档脚本 + T1 探针判定段,新增进 probe_gate 自检 |
| V2 | 三硬闸-脚本错误 | **反例实证**:注入一处 `Parse Error` 的套件 → 闸判红(而非绿) | 新增闸件单测(进 `run_all`) |
| V3 | 三硬闸-断言下限 | **反例实证**:零断言套件 → 判红 | `test_kit.finish` 断言 + `run_all` 套件数校验,新增进 `run_all` |
| V4 | 术语订正(机械面) | 本地闸脚本注释 / README 术语小节 / `CLAUDE.md` 口径句 / CI job 名 四处措辞到位;「冒烟=秒级存活检查」定义小节在册 | 源码断言(grep 旧措辞零命中) |
| V5 | L2 承载 | `integration/` 目录在册 + ≥1 套进闸;parity_anchor 入闸表且 README 登记 | 新集成套进 `run_all`;parity check 进重闸 |
| V6 | L2 存档往返 | 存→读→同种子续跑,与未存档一路输出逐位一致 | 新集成套断言 |
| V7 | L3 屏切换 | 六步主流程 + 三屏壳 `visible` 互斥(任意时刻 ≤1 可见)+ 放弃回退路径 | 新探针 `screen_flow_probe`,入轻 CI |
| V8 | L1 补洞 | 4 个 core 模块各有 ≥3 条真值断言;呈现层三件各有断言面 | 新单测进 `run_all`(27 → 27+7) |
| V9 | S5 扫描规则 | 规则在位且有反面案例触发 | `static_scan.sh` 新段 + 反例 fixture |
| V10 | 台账与 golden | 15 个游离脚本全定性(在役入台账/失效退役);golden 时效判定结论留档 | README 台账节;render_gate 结论进实现报告 |
| V11 | 零回归 | 批末四闸全绿:冒烟 1086/0(基线复核)+ run_all 全套 + probe --click/--full + 改前改后读数对拍;render_gate golden 对基线零漂移 | 全闸 |
**测试方式两分法**:V1–V11 全部归**能力验证**(自动化闸,调试接口直改游戏数据沿用现役 DebugOverlay/DS_DEBUG 门控);本批**无用户目检项**(纯基建,不改变玩家可见行为)——若 T7 golden 判失效需重制,则该处置登记 07 观测项并呈报。
**闸口径**:轻闸/重闸按 CLAUDE.md:116 分级周期口径;每卡出口自跑轻闸(触碰 presentation 加跑 ui_click/ui_layout);批末四闸全绿才合并;测试执行两入口铁规不破(开工后属迭代 loop 入口,跑测合法);MC/对拍跑批仍须用户批准。
---
## 8. 异常分级
- **L1(自救)**:断言拼写错、反例 fixture 没造对、manifest 条目漏登记、注释订正漏处——立红即修。
- **L2(记录继续)**:探针判定改法的具体写法、`integration/` 目录内部分类、新单测文件命名与落位、游离脚本定性存疑(先按"在役"登记并标注存疑)——登记随批内消化。
- **L3(停机呈报)**:触碰 §3 任一红线;**T0-① 反例实测判定为「判红」**(即我读错了,缺陷不成立,须重估全批范围);`smoke_all` 入 CI 后超时撞墙且无解;golden 基线判失效;新探针与 `ui_click_probe` 走查路径冲突到必须改现有探针契约;改判定后现有某探针/套件暴露**真实存量红**(不是本批引入的)——须呈报由用户定归属。
---
## 9. 代拍板白名单(超清单必呈报)
1. 三硬闸的具体实现写法(闸件是独立 `.sh` 还是并入既有脚本;错误扫描正则沿用 `run_smoke.py` 的 `ERROR_MARKERS`)。
2. 断言数下限的具体阈值(每套件 ≥1 还是 ≥3;倾向 ≥1,防误伤小套件)。
3. `integration/` 内部目录分类方式(按系统分 vs 按用例类型分)。
4. 新集成套是并入 `run_all` manifest 还是独立 runner(Q4 裁后执行细节)。
5. 新单测/新探针的文件命名与落点(不违背 code-conventions 命名纪律前提下)。
6. 4 个 core 模块单测的断言面取舍(覆盖到什么粒度)。
7. **断言本身写错且与现行设计冲突**时的修法(契约 1 的唯一例外:须在报告中逐条列明改了哪条断言、依据哪份设计)。
8. 游离脚本「在役/失效」的判定(存疑者按在役登记并标注)。
9. S5 规则的正反例 fixture 构造方式。
10. 提交切分与 worktree 分支命名(走既有 worktree 纪律)。
---
## 10. 待裁点
| # | 事项 | 备选案 | 推荐 |
|---|---|---|---|
| **Q1** | **测试域在效架构事实(双载体边界/S1–S8 扫描域/外部 wall-clock 超时/gdUnit4 deferred)的承载去向?**(v1.4 注:**已闭环**——事实已由 10-15 文档批迁入 `design/dev/architecture/architecture.md`「测试与质量域」节;七份历史决策文件已删,沿革归 git commit log) | —— | **已闭环**:并入 `design/dev/architecture/architecture.md` 增「测试与质量域」节(先于 Q1 裁断、由用户令直接执行) |
| ~~**Q2**~~ | ~~`smoke_all` 是否写进 CI 配置~~ | —— | **v1.2 作废**:CI 已删除;且 `run_smoke` 本就是本地轻闸首项(`CLAUDE.md:116`),不存在"要不要跑"的问题 |
| **Q3** | `run_all` 若因套件增长撞 CI `timeout 120s` 怎么办? | 甲=提 timeout 值;乙=改并发执行(有 test_kit static var 串台风险,见 E-11);丙=分层,L1 入 CI、慢件归重闸 | **甲**(先提值,最省事);并发与分层归后续批登记 06 |
| **Q4** | `integration/` 是并入 `run_all` manifest 还是独立 runner? | 甲=并入 manifest(统一入口,但致命类 expect=1 语义要扩);乙=独立 runner(CI 加一步) | **甲**——沿用既有 manifest 协议,零新机制 |
| **Q5** | S9 类型闸是否收回对 smoke/tests 的豁免(157 处 untyped 容器)? | 甲=维持豁免(项目既有裁决,本批不动);乙=收回并清偿存量 | **甲**——本批不扩大范围,清偿归后续批登记 06 |
| **Q6** | golden 基线若判失效如何处置? | 甲=显式重制并呈报;乙=维持旧基线 + 加失效注记 | **甲**(沿 iter26 T6 先例:核热区限预期面→显式重制+呈报→复闸),但**须先判再动,禁先动后判** |
| **Q7** | `tools/run_smoke.py` 与 `game/smoke/smoke_all.gd` 是否改名?(它们承载的是**系统级断言面**,名却叫 smoke——术语倒挂的源头之一) | 甲=不改名,只在 README/CLAUDE.md 加注「名是历史遗留,实为系统级断言面」;乙=改名(牵 `run_smoke.py` 调用点、CLAUDE.md 三处口径句、active.md 大量历史行文) | **甲**——历史行文中「冒烟 N 项」出现几十次,改名会把成本转嫁给所有历史记录的比对;加注即可纠正认知 |
**开工门**:Q1–Q6 裁决全落(零待裁点)+ 用户 `/iter` 发令 = U0。Q2 可在 T0-② 实测后裁(不阻塞 T0/T1 开工)。