Skip to content

feat(stores): set one default store for every repo on your machine#1363

Merged
clay-good merged 2 commits into
mainfrom
feat/global-default-store
Jul 17, 2026
Merged

feat(stores): set one default store for every repo on your machine#1363
clay-good merged 2 commits into
mainfrom
feat/global-default-store

Conversation

@clay-good

@clay-good clay-good commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Closes #1359.

What was missing

Stores let many code repos share one planning repo, but there was no machine-level default. To use a store without typing --store on every command, you had to add store: <id> to every repo's openspec/config.yaml. No global equivalent existed.

What it does

Adds defaultStore to the global config. Set it once:

openspec config set defaultStore team-plans

Now any command run outside a planning root — with no --store and no project pointer — resolves to that store. It's the last step in root resolution, so nothing about precedence changes:

1. --store <id>        explicit                → that store
2. nearest openspec/   local planning root     → this repo
3. store: pointer      project config.yaml     → that store
4. defaultStore (NEW)  global config           → that store   ← only new step
5. none of the above   registered stores?      → selection-hint error

Purely additive: --store, a local root, and a project store: pointer all still win. Existing users see no change unless they opt in. A stale or unregistered defaultStore degrades to the existing error, reshaped to say how to fix or clear it.

The fallback carries its own provenance: the root banner and every JSON root block (status, context, doctor, list, …) report source: "global_default", so users and tooling can tell a machine-wide default from a repo's store: pointer (review feedback from alfred).

Proof it works

Real output from the built CLI (bin/openspec.js) against a registered team-plans store, run from a scratch dir with no local root:

Before — no default set:

✖ Error: No OpenSpec root found in the current directory or its ancestors.
  Registered stores: team-plans. Pass --store <id> to use one, or run openspec init.

After openspec config set defaultStore team-plans:

Using OpenSpec root: team-plans (/private/var/.../team-plans)
No active changes found.

Stale default (unregistered id):

✖ Error: Global defaultStore 'ghost-plans': Unknown store 'ghost-plans'. No stores are registered.
  Fix: Register the store (openspec store register <path> --id ghost-plans)
       or clear the stale global default (openspec config unset defaultStore).

config get/unset defaultStore round-trip; unsetting reverts to the selection-hint error.

Tests

  • test/core/root-selection.test.ts — 6 new cases: resolves the default (with source: 'global_default'); local root, project pointer, and --store each win over it; stale-default degrade error; falls through to the hint when unset. Harness now pins XDG_CONFIG_HOME so no test reads the developer's real config.
  • test/commands/config.test.tsset/get/unset defaultStore round-trip + key validation (accepts defaultStore, rejects nested keys).
  • test/commands/global-default-store.test.ts — CLI-level: status --json reports the global_default root block and the human banner; a stale default surfaces unknown_store in the JSON failure payload with the clearing fix.
  • test/commands/doctor.test.ts, test/commands/context.test.ts — a global-default session added to each "all session shapes" test: both JSON payloads report source: "global_default" with the store id.

Full suite green except the 17 pre-existing environment-only zsh-installer failures (oh-my-zsh; unaffected by this change).

Scope

config-schema.ts (schema field + known key), global-config.ts (interface field), root-selection.ts (fallback step + degrade helper + global_default source value), docs (stores user guide, cli.md, agent-contract.md §3), changeset. No changes to store registration, metadata, or existing resolution precedence.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a machine-wide defaultStore configuration option for OpenSpec.
    • When no --store, local root, or project store pointer is available, commands can fall back to the configured default store.
    • Added openspec config set defaultStore <id> and openspec config unset defaultStore.
  • Bug Fixes

    • Improved diagnostics when the configured defaultStore is missing/unregistered, including actionable fix guidance.
  • Documentation

    • Updated CLI, stores beta user guide, and root-resolution/agent contract docs to describe the new fallback precedence and root.source: "global_default".
  • Tests

    • Added integration tests covering global default fallback across config, context, doctor, status, and root-selection behavior.

Adds a machine-level `defaultStore` to the global config. When no --store
flag, local planning root, or project-level `store:` pointer resolves, root
resolution now consults `defaultStore` before erroring — so users who plan
many code repos into one store can set it once instead of editing every
repo's openspec/config.yaml.

Purely additive: existing precedence (--store > local root > project pointer)
is unchanged; the fallback only replaces the failure path. A stale or
unregistered defaultStore degrades to the existing error, reshaped to point
at clearing the global default.

Closes #1359

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@clay-good
clay-good requested a review from TabishB as a code owner July 15, 2026 14:26
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds defaultStore to global configuration and uses it as a fallback during OpenSpec root resolution. CLI and stores documentation, configuration lifecycle tests, precedence tests, command integration coverage, and stale-default error handling are included.

Changes

Global default store fallback

Layer / File(s) Summary
Configuration contract and CLI lifecycle
src/core/config-schema.ts, src/core/global-config.ts, test/commands/config.test.ts
Adds the optional defaultStore setting, accepts it as a top-level key, and tests setting, reading, unsetting, and nested-key validation.
Root resolution fallback
src/core/root-selection.ts, test/core/root-selection.test.ts
Resolves the configured default after explicit, local, and project-level store sources; tests precedence, stale defaults, and fallback error behavior.
Command integration and documentation
test/commands/context.test.ts, test/commands/doctor.test.ts, test/commands/global-default-store.test.ts, src/commands/doctor.ts, docs/agent-contract.md, docs/cli.md, docs/stores-beta/user-guide.md, .changeset/add-global-default-store.md
Verifies global-default roots in command outputs and documents configuration, precedence, provenance, and stale-store handling.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Command
  participant resolveOpenSpecRoot
  participant GlobalConfig
  participant StoreRegistry
  Command->>resolveOpenSpecRoot: resolve without explicit store
  resolveOpenSpecRoot->>GlobalConfig: read defaultStore
  GlobalConfig-->>resolveOpenSpecRoot: store id
  resolveOpenSpecRoot->>StoreRegistry: resolve default store
  StoreRegistry-->>resolveOpenSpecRoot: root or stale-store error
  resolveOpenSpecRoot-->>Command: selected root or guidance
Loading

Suggested reviewers: tabishb, alfred-openspec

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed [#1359] The PR adds global defaultStore config, fallback root resolution, stale-default handling, config CLI support, docs, and tests as requested.
Out of Scope Changes check ✅ Passed No unrelated code changes are evident; the docs, contract, and tests all support the new defaultStore feature.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: adding a machine-level default store fallback for repos on the same machine.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/global-default-store

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 15, 2026

Copy link
Copy Markdown

Deploying openspec-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: c6d39d2
Status: ✅  Deploy successful!
Preview URL: https://133c83dd.openspec-docs.pages.dev
Branch Preview URL: https://feat-global-default-store.openspec-docs.pages.dev

View logs

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The global fallback currently calls resolveStoreRoot with source declared, so status, context, and doctor JSON report the same provenance as a repo store pointer. Please add a distinct OpenSpecRootSource value such as global_default and cover the public JSON surfaces so users and tooling can tell why the store was selected.

The machine-level defaultStore fallback resolved with source
'declared', so status, context, and doctor JSON could not tell a
global default from a repo's store: pointer (review feedback).
Add 'global_default' to OpenSpecRootSource, resolve the fallback
with it, and cover the status, context, and doctor JSON surfaces
plus the agent contract and store docs. Add the missing changeset.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@clay-good

Copy link
Copy Markdown
Collaborator Author

Addressed the review in c6d39d2:

  • Added a distinct global_default value to OpenSpecRootSource; the machine-level fallback now resolves with it instead of declared.
  • Covered the public JSON surfaces: status --json (new test/commands/global-default-store.test.ts, including the stale-default failure payload), plus global-default sessions in the doctor and context "all session shapes" tests — all report source: "global_default" with the store id.
  • Updated docs/agent-contract.md §3 (precedence + RootOutput union), docs/cli.md, and the stores user guide; added the missing changeset.

Full suite green except the 17 pre-existing environment-only zsh-installer failures.

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The remediation cleanly introduces global_default throughout root selection, status, context, and doctor JSON, docs, and stale-default tests. The original provenance blocker is resolved and current CI is green.

@clay-good
clay-good added this pull request to the merge queue Jul 17, 2026
Merged via the queue into main with commit 5199f41 Jul 17, 2026
15 checks passed
@clay-good
clay-good deleted the feat/global-default-store branch July 17, 2026 20:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Add defaultStore to global config as machine-level fallback in root resolution

2 participants