Skip to content

Feature: Store scoping — project-level store registry #1950

Description

@BaurinVladislav

Feature: Store scoping — project-level store registry

Problem

OpenSpec stores (beta) use a machine-level registry (~/.local/share/openspec/stores/registry.yaml) that maps store IDs to absolute filesystem paths. This creates two problems:

1. Manual registration on every machine.

After cloning a repository that contains a store, every developer must run openspec store register <path> to make it discoverable. There is no way to declare store bindings inside the repository and have OpenSpec discover them automatically. The references: declaration in a code repo's config.yaml can carry a remote: URL, but openspec doctor still requires the store to be registered — it can only print a fix command, not resolve the store on its own.

2. One checkout per store ID per machine.

Registering a second checkout under the same store ID fails. If a developer has two copies of the same store on the same machine (e.g., two clones of the same repository), only one can be registered; the other is unreachable via --store, references:, or store:.

Proposed behavior

Add an optional project-scoped store registry: a file (e.g., .openspec-store/registry.yaml) that maps store IDs to paths relative to that file.

# .openspec-store/registry.yaml (committed)
stores:
  platform-specs:
    path: platform-specs
  design-system-specs:
    path: design-system-specs

Resolution order when resolving a store ID (--store, references:, store: in config.yaml):

  1. Project-scoped registry — walk up from cwd, find nearest .openspec-store/registry.yaml, resolve ID to a relative path. No machine-level registration needed.
  2. Global registry (~/.local/share/openspec/stores/registry.yaml) — current behavior, fallback.
  3. Error with guidance.

This discovery pattern is well-established in the Node.js ecosystem — npm, yarn, and pnpm all discover config files by walking up the directory tree (package.json, .npmrc, .yarnrc). The same mechanism is already used by OpenSpec for finding the nearest openspec/ root.

Benefits

  • No manual registration — store bindings are committed; cloning the repository is enough.
  • Multiple checkouts work — each checkout resolves stores relative to its own root; the global registry is not involved.
  • Project isolation — different projects on the same machine can use the same store ID without collision.
  • Backward-compatible — global registry remains the fallback; existing setups are unaffected.
  • No new mandatory commands — the file can be created manually or generated by an optional openspec store register --scope project.

Use cases

UC-1: Store alongside a code repository

A developer clones a store and a code repository separately, placing them side by side on disk:

~/dev/
├── platform-specs/          ← store (cloned separately)
│   └── openspec/
└── api-server/              ← code repository
    ├── .openspec-store/registry.yaml   ← path: ../platform-specs
    └── openspec/config.yaml            ← references: [platform-specs]

The registry.yaml maps the store ID to a relative path. In this case it is recommended not to commit .openspec-store/registry.yaml (add to .gitignore), because the relative path depends on the developer's local directory layout.

cd ~/dev/api-server
openspec new change add-login --store platform-specs   # works immediately

No openspec store register needed. The store is resolved relative to the code repository's root.

UC-2: Meta-repo pattern

A team uses a single git repository that assembles multiple related repositories as submodules. One or more submodules are OpenSpec stores; the others are code repositories that reference those stores.

meta-repo/                          ← single git repo (submodules)
├── .openspec-store/registry.yaml   ← project-scoped registry
├── platform-specs/                 ← store submodule
│   └── openspec/
├── design-system-specs/            ← another store submodule
│   └── openspec/
├── api-server/                     ← code repo submodule
│   └── openspec/config.yaml        ← references: [platform-specs, design-system-specs]
├── web-client/                     ← code repo submodule
│   └── openspec/config.yaml        ← references: [platform-specs]
└── ...

A developer joins the team:

git clone --recurse-submodules <meta-repo-url>
cd meta-repo/api-server
openspec new change add-login --store platform-specs   # works immediately
openspec instructions                                  # references: [platform-specs] resolved

No openspec store register needed. No openspec doctor needed before first use — the project-scoped registry is discovered automatically during store ID resolution.

UC-3: Multiple clones of the same repository

A developer works on two features in parallel:

git clone --recurse-submodules <meta-repo-url> feature-a
git clone --recurse-submodules <meta-repo-url> feature-b

Both clones work independently — each has its own .openspec-store/registry.yaml, resolving platform-specs to its own platform-specs/ submodule. No conflict with the global registry, no unregister/register dance.

UC-4: Multiple projects on the same machine

A developer participates in two separate projects, each containing a store with the same ID:

~/dev/project-a/          ← clone of project A
├── .openspec-store/registry.yaml
└── platform-specs/       ← store for project A

~/dev/project-b/          ← clone of project B
├── .openspec-store/registry.yaml
└── platform-specs/       ← store for project B (same ID, different content)

Both projects can use the same store ID platform-specs without collision — each resolves it relative to its own project-scoped registry. This provides isolation between projects on the same machine.

UC-5: Existing setup (no regression)

A team that clones stores separately and registers them globally — no .openspec-store/registry.yaml exists, global registry is used as before.

Activity

  1. BaurinVladislav commented on Sep 21, 2026

    @BaurinVladislav
    Author

    Related: #1714 covers the same core limitation (one checkout per store ID per machine) from a git worktree perspective.

    How this proposal differs:

    #1714 suggests worktree-aware resolution — CLI detects worktrees via git worktree list and prefers the enclosing checkout. This works for git worktrees but not for independent clones, side-by-side repositories, or project isolation.

    This proposal (project-scoped registry) solves the same problem with a file-based approach:

    • Works for any directory layout, not just git worktrees
    • No git introspection required — just file discovery (same mechanism already used for openspec/ root)
    • Eliminates manual registration entirely (not just for worktrees)
    • Enables project isolation (same store ID in different projects without collision)

    Both approaches could coexist, but project-scoped registry covers a broader set of use cases with a simpler mechanism.

  2. BaurinVladislav commented on Sep 22, 2026

    @BaurinVladislav
    Author

    We've prepared a change spec for this feature: #1956

    The PR contains the full OpenSpec change proposal:

    • proposal.md — why and what
    • specs/store-discovery/spec.md — 6 requirements, 16 scenarios
    • design.md — 8 design decisions, including 3 approaches for registry merge semantics (merge / isolation / merge with source indication) with rationale for the chosen approach
    • tasks.md — 19 implementation tasks across 7 groups

    Validated with openspec validate --strict. No code changes — planning artifacts only.

    @clay-good could you review the spec in the PR? Would appreciate feedback on the design decisions, especially D8 (merge semantics).

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

    design-reviewNeeds product/design decision

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions