关于 specification-writing
用于编写实现规格的写作技能,为 Agent 和维护者提供自主启动工作的必要背景。它强调明确现状与目标、基于证据的调研、开放问题、决策记录,以及将长期决策保存在规格之外。
主要功能
- 01以背景为核心的规格编写指引
- 02一句话主题与维护者时间要求
- 03基于证据的调研和决策规范
- 04架构与 API 变更文档
- 05实现计划与规格生命周期
适用场景
- 01编写供自主实现的功能规格
- 02记录调研结果和未决设计问题
- 03规划架构或 API 变更
- 04工作完成后归档规格
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ epicenterhq/ epicenter --skill specification-writing安装后在 Claude Code 中输入/specification-writing确认已加载。
附带文件references/decision-hygiene.md
详细介绍
规范编写
文案部分请遵循 writing-voice。解释归属关系或流程时,先给出有用的模型,再结合相关边界说明,只保留实现所需的图示和文案。
规范为 Agent 或维护者提供自主实现功能所需的上下文。目标不是详尽描述一切,而是提供足够的依据,证明方向可信,并为实现者提供明确的起点。
规范是进行中的脚手架,不是持久记录。 它用于规划工作,并在工作进行期间记录研究内容。规范不具权威性,工作完成后也不应继续保留。持久决策记录在 docs/adr/,共享词汇记录在 docs/CONTEXT.md,当前状态记录在 docs/reference/ 和代码中。编写规范时,如果某项关键决策逐渐明确,应立即将其记录为 docs/adr/ 中的 Proposed ADR,并在规范中引用;不要把它埋在规范里,留待以后“整理”。工作完成后,请按照规范生命周期退役该规范。ADR 的接受流程请遵循 ADR 指南;实现完成并不意味着可以更改状态。历史记录由 Git 和 docs/spec-history.md 保留。
注意:本指南使用
[PLACEHOLDER]标记需要填写的内容。代码块展示的是模板;请将所有方括号中的内容替换为功能的具体细节。
参考资料
根据规范涉及的决策范围,按需阅读以下资料:
- 如果要编写的规范涉及诸多权衡、迁移清理,或“为保持一致性而保留”之类的决策,请阅读 references/decision-hygiene.md。
- 如果要编写的规范涉及架构、API、生命周期或归属关系变更,请阅读 ../pull-request/references/body-patterns.md,了解变更前后的示例和视觉表达节奏。
核心理念
规范应当:
- 提供上下文,而非指令:说明“为什么”和“是什么”,让实现者自行决定“如何做”
- 记录研究过程,而非结论:展示探索过什么、现有内容是什么、还缺少什么
- 保留开放问题:开放问题部分是规范的优点,不是缺陷
- 支持自主实现:阅读规范的 Agent 应当派生子 Agent 来验证并扩展内容
- 尊重维护者的时间:在要求读者了解历史、附录或实现日志之前,先明确展示当前要走的路径
- 避免流程作秀:只有当某个章节会影响实现或评审时才加入,不要仅仅因为模板列出了它就添加
好的规范是起点,而不是照着执行的脚本。
在规划章节之前,先应用 one-sentence-test。 如果无法用一个具体句子说明规范的主题,说明设计尚不连贯。这就是需要得出的结论,规范也尚未准备就绪。
维护者时间约定
规范的首屏必须回答:
What is this?
Is it active, implemented, superseded, or historical?
What is the current shape?
What is the target shape?
What proves the change is done?
允许编写较长的规范。深入的工作值得详尽记录。原则不是“超过 N 行就拆分”,而是不要让同一位读者同时承担彼此冲突的任务。
如果一份文档混合了以下内容,请拆分文档,或添加一个简短的当前重点部分:
- 北极星架构与具体执行步骤。
- 历史争论与当前实现路径。
- 规范内容与交接提示词。
- 有用但并非启动工作所必需的附录或台账。
如果决定将所有内容保留在同一个文件中,请在靠近文档开头处添加“如何阅读本规范”部分:
Read first:
One Sentence
Current State
Target Shape
Implementation Plan
Verification
Read if changing the architecture:
Design Decisions
Rejected Alternatives
Edge Cases
Historical only:
Implementation Notes
Superseded Decisions
Execution Prompts
读者应能在一分钟内了解当前事实,五分钟内掌握模型,十五分钟内了解执行路径。
规范放置位置
所有实现规范都放在根目录的 /specs/ 中。不要在 apps/ 或 packages/ 下创建嵌套规范。
新规范命名为 specs/YYYYMMDDThhmmss-feature-name.md:使用本地时间戳和 kebab-case 功能名称。
提示词和交接文档可以放在规范旁边,并使用 .prompt.md、.handoff.md 或 .execute.md 等明确后缀。它们应链接回规范的权威版本;除非后缀表明它们是当前实施计划,否则不应将其视为当前实施计划。
决策规范
对每项重要决策进行分类:
| 类别 | 判定依据 | 规则 |
|---|---|---|
| 1 | 证据 | 通过源码、测试或版本检查进行验证。 |
| 2 | 设计一致性 | 始终如一地遵循规范的核心主张。 |
| 3 | 约束下的取舍 | 有意做出选择并写明约束。 |
在做出任何“保留”决定之前,先问:“如果它本来不存在,我还会添加它吗?”
- 如果会,记录使用场景。
- 如果不会,但移除会造成不必要的改动,则在决策日志中将其记录为类别 3 的保留项。
- 如果不会,而且移除成本很低,就立即删除。
不要用设计一致性掩盖证据问题。删除旧路径前先进行验证。有关示例和失败模式,请阅读 references/decision-hygiene.md。
文档结构
并非每份规范都需要包含所有章节。小型功能可以省略研究发现。迁移规范则可以重点关注边缘情况。请酌情判断。
标题(必需)
# [Feature Name]
**Date**: [YYYY-MM-DD]
**Status**: Draft | In Progress
**Owner**: [Name/team responsible for decisions]
**Branch**: [optional: branch name if work has started]
**Supersedes**: [optional: previous spec paths]
**Superseded by**: [optional: later spec path]
一句话描述
每份规范都需要在概述前写一句具体的话。这是维护者的核心依据。
## One Sentence
[One sentence naming the new shape and the boundary it changes.]
概述
最多一段。描述功能的作用,不要推销它。
## Overview
[One to two sentences describing what this feature adds or changes and what it enables. Be specific about the capability, not vague about benefits.]
动机
按 当前状态、问题、期望状态 的顺序组织内容。
## Motivation
### Current State
[Show actual code or configuration demonstrating how things work TODAY. Use real code blocks, not prose descriptions.]
This creates problems:
1. **[Problem Title]**: [Specific explanation of what breaks or is painful]
2. **[Problem Title]**: [Specific explanation of what breaks or is painful]
### Desired State
[Brief description of what the target looks like. Can include a code snippet showing the ideal API or structure.]
研究发现
规范的亮点所在。记录你实际发现的内容,而非假设。
## Research Findings
### [Topic Researched]
[Description of what you investigated and methodology]
| [Category] | [Dimension 1] | [Dimension 2] |
| ------------- | -------------- | ---------------- |
| [Project/Lib] | [What they do] | [Their approach] |
| [Project/Lib] | [What they do] | [Their approach] |
**Key finding**: [Your main discovery, for example that no standard exists, or that everyone does X]
**Implication**: [What this means for your design decisions]
包括:
- 类似项目的做法(对比表)
- 搜索过但未找到的内容(“不存在既定模式”)
- 查阅过的文档链接或参考资料
设计决策
使用表格以便追溯。每项重要决策都应注明类别和理由。
决定架构、所有权、API 形式,或是否采用值得避免反复讨论的替代方案等关键决策(类别 2 的一致性决策或类别 3 的取舍),应记录在 ADR 中,而不应只写在此表中。一旦决策成形,就在 docs/adr/ 中将其写为 Proposed ADR,并在表格的理由栏中注明 ADR 编号。这样,表格就能将规范中的决策追溯至其长期归档位置;即使规范被删除,ADR 也会保留。
## Design Decisions
| Decision | Class | Choice | Rationale |
| ------------------- | ----------- | ---------------- | ------------------------------- |
| [Decision point] | 1 evidence | [What you chose] | [Source, test, or version checked] |
| [Decision point] | 2 coherence | [What you chose] | [How this follows the thesis] |
| [Decision point] | 3 taste | [What you chose] | [Constraint and trade-off] |
| [Deferred decision] | Deferred | Deferred | [Why it is deferred and what would bring it back] |
架构
图示优于文字说明。优先使用围栏文本图、文件树、路由表以及前后对照块。默认使用简单的 ASCII 图,因为它们编写快速且易于编辑。只有当精致图示值得付出额外成本时,才使用制表符绘图字符。
## Architecture
[Describe what the diagram shows]
[caller]
-> [boundary or route]
-> [policy or validation]
-> [storage or primitive]
对于多步骤流程:
Step 1: [Step name]
[What happens in this step]
Step 2: [Step name]
[What happens in this step]
目录(引入一组基元时)
如果规范引入一组相互关联的新基元(列类型、操作变体、错误类型、修饰符方法等),应以目录的形式呈现:使用紧凑的代码块列出所有基元,并为每项附上一行注释;之后仅针对需要详细说明的基元设置详细章节。
目录让读者在深入了解单个基元前,就能一眼浏览完整的功能面。
## The field.* catalog
```ts
field.string<TBrand?>(s?) // TEXT (TBrand for branded strings)
field.number(s?) // REAL
field.integer(s?) // INTEGER
field.boolean() // INTEGER 0/1
field.select([value]) // TEXT (single-element set)
field.select([...]) // TEXT + CHECK constraint
field.json<S extends TSchema>(schema) // TEXT JSON-encoded, schema required
nullable(inner) // Type.Union([inner, Type.Null()]) (standalone)
field.datetime(s?) // RFC 3339 string, branded DateTimeString
field.string<IanaTimeZone>() // IANA zone string, branded IanaTimeZone
```
Every primitive justified by N+ existing call sites in the audit.
### What was considered and rejected
| Candidate | Why rejected |
|---|---|
| `field.id()` | Subsumed by `field.string<IdBrand>()` + a co-located `generate*` factory |
| `field.array(of)` | Subsumed by `field.json(Type.Array(of))` |
| Declaring `_v` as a column | Library-managed; positional in `defineTable(v1, v2, ...)` |
“已排除候选项”表格通常与目录本身一样有用:它能说明实施者不应添加什么以及原因,让读者确信功能面经过了精简。
调用点(真实代码的变更前/后对比)
当规范变更面向使用者的 API 时,请展示至少 2-3 个真实调用点的翻译,不要编造示例。真实代码中的调用能体现抽象方案容易遗漏的语义变化。
先在代码库中找到实际用法。先按原样展示“变更前”代码,并标注文件:行号,然后展示“变更后”的翻译。对不明显的映射,请附上简短注释。
## Call sites: before and after
### honeycrisp notes table
**Before** (`apps/honeycrisp/src/lib/workspace.ts:78`):
```ts
const notesTable = defineTable(
type({ id: NoteId, title: 'string', _v: '1' }),
type({ id: NoteId, title: 'string', wordCount: 'number | undefined', _v: '2' }),
).migrate(...)
```
**After**:
```ts
const notesTable = defineTable(
{ id: field.string<NoteId>(), title: field.string() },
{
id: field.string<NoteId>(),
title: field.string(),
wordCount: nullable(field.number()),
},
).migrate(({ value, version }) => {
switch (version) {
case 1: return { ...value, wordCount: null };
case 2: return value;
}
});
```
**Semantic shift to flag**: rows previously stored with `wordCount` key absent will now read as `null` instead of `undefined`. Affects app code doing `if (row.wordCount === undefined)`. Also: `_v` no longer appears in the column record; it is library-managed and stripped from returned rows.
“需要标记的语义变化”说明至关重要:实现者需要根据这些说明在整个代码库中搜索并批量修改代码。
实施计划
分阶段制定计划。使用复选框跟踪进度。第 1 阶段应详细说明;后续阶段可以写得粗略一些(实现者会进一步完善)。
## Implementation Plan
### Phase 1: [Phase Name]
- [ ] **1.1** [Specific, atomic task]
- [ ] **1.2** [Specific, atomic task]
- [ ] **1.3** [Specific, atomic task]
### Phase 2: [Phase Name]
- [ ] **2.1** [Higher-level task the implementer will break down]
- [ ] **2.2** [Higher-level task]
平稳过渡的阶段顺序:构建、验证、移除
如果规范要替换旧代码路径,请规划使用者如何切换到新路径、如何验证替换有效,以及如何在弃用旧路径前确保能够恢复。对于可分离和不可分离的替换,请参考 rethink 的平稳过渡指南。应由依赖关系决定阶段,而不是强行规定固定的阶段数。
边缘情况
列出可能打破假设或需要特殊处理的场景。
## Edge Cases
### [Scenario Name]
1. [Initial condition]
2. [What happens]
3. [Expected outcome or "See Open Questions"]
### [Scenario Name]
1. [Initial condition]
2. [What happens]
3. [Expected outcome]
待解决问题
本节表示“由你决定”。请给出建议,但不要直接定论。
## Open Questions
1. **[Question about an unresolved design decision]**
- Options: (a) [Option A], (b) [Option B], (c) [Option C]
- **Recommendation**: [Your suggestion and why, but explicitly leave it open]
2. **[Another unresolved question]**
- [Context about why this is uncertain]
- **Recommendation**: [Suggestion or "Defer until X"]
相邻工作
大多数规范不需要本节;规范是执行主线,而非文件白名单。当相邻工作很重要时,只写实现者需要的信息:延后事项(当前不要求,但以后允许做)和顺带处理的事项(如果发现且有依据,允许一并修复)。
## Adjacent Work
- [Decision or feature]: [Why it is not required now, and what would bring it back.]
- [Issue or cleanup]: [Why it is not required, but may be fixed if discovered and grounded.]
决策日志
本节仅用于第 3 类保留项。每条记录都必须说明约束条件和重新审视的触发条件。
## Decisions Log
- Keep `[name]`: [constraint or trade-off].
Revisit when: [specific signal that would make the keep decision worth re-opening].
成功标准
如何判断工作已完成?使用复选框进行验证。
## Success Criteria
- [ ] [Specific, verifiable outcome]
- [ ] [Specific, verifiable outcome]
- [ ] [Tests pass / build succeeds / docs updated]
参考资料
将修改或查阅的文件。
## References
- `[path/to/file.ts]` - [Why this file is relevant]
- `[path/to/pattern.ts]` - [Pattern to follow or reference]
如果规范规定得过于细致,Agent 就会盲目照做。如果规范过于含糊,Agent 就会无所适从。理想的平衡是:提供足够的背景以便着手,同时留有足够的空间让实现者自主完成实现。