Files
gplan-2/design/dev/code-conventions.md
T
DKChan 18e22f1b44 docs(规约): 规则文件审计与目录划分批——150行上限+领域拆分+日期注记清零(2026-10-15 用户令)
- CLAUDE.md 137→140:日期注记全清;文件头红线收敛为指针(全表唯一权威=code-conventions §1.4.1);测试两入口/闸机轮次/MC 档位超长红线拆子条目(MC 精度数字收敛为 sim-data/README 档位表指针);规范导航补 test-conventions/concept-conventions 两行
- code-conventions 210→148:§3 测试纪律拆出独立成文;缺陷对照表与版本历史表退役(历史归 git);反面案例去日期化;与 §1.5 重复的流程条目合并
- doc-conventions 210→149:概念封装/引用纪律/归档三节拆出 concept-conventions.md(原 §7–9 合并桩节标注);§10/§11 保号——active.md 与 tools 脚本存量引用零断链;数值纪律强制收口条款移出表格单元格
- 新增 test-conventions.md(49 行):探针标配/回归断言/提交前全量验证/验收清单落码规范
- 新增 concept-conventions.md(54 行):概念封装建模/跨文档引用纪律/归档与事实源
- 机械闸:五文件行数≤150 ✓ IMPORTANT=0 ✓ 日期残留=0 ✓ 熵增闸 PASS ✓ doc 三闸(gdd_lint/doc_link_audit/doc_term_audit)对改动文件零波及(存量 GDD 基线失败与本批无关)
- 行为验证:全新上下文只读子代理五问导航 5/5 正确(含两份新文档命中与 §10/§11 交叉印证)
2026-10-15 08:07:09 +08:00

149 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 代码规范(Dungeon Sweeper)
> 约束范围:`game/` 代码同尺度;与 CLAUDE.md「硬性红线」节硬约束(数值表单点 / RNG 封闭流 / Core 零 UI / fail-fast / spec-AC 一致性)并行生效,冲突以用户裁决为准。
> 分工:本文管结构 / 契约 / 语言 / 数值 / 流程五类;测试的编写与验收纪律见 `design/dev/test-conventions.md`。
**写任何代码前必须通读本文件。**
---
## 1. 结构纪律——高内聚、低耦合
### 1.1 禁止上帝对象
一个类只承载一个职责。场景主脚本(main)= **组合根**:模式流转、信号接线、核心写调用的唯一出口;
渲染、文案拼装、状态装配一律拆分为独立视图/装配器类。单文件超过 ~400 行即触发审视。
> 反例:main.gd 曾长到 ~1100 行——棋盘渲染、HUD、结算日志格式化、四种模态覆盖层、调整板、
> 模拟器、关卡流程全部内联;后拆分为 9 个单一职责单元(views/ + hud_presenter),main 回到 ~430 行组合根。
### 1.2 一切功能以类封装,禁止裸函数堆
设计稿与实现同尺度:功能 = 类(状态 + 行为 + 信号),类是封装的最小交付单位。
工具性纯函数只允许挂在明确的宿主类下(静态装配器/工厂),不允许无主散落。
> 反例:供给控制器曾以裸函数伪代码呈现被否;正式实现按私有组合件 RewardSupplyController 封装。
### 1.3 依赖单向,核心写调用收口
依赖方向唯一:`views →(信号)→ 组合根 →(写调用)→ core`。
视图零核心写调用(纯 setter / 信号外发优先);"读核心状态 → 拼展示文案"集中于装配器(presenter);
核心写接口(settle_batch / submit_window_selection / runner 推进)只允许在组合根出现。
### 1.4 命名 = 领域命名,禁止系统 ID 前缀
文件名与类名禁止 `a1_`/`b1_`/`c1_` 等系统 ID 前缀(`a1_board_generator.gd` → `board_generator.gd`)。
设计追溯不靠前缀:每个核心实现文件头注必须带一行
`# 实现 GDD:design/gdd/systems/<循环>/<系统文档>.md(系统 ID Xn)`;系统 ID 只作为文档与 registry 的追溯锚。
豁免:D2 schema 类与 autoload(本就领域命名);文档/夹具登记名(如 b4_golden)属文档层。
> 反例:ID 前缀是跟随 GDD 文档名的事实约定,从未成文;`config_db`/`rng_service` 无前缀与 A/B/C 系列
> 并存——两套命名并行即规范缺失。
### 1.4.1 文件头 = `##` 文档注释,≤8 行,白名单制
文件头注释只用 `##` 文档注释(Godot 官方载体,进编辑器 F1 帮助),总行数 ≤8 行(不含空行,
从文件首个非空行到首个非注释代码行)。写法权威 = Google Python Style Guide §3.8 + PEP 257
(GDScript 官方风格指南对文件头零条款,GDScript 语法近 Python,沿用其规范——不自造标准)。
白名单(只许写这五类,超出一类即违规):
1. 一行职责摘要——这是什么;
2. 用途/验证问题一行——它为什么存在;
3. GDD 锚行(§1.4 格式);
4. 硬使用约束——使用本文件前必须知道的禁令,每条一行;
5. 指路行——架构文档/ADR/契约文档的位置。
黑名单(出现即违规):
- 日期与变更叙事:任何日期、「XX批/T几/迭代N 做了什么」、迁移/改名/退役经过——
改动史查 `git log`/`git blame`,变更背景读对应迭代实现报告;
- 与 code-conventions、ADR、架构文档重复的段落——规范与架构各有唯一权威文档,文件头只指路不复制;
- 场景结构、生命周期等代码机制描述——"Never describe the code"(pyguide §3.8.5),读者读代码或架构文档。
机器闸:`tools/audit_file_headers.py`(static_scan S10:行数上限 + 黑名单机械可判子集;
信号契约成文位置见 §2.1)。存量头注按白名单清偿后挂入收口闸序列;此间新增/改动的文件头必须即合规。
> 反例:main.gd 头注曾累积 42 行——历次提交各自追加「本批做了什么」而全部闸静默放行;删掉叙事后
> 仍剩 31 行,约 2/3 是对 code-conventions §1.3 与 ADR-0001 的整段复制、场景结构/生命周期机制描述
> (对规约与代码零新信息)。全仓头注按白名单重写,机制事实归 design/dev/architecture。
### 1.5 禁止事实约定
任何"大家默认这么做"的惯例必须成文进本文件(或 CLAUDE.md)才算存在。
写代码时发现未成文但必须遵守的约定 → 当场补条文,不许靠惯性延续。
---
## 2. 契约纪律——跨层/跨类调用的语义
### 2.1 信号与事件契约成文
每个跨类信号:参数语义、发送时点、消费方依赖,必须在发送方 `##` 文档注释成文,每信号一行
(参照 A2 三事件面先例);计入 §1.4.1 的 8 行上限,塞不下时迁契约文档并在文件头留指路行。
状态机的每次迁移路径都必须发出对应事件——**缺一条路径的事件 = 消费方永久等待 = 软锁**。
> 反例:旗动作花完末点 AP 后相位翻 Waiting 但不发 round_end,结算点依赖该事件 → 整局软锁;
> 自动玩家不用旗,冒烟路径覆盖不全没抓住。
### 2.2 跨层标识符语义单一且成文
位置(index)/ 句柄(handle)/ 键(key)是三种不同的东西。跨类传递标识符时,
传递方的语义必须与接收方契约逐字一致,禁止凭"当前恰好相等"传值。
> 反例:窗口选择契约 = 窗口条目位置,UI 却传库存数组句柄——未消耗时二者巧合相等;
> 消耗一件后错位 → 选中件不消耗、邻件被误耗。
### 2.3 模态覆盖层层级规则
覆盖层遮挡关系由场景树加入顺序决定。打开任何模态覆盖层时,必须显式隐藏会遮挡它的全屏层;
新增覆盖层必须登记其在 z 序中的位置,并提供"打开可见性"的场景断言。
> 反例:数值调整板已实现,但打开时被不透明的开始覆盖层完全遮住 = "点击无反应"。功能存在 ≠ 功能可达。
### 2.4 状态字段语义显式
跨战斗/跨场景携带或恢复的状态字段,其语义来源必须显式(表值 / 明确入参),禁止用入参隐式定义。
> 反例:spawn 把 hero 的 max_hp 设为入参 hero_hp——跨战斗携带 HP 时上限被压扁,
> 恢复点/治疗包的满血守卫全部失效。
---
## 3. 测试纪律
已独立成文:`design/dev/test-conventions.md`(探针标配 / 回归断言 / 提交前全量验证 / 验收清单落码规范)。
---
## 4. 语言纪律——GDScript 4.x 实战坑(本项目踩实)
1. **多行 lambda 内禁止嵌套 match / 复杂控制流**(解析不可靠,报"Expected expression for match
pattern")——信号处理体一律抽成命名方法 + `bind` 传状态。
2. **Dictionary 取值返回 Variant,无类型推断**——参与算术或传参前显式声明类型(`var x: float = ...`)。
3. **RichTextLabel:`append_text()` 不回写 `text` 属性**——读可见文本用 `get_parsed_text()`。
4. **lambda 捕获循环变量按创建时值**——循环内先落地局部变量再捕获,或用 `bind`。
5. **重命名脚本必须同步 `.uid` 文件**(资源身份稳定);新增 `class_name` 后必须跑一次编辑器扫描
刷新全局类缓存,否则 `--headless --script` 模式解析失败。
6. **`print` 格式占位符与实参类型必须匹配**(`%d` 配 String = 静默输出字面量)——工具输出错
等于没有输出。
---
## 5. 数值与汇报纪律
### 5.1 数值口径必须显式标注尺度
任何期望值/均值汇报必须写明口径:**每格 / 每板 / 每战 / 每关**。混用口径 = 误导
("可选类 3.32"不加"每板",会被读成每格期望 >1)。
### 5.2 调参先测量,改后复测
改任何玩法数值前,先用扫描工具产出基线(通关率/难度类读数走 `balance_verifier.py` 冒险段,
机制类读数走 `reward_expectation.gd` 等);改完必须复测回填数据。禁止拍脑袋调参。
### 5.3 数值设计必须跨尺度推演
在单一尺度校准的数值,必须推演其在更大尺度的复合结果(每战合理 × 关内囤积 = 关卡灾难)。
> 反例:可选类 25% 格子配比在单战斗尺度校准(击杀需求 18/54),×"库存关内延续"× 5 张板复合后
> = 每关 24.8 件、刀总伤 180 > Boss 130 HP——单尺度合理的参数在关卡尺度坐实灾难。
### 5.4 用户拍板的实现零发挥
拍板范围外的细节一律不自行补全——以"待办问题"清单列出等裁决。
(反面案例:清图奖励被自行设计成入库刀,用户纠正为"一次性高额伤害不入库"。)
---
## 6. 流程纪律
1. **大改动三段走**:设计稿/伪代码 → 用户审阅 → 实现;实现不得先于审阅。修改记录落切片 README。
2. **实现批收尾双登记**:切片 README 修改记录 + 根目录 `active.md` 条目(会话状态唯一状态源),缺一不可。
3. **呈现层缺陷的教训回写 D1 GDD 设计输入**(信号载荷 schema / 场景级测试 / 时点状态可见性)。