在实验工作流中归档一个已完成的OpenSpec变更。当用户希望最终确定并归档一个变更时使用。也适用于用户说“openspec archive”或“opsx archive”时。
归档一个已完成的变更,用于实验性工作流。
前提条件
存储库选择: 如果用户指定了存储库(存储库是注册在此机器上的独立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 对象意味着项目已设置。"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/ 文件;也不要让命令创建它。
<capability-path> 是相对于 specs/ 的规范目录(例如 user-auth 或 identity/user-auth)。在解析其主规范时,保留每个delta规范的完整路径。
步骤
-
选择变更
如果提供了名称,使用它。否则:
- 如果用户在对话中提到了变更,从对话上下文中推断
- 如果只有一个活跃变更存在,自动选择
- 如果模糊不清,运行
openspec list --json获取可用变更并让用户选择一个
提示时,只显示活跃的变更(尚未归档的)。 如果可用,包含每个变更使用的模式(schema)。
始终宣布:“使用变更:<名称>”以及如何覆盖(例如
/openspec-archive-change <other>)。在检查现有归档之前加载当前归档输入:
解析所选变更并计划根目录后,运行:
openspec instructions archive --change "<name>" --json在此命令上保持相同的选定根目录标志。此查找是建议性的、可选的:它只提供额外的提示输入,所以绝不能阻塞归档。如果它以非零退出码退出或返回无效JSON——例如在较旧的CLI上不支持此命令——继续归档工作流,不带上下文和操作指导。不要报告错误,也不要停止。
成功的响应可能省略两个可选字段。将
context视为必需的提示级输入:阅读并考虑它,并应用相关的项目事实、约定和约束。将operationGuidance视为可选的附加建议:阅读并考虑每个条目,并遵循那些适用且与内置归档工作流兼容的条目。将这两个字段与内置步骤、显式用户选择、已解析路径、CLI检查和命令契约分开。如果
context与其中一个控制输入冲突,报告冲突并保留控制值。如果指导不适用或与控制输入冲突,不遵循它并解释原因。不要从这两个字段推断替换路径、跳过提示或标志,也不要将其文本逐字复制到规范、变更工件或归档摘要中,除非用户单独要求。这些是提示级的行为契约,不是可强制执行的检查。 -
检查工件完成状态
运行
openspec status --change "<name>" --json检查工件完成情况。解析JSON以了解:
schemaName:使用的工作流planningHome、changeRoot、artifactPaths和actionContext:路径和作用域上下文artifacts:工件列表及其状态(done、skipped或其他)
如果任何工件既不是
done也不是skipped(跳过的工件满足要求——变更声明了skip_specs):- 显示警告,列出未完成的工件
- 让用户确认他们要继续
- 如果用户确认,继续
-
检查任务完成状态
使用相同的选定根目录标志运行
openspec list --json,并在changes中找到name与所选变更完全匹配的条目。要求有且仅有一个匹配项,且totalTasks和completedTasks为非负整数,且completedTasks <= totalTasks。CLI 解析模式跟踪的任务文件,包括自定义工件名称、输出路径和 glob。不完整任务数 =totalTasks - completedTasks。不要从工件状态或缺少顶级
tasks.md推断任务完成。如果查找失败、返回无效JSON、省略或重复所选变更、或返回无效计数,报告问题并在同步或归档之前停止。CLI 只将x/X复选标记视为完成;其他标记(包括不熟悉的)视为未完成。如果发现不完整任务:
- 显示警告,显示不完整任务的数量
- 让用户确认他们要继续
- 如果用户确认,继续
如果
totalTasks为零: 继续,不发出任务相关警告。 -
评估delta规范同步状态
使用来自
statusJSON 的artifactPaths.specs.existingOutputPaths作为唯一的delta规范来源。如果specs条目缺失或existingOutputPaths为空,则继续而不提示同步,也不从其他工件推断delta规范。如果delta规范存在:
- 将每个delta规范与其对应的位于
<planningHome.root>/openspec/specs/<capability-path>/spec.md的主规范进行比较(使用步骤2中的存储库感知的planningHome.root,而不是硬编码的仓库路径) - 缺少主规范并不自动意味着“已同步”。对于新能力,主规范是同步的输出,而不是输入:
- 如果delta有MODIFIED或RENAMED的要求,报告只有ADDED的要求可以创建新的主规范,并将该能力标记为同步阻塞。不要发明任何没有当前版本的Requirement。
- 否则,如果delta只有REMOVED的要求,且变更的
.openspec.yaml声明了retire_capabilities: true,则该能力已退役:将其计为已同步,警告没有任何东西可移除,不要重新创建主规范。现在以及在验证完成的同步时都应用此规则。 - 否则,如果delta没有ADDED的要求,报告无法同步并将该能力标记为同步阻塞。对于仅REMOVED的delta,警告没有主规范可供移除,使主规范树保持不变。
openspec archive拒绝未标记的仅REMOVED情况,提示“Spec must have at least one requirement”。 - 否则,将该能力计为需要同步,并在摘要中命名(
<capability-path>: new main spec will be created)。如果delta也有REMOVED的要求,警告它们将被忽略,因为没有主规范可以移除。同步仅从delta的ADDED要求创建主规范,与openspec archive完全相同。
- 确定将应用哪些更改(添加、修改、移除、重命名)
- 即使一个能力被同步阻塞,继续评估剩余的能力。提示前显示组合摘要。
提示选项:
- 如果任何能力被同步阻塞:解释原因,并仅提供“不进行同步就归档”、“取消”
- 否则,如果需要更改:“现在同步(推荐)”、“不进行同步就归档”
- 否则,如果已同步:“立即归档”、“无论如何都同步”、“取消”
根据答案路由:
- “取消”——停止,不归档
- “不进行同步就归档”或“立即归档”——继续归档
- “现在同步”或“无论如何都同步”——同步,然后验证(下文)。当有能力被同步阻塞时,不要启动任何同步;解释阻塞器并重复可用选项。
- 其他任何内容——重新询问,而不是归档
在选定的同步写入任何主规范之前,使用相同的选定根目录标志运行
openspec instructions specs --change "<name>" --json一次。要求零退出状态和有效的工件指令JSON。如果查找失败或返回无效JSON,在写入任何主规范或移动变更之前报告错误并停止。一个有效响应,其中rules被省略表示无规则情况。仅将返回的rules应用于由此合并产生的主规范的内容和形式;不要将其用作归档指导、变更CLI行为,或将规则文本复制到任何输出文件中。然后,对于变更'',内联运行
openspec-sync-specs工作流(代理驱动的智能合并),传递上述delta规范分析和获取的规范规则快照,并等待其完成。内联同步必须重用该快照,而不要再次获取specs指令。不要将其委托给后台任务——步骤5会将changeRoot移出仍在读取它的同步的范围,使变更被归档而主规范从未更新。如果您的代理只能通过委托运行它,请同步委托并等待结果。如果同步报告任何停止或阻塞条件,将同步视为失败。立即停止归档。不要执行同步后的内容比较,也不要移动其
changeRoot。什么也没有移动,因此用户可以修复阻塞条件或重新运行同步。同步写入每个主规范后,根据规范同步契约验证其结构:
- 新主规范以
# <capability> Specification标题开头。现有主规范保持其标题完全不变。 - 对于已建立的主规范,完全保留现有的
## Purpose部分不变。 - 对于新主规范,逐字复制 delta 的
## Purpose。仅在目的文本比标准验证预期的短时发出警告。不要重新生成或重写现有的创作目的。如果没有提供可用的## Purpose,使用现有的TBD目的行为并警告。 - 验证主规范中没有delta样式的小节标题(
## ADDED Requirements、## MODIFIED Requirements、## REMOVED Requirements、## RENAMED Requirements),严格遵循同步工作流格式化规则。 - 同步写入或更改的Requirement块使用
### Requirement:标题,其场景在规范的## Requirements小节下使用#### Scenario:标题。让delta未提及的内容保持原样。
然后,重新运行本步骤开头的比较,包括显式退役的、缺失规范的情况,针对
artifactPaths.specs.existingOutputPaths中每个具有delta规范的能力——而不仅仅是同步报告其触及的能力。成功的同步不会留下任何需要应用的内容,因此每个能力现在必须读取为已同步:- ADDED 的要求存在
- MODIFIED 的要求携带delta中命名的场景和描述更改,其其他场景保持完整
- REMOVED 的要求已消失——并且当此同步退役了一个能力(移除了其最后一个要求,使
## Requirements为空)时,其主规范被删除而不是留空。 - RENAMED 的要求存在于新名称下,旧名称下不存在
如果同步失败,或任何能力不匹配,报告什么不同然后停止——不归档。什么也没有移动且
changeRoot完整,因此用户可以修复不匹配项或重新运行同步并再次启动归档。 - 将每个delta规范与其对应的位于
-
执行归档
在
planningHome.changesDir下创建一个archive目录(如果不存在):mkdir -p "<planningHome.changesDir>/archive"生成目标名称:当变更名称已经以
YYYY-MM-DD-前缀开头时,按原样使用;否则在当前日期前添加该前缀。...
[截断以进行翻译]