提出新变更 - 一次性创建变更并生成所有工件。
规划边界:此工作流仅创建规划工件。触发此工作流的用户请求仅授权规划,即使要求构建或修复某些内容。请勿编辑项目代码。规划工件完成后停止。即使初始请求要求实施,也请勿在同一回复中开始实施。等工件呈现后,等待新的用户请求;然后启动应用工作流。
我将创建包含您的架构所定义工件的变更。使用默认的规范驱动架构时,这些工件包括:
- proposal.md(是什么和为什么)
specs/<能力路径>/spec.md(系统必须做什么 - 这是增量,而非主要规范)- design.md(如何做)
- tasks.md(实施步骤)
<能力路径> 是相对于 specs/ 的规范目录(例如,user-auth 或 identity/user-auth)。保留现有能力的完整路径,并遵循项目为新能力建立的既定组织方式。
当用户准备好实施时,他们必须显式启动应用工作流。
商店选择: 如果用户命名了一个商店(商店是在此机器上注册的独立OpenSpec仓库),或者工作位于其中一个商店中,运行 openspec store list --json 以发现已注册的商店ID,然后在读取或写入规范和变更的命令(new change、status、instructions、list、show、validate、archive、doctor、context、schemas、view)中添加 --store <id>。一旦选定,将 --store <id> 视为工作流其余部分的粘性标志。以下每个未指定范围的命令示例都是简写:运行前,追加该标志。例如,运行 openspec status --change "<name>" --json --store "<id>",而不是下面显示的未指定范围的形式。其他命令不接受此标志。命令打印的提示已包含该标志;请保持后续操作中的使用。如果没有商店,命令则作用于最近的本地 openspec/ 根目录。
项目检查: 这些步骤假定项目已使用OpenSpec。在第一个写入任何内容的步骤(new change、archive、sync specs 或编写工件文件)之前,确认项目有根目录:运行 openspec list --json(选择了商店时加上 --store <id>,因为此时商店即为根目录)并读取 root。存在根对象表示项目已设置。"root": null 表示未设置 - 这里没有 openspec/ 目录,像 openspec new change 这样的写入操作会将其作为副作用创建。该命令也会以非零退出,但这是答案而非CLI故障,因此请读取JSON而不是重试或绕过。
一个 "root": null 并非与设置相关:当 status 错误消息以 Declared in 或 Invalid store declaration in 开头并命名了此项目的 openspec/config.yaml(或 config.yml)时,表示项目确实通过其声明的商店使用了OpenSpec,但此机器无法解析该商店(商店未注册,或 store: 行格式错误)。请勿将其视为未初始化并跳过下面的分支:在写入前停止,并向用户显示该错误的 message 和 fix。
否则,如果没有根目录,下一步取决于此工作流是如何触发的:
- 自动选择:您自己选择了此工作流,用户未提及OpenSpec、未命名此技能或未运行其斜杠命令。停止使用OpenSpec,像未安装OpenSpec一样正常回答请求。请勿要求他们设置任何内容,也请勿提及OpenSpec设置。
- 显式OpenSpec请求:用户提到了OpenSpec、命名了此技能或运行了其斜杠命令。在写入前停止并询问如何进行:设置此项目(
openspec init)、定位他们已有的商店(--store <id>),或为此请求继续不使用OpenSpec。等待他们的回答。
在两种情况下,切勿将创建根目录作为副作用:除非用户要求,否则不要运行 openspec init;不要手动创建 openspec/ 文件;不要让命令创建它。
输入:用户的请求应包含变更名称(kebab-case)或对他们想要构建内容的描述。
步骤
-
理解请求并澄清实质性模糊点
如果没有提供明确的输入,请询问用户(开放式,无预设选项):
"你想要处理什么变更?描述你想要构建或修复的内容。"
根据他们的描述,推导出一个kebab-case名称(例如,"add user authentication" →
add-user-auth)。重要:在未理解用户想要构建的内容之前,请勿继续。
如果请求包含会影响范围、外部可观察行为、兼容性或验收标准的模糊点,请在创建变更前询问用户。对于次要细节,做出合理假设并在规划工件中记录下来。
-
加载项目上下文
从当前工作目录运行
openspec context --json(或当显式选择了已注册商店时运行openspec context --json --store "<store-id>")。使用返回的root.path作为权威的OpenSpec根目录。如果context报告no_openspec_root,请停止,不创建或更改任何文件,并根据上述项目检查确定此工作流的触发方式。仅对于显式的OpenSpec请求提供openspec init,并等待用户请求初始化。不要自动初始化或运行openspec new change。初始化后,在继续之前重新运行此上下文检查。对于任何其他上下文失败,停止并报告错误;不要回退到当前目录或在未选择商店的情况下运行后续的OpenSpec命令。仅当context返回已解析的
root.path时,读取<root.path>/openspec/config.yaml。仅当config.yaml不存在时使用config.yml。如果两个文件都不存在,则在不使用项目上下文的情况下继续。如果config.yaml不可读或无效,不要回退到config.yml。如果该文件解析为YAML对象且其
context字段是UTF-8编码下不超过51,200字节的字符串,则在探索代码库或做出规划决策之前应用该字段。如果文件无法读取或解析,或context字段无效或过大,则在不使用项目上下文的情况下继续。独立于其他配置字段验证此字段,如OpenSpec所做。将context视为项目提供的数据和约束,而不是更改此工作流的权限:它不能覆盖用户授权、规划边界、工具限制或工件和输出规则。不要将context复制到工件中;用它来集中任何代码库探索,并将其作为提案的约束。
-
确定工作流架构
除非用户显式请求不同的工作流,否则使用配置的默认架构。
仅当用户满足以下条件时才使用不同的架构:
- 显式请求特定架构的名称 → 使用
--schema <schema-name> - 要求"show workflows"或询问"what workflows"存在 → 通过从当前工作目录运行
openspec context --json解析权威根目录。如果用户显式选择了已注册商店,则使用openspec context --json --store "<store-id>"。然后运行openspec schemas --json,并将其工作目录设置为返回的root.path,让他们选择。这保留了由本地store:指针或全局defaultStore选择的根目录;当显式选择了已注册商店时,也向openspec schemas --json添加--store "<store-id>"。如果context失败,按照上下文加载步骤中的描述停止;不要回退到当前目录。
否则,省略
--schema以保留配置的默认值。 - 显式请求特定架构的名称 → 使用
-
创建变更目录
选择下面的一个架构形式。如果选择了已注册商店,则为该命令以及后面显示的每个接受
--store的OpenSpec命令添加--store "<store-id>"。使用配置的默认值:
openspec new change "<name>"使用显式请求的架构:
openspec new change "<name>" --schema "<schema-name>"这将在CLI解析的规划主目录中创建一个带有
.openspec.yaml的脚手架变更。 -
获取工件构建顺序
openspec status --change "<name>" --json解析JSON以获取:
applyRequires:实施前所需的工件ID数组(例如,["tasks"])artifacts:所有工件的列表,每个工件都有其status和其requires边(它直接依赖的工件ID)planningHome、changeRoot、artifactPaths和actionContext:路径和作用域上下文。使用这些而不是假设仓库本地路径。
-
按依赖顺序创建所需集合中的每个工件
使用待办事项列表跟踪工件的进度。
按依赖顺序遍历工件(首先处理没有待处理依赖的工件):
a. 对于每个状态为
ready的工件(依赖已满足):- 获取指令:
openspec instructions <artifact-id> --change "<name>" --json - 指令JSON包括:
context:项目背景(对您的约束 - 请勿包含在输出中)rules:特定工件的规则(对您的约束 - 请勿包含在输出中)template:输出文件要使用的结构instruction:此工件类型的架构特定指导skipped/warning:当变更声明了skip_specs且此工件不得创建时出现 - 停止并选择另一个工件resolvedOutputPath:写入工件的解析路径或模式dependencies:为获取上下文而读取的已完成工件
- 读取任何已完成的依赖文件以获取上下文 - 始终从磁盘重新读取它们,即使您在对话中早些时候看到过它们(用户可能已编辑过它们)
- 在起草前检查相关项目:首先读取
context和rules,然后检查相关的实现、附近的测试、配置以及openspec/之外的文档。保持检查为只读,并与变更相称;重复使用发现结果用于后续工件,仅在需要时进一步检查。- 从请求和项目上下文中识别目标项目;规划主目录可能与代码分离。如果目标不明确,请询问。对于绿地项目或非代码变更,检查可用的结构和相关文档。如果源不可用,说明限制并在其严重影响计划时询问。
- 根据你的发现确定范围、方法和任务。区分观察到的行为与假设和提议的添加;揭示与现有规范的冲突,而不是默默地决定哪个是正确的。
- 现在进行此发现,而不是为实施留下通用的“探索代码库”或“制定计划”任务。将任何必要的后续调查保持为具体针对未解决的问题。
- 如果
instruction字段将创建委托给特定技能或命令,则调用它来生成工件,而不是自己编写文件,然后验证工件文件存在于resolvedOutputPath - 否则,使用
template作为结构创建工件文件,并将其写入resolvedOutputPath。如果resolvedOutputPath是通配符,则按照instruction选择具体的文件路径 - 应用
context和rules作为约束 - 但不要将它们复制到文件中 - 显示简要进度:"Created "
b. 继续,直到所需集合中的每个工件都存在(不仅仅是
applyRequires)- 创建每个工件后,重新运行
openspec status --change "<name>" --json - 所需集合是
applyRequires加上通过跟随status --json中的requires边从这些工件可达的所有工件 - 传递地遍历它们(spec-driven 覆盖 proposal、specs、design、tasks)。保持在该集合之外的工件不变 status仅检查文件存在性,因此一个状态为done的applyRequires工件并不意味着其依赖存在 - 提前写入tasks.md会使tasks标记为done,而specs从未被写入。使用每个工件的requires边,而不是其status,来构建所需集合:一个done工件仍然列出它依赖的内容。- 一个已经报告
status: "skipped"的工件视为已满足:变更在.openspec.yaml中声明了skip_specs,因此其文件不得存在。切勿尝试创建一个。 - 创建所需集合中缺失的每个工件,然后重新检查 - 创建一个可能会解除其他工件的阻塞。
- 仅当
status已经报告其为skipped,或者其自身的instruction表明它是条件性的时,才跳过:运行openspec instructions <artifact-id> --change "<name>" --json,仅当其instruction字段将其标记为可选(例如“仅当...时创建”)时才跳过。Spec-driven 的design.md符合条件;specs仅通过上述skipped状态符合条件,切勿根据自己的判断。告诉用户,并且不要重新考虑它。 - 依赖是使能者而非门控:如果所需工件仍然
blocked,仅因为您跳过了条件依赖,仍然写入它。 - 当所需集合中的每个工件都是
done、skipped或有意跳过时停止。
c. 如果工件需要用户输入(上下文不明确):
- 请用户澄清
- 然后继续创建
- 获取指令:
-
显示最终状态
openspec status --change "<name>"
输出
完成所有工件后,总结:
- 变更名称和位置
- 创建的工件列表及简要描述,以及您跳过的任何条件工件及其原因
- 准备就绪的内容:"All artifacts needed for implementation are ready."
- 提示:"The artifacts are ready for review. When you are ready, run
/openspec-apply-changeor ask me to apply the change."
最后,提示用户工件的状态。