# 自定义 Schema 指南

本指南帮助你创建自定义的 Schema，以适配特定的开发工作流。

---

## 1. 什么是 Schema？

**Schema 是一个 YAML 文件，定义了 SpecPow 工作流中需要产出哪些工件（文档产物）、它们的依赖顺序、模板格式和 AI 指令。**

### Schema 控制什么

| 影响点 | 说明 |
|--------|------|
| 工件清单 | 定义有哪些工件（proposal、specs、design、tasks 等） |
| 依赖顺序 | 工件之间的先后关系（拓扑排序） |
| 模板格式 | 每个工件用什么模板生成 |
| AI 指令 | 生成每个工件时给 AI 的提示词 |
| Apply 前置条件 | 实施前必须完成哪些工件 |

### 内置 Schema 一览

SpecPow 提供了 6 个内置 Schema，覆盖不同的开发场景：

```
spec-driven:      proposal → specs → design → tasks
bug-fix:          proposal → analysis → fix-plan → tasks → regression-test
crud-module:      proposal → specs → design → tasks + api-docs → menu-register
mes-crud-module:  proposal → specs → design → tasks + api-docs → menu-register
prd-to-tech-doc:  analysis → tech-design → tech-tasks
mes-prd-to-code:  parse → confirm-scope → tech-design → tasks → test-gen → code
```

每个 Schema 的差异化体现在**工件流程和依赖关系**上。

### 三层查找机制

Schema 按以下优先级查找（从高到低）：

1. **项目级**: `.specpow/schemas/<name>/` — 仅当前项目可用（最高优先级）
2. **用户级**: `~/.specpow/schemas/<name>/` — 所有项目可用
3. **包内置**: `builtin/schemas/<name>/` — 随 SpecPow 包分发

你可以在项目级或用户级创建自定义 Schema，覆盖内置 Schema 的行为。

---

## 2. 快速开始（5 分钟上手）

本节带你创建一个最小可用的自定义 Schema。

### 2.1 创建目录结构

```bash
# 在项目根目录创建自定义 Schema
mkdir -p .specpow/schemas/my-workflow/templates
```

最终的目录结构：

```
.specpow/schemas/my-workflow/
├── schema.yaml
└── templates/
    └── proposal.md
```

### 2.2 编写最小 schema.yaml

创建 `schema.yaml` 文件，定义 3 个工件的简单工作流：

```yaml
name: my-workflow
description: 我的自定义工作流

apply:
  requires:
    - tasks

artifacts:
  - id: proposal
    name: 提案
    requires: []
    template: proposal.md
    instruction: |
      分析变更的动机、目标和约束。
      重点说明：为什么要做这个变更？期望达到什么效果？
    output: proposal.md

  - id: design
    name: 设计
    requires: [proposal]
    template: design.md
    instruction: |
      基于提案中的目标和约束，制定技术方案。
      重点设计：模块划分、接口定义、数据模型。
    output: design.md

  - id: tasks
    name: 任务
    requires: [design]
    template: tasks.md
    instruction: |
      将设计方案拆分为可独立执行的任务。
      每个任务应包含：具体操作、涉及文件、验证方法。
    output: tasks.md
```

**关键字段说明**：

- `id`: 工件唯一标识（如 `proposal`、`design`、`tasks`）
- `requires`: 依赖的其他工件 ID 数组（空数组 `[]` 表示无依赖）
- `template`: 模板文件名（相对于 `templates/` 目录）
- `instruction`: 给 AI 的指令文本，告诉 AI 如何生成该工件的内容（可选但推荐）
- `output`: 生成的文件路径

**关于 `instruction` 字段**：

`instruction` 是 Schema 的核心功能之一。它告诉 AI **如何思考和生成**每个工件的内容。例如：

```yaml
- id: proposal
  name: 提案
  requires: []
  template: proposal.md
  instruction: |
    分析变更的动机、目标和约束。
    重点说明：为什么要做这个变更？期望达到什么效果？
  output: proposal.md
```

当 AI 生成 `proposal.md` 时，会根据 `instruction` 中的指导来填充模板内容。没有 `instruction`，AI 只能依赖模板结构；有了 `instruction`，AI 能生成更贴合需求的内容。

### 2.3 添加模板文件

在 `templates/` 目录下创建模板文件。模板支持 `{{变量名}}` 占位符，运行时会被替换。

示例 `templates/proposal.md`：

```markdown
# {{changeName}} - 提案

## 背景
{{context}}

## 目标
- 

## 范围
- 包含：
- 不包含：
```

### 2.4 使用自定义 Schema

```bash
# 使用自定义 Schema 创建变更
specpow propose my-change --schema my-workflow
```

预期输出：

```
📋 创建变更提案: my-change

  Schema: my-workflow
  目录: .specpow/changes/my-change

  ✓ 创建工件: proposal → proposal.md
  ✓ 创建工件: design → design.md
  ✓ 创建工件: tasks → tasks.md

✅ 变更提案 "my-change" 创建完成！
```

---

## 3. 完整示例：创建 refactoring Schema

本节通过一个真实的"代码重构"场景，展示如何创建一个完整的自定义 Schema。

### 3.1 场景描述

代码重构工作流需要以下步骤：

1. 先分析现有代码的问题（proposal）
2. 制定重构方案（refactor-plan）
3. 评估影响范围（impact-analysis）— 可选
4. 拆分实施任务（tasks）

### 3.2 完整 schema.yaml

创建 `.specpow/schemas/refactoring/schema.yaml`：

```yaml
name: refactoring
description: 代码重构工作流 — 分析 → 方案 → 影响评估 → 任务拆分

apply:
  requires:
    - tasks

artifacts:
  - id: proposal
    name: 重构提案
    requires: []
    template: proposal.md
    instruction: |
      分析重构的动机、目标和约束。
      重点说明：为什么要重构？期望达到什么效果？
    output: proposal.md

  - id: refactor-plan
    name: 重构方案
    requires: [proposal]
    template: refactor-plan.md
    instruction: |
      制定详细的重构方案，包括：
      - 重构策略（提取方法/移动类/重命名等）
      - 分步实施计划
      - 每步的验证标准
    output: refactor-plan.md

  - id: impact-analysis
    name: 影响分析
    requires: [refactor-plan]
    template: impact-analysis.md
    conditional: true  # 可选工件，可通过 --skip-conditional 或 --include-conditional 控制
    instruction: |
      分析重构对现有代码的影响范围：
      - 受影响的模块和文件
      - 潜在的破坏性变更
      - 需要更新的测试用例
    output: impact-analysis.md

  - id: tasks
    name: 重构任务
    requires: [refactor-plan]
    template: tasks.md
    instruction: |
      将重构方案拆分为可独立执行的任务。
      每个任务应包含：
      - 具体操作
      - 涉及的文件
      - 验证方法
    output: tasks.md
```

**注意**：`impact-analysis` 工件设置了 `conditional: true`，表示它是可选工件。默认情况下会生成，但可以通过命令行参数控制：
- `specpow propose my-change --skip-conditional` — 跳过所有 conditional 工件
- `specpow propose my-change --include-conditional impact-analysis` — 只生成指定的 conditional 工件

### 3.3 模板文件示例

创建 `templates/proposal.md`：

```markdown
# {{changeName}} - 重构提案

## 重构动机
<!-- 当前代码存在什么问题？ -->

## 目标
<!-- 重构后期望达到什么状态？ -->

## 约束
<!-- 有哪些限制条件？（不能改 API、必须保持向后兼容等） -->

## 风险评估
<!-- 重构的潜在风险是什么？ -->
```

创建 `templates/refactor-plan.md`：

```markdown
# {{changeName}} - 重构方案

## 重构策略
<!-- 采用什么重构手法？ -->

## 分步计划

| 步骤 | 操作 | 涉及文件 | 验证标准 |
|------|------|----------|----------|
| 1    |      |          |          |

## 回滚方案
<!-- 如果重构出问题，如何回退？ -->
```

### 3.4 验证和调试

```bash
# 验证 Schema 格式是否正确
specpow schemas --validate refactoring

# 查看所有可用 Schema 列表
specpow schemas
```

**常见错误排查**：

| 错误 | 原因 | 解决方法 |
|------|------|----------|
| 依赖不存在的工件 | `requires` 中的 ID 未在 `artifacts` 中定义 | 检查 `requires` 数组中的 ID 是否都存在 |
| 循环依赖 | A 依赖 B，B 又依赖 A | 画出依赖图，找到并打破循环 |
| 模板文件缺失 | `templates/` 下缺少对应的 `.md` 文件 | 创建缺失的模板文件 |

---

## 4. 参考手册

本节作为查阅型内容，供你在编写 Schema 时随时翻看。

### 4.1 schema.yaml 字段详解

| 字段 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `name` | string | 是 | - | Schema 名称 |
| `description` | string | 否 | - | Schema 描述 |
| `apply.requires` | string[] | 否 | `['tasks']` | 实施前必须完成的工件 ID |
| `artifacts[].id` | string | 是 | - | 工件唯一标识 |
| `artifacts[].name` | string | 是 | - | 显示名称 |
| `artifacts[].description` | string | 否 | - | 工件描述 |
| `artifacts[].requires` | string[] | 否 | `[]` | 依赖的其他工件 ID |
| `artifacts[].template` | string | 否 | - | 模板文件路径（相对于 `templates/`） |
| `artifacts[].instruction` | string | 否 | - | 给 AI 的指令文本 |
| `artifacts[].output` | string | 否 | - | 输出文件路径或 glob 模式 |
| `artifacts[].conditional` | boolean | 否 | `false` | 是否为条件工件，可通过命令行参数控制是否生成 |

### 4.2 依赖关系规则

| 规则 | 说明 | 示例 |
|------|------|------|
| 引用的工件必须存在 | `requires` 中的 ID 必须在 `artifacts` 列表中定义 | ❌ `requires: [specs]` 但没有定义 `id: specs` |
| 不能有循环依赖 | A→B→C→A 会导致拓扑排序失败 | ❌ A requires B, B requires A |
| 可以有多个依赖 | 一个工件可以依赖多个前置工件 | ✅ `requires: [proposal, specs, design]` |
| 可以没有依赖 | 第一个工件通常 `requires: []` | ✅ `requires: []` |

### 4.3 模板语法

模板文件支持以下占位符：

| 占位符 | 说明 |
|--------|------|
| `{{changeName}}` | 变更名称 |
| `{{schemaName}}` | Schema 名称 |
| `{{date}}` | 当前日期 |
| `{{proposal.content}}` | 前置工件 proposal 的内容 |
| `{{project.name}}` | 项目名称 |
| `{{project.root}}` | 项目根目录 |

### 4.4 instruction 字段详解

`instruction` 是 Schema 的核心字段之一，它告诉 AI **如何生成每个工件的内容**。

**作用**：

- 指导 AI 思考方向：告诉 AI 应该关注什么、如何分析
- 控制输出质量：明确的指令能生成更贴合需求的内容
- 上下文注入：可以引用前置工件的内容作为输入

**示例对比**：

❌ **没有 instruction**：
```yaml
- id: proposal
  name: 提案
  requires: []
  template: proposal.md
  output: proposal.md
```
AI 只能依赖模板结构生成内容，可能过于泛化。

✅ **有 instruction**：
```yaml
- id: proposal
  name: 提案
  requires: []
  template: proposal.md
  instruction: |
    分析变更的动机、目标和约束。
    重点说明：
    - 为什么要做这个变更？
    - 期望达到什么效果？
    - 有哪些限制条件？
  output: proposal.md
```
AI 会根据指令生成更有针对性的内容。

**高级用法：引用前置工件**：

```yaml
- id: design
  name: 设计
  requires: [proposal]
  template: design.md
  instruction: |
    基于提案中的目标和约束，制定技术方案。
    参考提案内容：{{proposal.content}}
    
    重点设计：
    - 模块划分
    - 接口定义
    - 数据模型
  output: design.md
```

**最佳实践**：

1. **明确具体**：不要写"生成文档"，而是写"分析 X，说明 Y，列出 Z"
2. **结构化**：使用列表或分点说明，让 AI 更容易理解
3. **引用上下文**：通过 `{{工件名.content}}` 引用前置工件的内容
4. **控制范围**：明确说明"重点是什么"和"不需要什么"

### 4.5 常见错误和排查

| 错误信息 | 原因 | 解决方法 |
|----------|------|----------|
| `Schema "xxx" not found` | Schema 目录不存在或名称拼写错误 | 检查 `.specpow/schemas/<name>/schema.yaml` 是否存在 |
| `Artifact "xxx" requires unknown artifact "yyy"` | `requires` 引用了不存在的工件 ID | 检查 `requires` 数组中的 ID 是否都在 `artifacts` 中定义 |
| `Circular dependency detected` | 工件之间存在循环依赖 | 画出依赖图，找到并打破循环 |
| `Template file not found: xxx.md` | `templates/` 目录下缺少模板文件 | 创建对应的 `.md` 文件 |
| `Invalid schema.yaml format` | YAML 格式错误或字段类型不匹配 | 用 YAML 校验工具检查语法，对照字段详解表 |

### 4.6 进阶用法（可选阅读）

**条件工件**：设置 `conditional: true` 的工件是可选工件，可以通过命令行参数控制是否生成。

**使用方式**：

```bash
# 默认行为：生成所有 conditional 工件
specpow propose my-change

# 跳过所有 conditional 工件
specpow propose my-change --skip-conditional

# 只生成指定的 conditional 工件（逗号分隔）
specpow propose my-change --include-conditional impact-analysis,design
```

**参数说明**：
- `--skip-conditional`：跳过所有 `conditional: true` 的工件
- `--include-conditional <ids>`：只生成指定的 conditional 工件，其他 conditional 工件被跳过
- 两个参数互斥，不能同时使用

**下游依赖处理**：当 conditional 工件被跳过时，下游依赖工件会继续生成（不会被阻塞）。被跳过的工件在 `specpow status` 中显示为 `○ skipped (conditional)`。

---

## 总结

通过本指南，你应该能够：

1. ✅ 理解 Schema 的核心概念（工件、依赖、模板）
2. ✅ 在 5 分钟内创建一个最小可用的自定义 Schema
3. ✅ 参考完整示例创建自己的 Schema
4. ✅ 使用参考手册查阅字段说明和常见错误

**下一步**：尝试创建一个符合你项目需求的自定义 Schema，然后用 `specpow propose <name> --schema <your-schema>` 验证它是否工作正常。


