文件位置
openspec/config.yaml(也支持 .yml 扩展名)
核心解析入口:src/core/project-config.ts
文件由 readProjectConfig(projectRoot) 函数解析,流程如下:
- 查找文件 --- 通过
resolveConfigFilePath()依次尝试openspec/config.yaml和openspec/config.yml,先用哪个读哪个。 - YAML 解析 --- 用
yaml库解析为对象。 - 字段级校验 --- 使用 Zod schema 逐字段安全解析(
safeParse),某个字段坏了不影响其他字段。
配置结构(Zod Schema 定义)
typescript
const ProjectConfigSchema = z.object({
schema: z.string().min(1), // 工作流 schema 名称
context: z.string().optional(), // 项目上下文(≤50KB)
rules: z.record(z.string(), z.array(z.string())) // artifact 级规则
.optional(),
store: z.string().optional(), // 声明式默认 store
})
// references 字段由 parseDeclarationList() 独立解析
各字段校验规则
| 字段 | 类型 | 校验逻辑 |
|---|---|---|
schema |
非空字符串 | 缺失/空串跳过,后续 fallback 到默认值 'spec-driven' |
context |
字符串 | 上限 50KB(UTF-8 字节),超限忽略并告警 |
rules |
{ artifactId: [string] } |
空字符串规则被过滤 |
references |
string[] 或 {id, remote}[] |
去重、无效项跳过(有告警) |
store |
字符串 | 仅 config-only 目录下生效 |
各字段生效路径
1. schema --- 决定工作流
解析出的 schema 字段在 resolveSchemaForChange() (src/utils/change-metadata.ts:166)中按优先级决定:
1. 命令行 --schema 参数(显式覆盖)
2. [change-dir]/.openspec.yaml 里的 schema(变更级元数据)
3. openspec/config.yaml 里的 schema ← 这里!
4. 默认 'spec-driven'
一旦确定 schema,会加载对应的 schemas/<schema-name>/schema.yaml 来驱动变更工作流(生成哪些 artifact、依赖关系、模板)。
2. context --- 注入 AI 指令上下文
在 generateInstructions() (src/core/artifact-graph/instruction-loader.ts:273)中:
- 读取
projectConfig.context,注入到ArtifactInstructions.context字段 - 这些文本作为约束/背景信息传给 AI,但不会写入最终 artifact 文件本身
- 作用是告诉 AI 工具:本项目用 TypeScript、package manager 是 pnpm、注意跨平台路径等
3. rules --- artifact 级额外规则
同样在 generateInstructions() 中:
- 按 artifactId 匹配
projectConfig.rules[artifactId] - 注入到
ArtifactInstructions.rules字段 - 同时调用
validateConfigRules()校验 artifactId 是否在 schema 中存在,不存在的 ID 会告警
4. references --- 引用其他 store 的 spec
在 assembleReferenceIndex() (src/core/references.ts:259)中处理:
- 遍历声明的 store id,去注册中心查找对应的本地 checkout
- 读取被引用 store 的所有 spec,提取每份 spec 的第一行 Purpose
- 生成一个只读索引 (最大也是 50KB),注入到 artifact instructions 的
<referenced_stores>块中 - 这样 AI 生成时就知道可以去哪些上游 store 查 spec
5. store --- 声明式默认 store
在 readStorePointer() (src/core/project-config.ts:393)中解析:
- 仅当
openspec/目录下没有specs/或changes/子目录时才会生效 - 用于 root resolution:指向另一个已注册 store 作为实际工作目录
文件不存在时的行为
整个 readProjectConfig() 返回 null,系统完全正常工作 --- 所有字段都有默认 fallback:
| 字段 | 缺失时的默认行为 |
|---|---|
schema |
'spec-driven' |
context |
不注入额外上下文 |
rules |
不添加额外规则 |
references |
不引用外部 store |
store |
无声明式 store |
初始化流程
openspec init 命令(src/core/init.ts)在项目初始化时:
- 自动创建
openspec/config.yaml(包含默认内容schema: spec-driven) - 已有的 config 文件不会覆盖
设计要点
- 弹性解析(Resilient Parsing) --- 坏掉的字段单独被忽略而不是整体失败,文件缺失也完全可接受(走默认值)。
- 无缓存策略 --- 每次需要都直接读文件(1KB 约 0.5ms),避免缓存失效的复杂性。
- AI 辅助设计 ---
context和rules的设计目标是为 AI 生成提供项目级约束,而非控制程序逻辑。