diff --git a/reports/iter29-实现报告-2026-10-16.md b/reports/iter29-实现报告-2026-10-16.md deleted file mode 100644 index 180caba0..00000000 --- a/reports/iter29-实现报告-2026-10-16.md +++ /dev/null @@ -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**。 diff --git a/roadmap.md b/roadmap.md index 6504b50b..76be4d80 100644 --- a/roadmap.md +++ b/roadmap.md @@ -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 周 | — |