架构收窄批第3步(测试域分流): S系规则全集迁驻 game/tests/README.md「Static Scan」节+S7符号清单改代码自动提取。README: S1-S13 全表落驻(原指架构文档「测试与质量域」节, 该节已收窄), S3 warning 级状态句改本文件自持, 迁移句「正主」改白话(S11 全域扫描命中实证); static_scan.sh: 头注改指 README, S7 符号清单由 B2/B3/B4 三族文件自动提取公开方法面(非下划线 func 含 static, is_mapped 批量工具专用沿用原口径剔除)+提取非空自检+空提取跳扫, B1_SET 注释与 S8 反向对账错误消息去架构文档指路(清单单点=本脚本)。凭据: static_scan 单跑 exit=0 全绿(S8 ok 14文件反向对账, S7 ok 52文件符号全收敛, S13 gdstyle 基线外0)
This commit is contained in:
1 parent
5fb6c17fe6
commit
59406bd800
2 files changed
+54
-25
No files matched your search
+20
-4
@@ -331,10 +331,26 @@ godot --headless --path . res://probe/<probe>.tscn
|
||||
|
||||
## Static Scan(提交前本地跑)
|
||||
|
||||
`tests/static_scan.sh` 实现静态扫描域 S1–S12 的可执行子集;S1–S10 的规则全集与动机见
|
||||
`design/engineering/architecture/architecture.md`「测试与质量域」节,**S11/S12 两段的规则说明自持于本文件与
|
||||
`tests/static_scan.sh` 头注**,这两段不另进架构文档。
|
||||
新违规案例必须**先补扫描规则再修码**。
|
||||
`tests/static_scan.sh` 实现静态扫描域 S1–S13;**本节是全部 S 系规则的规则全集与动机**(2026-11-07 架构收窄批迁驻——原指架构文档「测试与质量域」节,该文档已收窄为约束面)。适用域分档(产品码三目录 vs 非产品码三目录)与无机器闸的理由见 `design/engineering/code-conventions.md` §4 第 13 条。
|
||||
新违规案例必须**先补扫描规则再修码**(code-conventions 缺陷-规范对照表纪律)。
|
||||
|
||||
S1–S10 扫描面止于产品码三目录(`game/core`+`game/data`+`game/presentation`);S11/S12 扫描面覆盖 game/ 全域;S13 扫描面=产品码三目录。
|
||||
|
||||
| # | 规则 | 判级与要点 |
|
||||
|---|---|---|
|
||||
| S1 | 玩法代码禁 `randomize()`、自建 `RandomNumberGenerator`(D2 Rule 5) | 硬失败;扫描面 game/core + game/presentation。非产品码三目录(`game/tests`/`game/probe`/`game/sim`)不进本闸与 S2——它们自建且**显式定种**的随机源是合法测量口径(同种子对拍改前改后读数),禁的是不定种形态 |
|
||||
| S2 | 禁全局随机函数 `randi()` / `randf()` / `randf_range()`——全局 RNG 绕开 RngService | 硬失败,与 S1 同段;实例方法 `.randi()` 排除;`rng-seed-exempt` 豁免注记在案 |
|
||||
| S3 | core/ 禁 ConfigDB 外 data 表加载(`load(` / `preload(` / `ResourceLoader.load`;schema class_name 直引豁免)(D2 Rule 2) | **warning 级**(不参与 fail 判定)——class_name 直引误报需人工复核;路径=`game/core`;豁免 `config_db.gd` 自身——它就是数值单一事实源的定义所属文件。本行即状态事实源(2026-11-03 架构审查 P1 裁维持 warning 级不升硬,升硬前提=可靠的直引误报排除判据,该判据未立),禁在别处另列 |
|
||||
| S4 | core/ 禁 `extends Control/Node2D`、禁 `.tscn` preload(D2 Rule 7) | 硬失败 |
|
||||
| S5 | core/ 禁裸引用 autoload 全局标识符(`ConfigDB` / `RngService` 直接写=--script 模式 parse error)——构造注入 only | 硬失败;形态=标识符后紧跟 `.` 的成员访问,排除注释行与字符串字面量 |
|
||||
| S6 | core↔core 信号连接只许方法 Callable(禁 lambda 捕获 self);发射者禁持有订阅者反向引用 | 硬失败(2026-10-25 落闸:内联 lambda 禁令机器闸——同行与跨行两形态、豁免注记 `s6-signal-exempt`);「发射者禁反向引用」句静态判据不成熟,归人工评审承接、不造弱判据 |
|
||||
| S7 | B1 编排唯一性:B2/B3/B4 API 符号命中文件 ⊆ B1 文件集;A2/B3/B4 零相互直调(B1 AC-43) | 硬失败;**符号清单=从 B2/B3/B4 三族文件自动提取公开方法面**(非下划线 func 名,无手写清单);B1 域文件集清单单点=本脚本 `B1_SET`(脚本即清单);豁免注记 `s7-b1-exempt`;缺位自检在位 |
|
||||
| S8 | 结算路径 `await` 零命中(B1 结算/编排文件集) | 硬失败,排除注释与字符串;文件集单点=`B1_SET`;自检两件:①清单内文件缺位即红;②**目录域反向对账**——`game/core/settlement/` 实有 `.gd` 未登记进 `B1_SET` 即红;装板/授予/效果端口/掷格四件结算码住 `game/core` 根下,纯目录域覆盖不住,根下新增由写卡段申报承接 |
|
||||
| S9 | 类型闸(`tools/audit_type_hints.py`):产品码无类型容器/有值返回缺类型/参数缺类型(R1/R2/R3 细则=code-conventions §4 第 7/8/12 条) | 硬失败;扫描面缺目录或零个 `.gd` 判红(防空转自检),归绿时打印扫描面件数供核 |
|
||||
| S10 | 文件头闸(`tools/audit_file_headers.py`):头注 `##` 载体+日期/变更叙事黑名单(细则=code-conventions §1.4.1) | 硬失败;同款防空转自检 |
|
||||
| S11 | 代码注释禁用词闸 | 规则说明见下段 |
|
||||
| S12 | 效果引擎无状态化旧件零残留闸 | 规则说明见下段 |
|
||||
| S13 | 第三方质量规则闸(`tools/gdstyle_gate.py`,钉 gdstyle v0.4.0:真缺陷类规则——重复 load、循环内 await、循环内分配、不可达代码等) | 硬失败;基线键=文件\|规则\|函数名(类/文件级=文件\|规则),数字漂移不假红,同键指标恶化超基线登记值即红;存量以 `tools/gdstyle_baseline.txt` 欠账簿登记;扫描面=产品码三目录 |
|
||||
|
||||
S11 代码注释禁用词:扫描 game/ 全域 .gd/.tres/.md/.json/.sh 的注释面;禁词全部由 `design/gdd/术语表.md`「退役词对照」第一列解析供给(脚本内不内联任何词面——单一事实源);解析结果再按「巢穴」「旗」「flag_*」三条整行排除,另设同形异义豁免「去重闸」(封印重判去重的机制判别器,与某闸档退役词后二字同形——行内含「去重闸」整行跳过);三层豁免(闸门自指/显式留档 20 处〔契约原名引述与带日期历史读数注释,清单见 `tests/static_scan.sh` 头注〕/非注释面天然不取);两道自检(扫描面文件数非空+词表解析非空)。
|
||||
|
||||
|
||||
+34
-21
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# tests/static_scan.sh — 静态扫描域 S1–S10 全量在位
|
||||
# 规则全集与动机:design/engineering/architecture/architecture.md「测试与质量域」节
|
||||
# 规则全集与动机:game/tests/README.md「Static Scan」节(2026-11-07 架构收窄批迁驻)
|
||||
# 正式工程(core/ 存在)时执行;否则优雅跳过(工程在 game/core——仓库根布局)。
|
||||
# 纪律:新违规案例必须先补本脚本规则再修码(design/engineering/code-conventions.md 缺陷-规范对照表)。
|
||||
set -u
|
||||
@@ -313,17 +313,16 @@ else
|
||||
fi
|
||||
|
||||
# ── S8 — 结算路径 await 零命中(B1 结算/编排文件集)──
|
||||
# 规则(architecture.md「静态扫描域 S1–S8 承接清单」S8 行 / D2 AC-14 口径):结算本体是
|
||||
# 可回放纯逻辑步骤流(同种子同输入逐位复现),await 会打断确定性——「模块归属」B1 行已写明
|
||||
# 「禁 await——切片 88 冒烟实证」。本闸为防回归防线(2026-10-24 人工 grep 盘面干净)。
|
||||
# 扫描面:B1 域文件集(与 architecture.md「模块归属」B1 行同口径)=battle_orchestrator.gd
|
||||
# 规则(game/tests/README.md「Static Scan」节 S8 行 / D2 AC-14 口径):结算本体是
|
||||
# 可回放纯逻辑步骤流(同种子同输入逐位复现),await 会打断确定性。本闸为防回归防线。
|
||||
# 扫描面:B1 域文件集=battle_orchestrator.gd
|
||||
# + battle_fx_host.gd + reward_grant.gd + board_installer.gd 三拆件 + settlement/ 五段段类 + damage_controller.gd,清单单点=B1_SET。
|
||||
# 执行侧字面值豁免:脚本必须带字面值才能工作;文件集与架构文档冲突时以 B1 域文件集为准。
|
||||
# 判据:文件集内出现 await 关键字即红(词边界 \bawait\b,任何形态——await 信号/协程/
|
||||
# get_tree());排除注释行(行首 #/##)与字符串字面量(双引号包裹段内的 await 非代码),
|
||||
# 口径仿 S1/S2 的 grep 排除手法。
|
||||
# 自检:①清单内文件缺位(路径写错/被改名)即判红——防扫面为空静默绿;②settlement/ 目录域反向对账——新段类未登记即判红(见下)。
|
||||
# B1 域文件集=单点清单(S8 await 禁扫面与 S7 豁免名单共用;改成员先改 architecture.md「模块归属」B1 行再改此处,2026-11-03 架构审查 P1 裁并点)
|
||||
# B1 域文件集=单点清单(S8 await 禁扫面与 S7 豁免名单共用;2026-11-03 并点。清单单点=本脚本,
|
||||
# 脚本即清单——2026-11-07 架构收窄后架构文档不再承载文件集登记,改成员只改此处)
|
||||
B1_SET="game/core/battle_orchestrator.gd game/core/battle_fx_host.gd game/core/reward_grant.gd game/core/board_installer.gd game/core/settlement/aura_segment.gd game/core/settlement/combat_segment.gd game/core/settlement/decay_segment.gd game/core/settlement/grant_segment.gd game/core/settlement/verdict_segment.gd game/core/settlement/round_cycle.gd game/core/damage_controller.gd game/core/settlement/battle_refresh_flow.gd game/core/effect_port.gd game/core/reward_roller.gd"
|
||||
S8_FILES="$B1_SET"
|
||||
S8_MISS=0
|
||||
@@ -335,13 +334,13 @@ for f in $S8_FILES; do
|
||||
done
|
||||
# 自检二(2026-11-03 架构审查 P1 裁):目录域反向对账——settlement/ 实有 .gd 未登记进 B1_SET=该扫的没扫,判红。
|
||||
# 只反向对 settlement/ 一个目录域:装板/授予/效果端口/掷格四件结算码住 game/core 根下,纯目录域覆盖不住,
|
||||
# 根下新增仍由「模块归属」B1 行+写卡段申报面承接(禁把目录判据当成全域判据)。
|
||||
# 根下新增仍由写卡段申报面承接(禁把目录判据当成全域判据)。
|
||||
S8_UNREG=""
|
||||
for f in $(find game/core/settlement -name '*.gd' 2>/dev/null | sort); do
|
||||
case " $B1_SET " in *" $f "*) ;; *) S8_UNREG="$S8_UNREG $f" ;; esac
|
||||
done
|
||||
if [ -n "$S8_UNREG" ]; then
|
||||
echo "[static_scan] S8 FAIL: settlement/ 有结算段类未登记进 B1 域文件集——await 禁扫面对它静默失效:$S8_UNREG(先改 architecture.md「模块归属」B1 行,再补 B1_SET)"
|
||||
echo "[static_scan] S8 FAIL: settlement/ 有结算段类未登记进 B1 域文件集——await 禁扫面对它静默失效:$S8_UNREG(补 B1_SET 登记)"
|
||||
S8_MISS=1
|
||||
fi
|
||||
if [ "$S8_MISS" -eq 1 ]; then
|
||||
@@ -361,13 +360,13 @@ else
|
||||
fi
|
||||
|
||||
# ── S7 — B1 编排唯一性:B2/B3/B4 API 符号命中文件 ⊆ B1 域文件集 ──
|
||||
# 规则(architecture.md「静态扫描域 S1–S8 承接清单」S7 行 / B1 AC-43;依赖约束节同款):
|
||||
# 规则(game/tests/README.md「Static Scan」节 S7 行 / B1 AC-43;依赖约束节同款):
|
||||
# B2/B3/B4 的对外 API 只允许被 B1 域文件集调用——全调用面集中 B1、禁第二调用方。
|
||||
# 附带覆盖「A2/B3/B4 零相互直调」:B2/B3/B4 自身文件 ∉ B1 文件集,命中彼家符号即红
|
||||
# (同族自调用豁免只放自家符号,彼家符号照红)。
|
||||
# 符号清单权威源:architecture.md「模块归属」各表「对外暴露」列——B2(unit_container)14 符号、
|
||||
# B3(status_container)5 符号、B4(reward_economy)9 符号;脚本字面值与文档冲突时以文档为准
|
||||
# (执行侧字面值豁免,同 S8 手法)。B4 表内 is_mapped 静默布尔投影注明批量工具专用、不在暴露面清单。
|
||||
# 符号清单=从 B2/B3/B4 三族文件自动提取公开方法面(非下划线 func 名,含 static func)——
|
||||
# 清单即代码现状,无手写清单、无文档同步义务(2026-11-07 架构收窄批改版;原手写清单
|
||||
# 对文档核录的做法废弃——头注计数与脚本字面已实测漂移 1 符号,手写必漂)。
|
||||
# 扫描面:$DIR_CORE(含 settlement/,B1 文件集在其中)。
|
||||
# 判据:文件内出现 `.符号(` 调用形态(符号后紧跟括号,天然词边界;`.my_grant(` 不误伤 `grant`)
|
||||
# 且该文件 ∉ B1 文件集 → 红。排除注释行与字符串字面量(口径仿 S1/S2/S8 的 grep 排除手法)。
|
||||
@@ -379,7 +378,7 @@ fi
|
||||
# (文件被改名=豁免名单失效,宁可红不可静默放行)。
|
||||
if require_scan_face "S7" "$DIR_CORE"; then
|
||||
# B1 域文件集(迭代 46 卡A 增补 battle_refresh_flow——换图刷新流程件=C13 拆分边界的 B1 域拆件,
|
||||
# 承载旧资源退役/装板编排调用;architecture.md「模块归属」BattleRefreshFlow 行随反哺点落文)
|
||||
# 承载旧资源退役/装板编排调用;清单单点=本脚本)
|
||||
S7_B1_SET="$B1_SET" # 与 S8 同一单点清单(原第二份抄本,2026-11-03 并点——两份靠人工同步必漂)
|
||||
S7_MISS=0
|
||||
for f in $S7_B1_SET game/core/unit_container.gd game/core/status_container.gd game/core/reward_economy.gd; do
|
||||
@@ -406,13 +405,27 @@ else
|
||||
"
|
||||
done < <(find "$DIR_CORE" -name '*.gd' 2>/dev/null | sort)
|
||||
}
|
||||
# 符号清单(2026-10-25 对 architecture.md「模块归属」表核录;改符号先改文档再改此处)
|
||||
s7_scan B2 game/core/unit_container.gd \
|
||||
'spawn|query_alive|rollable_skills|special_skills|check_skill_unlocks|set_pending_action|pending_action|basic_streak|commit_action|tick_cooldowns|apply_damage|heal|default_target|despawn_all'
|
||||
s7_scan B3 game/core/status_container.gd \
|
||||
'notify_battle_start|apply_effect|query_active|decay_round|notify_battle_end'
|
||||
s7_scan B4 game/core/reward_economy.gd \
|
||||
'roll_board|query_identity|query_full_snapshot|grant|grant_clear|retire_mapping|polarity_row|phase'
|
||||
# 符号清单自动提取(2026-11-07 架构收窄批):公开方法面=非下划线 func 名(含 static func),
|
||||
# 清单=代码现状;is_mapped 为批量工具专用投影、不在暴露面(沿用原手写清单口径,提取后剔除)
|
||||
extract_public_funcs() {
|
||||
grep -oE '^[[:space:]]*(static )?func [a-z][a-z_0-9]*' "$1" 2>/dev/null \
|
||||
| sed 's/^[[:space:]]*\(static \)\?func //' | paste -sd'|' -
|
||||
}
|
||||
B2_SYMS=$(extract_public_funcs game/core/unit_container.gd)
|
||||
B3_SYMS=$(extract_public_funcs game/core/status_container.gd)
|
||||
B4_SYMS=$(extract_public_funcs game/core/reward_economy.gd | sed 's/|is_mapped//')
|
||||
# 提取非空自检(防空转:提取失败=空 pattern,grep 零命中恒绿)
|
||||
S7_EXTRACT_MISS=0
|
||||
for v in "$B2_SYMS" "$B3_SYMS" "$B4_SYMS"; do
|
||||
if [ -z "$v" ]; then
|
||||
echo "[static_scan] S7 FAIL: 公开方法面提取为空——三族文件缺位或无公开 func(提取机制静默空转)"
|
||||
S7_EXTRACT_MISS=1
|
||||
fi
|
||||
done
|
||||
[ "$S7_EXTRACT_MISS" -eq 1 ] && fail=1
|
||||
[ -n "$B2_SYMS" ] && s7_scan B2 game/core/unit_container.gd "$B2_SYMS"
|
||||
[ -n "$B3_SYMS" ] && s7_scan B3 game/core/status_container.gd "$B3_SYMS"
|
||||
[ -n "$B4_SYMS" ] && s7_scan B4 game/core/reward_economy.gd "$B4_SYMS"
|
||||
if [ -n "$S7_HITS" ]; then
|
||||
echo "[static_scan] S7 FAIL: B2/B3/B4 API 符号越出 B1 文件集命中 $(printf '%s' "$S7_HITS" | grep -c .) 处(编排唯一性,B1 AC-43)"
|
||||
printf '%s' "$S7_HITS"
|
||||
|
||||
Reference in new issue
Block a user