Skip to content

After /opsx-archive executes successfully, sync is not executed. #799

Description

@lycfr
cfr@bogon ~ % openspec --version
1.2.0
Image Image

Activity

  1. BCEM commented on Mar 4, 2026

    @BCEM

    Also noticed that the main spec file is not updating: after each archive command execution an agent mentioned that /spec is empty and no action required.
    Looks related to changes in the latest release.

  2. sanzoghenzo commented on Mar 5, 2026

    @sanzoghenzo

    If I understand correctly, I also have this problem, at least for changes that create new specs that aren't already in the specs folder.

    It happened to me with both version 1.1.1 and 1.2.0. I had to manually instruct to copy over the spec.

    OpenSpec on OpenCode + GPT 4.1

  3. svaite commented on Mar 5, 2026

    @svaite

    If I understand correctly, I also have this problem, at least for changes that create new specs that aren't already in the specs folder.

    It happened to me with both version 1.1.1 and 1.2.0. I had to manually instruct to copy over the spec.

    OpenSpec on OpenCode + GPT 4.1

    Same behavior on Ubuntu 25 throught Opencode

  4. brainwang commented on Apr 9, 2026

    @brainwang

    Same for me. Has it been confirmed as a BUG? any quick fix/workaround?

  5. HowardYan888 commented on Apr 9, 2026

    @HowardYan888
    Contributor

    is some file is holding by other editors?

  6. BCEM commented on Apr 9, 2026

    @BCEM

    In my case it was caused by old models (like gpt 4.1) which are not working well with openspec scripts and instructions. More advanced models are capable to do this pretty well. Also, models are being confused sometimes by the outputs of CLI commands like "no matching specs".

  7. brainwang commented on Apr 9, 2026

    @brainwang

    is some file is holding by other editors?

    I do not think so.

  8. brainwang commented on Apr 9, 2026

    @brainwang

    In my case it was caused by old models (like gpt 4.1) which are not working well with openspec scripts and instructions. More advanced models are capable to do this pretty well. Also, models are being confused sometimes by the outputs of CLI commands like "no matching specs".

    Then is there any explict approach to make it always ok?

    Additionly, how can I make the workflow did not jump to next phase automatically, and let me to explict do it with skill/command call?

  9. BCEM commented on Apr 10, 2026

    @BCEM

    Experiment. The workflow is defined in .md files - add some extra instructions like "you MUST sync delta specs" etc - just explain the problem to AI and ask for prompt correction.

  10. gonefish commented on May 13, 2026

    @gonefish

    Does the init command not copy the opsx command and skills?

  11. brainwang commented on May 13, 2026

    @brainwang

    @gonefish No, it copied.

  12. clay-good commented on Aug 3, 2026

    @clay-good
    Collaborator

    Verified as resolved on current main; this is ready to close. The archive workflow now makes a requested sync synchronous, verifies the resulting main specs, and refuses to archive if the sync did not actually land.

    Proof:

    • The merged fix is fix(templates): don't archive a change before its spec sync finishes #1394 (fix(templates): don't archive a change before its spec sync finishes): fix(templates): don't archive a change before its spec sync finishes #1394
    • Current archive instructions require the sync to run inline and finish before the change is moved:
      - "Sync now" or "Sync anyway" — sync, then verify (below)
      - Anything else — ask again rather than archiving
      Before a selected sync writes any main spec, run
      \`openspec instructions specs --change "<name>" --json\` once with the same
      selected-root flags. Require a zero exit status and valid artifact-instruction
      JSON. If the lookup fails or returns invalid JSON, report the error and stop
      before writing any main spec or moving the change. A valid response with omitted
      \`rules\` is the no-rules case. Apply returned \`rules\` only to the content and
      form of main specs produced by this merge; do not use them as archive guidance,
      change CLI behavior, or copy the rule text into any output file.
      Then run the \`openspec-sync-specs\` workflow inline (agent-driven intelligent merge) for change '<name>', passing the delta spec analysis and the fetched specs-rule snapshot from above, and wait for it to finish. The inline sync must reuse that snapshot without fetching \`specs\` instructions again. Do not delegate it to a background task — step 5 would move \`changeRoot\` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
    • They then recompare every delta capability against the main specs and explicitly stop without archiving on any failure or mismatch:
      Then re-run the comparison from the top of this step against every capability that has a delta spec in \`artifactPaths.specs.existingOutputPaths\` — not only the ones the sync reports it touched. A successful sync leaves nothing left to apply, so each capability must now read as already synced:
      - ADDED requirements present
      - MODIFIED requirements carrying the scenario and description changes named in the delta, with their other scenarios intact
      - REMOVED requirements gone
      - RENAMED requirements present under the new name and absent under the old one
      If the sync failed, or any capability does not match, report what differs and stop — do not archive. Nothing has moved and \`changeRoot\` is intact, so the user can fix the mismatch or re-run the sync and start the archive again.
      5. **Perform the archive**
    • Regression coverage pins both the single-change and bulk-archive ordering/verification contracts:
      it('gates the archive on a completed spec sync (#1393)', () => {
      const generatedSkill = generateSkillContent(getArchiveChangeSkillTemplate(), 'PARITY-BASELINE');
      const commandContent = getOpsxArchiveCommandTemplate().content;
      // The single archive skill references openspec-sync-specs; opsx command references /opsx:sync.
      expect(generatedSkill, 'skill').toContain('run the `openspec-sync-specs` workflow inline');
      expect(commandContent, 'opsx command').toContain('run the `/opsx:sync` workflow inline');
      const variants: Array<[string, string]> = [
      ['skill', generatedSkill],
      ['opsx command', commandContent],
      ];
      for (const [variant, content] of variants) {
      expect(content, variant).toContain('Do not delegate it to a background task');
      expect(content, variant).toContain('Never archive while a spec sync is still in flight');
      // Verification must follow delta semantics.
      expect(content, variant).toContain('MODIFIED requirements carrying the scenario and description changes');
      expect(content, variant).toContain('REMOVED requirements gone');
      expect(content, variant).toContain('RENAMED requirements present under the new name and absent under the old one');
      // Verification is bound to the delta specs on disk, not to whatever the sync reports it touched.
      expect(content, variant).toContain('not only the ones the sync reports it touched');
      // Main spec paths are store-root aware
      expect(content, variant).toContain('<planningHome.root>/openspec/specs/<capability>/spec.md');
      }
      });
      it('gates bulk archive on inline synchronous spec sync and verification before moving change root', () => {
      const generatedSkill = generateSkillContent(getBulkArchiveChangeSkillTemplate(), 'PARITY-BASELINE');
      const commandContent = getOpsxBulkArchiveCommandTemplate().content;
      // The bulk archive skill references openspec-sync-specs; opsx command references /opsx:sync.
      expect(generatedSkill, 'bulk skill').toContain('run the `openspec-sync-specs` workflow inline');
      expect(commandContent, 'bulk opsx command').toContain('run the `/opsx:sync` workflow inline');
      const variants: Array<[string, string]> = [
      ['bulk skill', generatedSkill],
      ['bulk opsx command', commandContent],
      ];
      for (const [variant, content] of variants) {
      expect(content, variant).toContain('Do not delegate to a background task');
      expect(content, variant).toContain('Never archive a change while a spec sync is still in flight');
      expect(content, variant).toContain('Verify included delta specs before moving changeRoot');
      // Verification must follow delta semantics.
      expect(content, variant).toContain('MODIFIED requirements carrying scenario and description changes');
      expect(content, variant).toContain('REMOVED requirements gone');
      expect(content, variant).toContain('RENAMED requirements present under the new name and absent under the old one');
    • I generated a fresh Claude/core installation from main (45cca5db) and confirmed those synchronous-sync and post-sync-verification guardrails are present in the emitted openspec-archive-change/SKILL.md. The focused init/profile/template/adapter suite passed all 1,167 tests.

    The explicit “Archive without syncing” choice remains intentional; this fix covers the reported failure mode where a sync is selected/expected but archive completes before it is executed. The separate greenfield-main-spec discussion in #1222/#1264 remains open and is not being collapsed into this closure.

    Thank you to everyone who supplied model/tool/version details; those reports are what made the missing synchronization barrier clear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions