openspec/work/simplify-context-and-workspace-model/slices/store-root-parity/plan.md
Planned.
This plan follows the slice spec after the 2026-06-10 product review decisions. It is written as an implementation plan, but the product contract comes first: humans and agents should experience a context store as a normal OpenSpec root with one thin identity file.
Start from spec.md.
Also keep these nearby artifacts in view:
../../goal.md../../roadmap.md../../../AGENTS.mdThe core model for this slice is:
context store = normal OpenSpec root + .openspec-store/store.yaml
That means durable planning state lives in normal OpenSpec artifacts:
context-store-root/
.openspec-store/
store.yaml
openspec/
config.yaml
specs/
changes/
archive/
.openspec-store/store.yaml is identity metadata only. It is not a planning
model, workspace model, initiative model, migration marker, or compatibility
contract for old beta files.
What the human wants:
What the agent needs to know:
Where the work lives:
openspec/..openspec-store/store.yaml.How the user knows it worked:
created_files.context-store doctor --json reports openspec_root separately from
metadata and git.Make context-store setup, context-store register, and
context-store doctor agree on one product shape:
openspec init from context-store setup or register.openspec/, a config file
(openspec/config.yaml or openspec/config.yml), openspec/specs/,
openspec/changes/, and openspec/changes/archive/.openspec/config.yaml with the default
spec-driven schema..openspec-store/store.yaml
should succeed and only update local registry state when needed.--yes on context-store register as the
explicit confirmation for this slice. Without it, JSON/non-interactive mode
refuses before writing metadata or registry state.initiatives/, .openspec-workspace/,
workspace.yaml, AGENTS.md, .codex/, .claude/, and .cursor/ are
ignored. They are not migrated, deleted, repaired, or treated as proof of a
healthy root.created_files.openspec_root, separate from metadata
and git, and never repairs while inspecting.A human or agent asks OpenSpec to create a new context store in a missing or empty directory.
Expected result:
.openspec-store/store.yaml exists.openspec/config.yaml exists with schema: spec-driven.openspec/specs/, openspec/changes/, and
openspec/changes/archive/ exist.created_files lists the relative paths created by setup.A human has already run git init or cloned an empty repo, so the target folder
contains only .git/.
Expected result:
.git/ is preserved.A human already has a standalone OpenSpec root and wants it to become a context store.
Expected result:
.openspec-store/store.yaml is created..openspec-store/store.yaml is preserved.A teammate created a context store, pushed it to GitHub, and the human cloned it locally.
Expected result:
context-store register <path> validates the clone as a healthy OpenSpec
root with valid context-store identity.A human has a normal OpenSpec root that does not yet have
.openspec-store/store.yaml.
Expected result:
--yes.A human or agent wants to know whether registered stores are usable.
Expected result:
openspec/changes/archive/ appears under openspec_root.context-store setupSetup creates or preserves the context-store root for this machine.
Accept:
.git/.Reject:
.openspec-store/store.yaml.Mutations:
.openspec-store/store.yaml when missing.Human output should stay small:
Context store ready
ID: team-context
Location: /Users/me/src/team-context
OpenSpec root: ready
Registry: registered
Next: use normal OpenSpec specs and changes in this store.
JSON output should report exact state, including relative created_files.
context-store registerRegister remembers an existing local context store path. It is not an init command.
Accept:
.openspec-store/store.yaml.Reject:
--yes
is passed.Mutations:
.openspec-store/store.yaml and update the
local registry.openspec/ planning files during register.Interactive conversion prompt should be direct:
Turn this OpenSpec root into context store "team-context"?
context-store doctorDoctor is the non-mutating health surface.
It checks:
.openspec-store/store.yaml exists, parses, and matches the registry id.openspec/ exists.openspec/config.yaml or openspec/config.yml exists.openspec/specs/ exists.openspec/changes/ exists.openspec/changes/archive/ exists.It does not:
Setup and register mutation output should keep the existing created_files
field, but treat it as "relative paths created by this operation." It may list
directories and files.
For a no-op success:
{
"created_files": [],
"status": [
{
"code": "already_registered",
"severity": "info",
"message": "Context store is already registered at this path."
}
]
}
For doctor, each store should include a distinct openspec_root section beside
metadata and git:
{
"id": "team-context",
"root": "/Users/me/src/team-context",
"openspec_root": {
"present": true,
"config": {
"present": true,
"path": "openspec/config.yaml"
},
"specs": {
"present": true
},
"changes": {
"present": true
},
"archive": {
"present": false
},
"status": [
{
"code": "openspec_archive_missing",
"severity": "error",
"message": "Missing openspec/changes/archive/."
}
]
},
"metadata": {},
"git": {}
}
Exact diagnostic wording can follow existing CLI conventions, but the JSON shape must let agents distinguish root health from metadata and Git health.
Create src/core/openspec-root.ts.
Responsibilities:
openspec/config.yaml with schema: spec-driven when setup
needs config.config.yaml or config.yml.This helper should know nothing about context-store registry state, Git policy, prompts, agents, slash commands, workspaces, or initiatives.
Refactor the directory and config creation pieces from src/core/init.ts into
the new helper where useful.
Keep these behaviors separate:
openspec init may keep its current prompts, non-interactive config behavior,
legacy cleanup, tool detection, and generated assets.context-store setup uses only root scaffolding and default config creation.context-store register does not use root scaffolding.Do not call InitCommand.execute() from context-store operations.
Update src/core/context-store/operations.ts so setup classifies the target
before writing:
.git/, create full OpenSpec shape.Then perform mutations in a safe order:
Update setup JSON so created_files includes both OpenSpec-root paths and
.openspec-store/store.yaml when they were created.
Update register so it begins by inspecting the existing path:
.openspec-store/store.yaml supplies or confirms the store id.--yes.Register should not create openspec/, config.yaml, specs/, changes/, or
archive/. It only writes .openspec-store/store.yaml for confirmed
conversion, then updates the local registry.
Update registry and operation behavior so same id plus same root path is a stable no-op success.
Expected no-op behavior:
created_files: [].Same id with a different path and same path under a different id should keep the existing conflict protections unless the spec for a future replacement flow changes that.
Extend ContextStoreInspection in src/core/context-store/operations.ts with
OpenSpec-root inspection results.
Update src/commands/context-store.ts output types and printers so:
openspec_root.Update setup/register human output and help text in src/commands/context-store.ts
so the next step points toward normal OpenSpec specs and changes.
Avoid language like:
Use language like:
Do not add migration or cleanup logic for old beta files.
If old beta files exist inside an otherwise healthy root, setup/register should leave them byte-for-byte unchanged.
If old beta files are the only signal in a directory, setup/register should not treat that directory as healthy or registered. The folder is still arbitrary non-empty input unless the new root shape is present.
Add focused helper coverage, likely in test/core/openspec-root.test.ts:
config.yaml.config.yml.specs/.changes/.changes/archive/.Update test/commands/context-store.test.ts:
created_files..git/.--yes refuses.--yes writes identity and registry.created_files: [] and no duplicate
registry entry.initiatives/, .openspec-workspace/,
workspace.yaml, AGENTS.md, .codex/, .claude/, or .cursor/.openspec_root separate from metadata and git.openspec_root without creating it.Add or update operation-level tests around:
prepareContextStoreSetup.setupPreparedContextStore.registerExistingContextStore.doctorContextStores.Keep existing init and workspace tests honest:
openspec init still creates its expected files and generated assets.Run targeted tests first:
pnpm exec vitest run test/core/openspec-root.test.ts
pnpm exec vitest run test/core/context-store/registry.test.ts
pnpm exec vitest run test/commands/context-store.test.ts
pnpm exec vitest run test/core/init.test.ts
Then run the broader repo checks:
pnpm test
pnpm run build
openspec init is tempting to reuse, but it carries unrelated behavior.
Use only root scaffolding..openspec-store/store.yaml.openspec_root, metadata, and git as separate health
areas.pnpm test, and pnpm run build pass.