From 0b7e8df1d3365ab1e62e9e91fe07d3c81ab75385 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Sun, 27 Sep 2026 05:30:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(gate):=20=E7=86=B5=E5=A2=9E=E9=97=B8?= =?UTF-8?q?=E8=90=BD=E5=9C=B0=E2=80=94=E2=80=94repo=5Fstructure=5Faudit.py?= =?UTF-8?q?=20=E5=9B=9B=E6=9F=A5(=E9=A1=B6=E5=B1=82=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=99=BD=E5=90=8D=E5=8D=95/=E6=A0=B9=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E7=99=BD=E5=90=8D=E5=8D=95/=E5=9B=BE=E5=83=8F=E7=99=BD?= =?UTF-8?q?=E5=90=8D=E5=8D=95/=E9=80=80=E5=BD=B9=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E5=AE=88=E5=8D=AB)+design=20=E5=AD=90=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E7=99=BD=E5=90=8D=E5=8D=95;=E8=87=AA=E6=A3=80=E6=8E=A2?= =?UTF-8?q?=E9=92=88=E9=AA=8C=E8=AF=81=E6=8A=93=E8=BF=9D=E4=BE=8B=E8=83=BD?= =?UTF-8?q?=E5=8A=9B;CLAUDE.md=20=E4=BA=A4=E4=BB=98=E5=89=8D=E9=AA=8C?= =?UTF-8?q?=E6=94=B6=E8=A1=8C=E6=8C=82=E9=97=B8;=E5=88=A4=E6=8D=AE=3D?= =?UTF-8?q?=E6=96=B0=E7=B1=BB=E5=88=AB=E9=A1=BB=E5=90=8C=E6=89=B9=E6=94=B9?= =?UTF-8?q?=20CLAUDE.md+=E8=84=9A=E6=9C=AC=E7=99=BD=E5=90=8D=E5=8D=95?= =?UTF-8?q?=E7=95=99=E7=97=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 1 + tools/repo_structure_audit.py | 99 +++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 tools/repo_structure_audit.py diff --git a/CLAUDE.md b/CLAUDE.md index 312acabf..666475e0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -23,6 +23,7 @@ - 调参期批量工具:`--script res://smoke/adventure_monte_carlo.gd`(全冒险期望扫描)· `--script res://smoke/reward_expectation.gd`(奖励获取期望)——单关直入口口径已退役(2026-09-23 用户拍板),通关率/难度类测量一律走全冒险(原 level_monte_carlo/level_debug 已删) - 数值验证工具:`python tools/balance_verifier.py`(零强化冒险逐关条件通关率+止步节点归因+营地循环爬升战役——采集端 `--script res://smoke/verifier_batch.gd`,报告落 `design/qa/`;元进度换算率标定与曲线调参的自动化评判面,2026-09-22 数值验证工具批;直入口带判定随口径退役暂停,带重锚归曲线调参批) - 交付前验收 = smoke + 全部场景探针全绿(测试细则见 `design/dev/code-conventions.md`) +- 结构审计闸(熵增闸):`python tools/repo_structure_audit.py`——顶层目录/根文件/图像白名单/退役路径四查,违例 FAIL;新类别 = 同批修订 CLAUDE.md Project Layout + 脚本白名单,commit 留痕(2026-09-27 立闸) ## Project Layout diff --git a/tools/repo_structure_audit.py b/tools/repo_structure_audit.py new file mode 100644 index 00000000..881834f0 --- /dev/null +++ b/tools/repo_structure_audit.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""仓库结构审计闸(熵增闸,2026-09-27 用户立项)。 + +收口第四闸(与 doc_link_audit / doc_term_audit / gdd_lint 同列): +把目录三层裁定变成机器检查——任何会话乱放文件,收口时此处直接 FAIL。 + +四查: + 1. 顶层目录白名单(新增顶层目录 = FAIL,须同批改 CLAUDE.md + 本脚本白名单) + 2. 根目录文件白名单(根上只许运行文件与规则文件) + 3. 图像白名单(图只许在 artwork/ game/assets/ design/art/references/ design/dev/architecture/) + 4. 退役路径守卫(production/ docs/ tests/ qa-data/ candidates/mockups 旧路径再现 = FAIL) + +用法: python tools/repo_structure_audit.py # 全查,FAIL 时 exit 1 + python tools/repo_structure_audit.py --json # 机器读输出 +依据: CLAUDE.md「Project Layout」节(2026-09-27 目录三层收敛终态)。 +""" +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent + +# 1) 顶层目录白名单(点开头目录一律忽略) +TOP_DIRS = {"archive", "artwork", "design", "game", "sim-data", "tools"} +# 2) 根目录文件白名单 +ROOT_FILES = {"CLAUDE.md", "active.md", "roadmap.md", "stage.txt", "review-mode.txt", + ".gitignore", ".zcodeignore"} +# 3) 图像允许目录(相对路径前缀;archive/ 为归档 zip 不扫) +IMAGE_EXTS = {".png", ".jpg", ".jpeg", ".webp", ".gif", ".svg", ".pxo", ".psd", ".bmp"} +IMAGE_PREFIXES = ("artwork/", "game/assets/", "design/art/references/", "design/dev/architecture/") +# 4) 退役路径(目录或文件任一再现即 FAIL) +RETIRED = ["production", "docs", "tests", "qa-data", + "design/art/candidates", "production/mockups", + "design/ux", "game/tools"] +# design/ 一级子目录白名单(防 ad-hoc 新目录) +DESIGN_SUBDIRS = {"art", "dev", "gdd", "qa", "registry", "research"} + + +def scan(): + fails, notes = [], [] + + # 1) 顶层目录 + top_dirs = {p.name for p in ROOT.iterdir() if p.is_dir() and not p.name.startswith(".")} + extra = top_dirs - TOP_DIRS + if extra: + fails.append("顶层目录越界: %s(新增顶层目录须同批改 CLAUDE.md Project Layout + 本脚本 TOP_DIRS)" % sorted(extra)) + + # 2) 根目录文件(点开头忽略:.gitignore/.zcodeignore 白名单收编,.git 指针等本就不可见) + top_files = {p.name for p in ROOT.iterdir() if p.is_file() and not p.name.startswith(".")} + extra_f = top_files - {f for f in ROOT_FILES if not f.startswith(".")} + if extra_f: + fails.append("根目录文件越界: %s(运行文件与规则文件之外不放根上;新类别须同批改两处白名单)" % sorted(extra_f)) + + # 3) 图像白名单 + stray = [] + for ext in IMAGE_EXTS: + for p in ROOT.rglob("*" + ext): + rel = p.relative_to(ROOT).as_posix() + if rel.startswith((".git", ".zcode", ".qoder", "archive/")): + continue + if not rel.startswith(IMAGE_PREFIXES): + stray.append(rel) + if stray: + fails.append("图像越界 %d 个(只许 %s): %s" % + (len(stray), "、".join(IMAGE_PREFIXES), stray[:8])) + + # 4) 退役路径 + for rel in RETIRED: + if (ROOT / rel).exists(): + fails.append("退役路径再现: %s/(该目录已消亡,内容按 CLAUDE.md 终态归属;此路径出现即结构回潮)" % rel) + + # 5) design/ 一级子目录 + design_subs = {p.name for p in (ROOT / "design").iterdir() if p.is_dir()} if (ROOT / "design").is_dir() else set() + extra_d = design_subs - DESIGN_SUBDIRS + if extra_d: + fails.append("design/ 子目录越界: %s(合法: %s;新分类先过用户拍板再改两处白名单)" % (sorted(extra_d), sorted(DESIGN_SUBDIRS))) + + notes.append("顶层目录 %d 合法 · 根文件 %d 合法 · design/ 子目录 %s · 图像白名单命中 %s" % + (len(top_dirs & TOP_DIRS), len(top_files & ROOT_FILES), + sorted(design_subs), "、".join(IMAGE_PREFIXES))) + return fails, notes + + +def main(): + fails, notes = scan() + for n in notes: + print("✓ " + n) + if fails: + print("\n【结构违例】×%d" % len(fails)) + for f in fails: + print(" ! " + f) + print("\n处理: 文件归位到合法区;确需新类别 = 同批修订 CLAUDE.md Project Layout + 本脚本白名单,commit 留痕。") + sys.exit(1) + print("\n结构审计 PASS(四查全过:顶层目录/根文件/图像白名单/退役路径)") + sys.exit(0) + + +if __name__ == "__main__": + main()