Skip to content

spec-driven's specs instruction sends main-spec reads and edits to a cwd-relative path, so they miss the store #1702

Description

@clay-good

Summary

The specs artifact instruction tells an agent to read and edit a capability's main spec at openspec/specs/<capability-path>/spec.md — a path relative to the current directory. When the change lives in a registered store, the main spec lives under the store root, so that path is wrong.

The workflow templates already solved this. sync-specs.ts:44 and archive-change.ts:98 both say main specs live under `<planningHome.root>/openspec/specs/` — "use that (store-aware) root for every main-spec path below, not a hardcoded repo path." The schema instruction is the site that was missed.

The two affected operations

schemas/spec-driven/schema.yaml:

Line Text Operation
103 edit `openspec/specs/<capability-path>/spec.md` directly write (fixing a TBD Purpose)
106 Locate the existing requirement in openspec/specs/<capability-path>/spec.md read (step 1 of the MODIFIED workflow)

Two other mentions (lines 17 and 75) only describe the shape of a capability path rather than instructing a file operation, so they are not affected.

Reproduction

export XDG_DATA_HOME=/tmp/os-demo/data XDG_CONFIG_HOME=/tmp/os-demo/cfg
openspec store setup mystore --path /tmp/os-demo/mystore
mkdir -p /tmp/os-demo/mystore/openspec/specs/store-only-capability
# ...write a main spec there...

mkdir -p /tmp/os-demo/work && cd /tmp/os-demo/work   # a *different* repo
openspec new change modify-store-cap --store mystore
openspec instructions specs --change modify-store-cap --store mystore --json

The returned instruction contains both raw paths, while the very same JSON response reports the correct root:

"planningHome": { "root": "/tmp/os-demo/mystore", ... },
"root": { "path": "/tmp/os-demo/mystore", "source": "store", "store_id": "mystore" }

ls openspec/specs/ in the working repo does not contain store-only-capability — it is in the store.

Why it matters

The MODIFIED workflow's first step is "locate the existing requirement," and step 2 is "copy the ENTIRE requirement block." Sending that read to the wrong root produces one of two outcomes:

  1. The path does not exist. The agent cannot copy the block and may write a MODIFIED delta from memory — which validate rejects for dropping scenarios, or which loses content at archive time.
  2. Worse: a local capability happens to share the name. The read succeeds against a different capability, and the agent copies the wrong requirement block into the delta. This fails silently.

The Purpose edit at line 103 has the same split: it would write to a local file that is not the spec being described.

Suggested fix

Use the store-aware root already returned in the instructions JSON, matching the wording the workflow templates use:

edit `<planningHome.root>/openspec/specs/<capability-path>/spec.md` directly
1. Locate the existing requirement in `<planningHome.root>/openspec/specs/<capability-path>/spec.md`

Guidance text only — no CLI, parser, or archive behavior changes.

Notes

Found while hardening #1700 (which fixes #1689). Kept separate deliberately: #1700 is scoped to teaching the spec-inventory verb, and these two lines predate it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions