From b27e4710b6e284c5508dde6fa24c47e38d0c8dda Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sat, 17 Oct 2026 00:40:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E9=97=B8):=20=E9=AA=8C=E8=AF=81=E9=97=B8?= =?UTF-8?q?=E6=8C=89=E6=94=B9=E5=8A=A8=E5=9F=9F=E5=88=86=E8=B7=91=20+=20?= =?UTF-8?q?=E8=BF=AD=E4=BB=A3=E6=89=B9=E7=A6=81=E5=86=99=20design/=20+=20?= =?UTF-8?q?=E6=96=B0=E5=BB=BA=20reports/=20=E6=94=B6=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E6=89=B9=E4=BA=A7=E5=87=BA=E7=89=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户裁定(2026-10-17)五条:①代码批只跑代码闸、文档批只跑文档闸;②迭代批是代码实现批, 禁写 design/,文档只读、是实现批写测试与代码的输入依据;③迭代实现报告不写在 design/ 内; ④不设文档编写 skill——文档由用户在会话中讨论后落地;⑤本批属工具/规则面,不占迭代号。 一、tools/run_all_gates.sh(域划分唯一出处) - 新增 `--code`(01 冒烟/02 单元/03 探针 --click/04 探针 --full/05 渲染闸/09 静态扫描/11 回放对拍) 与 `--docs`(06 gdd_lint/07 doc_link/08 doc_term/08b active.md 体量);10 熵增闸两域都含 (文档批新增或挪动 .md 同样会绊它,且纯文本秒级);不带参数=全量,改闸件本身或跨两域时用。 - 域外闸不入 NAMES,不产生假变化;同时修「基线绿 → 现 SKIP」被误判为闸况变化(--quick 回执 此前永远自带三条假红)。 - 回执增 domain 与 not_run 字段。 - 导出 PYTHONIOENCODING=utf-8:GBK 控制台下 06/10 两闸打印 ✅/✗ 直接崩(假红)、09 的 FAIL 行 写成乱码字节致指纹漂移(假「红项清单变化」)、末尾汇总自身崩打不出结论——2026-10-16 实踩。 - 新增代码域素材齐盘预检:game/assets/art/** 与一切 *.import 边车都不入 git(2026-09-29 裁定), worktree 冷检出必空盘 → main_theme.tres 解析失败 → 冒烟 V1 七项假红。缺位即拒跑并列缺哪件, 附处置命令(cp 主区素材 + --editor --quit 刷导入;--import 不生成新素材边车)。 二、tools/render_gate.sh(空跑判绿改判红) - 棋盘/菜单两段 golden 在素材缺位时原为 SKIP,而闸末照样打印「全绿」exit 0——一个像素都没比 却报通过。2026-10-16 菜单废基线事故正是靠这个信号盲区混过 worktree 自检的。现改判红。 三、reports/ 新顶层目录(实现批唯一可写目录) - 收迭代实现报告/代拍板追认单/GDD 反哺点清单;repo_structure_audit.py TOP_DIRS 与 CLAUDE.md Project Layout 两处白名单同批改齐(熵增闸自陈的强制口径);reports/README.md 立范围与生命周期。 四、规则文本五处同步 - CLAUDE.md:验收口径改按域;硬性红线新增「两类批」条(迭代批禁写 design/ 与根规则文件、 产出只落 reports/、只跑代码域闸;非迭代批不占迭代号);Project Layout 登记 reports/。 - process-conventions:§3.10 批末按域验收;§3.12 补 active.md 例外(会话状态唯一源,钩子本就 豁免);§3.13 重写为「实现批禁写文档、产出物落 reports/、反哺点由文档批裁决后写入」; §4 分级周期口径改三档;§2 三段流程「三·实现」行加禁写面。 - doc-conventions 载体分工表:迭代实现报告落位由 design/qa/ 改 reports/。 - architecture.md「测试与质量域」:新增「改动域划分」节(域表+唯一出处声明+素材预检缘由), 本表是现状登记非第二份清单。 - 06-待办清单:登记本批两处代拍板(active.md 例外读法、reports/ 新顶层目录)与旧实现报告 iter27/iter28 两份仍在 design/qa/ 未迁位的欠账。 五、连带(不在本仓库,gitignored 本地 harness 配置) - .zcode/hooks/worktree-guard.sh:合回校验由「不碰 game/ 即全免」改为按 diff 路径推定所需域、 要求回执 domain 覆盖之;未登记路径按最严处理,禁默认放行。旧豁免的漏洞:改闸件本身反而零验证。 - ~/.zcode/skills/iter/SKILL.md:顶部加批性质与闸域两条强制声明;批末重闸移出 doc 三闸; 总收口产出物改落 reports/、roadmap 换代移出本批;定义面入库注明是搬运非撰写。 验证:--docs 域全绿(06 由基线红改善为绿,因 fcb8c3f8 已补 01 篇流程图);素材预检反例实测 (移走 button_stone.png → 拒跑 exit 1,还原后正常);渲染闸缺盘反例实测(exit 1 并报 FAIL); --code --docs 互斥与非法参数 exit 2;guard 内嵌 python 编译通过并按 docs 域回执放行 docs 批。 --- CLAUDE.md | 6 +- design/dev/architecture/architecture.md | 10 +++ design/dev/doc-conventions.md | 2 +- design/dev/process-conventions.md | 10 +-- design/gdd/06-待办清单.md | 8 ++ reports/README.md | 24 +++++ tools/render_gate.sh | 11 ++- tools/repo_structure_audit.py | 4 +- tools/run_all_gates.sh | 111 ++++++++++++++++++++---- 9 files changed, 157 insertions(+), 29 deletions(-) create mode 100644 reports/README.md diff --git a/CLAUDE.md b/CLAUDE.md index 246d498c..3ca6db10 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,7 +16,7 @@ ## 运行与验证(工作目录 = 含 project.godot 的切片目录) -- **交付前验收 = 跑 `tools/run_all_gates.sh`(不带 `--quick`),全绿才合回 master**——它是全部验证闸的唯一入口与唯一清单,闸件增减只改它,文档禁另列清单。跑完写回执 `game/tests/.gates/receipt.json`,合回时由 worktree-guard 校验(缺回执/quick 模式/回执 commit ≠ HEAD/含红项四况一律拦截) +- **交付前验收 = 跑 `tools/run_all_gates.sh`,按本批改动域选档(`--code` 只跑代码域/`--docs` 只跑文档域/不带参数=两域全量),所选域全绿才合回 master**——它是全部验证闸的唯一入口与唯一清单,**域划分也只写在它这一个地方**,闸件增减与分域调整都只改它,文档禁另列清单。改闸件本身、或一次改动同时跨两域时跑全量档(§6.2 共享件例外)。跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 字段),合回时由 worktree-guard 按 diff 路径推出该验哪个域再校验回执(缺回执/quick 模式/回执 commit ≠ HEAD/域不覆盖本次改动/含新增红,五况一律拦截) - 单件直跑(调试期最窄确认用,**不构成验收**):系统级断言面 `python tools/run_smoke.py`(名带 smoke 但不止存活检查,术语见 `game/tests/README.md`)· 场景探针 `…console.exe --headless --path . res://smoke/ui_probe.tscn` · 单元 `… --script res://tests/run_all.gd` - 调参批量:`--script res://smoke/adventure_monte_carlo.gd`(全冒险期望)· `res://smoke/reward_expectation.gd`(奖励期望);通关率/难度类测量一律走全冒险 - 数值验证:`python tools/balance_verifier.py`(采集端 `res://smoke/verifier_batch.gd`,报告落 `design/qa/`) @@ -33,6 +33,7 @@ - `game/assets/art/` — 已验收终稿(入包被游戏加载);`game/` — 游戏工程本体(代码 + smoke/ 冒烟 + tests/ 单元集成) - `sim-data/` — 机器读数据产物;数据件不入 git(入库例外 = README+analyze.py+growth_model.py);MC 锚与读数常驻留盘,无数值调整禁重跑 - `tools/` — python 工具脚本;`archive/` — 收口归档 zip(gitignored) +- `reports/` — 迭代批产出物(实现报告/代拍板追认单/GDD 反哺点清单);**实现批唯一可写目录**,只放 .md,不是事实源(细则见 `reports/README.md`) - 根目录:`active.md`(会话状态唯一状态源,退役规则见 doc-conventions 第 10 节)、`roadmap.md`(里程碑)、`stage.txt`/`review-mode.txt`(运行开关) ## 规范导航(动手前先查表) @@ -53,7 +54,8 @@ - 主工作区禁止直接改动与提交——一切改动走独立 worktree(豁免:active.md 会话收口、.zcode 钩子配置) - 测试执行两入口铁规——只有迭代 loop 开发与用户点名两个触发入口;其余会话零测试,需测试支撑先请示并注明未跑测 -- 交付前验收 = 跑 `tools/run_all_gates.sh` 全绿(唯一入口与唯一闸清单,禁在文档另列清单);合回 master 由 worktree-guard 校验回执强制(process-conventions §3) +- 交付前验收 = 跑 `tools/run_all_gates.sh` **按改动域**全绿(唯一入口、唯一闸清单、唯一域划分,禁在文档另列);合回 master 由 worktree-guard 按 diff 推域校验回执强制(process-conventions §3) +- **两类批**——**迭代批**(代码实现,`/iter` 发射):**禁写 `design/` 整个目录**(含新增/修改/删除),设计文档对它只读、是写测试与实现代码的输入依据;产出物只准写 `reports/`;批末只跑代码域闸。**非迭代批**(工具/规则/文档改动):不占 roadmap 迭代号,按改动域跑闸。文档内容的落地由用户在会话中讨论后进行,**无专用的文档编写入口/skill** - MC/模拟:无数值改动零跑;档位硬闸见 `sim-data/README.md`「MC 采集档位表」,一档只能由用户发起 - 闸机轮次纪律:新码先孤立验证 → 出红只跑红件最小单元 → 长批先最小参数试车 → 开工扫坑册(process-conventions §6) - 变异验证——新增/改写验收函数必须变异变红才算验收成立;无变异红记录只写「已实现,拦截能力未证」(test-conventions §6) diff --git a/design/dev/architecture/architecture.md b/design/dev/architecture/architecture.md index 85d460ff..dfde6d14 100644 --- a/design/dev/architecture/architecture.md +++ b/design/dev/architecture/architecture.md @@ -146,6 +146,16 @@ | **render_gate**(视觉 golden) | `tools/render_gate.sh` | 窗口化渲染闸:①shot_probe 七屏截图 → shot_checker A-D 语义断言;②fx_shot_probe / board_shot_probe 取证(超时/零字节/文件缺失任一 = FAIL);③棋盘 golden 逐像素容差比对(差 ≤16 / changed ≤2%,provisional)+ 菜单 golden;④批末孤儿残留清零 | 段级 FAIL 输出 + 汇总退出码 | 查退出码在位(每段 timeout 守卫 + 退出码/取证文件双检);**SCRIPT ERROR 扫描缺失**;断言数下限不适用(判定主体 = golden 容差与取证文件存在性,非断言计数) | | **static_scan**(静态扫描域) | `game/tests/static_scan.sh` | 静态扫描域 S1–S10 可执行载体(grep 规则 + `tools/audit_type_hints.py`〔S9〕+ `tools/audit_file_headers.py`〔S10〕) | fail 聚合退出码(任一硬失败规则命中即 exit 1) | 查退出码在位;SCRIPT ERROR 扫描与断言数下限不适用(逐规则命中即红,无运行时错误面与断言计数);S3 现为 warning 级、不参与 fail 判定(见下表) | +### 改动域划分(2026-10-17 用户裁「代码批只跑代码闸、文档批只跑文档闸」) + +| 域 | 闸 | 本批改动落在这些路径即属该域 | +|---|---|---| +| **代码域** | run_smoke、run_all、probe_gate `--click`、probe_gate `--full`、render_gate、static_scan、回放确定性对拍(条件件) | `game/`、`tools/`、`artwork/` | +| **文档域** | gdd_lint、doc_link_audit、doc_term_audit、active.md 体量闸 | `design/`、`active.md`、`roadmap.md`、`CLAUDE.md` | +| **两域都含** | repo_structure_audit(熵增闸:顶层目录/根文件/图像白名单/退役路径/design 子目录五查——文档批新增或挪动 `.md` 同样会绊它;纯文本扫描秒级) | — | + +口径三条:**域划分的唯一出处是 `tools/run_all_gates.sh`**(`--code`/`--docs`/不带参数=全量),本表是它的现状登记、不是第二份清单;改闸件本身或一次改动跨两域 → 跑全量档(§6.2 共享件例外);合回闸按 merge diff 路径推出应验的域,再校验回执的 `domain` 字段与新增红。代码域跑前先过**素材齐盘预检**——`game/assets/art/**` 与一切 `*.import` 边车都不入 git(2026-09-29 裁定),worktree 冷检出必空盘,空盘态冒烟 V1 成片假红、渲染闸更是一段 golden 都不比。 + ### 三条判定硬闸现行状态 | 硬闸 | run_smoke | run_all | probe_gate | render_gate | static_scan | diff --git a/design/dev/doc-conventions.md b/design/dev/doc-conventions.md index 5e4d3d12..221253be 100644 --- a/design/dev/doc-conventions.md +++ b/design/dev/doc-conventions.md @@ -115,7 +115,7 @@ | sim-data 白名单 | 只留 README、日志、分析工具 .py、现行 MC 对拍锚、设计文档 md;可再生原始数据不入库 | | 出口清理项 | 每迭代出口检查单必含:本迭代中间产物逐份核对(结论已吸收、未决已迁移)后删除 | | 暂存地非档案库 | `design/research/` 与 `design/qa/` 是中间文档暂存地,入内文件默认到期即死 | -| 迭代实现报告 | 实现批收口的迭代级报告(各卡状态/代拍板追认单/GDD 反哺点节),属中间文档:反哺点未决先迁 06,裁决落文吸收后按「证据只留最新」退役 | +| 迭代实现报告 | 实现批收口的迭代级报告(各卡状态/代拍板追认单/GDD 反哺点清单),属中间文档,**落 `reports/` 不落 `design/`**(2026-10-17 用户裁:实现批禁写 `design/`,`reports/` 是其唯一可写目录):反哺点裁决落文吸收后按「证据只留最新」退役 | | 证据只留最新 | 测试证据只保留最新一次迭代或最新一轮同类审计版本;新证据落位后同批删上一份;待人清单过人闸后同规退役 | **删除方式**:按 `design/dev/concept-conventions.md` 第 2 节删除概念 SOP 口径——先查引用面(grep 文件名于阻塞域),改写或删除指路句,再删文件,收尾跑死链检查全绿。 diff --git a/design/dev/process-conventions.md b/design/dev/process-conventions.md index 19cf3b21..78e350c4 100644 --- a/design/dev/process-conventions.md +++ b/design/dev/process-conventions.md @@ -22,7 +22,7 @@ |---|---|---| | 一·设计 | GDD 文档或 art-bible | 用户提起或从 `06-待办清单` 取出;会话内进行、用户主导、禁无监管自跑;经用户点头确认心智一致才许写入文档档;完成判据=待裁点清零,带未裁点的是讨论稿;不自动进写卡段,写卡须用户另行发令 | | 二·写需求卡片 | 需求卡片(写作规范见用户级技能 req-card) | 设计定稿后用户发令;例外=缺陷修复/工程债/测试基建/重构凭 bug 证据或 06 条目直接进入;新玩法/新系统/新内容必须有设计定稿前置;卡片必经 grilling 多轮复核、零待裁点才成立;入库必在 06 登记一条(防遗忘);编入后续未完成迭代(roadmap 迭代行),无空位与用户确认开新一轮 | -| 三·实现 | 代码与配置,可运行的游戏 | 需求卡片永不单独施工,统一由用户指定迭代号触发(=U0 开工令),按 /iter 技能与本文 §3 执行;设计权威文档(GDD/art-bible)批内一律不更新,实现批只出迭代实现报告(见 §3 第 12 条);完成人闸=实机目检清单+代拍板追认单登记交用户即完成批内义务 | +| 三·实现 | 代码与配置,可运行的游戏 | 需求卡片永不单独施工,统一由用户指定迭代号触发(=U0 开工令),按 /iter 技能与本文 §3 执行;**本段是代码实现批:禁写 `design/` 与根规则文件,产出物只落 `reports/`(见 §3 第 13 条)**;设计权威文档(GDD/art-bible)批内一律不更新;完成人闸=实机目检清单+代拍板追认单登记交用户即完成批内义务 | ## 3. 批次工作流标准清单 @@ -44,10 +44,10 @@ 9. **验收函数变异验证**——新增/改写的验收函数(冒烟断言/探针自检/闸机判据)必须配一次变异验证:故意把被测件改坏成该类缺陷形态 → 跑最窄那道闸确认变红 → `git checkout` 精确还原;变异点/跑的闸/红输出摘要/还原结论四件记入实现报告变异验证记录表;**无变异红记录的断言不写「已验收」,只写「已实现,拦截能力未证」**(细则见 test-conventions §6) **完成** -10. **批末全量验收才合并**——在任务 worktree 内跑 `tools/run_all_gates.sh`(不带 `--quick`),全绿才合回 master。该脚本是全部验证闸的**唯一入口与唯一清单**,闸件增减只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。跑完写回执 `game/tests/.gates/receipt.json`,合回时由 worktree-guard 强制校验 +10. **批末按域验收才合并**——在任务 worktree 内跑 `tools/run_all_gates.sh`,**按本批改动域选档**:只碰 `game/`、`tools/`、`artwork/` → `--code`;只碰 `design/`、`active.md`、`roadmap.md`、`CLAUDE.md` → `--docs`;同时跨两域、或改的是全部闸共用的壳(闸脚本本身/探针包装器)→ 全量档不带参数。所选域全绿才合回 master。该脚本是全部验证闸的**唯一入口、唯一清单与唯一域划分**,闸件增减、分域调整都只改它;**本文与其他文档禁另列闸件清单**(清单散落多处正是 iter27 漏跑 `probe_gate --full` 的成因)。跑完写回执 `game/tests/.gates/receipt.json`(含 `domain` 字段),合回时由 worktree-guard 按 diff 路径推域强制校验 11. **合并后主区全量复跑**——合并可能引入冲突或环境差异,落回 master 后再跑全套 -12. **会话完成落记**——active.md 快照与状态记录更新(含删除已完结旧条目,doc-conventions 第 10 节);跨会话教训写入记忆库;漏删旧条目由 `tools/audit_active_state.py` 在全量档抓(合回闸强制,脚本即清单) -13. **文档同步闸**——机械面文档(roadmap 行/registry/active.md 记账/README 工具注)合并前顺带更新完毕方算完成;设计权威文档批内不更新:实现批完成出迭代实现报告(`design/qa/iter-实现报告-<日期>.md`:各卡状态+代拍板追认单+GDD 反哺点节),反哺点同步登记 `06-待办清单`,用户逐点裁决后另批写入文档;反哺点未写入期间相关 GDD 节视为待反哺,读前先查 06;需求卡片必列「关联文档同步面」按两层标注 +12. **会话完成落记**——active.md 快照与状态记录更新(含删除已完结旧条目,doc-conventions 第 10 节);跨会话教训写入记忆库;漏删旧条目由 `tools/audit_active_state.py` 在全量档抓(合回闸强制,脚本即清单)。**例外**:active.md 是会话状态唯一源、不是文档产出物,实现批照写(合回闸钩子本就对它放行);`design/` 与根规则文件(`CLAUDE.md`/`roadmap.md`)实现批禁写,见 §13 +13. **文档同步闸(2026-10-17 改口径:实现批禁写文档)**——实现批**禁写 `design/` 整个目录与根规则文件**,要往外交代的东西一律落 `reports/`:实现报告 `reports/iter-实现报告-<日期>.md`(各卡状态+实机目检总清单+遗留读数义务)、代拍板追认单 `reports/iter-代拍板-<日期>.md`、GDD 反哺点清单 `reports/iter-反哺点-<日期>.md`(每条写清哪个文件哪一节、改成什么、依据是什么)。设计权威文档(GDD/art-bible)**任何批都不直改**:用户在会话中逐点裁决后,由非迭代批(工具/规则/文档批)写入 `design/`,并把反哺条目从 `reports/` 清掉(吸收即死)。机械面文档(roadmap 行/registry/README 工具注)同样归非迭代批在合并前更新。反哺点未落地期间相关 GDD 节视为待反哺,读前先查 `reports/` 与 06;需求卡片必列「关联文档同步面」按两层标注 14. **实机目检归用户,只登记不阻塞**——真人检测项登记进实现报告目检总清单与 07-体验验证清单即完成批内义务;禁把等目检当停工理由或完成前置 15. **中间产物吸收即死**——细则见 CLAUDE.md 硬性红线与 doc-conventions 第 11 节 @@ -57,7 +57,7 @@ **MC/verifier 触发细则**:仅在数值验证场景触发(改数值前后取证);normal 档为主、perfect 按需;**无数值改动的会话禁跑 MC**;结构位移类「存档锚」须先向用户请示定性再跑;需求卡片预设步骤的前提消失时(如「带值裁定后重锚」实际未投值)停问勿照字面执行。 -**分级周期口径**:全部验证闸收编在 `tools/run_all_gates.sh` 一个入口下(**脚本即清单**),分两档——`--quick` 档跳过渲染闸(需开显示器)与静态扫描,供迭代中段自检;**完成出库一律跑全量档(不带 `--quick`)**,回执由合回闸强制校验。真人实机目检归用户不进自动闸。 +**分级周期口径**:全部验证闸收编在 `tools/run_all_gates.sh` 一个入口下(**脚本即清单,且即域划分的唯一出处**),分三档——`--code`/`--docs` 按本批改动域只跑该域闸(代码域=冒烟/单元/探针两档/渲染闸/静态扫描/回放对拍;文档域=gdd_lint/doc_link/doc_term/active.md 体量;熵增闸两域都含);`--quick` 是全量档的中段自检形态(跳过渲染闸与静态扫描);**不带参数=两域全量**,用于改闸件本身或一次改动跨两域。完成出库按所选域全绿,回执由合回闸按 diff 推域强制校验。真人实机目检归用户不进自动闸。 ## 5. MC 采集档位细则 diff --git a/design/gdd/06-待办清单.md b/design/gdd/06-待办清单.md index 4f07e43d..d32137ab 100644 --- a/design/gdd/06-待办清单.md +++ b/design/gdd/06-待办清单.md @@ -602,6 +602,14 @@ ### 文档与结构 +#### 闸按改动域分跑批的两处代拍板 + 旧实现报告迁位欠账(2026-10-17 登记,闸分域批) + +**现状**:用户裁定「改代码的批只跑代码闸、改文档的批只跑文档闸」与「迭代批禁写 `design/`」两条,落地时撞上一处既有规则没覆盖的缝隙,本批按推荐方案先做并登记待追认:① **active.md 记账例外**——§3.12 要求每批更新 active.md 快照与状态记录,而合回闸钩子本就对 active.md 放行(它被定为「会话状态唯一源」而非文档产出物);本批把禁写面落在 `design/` 与根规则文件(`CLAUDE.md`/`roadmap.md`),**active.md 实现批照写**,读法若不符请推翻。② **`reports/` 新顶层目录**——熵增闸「新增顶层目录须同批改 CLAUDE.md Project Layout + 脚本白名单两处」,本批已同批改齐(`repo_structure_audit.py` TOP_DIRS + CLAUDE.md 第 36 行 + `reports/README.md` 立目)。③ **旧实现报告未迁位**——`design/qa/iter27-实现报告-2026-10-14.md`、`iter28-实现报告-2026-10-15.md` 两份现行报告仍在 `design/qa/`,且 active.md 第 32 行待追认指针指向该路径;新口径要求实现批产出物归 `reports/`。本批未搬(搬动会改动两份历史报告的落位与引用面,属文档动作,不属闸改造)。 + +**归属**:①②待用户追认(本批已生效);③归下一次文档批 + +**状态**:待办——①②待追认,③待迁位(迁时同批改 active.md:32 指针并按死链检查复验) + #### 文档/结构闸存量欠账(2026-10-08 迭代 19 总完成批登记) 新重闸口径首次全闸套跑翻出;经 T0 基点 9160942 复测确认为存量/外部批旧债,非本迭代新增。 diff --git a/reports/README.md b/reports/README.md new file mode 100644 index 00000000..78db298d --- /dev/null +++ b/reports/README.md @@ -0,0 +1,24 @@ +# reports/ — 迭代批的产出物存放处 + +迭代(代码实现)批**禁写 `design/` 整个目录**——那里是设计权威文档与规范,对实现批只读。 +实现批自己要往外交代的东西,一律落在本目录。 + +## 放什么 + +| 文件 | 内容 | 谁写 | +|---|---|---| +| `iter-实现报告-<日期>.md` | 各任务卡状态、实机目检总清单、遗留读数义务 | 迭代批 | +| `iter-代拍板-<日期>.md` | 批内 agent 替你拍的决定清单,等你追认或推翻 | 迭代批 | +| `iter-反哺点-<日期>.md` | 需要写进 `design/` 权威文档的点,逐条列清"哪个文件哪一节、改成什么、依据是什么" | 迭代批 | + +## 不放什么 + +- **不放设计结论。** 本目录的文件是"待处理的申请单",不是事实源。反哺点由你在会话中讨论定稿后, + 另开文档批写进 `design/`,写完即可删除本目录对应条目。 +- **不放图。** 图像白名单不含本目录,放了会被熵增闸判红。 +- **不放进度状态。** 会话状态的唯一来源是根目录 `active.md`。 + +## 生命周期 + +一个迭代号一组文件,落档即死:反哺点被 `design/` 吸收后,对应文件删除,历史进 git commit log +(与"历史决策记录只进 git log、不设决策文档"这条红线同口径)。 diff --git a/tools/render_gate.sh b/tools/render_gate.sh index dcba45d9..8dc43970 100644 --- a/tools/render_gate.sh +++ b/tools/render_gate.sh @@ -14,7 +14,8 @@ # ③ 棋盘 golden 逐像素(T2):board 整屏帧按 board_meta.json 取样坐标裁剪棋盘区 # (tools/golden_board_crop.py——禁硬编码像素)→ tools/golden_compare.py 对 # game/tests/golden/ 三张基线(容差 provisional:差 ≤16/changed ≤2%,S2 停机点口径)。 -# 素材缺位=SKIP 不判红(Q5 运行时口径;基线生成期素材缺位=L3 停机——基线禁锚回退态)。 +# 素材缺位=判红(2026-10-17 改;此前是 SKIP 不判红——但缺盘时这一段根本不对比,闸末仍打 +# 「全绿」exit 0,等于空跑判绿。基线生成期素材缺位=L3 停机,运行期同样无权出验收结论)。 # ④ 基准档 1280 单档(Q4)。1920 档注记:16:9 全屏下 stretch=canvas_items/aspect=keep 等比 # 1.35×+左右黑边各 96px,构图与基准档一致(迭代 9 分辨率案甲)——分辨率维度不重复建闸。 # (原由 ui_layout_probe 两档断言承担,探针已随 2026-10-15 裁「无使用的探针删除」退役, @@ -112,7 +113,9 @@ if [ ! -d "$REPO/game/tests/golden" ]; then elif [ ! -s "$BD/board_meta.json" ]; then echo "[render_gate] SKIP — board_meta.json 缺失(取证段已红),golden 对比不判" else - # Q5 运行时素材缺位=SKIP:基线锚在素材齐盘态,缺盘态对比必误红——SKIP 不判红,注记在案 + # 素材缺位=判红(2026-10-17 由 SKIP 改):缺盘态下这一段一个像素都不比,而闸末照样打印 + # 「全绿」退出码 0——那是空跑判绿,比误红危险得多(2026-10-16 菜单废基线事故正是靠它蒙过 + # worktree 自检的)。基线锚在素材齐盘态(Q5),缺盘态本就无权出验收结论。 _asset_miss=0 for a in assets/fonts/ui_font_fusion_pixel_zh_hans.ttf \ assets/art/board/flag_seal.png \ @@ -120,7 +123,7 @@ else assets/art/board/terrain_wall_n_dungeon.png assets/art/board/terrain_floor_dungeon_1.png \ assets/art/board/terrain_wall_n_underdark.png assets/art/board/terrain_floor_underdark_1.png \ assets/art/board/terrain_wall_n_deep_underdark.png assets/art/board/terrain_floor_deep_underdark_1.png; do - [ -f "$GAME/$a" ] || { echo "[render_gate] SKIP — 棋盘素材缺位: $a(Q5——基线禁锚回退态,运行时 SKIP)"; _asset_miss=1; break; } + [ -f "$GAME/$a" ] || { echo "[render_gate] FAIL — 棋盘素材缺位: $a(Q5:基线锚素材齐盘态,缺盘不得出验收结论)"; _asset_miss=1; } done if [ "$_asset_miss" -eq 0 ]; then CROP="$TMP/crop" @@ -143,7 +146,7 @@ if [ ! -s "$REPO/game/tests/golden/menu_boot.png" ]; then elif [ ! -s "$SHOT/shot_menu.png" ]; then echo "[render_gate] SKIP — shot_menu.png 缺失(shot_probe 段已红),菜单 golden 不判" elif [ ! -f "$GAME/assets/fonts/ui_font_fusion_pixel_zh_hans.ttf" ] || [ ! -f "$GAME/assets/art/ui/button_stone.png" ]; then - echo "[render_gate] SKIP — 菜单素材缺位(字体/按钮石纹——Q5 口径:基线禁锚回退态,运行时 SKIP)" + echo "[render_gate] FAIL — 菜单素材缺位(字体/按钮石纹):基线锚素材齐盘态,缺盘不得出验收结论(2026-10-17 由 SKIP 改判红)"; _rc=1 else MENUG="$TMP/menugolden" mkdir -p "$MENUG" diff --git a/tools/repo_structure_audit.py b/tools/repo_structure_audit.py index 3bbfee6b..89366a11 100644 --- a/tools/repo_structure_audit.py +++ b/tools/repo_structure_audit.py @@ -21,7 +21,9 @@ from pathlib import Path ROOT = Path(__file__).resolve().parent.parent # 1) 顶层目录白名单(点开头目录一律忽略) -TOP_DIRS = {"archive", "artwork", "design", "game", "sim-data", "tools"} +# reports/ 收编(2026-10-17 用户裁「迭代批禁写 design/」):迭代实现报告/代拍板追认单/GDD 反哺点 +# 清单的家——实现批只准往这里写,design/ 全目录对它只读。只放 .md,不放图(图像白名单不含此目录)。 +TOP_DIRS = {"archive", "artwork", "design", "game", "sim-data", "tools", "reports"} # 2) 根目录文件白名单 ROOT_FILES = {"CLAUDE.md", "active.md", "roadmap.md", "stage.txt", "review-mode.txt", ".gitignore", ".zcodeignore"} diff --git a/tools/run_all_gates.sh b/tools/run_all_gates.sh index f1d41170..20633c4c 100644 --- a/tools/run_all_gates.sh +++ b/tools/run_all_gates.sh @@ -6,16 +6,25 @@ # 跑完写回执 game/tests/.gates/receipt.json(commit + 各闸退出码 + 存量基线比对 + 时间戳); # .zcode/hooks/worktree-guard.sh 在主工作区 git merge 时校验本回执,缺或红即拦截。 # -# 用法:tools/run_all_gates.sh [--quick] -# --quick 跳过渲染闸(窗口化,需开显示器)与静态扫描(纯文本扫描),供迭代中段自检; -# 收口出库禁用,必须不带本参数跑全量。 +# 用法:tools/run_all_gates.sh [--code|--docs|--quick] +# --code 只跑代码域闸(01 冒烟/02 单元/03 探针 --click/04 探针 --full/05 渲染闸/09 静态扫描 +# /11 回放确定性对拍+10 熵增闸)——本批改动只碰 game/、tools/、artwork/ 时用本档。 +# --docs 只跑文档域闸(06 gdd_lint/07 doc_link/08 doc_term/08b active.md 体量+10 熵增闸) +# ——本批改动只碰 design/、active.md、roadmap.md、CLAUDE.md 时用本档。 +# --quick 全量档的中段自检形态:跳过渲染闸(窗口化,需开显示器)与静态扫描;完成出库禁用。 +# 不带参数 = 全量档(两域全跑)。改闸件本身、或改动同时跨两域时,按 process-conventions §6.2 +# 共享件例外跑全量。 +# **域划分只在本脚本里定义一处**——文档只写「按改动域跑本脚本」,禁另列闸清单(清单散落多处 +# 正是 iter27 漏跑 probe_gate --full 的成因)。 # # 判定口径:每道闸与 game/tests/.gates/baseline.json 里的存量基线比对—— # 基线绿 + 现绿 → 绿 # 基线红 + 现红且清单未变 → 「存量红·对基线零新增」,不判失败(但会在回执里留名) # 基线红 + 现红但清单新增 → 红(新增红项) # 任意闸 exit 与基线不符 → 红 +# 本次未跑的闸(--quick 跳过/条件闸件未触面)→ 不比闸况,只记进回执 not_run # 基线文件首次自动生成(把当次结果当基线),此后每次跑批与之比。 +# 分域跑批只比本域跑过的闸——域外闸不入 NAMES,不产生「基线绿 → 现红」的假变化。 # # 退出码:0=全绿或仅存量红零新增;1=有新增红/闸况变化;2=用法错 set -u @@ -25,8 +34,21 @@ STAMP_DIR="$GAME/tests/.gates" RECEIPT="$STAMP_DIR/receipt.json" BASELINE="$STAMP_DIR/baseline.json" QUICK=0 -[ "${1:-}" = "--quick" ] && QUICK=1 -[ $# -gt 1 ] && { echo "用法: tools/run_all_gates.sh [--quick]"; exit 2; } +DOMAIN="full" +for _a in "$@"; do + case "$_a" in + --quick) QUICK=1 ;; + --code|--docs) + [ "$DOMAIN" = "full" ] || { echo "用法: --code 与 --docs 互斥"; exit 2; } + DOMAIN="${_a#--}" ;; + *) echo "用法: tools/run_all_gates.sh [--code|--docs|--quick]"; exit 2 ;; + esac +done + +# 控制台编码:本仓库闸件输出中文与 ✅/✗ 符号。Windows GBK 控制台下 python 打印这些字符会抛 +# UnicodeEncodeError——06/10 两闸直接崩(假红)、09 的 FAIL 行被写成乱码字节(指纹漂移, +# 与基线比必判「红项清单变化」假红)、末尾汇总自身崩掉打不出结论。2026-10-16 实踩。 +export PYTHONIOENCODING=utf-8 # 回执与日志是中间产物(不入库),但 .gitignore 本身必须入库——漏了就等于把它们纳入版本管理。 # 每次跑批前自愈重建,避免「清目录重跑」把 .gitignore 一起带走(2026-10-15 实踩)。 @@ -53,10 +75,45 @@ case "$PY" in */Scripts/python.exe|*/Scripts/python) export PATH="$(dirname "$PY"):$PATH" ;; esac +# ── 域判定:这道闸属哪个域、本次跑不跑 ── +in_domain() { # in_domain + local dom="$1" + [ "$dom" = "both" ] && return 0 + [ "$DOMAIN" = "full" ] && return 0 + [ "$DOMAIN" = "$dom" ] && return 0 + return 1 +} + +# ── 素材齐盘预检(2026-10-17 加)── +# game/assets/art/** 与一切 *.import 边车都不入 git(.gitignore 2026-09-29 裁定「素材不入 git」), +# 所以 git worktree 检出的工程天生缺全部美术素材:main_theme.tres 解析失败 → 按钮五个样式盒全空 +# → 冒烟 V1 成片假红;渲染闸又会因素材缺位把两段 golden 直接跳过(见 render_gate.sh)。 +# 缺位即拒跑——禁带着空盘跑完,把「什么都没比」的回执当全绿合回。 +if in_domain code; then + _miss="" + for a in assets/art/ui/button_stone.png assets/fonts/ui_font_fusion_pixel_zh_hans.ttf \ + assets/art/board/flag_seal.png assets/art/character/character_hero_idle.png \ + assets/art/board/terrain_wall_n_dungeon.png assets/art/board/terrain_floor_dungeon_1.png \ + assets/art/board/terrain_wall_n_underdark.png assets/art/board/terrain_floor_underdark_1.png \ + assets/art/board/terrain_wall_n_deep_underdark.png assets/art/board/terrain_floor_deep_underdark_1.png; do + [ -f "$GAME/$a" ] || _miss="$_miss $a" + [ -f "$GAME/$a.import" ] || _miss="$_miss $a.import" + done + [ -f "$GAME/.godot/uid_cache.bin" ] || _miss="$_miss .godot/uid_cache.bin(导入未刷)" + if [ -n "$_miss" ]; then + echo "[gates] 素材齐盘预检 FAIL — 本工作区缺:$_miss" + echo " 处置:cp -r <主工作区>/game/assets/art/. <本区>/game/assets/art/" + echo " 再刷导入:$GODOT --headless --path <本区>/game --editor --quit" + echo " (--import 不生成新素材的 .import 边车,必须走 --editor --quit)" + exit 1 + fi + echo "[gates] 素材齐盘预检 PASS(10 件素材+边车+导入缓存在位)" +fi + declare -a NAMES=() EXITS=() LOGS=() FAILED=0 -echo "重闸全量跑批 · 仓库 $REPO" +echo "重闸跑批 · 域=$DOMAIN · 仓库 $REPO" echo "起跑 commit: $(git -C "$REPO" rev-parse --short HEAD 2>/dev/null || echo '非git目录')" echo "python: $PY" echo "godot: $GODOT" @@ -98,6 +155,8 @@ gate_unit() { # run_all 是 --script 模式,退出码为唯一判据 return 0 } +# ══ 代码域(1–5)══ +if in_domain code; then # ── 1. 系统级断言面(冒烟三闸:退出码+摘要失败数+SCRIPT ERROR 扫描,判定无洞)── gate "01 冒烟全量" "smoke.log" "$PY" "$REPO/tools/run_smoke.py" # ── 2. 单元套件 ── @@ -113,13 +172,18 @@ else NAMES+=("05 渲染闸"); EXITS+=("SKIP"); LOGS+=("") echo ""; echo "[gates] 05 渲染闸 → SKIP(--quick)" fi +fi +# ══ 文档域(6–8b)══ +if in_domain docs; then # ── 6. 文档三闸 ── gate "06 gdd_lint" "gdd_lint.log" "$PY" "$REPO/tools/gdd_lint.py" 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 # ── 6b. 状态文件体量闸(active.md 只记现在:条目数/快照行数/编号/死指针四条硬上限)── gate "08b active.md 体量" "active_state.log" "$PY" "$REPO/tools/audit_active_state.py" -# ── 6c. 回放确定性对拍(条件闸件——迭代 29 T3 收编)── +fi +# ── 6c. 回放确定性对拍(条件闸件——迭代 29 T3 收编;属代码域)── +if in_domain code; then # 触面判定:本批 diff 相对 master 分叉点,命中下列路径面之一即「触面」。 # 触面也**不自动跑**——对拍调的是 MC 脚本,属「MC/统计类跑批发起前须用户批准」红线管辖; # 闸只负责每次跑批都把触面判定做一遍并显式要求批准,杜绝「该跑却没人记得」。 @@ -157,23 +221,29 @@ else echo "[gates] $_parity_name → 本批 diff 触及数值/存档/RNG/结算面,对拍需用户批准发起" echo " (批准后设 PARITY_APPROVED=1 重跑本脚本即实跑;不得借机重锚)" fi +fi -# ── 7. 静态扫描域 + 熵增闸 ── +# ── 7. 静态扫描(代码域)+ 熵增闸(两域都跑:它查顶层目录/根文件/图像白名单/退役路径, +# 文档批新增或挪动 design/ 下的文件同样会绊它;纯文本扫描,秒级)── if [ $QUICK -eq 0 ]; then - gate "09 静态扫描" "static_scan.log" bash "$GAME/tests/static_scan.sh" + if in_domain code; then + gate "09 静态扫描" "static_scan.log" bash "$GAME/tests/static_scan.sh" + fi gate "10 熵增闸" "entropy.log" "$PY" "$REPO/tools/repo_structure_audit.py" else - NAMES+=("09 静态扫描"); EXITS+=("SKIP"); LOGS+=("") - NAMES+=("10 熵增闸"); EXITS+=("SKIP"); LOGS+=("") - echo ""; echo "[gates] 09 静态扫描 / 10 熵增闸 → SKIP(--quick)" + if in_domain code; then + NAMES+=("09 静态扫描"); EXITS+=("SKIP"); LOGS+=("") + fi + NAMES+=("10 熵增闸"); EXITS+=("SKIP"); LOGS+=("") + echo ""; echo "[gates] 09 静态扫描(代码域)/ 10 熵增闸 → SKIP(--quick)" fi # ── 与存量基线比对 ── -"$PY" - "$BASELINE" "$RECEIPT" "$QUICK" "$REPO" "${NAMES[@]}" -- "${EXITS[@]}" -- "${LOGS[@]}" <<'PYEOF' +"$PY" - "$BASELINE" "$RECEIPT" "$QUICK" "$DOMAIN" "$REPO" "${NAMES[@]}" -- "${EXITS[@]}" -- "${LOGS[@]}" <<'PYEOF' import hashlib, json, os, subprocess, sys -baseline_path, receipt_path, quick, repo = sys.argv[1:5] -rest = sys.argv[5:] +baseline_path, receipt_path, quick, domain, repo = sys.argv[1:6] +rest = sys.argv[6:] sep = rest.index("--") names = rest[:sep] tail = rest[sep + 1:] @@ -227,7 +297,7 @@ else: with open(baseline_path, encoding="utf-8") as f: base = json.load(f).get("gates", {}) -changed, inherited_red = [], [] +changed, inherited_red, not_run = [], [], [] for n in names: b, c = base.get(n), now[n] if b is None: @@ -235,6 +305,11 @@ for n in names: continue if b["exit"] == c["exit"] and b["fp"] == c["fp"]: continue + if c["fp"] == "skip": + # 本闸这次没跑(--quick 跳过/条件闸件未触面)——没有结果可比,不判闸况变化。 + # 2026-10-17 修:此前 SKIP 落进「基线绿 → 现红」分支,任何 --quick 回执都自带假红。 + not_run.append(n) + continue if b["exit"] != "0" and c["exit"] != "0" and b["fp"] == c["fp"]: continue # 同款存量红,清单未变 if b["exit"] == "0" and c["exit"] != "0": @@ -253,10 +328,12 @@ with open(receipt_path, "w", encoding="utf-8") as f: "commit_short": head()[:8], "timestamp": subprocess.run(["date", "-Iseconds"], capture_output=True, text=True).stdout.strip(), "quick_mode": quick == "1", + "domain": domain, "first_baseline_run": first_run, "gates": [{"name": n, "exit": e, "fingerprint": now[n]["fp"]} for n, e in zip(names, exits)], "changed_vs_baseline": changed, "inherited_red": inherited_red, + "not_run": not_run, "all_green": all_green, }, f, ensure_ascii=False, indent=1) @@ -273,6 +350,8 @@ if inherited_red: print("【改善】") for c in inherited_red: print(" ↓ " + c) +if not_run: + print("【本次未跑(域外/--quick/未触面)】" + "、".join(not_run)) stock = [n for n, e in zip(names, exits) if e not in ("0", "SKIP")] if stock: print("【存量红·对基线零新增】" + "、".join(stock))