rules(规则审阅落地批): 十条评审意见落地——呈报四问/文件头缩窄/禁词表冻结/lean死条文退役/日期批注清除/AGENTS瘦身/分域闸口径/状态栏纯化

- AGENTS.md: 呈报六问改读后自检四问;文件头红线删行数上限与白名单;日期批注清除;回执五况细则挪 process-conventions §3.10;流水线闸过时状态句改事实句
- process-conventions: §1.2 非迭代批改按改动域跑闸;§3.10 承接五况;两处日期批注清除;§2 表格凭据落点改现状清单
- code-conventions: §1.4.1 重写(删 8 行上限/两类白名单/90 文件清偿义务);§2.1 联动;两处日期批注清除
- test-conventions: 三处日期批注清除(三处实证日期属证据保留)
- doc-conventions: §1 lean 死条文删除;§4 词表冻结条款;§12.1 重写(状态行只写状态词;现状=关联文档清单;删括号凭据全部条款);三处日期批注清除;§5 重复词修复
- 术语表: 用语规范节加词表冻结句
- 06-待办清单: 11 条带括号状态行迁移(括号证据移入现状关联文档清单;待裁决说明并入现状;每条状态行只留状态词)
- tools/audit_file_headers.py: 删 R1 行数上限(R0 载体/R2 日期/R3 变更叙事保留)
- tools/gdd_lint.py: R2 表格占比判据与 R2_EXEMPT 整体退役(lean 解读对象已亡,只产存量噪音)
- tools/doc_term_audit.py: 词表冻结注释
- tools/pipeline_gate.py: 状态栏括号凭据核对废止,新增状态行禁括号判据(点名病因短路);闭合证据改现状正文核对(卡片路径磁盘存在性+交叉核、文档路径+版本、art-bible+版本)
- game/tests/static_scan.sh: S10 注释同步

变异验证(test-conventions §6):pipeline_gate 注入括号→状态行含括号内容红 exit1;删正文卡片路径→正文缺卡片路径+交叉核红 exit1;audit_file_headers 注入变更叙事→R2/R3 红 exit1、头注 12 行纯说明→0 红(上限停用实证)。坑册新增:未提交工作区禁 git checkout 变异还原(会连带回滚在途改动),须备份文件法。

全批净变化 +155/−146(12 文件)。
This commit is contained in:
DKChan committed 2026-10-20 23:15:05 +08:00
1 parent 880ee91b20
commit 811f988032
12 files changed
+155 -146

No files matched your search

+2 -8
View File
@@ -3,9 +3,8 @@
"""audit_file_headers.py — 文件头机器闸(code-conventions §1.4.1)。
文件头 = .gd 文件顶部连续注释区:连续的空行或 # 注释行,首个非注释代码行止。
本脚本只实现机械可判子集(两类白名单的人判面见规范正文):
本脚本只实现机械可判子集(2026-10-20 缩窄:行数上限 R1 与类别白名单已停用):
R0 载体:头注非空时首行必须以 `##` 文档注释开头;
R1 行数上限:头注注释行(非空)> 8 行;
R2 日期:头注出现日期模式(2026-09-12 / 2026年9月 一类)——日期只属于 git log 与迭代报告;
R3 变更叙事词:「迭代N / 本迭代」、批次叙事复合词(本批/拆件批/改造批……)、「迁移到/改名为」
一类变更经过。清单从紧防误伤:「结算批」等玩法机制词不在黑名单;
@@ -20,8 +19,6 @@ from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
SCAN_DIRS = ["game/core", "game/data", "game/presentation"]
MAX_HEADER_LINES = 8
DATE_PAT = re.compile(r"\d{4}\s*[-/.年]\s*\d{1,2}")
NARRATIVE_PATS = [
(re.compile(r"迭代\s*\d|本迭代"), "变更叙事「迭代N」"),
@@ -52,9 +49,6 @@ def audit_path(path):
rows = header_lines(text)
if rows and not rows[0][1].lstrip().startswith("##"):
hits.append((rows[0][0], "R0 头注必须以 ## 文档注释开头"))
if len(rows) > MAX_HEADER_LINES:
hits.append((rows[0][0], "R1 头注 %d 行 > 上限 %d 行(区间 %d-%d 行)"
% (len(rows), MAX_HEADER_LINES, rows[0][0], rows[-1][0])))
for lineno, line in rows:
if DATE_PAT.search(line):
hits.append((lineno, "R2 头注日期"))
@@ -77,7 +71,7 @@ def main():
if violations:
print("[audit_file_headers] FAIL: 文件头存量 %d 处(存量按 code-conventions §1.4.1 白名单清偿)" % violations)
return 1
print("[audit_file_headers] ok: 头注 ≤8 行且零黑名单命中")
print("[audit_file_headers] ok: 头注零黑名单命中(R0 载体/R2 日期/R3 变更叙事)")
return 0
+2
View File
@@ -46,6 +46,8 @@ RETIRED_TERMS = [
("雾格", "2026-09-14", "(雾格移除)", "不做清单"),
("兄弟精", "2026-09-26", "L2 熊地精(bugbear 复用)", "变体名未经批准退役批——用户质问查证后全仓清除"),
# —— 用语规范(2026-10-15 用户令):权威=design/gdd/术语表.md「用语规范」A 表,机器闸吸收 ——
# 词表冻结:本节禁再新增词条;扩表唯一入口=用户实读反馈并经用户裁决,由非迭代批写入,agent 禁自行增词
# (doc-conventions §4「词表冻结」)。
# 注意:「随批」「台账」两词本身是上方行级白名单信号(行内出现即归留档类),对其扫描会自豁免,
# 故不列入;「收口」「在飞」等照常扫描。术语表「用语规范」节已加入节级豁免(词表自身不命中)。
("对标", "2026-10-15", "参照", "用语规范 A 表(STC 1.7)"),
+7 -29
View File
@@ -9,7 +9,6 @@ r"""gdd_lint.py — GDD lean 格式门禁
规则(口径权威 = design/engineering/doc-conventions.md 第 5–6 节文档骨架与结构纪律):
R1 散文段落 ≤ 240 字(代码块/表格/列表/引用不计)
R2 表格字符占比(词条表分域豁免,见 R2_EXEMPT 注)
R3 正文禁止修订注记:~~删除线~~、"作废"、括号内"日期+评审/修订/拍板/裁决/核销"
(登记处/清单按 RULE_EXEMPT 分域豁免)
R4 禁止变更记录节:出现「版本历史/修订记录/变更记录/修改记录/更新记录/更新日志」标题即红
@@ -31,29 +30,18 @@ import sys
from pathlib import Path
MAX_PARA = 240
# 2026-09-14 放宽 60→30:lean 改革后的新模板(参考 analysis/design 两件套)允许
# 短段落与编号人话规则——防铺开的职责改由 R1(段 ≤240 字)与 R9(节点 ≤80 行)承担,
# R2 降为兜底下限(防纯散文文档)。
MIN_TABLE_RATIO = 30
# R2 表格占比判据已停用(2026-10-20 规则审阅批):lean 体系退役后其解读对象已不存在,
# 分域豁免越加越多、只产存量噪音(af94ce97 指纹刷新为证),判据与 R2_EXEMPT 一并拆除。
MAX_PAREN_SPAN = 80
# 规则分域(2026-10-03 用户裁「测试要快/要准」优化批):R1/R2 的体裁前提是「规范文」
# (短段+表格化);登记处/清单/概念文的自然形态就是长条目与叙事散文,硬套=自 2026-09-20
# 规则分域(2026-10-03 用户裁「测试要快/要准」优化批):R1 的体裁前提是「规范文」
# (短段+表格化);登记处/清单的自然形态就是长条目与叙事散文,硬套=自 2026-09-20
# 上线起 8/8 常红零过滤价值(test-perf-report-2026-10-02 §6.2 实证)。R3/R7 不豁免——
# 「provenance 出正文」对一切体裁有效;R4 变更记录节禁令自 2026-10-19 起对一切体裁有效。
R1_EXEMPT = {
"06-待办清单.md", # 全项目待办登记处:现状/状态/方案长条目为体裁
"07-体验验证清单.md", # 体验验证读数清单:同上
}
R2_EXEMPT = {
"01-核心循环.md", # 循环概念文:叙事散文为体裁
"03-内容规模.md", # 规模框架文:多维档位靠散文交代,结构表只有一张
"04-美术音频方向.md", # 美术方向文:同上
"05-不做清单.md", # 清单文:条目散文为体裁
"06-待办清单.md",
"07-体验验证清单.md",
"术语表.md", # 词条表:表格为主体形态,表格占比高是体裁本身
}
# 规则粒度分域豁免(2026-10-15 用户令「清单类文件与术语表不做 GDD 规范性检查」)。
# 原则:**按规则豁免,不按文件拉黑**——体裁规则(R3/R5/R7)对登记处/清单/词条表
# 不适用,但机械可判的完整性规则(R4/R6/R8/R9/R11)一律不豁免,真缺陷不得藏起来。
@@ -201,17 +189,7 @@ def lint_file(path):
if len(p) > MAX_PARA:
issues.append(f"R1 散文段 {len(p)} 字(>{MAX_PARA}): {p[:50]}…")
# R2 表格占比(概念/方向/清单文分域豁免,见 R2_EXEMPT 注)
code_chars = sum(len(l) for l in code_lines)
table_chars = sum(len(l) for l in text.splitlines() if l.strip().startswith("|"))
total = max(len(text) - code_chars, 1)
ratio = int(100 * table_chars / total)
if fname in R2_EXEMPT:
infos.append(f"表格占比 {ratio}%(R2 分域豁免)")
elif ratio < MIN_TABLE_RATIO:
issues.append(f"R2 表格占比 {ratio}%(<{MIN_TABLE_RATIO}%)")
else:
infos.append(f"表格占比 {ratio}%")
# R2 表格占比已停用(见文件头注释)。
# R3 修订注记(代码块豁免;登记处/清单按 RULE_EXEMPT 豁免——变更记录节已整体禁设,无豁免域)
body_no_code = "\n".join(l for l in text.splitlines() if l not in code_lines)
@@ -307,7 +285,7 @@ def lint_file(path):
issues.append(f"R7 最长括号跨度 {longest_span} 字(>{MAX_PAREN_SPAN})——疑似审计注记挤进正文")
infos.append(f"最长括号跨度 {longest_span} 字")
return {"file": str(path), "issues": issues, "infos": infos, "table_ratio": ratio}
return {"file": str(path), "issues": issues, "infos": infos}
def main():
@@ -347,7 +325,7 @@ def main():
else:
for r in results:
mark = "❌ FAIL" if r["issues"] else "✅ PASS"
print(f"\n{mark} {r['file']} (表格占比 {r['table_ratio']}%)")
print(f"\n{mark} {r['file']}")
for i in r["infos"]:
print(f" · {i}")
for issue in r["issues"]:
+71 -41
View File
@@ -119,8 +119,12 @@ def check_title(title, line, issues, where):
return face
def check_segment(word, creds, line, issues, where, root):
"""判据 3 的单段校验。返回 (合法状态词|None, 已写卡路径|None, 已编迭代号|None)。"""
def check_segment(word, line, issues, where):
"""判据 3 的单段校验。返回 (合法状态词|None, 已编迭代号|None)。
2026-10-20 状态栏纯化批:状态行只写状态词(§12.1),括号凭据已废止——
闭合证据改到现状关联文档清单核对(check_todo 内),本函数只认状态词本身。
"""
def red(name, msg):
issues.append((where, line, name, msg))
@@ -128,40 +132,24 @@ def check_segment(word, creds, line, issues, where, root):
base = "已编迭代" if word.startswith("已编迭代") else word
if im is None and base == "已编迭代":
red("状态词非法", "「%s」缺迭代号(须「已编迭代 N」)" % word)
return None, None, None
return None, None
if base not in STATUS_WORDS:
red("状态词非法", "「%s」不在合法状态词白名单" % word)
return None, None, None
cred_text = ";".join(creds)
if base in ("文档设计已闭合", "已写入"):
if not (VER_RE.search(cred_text) and ("/" in cred_text or ".md" in cred_text)):
red("凭据缺失", "「%s」须带括号落点凭据(文档路径+版本号形态)" % base)
elif base == "美术设计已闭合":
if not ("art-bible" in cred_text.lower() and VER_RE.search(cred_text)):
red("凭据缺失", "「美术设计已闭合」凭据须含 art-bible 路径+版本号")
elif base == "已写卡":
rp = None
for c in creds:
m = RESEARCH_RE.search(c)
if m:
rp = m.group(0)
break
if rp is None:
red("凭据缺失", "「已写卡」括号内须为 reports/ 下的需求卡片路径")
elif not (root / rp).is_file():
red("卡片路径不存在", "已写卡路径 %s 相对仓库根不存在" % rp)
return base, rp, None
red("状态词非法", "「%s」不在合法状态词白名单" % base)
return None, None
if im is not None:
return base, None, int(im.group(1))
return base, None, None
return base, int(im.group(1))
return base, None
def check_status(raw, line, face, issues, where, root):
"""判据 3+状态段累积制(§12.1)+特批/备选。返回 (状态词列表, 已写卡路径列表, 已编迭代号|None)。
def check_status(raw, line, face, issues, where):
"""判据 3+状态段累积制(§12.1)+特批/备选。返回 (状态词列表, 已编迭代号|None)。
累积制:按+切段后段数不限,逐段各自合法;再查两条结构约束——
下游段(待写卡/已写卡/已编迭代)在场时上游闭合段必须在场(带日期特批可豁免);
双轨设计阶段(无下游段)必须恰两段且一文档一美术。
状态行只写状态词(§12.1):去掉特批标记与(备选)后缀后,残留任何括号即红——
证据写现状关联文档清单,由 check_todo 到正文核对;此处点名病因后短路,
不再产出碎片「状态词非法」误导红。
"""
content = raw.strip()
m = re.match(r"^\*\*(.+)\*\*$", content)
@@ -175,28 +163,28 @@ def check_status(raw, line, face, issues, where, root):
issues.append((where, line, "特批标记", "〔用户特批〕缺日期(带 YYYY-MM-DD 才放行跳轨)"))
content = APPROVAL_RE.sub("", content)
content = content.replace("(备选)", "").replace("(备选)", "").strip()
if re.search(r"[(())]", content):
issues.append((where, line, "状态行含括号内容",
"括号里的信息(路径/版本/说明)写进现状关联文档清单,状态行只留状态词(§12.1)"))
return [], None
segs = [s.strip() for s in content.split("+") if s.strip()]
if not segs:
issues.append((where, line, "状态词非法", "状态行内容为空"))
return [], [], None
words, cards, iter_no = [], [], None
return [], None
words, iter_no = [], None
for seg in segs:
creds = CRED_RE.findall(seg)
word = CRED_RE.sub("", seg).strip()
w, rp, it = check_segment(word, creds, line, issues, where, root)
w, it = check_segment(seg, line, issues, where)
if w:
words.append(w)
if rp:
cards.append(rp)
if it is not None:
iter_no = it
downstream = [w for w in words if w in DOWNSTREAM_WORDS]
if downstream and face in PRESENCE_FACES and not dated_approval:
need = []
if face in ("文档轨", "双轨") and "文档设计已闭合" not in words:
need.append("文档设计已闭合(凭据)")
need.append("文档设计已闭合")
if face in ("美术轨", "双轨") and "美术设计已闭合" not in words:
need.append("美术设计已闭合(凭据)")
need.append("美术设计已闭合")
if need:
issues.append((where, line, "在场约束",
"下游段(%s)在场但缺上游闭合段 %s(带日期特批可豁免)"
@@ -208,11 +196,16 @@ def check_status(raw, line, face, issues, where, root):
issues.append((where, line, "双轨段数",
"设计面=双轨设计阶段须恰两段且一文档一美术(现 %d 段:文档轨 %d、美术轨 %d)"
% (len(segs), n_doc, n_art)))
return words, cards, iter_no
return words, iter_no
def check_todo(text, issues, where, root):
"""06 全部判据。返回交叉核链(已编迭代条目 → (行号, 迭代号, 卡片路径|None))。"""
"""06 全部判据。返回交叉核链(已编迭代条目 → (行号, 迭代号, 卡片路径|None))。
2026-10-20 状态栏纯化批:闭合类状态词的证据核对从状态栏括号改到现状关联文档
清单(§12.1「状态行只写状态词」)——已写卡对 reports/ 卡片路径+磁盘存在性,
文档轨闭合/已写入对 design/ 路径+版本号,美术轨闭合对 art-bible+版本号。
"""
entries = parse_todo(text)
links = []
if not entries:
@@ -233,8 +226,45 @@ def check_todo(text, issues, where, root):
issues.append((where, line, "属性缺失", "条目缺「**%s**」属性" % req))
if "状态" not in attrs:
continue
words, cards, iter_no = check_status(
attrs["状态"], attr_line["状态"], face, issues, where, root)
words, iter_no = check_status(
attrs["状态"], attr_line["状态"], face, issues, where)
# 现状属性全文(含清单续行):证据核对只看现状,其他属性不算数
xian_lines, in_xian = [], False
for ln, raw in e["body"]:
m = ATTR_LINE_RE.match(raw.strip())
if m:
in_xian = m.group(1) == "现状"
if in_xian:
xian_lines.append(m.group(2))
elif in_xian:
xian_lines.append(raw)
xian = "\n".join(xian_lines)
xline = attr_line.get("现状", line)
# 证据核对一:已写卡 → 现状清单须有卡片路径,逐条验磁盘存在
cards = []
if "已写卡" in words:
cards = RESEARCH_RE.findall(xian)
if not cards:
issues.append((where, xline, "正文缺卡片路径",
"状态段「已写卡」在场,但现状关联文档清单里没有 "
"reports/需求卡片-*.md 路径(§12.1:证据写正文)"))
else:
for rp in cards:
if not (root / rp).is_file():
issues.append((where, line, "卡片路径不存在",
"现状清单里的卡片路径 %s 相对仓库根不存在" % rp))
# 证据核对二:文档轨闭合/反哺已写入 → 现状清单须有文档路径+版本号
# (06 正文里的指针是相对路径,形如 systems/… 或 design/…,只认 .md 收尾+版本号)
if "文档设计已闭合" in words or "已写入" in words:
if not (re.search(r"[^\s ,,()()]+\.md", xian) and VER_RE.search(xian)):
issues.append((where, xline, "正文缺设计定稿指针",
"状态段含文档轨闭合词,但现状清单里没有带版本号的文档路径(§12.1:证据写正文)"))
# 证据核对三:美术轨闭合 → 现状清单须有 art-bible 版本凭据
if "美术设计已闭合" in words:
if not ("art-bible" in xian.lower() and VER_RE.search(xian)):
issues.append((where, xline, "正文缺美术凭据",
"状态段「美术设计已闭合」在场,但现状清单里没有 "
"art-bible 版本凭据(§12.1:证据写正文)"))
need_gap = any(w in GAP_REQUIRED for w in words)
if need_gap and "缺口" not in attrs:
issues.append((where, line, "缺口属性",