Files
gplan-2/design/engineering/code-conventions.md
T
DKChan abfd70549b 文档(架构审查P1余项·裁决落文): 第四节四条定案进规约与审计报告标注——①code-conventions §4 新增第13条按来源分档(S系扫描面一律止于产品码三目录;tests/probe/sim 各由运行期通道承接:闸00/01/02、闸03/04/05〔含同目录 type_glow_shot_probe 人工取证件与 probe_ui_util 共用面两件不入闸〕、不入CI的手动调参批量;三目录允许自建 RandomNumberGenerator 条件=必须显式定种,禁的是不定种形态;该禁止无机器闸、由文字纪律+独立验收审计人工评审承载;并废止「测量面」一词对本文件统称用途,保留 run_all_gates 选闸原义)②architecture S 表补齐登记面(标题 S1–S8→S1–S13、S11/S12/S13 三行入表、S1/S2 加适用域注、S3 加维持warning裁定、S9/S10 缺空面自检查登记为已知偏差)、版本1.31→1.32 ③test-conventions §8.3 新增「新增core件的覆盖登记义务」(点名或申报经宿主链覆盖,明示非覆盖率要求;立每core件点名闸与补11件空套件两案裁否,凭 test-conventions §7「覆盖率不是目标」)+process-conventions §3 第6条加承接指向 ④审计报告四处标注:4.1不扩面裁定与读数订正(8处→7处,experiment_density 随迭代50 T9 退役;randomize() 两处命中均在注释、实际调用零命中)、4.2三面不扩(三层存量实跑≈2162处:S9 1070/S10 117/S13 975,其中禁调试打印274与循环内await70属工具码功能需要;产品码面归零不可换存量红)+S8现14文件非9+纯目录域方案证伪(四件结算码在core根下)+待执行清单三项归工程改动批零卡片、4.3结案(三处豁免2026-10-31迭代44已清偿,基线现5键)、4.4定性改判为「无按文件登记惯例」+读数4件→11件(后一档三件全是迭代44/46/49拆件产物=复发模式)。待办清单与roadmap按用户裁不动(整份审计未闭合),报告顶部加禁退役注记防收尾误删
2026-11-03 19:52:03 +08:00

11 KiB
Raw Blame History

代码规范(Dungeon Sweeper)

约束范围:game/ 代码同尺度;与 AGENTS.md「硬性红线」节硬约束(数值表单点 / RNG 封闭流 / Core 零 UI / fail-fast / spec-AC 一致性)并行生效,冲突以用户裁决为准。 分工:本文管结构 / 契约 / 语言 / 数值 / 流程五类;测试的编写与验收见 design/engineering/test-conventions.md;真实反例库见同目录 code-conventions-cases.md(不常驻加载,对规则适用有疑义时查)。

定代码方案前必须通读本文件。


1. 结构纪律——高内聚、低耦合

1.1 禁止上帝对象

一个类只承载一个职责。场景主脚本(main)= 组合根:模式流转、信号接线、核心写调用的唯一出口;渲染、文案拼装、状态装配一律拆分为独立视图/装配器类。单文件超过 ~400 行即触发审视。

拆分判据(硬纪律):拆分/收敛/重构类需求卡片与实现段一律写明并遵守——①拆分边界沿职责轴、不沿阈值轴:每个拆出单元(私有函数族/新件/段件/状态件/参数束)须有可陈述的职责语句与单一变化原因;②拆出单元须有集体身份:成员共同表达一个可命名的领域概念,命不出名字、只能说「剩下的成员」的打包=凑计数袋,禁;③「过阈值」只许作为发现信号,作为拆分或收拢的动机即违规;④按上述纪律拆不出内聚单元 → 停卡呈报走豁免/还债通道,禁硬拆凑绿;⑤禁止为迎合机检消息文本(含计数)而保留或新增无效成员/别名;机检键与卡片契约互斥时,实现段一律停卡呈报,不得自造第三条路。①–④=拆分边界纪律(2026-10-28 用户裁:任何拆分满足领域驱动设计〔DDD〕的职责切分与类的高内聚)。

1.2 一切功能以类封装,禁止裸函数堆

设计稿与实现同尺度:功能 = 类(状态 + 行为 + 信号),类是封装的最小交付单位。工具性纯函数只允许挂在明确的宿主类下(静态装配器/工厂),不允许无主散落。

1.3 依赖单向,核心写调用完成

依赖方向唯一:views →(信号)→ 组合根 →(写调用)→ core。视图零核心写调用(纯 setter / 信号外发优先);"读核心状态 → 拼展示文案"集中于装配器(presenter);核心写接口(settle_batch / player_request_refresh / runner 推进)只允许在组合根出现——组合根=main.gd+其构造注入的组合根拆件(成员与边界见架构文档「模块归属·呈现层」),视图不得直连拆件。

1.4 命名 = 领域命名,禁止系统 ID 前缀

文件名与类名禁止 a1_/b1_/c1_ 等系统 ID 前缀(a1_board_generator.gd → board_generator.gd)。设计追溯不靠前缀:代码↔设计的追溯由 GDD 文档承担,系统 ID 只作为文档的追溯锚;文件头注不承担追溯职责,不写任何文档链接(§1.4.1)。豁免:D2 schema 类与 autoload;文档/夹具登记名(如 b4_golden)属文档层。

1.4.1 文件头 = ## 文档注释,介绍本模块

头注以 Python 规范为权威(GDScript 语法近 Python,官方风格指南对文件头零条款,不自造标准):Google Python Style Guide §3.8.2——文件应以描述模块内容与用法的 docstring 开头;PEP 257——模块一般都应有 docstring,摘要压一行。载体只用 ## 文档注释(Godot 官方载体,进编辑器 F1 帮助),头注以说清模块为度,长度不设硬限。

黑名单(出现即违规):日期与变更叙事——改动史查 git log/git blame,变更背景读迭代实现报告;与其他文档或其他文件头重复的内容——零新信息不写;一切文档反向链接——「实现 GDD:xxx」「指路:架构见 xxx」一类全部废止,代码↔设计追溯由 GDD 文档承担。

机器闸:tools/audit_file_headers.py(static_scan S10:变更叙事黑名单机械可判子集)。行数上限与类别白名单已停用。

1.5 禁止事实约定

任何"大家默认这么做"的惯例必须成文进本文件(或 AGENTS.md)才算存在。写代码时发现未成文但必须遵守的约定 → 当场补条文,不许靠惯性延续。


2. 契约纪律——跨层/跨类调用的语义

2.1 信号与事件契约成文

每个跨类信号:参数语义、发送时点、消费方依赖,必须在发送方 ## 文档注释成文,每信号一行;装不下时迁契约文档。状态机的每次迁移路径都必须发出对应事件——缺一条路径的事件 = 消费方永久等待 = 软锁。

2.2 跨层标识符语义单一且成文

位置(index)/ 句柄(handle)/ 键(key)是三种不同的东西。跨类传递标识符时,传递方的语义必须与接收方契约逐字一致,禁止凭"当前恰好相等"传值。

2.3 模态覆盖层层级规则

覆盖层遮挡关系由场景树加入顺序决定。打开任何模态覆盖层时,必须显式隐藏会遮挡它的全屏层;新增覆盖层必须登记其在 z 序中的位置,并提供"打开可见性"的场景断言。

2.4 状态字段语义显式

跨战斗/跨场景携带或恢复的状态字段,其语义来源必须显式(表值 / 明确入参),禁止用入参隐式定义。


3. 测试纪律

已独立成文:design/engineering/test-conventions.md(探针标配 / 回归断言 / 提交前验证 / 验收清单落码规范)。


4. 语言纪律——GDScript 4.x 实战坑(本项目踩实)

  1. 多行 lambda 内禁止嵌套 match / 复杂控制流(解析不可靠)——信号处理体一律抽成命名方法 + 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 = 静默输出字面量)。
  7. 语言纪律已机器化为 S9 类型闸——tools/audit_type_hints.py,执行载体=静态扫描域 S9(game/tests/static_scan.sh),退出码 0=归零。扫描面=产品码三目录(game/core+game/data+game/presentation);非产品码三目录的适用域分档见本节第 13 条。三规则:R1 无类型容器(显式非参数化 Dictionary/Array 注解)/R2 有值返回缺返回类型(纯副作用缺 -> void 属写法面不入闸)/R3 参数缺类型(_init 豁免——构造注入面形态自由)。
  8. := {} 推断形态不算债——var x := {} 类赋值推断属类型推断合法面,不入 R1 扫描;只有显式写的非参数化容器注解才算无类型容器。
  9. 行级白名单通道 # type-hint: ignore——豁免该行不入 S9;豁免理由必须随行注明(现行例:board_generator 四函数的 db 参鸭子型——typed 参拒冒烟夹具 _BoardRowStub 注入)。
  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=闸 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),与本条分档无关;本文不再用「测量面」统称这三个目录。

5. 数值与汇报纪律

5.1 数值口径必须显式标注尺度

任何期望值/均值汇报必须写明口径:每格 / 每板 / 每战 / 每关。混用口径 = 误导。

5.2 调参先测量,改后复测

改任何玩法数值前,先用扫描工具产出基线(通关率/难度类读数走 balance_verifier.py 冒险段,机制类读数走 reward_expectation.gd 等);改完必须复测回填数据。禁止拍脑袋调参。

5.3 数值设计必须跨尺度推演

在单一尺度校准的数值,必须推演其在更大尺度的复合结果(每战合理 × 关内囤积 = 关卡灾难;反例见 code-conventions-cases.md §5.3)。

5.4 用户拍板的实现零发挥

拍板范围外的细节一律不自行补全——以"待办问题"清单列出等裁决。


6. 流程纪律

  1. 大改动三段走:设计稿/伪代码 → 用户审阅 → 实现;实现禁止先于审阅。改动明细只进 git 提交信息,README 与代码内不写修改记录(doc-conventions 第 5 节「禁写变更记录」)。
  2. 实现批完成后的登记义务:待办清单条目按 doc-conventions §12.1 推进状态,批次过程与结论进 git 提交信息与 reports/(根目录会话状态文件已废除,禁止再在其中登记条目)。
  3. 呈现层缺陷的教训回写 D1 GDD 设计输入(信号载荷 schema / 场景级测试 / 时点状态可见性)。