<!-- title: 构建只读沙箱的变更影响面技能 -->

> 本教程面向已跑通首个 Claude Code 项目、想系统掌握自定义 Skill 开发的工程师。学完后你将独立构建一个**只读、可复用、带内置规则库**的"代码变更影响面分析 Skill"，并真正理解 `allowed-tools` 沙箱、`model`/`effort` 分级、`${CLAUDE_SKILL_DIR}` 资源定位与渐进式披露四项核心机制。

---

## 一、为什么要专门做一个"变更影响面分析"Skill

在日常开发里，最危险的往往不是写新代码，而是**改一行老代码却不知道波及了谁**。一次看似无害的重命名、一次签名调整、一次常量改动，可能悄悄打断了十公里外的调用方，而单元测试又恰好没覆盖到那条路径。

把"分析这次改动会影响什么"这件事固化成一个 **Skill**，而不是每次都靠人工提示词"祈祷"，有三个工程价值：

1. **可复用**：团队成员输入 `/blast-radius HEAD~1` 即可得到一份结构化影响面报告，无需每人重写提示词。
2. **可约束**：通过 `allowed-tools` 把这个 Skill **关进只读笼子**——它只能读、只能搜、只能跑只读 git 命令，绝不会误删或误改代码。分析类任务本就该只读。
3. **可分级**：通过 `model` + `effort` 给"读代码+追踪调用"这种中等难度任务匹配合适的算力，避免动辄用最贵模型烧钱。

本教程与"渐进式披露构建分层 Skill""触发优先"等已有主题不重叠——它的核心是**把一个真实的高频开发痛点，用 Skill 的安全沙箱机制工程化解决**，并顺带讲透几项官方文档里零散、社区里常被误用的 frontmatter 字段。

---

## 二、前置条件

| 项目 | 要求 |
|------|------|
| Claude Code 版本 | v2.1.154 或更高（支持当前 `allowed-tools`/`effort` 语义） |
| 操作环境 | 已能正常启动 `claude` 交互会话 |
| 示例代码库 | 任意一个 **git 仓库**（有若干次提交、有 `import`/调用关系即可，Node 或 Python 项目最佳） |
| 基础概念 | 知道 `.claude/` 目录的作用，会读 YAML frontmatter |

如果你还没有合适仓库，临时建一个：

```bash
mkdir demo-svc && cd demo-svc && git init -q
echo 'export function calcTax(base){ return base*0.13 }' > tax.js
echo 'import {calcTax} from "./tax.js"; export function checkout(items){ return items.reduce((s,i)=>s+calcTax(i.price),0) }' > cart.js
git add -A && git commit -qm "init demo service"
```

---

## 三、Skill 目录骨架

Claude Code 的 Skill 存放路径有三层，优先级从高到低：

- 项目级：`.claude/skills/<name>/SKILL.md`（随仓库走，团队共享）
- 用户级：`~/.claude/skills/<name>/SKILL.md`（个人跨项目）
- 插件级：随插件包分发

我们做的是团队共享工具，放**项目级**。一个完整的 Skill 不只有一个文件，而是"瘦入口 + 胖参考库"的目录：

```
.claude/skills/blast-radius/
├── SKILL.md                       # 瘦入口：frontmatter + 何时用 + 工作流骨架
└── references/
    └── impact-heuristics.md       # 胖参考库：追踪启发式规则，按需加载
```

**预期结果**：目录创建好后，在 `claude` 会话里输入 `/` 应能看到 `blast-radius` 出现在补全列表中（前提是 `disable-model-invocation` 没开，且 `description` 非空）。

**常见问题**：

- *Skill 没出现在 `/` 补全里*？九成是 frontmatter 没写 `description`，或 YAML 缩进/破折号写错。`description` 会被截断到约 250 字符用于列表展示，务必写清"做什么 + 何时用"。
- *目录名和 `name` 字段不一致*？以目录名为准（`name` 字段可省略，默认取目录名）。建议两者保持一致，避免混淆。

---

## 四、动手写 SKILL.md：四个关键字段

### 步骤 1：frontmatter 配置

```yaml
---
name: blast-radius
description: 分析一次代码变更（git diff/commit 或指定文件）的影响面，列出受影响的文件、函数、调用方与回归测试。只读，绝不修改代码。当用户问"这次改动会影响什么/波及哪些模块/要不要补测试"时使用。
argument-hint: "[commit范围或文件路径，如 HEAD~1 / src/foo.js]"
allowed-tools: Read, Grep, Glob, Bash
model: sonnet
effort: medium
---
```

逐字段解释（这是本教程的核心知识）：

**① `allowed-tools: Read, Grep, Glob, Bash` —— 只读沙箱**

这是整个 Skill 安全性的基石。它是一个**白名单**：Skill 处于激活状态时，模型只能调用这四个工具。

- `Read` / `Grep` / `Glob`：读文件、搜代码、按 glob 找文件——分析任务的全套只读武器。
- `Bash`：用来跑 `git show`、`git diff`、`git log` 这类只读 git 命令。
- **故意不写** `Write`、`Edit`、`NotebookEdit`：分析类任务本就不该写文件；不放进白名单，就从机制上杜绝了"顺手帮你改了几个文件"的越权行为。

关键认知：`allowed-tools` 是 Skill 级别的**收窄**，不会扩大你在 `settings.json` 里的总权限。也就是说，即使你全局允许了 `Edit`，这个 Skill 在它生命周期内依然写不了文件——这正是我们想要的"最小权限原则"。

**② `model: sonnet` —— 给中等任务配中等算力**

模型别名的官方语义：`sonnet` 对应当前最新的 Sonnet（API 上即 Sonnet 4.6），适合日常编码任务。读代码、追调用链、写影响面报告这类任务，`sonnet` 的速度与成本远优于 `opus`，而质量足够。

何时改用 `opus`（最强推理，对应 Opus 4.8）？只有当你的代码库**跨多语言、调用链极深、需要强逻辑推断**时才值得。何时用 `haiku`？纯机械的"列出 diff 文件名"可降级，但影响面分析通常需要推理，不建议。

> 模型解析优先级（高→低）：会话内指定 > Skill 的 `model` frontmatter > 主会话模型。所以 Skill 里写 `model: sonnet` 会**覆盖**主会话设置，专门为这个任务降档省钱。

**③ `effort: medium` —— 推理深度旋钮**

取值 `low|medium|high|max`（`max` 仅 Opus 可用）。`medium` 让模型在"够用"的思考预算内完成，避免对每个调用关系都做 `max` 级深挖。这是除 `model` 之外第二根成本杠杆。

**④ `argument-hint` —— 补全友好性**

斜杠补全时会显示 `/blast-radius [commit范围或文件路径]`，让调用者一眼知道该传什么。配合正文里的 `$ARGUMENTS` 接收参数。

**预期结果**：保存后，`/blast-radius` 在补全里带有参数提示；调用 `/blast-radius HEAD~1` 时，`$ARGUMENTS` 即为字符串 `HEAD~1`。

**常见问题**：

- *想让它连 Bash 都不能跑（连 git 命令也不允许）*？把 `allowed-tools` 改成 `Read, Grep, Glob`，并把 git 信息改为让用户在参数里直接贴 diff 文本——代价是体验变差。安全与便利在此权衡。
- *`allowed-tools` 写了但模型还是调了别的工具*？确认你确实是通过 Skill 激活（而非主会话直接发指令）。Skill 未激活时，`allowed-tools` 不生效。

---

### 步骤 2：写正文——瘦入口与资源定位

SKILL.md 正文应保持精炼（建议 500 行以内），它**每次激活都会被读进上下文**，所以越短越省 token。详细规则下沉到 bundled 参考文件。

```markdown
# 变更影响面分析（Blast Radius）

## 何时使用
用户给出一次改动（git 范围、commit、文件路径），想知道它会波及哪些模块、
哪些函数、哪些测试可能回归。本技能**只读**，绝不修改任何代码。

## 输入
- `$ARGUMENTS`：commit 范围（如 `HEAD~1`、`main..feature`）或文件路径。
- 若 `$ARGUMENTS` 为空，先用 `git diff --name-only` 列出工作区改动，
  再询问用户确认范围。

## 工作流
1. 用 Bash 跑 `git show --stat $ARGUMENTS` / `git diff $ARGUMENTS`，
   拿到**变更文件清单**与**变更行**。
2. 用 Read 读取每个变更文件，识别**被改动的符号**（函数、类、导出常量、类型）。
3. 用 Grep 反向追踪：对每个被改符号，搜索整个仓库的引用与调用方，
   构建"变更 → 直接依赖 → 间接依赖"两层波及图。
   详细追踪启发式见 `references/impact-heuristics.md`，务必先读它再追踪。
4. 用 Glob + Grep 定位**回归测试**：按 `*.test.*`/`*.spec.*`/`test_*` 模式，
   找到覆盖了被改符号或其调用方的测试文件，标记"已覆盖/未覆盖"。
5. 输出**结构化影响面报告**（见下文模板）。

## 报告模板
## 影响面报告：$ARGUMENTS
- 变更摘要：<文件数>个文件，<符号数>个符号
- 直接受影响：<文件:符号> 列表
- 间接波及：<调用链>
- 回归测试：已覆盖 N 个 / **缺口** M 个（标红）
- 风险评级：低/中/高（依据：是否有未覆盖测试 + 波及面大小）

## 资源定位
- 追踪规则库：`${CLAUDE_SKILL_DIR}/references/impact-heuristics.md`
- 资源定位符 `${CLAUDE_SKILL_DIR}` 在运行时展开为本 SKILL.md 所在目录的绝对路径，
  因此**写死路径或用相对路径都不可靠**——一律用 `${CLAUDE_SKILL_DIR}` 拼接。
```

两个要点：

**动态变量 `${CLAUDE_SKILL_DIR}`**：运行时被替换为 Skill 目录绝对路径，是访问 bundled 文件的**唯一可靠方式**。此外还有 `$ARGUMENTS`（全部入参）、`$N`/`$ARGUMENTS[N]`（第 N 个位置参数，0 基）、`${CLAUDE_SESSION_ID}`（当前会话 ID）可用。

**渐进式披露**：正文只写"做什么"，把"怎么精确追踪调用方"这种大段细节放进 `references/impact-heuristics.md`，并明确指示"务必先读它再追踪"。这样 99% 的简短分析只需瘦入口进上下文，复杂分析才加载胖参考库，token 消耗随难度自适应。

**预期结果**：调用 `/blast-radius HEAD~1`，模型会先读 `references/impact-heuristics.md`，再执行五步工作流。

**常见问题**：

- *模型没去读参考文件就直接分析了*？检查正文是否给了**明确的触发指令**（"务必先读它再追踪"）。模型不会"猜"你希望它读哪个文件——指令越显式越可靠。
- *`${CLAUDE_SKILL_DIR}` 没展开成路径*？确认拼写与版本（旧版本可能不支持）。退路是用相对路径 `references/impact-heuristics.md`，但跨目录调用时不可靠。

---

## 五、写 bundled 参考库：把"专家经验"固化

在 `.claude/skills/blast-radius/references/impact-heuristics.md` 写入追踪启发式。这是整个 Skill 的"大脑"——它把"高级工程师如何判断影响面"的经验编码下来：

```markdown
# 影响面追踪启发式

## 1. 识别被改符号（按语言）
- JS/TS：`export (function|const|class)`、`function name(`、`class Name`
- Python：`^def name`、`^class Name`、模块级赋值（可能是常量/配置）
- 提取符号名后，进入第 2 步用 Grep 反查。

## 2. 反向追踪调用方（两层）
- 第一层（直接）：`grep -rn "符号名"` 找到所有引用点。
- 第二层（间接）：对第一层结果中的函数，再 `grep` 其函数名，
  判断是否被更上层调用。**通常两层足够**，更深需人工判断。
- 注意区分**定义点**与**引用点**：用 `def|function|class` 前缀排除定义。

## 3. 测试覆盖判定
- 用 Glob 找 `**/*.test.{js,ts}`、`**/*.spec.{js,ts}`、`**/test_*.py`、`**/*_test.go`。
- 对每个测试文件 Grep 被改符号：命中=已覆盖，未命中=缺口。
- **缺口处的调用方**是最高风险点，优先在报告中标红。

## 4. 风险评级规则
- 高：被改符号是公共 API/导出，且存在未覆盖测试的调用方。
- 中：被改符号有内部调用方，测试基本覆盖。
- 低：仅私有实现改动，且测试全覆盖。
```

**设计哲学**：把易变的"规则"与稳定的"流程"分离。SKILL.md 的五步工作流很少变，而追踪规则会随团队语言栈演进——分文件后，更新规则不必动入口文件，版本可追溯。

---

## 六、可选进阶：用 `context: fork` 做隔离分析

当仓库巨大、分析耗时较长，你不想让大段 grep 结果污染主对话上下文时，可在 frontmatter 加两行：

```yaml
context: fork        # 在隔离的子智能体上下文里运行
agent: Explore       # 复用 Explore（只读搜索）子代理类型
```

`context: fork` 让 Skill 在一个独立子智能体里执行，结束后**只把最终报告**返回主会话——中间成百上千行 grep 输出不占用主上下文窗口。配合 `agent: Explore`（只读搜索专用子代理），既隔离又复用现成的搜索型系统提示。

> 注意：`fork` 模式下子智能体无法写文件、无法推进主会话状态，纯产出文本。这对"只读分析报告"场景是完美匹配，但对需要落盘改文件的任务则不适用——本教程的影响面分析正好契合。

---

## 七、端到端实战：跑通一次真实分析

在你的 `demo-svc` 里制造一次"危险改动"：

```bash
# 故意改动一个被多处依赖的公共函数签名
cat > tax.js <<'EOF'
export function calcTax(base, region = 'CN'){ return base * (region==='CN' ? 0.13 : 0.0) }
EOF
git add -A && git commit -qm "tax: support multi-region"
```

现在在 `claude` 会话里：

```
/blast-radius HEAD~1
```

**预期输出（影响面报告，节选）**：

```
## 影响面报告：HEAD~1
- 变更摘要：1 个文件，1 个符号（calcTax）
- 变更内容：calcTax 签名新增可选参数 region，逻辑变更
- 直接受影响：cart.js:checkout —— 调用 calcTax(i.price)，签名兼容（可选参数）
- 间接波及：任何调用 checkout 的上层（本次仓库内无）
- 回归测试：仓库内无 *.test.* 文件 —— **测试缺口：checkout 未被覆盖**
- 风险评级：中（签名向后兼容，但关键路径无测试保护）
- 建议：为 checkout 补单测，覆盖多 region 税率场景
```

注意模型**全程只用** Read/Grep/Glob/Bash，没有尝试"顺手帮你改代码或建测试文件"——这就是 `allowed-tools` 沙箱在起作用。它给出了判断与建议，但把"要不要补测试、怎么补"的决定权交还给你。

**常见问题**：

- *报告把 `git log` 的 commit 信息也当成了代码影响*？在 `impact-heuristics.md` 的第 2 步里加约束："只在源码文件中搜索，排除 `.git/`、`node_modules/`、`dist/`"。
- *分析很慢*？把 `effort` 从 `medium` 降到 `low`，或开启 `context: fork` 避免主上下文被中间结果拖慢。

---

## 八、整体工作流架构

```mermaid
flowchart TD
    A["用户: /blast-radius HEAD~1"] --> B["激活 Skill"]
    B --> C{"allowed-tools 沙箱生效\n只读: Read/Grep/Glob/Bash"}
    C --> D["Bash: git diff 拿变更文件与行"]
    D --> E["Read: 读取变更文件\n识别被改符号"]
    E --> F["加载 references/impact-heuristics.md\n按需: 渐进式披露"]
    F --> G["Grep: 反向追踪调用方\n两层波及图"]
    G --> H["Glob+Grep: 定位回归测试\n标记覆盖/缺口"]
    H --> I["输出结构化影响面报告\n风险评级"]
    I --> J{"context: fork?"}
    J -- 是 --> K["仅报告回传主会话\n中间结果隔离"]
    J -- 否 --> L["直接呈现在主会话"]
    K --> M["用户决策: 是否补测试/回滚"]
    L --> M
    style C fill:#ffe4e1,stroke:#c44
    style F fill:#e6f3ff,stroke:#36c
```

---

## 九、调试与迭代清单

- **看不到 Skill**：检查 `description` 非空、frontmatter YAML 合法、路径在 `.claude/skills/<name>/SKILL.md`。
- **不按规则追踪**：在正文用祈使句显式要求"先读 references 再追踪"，不要留给模型推断。
- **越权写文件**：确认 `allowed-tools` 白名单不含 `Write/Edit`，并确认是通过 Skill 激活而非裸指令。
- **太贵太慢**：`model: sonnet` + `effort: medium` 是甜点；大型仓库加 `context: fork`。
- **触发不准**：精炼 `description` 的"何时用"部分，加入用户真实问法（"会影响什么""波及哪些模块""要不要补测试"）。

---

## 十、延伸阅读

- 官方 Skill frontmatter 全字段（`disable-model-invocation`/`user-invocable`/`paths`/`hooks`/`shell`）：[code.claude.com 文档](https://code.claude.com/docs/en/sub-agents)
- 模型别名与 `effort`/`opusplan` 语义：[Model configuration](https://code.claude.com/docs/en/model-config)
- 权限与沙箱机制（allow/deny 规则、四种权限模式）：[Claude Code Permissions Guide](https://www.claudedirectory.org/blog/claude-code-permissions-guide)
- 把 `allowed-tools` 收窄与 `settings.json` 总权限的关系：[AgentPatterns Skill Frontmatter Reference](https://www.agentpatterns.ai/tool-engineering/skill-frontmatter-reference/)
- Skill 编写全流程（SKILL.md 格式、参数、工具权限）：[Sherlock — How to Write Skills for Claude Code](https://sherlock.xyz/post/how-to-write-skills-for-claude-code-and-cowork)

---

## 参考来源

1. [Claude Code Skill Frontmatter: Every YAML Option Explained](https://allahabadi.dev/blogs/ai/claude-code-skills-frontmatter-complete-guide/) — 15 个 frontmatter 字段逐一详解（含 `allowed-tools`/`model`/`effort`/`context: fork`）
2. [Claude Code Frontmatter Reference (GitHub)](https://github.com/ThibautBaissac/rails_ai_agents/blob/main/docs/claude-code-frontmatter-reference.md) — 基于 code.claude.com 官方文档整理的 Skills/Agents/Commands frontmatter 与 `$ARGUMENTS`/`${CLAUDE_SKILL_DIR}` 变量速查
3. [Model configuration — Claude Code Docs](https://code.claude.com/docs/en/model-config) — `model` 别名（sonnet/opus/haiku/best/fable）与 `effort` 取值的官方语义与解析优先级
4. [SKILL.md Frontmatter Reference — AgentPatterns.ai](https://www.agentpatterns.ai/tool-engineering/skill-frontmatter-reference/) — `allowed-tools` 收窄语义、`context: fork` 隔离执行与 `agent` 子代理类型搭配
5. [Claude Code Permissions (2026)](https://www.claudedirectory.org/blog/claude-code-permissions-guide) — Skill 级 `allowed-tools` 与全局权限/沙箱的边界关系

---

## 训练营适用性评估

- **适用类型**：面向**已入门 Claude Code**、希望进入"自定义工具开发"模块的中级训练营（企业研发提效方向最佳）。不适合零基础（需先掌握首个项目跑通与 `.claude/` 目录概念）。
- **预计教学时长**：**1.5–2 小时**。其中概念铺垫（`allowed-tools`/`model`/`${CLAUDE_SKILL_DIR}`）约 30 分钟，动手构建 Skill 约 50 分钟，端到端实战与调试约 30–40 分钟。
- **可裁剪为练习题**：①给定仓库，让学生把 `allowed-tools` 故意写宽（含 `Edit`），观察越权行为；②把 `effort` 在 `low`/`medium`/`max` 间切换，对比报告质量与耗时；③要求学生为本团队的主语言扩展 `impact-heuristics.md`。
- **延伸衔接**：本教程是"安全沙箱化 Skill"的范本，可自然衔接后续"把 Skill 发布成可分发插件""用 hooks 给 Skill 加生命周期校验"两讲。

掌握 `allowed-tools` 把 Skill 关进只读笼子、用 `${CLAUDE_SKILL_DIR}` 定位内置资源、以 `model`/`effort` 调节算力，你便拥有了构建**安全、可复用、成本可控**的团队级 Skill 的全部基础。

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
