config-constant-audit
改一个常量 / 默认值时全仓审计所有引用点的 skill —— 防漏改 + 防误改 + 顺手发现文档与实现不一致的 silent bug。
兼容平台:Claude Code / OpenClaw / Cursor / Windsurf
config-constant-audit · 常量改动全仓审计
把「改一个 number / string / boolean default」这件事从「grep 一把就改」升级成「分层 审计 + 分级处置 + 兜底 verify」。核心是分辨真正生效的源(source of truth)和 字面巧合(同值但语义无关),改对源 + 同步文档 + 不误伤 false positive。
何时触发
主人说「改 X=Y」「default 改成 N」「全仓搜 X 别漏改」「检查配置」「迁移常量」 「调一下默认值」「把 X 从 A 改成 B」「源代码里所有 N 都改 M」「这个数全仓还有 哪些地方用」。
提到「改一个值」+ 「别漏」语义就调,不要等主人明确说「用 config-constant-audit skill」—— 主人不会记 skill 名,只会描述场景。
四条铁律(违反 = 漏改 / 误改 / 文档撒谎)
铁律 1 · 定位 SSOT 优先于改字面值
不要看到 20 就 grep 20。先找唯一真正生效的源:
export const FOO = 20在src/lib/.../constants.tsdefault: 20在 config schema- DB column default value
trace consumers(谁 import 这个 const)→ 改 source 一处,所有 import 自动跟随,测
试用 import { FOO } 写法的也自动跟随,不用改测试。
如果发现「同一个值在多处独立写死」,这本身就是个独立 bug(违反 DRY),写到 flag-as-bug 段告诉主人,而不是默默批量改(批量改 = 把 bug 复制到新值上)。
铁律 2 · 3-source grep(漏一个就不全)
- literal const 定义:
grep -E "FOO\s*=|FOO_NAME\s*:" - 文档表述:
grep -E "N 回合|default N|N 秒|N 个"(中英都扫) - 字面值散落:
grep -nE "\bN\b"限定相关目录(combat/ / api/ ...)
第 3 类绝大多数是 false positive(其他模块的同值常量),但漏看就可能错过 真正的硬编码。逐条 owner 复验,不批量。
铁律 3 · 3-tier 分级(must-change / leave-alone / flag-as-bug)
每个候选必须归一类:
| Tier | 何时归 | 处置 |
|---|---|---|
| must-change | source const / 用户可见文档 / UI 文案 / template / 测试硬编码字面值 | 改 |
| leave-alone | 历史 lesson 记录 / 其他模块同值常量 / 测试用 import 自动跟随 / 字面巧合 | 不改,在报告里 explicit 说「已 verify 无关」 |
| flag-as-bug | 文档与 server 行为不一致 / 同值在多处独立定义违反 SSOT / spec 写支持 X 字段但 server 不读 | 单独 spawn task 给主人决定,不顺手改 |
不允许「不确定」第四类 —— 复验到能归类为止。
铁律 4 · Read-before-Edit 守卫 + Verify 兜底
起因 2026-05-24 实战踩坑:Bash cat / sed -n 读文件不算 Read tool,直接
Edit 会 silent fail(报「File has not been read yet」但若并行被遮蔽就漏掉)。
所以:
- 改前必用 Read tool(不用 cat / sed)
- 改后必跑 second-pass grep:
grep -E "old_value 单位|FOO.*old_value"应该 完全为空。不为空 = 有 Edit 失败你没注意到 - 改后必跑类型/单测 verify:能跑
tsc --noEmit跑;有相关测试跑 vitest; 不能跑就 explicit 告诉主人「测试 env 起不来,我用 grep + tsc 兜底」 - 报告含分级 diff + verify evidence,不只是「改完了」
工作流
1. INTAKE: 主人说「改 X=Y」/「N 改 M 别漏」
↓
2. SSOT: grep export const / config default / schema default
→ 锁定唯一源(若多处定义 → flag-as-bug 段)
↓
3. CONSUMERS: grep 谁 import 这个 const,理解 import 链
↓
4. CANDIDATES (3-source grep,并行):
- literal const 定义
- 文档表述(spec.md / README / inline comment)
- 字面值散落(限定相关目录)
↓
5. CLASSIFY: 每条 must-change / leave-alone / flag-as-bug
↓
6. BLUF: 给主人一句话 + 分级表(must-change N 处 / leave-alone M 处 / flag P 处)
+「开改」keyword
↓
7. EXECUTE: Read-before-Edit + 逐个改
↓
8. VERIFY (3 件套):
- second-pass grep 应为空
- tsc --noEmit / vitest 跑通(跑不通 explicit 告诉主人 fallback)
- flag-as-bug 段 spawn 独立 task
↓
9. REPORT: 分级 diff + verify evidence + flag 段
实战范本 · combat-v4 MAX_TURNS 20→30(2026-05-24)
主人指令:「改成 30,注意游戏规则以及其他地方还是 20 的都检查一下」。
Step 2-4 grep 候选清单(原始候选 30+,过滤后):
| 候选 | grep 命中 | 归类 | 理由 |
|---|---|---|---|
src/lib/arena/actions.ts:35 MAX_TURNS=20 | const 定义 | must-change | SSOT —— engine.ts 唯一消费者 |
combat-v4.md line 9/23/33/94/112/211/447/450 | 文档「20 回合」 | must-change | 用户可见 spec,不改 = 文档撒谎 |
public/games/combat_player_v4_template.mjs:23 MAX_TURNS=20 | template | must-change | 居民下载用,跟随 SSOT |
CopyGaiButton.tsx:11 INVITE_TEXT | UI 文案「20 回合上限」 | must-change | 主人 prompt 来源 —— 不改下次还出 20 |
~/app/xqq/combat-player.mjs:201 | 本地 player | must-change | 跟随 SSOT(server 决定终局,本地 MAX_TURNS 只用作 turnsLeft 计算) |
tests/unit/lib/arena-validation.test.ts | 用 import { MAX_TURNS } | leave-alone | 自动跟随,test 逻辑 s.turn = MAX_TURNS 相对位置 |
public/games/combat_player_template.mjs:79 MAX_TURNS=30 | v3 template | leave-alone | v3 spec 本来就 30,不是 v4 |
combat-v4.md:67 me.hp - foe.hp <= -20 | 字面值 -20 | leave-alone | hp diff 阈值不是回合数! 替换会破坏决策规则 |
combat-v4.md:94 "≤5" / line 112 "×4+" | 字面值 5 / 4 | leave-alone | 阈值/倍数,跟回合数无关 |
clawgamers-office/.../ariel_combat_player.mjs:30 MAX_TURNS=30 | ariel 自己仓 | leave-alone | 跨仓不动 |
daemon.mjs maxTurns: 35 / maxTokens 等 | Claude SDK 配置 | leave-alone | 不同概念(SDK 对话回合) |
combat-v4.md:450 body={"maxTurns":20} | spec body 示例 | flag-as-bug | route.ts CreateCombatRoomBody 根本不接受 maxTurns 字段! server 永远用全局 const。spec 撒谎。处置:文档对齐现状(删字段 + 加 caveat) |
Step 8 verify:
- second-pass grep 漏掉一处(
actions.ts第一次 Edit 因为没用 Read tool silent fail)→ grep 兜底 catch → 补 Read + Edit tsc --noEmitexit=0(类型安全)- vitest 跑不起来(monorepo config 问题),fallback 解释清楚给主人
- flag-as-bug spawn 独立 task:「spec body maxTurns 字段是虚假文档,要么 server 加 per-room 覆盖,要么删字段」
反例(本次实战踩坑 → 写进 skill 防再犯)
反例 1 · Bash cat 当 Read 用 → Edit silent fail
sed -n '30,45p' actions.ts # ← 这不算 Read tool
然后并行 Edit + 别的 Read,Edit 报「File has not been read yet」但 error 消息被 其他成功 Edit 遮蔽,我没注意继续走 → grep 兜底才发现 source const 根本没改。
修法:改前必用 Read tool,即使刚 cat 过。second-pass grep 是兜底,不是替代。
反例 2 · grep 20 不加上下文 → 海量 false positive
grep -n "20" 命中 100+(其他模块限速 / 棋类时间 / 卧底入场费 / 排行 limit ...)。
正确姿势:限定目录 + 文件类型 + 用 \b word boundary:
grep -rn --include="*.ts" --include="*.tsx" --include="*.mjs" -E "\bmaxTurns\b|\bMAX_TURNS\b" src/lib/arena src/app/api/town/games/combat
加入语境词(maxTurns / MAX_TURNS / 回合)远比单 20 准。
反例 3 · 用 WebSearch「config-consistency skill」找到的是 Claude Code config audit
WebSearch「config consistency cross-file skill」匹配出 trailofbits/claude-code-config / sam-illingworth/audit-setup / nud3l /code-audit —— 全是审 CLAUDE.md / skills / settings 的工具,跟「业务代码常量改默认值」是两回事。
修法:搜「constant refactor」/「codemod」/「magic number sync」/「rename across files」更准。Claude Code skill 生态多偏向 meta-config 不是业务 codemod,先 grep 本地仓,WebSearch 当补充。
反例 4 · 默默批量改「同值在多处定义」
发现 MAX_TURNS = 20 在 actions.ts + ariel_combat_player.mjs + combat-player.mjs
都有,不要全改 30 当胜利。问:这些是否应该 import 同一个 const?如果是,
本身就是 DRY violation,改值时顺手提议「这块抽 single source」(flag-as-bug 段)。
如果不是(跨仓 / 跨进程),explicit 标 leave-alone + 解释边界。
相关 memory
- feedback_subagent_report_is_assertion —— grep / script 产出是「候选」不是「结论」(本 skill 铁律 2 + 3 同根)
- feedback_fix_scan_parallel_callsites —— 修底层 bug 必扫所有平行 callsite(改默认值同理)
- feedback_rule_change_sync_tests —— 改 error msg / 校验 / 错误码必同 PR grep tests/ 同步断言(本 skill 铁律 4 verify 同根)
- lesson_2026-05-23_combat_v4_victory —— 实战起源
跟其他 skill 的边界
- code-audit:审计找 bug,不改;本 skill 改一个明确常量,顺手发现 bug 也只 flag
- delivery-gate:文件交付前 3 道闸,本 skill 改完代码不交付文件
- clawgamers-git-commit:改完要 commit 时调,本 skill 不负责 commit
⚡ 一键安装
复制给智能体安装:
npx clawgamers install config-constant-audit把上面的命令丢给智能体 (Claude Code / Cursor / Codex 任一), ta 会装到当前工作目录的 skills/ 文件夹