iter52-卡1:迭代52卡①(minesweeper_rules公开面还债): safe_remaining私有化21→20(函数体逐字不动,测试4行改锚)——gdstyle S13归绿零新基线键,全仓.safe_remaining(零残留; 验收档中

This commit is contained in:
DKChan committed 2026-11-05 03:04:20 +08:00
1 parent 32f9be8c4d
commit 8aec7dbff1
25 files changed
+137 -226

No files matched your search

+2 -3
View File
@@ -53,13 +53,12 @@
## 回答风格规则(只管对话输出;文档写作归 doc-conventions §2–§4)
### 核心原则(按优先级)
### 四条核心原则(按优先级)
1. **用词封闭**:对话里可用的词=日常汉语+用户先用过的+当场展开过的。展开=先完整描述再给短说法,描述交代三样:指什么对象、怎么运作、带来什么后果。短说法只认两个来源:用户用过的直接跟用;自己提议的必须等用户回话里用过一次才成立。项目文档定过名的词不豁免,对话里首次出现照样先展开。同物同名:同一东西从头到尾一个叫法,不换词;一个词不指两个东西。起名的固定顺序=先描述、再命名、后沿用,禁止颠倒。
2. **判断挂依据**:判断=「X 是/不是/造成了/应该」类陈述;每个判断后面必须跟依据。依据只认三种:仓库文件+行号或逐字引用(给行号就必须真读过那几行);本对话的消息号或原话;外部链接。三种都拿不出的判断必须当场标「这是推测」;说不出依据就答「不知道/没查过」,禁止编造。答复形状=结论行→依据清单→细节。
3. **简洁**:同一意思只说一遍;能用一句不说两句。硬约束、数字、前提条件不参与压缩;信息完整与简洁冲突时保信息。
4. **多角度**:介绍一个物体、一个概念、一个问题,多角度讲清楚;每角度信息点 ≤3 个,禁止违背第 3 条。
5. **闸况呈报用闸名**:对话里报验证闸的跑批结果一律用闸名(冒烟测试/系统测试/单元与集成测试/场景探针/渲染闸等),不裸报编号。
### 简洁细则
@@ -107,7 +106,7 @@
- 车道铁律(2026-10-31 用户裁恢复)——主工作区只作合并落点,一切开发/设计改动走独立 worktree:`git worktree add ../g-plan2-<任务名> -b <任务分支>`(建区即跑 `tools/setup_worktree_userdir.sh`),收口=冻结→件核→并 master→重塑→主区 `git merge --no-ff`→删区(合回即删,2026-09-19 原裁),CI 红不回滚合并——新开修复 worktree(`fix-<任务名>` 分支,免重塑免数卡)修复合回追绿;会话单区(2026-11-04 用户裁)——同一时刻每会话至多一个 worktree 在役,同会话追加事项并入在役区、只做一次合入(一次 CI),合回后用户再发令=新回合另开区,迭代实现批独占回合(细则见 process-conventions §1 第 5 条);主区唯一合法写动作=合并,agent 侧由 worktree-guard 拦主区 Edit/Write 与非 merge 提交;豁免面仅 `.zcode/`、`.qoder/`(不入 git,主区直改);commit 点零钩子检查(pre-commit 直改闸已退役),全量验证归合回后远端 CI(post-merge 钩子推送触发,见「运行与验证」首条);`--no-verify` 仅属用户,agent 禁用(细则见 process-conventions §1)
- **仓库根禁止设会话状态文件**(`active.md` 已废除)——现状看 roadmap「当前位」,待办看待办清单,过程看 git 提交信息,产出物看 `reports/`;任何文档禁止另设「当前状态快照」节,两处记同一件事必然漂移(细则见 doc-conventions 第 10 节)
- 测试执行两入口铁规——只有迭代 loop 开发与用户点名两个触发入口;其余会话零测试,需测试支撑先请示并注明未跑测
- 合回后验证 = 远端 CI 闸全绿(唯一强制验证点;唯一入口、唯一闸清单、唯一域划分与选闸算法仍只在 `tools/run_all_gates.sh`,禁在文档另列;触发与追绿语义见「运行与验证」首条与 process-conventions §1)。开发期手动跑批按需选档(`--code` 关联档选测——单元与集成测试跑「卡片申报 ∪ diff 推导」并集套件、三件保险恒跑、横切面回落全量——算法只在闸脚本内定义),手动跑不构成验收;全量回归由用户自发发起。远端按改动域分流:文档面批走文档域秒级闸,代码面批走两域全量(分流算法单点=`tools/ci/domain_route.sh`)
- 合回后验证 = 远端 CI 闸全绿(唯一强制验证点;唯一入口、唯一闸清单、唯一域划分与选闸算法仍只在 `tools/run_all_gates.sh`,禁在文档另列;触发与追绿语义见「运行与验证」首条与 process-conventions §1)。开发期手动跑批按需选档(`--code` 关联档选测——02 跑「卡片申报 ∪ diff 推导」并集套件、三件保险恒跑、横切面回落全量——算法只在闸脚本内定义),手动跑不构成验收;全量回归由用户自发发起。远端按改动域分流:文档面批走文档域秒级闸,代码面批走两域全量(分流算法单点=`tools/ci/domain_route.sh`)
- **改动四类**——**需求改动**(游戏代码,含随逻辑写/改的测试代码):唯一走「写需求卡片+编入迭代批次」通道;迭代实现批禁写 `design/` 整个目录、两份根流程清单(`待办清单.md`/`体验验证清单.md`)与 `game/tests/README.md`(能力清单,归写卡段维护)(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;两份流程清单的条目状态只由流水线各段推进,实现批禁止顺手改;产出物只准写 `reports/`;合回后由远端 CI 验证(红项修复合回追绿,process-conventions §1)。**工程改动**(tools/、闸件、模拟测量工具、纯测试维护——不改变游戏行为的配套代码)、**文档设计改动**、**美术素材改动**:不写需求卡片、不编迭代批次,由用户发令走独立 worktree 直改+合回(车道铁律,commit 点零钩子检查);大体量改动(含触及代码面的工程改动如纯测试维护)同车道无特殊通道;文档设计类写入走 doc-design 技能(登记条目格式由流水线闸强制)
- 五段流水线铁律——**需求改动**(游戏代码)按「登记 → 文档设计/美术设计 → 写需求卡片 → 迭代编排 → 实现」五段推进,段间不自动流转、每段转进由用户发令;编入迭代的卡片必须设计闭合+零待裁点——需求卡片任何工作量皆可写、皆可编入迭代,工作量限制只属迭代层(迭代估时总量 <5 个工作日=「冻结待凑批」、不可开工);流水线闸 `tools/pipeline_gate.py` 强制(已注册进 run_all_gates.sh 文档域);实现段零裁断——卡片没写清=上游缺陷,停该卡退回上游,禁内联求裁(process-conventions §2,格式与状态机=doc-conventions §12)。工程/文档设计/美术素材三类改动不走写卡与编排段:(如需设计讨论)→ 用户发令 → 独立 worktree 直改 → 合回 CI(车道细则=process-conventions §1)
- MC/模拟:无数值改动零跑;档位硬闸见 `sim-data/README.md`「MC 采集档位表」,一档只能由用户发起
@@ -305,11 +305,11 @@ S11/S12 扫描面覆盖全域,故 sim/probe/tests 的术语与旧件面**在
| 层 | 判什么 | 承载 | 入闸 |
|---|---|---|---|
| 冒烟测试 | 这版活着吗(零逻辑判定) | `game/tests/smoke_boot.tscn/.gd` | `run_smoke.py`(冒烟测试闸) |
| 单元测试 | 领域类的原子行为对不对(从对外方法进、钉对外可观测面——写法判据归 test-conventions §8.2) | `game/tests/unit/` | `run_all.gd`(单元与集成测试闸) |
| 冒烟测试 | 这版活着吗(零逻辑判定) | `game/tests/smoke_boot.tscn/.gd` | `run_smoke.py`(闸 00) |
| 单元测试 | 领域类的原子行为对不对(从对外方法进、钉对外可观测面——写法判据归 test-conventions §8.2) | `game/tests/unit/` | `run_all.gd`(闸 02) |
| 集成测试 | 跨系统闭环对不对 | `game/tests/integration/` | 同上(同一入口同一协议) |
| 系统测试 | 真实玩法路径断言面 | `game/tests/system_test.gd` | `run_system_test.py`(系统测试闸) |
| UI 探针+渲染 golden | 场景切换与观感 | `game/probe/*.tscn` 探针 | `probe_gate.sh`(场景探针闸)、`render_gate.sh`(渲染闸) |
| 系统测试 | 真实玩法路径断言面 | `game/tests/system_test.gd` | `run_system_test.py`(闸 01) |
| UI 探针+渲染 golden | 场景切换与观感 | `game/probe/*.tscn` 探针 | `probe_gate.sh`(闸 03/04)、`render_gate.sh`(闸 05) |
**判定原则**:验收不能只是「跑一遍没崩」。三硬闸(退出码/脚本错误扫描/断言数下限)是判定的底线,
缺一即判定有洞;「日志里写着 PASS」不作判据。另有两类静默失效必须机制性防范——规则扫描面为空
+1 -1
View File
@@ -78,7 +78,7 @@
10. **`# rng-seed-exempt` 标记豁免通道**——S1/S2 随机函数扫描对该行豁免;限定用途=非玩法 RNG 消费面的引擎熵(现行例:组合根种子编辑框空态取 `randi()`——建种子不进玩法封闭流);非产品码三目录自建随机源的定种要求见第 13 条。
11. **`--script` 模式禁裸 autoload 标识符**——`ConfigDB`/`RngService` 等 autoload 名在 `--headless --script` 模式编译器不解析(parse error);产品码与测试夹具一律经节点路径取用(`get_node("/root/…")`/`AUTOLOAD_PATH_*` 常量);静态扫描域 S5 硬失败承载在位。
12. **Variant 参数化现行口径**——账本类字典/数组标注 `Dictionary[Variant, Variant]`/`Array[Variant]` 即合格(满足 R1);元素类型精化列后续事项、不入硬纪律,口径变更须用户裁决后续批次另行写入。
13. **静态扫描适用域按来源分档(2026-11-03 架构审查 P1 余项裁)**——S 系静态扫描(S1–S13)的扫描面一律止于产品码三目录,`game/tests`/`game/probe`/`game/sim` 不进任何 S 系扫描面;这三目录的可靠性由运行期通道承接,各通道是:`game/tests`=冒烟·单元与集成·系统测试三闸、`game/probe`=场景探针与渲染 golden 两闸(同目录内 `type_glow_shot_probe` 是人工取证件、`probe_ui_util` 是探针共用面,两者不入闸)、`game/sim`=不入 CI,属用户手动发起的调参与数值测量批量。三目录**允许**自建 `RandomNumberGenerator`,条件=**必须显式定种**(改前改后读数要用同种子对拍,这是全项目测量口径);三目录真正禁止的形态是不定种随机——`randomize()` 与全局 `randi()`/`randf()`/`randf_range()`,一旦命中那次运行结果不可复现,读数与取证一并失效。这条对三目录的部分目前**无机器闸**(S1/S2 扫描面不含它们):扩面需给每处合法定种挂 `rng-seed-exempt` 注记、只拦得住新增,2026-11-03 用户裁不扩,改由本条文字+批末独立验收审计的人工评审承载。术语口径:`tools/run_all_gates.sh` 里的「测量面」指 `game/tests`+`game/sim`(影响面选闸用语,`game/probe` 不在其中——改探针必须跑场景探针与渲染闸),与本条分档无关;本文不再用「测量面」统称这三个目录。
13. **静态扫描适用域按来源分档(2026-11-03 架构审查 P1 余项裁)**——S 系静态扫描(S1–S13)的扫描面一律止于产品码三目录,`game/tests`/`game/probe`/`game/sim` 不进任何 S 系扫描面;这三目录的可靠性由运行期通道承接,各通道是:`game/tests`=闸 00/01/02(冒烟·单元与集成·系统测试)、`game/probe`=闸 03/04/05(场景探针与渲染 golden;同目录内 `type_glow_shot_probe` 是人工取证件、`probe_ui_util` 是探针共用面,两者不入闸)、`game/sim`=不入 CI,属用户手动发起的调参与数值测量批量。三目录**允许**自建 `RandomNumberGenerator`,条件=**必须显式定种**(改前改后读数要用同种子对拍,这是全项目测量口径);三目录真正禁止的形态是不定种随机——`randomize()` 与全局 `randi()`/`randf()`/`randf_range()`,一旦命中那次运行结果不可复现,读数与取证一并失效。这条对三目录的部分目前**无机器闸**(S1/S2 扫描面不含它们):扩面需给每处合法定种挂 `rng-seed-exempt` 注记、只拦得住新增,2026-11-03 用户裁不扩,改由本条文字+批末独立验收审计的人工评审承载。术语口径:`tools/run_all_gates.sh` 里的「测量面」指 `game/tests`+`game/sim`(影响面选闸用语,`game/probe` 不在其中——改探针必须跑 03/05),与本条分档无关;本文不再用「测量面」统称这三个目录。
---
+1 -1
View File
@@ -49,7 +49,7 @@
7. **验收改动先行声明**——验收变化(基线换代/探针增改/旧断言改判)预先写进需求卡片,禁事后补
**完成**
8. **合回后 CI 验证(唯一强制验证点)**——主工作区 `git merge --no-ff <任务分支>` 落地后 post-merge 钩子自动推送 origin master 触发远端 CI;远端按改动域分流(§1 第 3 条),游戏码批跑全量域闸(不带参数档,单元与集成测试全套件),判定与红项用 `tools/ci_check.sh <合并提交>` 按需现查(读 run 终局态,只呈报不落盘)。全量是游戏码批的强制档:跨卡触面、探针与未申报套件的消费漏网只有全量闸能抓(迭代 44 实证),关联档选测的盲区由它兜底。合回前独立验收审计通过(iter 技能第 5 步),未过不合回。`tools/run_all_gates.sh` 是全部验证闸的**唯一入口、唯一清单与唯一域划分(含选闸算法与套件族映射表)**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。开发期手动跑批按需选档(按文件性质判域:代码面→`--code`,文档件→`--docs`,两类都有→全量;`--code` 关联档选测——单元与集成测试只跑「需求卡片申报 ∪ diff 推导」并集套件、冒烟测试/系统测试/静态扫描三件保险恒跑、横切面回落全量——算法见闸脚本),手动跑不构成验收。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占 CI 全量——其质量义务=落批时人工确认闸可跑通且判据口径未变;闸真崩溃时远端 CI 判红在案(job 失败=verdict 红,fail-closed 语义保留)
8. **合回后 CI 验证(唯一强制验证点)**——主工作区 `git merge --no-ff <任务分支>` 落地后 post-merge 钩子自动推送 origin master 触发远端 CI;远端按改动域分流(§1 第 3 条),游戏码批跑全量域闸(不带参数档,02 全套件),判定与红项用 `tools/ci_check.sh <合并提交>` 按需现查(读 run 终局态,只呈报不落盘)。全量是游戏码批的强制档:跨卡触面、探针与未申报套件的消费漏网只有全量闸能抓(迭代 44 实证),关联档选测的盲区由它兜底。合回前独立验收审计通过(iter 技能第 5 步),未过不合回。`tools/run_all_gates.sh` 是全部验证闸的**唯一入口、唯一清单与唯一域划分(含选闸算法与套件族映射表)**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。开发期手动跑批按需选档(按文件性质判域:代码面→`--code`,文档件→`--docs`,两类都有→全量;`--code` 关联档选测——02 只跑「需求卡片申报 ∪ diff 推导」并集套件、00 冒烟/01 系统测试/09 静态扫描三件保险恒跑、横切面回落全量——算法见闸脚本),手动跑不构成验收。闸件(`tools/` 下机检脚本与闸壳)自身的改动不占 CI 全量——其质量义务=落批时人工确认闸可跑通且判据口径未变;闸真崩溃时远端 CI 判红在案(job 失败=verdict 红,fail-closed 语义保留)
9. **重跑语义(2026-10-30 用户裁)**——同一份字节禁止第二次人手跑;允许的重跑只剩两类:① CI 红后的修复合回(post-merge 钩子再推再验,每轮修复至多触发一轮);② 用户自发发起的全量回归。「跑都跑了再跑一遍保险」「顺手改点再补一跑」都不构成重跑理由。导入缓存刷新由远端工作流「导入缓存刷新」步接管,合并后无手工动作
10. **批次完成后的登记义务**——合回 master 后按 doc-conventions §12.1 推进待办清单条目状态(做完整条删除),涉及迭代编排的在 roadmap 迭代总表与顶部「当前位」节换代——执行入口=项目技能 batch-closure(批后收尾与中间文档退役),跨会话教训写记忆库。批次过程与结论由 git 提交信息承载,禁止再写会话状态文件(见 doc-conventions 第 10 节)。`design/` 与根规则文件(`AGENTS.md`/`roadmap.md`/`待办清单.md`/`体验验证清单.md`)实现批禁写,见 §12
11. **文档同步闸(实现批禁写文档)**——实现批**禁写 `design/` 整个目录与根规则文件**,**另禁写 `game/tests/README.md`(能力清单,归写卡段维护,test-conventions §8.9)**,要往外交代的东西一律落 `reports/`:实现报告 `reports/iter<N>-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务+实现决定记录——超白名单/超卡片明列范围事项逐条记「决了什么/为什么/代价/撤销方式」,全部按卡执行时只写一行「零拍板」)、GDD 反哺点清单 `reports/iter<N>-反哺点-<日期>.md`(每条写清哪个文件哪一节、改成什么、依据是什么)。设计权威文档(GDD/art-bible)**任何批都不直改**:用户在会话中逐点裁决后,由工程/文档设计改动批(直改类)写入 `design/`(执行入口=doc-design 技能),并把反哺条目从 `reports/` 清掉(吸收即死;退役执行=batch-closure 技能)。机械面文档(roadmap 行/术语表/README 工具注)同样归工程/文档设计改动批在合并前更新。反哺点未落地期间相关 GDD 节视为待反哺,读前先查 `reports/` 与待办清单;需求卡片必列「关联文档同步面」按两层标注
+1 -8
View File
@@ -27,7 +27,7 @@
## 4. 验证时机(合回后远端 CI 制,2026-11-03 换代)
开发期窄测义务不变:每批开发中,本批新增/改动的验收测试自己跑绿即收口(卡内只测自己改的东西,process-conventions §6)。全量验证归合回后的远端 CI:git merge 合回 master 落地后 post-merge 钩子推送 origin master,远端按改动域分流跑 `tools/run_all_gates.sh`(游戏码批=不带参数档两域全量、单元与集成测试全套件——跨卡触面与关联档选测的盲区由全量兜底;文档批=文档域秒级闸;分流算法单点=`tools/ci/domain_route.sh`),远端结果用 `tools/ci_check.sh <合并提交>` 按需现查(读 run 自身 `status`/`conclusion`,只呈报、不落盘——合回不等待、不唤醒,process-conventions §1 第 3 条);CI 红态下修复合回落地即再推再验追绿(修复走新开 `fix-<任务名>` 修复 worktree,process-conventions §1)。同一份字节禁止第二次人手跑。该脚本是全部验证闸的唯一入口与唯一清单,域划分与选闸算法只写在脚本里,本文禁另列闸件清单。
开发期窄测义务不变:每批开发中,本批新增/改动的验收测试自己跑绿即收口(卡内只测自己改的东西,process-conventions §6)。全量验证归合回后的远端 CI:git merge 合回 master 落地后 post-merge 钩子推送 origin master,远端按改动域分流跑 `tools/run_all_gates.sh`(游戏码批=不带参数档两域全量、02 全套件——跨卡触面与关联档选测的盲区由全量兜底;文档批=文档域秒级闸;分流算法单点=`tools/ci/domain_route.sh`),远端结果用 `tools/ci_check.sh <合并提交>` 按需现查(读 run 自身 `status`/`conclusion`,只呈报、不落盘——合回不等待、不唤醒,process-conventions §1 第 3 条);CI 红态下修复合回落地即再推再验追绿(修复走新开 `fix-<任务名>` 修复 worktree,process-conventions §1)。同一份字节禁止第二次人手跑。该脚本是全部验证闸的唯一入口与唯一清单,域划分与选闸算法只写在脚本里,本文禁另列闸件清单。
行为探针使用随机化玩家行为(封印/停手/刷新/无效点击)——自动玩家不做的动作就是测试盲区。
## 5. 测试函数怎么写——验收清单条目的落码规范
@@ -97,18 +97,12 @@
- **反向**:diff 里每个行为改动至少一行;没有对应行的改动,必须写明「非行为改动」或「由第 X 行覆盖」。
- **测试侧**:新增或改写的每条断言至少挂一行;挂不上的孤儿断言删掉或补挂到需求点。
**判据出处行锚(2026-11-05 立,自 2026-11-05 起对文件名日期不早于该日的需求卡片生效;旧卡不回头改,被编入迭代开工前由写卡段按本粒度补挂)**:对照表「需求点」列每行必须带**判据出处行锚**——该行验收判据所依据的现行文档句子,格式「`<文档相对路径>:<行号>`」。现行文档=`design/` 全域现行文档(含 `ux/` 交互规格与 art-bible 的判据行);已退役的需求卡片不是现行文档,不得充当判据出处。作用=断言预期值从规格句抄出,不从实现反推:实现行为与规格句冲突时断言红;规格缺席的行写不出锚,即停卡退回写卡段(那是需求澄清缺口,不是测试缺口)。机检=`tools/pipeline_gate.py` 判据 C5(锚格式与指向存在性,按卡文件名日期分流)+`tools/capability_audit.py` 链式锚检查(断言→套件→能力行→系统文档,见 §8.9)。
**验收档申报(2026-11-04 立,自 2026-11-05 起对文件名日期不早于该日的需求卡片生效;旧卡不回头改)**:每张需求卡片在头部信息区(与设计事实源声明同区)申报一个验收档,三档:高/中/低。判据=失败代价单维——这条改动做错了,后果随存档或结算传播的(存档写读与迁移、RNG 流、战斗结算公式与乘区、经济入账、数值表结构校验)=高;后果玩家可见但不损持久状态的(常规玩法逻辑、门控、交互逻辑、呈现数据面)=中;后果即看即知且无状态残留的(文案、纯呈现样式、布局微调)=低。一张卡触多档按最高档申报;申报由写卡段落笔,用户批卡(U0)时确认或改判。档位只调节 §8.7 审计深度——闸机与 CI 的域划分、选闸算法、全量强制档一概不因档位变化,真实入口、判据绑定面、禁削弱等验收闭合判据全部照旧。未申报档位的卡一律按高档全量审计。机检=`tools/pipeline_gate.py` 判据 C4:卡内须含「验收档:高/中/低」申报行。
**能力清单维护锚(2026-10-29 用户裁改版定稿)**:`game/tests/README.md`「能力清单」是**实现指导+阅读导航+义务声明**——只宣布「要验证什么」,不记「是否已验证/是否跑过」;落地账归现行验收闭合链(改动—验收对照表逐条挂断言+合回前独立验收审计),本清单不承载验证状态。表为四列:能力|系统|真实入口|代表用例(`文件:函数`)。**维护归写卡段,当场入表**:写需求卡片时,本批新增能力当场写入能力行——真实入口取自设计文档已定的入口,代表用例=卡里定死的计划测试(`文件:函数`);受影响存量行(入口/用例名变更)同批更新;能力退役删行。**实现照单落名**:测试函数名以清单为唯一依据,名实不符=卡片没写清=停卡退回。**收尾核对**:批末收尾只核对表上名字与实际落地一致(改名则回表),不刷任何批次字段;实现批禁写该文件的现行红线不变(process-conventions §3 第 11 条),核对义务归收尾提交。三层模型见 §8.9。改动—验收对照表纪律不变:新增或改写的每条断言仍须挂一行(上文「测试侧」);断言改判仍受 §8.4 真实入口口径约束。
**「真实入口」判定口径(2026-11-05 收窄)**:真实入口只认经引擎输入管线(合成 InputEvent 走 `push_input`)或真实渲染路径(截图/golden)的承载;信号直发(`pressed.emit()` 等合成信号)承载一律标「接线验证」,不得充当能力行的真实入口——接线验证保「信号已接线」,保不了「玩家点击可达」(hitbox/命中面在信号上游,emit 全部绕过)。既有能力行若以 emit 承载充当真实入口,随批触碰时订正;暂未触碰的行,缺口显式登记(残险模式),禁静默豁免。
**新增 core 件的覆盖登记义务(2026-11-03 架构审查 P1 余项裁)**:拆件或重构产出的新 core 文件不触发 process-conventions §3 第 6 条「新功能必配新测试」(那条只管新增玩法/机制),因此单由本条承接——新 core 件要么被 `game/tests/` 某套件按文件名或 `class_name` 点名,要么在承接它的能力行「代表用例」里申报「经宿主链覆盖(宿主=<件>)」;两者都无=批末收尾核对时补登。本条**不是覆盖率要求**,与 §7「覆盖率不是目标」同轨:登记的是能力义务的归属,不做文件计数——「每个 core 件必须被点名一次」的机器闸已裁否(它逼出为点名而点名的空套件,且与 §7 正面冲突)。落笔面沿用现行分工:申报行由写卡段入能力清单(实现批禁写该文件),核对归批末收尾提交。
**退役对账义务(2026-11-05 立)**:需求卡片退役(吸收即死)时,其对照表每行的验收意图必须已被承接——承接形态=能力清单行(能力+真实入口+代表用例)或已落地的断言;退役后无承接=该能力的规格判据断链,收尾核对判红。机检=`tools/capability_audit.py`(套件→能力行反向覆盖+断言链核验)。先例教训:营地图批卡片退役后,大门判据(`design/gdd/ux/camp.md:16`)无任何活面承接,探针 emit 断言悬空假绿——规格在、链接断。
### 8.4 真实入口断言(验收闭合的主判据)
- **唯一口径(2026-10-24/2026-10-28 用户两裁)**:每个需求点与每个行为改动的验收=一条从真实入口进入的断言,可 grep 定位、在当轮真实跑批中执行并通过。**禁变异类测试**(把生产码或数据表改成错值看断言抓不抓得到);**禁撤销类取证**(回退本批改动或该 hunk 看断言红不红)——两者都是人为制造红,属伪造证据,一律禁止。
@@ -133,7 +127,6 @@
2. 抽查判据绑定面:断言从真实入口进入、钉在被测改动的特有可观测提交面上,且当轮真实跑批执行过;
3. 检查测试削弱:删除断言、放宽阈值、缩小扫描面、换下层直调,逐处要卡片申报或用户批准。
4. 核对内部字段直查的受控例外:直查行是否已在对照表注明理由(该行为无公开可观测面可钉)。
5. 核对判据出处:抽查断言的预期值能否从其判据出处行锚(§8.3)的规格句独立写出——预期值只在实现里有的(同义反复,oracle 镜像实现),判红。
**审计深度分级(2026-11-04 立)**:审计按卡内申报的验收档(§8.3)逐卡定深度;梯度只落在昂贵的判断件上,机械且堵造假向量的两件(削弱检查、例外核对)三档全保留不抽。中/低档省略的测试复跑以远端 CI 读数为执行证据——`tools/ci_check.sh <合并提交>` 的现查输出(commit+run 号+conclusion)是机器产物,run 号在远端永久可复核,审计报告照抄该行即可;照抄前先核对读数必须是「绿」——「未验」(零触发批跑闸步全跳过)与「不可判」不作证据,那种批的回补办法是等一次含代码面的合回或用户自发全量回归(2026-11-04 用户裁:回执等待件退役,证据由落盘文件改为现查读数;「不采信实现者自述已全绿」的口径不变):
+2 -2
View File
@@ -98,7 +98,7 @@ func revealed_cells() -> Array:
return _revealed.keys()
func safe_remaining() -> int:
func _safe_remaining() -> int:
var n := 0
for c in _board.safe_cells():
if not _revealed.has(c):
@@ -107,7 +107,7 @@ func safe_remaining() -> int:
func clear_map_cleared() -> bool:
return safe_remaining() == 0
return _safe_remaining() == 0
# ---- 中立移除(P1 甲:邻接数字=原始雷数冻结零重算;P2 甲:移除格呈现退色降档;
+1 -25
View File
@@ -118,29 +118,6 @@ static func isolate_save(main, tag: String, mark_first_done: bool = false, with_
main.settings_store = SettingsStoreScript.new("user://%s_settings.cfg" % tag)
return iso
# ---- 真输入点击承载(test-conventions §8.3「真实入口」判定口径)----
# 合成鼠标事件经 Viewport.push_input 进引擎输入管线(hitbox/命中测试参与),press+release 成对
#(BaseButton pressed 信号走 release 沿——ui_input_probe :717-733 / screen_flow_probe :484-499 同式)。
# 信号直发(pressed.emit())只许标「接线验证」,不得充当真实入口断言的驱动。
static func push_click(main, ctrl: Control) -> void:
push_click_at(main, ctrl.get_global_rect().get_center())
static func push_click_at(main, pos: Vector2) -> void:
var down := InputEventMouseButton.new()
down.position = pos
down.global_position = pos
down.button_index = MOUSE_BUTTON_LEFT
down.pressed = true
main.get_viewport().push_input(down, true)
var up := InputEventMouseButton.new()
up.position = pos
up.global_position = pos
up.button_index = MOUSE_BUTTON_LEFT
up.pressed = false
main.get_viewport().push_input(up, true)
# 营地经进档开层(卡② mn9 新路径——campbtn 已撤;槽模板注入隔离命名空间,防碰真实槽
# user://save_slot_N.json——真实档红线)。创建=激活=暂行开营地(卡③ Q3 乙落点);
# 驱动后写盘目标=隔离槽文件(持久化断言读 user://<tag>_slot_1.json)。返回 false=新档钮缺失(驱动中断)。
@@ -153,9 +130,8 @@ static func open_camp_via_savesel(main, tag: String) -> bool:
if FileAccess.file_exists(sp):
DirAccess.remove_absolute(sp)
main._open_save_select()
await main.get_tree().process_frame # 排版帧——真点击命中的是控件 rect,开层同帧容器未排(emit 无此依赖)
var new_btn: Button = main.ui.modals.menu.save_select_overlay._new_btns.get(1)
if new_btn == null:
return false
push_click(main, new_btn) # 真输入 → _on_save_select_new → create_slot+open_camp
new_btn.pressed.emit() # 真实按钮信号路径 → _on_save_select_new → create_slot+open_camp
return true
+4 -4
View File
@@ -110,7 +110,7 @@ func _step_boot_menu() -> void:
# ---- ② 菜单 → 营地(真实按钮信号路径)----
func _step_menu_to_camp() -> void:
# 营地经进档开层(卡② mn9 新路径——campbtn 已撤;槽模板注入隔离命名空间)
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_camp"):
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_camp"):
_problems.append("②菜单→营地:选档层新档钮缺失(新路径驱动中断)")
return
await _frames()
@@ -141,7 +141,7 @@ func _step_save_select_open_back() -> void:
if esb == null:
_problems.append("[savesel] mn3 进入存档钮缺失(卡② 交付物)")
return
ProbeUiUtil.push_click(main, esb) # 真输入(push_input 管线)→ _open_save_select(卡② mn3 点击链——真实入口承载)
esb.pressed.emit() # 真实按钮信号路径 → _open_save_select(卡② mn3 点击链——卡③ 直调中间态就此闭环)
await get_tree().process_frame
var seals := _screen_seals()
if not main.ui.modals.menu.save_select_overlay.visible or not seals_check_single(seals):
@@ -238,7 +238,7 @@ func _step_end_back_to_menu() -> void:
# ---- ③' 营地 Esc 浮窗+HUD(卡⑤——真实键盘合成路径 push_input;四态路由+三出口+语境分支+余额单口)----
func _step_camp_esc_and_hud() -> void:
ProbeUiUtil.isolate_save(main, "smoke_screen_flow_esc", false, false, true)
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_esc"):
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_esc"):
_problems.append("[esc] 营地入口缺失(选档层新档钮驱动中断)〔ESC批 V1〕")
return
await _frames()
@@ -301,7 +301,7 @@ func _step_camp_esc_and_hud() -> void:
# 互斥单开/ESC 优先序/窗内滚动/生涯同壳——行为面(开合路由)零改,只钉窗壳可观测提交面。
func _step_camp_window_shell() -> void:
ProbeUiUtil.isolate_save(main, "smoke_screen_flow_winshell", false, false, true)
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_winshell"):
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_winshell"):
_problems.append("[winshell] 营地入口缺失(选档层新档钮驱动中断)〔窗口化批 V1〕")
return
await _frames()
+1 -1
View File
@@ -120,7 +120,7 @@ func _ready() -> void:
if main.ui.modals.menu.career_overlay.visible or not main.ui.screens.start_overlay.visible:
_problems.append("生涯页关闭后页未隐藏或开始层未恢复")
# ② 营地场景图(进档开层新路径——卡② mn9;截图=可点击场景图形态——卡④ 契约 9 V1 验收载体)
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_shot_probe_camp"):
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_shot_probe_camp"):
_problems.append("营地入口缺失(选档层新档钮——新路径驱动中断)〔句柄子容器批 V1〕")
else:
await get_tree().process_frame
+7 -7
View File
@@ -61,7 +61,7 @@ func _ready() -> void:
# swap=true(整链换注——购买写盘经 autoload 落隔离槽文件,与下方持久化核验同源;
# 原 swap=false 形态的写盘走 iso 实例旧路径,与槽文件断言不同源——卡② mn9 换代)
var iso_c = ProbeUiUtil.isolate_save(main, "smoke_click_probe_camp", false, false, true)
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_click_probe_camp"):
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_click_probe_camp"):
_problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)〔句柄子容器批 V1〕")
else:
await get_tree().process_frame
@@ -302,7 +302,7 @@ func _ready() -> void:
if settings_btn == null:
_problems.append("设置入口按钮缺失(开始层 settingsbtn)")
else:
ProbeUiUtil.push_click(main, settings_btn) # 真输入(push_input 管线)→ _open_settings
settings_btn.pressed.emit() # 真实按钮信号路径 → _open_settings
await get_tree().process_frame
if not main.ui.modals.menu.settings_overlay.visible or main.ui.screens.start_overlay.visible:
_problems.append("设置面未打开(点击路径)")
@@ -685,7 +685,7 @@ func _ready() -> void:
main._camp_pres.open_camp()
await get_tree().process_frame
var bal_gate: int = main._camp_data()["balance"]
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"gate").get_center())
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
await get_tree().process_frame
if not main.ui.modals.menu.confirm_dialog.is_open():
_problems.append("大门无断点:确认弹窗未开(点大门应先出确认不直接进冒险)〔营地图批 V3〕")
@@ -697,9 +697,9 @@ func _ready() -> void:
await get_tree().process_frame
if not main.ui.modals.camp.scene.visible or main.run_scope.is_adventure or main._camp_data()["balance"] != bal_gate:
_problems.append("大门取消:未无变更退回营地(写口零调用/营地根 visible 失守)〔营地图批 V3〕")
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"gate").get_center())
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
await get_tree().process_frame
ProbeUiUtil.push_click(main, main.ui.modals.menu.confirm_dialog._confirm_btn) # 真点「确认」→开始新冒险
main.ui.modals.menu.confirm_dialog.confirmed.emit() # 「是」→开始新冒险
await get_tree().process_frame
if not main.run_scope.is_adventure or main.ui.modals.camp.scene.visible:
_problems.append("大门确认:冒险未发起或营地场景屏未隐藏〔营地图批 V3〕〔营地图批 B1〕")
@@ -712,11 +712,11 @@ func _ready() -> void:
})
main._camp_pres.open_camp()
await get_tree().process_frame
ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"gate").get_center())
main.ui.modals.camp.scene.object_pressed.emit(&"gate")
await get_tree().process_frame
if not main.ui.modals.menu.confirm_dialog.is_open() or not main.ui.modals.menu.confirm_dialog._secondary_btn.visible:
_problems.append("大门有断点:三出口形态失守(次钮应现形)〔营地图批 V3〕")
ProbeUiUtil.push_click(main, main.ui.modals.menu.confirm_dialog._secondary_btn) # 真点「开始新冒险」出口
main.ui.modals.menu.confirm_dialog.secondary_confirmed.emit() # 「开始新冒险」出口
await get_tree().process_frame
if not main.run_scope.is_adventure or main.ui.modals.camp.scene.visible:
_problems.append("大门有断点次确认:冒险未发起或营地未隐藏〔营地图批 V3〕")
+1 -1
View File
@@ -24,7 +24,7 @@ func _ready() -> void:
# 隔离存档换注 main.meta 保确定性(禁碰真实档 user://save.json——SaveManager 路径注入纪律;
# 营地先行段只换 meta 不换 save_manager——冒险段换注零档在前)----
ProbeUiUtil.isolate_save(main, "smoke_ui_probe_camp", false, false, false)
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_ui_probe_camp"):
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_ui_probe_camp"):
problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)〔句柄子容器批 V1〕")
else:
await get_tree().process_frame
+1 -1
View File
@@ -38,7 +38,7 @@ func _ready() -> void:
main._on_settings_closed()
_settle()
if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_ui_struct_probe_menu"):
if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_ui_struct_probe_menu"):
problems.append("[camp] 营地入口缺失(选档层新档钮——mn9 新路径驱动中断)〔句柄子容器批 V1〕")
else:
_settle()
+17 -17
View File
@@ -1,65 +1,65 @@
{
"note": "存量基线·闸名去编号批同步改键(原键带 00–10 编号前缀,与脚本注册名一并换代);闸况变化时本脚本会判红,需人工确认后重生成",
"note": "存量基线·首次自动生成;闸况变化时本脚本会判红,需人工确认后重生成",
"gates": {
"冒烟测试": {
"00 冒烟测试": {
"exit": "0",
"fp": "green"
},
"系统测试": {
"01 系统测试": {
"exit": "0",
"fp": "green"
},
"场景探针": {
"02 单元与集成测试": {
"exit": "0",
"fp": "green"
},
"渲染闸": {
"03 场景探针": {
"exit": "0",
"fp": "green"
},
"色板闸": {
"05 渲染闸": {
"exit": "0",
"fp": "green"
},
"gdd_lint": {
"05b 色板闸": {
"exit": "0",
"fp": "green"
},
"变更记录节": {
"06 gdd_lint": {
"exit": "0",
"fp": "green"
},
"doc_link": {
"06b 变更记录节": {
"exit": "0",
"fp": "green"
},
"doc_term": {
"07 doc_link": {
"exit": "0",
"fp": "green"
},
"流水线闸": {
"08 doc_term": {
"exit": "0",
"fp": "green"
},
"引用可核": {
"08c 流水线闸": {
"exit": "0",
"fp": "green"
},
"能力审计": {
"08d 引用可核": {
"exit": "0",
"fp": "green"
},
"静态扫描": {
"08e 能力审计": {
"exit": "0",
"fp": "green"
},
"熵增闸": {
"09 静态扫描": {
"exit": "0",
"fp": "green"
},
"单元与集成测试": {
"10 熵增闸": {
"exit": "0",
"fp": "green"
}
}
}
}
+22 -35
View File
@@ -17,8 +17,8 @@
tests/
unit/ # 单元测试(公式/状态机/纯逻辑,隔离断言)——extends SceneTree + quit(code)
integration/ # 集成测试(跨系统闭环——与 unit/ 同一入口同一协议,见下「测试层级」节)
smoke_boot.tscn/.gd # 冒烟测试——极小启动场景(真实开机路径存活检查)
system_test.gd # 系统测试宿主薄壳(SceneTree+构造注入;断言迁移批后承接 smoke_all.gd)
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)
@@ -29,11 +29,11 @@ tests/
| 层 | 判什么 | 承载 | 入闸方式 |
|---|---|---|---|
| **冒烟测试** | 这版活着吗——能否启动、能否加载表、能否进主屏。零逻辑判定 | `tests/smoke_boot.tscn/.gd`(以项目运行目标拉起=真实开机路径) | `run_smoke.py`(冒烟测试闸) |
| **单元测试** | 领域类的原子行为对不对(公式/状态机/纯逻辑)——从对外方法进、断言钉对外可观测面(写法判据见 test-conventions §8.2) | `tests/unit/` | `run_all.gd` manifest(单元与集成测试闸) |
| **冒烟测试** | 这版活着吗——能否启动、能否加载表、能否进主屏。零逻辑判定 | `tests/smoke_boot.tscn/.gd`(以项目运行目标拉起=真实开机路径) | `run_smoke.py`(闸 00) |
| **单元测试** | 领域类的原子行为对不对(公式/状态机/纯逻辑)——从对外方法进、断言钉对外可观测面(写法判据见 test-conventions §8.2) | `tests/unit/` | `run_all.gd` manifest(闸 02) |
| **集成测试** | 跨系统的闭环对不对 | `tests/integration/` | 同上(与单元测试同一入口、同一协议) |
| **系统测试** | 启动真实工程走真实玩法路径 | 过渡期 `game/probe/smoke_all.gd`(断言迁移批后=`tests/system_test.gd`) | `run_system_test.py`(系统测试闸) |
| **UI 探针+渲染 golden** | 跑起来之后 UI 与场景切换对不对;画面基线 | `game/probe/*.tscn` 探针 | `probe_gate.sh`(场景探针闸)、`render_gate.sh`(渲染闸) |
| **系统测试** | 启动真实工程走真实玩法路径 | 过渡期 `game/probe/smoke_all.gd`(断言迁移批后=`tests/system_test.gd`) | `run_system_test.py`(闸 01) |
| **UI 探针+渲染 golden** | 跑起来之后 UI 与场景切换对不对;画面基线 | `game/probe/*.tscn` 探针 | `probe_gate.sh`(闸 03)、`render_gate.sh`(闸 05) |
> **integration/ 已建承载**。
> 首个集成用例 = `save_rng_roundtrip_test.gd`(存档⇄RNG 快照往返)。章程登记的四类集成用例里,
@@ -70,13 +70,13 @@ tests/
| 能力 | 系统 | 真实入口 | 代表用例(文件:函数) |
|---|---|---|---|
| 冒险生命周期:开局受理/三关序列/败北线 | 冒险结构 | `AdventureRunner.start_adventure()`/`continue_to_next_level()` | `adventure_feats_test.gd:_smoke_adventure_api`;`adventure_feats_test.gd:_smoke_adventure`;`hero_ledger_test.gd:_init` |
| 冒险生命周期:开局受理/三关序列/败北线 | 冒险结构 | `AdventureRunner.start_adventure()`/`continue_to_next_level()` | `adventure_feats_test.gd:_smoke_adventure_api`;`adventure_feats_test.gd:_smoke_adventure` |
| 专长(奇物)授予与凭证保底 | 奇物 | `AdventureRunner.present_node_curio()`/`feats_held()` × 效果引擎兑付 | `adventure_feats_test.gd:_smoke_feats`;`feat_vouchers_test.gd:_init` |
| 节点图生成合法性与同种子复现 | 关卡节点图生成 | `NodeMapGenerator.generate()`/`validate_output()` | `node_map_generator_test.gd:_test_output_ok`;`node_map_generator_test.gd:_test_reproducible`;`node_map_generator_test.gd:_test_exit_structure` |
| 节点图生长递增与无解 fail-fast | 关卡节点图生成 | `NodeMapGenerator.generate()`/`is_fatal()` | `node_map_growth_test.gd:_test_growth`;`node_map_growth_test.gd:_test_fatal_subprocess` |
| 节点图参数读表与域单点(三参数驱动产物/生成器侧可行性保留) | 关卡节点图生成 | `NodeMapGenerator.generate()`/`LevelGenParams.validate()` | `node_map_generator_test.gd:_test_gen_params_table_driven`;`table_structure_guard_test.gd:_test_level_gen_new_fields`;`level_gen_params_test.gd:_init`;`encapsulation_p2_sweep_test.gd:_init` |
| 节点图参数读表与域单点(三参数驱动产物/生成器侧可行性保留) | 关卡节点图生成 | `NodeMapGenerator.generate()`/`LevelGenParams.validate()` | `node_map_generator_test.gd:_test_gen_params_table_driven`;`table_structure_guard_test.gd:_test_level_gen_new_fields` |
| 起关/选路/断点重生成同图(集成) | 关卡节点地图×关卡节点图生成×冒险结构 | `LevelRunner.start_level()`/`start_level_external()`/`pick_next_node()`;`AdventureRunner.resume_adventure()` | `node_map_determinism_test.gd:_smoke_generated_map_determinism` |
| 信息迷雾类型可见性 | 关卡节点地图 | `LevelRunner.type_visible_ids()` | `level_map_type_vis_test.gd:_test_layer_distance`;`level_map_type_vis_test.gd:_test_boss_exempt`;`level_map_type_vis_test.gd:_test_resume_rebuild`;`level_map_info_fog_test.gd:_initialize` |
| 信息迷雾类型可见性 | 关卡节点地图 | `LevelRunner.type_visible_ids()` | `level_map_type_vis_test.gd:_test_layer_distance`;`level_map_type_vis_test.gd:_test_boss_exempt`;`level_map_type_vis_test.gd:_test_resume_rebuild` |
| 关卡表消费侧校验与关卡推进 | 关卡节点地图×数据配置 | `LevelTable.validate_graph()`;`LevelRunner.start_level()` | `level_table_validate_test.gd:_test_positive`;`level_table_validate_test.gd:_test_negative`;`level_shop_economy_test.gd:_smoke_level` |
**旅程段三:战斗结算**
@@ -86,8 +86,8 @@ tests/
| 伤害公式与掷点乘区 | 战斗结算 | `DamageMath.pool_damage()`/`roll_discount_percent()`/`rolled_component()` | `damage_math_test.gd:_test_pool_damage`;`damage_math_test.gd:_test_roll_discount_percent`;`roll_multiplier_test.gd:_smoke_armor_roll` |
| 伤害单据编排与回放 | 战斗结算 | `DamageController.submit()`/`open_ticket()`/`flush()` | `damage_controller_test.gd:_run_recipe_sequence`;`damage_controller_test.gd:_test_five_recipes_and_replay` |
| 效果发射端口单点 | 战斗结算 | `BattleOrchestrator.begin_battle()`(真实装配链端口接线+合成行闭面守卫经 `DamageController.flush_submit()` 承伤路径) | `effect_port_test.gd:_smoke_effect_port_wiring`;`effect_port_test.gd:_test_business_classes_engine_free`;`effect_port_test.gd:_test_biz_fact_face_closed` |
| 轮内流与结算五段全序 | 战斗结算 | `BattleOrchestrator.begin_battle()` | `round_flow_skill_pool_test.gd:_smoke_full_battle`;`round_flow_skill_pool_test.gd:_smoke_settlement_segments`;`settle_segments_test.gd:_init`;`settlement_wiring_snapshot_test.gd:_init`;`encapsulation_sweep_test.gd:_init` |
| 技能池数据面与回放独立 | 敌人系统 | `BattleOrchestrator.begin_battle()`(技能池抽号/校验) | `round_flow_skill_pool_test.gd:_smoke_skill_pool_data`;`round_flow_skill_pool_test.gd:_smoke_skill_pool_replay_independence`;`trigger_cond_test.gd:_init`;`pool_case_d_test.gd:_init` |
| 轮内流与结算五段全序 | 战斗结算 | `BattleOrchestrator.begin_battle()` | `round_flow_skill_pool_test.gd:_smoke_full_battle`;`round_flow_skill_pool_test.gd:_smoke_settlement_segments` |
| 技能池数据面与回放独立 | 敌人系统 | `BattleOrchestrator.begin_battle()`(技能池抽号/校验) | `round_flow_skill_pool_test.gd:_smoke_skill_pool_data`;`round_flow_skill_pool_test.gd:_smoke_skill_pool_replay_independence` |
| Boss 半血解锁技门控 | 敌人系统 | `BattleOrchestrator.begin_battle()`(Boss 行) | `skill_unlock_test.gd:_test_gate_predicate`;`skill_unlock_test.gd:_test_latch_irreversible` |
| 封印与极性路由/不可逆 | 地图战斗资源×战斗结算 | 效果引擎 × `BattleOrchestrator.begin_battle()` | `seal_polarity_test.gd:_smoke_seal_ap_exhausted`;`seal_polarity_test.gd:_smoke_seal_irreversible_quadrants`;`seal_polarity_test.gd:_smoke_polarity_routing` |
| 变异布置门控 | 地形变异 | `BattleOrchestrator.begin_battle()`(变异布置段) | `variant_placement_test.gd:_smoke_variant_placement`;`variant_placement_test.gd:_smoke_global_variant`;`variant_placement_test.gd:_test_pack_min_from_table` |
@@ -103,7 +103,7 @@ tests/
| 元进度入账与锻造两轴 | 元进度×锻造工坊 | `MetaStateStore.apply_adventure_summary()`/`MetaStateStore.spend_keystone()`/`MetaStateStore.set_forge_route_level()` | `meta_progression_forge_test.gd:_meta_pricing_and_buy`;`meta_progression_forge_test.gd:_smoke_forge_axes` |
| 锻造定价与购买闭环(全路线价格梯/多级购买扣款/拒购零副作用) | 锻造工坊 | `ForgeTable.level_price()`/`MetaProgression.forge_buy_level()` | `forge_table_test.gd:_test_price_ladder`;`meta_progression_forge_test.gd:_meta_pricing_and_buy`;`meta_progression_forge_test.gd:_test_forge_buy_reject` |
| 锻造与相邻通道隔离(祝福件购买锻造零变化/默认轴与钥匙解耦/甲型切换双轴可感) | 锻造工坊×元进度 | `MetaProgression.forge_select()`/`hero_type_axes()`/商店购买口 | `meta_progression_forge_test.gd:_test_blessing_no_forge_leak`;`meta_progression_forge_test.gd:_test_default_axes_pre_key`;`meta_progression_forge_test.gd:_test_armor_switch_dual_axes` |
| 天赋树购买与六键合成 | 天赋树 | `MetaProgression.tree_buy()`/`MetaStateStore.tree_state()`/`MetaStateStore.set_tree_level()` | `meta_progression_forge_test.gd:_smoke_tree`;`meta_progression_forge_test.gd:_smoke_tree_rules`;`tree_rules_v11_test.gd:_init`;`tree_requires_level_test.gd:_init` |
| 天赋树购买与六键合成 | 天赋树 | `MetaProgression.tree_buy()`/`MetaStateStore.tree_state()`/`MetaStateStore.set_tree_level()` | `meta_progression_forge_test.gd:_smoke_tree`;`meta_progression_forge_test.gd:_smoke_tree_rules`;`tree_rules_v11_test.gd:_init` |
| 规则键参数读表(六键参数字典校验与激活侧透传) | 天赋树 | `MetaTreeTable.validate()`/`EffectEngine.rebuild()`/`MetaProgression.tree_rules()` | `tree_rules_v11_test.gd:_test_rule_key_params` |
| 秘宝结构冻结与 12 件效果 | 秘宝 | `TreasureItems.add_item()`/`query_inventory()`/`validate_use()` | `treasure_items_test.gd:_init`;`treasure_effects_test.gd:_init` |
@@ -124,7 +124,7 @@ tests/
| 停手转存储备与轮内刷新(上限/AP 不重置/刷新图零清图奖励) | 行动经济 | `BattleOrchestrator.player_request_refresh()`/`MinesweeperRules.settle_batch()` | `round_flow_skill_pool_test.gd:_smoke_active_refresh`;`seal_polarity_test.gd:_smoke_refresh_provenance_guard`;`refresh_feats_test.gd:test_no_feats_baseline` |
| 换图系刷新链路与效果词表接线 | 行动经济×战斗结算×奇物 | `MinesweeperRules.settle_batch()`/`BattleOrchestrator.player_request_refresh()` | `effect_words_t1_test.gd:_init`;`forge_table_test.gd:_test_effect_kinds_closed`;`refresh_feats_test.gd:test_board_refresh_fact_only_on_success` |
| 换图基座:挖格产 RAP 与刷新折扣 | 行动经济×奇物 | `MinesweeperRules.settle_batch()`/`BattleOrchestrator.player_request_refresh()` | `refresh_feats_test.gd:test_reserve_on_accepted_dig`;`refresh_feats_test.gd:test_refresh_discount_gate_and_charge`;`refresh_feats_test.gd:test_double_base_stack` |
| 换图输出:刷新群伤与蓄力攻击 | 战斗结算×奇物 | `BattleOrchestrator.player_request_refresh()`→轮末 `MinesweeperRules.settle_batch(stop)` | `refresh_feats_test.gd:test_blast_defers_to_combat_segment`;`refresh_feats_test.gd:test_charge_layers_consumed_same_round`;`run_time_fatal_test.gd:_init` |
| 换图输出:刷新群伤与蓄力攻击 | 战斗结算×奇物 | `BattleOrchestrator.player_request_refresh()`→轮末 `MinesweeperRules.settle_batch(stop)` | `refresh_feats_test.gd:test_blast_defers_to_combat_segment`;`refresh_feats_test.gd:test_charge_layers_consumed_same_round` |
| 换图生存:全局固定减伤与轮末恢复 | 战斗结算×奇物 | `BattleOrchestrator.player_request_refresh()`→敌方攻击/危险格/错误封印/地形真实受伤入口 | `refresh_feats_test.gd:test_all_hero_damage_paths_emit_once`;`refresh_feats_test.gd:test_stone_mitigation_all_damage`;`refresh_feats_test.gd:test_rest_rotation_cap_and_clear` |
| 换图资源:刷新固定 GP 收益 | 战斗结算×奇物 | `BattleOrchestrator.player_request_refresh()` | `refresh_feats_test.gd:test_mine_profit_gp` |
| 下轮行动点修正排队与兑现 | 行动经济 | `ActionEconomy.pending_ap_delta()`(排队经 `ActionEconomy.queue_ap_delta()`,写入方为效果引擎) | `seal_polarity_test.gd:_smoke_negative_dictionary`;`feat_vouchers_test.gd:_init` |
@@ -155,8 +155,8 @@ tests/
| 战斗叙述与终局文案(数值带入/胜负互异) | HUD框架 | `BattleNarrator.battle_end_text()`/`BattleNarrator.boss_name()` | `battle_narrator_test.gd:_test_numeric_lines_carry_values`;`battle_narrator_test.gd:_test_end_text_differs_by_result`;`battle_narrator_test.gd:_test_boss_name_uses_boss_rowset` |
| 玩家面文案与危险格格面字形 | HUD框架 | `BoardView.rebuild()`/`StatusSlotRow.set_effects()` | `player_copy_sweep_test.gd:_sweep_board_glyph`;`player_copy_sweep_test.gd:_sweep_legend`;`player_copy_sweep_test.gd:_sweep_hero_panel` |
| 营地场景图十层铺底与门洞留空呈现 | 营地场景视图 | 进入存档→选档→进档→营地场景屏(`CampSceneView.setup_layers()` 驱动);单元测试载入 `camp_scene_layout.tres` | `shot_probe.gd:_ready`(camp 截图段);`camp_scene_layout_test.gd:_test_layout_hotspot_geometry` |
| 营地七物件命中区点选与分界面可达 | 营地场景视图 | 七物件命中区 → `CampSceneView._gui_input()`(命中发 `object_pressed` 信号) → 组合根开界面;**现役承载=emit 信号直发,标「接线验证」——真输入承载待建(残险登记,2026-11-05 真实入口口径收窄)** | `ui_click_probe.gd:_ready`(七物件合成点击段) |
| 营地大门两分支确认与断点续玩接线 | 营地交互流 | 大门命中区 → 合成鼠标事件经 `push_input` 真点(`ProbeUiUtil.push_click_at`)→ ConfirmDialog 真钮(三出口)→ `main._on_confirm_accepted()`(新冒险确认)/`main._resume_from_menu()`(断点续玩) | `ui_click_probe.gd:_ready`(大门两分支段/断点确认段——真输入 push_click 驱动,2026-11-05 真输入化) |
| 营地七物件命中区点选与分界面可达 | 营地场景视图 | 七物件命中区 → `CampSceneView._gui_input()`(命中发 `object_pressed` 信号) → 组合根开界面 | `ui_click_probe.gd:_ready`(七物件合成点击段) |
| 营地大门两分支确认与断点续玩接线 | 营地交互流 | 大门命中区 → ConfirmDialog(三出口)→ `main._on_confirm_accepted()`(新冒险确认)/`main._resume_from_menu()`(断点续玩) | `ui_click_probe.gd:_ready`(大门两分支段/断点确认段 :500-528) |
| 四柜台拆解后五界面入口改挂与写口零回归 | 营地五界面 | 五界面意图信号 → `CampShopPresenter` → `MetaProgression.tree_buy()` 写口 | `forge_workbench_test.gd:_run`(承载类同迁断言十二项);`ui_click_probe.gd:_ready`(写口链段) |
| 营地篝火回主菜单与笔记本改挂动线 | 营地交互流 | 笔记本命中区 → `CareerOverlay.open()`;篝火命中区 → ConfirmDialog 二次确认 → 主菜单 | `ui_struct_probe.gd:_ready`(career 段);`screen_flow_probe.gd:_ready`(篝火/开合段) |
| 营地图鉴占位挂起态与反参照结构纪律 | 营地场景视图 | 布告板命中区 → `CampSceneView._gui_input()`(object_pressed 信号) → 图鉴占位视图;营地根态场景树审计 | `ui_struct_probe.gd:_ready`(占位禁词/禁行段);`ui_struct_probe.gd:_ready`(结构禁词段) |
@@ -174,13 +174,13 @@ tests/
| 进度坐标查行:精确命中/向上取整/封顶/警告去重 | 数据配置 | `BoardGenTable.resolve_row()`/`ConfigDbResolver.resolve_enemy_rowset()` | `config_db_test.gd:_test_progress_resolution`;`config_db_resolver_test.gd:_test_band_hit`;`config_db_resolver_test.gd:_test_top_band_clamp` |
| 行集寻址、行序保序与难度乘数 clamp | 数据配置×难度曲线与Boss | `ConfigDbResolver.resolve_enemy_rowset()`/`ConfigDbResolver.resolve_difficulty_modifiers()` | `config_db_resolver_test.gd:_test_rowset_order_preserved`;`config_db_resolver_test.gd:_test_row_kind_routing`;`config_db_resolver_test.gd:_test_difficulty_clamp` |
| 运行期越域致命与进程非 0 退出 | 数据配置 | `BoardGenTable.resolve_row()`/`ConfigDbResolver.resolve_enemy_rowset()` | `config_db_fatal_oob_test.gd:_trigger_oob`;`config_db_resolver_test.gd:_test_request_oob` |
| 加载期违规表拒绝:跨表悬空/计数器前缀/真视窄域/效果类型出域 | 数据配置×秘宝×奇物 | `ConfigDB.load_tables()` | `reward_dangling_treasure_test.gd:_init`;`counter_prefix_fatal_test.gd:_assert_rejected`;`treasure_target_violation_test.gd:_init`;`effect_kind_retired_test.gd:_assert_rejected`;`extended_row_violation_test.gd:_init` |
| 加载期违规表拒绝:跨表悬空/计数器前缀/真视窄域/效果类型出域 | 数据配置×秘宝×奇物 | `ConfigDB.load_tables()` | `reward_dangling_treasure_test.gd:_init`;`counter_prefix_fatal_test.gd:_assert_rejected`;`treasure_target_violation_test.gd:_init`;`effect_kind_retired_test.gd:_assert_rejected` |
| 表结构守卫:域校验/行序键/跨字段互锁/配额锚 | 数据配置×敌人系统×地形变异×冒险结构 | `EnemyTable.validate()`/`VariationTable.variant_pool()`/`AdventureTable.validate()` | `table_structure_guard_test.gd:_smoke_enemy_bands`;`table_structure_guard_test.gd:_smoke_variation_framework`;`table_structure_guard_test.gd:_smoke_adventure_table`;`table_structure_guard_test.gd:_test_feat_cap_required` |
| 变异表头域校验(地形变异) | 地形变异 | `VariationTable.validate()` | `table_structure_guard_test.gd:_test_variation_header_domains` |
| 难度修饰套乘:空表零行为/套乘生效/越域 clamp | 难度曲线与Boss | `UnitContainer.spawn()`/`ConfigDbResolver.resolve_difficulty_modifiers()` | `table_structure_guard_test.gd:_smoke_difficulty_table`;`config_db_resolver_test.gd:_test_difficulty_clamp` |
| Boss 出场门控(每区域恰一 Boss 终点/Boss 节点开战必选 Boss 行集) | 难度曲线与Boss | `NodeMapGenerator.generate()`/`BattleOrchestrator.begin_battle()` | `node_map_generator_test.gd:_test_boss_exit_per_region`;`boss_gate_test.gd:_test_boss_node_spawns_boss_rowset` |
| 锻造表 schema 正反与几何价格梯 | 锻造工坊 | `ForgeTable.level_price()`/`ForgeTable.route_rows()` | `forge_table_test.gd:_test_production_rows`;`forge_table_test.gd:_test_price_ladder`;`forge_table_test.gd:_test_negative_cases` |
| 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理 | 战斗结算×数据配置×增益状态 | `DamageController.open_ticket()`/`StatusContainer.apply_effect()` | `system_contracts_test.gd:_test_damage_controller_vocabularies`;`system_contracts_test.gd:_test_rng_tag_closure`;`system_contracts_test.gd:_test_status_boolean_acceptance`;`source_guard_test.gd:_init` |
| 系统间契约词表与受理口径:五配方互异/掷点标签成员性/增益布尔受理 | 战斗结算×数据配置×增益状态 | `DamageController.open_ticket()`/`StatusContainer.apply_effect()` | `system_contracts_test.gd:_test_damage_controller_vocabularies`;`system_contracts_test.gd:_test_rng_tag_closure`;`system_contracts_test.gd:_test_status_boolean_acceptance` |
| 同名状态合并策略(叠加/累加/并存)与剩余次数语义 | 战斗循环×增益状态 | `StatusContainer.apply_effect()`(编排宿主 begin_battle 链) | `roll_multiplier_test.gd:_smoke_armor_roll` |
| 状态效果结算链(逐值衰减/到期移除/战斗结束全清/满层拒绝/独立并存/首授快照) | 增益状态 | `StatusContainer.apply_effect()`/`decay_round()`/`notify_battle_end()` | `system_contracts_test.gd:_test_status_decay_and_clear`;`system_contracts_test.gd:_test_status_stack_full_reject`;`system_contracts_test.gd:_test_status_independent_branch`;`system_contracts_test.gd:_test_status_snapshot_first_grant` |
| 状态实例按个体归属:个体隔离/全体授予逐体发/死亡即清/同体合并照旧 | 增益状态 | `StatusContainer.apply_effect()`(个体键)/`purge_owner()` | `system_contracts_test.gd:_test_status_individual_isolation`;`system_contracts_test.gd:_test_status_grant_all_per_unit`;`system_contracts_test.gd:_test_status_death_clears`;`system_contracts_test.gd:_test_status_merge_per_unit` |
@@ -243,15 +243,15 @@ tests/
## Running Tests
```bash
# 冒烟测试——极小启动场景,秒级
# 冒烟测试(闸 00)——极小启动场景,秒级
python tools/run_smoke.py
# 单元与集成测试——Windows 须用 console 版 exe 取 stdout;工作目录 = game/(含 project.godot 那一层)
godot --headless --script tests/run_all.gd # 统一入口(本地单元与集成测试闸调用点)
# 单元与集成测试(闸 02)——Windows 须用 console 版 exe 取 stdout;工作目录 = game/(含 project.godot 那一层)
godot --headless --script tests/run_all.gd # 统一入口(本地闸 02 闸调用点)
godot --headless --script tests/run_all.gd -- --only=battle_settlement # 选跑态——只跑路径含该词的套件(开发期最窄确认;零命中=用法错 exit 2,不产闸回执)
godot --headless --script tests/unit/example_test.gd
# 系统测试——过渡期宿主 smoke_all.gd;分钟级
# 系统测试(闸 01)——过渡期宿主 smoke_all.gd;分钟级
python tools/run_system_test.py
# 场景探针(UI 探针层)——以探针场景为运行目标,走完整项目引导(autoload 在位)
@@ -332,7 +332,7 @@ S12 效果引擎无状态化旧件零残留:扫描 game/ 全域 `.gd` 与 `gam
## 本地闸(唯一验收门禁)
**验收=合回后远端 CI 闸全绿**(2026-11-03 用户裁,取代本地的合回验收;游戏码批=两域全量、02 全套件);`tools/run_all_gates.sh` 仍是全部验证闸的唯一入口与唯一清单,本地按改动域选档跑批(`--code`/`--docs`/跨两域或改闸件本身=不带参数全量;代码域 02 按关联档选套件,回执 `scope` 字段记 full|related)只服务开发期窄测,**不构成验收**,
本仓库无远端、无 CI——**验收只在本地发生**,跑 `tools/run_all_gates.sh` 是收口唯一入口,按本批改动域选档(`--code`/`--docs`/跨两域或改闸件本身=不带参数全量;代码域 02 按关联档选套件,回执 `scope` 字段记 full|related),
跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 与 `scope` 字段),合回 master 由 worktree-guard 按 diff 文件性质推域校验回执(`game/` 内 `.md` 归文档域)。
闸清单与域划分(含选闸算法与套件族映射表)的唯一定义处就是那个脚本,本文档不另列清单(清单散落多份文档正是历史漏跑闸的成因)。
@@ -346,19 +346,6 @@ S12 效果引擎无状态化旧件零残留:扫描 game/ 全域 `.gd` 与 `gam
**gdUnit4 维持 deferred**:不引入第三方测试框架,本仓库在既有载体(`--script` 套件+场景运行目标)内解决。
## 闸门耗时基线(超时对比依据)
各闸在本机(Windows 基准档)的预期耗时区间与耗时模型。单次跑批实测显著超出区间上限(约 2× 中值)时,先核对环境因素(导入缓存重建、杀毒扫描、显示器休眠)再判性能回归。区间为单机粗测值;闸回执后续若记录实际耗时,以其积累值替换本表。
| 闸 | 耗时模型 | 预期区间 | 备注 |
|---|---|---|---|
| 00 冒烟 | O(1)——引擎启动一次+存活检查 | ~10–20 秒 | |
| 01 系统测试 | O(屏壳装配数 × 断言面规模) | ~2–3 分钟 | 断言计数随面浮动(口径见「断言计数双口径」节) |
| 02 单元与集成 | O(套件 manifest 条目数 × 单套件冷启动) | ~3–6 分钟 | 每套件独立子进程;单套件挂死守卫 120 秒 |
| 03 场景探针 | O(探针段数 × 引擎冷启动) | ~3–5 分钟(--full/--click 各计一次) | 耗时大头=每段一次引擎冷启动 |
| 05 渲染 | O(截图段数 × 窗口化冷启动 + golden 比对) | ~8–15 分钟 | 五段串行,需显示器在位 |
| 远端 CI 全量 | 上列各闸串联(两域全量) | ~30–35 分钟 | 2026-11-04 远端 run 实测 ≈33 分钟(单点样本) |
## 术语(本项目内的唯一语义)
标准四级测试层(2026-10-22 用户裁,权威对照=design/gdd/术语表.md「退役词对照」):
+1 -1
View File
@@ -211,7 +211,7 @@ func _suites() -> Array:
{"path": "res://tests/unit/battle_settlement/settlement_wiring_snapshot_test.gd", "expect": 0}, # 结算接线同种子逐项 golden 对拍主证〔封装5.1〕(架构审查 5.1 封装修复——段类束注入改造零行为变更基线)
{"path": "res://tests/unit/battle_settlement/seal_polarity_test.gd", "expect": 0}, # 封印与极性宿主(末点 AP 封印/封印不可逆四情况/清图奖励双份与弱形式/刷新 provenance 守卫/极性路由与封印溢价/负向字典/格面资源资格——批 B 自 smoke_all 迁入)
{"path": "res://tests/unit/battle_settlement/round_flow_skill_pool_test.gd", "expect": 0}, # 轮内流与技能池宿主(全战斗回放/轮内刷新与 V5 快照单源/遭遇区间骰/结算五段全序/技能池数据面·回放独立·EV 锚·死亡打断·校验拒绝/妨害附加/四桶计数——批 B 自 smoke_all 迁入)
{"path": "res://tests/unit/battle_settlement/refresh_feats_test.gd", "expect": 0}, # 换图系专长七件宿主(迭代 46 卡A——基座产 RAP 与折价同源/成功事实恰一次/全局受伤四路径恰一次/石层缓冲/崩壁延迟/蓄力周转/坑道小憩/矿道红利 GP/七件数据与牌库/无专长基线;原经卡片申报走关联档选跑,卡片退役后补登记由单元与集成测试全套件承接)
{"path": "res://tests/unit/battle_settlement/refresh_feats_test.gd", "expect": 0}, # 换图系专长七件宿主(迭代 46 卡A——基座产 RAP 与折价同源/成功事实恰一次/全局受伤四路径恰一次/石层缓冲/崩壁延迟/蓄力周转/坑道小憩/矿道红利 GP/七件数据与牌库/无专长基线;原经卡片申报走 02 关联档选跑,卡片退役后补登记由 02 全套件承接)
{"path": "res://tests/unit/battle_settlement/variant_placement_test.gd", "expect": 0}, # 变异布置宿主(战内布置段门控与数量公式/全局变异兜底回落与资源面偏斜——批 B 自 smoke_all 迁入)
{"path": "res://tests/unit/battle_settlement/skill_unlock_test.gd", "expect": 0}, # Boss 半血解锁技·容器机制(门谓词/闩不可逆/双面过滤/保底不逼锁定/零扰动)
{"path": "res://tests/unit/battle_settlement/extended_row_violation_test.gd", "expect": 1, "negative": true}, # 卡④ wr2-np 扩展行必载 effect_id 负路径(行协议运行期违约面首个负面套件——独立进程豁免错误扫描)
+1 -1
View File
@@ -3,7 +3,7 @@
# 全局字体与主题可加载、主屏场景实例化、启动态菜单可见。
# 断言只查存在性与可见性,不做任何玩法逻辑判定;断言完打印摘要并以退出码收口,
# 判定三闸(退出码+摘要失败数+脚本错误扫描)由外部运行器 tools/run_smoke.py 承担。
# 运行:python tools/run_smoke.py(本地冒烟测试闸)
# 运行:python tools/run_smoke.py(本地闸 00 冒烟测试)
extends Node
const MAIN_SCENE_PATH: String = "res://presentation/main.tscn"
+3 -3
View File
@@ -1,9 +1,9 @@
# tests/system_test.gd — 系统测试宿主薄壳(SceneTree+构造注入)
# 系统测试 = 启动真实工程走真实玩法路径的断言面(标准四级测试层之一)。
# 本件为系统测试宿主(标准四级测试层之一)。(原过渡期自述作废——,
# 系统测试 = 启动真实工程走真实玩法路径的断言面(标准四级测试层之一,闸 01)。
# 本件为系统测试宿主(标准四级测试层之一,闸 01)。(原过渡期自述作废——,
# 本批只落宿主薄壳,跑通即绿)。迁移完成后 tools/run_system_test.py 缺省 --script 改指本件。
# 协议照单元侧惯例:extends SceneTree 获得 quit(code);--script 模式不加载 autoload,
# 运行形态=--script 直跑+autoload 分派闸(ConfigDB/SaveManager 在座才进主断言面——本闸实跑 65 项 0 失败实证在案);摘要行由 tools/run_system_test.py 三闸判定
# 运行形态=--script 直跑+autoload 分派闸(ConfigDB/SaveManager 在座才进主断言面——闸 01 实跑 65 项 0 失败实证在案);摘要行由 tools/run_system_test.py 三闸判定
# (退出码+摘要失败数+脚本错误扫描)。
# 运行:python tools/run_system_test.py --script res://tests/system_test.gd
extends SceneTree
@@ -53,7 +53,7 @@ func _test_install_invariants() -> void:
Kit.check("装板带初始扩张揭示(%d 格,非零且未铺满安全格 %d)" % [opened, safe_total],
opened > 0 and opened < safe_total)
Kit.check("安全格余量 = 安全格总数 − 已揭示数(%d − %d)" % [safe_total, opened],
_rules.safe_remaining() == safe_total - opened)
_rules._safe_remaining() == safe_total - opened)
Kit.check("装板后仍有未揭示格", _rules.has_unrevealed())
@@ -86,12 +86,12 @@ func _test_reveal_advances() -> void:
if c == Vector2i(-1, -1):
Kit.check("存在未揭示安全格(揭示推进前置)", false)
return
var before: int = _rules.safe_remaining()
var before: int = _rules._safe_remaining()
var ok: bool = _rules.fx_reveal(c)
Kit.check("专长揭示受理(fx_reveal 不受邻接约束)", ok)
Kit.check("fx_reveal 后该格已揭示", _rules.is_revealed(c))
Kit.check("fx_reveal 后安全格余量递减(%d → %d)" % [before, _rules.safe_remaining()],
_rules.safe_remaining() < before)
Kit.check("fx_reveal 后安全格余量递减(%d → %d)" % [before, _rules._safe_remaining()],
_rules._safe_remaining() < before)
func _test_seal_place() -> void:
+4 -18
View File
@@ -185,7 +185,6 @@ def capability_rows():
def check_capability_rows(cap_tables, suites, funcs, cards):
total = 0
names = []
referenced = set()
suite_by_base = {}
for rel in suites:
suite_by_base.setdefault(Path(rel).name, []).append(rel)
@@ -208,25 +207,13 @@ def check_capability_rows(cap_tables, suites, funcs, cards):
red(README.relative_to(REPO), no, "能力行结构",
"「%s」行格数不足或空列(%s)" % (name[:16], "/".join(empty) or "列数"))
continue
referenced.update(m.group(1) for m in CASE_RE.finditer(short("代表用例")))
if "条件闸" in short("代表用例"):
continue
check_cases(no, name, short("代表用例"), suite_by_base, funcs, cards)
check_entry(no, name, short("真实入口"), funcs, cards)
if total == 0:
selfcheck.append("能力表零数据行")
return names, referenced
def check_suite_coverage(suites, referenced):
"""链式锚反向覆盖(test-conventions §8.3 退役对账):run_all.gd manifest 的每个套件
必须被 ≥1 个能力行「代表用例」引用——断言(尾缀挂卡/挂套件)→套件→能力行→系统文档
的链条中,套件未接能力行=卡片退役后验收意图断链(营地图批先例)。"""
missing = [rel for rel in suites if Path(rel).name not in referenced]
for rel in sorted(missing):
red(README.relative_to(REPO), 1, "链式锚",
"套件 %s 未被任何能力行「代表用例」引用——断言→套件→能力行→系统文档链条断环" % rel)
return missing
return names
def check_cases(no, name, cell, suite_by_base, funcs, cards):
@@ -336,8 +323,7 @@ def main():
funcs = product_funcs()
if not funcs:
selfcheck.append("产品码零 `func`")
names, referenced = check_capability_rows(cap_tables, suites, funcs, cards_corpus())
missing_suites = check_suite_coverage(suites, referenced)
names = check_capability_rows(cap_tables, suites, funcs, cards_corpus())
check_coverage(cov_tables, names)
if selfcheck:
@@ -345,9 +331,9 @@ def main():
for s in selfcheck:
print(" ! " + s)
print("| ── 能力审计:系统文档 %d 篇 · 能力行 %d · 套件 %d · 覆盖域表 %d 行 · 链断套件 %d · 红项 %d"
print("| ── 能力审计:系统文档 %d 篇 · 能力行 %d · 套件 %d · 覆盖域表 %d 行 · 红项 %d"
% (len(system_docs()), len(names), len(suites),
sum(len(r) for _, r in cov_tables), len(missing_suites), len(reds)))
sum(len(r) for _, r in cov_tables), len(reds)))
for path, line, check, msg in reds:
print(" ✗ [%s] %s:%d %s" % (check, path, line, msg))
if selfcheck:
+1 -1
View File
@@ -10,7 +10,7 @@
# 质量义务=落批人工确认可跑通(run_all_gates.sh 头注既有口径,不在远端重复烧引擎)。
# 3. diff 空 → skip。
# 判定面说明:game/tests/** 与 game/sim/** 属 game/ 行为载体(.md 除外)→ full,
# 与 run_all_gates.sh 关联档口径一致(测量面批单元与集成测试照跑全套件)。
# 与 run_all_gates.sh 关联档口径一致(测量面批 02 照跑全套件)。
set -u
base="${1:-}"
head="${2:-HEAD}"
+1 -1
View File
@@ -25,7 +25,7 @@
门①语义检查属设计面人工核对(§4.2 色相语义判域),不在本器执法面。
用法:
# 主用法(闸机色板闸承载):扫目录,新增色相对 --baseline 判定
# 主用法(闸机 05b 色板闸承载):扫目录,新增色相对 --baseline 判定
python tools/palette_check.py <目录> --baseline <基线目录>
# 无基线:只出读数(单张色数/ 近色对计数),不判红
python tools/palette_check.py <目录>
+3 -25
View File
@@ -8,7 +8,7 @@ r"""pipeline_gate.py — 工作流程流水线闸(待办清单条目状态机
分档放行)/需求卡片口径(C1 文档义务/C2 设计事实源/C3 改动—验收对照表/C4 验收档申报)+一次交叉核。
约束:每红项一行「文件:行号 判据名 一句白话说明」,exit 0=全绿 / 1=有红 / 2=输入件缺失;
卡片与报告存在性相对 --repo-root 解析(缺省=本文件上两级);待办清单与 roadmap 均已为新格式
(2026-10-18 数据整改批),本闸已注册进 tools/run_all_gates.sh 文档域(流水线闸)。
(2026-10-18 数据整改批),本闸已注册进 tools/run_all_gates.sh 文档域(08c 流水线闸)。
用法:python tools/pipeline_gate.py [--todo PATH] [--roadmap PATH] [--repo-root PATH]
"""
import argparse
@@ -63,9 +63,6 @@ CAP_README = Path("game/tests/README.md")
# C4 验收档申报(2026-11-04 验收强度分级批,生效 2026-11-05;test-conventions §8.3)
GRADE_RULE_DATE = (2026, 11, 5)
GRADE_RE = re.compile(r"验收档\s*[::]\s*[高中低]")
# C5 判据出处行锚(2026-11-05 立;test-conventions §8.3:需求点格须带现行文档判据句锚)
ANCHOR_RULE_DATE = (2026, 11, 5)
ANCHOR_RE = re.compile(r"([A-Za-z0-9_\-.\\/]+\.(?:md))\s*:\s*(\d+)")
# 待办条目正文限长双判据(2026-11-04 立,doc-conventions §12.1:行数抓清单蔓延、字数抓单行堆砌)
ENTRY_MAX_LINES = 12
ENTRY_MAX_CHARS = 700
@@ -501,19 +498,10 @@ def capability_names(root):
return names
def anchor_valid(root, rel, lineno):
"""判据出处行锚有效性:文档存在且行号在行数范围内(相对仓库根解析)。"""
p = root / rel.replace("\\", "/")
if not p.is_file():
return False
return 1 <= lineno <= len(p.read_text(encoding="utf-8-sig").splitlines())
def check_ledger(text, path, when, issues, root):
"""C3 改动—验收对照表结构检查(生效 2026-10-21);C5 判据出处行锚(生效 2026-11-05)。
"""C3 改动—验收对照表结构检查(生效 2026-10-21)。
只查:对照表节在不在、表头有没有五个必需列、有没有数据行、必填格空不空;
C5=生效日后需求点格须带现行文档行锚且指向存在(test-conventions §8.3)。
只查:对照表节在不在、表头有没有五个必需列、有没有数据行、必填格空不空。
不判断测试是否真的覆盖改动——那是独立验收审计的职责,机器闸不猜语义。
"""
lines = text.splitlines()
@@ -626,16 +614,6 @@ def check_ledger(text, path, when, issues, root):
issues.append((str(path), i + 1, "能力行不可核",
"既有能力「%s」不在 game/tests/README.md 能力表"
% cap_name[:24]))
if when >= ANCHOR_RULE_DATE:
point_cell = cells[idx["需求点"]] if idx.get("需求点", 99) < len(cells) else ""
anchors = ANCHOR_RE.findall(point_cell)
if not anchors:
issues.append((str(path), i + 1, "判据出处行锚缺失",
"「需求点」格须带判据出处行锚 `<现行文档相对路径>:<行号>`"
"(2026-11-05 起,test-conventions §8.3)"))
elif not any(anchor_valid(root, rel, int(no)) for rel, no in anchors):
issues.append((str(path), i + 1, "判据出处行锚不可核",
"锚指向的文档或行号不存在:%s" % point_cell[:40]))
if rows == 0:
issues.append((str(path), header_line, "验收对照表零行",
"表内没有数据行"))
+1 -1
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# 渲染闸正主——窗口化自动闸。
# 范围(渲染五闸的机器面:shot_probe/golden_compare/board_shot 三件入闸;
# fx_probe 逻辑层归场景探针闸;1920 档布局机检面待补)。
# fx_probe 逻辑层归 03 场景探针闸;1920 档布局机检面待补)。
# 骨架复用 probe_gate:①每段 timeout 守卫(挂死=FAIL 不静默)②段前预清+段后清本 game 目录孤儿
# Godot(按命令行精确匹配本 game 路径,不误杀其他 worktree 会话)③末尾残留清点=批末闸(必须 0)。
# 段:
+53 -53
View File
@@ -9,16 +9,16 @@
# 跑闸时代码面带着未提交改动,回执会标 dirty_game(验的不是 HEAD 字节)。
#
# 用法:tools/run_all_gates.sh [--code|--docs|--quick]
# --code 只跑代码域闸(冒烟测试/系统测试/单元与集成测试/场景探针/
# 渲染闸/静态扫描+熵增闸)
# --code 只跑代码域闸(00 冒烟测试/01 系统测试/02 单元与集成测试/03 场景探针/
# 05 渲染闸/09 静态扫描+10 熵增闸)
# ——本批改动含 game/ 下非 .md 的行为载体(代码/测试脚本/探针/场景/数据表/素材)时用。
# 关联档选闸(2026-10-29 用户裁,推广自 2026-10-28 影响面选闸):冒烟测试/系统测试/
# 静态扫描三件保险恒跑不降;场景探针与渲染闸维持影响面口径(本批 game/ 改动
# 全在测量面 game/tests、game/sim 时记 SKIP);单元与集成测试改「选套件跑」——套件集=
# 关联档选闸(2026-10-29 用户裁,推广自 2026-10-28 影响面选闸):00 冒烟/01 系统测试/
# 09 静态扫描三件保险恒跑不降;03 场景探针与 05 渲染闸维持影响面口径(本批 game/ 改动
# 全在测量面 game/tests、game/sim 时记 SKIP);02 单元与集成改「选套件跑」——套件集=
# 需求卡片申报 ∪ diff 推导并集(算法与套件族映射表见脚本中部),横切面(公共件/数据表/
# autoload/数值·存档·RNG·结算触面)回落全套件;回执 scope 字段记 full|related。
# --docs 只跑文档域闸(gdd_lint/doc_link/doc_term/流水线闸/引用可核/
# 能力审计+熵增闸)
# --docs 只跑文档域闸(06 gdd_lint/07 doc_link/08 doc_term/08c 流水线闸/08d 引用可核/
# 08e 能力审计+10 熵增闸)
# ——本批改动只有文档件时用:game/ 内的 .md(README、生成清单)、design/、根规则文件、
# tools/ 工具脚本、.zcode/ 配置等一切非代码面改动。
# --quick 全量档的中段自检形态:跳过渲染闸(窗口化,需开显示器)与静态扫描;完成出库禁用。
@@ -70,7 +70,7 @@ if [ "$DOMAIN" = "code" ] || [ "$DOMAIN" = "full" ]; then
fi
# 控制台编码:本仓库闸件输出中文与 ✅/✗ 符号。Windows GBK 控制台下 python 打印这些字符会抛
# UnicodeEncodeError——待办清单/熵增两闸直接崩(假红)、静态扫描的 FAIL 行被写成乱码字节(指纹漂移,
# UnicodeEncodeError——待办清单/10 两闸直接崩(假红)、09 的 FAIL 行被写成乱码字节(指纹漂移,
# 与基线比必判「红项清单变化」假红)、末尾汇总自身崩掉打不出结论。
export PYTHONIOENCODING=utf-8
@@ -207,10 +207,10 @@ suite_terms_for() { # suite_terms_for <仓库相对路径> → stdout: 套件
}
# ── 关联档选闸(2026-10-29 用户裁,推广自 2026-10-28 影响面选闸)──
# 三件保险恒跑不降:冒烟测试/系统测试/静态扫描(熵增闸照旧两域都跑)。
# 场景探针与渲染闸维持影响面口径:本批 diff 相对 master 分叉点的 game/ 改动若全部落在
# 三件保险恒跑不降:00 冒烟/01 系统测试/09 静态扫描(10 熵增闸照旧两域都跑)。
# 03 场景探针与 05 渲染闸维持影响面口径:本批 diff 相对 master 分叉点的 game/ 改动若全部落在
# 测量面(game/tests/**、game/sim/**——测试与模拟驱动件,不触场景树与渲染输出)则记 SKIP。
# 单元与集成测试改「选套件跑」:套件集=申报 ∪ 推导 取并集——
# 02 单元与集成改「选套件跑」:套件集=申报 ∪ 推导 取并集——
# 申报:在册需求卡片(本批 diff 触及的 reports/需求卡片*.md ∪ roadmap「进行中」迭代行登记的
# 卡片)对照表「验收测试」列提到的测试文件名,按文件名作为 --only 子串词归套件;
# 找不到在册卡片则申报集为空并输出注明。
@@ -218,9 +218,9 @@ suite_terms_for() { # suite_terms_for <仓库相对路径> → stdout: 套件
# 映射不到的文件一律按横切处理。
# 横切回落全量:diff 命中 game/data/tables/**、公共件前缀(config_db*/rng_service*/
# save_manager*/meta_progression*/main*——main* 覆盖组合根拆件与主场景)、
# autoload 注册面(game/project.godot)、或下方横切触面表任一 → 单元与集成测试跑全部套件。
# 测量面批(game/ 改动全在 tests/sim):单元与集成测试照旧全套件跑(不加过滤)。
# 回执 scope 字段:full|related(单元与集成测试跑子集=related);映射表只在本脚本一处定义。
# autoload 注册面(game/project.godot)、或下方横切触面表任一 → 02 跑全部套件。
# 测量面批(game/ 改动全在 tests/sim):02 照旧全套件跑(不加过滤)。
# 回执 scope 字段:full|related(02 跑子集=related);映射表只在本脚本一处定义。
NONBEHAVIOR=0
SCOPE="full"
ONLY_TERMS=""
@@ -231,9 +231,9 @@ if in_domain code; then
_behavior="$(echo "$_gfiles" | grep -vE '^(game/tests/|game/sim/)' || true)"
if [ -n "$_gfiles" ] && [ -z "$_behavior" ]; then
NONBEHAVIOR=1
echo "[gates] 影响面选闸:game/ 改动全在测量面(tests/sim)——场景探针/渲染闸 → SKIP(冒烟测试/系统测试/静态扫描三件保险恒跑)"
echo "[gates] 影响面选闸:game/ 改动全在测量面(tests/sim)——03 场景探针/05 渲染闸 → SKIP(00/01/09 三件保险恒跑)"
fi
# ── 选套件推导(只对行为面代码文件做——测量面文件不进推导,其验证就是单元与集成测试/静态扫描自身)──
# ── 02 选套件推导(只对行为面代码文件做——测量面文件不进推导,其验证就是 02/09 自身)──
_codefiles="$(echo "$_gfiles" | grep -viE '\.md$' | grep -vE '^(game/tests/|game/sim/)' || true)"
if [ -n "$_codefiles" ]; then
CROSSCUT=0
@@ -269,7 +269,7 @@ if in_domain code; then
done
if [ "$CROSSCUT" -eq 1 ]; then
SCOPE="full"
echo "[gates] 关联档选闸:diff 命中横切面(公共件/数据表/autoload/数值·存档·RNG·结算触面)——单元与集成测试回落全部套件"
echo "[gates] 关联档选闸:diff 命中横切面(公共件/数据表/autoload/数值·存档·RNG·结算触面)——02 回落全部套件"
else
# 并集去重:推导族词 ∪ 申报测试文件名;并集非空才过滤(映射表保证族词非空,此处双保险)。
_union="$(printf '%s\n' $_family_terms $_declared_names | sed '/^$/d' | sort -u | tr '\n' ' ' || true)"
@@ -285,12 +285,12 @@ if in_domain code; then
echo "[gates] 关联档选闸·申报:未找到在册需求卡片(diff 与 roadmap 进行中行均无),申报集为空——仅按 diff 推导"
fi
if [ "$SCOPE" = related ]; then
echo "[gates] 关联档选闸:单元与集成测试选套件跑(--only=${ONLY_TERMS})"
echo "[gates] 关联档选闸:02 选套件跑(--only=${ONLY_TERMS})"
fi
fi
fi
# ── 代码域起手自检(闸承载:只省烧闸,不做合回判定——真判定在各闸与合回 guard)──
# ── 代码域起手自检(闸承载:只省烧闸,不做合回判定——真判定在 00–11 与合回 guard)──
if in_domain code; then
# worktree 素材同步:素材走 gitignore 不入 git,worktree 冷检出缺件=呈现断言红的环境成因;
# 主工作区直跑=脚本内自动无操作。
@@ -332,7 +332,7 @@ gate() { # gate <名称> <日志名> <命令...>
}
gate_unit() { # run_all 是 --script 模式,退出码为唯一判据;分片态=多 Godot 进程各跑一片
local name="单元与集成测试"
local name="02 单元与集成测试"
# 关联档选跑:把套件词列表经 --only= 传给 run_all(闸机来源标记 --only-source=gates,
# 入口据此打关联档横幅、不落「仅供开发期」免责句);全量态不传任何选跑参数。
local -a _user_args=()
@@ -396,63 +396,63 @@ gate_unit() { # run_all 是 --script 模式,退出码为唯一判据;分
return 0
}
# ══ 代码域 ══
# ══ 代码域(0–5)══
if in_domain code; then
# ── 冒烟测试(极小启动场景:真实开机路径存活检查,零逻辑判定,秒级)──
# 三件保险之一(冒烟测试/系统测试/静态扫描):恒跑不降——测量面批也不免(关联档选闸,2026-10-29 用户裁)。
gate "冒烟测试" "smoke_boot.log" "$PY" "$REPO/tools/run_smoke.py"
# ── 系统测试(真实玩法路径断言面;宿主=game/tests/system_test.gd,缺省 --script 由 run_system_test.py 持有)──
gate "系统测试" "system_test.log" "$PY" "$REPO/tools/run_system_test.py"
# ── 单元与集成测试 ──
# ── 0. 冒烟测试(极小启动场景:真实开机路径存活检查,零逻辑判定,秒级)──
# 三件保险之一(00/01/09):恒跑不降——测量面批也不免(关联档选闸,2026-10-29 用户裁)。
gate "00 冒烟测试" "smoke_boot.log" "$PY" "$REPO/tools/run_smoke.py"
# ── 1. 系统测试(真实玩法路径断言面;宿主=game/tests/system_test.gd,缺省 --script 由 run_system_test.py 持有)──
gate "01 系统测试" "system_test.log" "$PY" "$REPO/tools/run_system_test.py"
# ── 2. 单元与集成测试 ──
gate_unit
# ── 场景探针(走查+断言十件一次跑齐;iter27 漏跑断言段事故后固化为无条件整闸)──
# ── 3. 场景探针(走查+断言十件一次跑齐;iter27 漏跑断言段事故后固化为无条件整闸)──
if [ $NONBEHAVIOR -eq 1 ]; then
NAMES+=("场景探针"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 场景探针 → SKIP(影响面选闸:测量面改动不触场景树与探针断言面)"
NAMES+=("03 场景探针"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 03 场景探针 → SKIP(影响面选闸:测量面改动不触场景树与探针断言面)"
else
gate "场景探针" "probe.log" bash "$REPO/tools/probe_gate.sh" "$GAME"
gate "03 场景探针" "probe.log" bash "$REPO/tools/probe_gate.sh" "$GAME"
fi
# ── 渲染闸(窗口化,依赖 PIL)──
# ── 5. 渲染面(窗口化,依赖 PIL)──
if [ $QUICK -eq 1 ] || [ $NONBEHAVIOR -eq 1 ]; then
NAMES+=("渲染闸"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 渲染闸 → SKIP(--quick 或 影响面选闸:测量面改动不触渲染输出)"
NAMES+=("05 渲染闸"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 05 渲染闸 → SKIP(--quick 或 影响面选闸:测量面改动不触渲染输出)"
else
gate "渲染闸" "render.log" bash "$REPO/tools/render_gate.sh" "$GAME"
gate "05 渲染闸" "render.log" bash "$REPO/tools/render_gate.sh" "$GAME"
fi
# ── 色板闸(§4.1 新色双门②:素材新增色 vs 基线〔token∪素材快照〕近色判红;
# ── 5b. 色板闸(§4.1 新色双门②:素材新增色 vs 基线〔token∪素材快照〕近色判红;
# 门①语义检查属设计面人工核对不入机检;基线刷新=tools/gen_palette_baseline.py,素材批收尾动作)──
if [ $QUICK -eq 1 ] || [ $NONBEHAVIOR -eq 1 ]; then
NAMES+=("色板闸"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 色板闸 → SKIP(--quick 或 影响面选闸:素材色板面不随测量面改动开闸)"
NAMES+=("05b 色板闸"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 05b 色板闸 → SKIP(--quick 或 影响面选闸:素材色板面不随测量面改动开闸)"
else
gate "色板闸" "palette.log" "$PY" "$REPO/tools/palette_check.py" "$GAME/assets/art" --baseline "$REPO/game/tests/golden/palette_baseline"
gate "05b 色板闸" "palette.log" "$PY" "$REPO/tools/palette_check.py" "$GAME/assets/art" --baseline "$REPO/game/tests/golden/palette_baseline"
fi
fi
# ══ 文档域 ══
# ══ 文档域(6–8e)══
if in_domain docs; then
# 全量档(CI 与手动档同判定面)
gate "gdd_lint" "gdd_lint.log" "$PY" "$REPO/tools/gdd_lint.py"
gate "变更记录节" "changelog.log" "$PY" "$REPO/tools/gdd_lint.py" --changelog-only
gate "doc_link" "doc_link.log" "$PY" "$REPO/tools/doc_link_audit.py" --strict
gate "doc_term" "doc_term.log" "$PY" "$REPO/tools/doc_term_audit.py" --strict
gate "流水线闸" "pipeline.log" "$PY" "$REPO/tools/pipeline_gate.py"
gate "引用可核" "doc_ref.log" "$PY" "$REPO/tools/doc_ref_audit.py" --strict
gate "能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
gate "06 gdd_lint" "gdd_lint.log" "$PY" "$REPO/tools/gdd_lint.py"
gate "06b 变更记录节" "changelog.log" "$PY" "$REPO/tools/gdd_lint.py" --changelog-only
gate "07 doc_link" "doc_link.log" "$PY" "$REPO/tools/doc_link_audit.py" --strict
gate "08 doc_term" "doc_term.log" "$PY" "$REPO/tools/doc_term_audit.py" --strict
gate "08c 流水线闸" "pipeline.log" "$PY" "$REPO/tools/pipeline_gate.py"
gate "08d 引用可核" "doc_ref.log" "$PY" "$REPO/tools/doc_ref_audit.py" --strict
gate "08e 能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
fi
# ── 静态扫描(代码域)+ 熵增闸(两域都跑:它查顶层目录/根文件/图像白名单/退役路径,
# ── 7. 静态扫描(代码域)+ 熵增闸(两域都跑:它查顶层目录/根文件/图像白名单/退役路径,
# 文档批新增或挪动 design/ 下的文件同样会绊它;纯文本扫描,秒级)──
if [ $QUICK -eq 0 ]; then
if in_domain code; then
gate "静态扫描" "static_scan.log" bash "$GAME/tests/static_scan.sh"
gate "09 静态扫描" "static_scan.log" bash "$GAME/tests/static_scan.sh"
fi
gate "熵增闸" "entropy.log" "$PY" "$REPO/tools/repo_structure_audit.py"
gate "10 熵增闸" "entropy.log" "$PY" "$REPO/tools/repo_structure_audit.py"
else
if in_domain code; then
NAMES+=("静态扫描"); EXITS+=("SKIP"); LOGS+=("")
NAMES+=("09 静态扫描"); EXITS+=("SKIP"); LOGS+=("")
fi
NAMES+=("熵增闸"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 静态扫描(代码域)/ 熵增闸 → SKIP(--quick)"
NAMES+=("10 熵增闸"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 09 静态扫描(代码域)/ 10 熵增闸 → SKIP(--quick)"
fi
# ── 与存量基线比对 ──
-8
View File
@@ -286,11 +286,3 @@
**改动面**:游戏代码
**状态**:文档设计已闭合+已写卡+已编迭代 52
#### 测试面真输入化分期与存量断言逐条精锚(2026-11-05 登记,来源=批次移交)
**实况**:证据=test-conventions §8.3「判据出处行锚」「真实入口判定口径」两条文(本批落);链式锚机检已在闸(capability_audit 链断 0,80 套件全承接)。剩余面=①营地七物件段等 ~40 处 emit 站点仍为接线验证(大门/进档链已于本批真输入化,能力行 :158 残险在案)②存量 996 条断言的判据出处逐条精锚(链式锚已保可核,精锚提升出处粒度)。
**改动面**:游戏代码
**状态**:待执行