fix(闸收敛批二): gdd_lint 分域豁免 + doc_term 语境豁免 + 孤儿探针退役

用户令四条:清单类与术语表免 GDD 规范性检查、退役词统一处理、文件头另处解决、
无使用的探针全删。

- tools/gdd_lint.py:新增 RULE_EXEMPT(规则粒度分域豁免,**按规则不按文件**)——
  06 免 R3/R4/R7、07 免 R3/R5/R7、术语表免 R2/R5。
  理由逐条写进代码注释:R3 禁修订注记与「登记处条目要能回答谁在何时裁的」直接冲突;
  R4 版本历史节与 06「解决即移除」的状态登记体裁冲突;R5 是节名含「流程」即触发的误匹配
  (07 的「㉖全流程走查」是目检清单节、术语表「开发流程」是词条节);
  R7 长括注在登记处是常态。
  **机械可判的完整性规则 R6/R8/R9/R10/R11 一律不豁免**,真缺陷不得藏起来。
- design/gdd/术语表.md:版本号撞号订正(10-12 那条 v1.2→v1.9,两条 10-13 顺延 v1.10/v1.11),
  按真实时序重排。原撞号源于 2026-10-11 与 10-12 两批各写一个 v1.2。
- tools/doc_term_audit.py:五组退役词按语境豁免而非改正文——
  充能(秘宝主动件现行机制,扩 exempt 信号)、可选类道具与雾格(否定式引用「现行池内无可选类」
  「不做清单」——替换会毁掉句意)、伏击/heavy_multiplier(battle.md 占位表里的退役敌人 id,
  现行规格在 敌人系统.md)、拔旗(06 待办「件派生动词白名单」是待评估设计意图非在用机制)。
  疑似现行 6 处 → 0。
- 孤儿探针退役:ui_layout_probe 与 ui_audit_probe 共 6 文件(.gd/.uid/.tscn)删除——
  两者均无任何闸脚本执行(全仓仅注释与历史报告提及)。
  连带清理 render_gate.sh 头注、board_view.gd cell_size 注释、game/README.md roster 行。
  **遗留**:ui_layout_probe 原承担 1920 档布局机检面,现无承载,已在 render_gate 头注标明待补。
- roadmap.md 迭代循环第 4/5 条:轻闸/重闸改指 run_all_gates.sh,闸清单不再散落第四处。

闸况:9 道绿(含 gdd_lint / doc_link / doc_term 三闸全绿);static_scan S10 文件头 125 处
按用户令不在本批处理,已落 baseline.json 作为存量红。
This commit is contained in:
DKChan committed 2026-10-15 21:42:35 +08:00
1 parent 1d4ac91e3b
commit 94f1bba840
16 files changed
+134 -324

No files matched your search

+42 -19
View File
@@ -8,12 +8,13 @@ r"""gdd_lint.py — GDD lean 格式门禁
规则(与 .claude/docs/templates/game-design-document.md 纪律一一对应):
R1 散文段落 ≤ 240 字(代码块/表格/列表/引用不计)
R2 表格字符占比 ≥ 60%(代码块不计入分母)
R3 正文禁止修订注记:~~删除线~~、"作废"、括号内"日期+评审/修订/拍板/裁决/核销"(版本历史节豁免)
R4 必须有「版本历史」节且含表格
R5 流程/状态类章节必须含代码块图(字符画或 Mermaid)
R2 表格字符占比(词条表分域豁免,见 R2_EXEMPT 注)
R3 正文禁止修订注记:~~删除线~~、"作废"、括号内"日期+评审/修订/拍板/裁决/核销"(版本历史节豁免;
登记处/清单按 RULE_EXEMPT 分域豁免)
R4 必须有「版本历史」节且含表格(登记处按 RULE_EXEMPT 分域豁免)
R5 流程/状态类章节必须含代码块图(字符画或 Mermaid);清单节/词条节按 RULE_EXEMPT 分域豁免
R6 表格列数一致(含转义 \| 识别)
R7 括号嵌套跨度 ≤ 80 字(长括号 = 审计注记挤进正文的信号)
R7 括号嵌套跨度 ≤ 80 字(长括号 = 审计注记挤进正文的信号;登记处/清单按 RULE_EXEMPT 分域豁免)
R8 核心规则表:规则列必须是"谁·何时·做什么"陈述句所在的可读表——说明/要点列
禁止出现编号引用(AC-x/F-x/EC-x/E-x/OQ-x/Rule-x/义务),引用只允许在依据类列
R9 一页纸节点:任一标题节点的直接内容(到下一标题为止)≤ 80 行——装不下就拆子节点
@@ -50,6 +51,24 @@ R2_EXEMPT = {
"05-不做清单.md", # 清单文:条目散文为体裁
"06-待办清单.md",
"07-体验验证清单.md",
"术语表.md", # 词条表:表格为主体形态,表格占比高是体裁本身
}
# 规则粒度分域豁免(2026-10-15 用户令「清单类文件与术语表不做 GDD 规范性检查」)。
# 原则:**按规则豁免,不按文件拉黑**——体裁规则(R3/R4/R5/R7)对登记处/清单/词条表
# 不适用,但机械可判的完整性规则(R6/R8/R9/R10/R11)一律不豁免,真缺陷不得藏起来。
# 各条理由:
# R3 正文禁修订注记——登记处/清单的条目「带日期+裁决来源」是设计目的本身
# (条目要能回答「这条谁在何时裁的」),禁它等于禁登记处的核心价值。
# R4 必须有版本历史节——06 是「解决即移除」的状态登记,条目不按版本累积,
# 强塞版本历史表反而制造第二套需同步的真相。
# R5 流程/状态类节须含代码块图——节名含「流程/生命周期」即触发,但 07 的
# 「㉖…全流程走查」「㉗…目检」是目检清单节、术语表的「开发流程」是词条节,
# 两者都不是流程描述,属节名误匹配。
# R7 括号跨度 ≤80 字——长括注在登记处/清单里是常态(现状+裁决+归属挤在一行)。
RULE_EXEMPT = {
"06-待办清单.md": {"R3", "R4", "R7"},
"07-体验验证清单.md": {"R3", "R5", "R7"},
"术语表.md": {"R5"},
}
DEFAULT_DIR = "design/gdd"
@@ -187,6 +206,7 @@ def lint_file(path):
# R1 段落长度(登记处/清单文分域豁免,见 R1_EXEMPT 注)
fname = Path(path).name
ex = RULE_EXEMPT.get(fname, set()) # 规则粒度分域豁免(见 RULE_EXEMPT 注)
if fname not in R1_EXEMPT:
for p in paras:
if len(p) > MAX_PARA:
@@ -204,27 +224,29 @@ def lint_file(path):
else:
infos.append(f"表格占比 {ratio}%")
# R3 修订注记(版本历史节豁免;代码块豁免)
# R3 修订注记(版本历史节豁免;代码块豁免;登记处/清单按 RULE_EXEMPT 豁免)
body_no_code = "\n".join(
l for l in text.splitlines() if l not in code_lines and l not in version_lines
)
for m in re.finditer(r"~~.+?~~", body_no_code):
for m in (() if "R3" in ex else re.finditer(r"~~.+?~~", body_no_code)):
issues.append(f"R3 删除线残留: …{m.group(0)[:40]}…")
if re.search(r"作废", body_no_code):
if "R3" not in ex and re.search(r"作废", body_no_code):
line_no = next(
i + 1
for i, l in enumerate(body_no_code.splitlines())
if "作废" in l
)
issues.append(f"R3 \"作废\"注记残留(第 {line_no} 行附近)")
for m in DATE_KEYWORD_RE.finditer(body_no_code):
issues.append(f"R3 正文修订注记: {m.group(0)[:50]}")
if "R3" not in ex:
for m in DATE_KEYWORD_RE.finditer(body_no_code):
issues.append(f"R3 正文修订注记: {m.group(0)[:50]}")
# R4 版本历史
if VERSION_SECTION not in text:
issues.append(f"R4 缺「{VERSION_SECTION}」节")
elif not any(l.strip().startswith("|") for l in version_lines):
issues.append(f"R4 「{VERSION_SECTION}」节无表格")
# R4 版本历史(登记处按 RULE_EXEMPT 豁免——条目「解决即移除」,不按版本累积)
if "R4" not in ex:
if VERSION_SECTION not in text:
issues.append(f"R4 缺「{VERSION_SECTION}」节")
elif not any(l.strip().startswith("|") for l in version_lines):
issues.append(f"R4 「{VERSION_SECTION}」节无表格")
# R10 版本历史版本号唯一性(实证撞号:v0.19 曾被并行两批同时占用)
ver_seen = {} # key: 归一版本号 → [首次原样 token, 出现次数]
@@ -248,20 +270,21 @@ def lint_file(path):
if count > 1:
issues.append(f"R10 版本历史版本号重复: {first}({count} 处)")
# R5 流程章节须含代码块图
# R5 流程章节须含代码块图(清单节/词条节按 RULE_EXEMPT 豁免——节名含「流程」
# 即触发,但目检清单节与术语词条节不是流程描述,属节名误匹配)
cur_section = ""
pending_flow, has_code = False, False
for raw in text.splitlines():
stripped = raw.strip()
if raw.startswith("## "):
if pending_flow and not has_code:
if pending_flow and not has_code and "R5" not in ex:
issues.append(f"R5 「{cur_section}」节无流程图(须含代码块字符画/Mermaid)")
cur_section = stripped.lstrip("#").strip()
pending_flow = bool(FLOW_SECTION_RE.search(cur_section)) and cur_section != VERSION_SECTION
has_code = False
elif stripped.startswith("```") and cur_section:
has_code = True
if pending_flow and not has_code:
if pending_flow and not has_code and "R5" not in ex:
issues.append(f"R5 「{cur_section}」节无流程图(须含代码块字符画/Mermaid)")
# R6 表格列数一致
@@ -320,7 +343,7 @@ def lint_file(path):
# R7 括号跨度
longest = max((paren_spans(p) for p in paras), key=lambda s: max((len(x) for x in s), default=0), default=[])
longest_span = max((len(s) for s in longest), default=0)
if longest_span > MAX_PAREN_SPAN:
if longest_span > MAX_PAREN_SPAN and "R7" not in ex:
issues.append(f"R7 最长括号跨度 {longest_span} 字(>{MAX_PAREN_SPAN})——疑似审计注记挤进正文")
infos.append(f"最长括号跨度 {longest_span} 字")