Problem
OpenSpec stores are great for cross-repo planning, but there is no way to set a machine-level default store. Today, the only way to make a repo use a store without --store on every command is to add store: <id> to that repo's openspec/config.yaml:
# web-app/openspec/config.yaml
store: team-plans
If you work across many repos that all share the same planning store, you must repeat this line in every repo. There is no global equivalent.
The global config (~/.config/openspec/config.json, managed by openspec config) has no store or defaultStore field — verified in src/core/config-schema.ts and src/core/global-config.ts. The GlobalConfig interface only defines featureFlags, profile, delivery, workflows, and openers. Even if you manually add a store key to the JSON, no code path reads it: root-selection.ts only consults the project-level config.yaml for store pointers.
Proposal
Add a defaultStore field to the global config that acts as a fallback in root resolution — consulted only when no --store flag, no local planning root, and no project-level store: pointer resolve.
Root resolution order (proposed change)
1. --store <id> explicit → that store
2. nearest openspec/ local planning root → this repo
(walk-up from cwd,
qualified)
3. store: pointer project config.yaml → that store
4. defaultStore (NEW) global config.json → that store
(machine-level fallback)
5. none of the above stores registered? → error with
selection hint
Step 4 is the only new step. It preserves all existing precedence: --store still wins, local planning roots still win, project-level pointers still win. It only changes the failure path — instead of erroring, it tries the global default first.
Configuration
Or via CLI:
openspec config set defaultStore team-plans
Schema change
Add to GlobalConfigSchema in src/core/config-schema.ts:
defaultStore: z
.string()
.optional()
.describe('Store id used as fallback root when no explicit --store, local root, or project-level store: pointer resolves'),
And to the GlobalConfig interface in src/core/global-config.ts:
Resolution logic
In root-selection.ts, after the nearest-root and project-level pointer checks fail, before the final error, consult getGlobalConfig().defaultStore:
// Step 4 (new): global defaultStore fallback
const globalConfig = getGlobalConfig();
if (globalConfig.defaultStore) {
return resolveStoreRoot(globalConfig.defaultStore, globalDataDir, 'declared');
}
// Step 5: error with selection hint (existing behavior)
If the defaultStore id is not registered, it should degrade to the existing error with a hint to either register it or clear the stale global default — same pattern as unresolvable project-level store: pointers.
Why this matters
- Reduces friction for single-store workflows. A solo developer or small team with one planning repo should not need to touch every code repo's
openspec/config.yaml just to point at the same store.
- Makes stores more approachable. Today the cognitive load is: "set up store → register → remember
--store everywhere OR edit every repo's config.yaml." With this, it becomes: "set up store → register → set global default → done."
- Non-breaking. It is purely additive to the resolution chain, inserted before the error case. Existing users see no behavior change unless they opt in.
Design considerations
-
Why not reuse the store key name? Using defaultStore makes the intent clear and avoids confusion with the project-level store: pointer (which is a direct declaration, not a fallback). Different name, different semantics.
-
Should this be in the stores beta docs? Yes — the Stores User Guide would get a short section under "Declaring a default store" covering both per-repo (store: in config.yaml) and per-machine (defaultStore in global config) options.
-
Interaction with openspec config. openspec config set defaultStore <id> and openspec config get defaultStore should work. validateConfigKeyPath in config-schema.ts would need defaultStore added to KNOWN_TOP_LEVEL_KEYS.
-
What if the store is not registered? Same as an unresolvable project-level pointer: degrade to the error with a selection hint, plus a note that the global defaultStore is stale and how to clear it (openspec config set defaultStore "" or similar).
Scope
This is a small change:
src/core/config-schema.ts — add field to schema + known keys
src/core/global-config.ts — add field to interface + defaults
src/core/root-selection.ts — insert fallback step before error
docs/stores-beta/user-guide.md — document the new option
docs/cli.md — mention in config section
No changes to store registration, metadata, or existing resolution semantics.
Problem
OpenSpec stores are great for cross-repo planning, but there is no way to set a machine-level default store. Today, the only way to make a repo use a store without
--storeon every command is to addstore: <id>to that repo'sopenspec/config.yaml:If you work across many repos that all share the same planning store, you must repeat this line in every repo. There is no global equivalent.
The global config (
~/.config/openspec/config.json, managed byopenspec config) has nostoreordefaultStorefield — verified insrc/core/config-schema.tsandsrc/core/global-config.ts. TheGlobalConfiginterface only definesfeatureFlags,profile,delivery,workflows, andopeners. Even if you manually add astorekey to the JSON, no code path reads it:root-selection.tsonly consults the project-levelconfig.yamlfor store pointers.Proposal
Add a
defaultStorefield to the global config that acts as a fallback in root resolution — consulted only when no--storeflag, no local planning root, and no project-levelstore:pointer resolve.Root resolution order (proposed change)
Step 4 is the only new step. It preserves all existing precedence:
--storestill wins, local planning roots still win, project-level pointers still win. It only changes the failure path — instead of erroring, it tries the global default first.Configuration
Or via CLI:
openspec config set defaultStore team-plansSchema change
Add to
GlobalConfigSchemainsrc/core/config-schema.ts:And to the
GlobalConfiginterface insrc/core/global-config.ts:Resolution logic
In
root-selection.ts, after the nearest-root and project-level pointer checks fail, before the final error, consultgetGlobalConfig().defaultStore:If the
defaultStoreid is not registered, it should degrade to the existing error with a hint to either register it or clear the stale global default — same pattern as unresolvable project-levelstore:pointers.Why this matters
openspec/config.yamljust to point at the same store.--storeeverywhere OR edit every repo's config.yaml." With this, it becomes: "set up store → register → set global default → done."Design considerations
Why not reuse the
storekey name? UsingdefaultStoremakes the intent clear and avoids confusion with the project-levelstore:pointer (which is a direct declaration, not a fallback). Different name, different semantics.Should this be in the stores beta docs? Yes — the Stores User Guide would get a short section under "Declaring a default store" covering both per-repo (
store:inconfig.yaml) and per-machine (defaultStorein global config) options.Interaction with
openspec config.openspec config set defaultStore <id>andopenspec config get defaultStoreshould work.validateConfigKeyPathinconfig-schema.tswould needdefaultStoreadded toKNOWN_TOP_LEVEL_KEYS.What if the store is not registered? Same as an unresolvable project-level pointer: degrade to the error with a selection hint, plus a note that the global
defaultStoreis stale and how to clear it (openspec config set defaultStore ""or similar).Scope
This is a small change:
src/core/config-schema.ts— add field to schema + known keyssrc/core/global-config.ts— add field to interface + defaultssrc/core/root-selection.ts— insert fallback step before errordocs/stores-beta/user-guide.md— document the new optiondocs/cli.md— mention in config sectionNo changes to store registration, metadata, or existing resolution semantics.