Merge branch 'iter29-closure-20261020'
This commit is contained in:
commit
fb2dcbf6ec
2 files changed
+1
-170
No files matched your search
@@ -1,169 +0,0 @@
|
||||
# 迭代 29 实现报告 · 测试分层与门禁语义修正批(2026-10-16)
|
||||
|
||||
- 需求卡片:已退役(git 历史可考)——原「测试分层与门禁语义修正批」**v2.0**,三轮复查定稿、零待裁点
|
||||
- 开工门 U0:用户 `/iter 29` 发令(2026-10-16)。基线 master `752c9a0a`
|
||||
- worktree:`g-plan2-iter29-testlayers`,分支 `iter29-testlayers-20261016`,八提交(`698d864d` … `c3b64030`)
|
||||
- 用户原始诉求(本批要兑现的三条):①最单元的逻辑代码有它的单元测试;②整体的模块有它的接口测试;
|
||||
③整体的系统在执行之后,也有它 UI 方面的和场景切换相关的测试。**并且验收不能只是"冒烟"——
|
||||
可以是冒烟,但更重要的是逻辑正确性**
|
||||
|
||||
---
|
||||
|
||||
## 一、批次概览
|
||||
|
||||
| 卡 | 内容 | 状态 |
|
||||
|---|---|---|
|
||||
| T0 | 证据坐实与基线冻结(三反例实测 + 墙钟 + 行号复核 + 存量红留档) | ✅ |
|
||||
| T1 | 本地闸判定三硬闸(退出码/脚本错误扫描/断言数下限) | ✅ |
|
||||
| T2 | 术语订正 + CI 残留清除(机械面) | ✅ |
|
||||
| T3 | L2 接口层建承载(`integration/` + parity 条件闸件收编) | ✅ |
|
||||
| T4 | L3 屏壳切换探针 `screen_flow_probe`(入轻闸默认段) | ✅ |
|
||||
| T5 | L1 覆盖补洞(4 个 core + 呈现层两件) | ✅ |
|
||||
| T6 | 扫描域:S3 路径修正 + 零命中自检 + 补 S5 | ✅ |
|
||||
| T7 | 游离脚本台账 + golden 时效判定 | ✅ |
|
||||
| T8 | 文档同步闸(架构事实源回写 + 06 登记)与全闸 | ✅ |
|
||||
|
||||
**读数**:冒烟 **1127/0**(与开工日持平,本批零新增冒烟断言);单元套件 **27 → 33 套**;
|
||||
在闸探针 **9 → 10 件**(新增 `screen_flow_probe`);轻闸 `--quick` 墙钟 363s → 405s;
|
||||
全量重闸 **exit=0,存量红零新增**。
|
||||
|
||||
---
|
||||
|
||||
## 二、四件事的交付
|
||||
|
||||
### ① 本地闸三硬闸(本批改造成效最大的一卡)
|
||||
|
||||
判定语义从「超时 + 日志含 PASS 串」扭到三硬闸,**每一条都有反例实证**(不是推断,是跑出来的):
|
||||
|
||||
| 硬闸 | 反例构造 | 改前判定 | 改后判定 |
|
||||
|---|---|---|---|
|
||||
| 退出码为唯一权威判据 | 探针打印 PASS 后 `quit(1)` | `[probe_gate] 全绿` ❌ | `FAIL — 退出码=1(PASS 串不抵)` ✅ |
|
||||
| 脚本错误扫描命中即红 | 套件中途触发 `SCRIPT ERROR` 后仍退出 0 | `[run_all] PASS` ❌ | `FAIL — 输出含脚本错误标记` ✅ |
|
||||
| 断言数为零即红 | `Kit.finish()` 时 `checks==0` | `all green (0 checks)` ❌ | 返回专用退出码 **2** → `FAIL` ✅ |
|
||||
|
||||
实现要点:
|
||||
- `probe_gate.sh` 八件探针判定统一走 `verdict` 函数(退出码优先 → 脚本错误扫描 → 签名标记附加判据);
|
||||
`ui_click_probe` 本就走退出码语义、日志不写 PASS,签名标记传它自己的 `click_probe` 串,不强加。
|
||||
- 新闸件 `tools/error_scan.py`:错误标记 `import` 自 `run_smoke.py` 的 `ERROR_MARKERS`,**禁第二份清单**。
|
||||
- `run_all.gd` 侧遇到两个本机事实(都写进注释防复发):
|
||||
① `OS.execute` 的输出管道在本机恒空(实测 0 行)——子套件输出必须交给系统命令解释器重定向落盘再读;
|
||||
② `OS.get_executable_path()` 返回的是 GUI 子系统 exe,它起的子进程 stdout/stderr 句柄无效——
|
||||
必须换 `*_console.exe`(`GODOT_BIN` 优先)。
|
||||
- 断言下限用**专用退出码 2**(不是 1):致命类套件 manifest 期望 1,零断言若也返回 1 会被 expect 协议放过。
|
||||
|
||||
### ② L2 接口层从零建承载
|
||||
|
||||
- 建 `game/tests/integration/`,首个集成用例 `save_rng_roundtrip_test.gd`(17 条断言):存档 ⇄ RNG 快照往返,
|
||||
跨 SaveManager(落盘 + 版本迁移链)× AdventureRunner(断点消费)× RngService(续跑零跳号)× 停点态确定性。
|
||||
与 `unit/core/breakpoint_resume_test` 的分工:那件是单测只验首战开板一致,本件验跨系统闭环,
|
||||
含**「旧版本档经迁移链后断点仍可续玩」**这条真实玩家路径。
|
||||
- `tools/parity_anchor.py` 收编为重闸 **11 号条件闸件**:每次跑批都对 merge-base 做一遍触面判定
|
||||
(`data/tables`/`rng_service`/`save_manager`/`meta_progression`/`settlement`/`damage_math`/
|
||||
`damage_controller`/`battle_orchestrator`/`adventure_monte_carlo`),未触面 SKIP;**触面也不自动跑**——
|
||||
它调的是 MC 脚本,属「跑批发起前须用户批准」红线,闸只负责把触面判定做一遍并显式要求批准
|
||||
(批准后 `PARITY_APPROVED=1` 重跑)。**本批即判为触面(改了 `battle_orchestrator.gd`),故挂在 NEED-APPROVAL 待你批准**(见第六节)。
|
||||
|
||||
### ③ L3 屏壳切换探针
|
||||
|
||||
新建 `game/smoke/screen_flow_probe.gd/.tscn`:走 菜单→营地→菜单→冒险→(关卡屏⇄战斗屏)→放弃→终局→菜单
|
||||
六步主流程与两条回退路径,每步断言屏级互斥 + 流程状态位,入**轻闸默认段**。全库 `change_scene` 零命中、
|
||||
屏切换全靠 visible 互斥,这正是最容易被装配顺序改动悄悄破坏又最难被单测覆盖的面。
|
||||
|
||||
判据两处订正(首跑实测抓出):① 屏标记不能裸看 `visible`——战斗容器 `battle_root` 在局外也 visible,
|
||||
会把 boot 态误判成「菜单与冒险同时在场」;② 互斥不能用「可见屏计数 ≤1」——终局覆盖层合法叠在冒险内容之上
|
||||
(终局态 `is_adventure` 尚未收束,是设计如此),改为冲突对判定,合法叠加仅「终局+冒险」这一对。
|
||||
|
||||
### ④ L1 补洞 + 术语订正 + 台账清理
|
||||
|
||||
- 新增六套(全部进 manifest):`board_generator`(9 条)/`minesweeper_rules`(12 条)/
|
||||
`config_db_resolver`(8 条)/`config_db_cross_validator`(4 条)/`hud_presenter`(5 条)/`battle_narrator`(9 条)。
|
||||
三屏壳的断言面由 ③ 的探针承担,不另起第二载体。
|
||||
- 术语:「冒烟」回归本义=秒级存活检查,写进 README 术语小节,并显式点破两处历史遗留命名
|
||||
(`run_smoke.py` 与 `game/smoke/` 名带 smoke,实际承载的是系统级断言面与场景探针——判定看语义不看文件名)。
|
||||
- 台账:`game/smoke/` 15 个不带 `.tscn`、不入闸的手动入口件逐个定性成表(用途 + 触发方式 + 结果由谁判),
|
||||
存疑一律按在役登记并标注,禁「没看见=死件」式清理。
|
||||
- golden:**先判再动**——实测棋盘三图 changed 0.00%/0.00%/0.00%、菜单 0.15%,均远低于 2% 阈值 ⇒ 判有效,
|
||||
留在位不重制(禁「重制一下就绿了」掩盖真实视觉回归)。
|
||||
|
||||
---
|
||||
|
||||
## 三、代拍板追认单(超清单必呈报;本批共 12 项)
|
||||
|
||||
> **追认结果(2026-10-17 用户令)**:以下十二项**全部同意,无修改意见**。第四节「经你裁决的四项」是施工中已问过、你已答的四条,不在本追认范围,维持原记录。
|
||||
|
||||
| # | 代拍板事项 | 处置与依据 |
|
||||
|---|---|---|
|
||||
| 1 | 三硬闸的具体实现写法 | `probe_gate` 抽 `verdict` 函数;错误扫描抽独立闸件 `tools/error_scan.py`(标记 import 自 `run_smoke.py`) |
|
||||
| 2 | 断言数下限的阈值 | 每套件 ≥1(防误伤小套件);零断言用专用退出码 2 与「断言失败=1」区分 |
|
||||
| 3 | `integration/` 内部目录分类 | 平铺,按系统命名(`save_rng_roundtrip_test.gd`)——首例而已,够用不加层 |
|
||||
| 4 | 新集成套并入 `run_all` manifest 的执行细节 | 零新机制,与单元套件同一入口同一协议 |
|
||||
| 5 | 新单测/新探针的文件命名与落点 | 沿用 `[system]_[feature]_test.gd` 与 `presentation/` 归位 |
|
||||
| 6 | 4 个 core 模块单测的断言面取舍 | 各 4–12 条真值断言,期望值一律由盘上/注入内容数出来(零玩法数值字面量) |
|
||||
| 7 | **断言本身写错且与现行设计冲突**时的修法(契约 1 唯一例外) | 见第四节「经你裁决的四项」,逐条列明 |
|
||||
| 8 | 游离脚本「在役/失效」的判定 | 存疑按在役登记并标注;`CLAUDE.md` 在册入口禁当死件 |
|
||||
| 9 | S5 规则的正反例构造 | 形态=标识符后紧跟 `.` 的成员访问,排除注释行与字符串字面量;core 实测零命中 |
|
||||
| 10 | 提交切分与分支命名 | 八提交按 T0–T8 一卡一笔;分支 `iter29-testlayers-20261016` |
|
||||
| 11 | **S3 零命中自检的实现形态** | 扫描路径收为单点变量 + 规则自己数扫描面文件数,为零即判红,且规则体包进自检成立分支 |
|
||||
| 12 | **条件闸件的 diff 触面路径表** | 写进 `run_all_gates.sh` 内(不自造第二份清单),见上文 ② |
|
||||
|
||||
---
|
||||
|
||||
## 四、经你裁决的四项(施工中冒出的分歧,未自答)
|
||||
|
||||
1. **`fx_grant_gp` 补进行来源断言豁免表**(你裁「本批补豁免」):`grant_segment.gd:221` 发「专长·GP 入账」
|
||||
效果行(source_class=`fx_grant_gp`、origin_cell=(-1,-1)),而 `SOURCE_EXEMPT_CLASSES` 只豁免了 `fx_reveal`
|
||||
⇒ 每局触发行来源断言失败 7 次,因脚本错误不改退出码被闸长期漏过。改动=一行常量加一个字符串,
|
||||
零玩法行为变更(迭代 21 同款修过 `fx_reveal`,本例是同一张表的漏网)。
|
||||
2. **manifest 增 `negative` 字段**(负面套件豁免错误扫描一闸):仅 `reward_dangling_treasure` 一件——
|
||||
坏表是它的测试目标,输出里的 Parse Error 属误伤。退出码与断言下限照判,不是开后门。
|
||||
3. **`config_db_test` 的 10 条被吞断言**(你裁「本批全修」):`rows` 是 `Array[Dictionary]` 定型属性,
|
||||
直接赋 untyped 数组字面量触发 `Invalid assignment` ⇒ **中断整个函数**,敌人表「行集构成三条纪律」
|
||||
的 10 条断言从未执行过(文件 31 处 check,实跑 21 条)。改 `rows.assign([...])` 后 26 条全跑,
|
||||
其中两条正向断言见红,根因是**夹具缺 depth-0 锚行**(`validate` 的 anchor 规则),补锚行后全绿。
|
||||
4. **`system_contracts_test` 的空桩换真实实例**(同上):`RngStub`/`UnitContainerStub` 赋给定型成员
|
||||
报 `Trying to assign value of type ''`,`RngStub` 类删除,`UnitContainer`/`StatusContainer` 改真实实例。
|
||||
|
||||
---
|
||||
|
||||
## 五、闸与读数(全量重闸 `tools/run_all_gates.sh`,commit `c3b64030`)
|
||||
|
||||
| 闸 | exit | 说明 |
|
||||
|---|---|---|
|
||||
| 01 冒烟全量 | 0 | **1127 项检查 0 项失败**(与开工日持平) |
|
||||
| 02 单元套件 | 0 | **33 套**(26 单元 + 1 集成 + 6 新单测);编排层另扫 `unit.log` 零命中 |
|
||||
| 03 探针 --click | 0 | 五件(含新增 screen_flow)全绿,残留 Godot=0 |
|
||||
| 04 探针 --full | 0 | 四件全绿 |
|
||||
| 05 渲染闸 | 0 | shot_checker 全过;棋盘 golden 三图 0.00%;菜单 0.15%(≤2%) |
|
||||
| 06 gdd_lint / 07 doc_link / 08 doc_term | 0 | doc_term 首跑抓到我新写的退役词「对拍」1 处,已改正 |
|
||||
| 08b active.md 体量 | 0 | — |
|
||||
| 09 静态扫描 | 1 | **存量红零新增**:S10 文件头存量 127 处,指纹与基线 `4354b2aa72a7` 一致 |
|
||||
| 10 熵增闸 | 0 | — |
|
||||
| 11 回放确定性对拍 | NEED-APPROVAL | 本批 diff 触及结算面,对拍需你批准发起(见第六节) |
|
||||
|
||||
**验收通过**(`all_green=true`,存量红零新增)。轻闸 `--quick` 墙钟 405s(开工日 363s + 新探针约 40s)。
|
||||
|
||||
---
|
||||
|
||||
## 六、待裁与遗留
|
||||
|
||||
0. **【合回状态:已合并进 master(`fff4f120`,十提交,2026-10-16)】** 本项原文为「暂不合回,等两个非本批红处理完」
|
||||
(2026-10-16 用户裁),两个红其后各自结案:①渲染闸 `menu_boot.png` changed=4.61%(菜单 golden 热区漂移)
|
||||
——基线已在合并代码上显式重采(`dac9a94b`),现行菜单与棋盘三张 golden 均 0.00%;②gdd_lint
|
||||
`01-核心循环.md` R5 缺流程图——由引入批补流程图结案(提交 `fcb8c3f8`+`8c18b52b`),基线 06 已回绿。
|
||||
本段为 2026-10-17 迁位批(`design/qa/` → `reports/`)改写,原待裁文字 git 历史可考。
|
||||
|
||||
1. **11 号闸的 parity 对拍要不要跑一次**(需你批准,我不能自行发起):触面判定是机械的路径匹配,
|
||||
本批对 `battle_orchestrator.gd` 的改动实际只是加一个断言豁免常量(零玩法行为变更、零数值改动)。
|
||||
- 若按「MC 无数值改动零跑」红线 ⇒ 不跑,闸维持 NEED-APPROVAL(回执里留名,不判失败);
|
||||
- 若要验证一次 ⇒ 批准后 `PARITY_APPROVED=1 tools/run_all_gates.sh`(三变体约 10–15 分钟)。
|
||||
2. **GDD 反哺点三条**(2026-10-17 用户逐条裁决:三条全部写入):
|
||||
- 「三硬闸」与「新写测试件全类型标注」→ **test-conventions.md §7**(code-conventions §3 已独立成文至此,故落此处);
|
||||
- 「判定可靠性优先于覆盖率」→ **test-conventions.md §8**(本批核心命题成节);
|
||||
- `screen_flow_probe` 转机检 → **07 v1.4**:㉖① 保留不注销,节首加机检承接注记。
|
||||
本条补登记为 06 专条(原漏登)。
|
||||
3. **06 已登记本批未收的七项**(不阻塞不排期):余下 3 个 core 零指名模块、`render_gate` 的脚本错误扫描缺失、
|
||||
S7/S8(S6 亦仍在 TODO)、E 级卫生件、行覆盖率度量、`smoke_all` 拆分、parity 触面判定偏粗。
|
||||
4. **本批最有价值的副产品**(2026-10-17 用户裁决:写入):脚本错误会**静默中断所在函数的剩余断言**而
|
||||
套件仍报「全绿」——本批至少三处实证(敌人表行集纪律 10 条零覆盖、`Callable` 形参类型写错让解析函数
|
||||
返回 null)。已由三硬闸之二机制性拦住;机制事实与「写验收函数须自证断言全数执行」义务已写入
|
||||
**test-conventions.md §8**。
|
||||
+1
-1
@@ -62,7 +62,7 @@
|
||||
| 迭代 26 | 已完成(2026-10-14) | 已退役(git 历史可考) | 2026-10-14 | — | 已退役(git 历史可考) |
|
||||
| 迭代 27 | 已完成(2026-10-14) | 已退役(git 历史可考) | 2026-10-14 | — | 已退役(git 历史可考) |
|
||||
| 迭代 28 | 已完成(2026-10-15) | 已退役(git 历史可考) | 2026-10-15 | — | 已退役(git 历史可考) |
|
||||
| 迭代 29 | 已完成(2026-10-16) | 已退役(git 历史可考) | 2026-10-16 | — | reports/iter29-实现报告-2026-10-16.md |
|
||||
| 迭代 29 | 已完成(2026-10-16) | 已退役(git 历史可考) | 2026-10-16 | — | 已退役(git 历史可考) |
|
||||
| 迭代 30 | 已完成(2026-10-17) | 已退役(git 历史可考) | 2026-10-16 | — | reports/iter30-实现报告-2026-10-17.md |
|
||||
| 迭代 31 | 待开工 | reports/需求卡片-代码术语刷新批-2026-10-18.md、reports/需求卡片-凭证件裁削批-2026-10-18.md | 2026-10-18 | ≈1.5 周 | — |
|
||||
|
||||
|
||||
Reference in new issue
Block a user