- 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 文件)。
80 lines
3.3 KiB
Python
80 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""audit_file_headers.py — 文件头机器闸(code-conventions §1.4.1)。
|
||
|
||
文件头 = .gd 文件顶部连续注释区:连续的空行或 # 注释行,首个非注释代码行止。
|
||
本脚本只实现机械可判子集(2026-10-20 缩窄:行数上限 R1 与类别白名单已停用):
|
||
R0 载体:头注非空时首行必须以 `##` 文档注释开头;
|
||
R2 日期:头注出现日期模式(2026-09-12 / 2026年9月 一类)——日期只属于 git log 与迭代报告;
|
||
R3 变更叙事词:「迭代N / 本迭代」、批次叙事复合词(本批/拆件批/改造批……)、「迁移到/改名为」
|
||
一类变更经过。清单从紧防误伤:「结算批」等玩法机制词不在黑名单;
|
||
新失败模式先补本脚本规则再修码(static_scan 纪律)。
|
||
扫描域 = game/core + game/data + game/presentation(tests/smoke 豁免,同 audit_type_hints 口径)。
|
||
退出码:0=归零,1=有存量(逐条列 file:line)。用法:python tools/audit_file_headers.py
|
||
"""
|
||
import re
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parent.parent
|
||
SCAN_DIRS = ["game/core", "game/data", "game/presentation"]
|
||
|
||
DATE_PAT = re.compile(r"\d{4}\s*[-/.年]\s*\d{1,2}")
|
||
NARRATIVE_PATS = [
|
||
(re.compile(r"迭代\s*\d|本迭代"), "变更叙事「迭代N」"),
|
||
(re.compile(
|
||
r"本批|拆件批|改造批|重构批|实装批|清偿批|落地批|收口批|收尾批|转正批|对齐批|拆分批|换代批|升级批|随批"),
|
||
"变更叙事「XX批」"),
|
||
(re.compile(r"迁移到|迁至|改名为|更名为|原名为|曾用名"), "变更经过叙述"),
|
||
]
|
||
|
||
|
||
def header_lines(text):
|
||
"""返回文件头注释区的注释行 list[(lineno, line)],首个非注释非空行止;空行不计数。"""
|
||
rows = []
|
||
for lineno, line in enumerate(text.splitlines(), start=1):
|
||
stripped = line.lstrip("\ufeff").strip()
|
||
if stripped == "":
|
||
continue
|
||
if stripped.startswith("#"):
|
||
rows.append((lineno, line))
|
||
else:
|
||
break
|
||
return rows
|
||
|
||
|
||
def audit_path(path):
|
||
hits = []
|
||
text = path.read_text(encoding="utf-8", errors="replace")
|
||
rows = header_lines(text)
|
||
if rows and not rows[0][1].lstrip().startswith("##"):
|
||
hits.append((rows[0][0], "R0 头注必须以 ## 文档注释开头"))
|
||
for lineno, line in rows:
|
||
if DATE_PAT.search(line):
|
||
hits.append((lineno, "R2 头注日期"))
|
||
for pat, label in NARRATIVE_PATS:
|
||
if pat.search(line):
|
||
hits.append((lineno, "R3 " + label))
|
||
return hits
|
||
|
||
|
||
def main():
|
||
violations = 0
|
||
for d in SCAN_DIRS:
|
||
base = ROOT / d
|
||
if not base.is_dir():
|
||
continue
|
||
for path in sorted(base.rglob("*.gd")):
|
||
for lineno, msg in audit_path(path):
|
||
print("%s:%d: %s" % (path.relative_to(ROOT), lineno, msg))
|
||
violations += 1
|
||
if violations:
|
||
print("[audit_file_headers] FAIL: 文件头存量 %d 处(存量按 code-conventions §1.4.1 白名单清偿)" % violations)
|
||
return 1
|
||
print("[audit_file_headers] ok: 头注零黑名单命中(R0 载体/R2 日期/R3 变更叙事)")
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|