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
将某个 change 中的 delta specs 同步到 main specs。
这是一项 Agent 驱动的操作——你将读取 delta specs,并直接编辑 main specs 以应用变更。这样可以进行智能合并(例如,在不复制整个 requirement 的情况下添加一个 scenario)。
Store 选择: 如果用户指定了某个 store(store 是注册在本机上的独立 OpenSpec repo),或者工作内容位于某个 store 中,请运行
openspec store list --json
以发现已注册的 store id,然后在读取或写入 specs 和 changes 的命令(
new change
、
status
、
instructions
、
list
、
show
、
validate
、
archive
、
doctor
、
context
、
schemas
、
view
)上传入
--store <id>
。选定后,在余下的 workflow 中将
--store <id>
视为持续有效。下面每个未指定 store 的命令示例都是简写:运行前应追加该参数。例如,应运行
openspec status --change "<name>" --json --store "<id>"
,而不是下面所示的未指定 store 的形式。其他命令不接受该参数。命令打印的提示已经带有该参数;后续命令应继续保留它。未使用 store 时,命令会作用于最近的本地
openspec/
root。
<capability-path>
是相对于
specs/
的 spec 目录(例如
user-auth
或
identity/user-auth
)。解析对应的 main spec 时,必须保留每个 delta spec 的完整路径。
输入: 可以选择指定 change 名称。如果省略,请检查能否从对话上下文中推断。如果信息含糊或不明确,必须提示用户选择可用的 change。
步骤
  1. 选择 change
    如果提供了名称,则使用该名称。否则:
    • 如果用户在对话上下文中提及了某个 change,则进行推断
    • 如果仅存在一个活跃 change,则自动选择
    • 如果存在歧义,则运行
      openspec list --json
      获取可用 changes,并要求用户选择
    提示用户时,显示包含 delta specs(位于
    specs/
    目录下)的 changes。
    始终声明:
    Using change: <name>
    ,并说明如何覆盖它(例如
    /openspec-sync-specs <other>
    )。
  2. 解析 change 上下文
    运行:
    bash
    openspec status --change "<name>" --json
    JSON 中包含
    planningHome.root
    。Main specs 位于
    <planningHome.root>/openspec/specs/
    下——下文中所有 main-spec 路径都必须使用这个支持 store 的 root,不能硬编码 repo 路径。选择 store 时,它指向该 store,而不是当前 repo。
  3. 查找 delta specs
    仅使用 status JSON 中的
    artifactPaths.specs.existingOutputPaths
    作为 delta spec 路径来源。如果缺少
    specs
    项,或
    existingOutputPaths
    为空,请报告没有可同步的 delta specs,不要从其他 artifacts 推断路径,并立即停止,不要请求 artifact instructions 或写入 main spec。
    除非调用方缩小了范围,否则同步
    existingOutputPaths
    中的每个路径。 调用方通过指定来自
    existingOutputPaths
    的完整条目列表来缩小范围——应逐字复制这些绝对路径值。Archive 会内联执行此操作,用户也可以手动执行,例如选择以
    /specs/billing/invoices/spec.md
    结尾的条目。 此时只同步指定的路径,并让其余 delta specs 保持不变: 批量 Archive 会排除找不到实现的 delta;如果仍同步它,就会写入调用方有意不予处理的主规格。 将这一缩小后的选择范围贯穿第 4 步;绝不能重新扩大为完整列表。如果指定路径不在
    existingOutputPaths
    中,不要同步它——应报告该情况并停止,而不是静默忽略。如果指定列表为空,请报告没有可同步的内容并停止,不要写入 main spec。
    每个 delta spec 文件包含如下部分:
    • ## ADDED Requirements
      - 要添加的新 requirements
    • ## MODIFIED Requirements
      - 对现有 requirements 的变更
    • ## REMOVED Requirements
      - 要移除的 requirements
    • ## RENAMED Requirements
      - 要重命名的 requirements(FROM:/TO: 格式)
    如果未找到 delta specs,请通知用户并停止。
  4. 对每个 delta spec,将变更应用到 main specs
    在首次写入 main spec 之前,获取当前的一份 specs-rule 快照:
    • 如果 Archive 内联调用了此 workflow,并提供了来自
      openspec instructions specs --change "<name>" --json
      的有效快照,则复用该快照,不要再次获取相同的 instructions。
    • 否则,现在使用与之前相同的 selected-root 参数运行一次该命令。
    • 如果直接查询以非零状态退出,或返回无效的 artifact-instruction JSON,请报告错误,并在写入任何 main spec 前停止。不要将失败视为不存在规则集。
    • 如果响应有效但省略了
      rules
      ,则表示未配置 artifact rules,继续执行现有的语义合并。
    返回的
    rules
    仅适用于此次合并所生成 main specs 的内容和形式。Artifact rules 不是操作指引,不能更改 selected roots、delta paths、CLI 检查或 workflow 步骤。应将其文本作为约束使用,但不要逐字复制到 main spec 或摘要中。
    对第 3 步中选定的每个 capability delta spec 路径——即完整的
    existingOutputPaths
    列表,或调用方提供的缩小子集(这些路径可能属于选定的 store,而非当前 repo):
    a. 读取 delta spec,以了解预期的变更
    b. 读取 main spec,其路径为
    <planningHome.root>/openspec/specs/<capability-path>/spec.md
    (可能尚不存在)
    c. 智能应用变更:
    ADDED Requirements:
    • 如果 main spec 中不存在该 requirement,则添加它
    • 如果 requirement 已经存在,则更新它以匹配 delta(视为隐式 MODIFIED)
    MODIFIED Requirements:
    • 在 main spec 中找到该 requirement
    • 应用变更——具体包括:
      • 添加 main spec 中尚不存在的 scenarios
      • 修改现有 scenarios
      • 更改 requirement 描述
    • 保留 delta 未提及的 scenarios 和内容
    REMOVED Requirements:
    • 从 main spec 中移除整个 requirement block
    • 退役该 capability。只有在以下所有条件均成立时,才删除整个
      spec.md
      ;如果删除后目录中已无其他内容,再删除该目录:
      1. 本次运行移除 requirements 后没有留下任何 requirement blocks;
      2. spec 的其余部分格式正确(仍包含
        ## Purpose
        );
      3. main spec 在此次同步前并非空——如果本次没有移除任何内容,则不进行修改;
      4. 整个文件中每个其他非空行都可归为标题、Purpose、Requirements header,或规范 requirement 的 statement、scenarios 或 fenced examples;
      5. 该 change 的
        .openspec.yaml
        声明了
        retire_capabilities: true
        ;
      6. spec.md
        解析后的路径位于真实 specs root 内(不要跟随 capability 目录的 symlink 去删除外部文件)。 如果移除选定 requirements 后将不留下任何 requirement blocks,且任一退役条件不满足,则不要修改 main spec。停止该 capability 的同步,报告阻塞条件,并告诉用户如何解决。绝不能写入或保留空的
        ## Requirements
        section。 如果仅缺少该标记,也要明确指出——用户只需补充它即可完成退役。
    • 删除该文件也会删除其
      ## Purpose
      ;任何其他 section 都会阻止退役。报告退役情况时,要明确指出 Purpose。仅当该 spec 位于调用方的 checkout 中时,才提供可直接粘贴的
      git checkout
      ;否则提供限定于该 checkout 的恢复指引。
    RENAMED Requirements:
    • 找到 FROM requirement,将其重命名为 TO
    Delta 中的
    ## Purpose
    :
    • Main spec 已经有一个 Purpose,并且它是权威版本——不要修改它(
      openspec archive
      也是这样处理;它会发出警告并继续)
    d. 如果 capability 尚不存在,则创建新的 main spec:
    • 创建
      <planningHome.root>/openspec/specs/<capability-path>/spec.md
    • 添加 Purpose section:如果 delta 中有 Purpose 正文,则逐字复制;仅在 delta 没有 Purpose 时,才写入简短的 TBD 占位内容
    • 添加 Requirements section,并包含 ADDED requirements
    • 遵循下方的 Main Spec 格式参考
  5. 验证更新后的 main specs
    使用与前面相同的 selected-root 参数运行
    openspec validate --specs
    。如果验证失败,请报告问题,并且不要声称同步已成功。
  6. 显示摘要
    应用完所有变更后,汇总:
    • 哪些 capabilities 已更新
    • 进行了哪些变更(requirements 新增、修改、移除或重命名)
    • 是否有新建的 main spec 仍留有 TBD Purpose 占位内容,以便立即补写,而不是继续搁置
    • 是否有 capability 已退役;注明被删除的
      spec.md
      、其 Purpose,以及可直接粘贴的
      git checkout
      或限定于该 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

**Main Spec 格式参考**

Main specs 是 delta 合并的目标。它们绝不能包含 delta 操作标题(`## ADDED/MODIFIED/REMOVED/RENAMED Requirements`)——同步后,每个 requirement 都必须位于单个 `## Requirements` section 下:

```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 block 包含完整 requirement——正文以及变更后保留的每个 scenario。`openspec validate` 和 `openspec archive` 都会拒绝任何丢弃 main spec 中仍然存在的 scenario 的 block。
- 保留 delta 未提及的所有内容,并维持其在 main spec 中的现有顺序
- 运用判断,以合理方式合并变更

**成功时的输出**

```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 和 main specs
- 保留 delta 未提及的现有内容
- 绝不将 delta 文件原样复制到 main spec——应合并其内容,使 main spec 保持 Main Spec 格式参考的结构,并且不包含 delta 操作标题
- 如果有不清楚之处,请询问用户
- 执行过程中展示正在进行的更改
- 操作应具备幂等性——运行两次应得到相同结果
- 仅使用 `artifactPaths.specs.existingOutputPaths`;绝不从无关 artifacts 推断 delta specs
- 遵循调用方提供的 `existingOutputPaths` 子集;绝不重新扩大为完整列表
- 直接同步时只获取一次 specs instructions,或复用 Archive 内联提供的快照
- 每次写入 main spec 前,如果 specs-instruction 响应以非零状态退出或包含无效 JSON,则停止操作
- Artifact rules 仅约束正在写入的 specs,且绝不复制到输出文件中