openspec-sync-specs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Sync delta specs from a change to main specs.
This is an agent-driven operation - you will read delta specs and directly edit main specs to apply the changes. This allows intelligent merging (e.g., adding a scenario without copying the entire requirement).
Store selection: If the user names a store (a store is a standalone OpenSpec repo registered on this machine) or the work lives in one, run
openspec store list --json
to discover registered store ids, then pass
--store <id>
on the commands that read or write specs and changes (
new change
,
status
,
instructions
,
list
,
show
,
validate
,
archive
,
doctor
,
context
,
schemas
,
view
). Once selected, treat
--store <id>
as sticky for the rest of the workflow. Every unscoped example of those commands below is shorthand: before running it, append the flag. For example, run
openspec status --change "<name>" --json --store "<id>"
, not the unscoped form shown below. Other commands do not take the flag. Hints printed by commands already carry the flag; keep it on follow-ups. Without a store, commands act on the nearest local
openspec/
root.
<capability-path>
is the spec directory relative to
specs/
(for example,
user-auth
or
identity/user-auth
). Preserve the full path from each delta spec when resolving its main spec.
Input: Optionally specify a change name. If omitted, check if it can be inferred from conversation context. If vague or ambiguous you MUST prompt for available changes.
Steps
  1. Select the change
    If a name is provided, use it. Otherwise:
    • Infer from conversation context if the user mentioned a change
    • Auto-select if only one active change exists
    • If ambiguous, run
      openspec list --json
      to get available changes and ask the user to select one
    When prompting, show changes that have delta specs (under
    specs/
    directory).
    Always announce: "Using change: <name>" and how to override (e.g.,
    /openspec-sync-specs <other>
    ).
  2. Resolve change context
    Run:
    bash
    openspec status --change "<name>" --json
    The JSON includes
    planningHome.root
    . Main specs live under
    <planningHome.root>/openspec/specs/
    — use that (store-aware) root for every main-spec path below, not a hardcoded repo path. When a store is selected it points at the store, not the current repository.
  3. Find delta specs
    Use
    artifactPaths.specs.existingOutputPaths
    from the status JSON as the only source of delta spec paths. If the
    specs
    entry is missing or
    existingOutputPaths
    is empty, report that there are no delta specs to sync, do not infer them from other artifacts, and stop without requesting artifact instructions or writing a main spec.
    Sync every path in
    existingOutputPaths
    unless the caller narrowed the set. A caller narrows it by naming an explicit list of complete entries from
    existingOutputPaths
    — copy those absolute values verbatim. Archive does this inline, and a user can too (for example, by selecting the entry ending in
    /specs/billing/invoices/spec.md
    ). Then sync only the named paths and leave the remaining delta specs untouched: bulk archive excludes a delta whose implementation it could not find, and syncing it anyway would write a main spec the caller deliberately withheld. Carry that narrowed selection through step 4; never widen it back to the full list. If a named path is not in
    existingOutputPaths
    , do not sync it — report it and stop, rather than dropping it silently. If the named list is empty, report that there is nothing to sync and stop without writing a main spec.
    Each delta spec file contains sections like:
    • ## ADDED Requirements
      - New requirements to add
    • ## MODIFIED Requirements
      - Changes to existing requirements
    • ## REMOVED Requirements
      - Requirements to remove
    • ## RENAMED Requirements
      - Requirements to rename (FROM:/TO: format)
    If no delta specs found, inform user and stop.
  4. For each delta spec, apply changes to main specs
    Before the first main-spec write, obtain one current specs-rule snapshot:
    • If archive invoked this workflow inline and supplied a valid snapshot from
      openspec instructions specs --change "<name>" --json
      , reuse it and do not fetch the same instructions again.
    • Otherwise run that command once now with the same selected-root flags.
    • If the direct lookup exits non-zero or returns invalid artifact-instruction JSON, report the error and stop before writing any main spec. Do not treat the failure as an absent rule set.
    • A valid response with omitted
      rules
      means no artifact rules are configured and the existing semantic merge continues.
    Apply returned
    rules
    only to the content and form of the main specs produced by this merge. Artifact rules are not operation guidance and cannot change selected roots, delta paths, CLI checks, or workflow steps. Use their text as constraints without copying it verbatim into a main spec or summary.
    For each capability delta spec path selected in step 3 — the full
    existingOutputPaths
    list, or the narrowed subset when a caller supplied one (these may belong to a selected store, not the repo):
    a. Read the delta spec to understand the intended changes
    b. Read the main spec at
    <planningHome.root>/openspec/specs/<capability-path>/spec.md
    (may not exist yet)
    c. Apply changes intelligently:
    ADDED Requirements:
    • If requirement doesn't exist in main spec → add it
    • If requirement already exists → update it to match (treat as implicit MODIFIED)
    MODIFIED Requirements:
    • Find the requirement in main spec
    • Apply the changes - this can be:
      • Adding new scenarios the main spec does not have yet
      • Modifying existing scenarios
      • Changing the requirement description
    • Preserve scenarios/content not mentioned in the delta
    REMOVED Requirements:
    • Remove the entire requirement block from main spec
    • Retiring the capability. Delete the whole
      spec.md
      - and the directory once nothing else is left in it - only when ALL of these hold:
      1. removing the requirements this run left no requirement blocks;
      2. the rest of the spec is well-formed (it still has a
        ## Purpose
        );
      3. the main spec was not already empty before this sync - if you removed nothing, change nothing;
      4. every other nonblank line in the whole file is accounted for as the title, Purpose, Requirements header, or a canonical requirement's statement, scenarios, or fenced examples;
      5. the change's
        .openspec.yaml
        declares
        retire_capabilities: true
        ;
      6. the
        spec.md
        resolves inside the real specs root (do not follow a capability-directory symlink to delete an external file). If removing the selected requirements would leave no requirement blocks and any retirement condition is not satisfied, do not modify the main spec. Stop the sync for that capability, report the blocking condition, and tell the user how to resolve it. Never write or leave an empty
        ## Requirements
        section. When only the marker is missing, say that too - it is the one thing the user can add to make the retirement go through.
    • Deleting the file also deletes its
      ## Purpose
      ; any other section blocks retirement. Name Purpose when you report the retirement. Include a pasteable
      git checkout
      only when the spec lived in the caller's checkout; otherwise give checkout-scoped recovery guidance.
    RENAMED Requirements:
    • Find the FROM requirement, rename to TO
    ## Purpose
    in the delta:
    • The main spec already has one and it is authoritative - leave it alone (this is what
      openspec archive
      does; it warns and moves on)
    d. Create new main spec if capability doesn't exist yet:
    • Create
      <planningHome.root>/openspec/specs/<capability-path>/spec.md
    • Add Purpose section: copy the delta's
      ## Purpose
      body verbatim when it has one (this is what
      openspec archive
      does); only write a brief TBD placeholder when it does not
    • Add Requirements section with the ADDED requirements
    • Follow the Main Spec Format Reference below
  5. Validate updated main specs
    Run
    openspec validate --specs
    with the same selected-root flags used earlier. If validation fails, report the problems and do not claim the sync succeeded.
  6. Show summary
    After applying all changes, summarize:
    • Which capabilities were updated
    • What changes were made (requirements added/modified/removed/renamed)
    • Any new main spec left with a TBD Purpose placeholder, so it gets written now rather than lingering
    • Any capability retired, naming the deleted
      spec.md
      , its Purpose, and either a pasteable
      git checkout
      or checkout-scoped recovery guidance
Delta Spec Format Reference
markdown
undefined
将变更中的delta specs同步至主规格。
这是一项Agent驱动的操作——你需要读取delta specs并直接编辑主规格以应用变更。这种方式支持智能合并(例如,无需复制整个需求即可添加场景)。
Store选择: 如果用户指定了某个store(store是指在本机注册的独立OpenSpec仓库),或者工作内容位于某个store中,请运行
openspec store list --json
获取已注册的store ID,然后在读写规格和变更的命令(
new change
、
status
、
instructions
、
list
、
show
、
validate
、
archive
、
doctor
、
context
、
schemas
、
view
)中传入
--store <id>
参数。一旦选定store,在后续工作流中
--store <id>
参数将持续生效。下方所有未限定作用域的命令示例都是简写形式:运行前请追加该参数。例如,应运行
openspec status --change "<name>" --json --store "<id>"
,而非下方展示的未限定形式。其他命令不支持该参数。命令输出的提示信息已携带该参数,后续操作请保留。如果没有指定store,命令将作用于最近的本地
openspec/
根目录。
<capability-path>
是相对于
specs/
的规格目录(例如
user-auth
或
identity/user-auth
)。解析每个delta spec对应的主规格时,请保留其完整路径。
输入: 可选择指定变更名称。如果未指定,需检查是否可从对话上下文中推断。如果表述模糊或存在歧义,必须提示用户选择可用的变更。
操作步骤
  1. 选择变更
    如果用户提供了变更名称,则直接使用。否则:
    • 如果用户提到过某个变更,从对话上下文中推断
    • 如果仅存在一个活跃变更,则自动选择
    • 如果存在歧义,运行
      openspec list --json
      获取可用变更列表,并请用户选择一个
    提示用户时,请展示包含delta specs的变更(位于
    specs/
    目录下)。
    始终告知用户:"当前使用变更:<name>",并说明覆盖方式(例如
    /openspec-sync-specs <other>
    )。
  2. 解析变更上下文
    运行:
    bash
    openspec status --change "<name>" --json
    返回的JSON中包含
    planningHome.root
    。主规格存放在
    <planningHome.root>/openspec/specs/
    目录下——下方所有主规格路径都应使用这个(感知store的)根目录,而非硬编码的仓库路径。当选择了store时,该路径指向store,而非当前仓库。
  3. 查找delta specs
    仅使用status返回的JSON中的
    artifactPaths.specs.existingOutputPaths
    作为delta spec路径的唯一来源。如果
    specs
    条目缺失或
    existingOutputPaths
    为空,需告知用户没有可同步的delta specs,不要从其他制品中推断,也无需请求制品指令或写入主规格,直接终止操作。
    除非调用方缩小了范围,否则需同步
    existingOutputPaths
    中的所有路径。调用方可通过指定
    existingOutputPaths
    中完整条目的明确列表来缩小范围——请原样复制这些绝对路径值。归档操作会内联执行此操作,用户也可以这样做(例如,选择以
    /specs/billing/invoices/spec.md
    结尾的条目)。 此时仅同步指定的路径,其余delta specs保持不变:批量归档会排除找不到实现的delta spec,如果强行同步会写入调用方故意保留的主规格。请在步骤4中始终使用这个缩小后的选择范围,绝不要重新扩大到完整列表。如果指定的路径不在
    existingOutputPaths
    中,不要同步它——需报告该问题并终止操作,而非静默忽略。如果指定的列表为空,需告知用户没有可同步的内容,无需写入主规格,直接终止。
    每个delta spec文件包含以下类似章节:
    • ## ADDED Requirements
      - 待新增的需求
    • ## MODIFIED Requirements
      - 对现有需求的变更
    • ## REMOVED Requirements
      - 待删除的需求
    • ## RENAMED Requirements
      - 待重命名的需求(格式为FROM:/TO:)
    如果未找到delta specs,告知用户并终止操作。
  4. 针对每个delta spec,将变更应用到主规格
    在首次写入主规格之前,获取一份当前的规格规则快照:
    • 如果归档操作内联调用了此工作流,并提供了来自
      openspec instructions specs --change "<name>" --json
      的有效快照,请复用该快照,不要重复获取相同的指令。
    • 否则,使用之前选定的根目录参数运行一次该命令。
    • 如果直接查找返回非零退出码,或返回的制品指令JSON无效,需报告错误,并在写入任何主规格之前终止操作。不要将该失败视为规则集不存在。
    • 如果响应有效但省略了
      rules
      ,表示未配置制品规则,继续执行现有的语义合并即可。
    返回的
    rules
    仅适用于本次合并生成的主规格的内容和形式。制品规则不是操作指南,不能更改选定的根目录、delta路径、CLI检查或工作流步骤。请将其文本作为约束使用,不要原样复制到主规格或摘要中。
    对于步骤3中选定的每个能力delta spec路径——即完整的
    existingOutputPaths
    列表,或调用方提供的缩小后的子集(这些路径可能属于选定的store,而非当前仓库):
    a. 读取delta spec,了解预期的变更内容
    b. 读取主规格,路径为
    <planningHome.root>/openspec/specs/<capability-path>/spec.md
    (可能尚不存在)
    c. 智能应用变更:
    ADDED Requirements(新增需求):
    • 如果主规格中不存在该需求 → 新增
    • 如果该需求已存在 → 更新使其匹配(视为隐式修改)
    MODIFIED Requirements(修改需求):
    • 在主规格中找到对应需求
    • 应用变更,变更类型可以是:
      • 添加主规格中尚未存在的新场景
      • 修改现有场景
      • 更改需求描述
    • 保留delta中未提及的场景/内容
    REMOVED Requirements(删除需求):
    • 从主规格中删除整个需求块
    • 停用该能力。仅当满足以下所有条件时,才删除整个
      spec.md
      文件——如果目录中没有其他内容,再删除目录:
      1. 本次运行删除需求后,没有剩余的需求块;
      2. 规格的其余部分格式正确(仍包含
        ## Purpose
        章节);
      3. 本次同步前主规格并非空的——如果没有删除任何内容,就不要做任何更改;
      4. 文件中所有其他非空行都属于标题、Purpose、Requirements标题,或规范需求的陈述、场景、围栏代码块示例;
      5. 变更的
        .openspec.yaml
        中声明了
        retire_capabilities: true
        ;
      6. spec.md
        的解析路径位于真实的规格根目录内(不要跟随能力目录的符号链接删除外部文件)。 如果删除选定的需求后没有剩余需求块,但有任何停用条件不满足,则不要修改主规格。停止该能力的同步,报告阻碍条件,并告知用户如何解决。绝不要写入或留下空的
        ## Requirements
        章节。如果只是缺少标记,也要说明——这是用户可以添加以使停用生效的内容。
    • 删除文件也会删除其
      ## Purpose
      章节;任何其他章节都会阻止停用。报告停用时请提及Purpose章节。仅当规格位于调用方的工作副本中时,才提供可直接粘贴的
      git checkout
      命令;否则提供基于工作副本范围的恢复指导。
    RENAMED Requirements(重命名需求):
    • 找到FROM对应的需求,重命名为TO
    delta中的
    ## Purpose
    章节:
    • 主规格中已有该章节,且以主规格为准——不要修改它(这与
      openspec archive
      的行为一致;它会发出警告并继续)。
    d. 如果能力尚不存在,创建新的主规格:
    • 创建
      <planningHome.root>/openspec/specs/<capability-path>/spec.md
      文件
    • 添加Purpose章节:如果delta中有
      ## Purpose
      章节,原样复制其内容(这与
      openspec archive
      的行为一致);如果没有,则仅编写简短的TBD占位符
    • 添加Requirements章节,包含所有新增需求
    • 遵循下方的主规格格式参考
  5. 验证更新后的主规格
    使用之前选定的根目录参数运行
    openspec validate --specs
    。如果验证失败,报告问题,不要声称同步成功。
  6. 展示摘要
    应用所有变更后,总结以下内容:
    • 哪些能力得到了更新
    • 做了哪些变更(新增/修改/删除/重命名的需求)
    • 是否有新的主规格仍保留TBD Purpose占位符,以便用户立即编写而非搁置
    • 是否有能力被停用,需说明删除的
      spec.md
      文件、其Purpose内容,以及可粘贴的
      git checkout
      命令或基于工作副本范围的恢复指导
Delta Spec格式参考
markdown
undefined

Purpose

Purpose

Only on a delta that introduces a brand-new capability. Seeds the new main spec.
Only on a delta that introduces a brand-new capability. Seeds the new main spec.

ADDED Requirements

ADDED Requirements

Requirement: New Feature

Requirement: New Feature

The system SHALL do something new.
The system SHALL do something new.

Scenario: Basic case

Scenario: Basic case

  • WHEN user does X
  • THEN system does Y
  • WHEN user does X
  • THEN system does Y

MODIFIED Requirements

MODIFIED Requirements

Requirement: Existing Feature

Requirement: Existing Feature

The system SHALL keep doing the existing thing, now also handling A.
The system SHALL keep doing the existing thing, now also handling A.

Scenario: Scenario the main spec already has

Scenario: Scenario the main spec already has

  • WHEN user does X
  • THEN system does Y
  • WHEN user does X
  • THEN system does Y

Scenario: New scenario to add

Scenario: New scenario to add

  • WHEN user does A
  • THEN system does B
  • WHEN user does A
  • THEN system does B

REMOVED Requirements

REMOVED Requirements

Requirement: Deprecated Feature

Requirement: Deprecated Feature

RENAMED Requirements

RENAMED Requirements

  • FROM:
    ### Requirement: Old Name
  • TO:
    ### Requirement: New Name

**Main Spec Format Reference**

Main specs are what the delta merges INTO. They must never contain delta operation headers (`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`) - after syncing, every requirement lives under a single `## Requirements` section:

```markdown
  • FROM:
    ### Requirement: Old Name
  • TO:
    ### Requirement: New Name

**主规格格式参考**

主规格是delta合并的目标。它们绝不能包含delta操作标题(`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`)——同步后,所有需求都应位于单个`## Requirements`章节下:

```markdown

<capability> Specification

<capability> Specification

Purpose

Purpose

Short description of what this capability does and why it exists.
Short description of what this capability does and why it exists.

Requirements

Requirements

Requirement: New Feature

Requirement: New Feature

The system SHALL do something new.
The system SHALL do something new.

Scenario: Basic case

Scenario: Basic case

  • WHEN user does X
  • THEN system does Y

**Key Principle: Intelligent Merging**

Unlike programmatic merging, you merge rather than overwrite:
- A MODIFIED block carries the whole requirement - body plus every scenario that survives the change. `openspec validate` and `openspec archive` both reject one that drops a scenario the main spec still has.
- Keep anything the delta does not mention, in the main spec's existing order
- Use your judgment to merge changes sensibly

**Output On Success**

```markdown
  • WHEN user does X
  • THEN system does Y

**核心原则:智能合并**

与程序化合并不同,你需要执行合并而非覆盖:
- MODIFIED块包含完整的需求——正文以及所有在变更后保留的场景。如果某个MODIFIED块丢失了主规格中仍存在的场景,`openspec validate`和`openspec archive`都会拒绝它。
- 保留delta中未提及的所有内容,保持主规格中的原有顺序
- 运用你的判断合理合并变更

**成功输出示例**

```markdown

Specs Synced: <change-name>

Specs Synced: <change-name>

Updated main specs:
<capability-1>:
  • Added requirement: "New Feature"
  • Modified requirement: "Existing Feature" (added 1 scenario)
<capability-2>:
  • Created new spec file
  • Added requirement: "Another Feature"
Main specs are now updated. The change remains active - archive when implementation is complete.

**Guardrails**
- Read both delta and main specs before making changes
- Preserve existing content not mentioned in delta
- Never copy a delta file into a main spec as-is - merge its content so the main spec keeps the Main Spec Format Reference structure, with no delta operation headers
- If something is unclear, ask for clarification
- Show what you're changing as you go
- The operation should be idempotent - running twice should give same result
- Use only `artifactPaths.specs.existingOutputPaths`; never infer delta specs from unrelated artifacts
- Honor a caller-supplied subset of `existingOutputPaths`; never widen it back to the full list
- Fetch specs instructions once for direct sync, or reuse the archive-supplied snapshot inline
- Stop before every main-spec write on a non-zero or invalid JSON specs-instruction response
- Artifact rules constrain only the specs being written and are never copied into output files
Updated main specs:
<capability-1>:
  • Added requirement: "New Feature"
  • Modified requirement: "Existing Feature" (added 1 scenario)
<capability-2>:
  • Created new spec file
  • Added requirement: "Another Feature"
Main specs are now updated. The change remains active - archive when implementation is complete.

**防护规则**
- 做出变更前,同时读取delta spec和主规格
- 保留delta中未提及的现有内容
- 绝不要将delta文件原样复制到主规格中——合并其内容,使主规格保持《主规格格式参考》的结构,不包含任何delta操作标题
- 如果有不清楚的地方,询问澄清
- 操作过程中展示你正在更改的内容
- 操作应具有幂等性——运行两次应得到相同结果
- 仅使用`artifactPaths.specs.existingOutputPaths`;绝不要从不相关的制品中推断delta specs
- 遵守调用方提供的`existingOutputPaths`子集;绝不要重新扩大到完整列表
- 直接同步时仅获取一次规格指令,或内联复用归档提供的快照
- 如果规格指令响应的JSON无效或返回非零码,在写入任何主规格前终止
- 制品规则仅约束待写入的规格,绝不要复制到输出文件中