diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bd3e36014..a753bc72a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -83,7 +83,7 @@ jobs: name: Test (${{ matrix.label }}) runs-on: ${{ matrix.os }} timeout-minutes: 15 - if: github.event_name == 'push' + if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' strategy: fail-fast: false matrix: diff --git a/README.md b/README.md index dcf6586b4..334b350fd 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ If you want the expanded workflow (`/opsx:new`, `/opsx:continue`, `/opsx:ff`, `/ → **[Workflows](docs/workflows.md)**: combos and patterns
→ **[Commands](docs/commands.md)**: slash commands & skills
→ **[CLI](docs/cli.md)**: terminal reference
+→ **[Stores](docs/stores-beta/user-guide.md)**: plan in a separate repo, shared across your team (beta)
→ **[Supported Tools](docs/supported-tools.md)**: tool integrations & install paths
→ **[Concepts](docs/concepts.md)**: how it all fits
→ **[Multi-Language](docs/multi-language.md)**: multi-language support
diff --git a/docs/agent-contract.md b/docs/agent-contract.md new file mode 100644 index 000000000..577c0824d --- /dev/null +++ b/docs/agent-contract.md @@ -0,0 +1,149 @@ +# OpenSpec Agent Contract + +Machine-readable surfaces of the `openspec` CLI, verified against `src/` (capstone audit, 2026-06-11). Every shape below is documented from the emitting code. + +## 1. General conventions + +- **One JSON document per invocation.** In `--json` mode, stdout carries exactly one JSON document (2-space pretty-printed). Human prose, spinners, and the store banner go to stderr. +- **Store banner.** In human mode, a store-selected root prints `Using OpenSpec root: ()` to stderr. Never printed in JSON mode. +- **Key casing is surface-dependent** (see Known inconsistencies): store/repo/doctor/context payloads use `snake_case`; workflow payloads (`status`, `instructions`, `new change`, `validate`, `list`) use `camelCase`, except the embedded `root` object, which always uses `store_id`. +- **Optional keys are omitted, not null**, in most payloads (e.g. `root.store_id`, `member.path`). Exceptions that use explicit `null` are called out per shape (store doctor `git.*`, failure payloads). + +## 2. The diagnostic envelope + +One envelope shape is shared by every machine-readable diagnostic (`StoreDiagnostic`): + +```json +{ + "severity": "error" | "warning" | "info", + "code": "snake_case_string", + "message": "human sentence", + "target": "dotted.surface (optional)", + "fix": "one actionable sentence/command (optional)" +} +``` + +Diagnostics appear in two positions: **status arrays** (`status: StoreDiagnostic[]` at top level or per entry) for health findings, and **thrown errors** converted to a single-element `status` array on command failure. + +## 3. Root selection and `RootOutput` + +All root-resolving commands (`list`, `show`, `validate`, `status`, `instructions`, `instructions apply`, `new change`, `archive`, `doctor`, `context`) resolve one OpenSpec root with one precedence: + +1. `--store ` → the registered store's root (`source: "store"`). +2. Otherwise, nearest ancestor with `openspec/`: planning shape → `source: "nearest"` (a `store:` pointer is ignored with a stderr warning); config-only dir with a valid `store:` pointer → that store, `source: "declared"`. +3. No nearest root + registered stores exist → error `no_root_with_registered_stores`. +4. No root, no stores: scaffolding commands treat the cwd as `source: "implicit"`; diagnostic commands (`doctor`, `context`) fail with `no_openspec_root` instead — they inspect, never scaffold. + +Successful JSON payloads embed the root: + +```json +"root": { "path": "/abs/path", "source": "store" | "declared" | "nearest" | "implicit", "store_id": "id (only when store-selected)" } +``` + +**Root-failure contract**: in JSON mode a resolution failure prints `{ ...commandNullShape, "status": [diagnostic] }` on stdout and exits 1. + +## 4. Command JSON shapes + +### 4.1 `list --json` +`{ "changes": [ { "name", "completedTasks", "totalTasks", "lastModified", "status": "no-tasks"|"complete"|"in-progress" } ], "root": RootOutput }` — note the per-change `status` is a string enum here. `--specs`: `{ "specs": [ { "id", "requirementCount" } ], "root" }`. + +### 4.2 `show --json` +Change: `{ "id", "title", "deltaCount", "deltas": [...], "root" }`. Spec: `{ "id", "title", "overview", "requirementCount", "requirements": [...], "metadata": { "version", "format", "sourcePath"? }, "root" }`. + +### 4.3 `validate --json` +`{ "items": [ { "id", "type": "change"|"spec", "valid", "issues": [ { "level", "path", "message", "line"?, "column"? } ], "durationMs" } ], "summary": { "totals": {items,passed,failed}, "byType": {...} }, "version": "1.0", "root" }`. Exit 1 when any item fails. + +### 4.4 `status --json` +`{ "changeName", "schemaName", "planningHome"?: { "kind", "root", "changesDir", "defaultSchema" }, "changeRoot", "artifactPaths": { "": {outputPath, resolvedOutputPath, existingOutputPaths} }, "nextSteps": ["..."], "actionContext": { "mode": "repo-local", "sourceOfTruth": "repo", "planningArtifacts", "linkedContext", "allowedEditRoots", "requiresAffectedAreaSelection", "constraints" }, "isComplete", "applyRequires", "artifacts": [ {id, outputPath, status: "done"|"ready"|"blocked", missingDeps?} ], "root" }`. No active changes: `{ "changes": [], "message", "root" }`, exit 0. + +### 4.5 `instructions --json` +`{ "changeName", "artifactId", "schemaName", "changeDir", "planningHome"?, "outputPath", "resolvedOutputPath", "existingOutputPaths", "description", "instruction"?, "context"?, "rules"?, "references"?: ReferenceIndexEntry[], "targets"?: EffectiveTargets, "template", "dependencies": [{id,done,path,description}], "unlocks", "root" }`. + +`ReferenceIndexEntry`: `{ "store_id", "root"?, "specs"?: [{id,summary}], "fetch"?, "status": [] }` — resolved entries carry root/specs/fetch; unresolved carry store_id + warning status. Index capped at 50KB (`reference_index_truncated`). + +`EffectiveTargets`: `{ "source": "store"|"change", "repos": [ { "id", "remote"?, "path"? } ], "status": [] }`. + +### 4.6 `instructions apply --json` +`{ "changeName", "changeDir", "schemaName", "targets"?, "contextFiles": { "": ["/abs", ...] }, "progress": {total,complete,remaining}, "tasks": [{id,description,done}], "state": "blocked"|"all_done"|"ready", "missingArtifacts"?, "instruction", "references"?, "root" }`. + +### 4.7 `new change --json` +Success: `{ "change": { "id", "path", "metadataPath", "schema" }, "root" }`. Failure: `{ "change": null, "status": [d] }`, exit 1. + +### 4.8 `archive --json` +Success: `{ "archive": { "change", "archivedAs": "YYYY-MM-DD-name", "path", "specsUpdated", "totals"? }, "root" }`. Failure: `{ "archive": null, "root"?, "status": [d] }`, exit 1. JSON mode is strictly non-interactive: every prompt point becomes an `archive_*` code. + +### 4.9 `doctor --json` +`{ "root": { "path", "source", "store_id"?, "healthy", "status": [] }, "store": { "id", "metadata": {present,valid,remote?}, "origin_url"?, "status": [] } | null, "references": [...], "targets": [ {id, remote?, path?, "status": []} ], "status": [] }`. Health findings of any severity exit 0. Failure payload: `{ "root": null, "store": null, "references": [], "targets": [], "status": [d] }`, exit 1. + +### 4.10 `context --json` +`{ "root": { "path", "source", "store_id"?, "role": "openspec_root" }, "members": [ { "role": "referenced_store"|"target_repo", "id", "path"?, "remote"?, "fetch"?, "status": [] } ], "status": [] }`. AVAILABLE = path present AND status empty. `--code-workspace ` writes `{folders:[{name,path}]}` (available members only, `ref:`/`repo:` prefixes); in JSON mode the write runs before printing so stdout holds exactly one document even on write failure. Failure: `{ "root": null, "members": [], "status": [d] }`, exit 1. + +### 4.11 `store ... --json` +setup/register: `{ "store": {id, root, metadata_path?}, "registry": {path, registered, already_registered}, "git": {is_repository, initialized, committed}, "created_files": [], "status": [] }`. unregister/remove: `{ "store", "registry": {path, removed}, "files": {deleted, deleted_path, left_on_disk}, "status": [] }`. list: `{ "stores": [{id, root}], "status": [] }`. doctor: `{ "stores": [ { id, root, metadata_path?, openspec_root: {...healthy, status}, metadata: {present, valid, id?, remote}, git: {is_repository, has_commits, has_uncommitted_changes, has_remote, origin_url}, status } ], "status": [] }` (`null` = unknown/not probed). Health findings exit 0; failures exit 1 with the matching null-shape. Prompt cancellation exits 130. + +### 4.12 `repo ... --json` +register: `{ "repo": {id, path}, "registry": {path, registered, already_registered}, "status": [] }`. unregister: `{ "repo", "registry": {path, removed}, "status": [] }`. list: `{ "repos": [{id, path}], "status": [] }` (sorted). Failures: null-shapes with `status: [d]`, exit 1. + +### 4.13 `schemas --json` / `templates --json` +`schemas`: bare array `[ {name, description, artifacts, source} ]`. `templates`: keyed object `{ "": {path, source} }`. Both cwd-based, no root/status keys. + +## 5. Exit-code contract + +| Situation | Exit | Stdout | +|---|---|---| +| Success, incl. health findings (doctor/context/store doctor) | 0 | the payload | +| Command failure in `--json` mode | 1 | one JSON document with `status: [d]` and the command's null-shape | +| `validate` with failing items | 1 | full report | +| Prompt cancellation (`store` group, human mode) | 130 | stderr only | + +## 6. Diagnostic code catalog + +### Resolution +`no_openspec_root`, `no_root_with_registered_stores`, `no_registered_stores`, `unknown_store`, `store_id_is_repo`, `store_identity_mismatch`, `unhealthy_store_root`, `store_path_not_supported`, `invalid_store_pointer`, `initiative_option_removed`, `areas_option_removed`; pass-through: `invalid_store_id`, `invalid_store_registry`, `invalid_store_metadata`. + +### OpenSpec-root health (error, no fix) +`openspec_store_root_missing`, `openspec_root_missing`, `openspec_config_missing`, `openspec_specs_missing`, `openspec_changes_missing`, `openspec_archive_missing`, plus `_not_directory` variants of each. + +### Store registry/identity/state +`invalid_store_id`, `invalid_store_registry`, `invalid_store_metadata`, `store_registry_busy`, `store_not_found`, `no_store_registry`, `store_registry_changed`, `store_metadata_missing`, `store_metadata_id_mismatch`, `store_metadata_invalid`, `store_id_conflict`, `store_path_conflict`, `store_id_claimed_by_repo`, `store_path_claimed_by_repo`, `store_already_registered` (info). + +### Store setup/register/remove +`store_setup_id_required`, `store_setup_path_required`, `store_setup_path_not_directory`, `store_setup_inside_git_repo`, `store_setup_non_empty_directory`, `store_setup_cancelled`, `store_path_required`, `store_path_missing`, `store_path_not_directory`, `store_register_root_unhealthy`, `store_register_identity_confirmation_required`, `store_register_cancelled`, `store_remote_empty`, `store_remote_requires_hand_edit`, `store_remove_confirmation_required`, `store_remove_cancelled`, `store_remove_path_not_directory`, `store_remove_metadata_missing`, `store_root_missing` (warning in remove, error in doctor), `store_root_not_directory`. + +### Store git +`store_git_init_failed`, `store_git_identity_missing`, `store_git_commit_failed`, `store_git_no_commits` (warning), `store_clone_fragile_directories` (warning), `store_remote_divergence` (info, doctor). + +### Repo map +`invalid_repo_id`, `repo_path_missing`, `repo_path_not_directory`, `repo_id_claimed_by_store`, `repo_path_claimed_by_store`, `repo_id_conflict`, `repo_path_conflict`, `repo_not_found`. + +### References (warning) +`reference_invalid_id`, `reference_registry_unreadable`, `reference_unresolved`, `reference_root_unhealthy`, `reference_index_truncated`. + +### Targets (warning) +`target_invalid_id`, `target_not_declared`, `target_unmapped`, `target_path_missing`. + +### Relationships (warning; doctor; context keeps only the registry one) +`relationship_registry_unreadable`, `root_pointer_ignored`, `root_pointer_invalid`, `pointer_declarations_inert`. + +### Archive (JSON mode) +`archive_change_name_required`, `archive_change_not_found`, `archive_validation_failed`, `archive_confirmation_required`, `archive_tasks_incomplete`, `archive_spec_update_failed`, `archive_spec_validation_failed`, `archive_target_exists`, `archive_error`. + +### Context writes +`context_file_exists`, `context_output_dir_missing`. + +### Fallbacks +`doctor_failed`, `context_failed`, `store_error`, `repo_error`, `change_error`, `archive_error`. + +## Known inconsistencies + +Recorded by the capstone audit; published-key renames are product decisions deferred past this release: + +1. ~~In `--json` mode, several failure paths printed stderr only with no JSON document.~~ Fixed in the capstone gauntlet round: `show`/`validate` unknown and ambiguous items emit `{status:[{code: unknown_item | ambiguous_item, ...}]}`; thrown errors in `status`/`instructions`/`list`/`show`/`validate` route through the JSON-aware failure helper (the command's null-shape + `status`); `store --json` emits `{status:[{code: unknown_store_subcommand}]}`; `list` carries its `{changes|specs: [], root: null}` null-shape on resolution failures. +2. `store_root_missing` is emitted with two severities (warning in remove, error in store doctor) — context-dependent, documented above. +3. `target_invalid_id` carries `target: "targets"` from instructions surfaces and `target: "relationships"` from doctor/context. +4. snake_case (store family) vs camelCase (workflow family) key casing; `root.store_id` is snake_case everywhere. +5. Four parallel envelope type declarations exist in src; archive diagnostics never carry `target`. +6. `list --json` reuses the `status` key as a string enum per change. +7. Only `validate` output carries a `version` field. +8. `schemas`/`templates` ignore root selection (cwd-based, no `--store`). +9. Deprecated noun forms (`change`/`spec` subcommands) emit unenveloped payloads without `root`/`status`. diff --git a/docs/cli.md b/docs/cli.md index 103dd7d4f..d38a55e63 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -7,12 +7,15 @@ The OpenSpec CLI (`openspec`) provides terminal commands for project setup, vali | Category | Commands | Purpose | |----------|----------|---------| | **Setup** | `init`, `update` | Initialize and update OpenSpec in your project | -| **Workspaces (beta)** | `workspace setup`, `workspace list`, `workspace ls`, `workspace link`, `workspace relink`, `workspace doctor`, `workspace update`, `workspace open` | Set up local views over linked repos or folders | -| **Shared context (beta)** | `context-store setup`, `context-store register`, `context-store unregister`, `context-store remove`, `context-store list`, `context-store doctor`, `initiative create`, `initiative show`, `initiative list` | Manage local context-store registrations and durable initiative context | +| **Stores (standalone OpenSpec repos)** | `store setup`, `store register`, `store unregister`, `store remove`, `store list`, `store doctor` | Manage stores — standalone OpenSpec repos you've registered | +| **Repo map** | `repo register`, `repo unregister`, `repo list` | Map target repo ids to local checkout paths on this machine | +| **Health** | `doctor` | Report relationship health for the resolved root | +| **Working context** | `context` | Assemble the working set (root + referenced stores + target repos) | +| **Personal worksets** | `workset create`, `workset list`, `workset open`, `workset remove` | Keep and open personal, local working views in your tool | | **Browsing** | `list`, `view`, `show` | Explore changes and specs | | **Validation** | `validate` | Check changes and specs for issues | | **Lifecycle** | `archive` | Finalize completed changes | -| **Workflow** | `new change`, `set change`, `status`, `instructions`, `templates`, `schemas` | Artifact-driven workflow support | +| **Workflow** | `new change`, `status`, `instructions`, `templates`, `schemas` | Artifact-driven workflow support | | **Schemas** | `schema init`, `schema fork`, `schema validate`, `schema which` | Create and manage custom workflows | | **Config** | `config` | View and modify settings | | **Utility** | `feedback`, `completion` | Feedback and shell integration | @@ -31,6 +34,7 @@ These commands are interactive and designed for terminal use: |---------|---------| | `openspec init` | Initialize project (interactive prompts) | | `openspec view` | Interactive dashboard | +| `openspec workset open ` | Open a saved workset (editor window or terminal agent session) | | `openspec config edit` | Open config in editor | | `openspec feedback` | Submit feedback via GitHub | | `openspec completion install` | Install shell completions | @@ -48,22 +52,16 @@ These commands support `--json` output for programmatic use by AI agents and scr | `openspec instructions` | Get next steps | `--json` for agent instructions | | `openspec templates` | Find template paths | `--json` for path resolution | | `openspec schemas` | List available schemas | `--json` for schema discovery | -| `openspec workspace setup --no-interactive` | Create a workspace with explicit inputs | `--json` for structured setup output | -| `openspec workspace list` | Browse known workspaces | `--json` for typed workspace objects | -| `openspec workspace link` | Link a repo or folder | `--json` for structured link output | -| `openspec workspace relink` | Repair a linked path | `--json` for structured link output | -| `openspec workspace doctor` | Check one workspace | `--json` for structured status output | -| `openspec workspace update` | Refresh workspace-local guidance and agent skills | `--tools` selects agents; profile selects workflows | -| `openspec context-store setup ` | Create a local context store | `--json` with explicit inputs for structured setup output | -| `openspec context-store register ` | Register an existing context store | `--json` for structured registration output | -| `openspec context-store unregister ` | Forget a local context-store registration | `--json` for structured cleanup output | -| `openspec context-store remove ` | Delete a registered local context-store folder | `--yes --json` for non-interactive deletion | -| `openspec context-store list` | Browse registered context stores | `--json` for structured registrations | -| `openspec context-store doctor` | Check local store setup | `--json` for structured diagnostics | -| `openspec initiative list` | Browse shared initiatives | `--json` for structured initiative records | -| `openspec initiative show ` | Resolve an initiative | `--json` for canonical paths and metadata | -| `openspec new change ` | Create repo-local change scaffolding | `--json`, plus `--initiative` for shared coordination links | -| `openspec set change ` | Update checked-in change metadata | `--json`, plus `--initiative` for shared coordination links | +| `openspec store setup ` | Create and register a local store | `--json` with explicit inputs for structured setup output | +| `openspec store register ` | Register an existing store | `--json` for structured registration output | +| `openspec store unregister ` | Forget a local store registration | `--json` for structured cleanup output | +| `openspec store remove ` | Delete a registered local store folder | `--yes --json` for non-interactive deletion | +| `openspec store list` | Browse registered stores | `--json` for structured registrations | +| `openspec store doctor` | Check local store setup | `--json` for structured diagnostics | +| `openspec new change ` | Create repo-local change scaffolding | `--json`, plus `--store ` to use a registered store as the OpenSpec root | +| `openspec workset create [name]` | Compose a personal working view | `--member --json` for non-interactive composition | +| `openspec workset list` | Browse saved worksets | `--json` for structured views | +| `openspec workset remove ` | Delete a saved view | `--yes --json` for non-interactive removal | --- @@ -107,7 +105,7 @@ openspec init [path] [options] `--profile custom` uses whatever workflows are currently selected in global config (`openspec config profile`). -**Supported tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `bob`, `claude`, `cline`, `codex`, `forgecode`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `junie`, `kilocode`, `kimi`, `kiro`, `opencode`, `pi`, `qoder`, `lingma`, `qwen`, `roocode`, `trae`, `windsurf` +**Supported tool IDs (`--tools`):** `amazon-q`, `antigravity`, `auggie`, `bob`, `claude`, `cline`, `codex`, `forgecode`, `codebuddy`, `continue`, `costrict`, `crush`, `cursor`, `factory`, `gemini`, `github-copilot`, `iflow`, `junie`, `kilocode`, `kimi`, `kiro`, `opencode`, `pi`, `qoder`, `lingma`, `qwen`, `roocode`, `trae`, `vibe`, `windsurf` **Examples:** @@ -177,318 +175,224 @@ openspec update --- -## Workspace Commands +## Stores (standalone OpenSpec repos) -Workspace commands are in beta. The local-view model below is the current direction, but external automation, integrations, and long-lived workflows should still treat command behavior, state files, and JSON output as evolving. +> **Beta.** Stores and the features built on them (references, targets, working context, worksets) are new; command names, flags, file formats, and JSON output may change shape between releases. For the problem-first walkthrough, see the [stores guide](stores-beta/user-guide.md). -Coordination workspaces are machine-local views over linked repos or folders. Workspace visibility is not change commitment: link the repos or folders OpenSpec should know about, then create changes when you are ready to plan specific work. +A store is a standalone OpenSpec repo you've registered on this machine — for example a planning repo or a contracts repo. Registering a store lets normal commands (`list`, `show`, `status`, `validate`, `new change`, `archive`, ...) act in it from anywhere by passing `--store `. -### `openspec workspace setup` +### `openspec store setup` -Create a workspace in the standard OpenSpec workspace location and link at least one existing repo or folder. +Create and register a local store. With no arguments in a terminal, +OpenSpec guides the user through setup. Agents and scripts should pass explicit +inputs and use `--json`. ```bash -openspec workspace setup [options] +openspec store setup [id] [options] ``` **Options:** | Option | Description | |--------|-------------| -| `--name ` | Workspace name. Names must be kebab-case | -| `--link ` | Link an existing repo or folder and infer the link name from the folder name | -| `--link =` | Link an existing repo or folder with an explicit link name | -| `--opener ` | Store a preferred opener during non-interactive setup: `codex-cli`, `claude`, `github-copilot`, or `editor` | -| `--tools ` | Install workspace-local OpenSpec skills for agents. Use `all`, `none`, or comma-separated tool IDs | -| `--no-interactive` | Disable prompts; requires `--name` and at least one `--link` | -| `--json` | Output JSON; requires `--no-interactive` | - -**Examples:** - -```bash -openspec workspace setup -openspec workspace setup --no-interactive --name platform --link /repos/api --link web=/repos/web -openspec workspace setup --no-interactive --name platform --link /repos/api --opener codex-cli -openspec workspace setup --no-interactive --name platform --link /repos/api --tools codex,claude -openspec workspace setup --no-interactive --json --name checkout --link /repos/platform/apps/checkout -``` - -Interactive setup asks for a preferred opener and can install workspace-local OpenSpec skills for selected agents. Non-interactive setup stores a preferred opener only when `--opener` is provided; otherwise `workspace open` prompts later in interactive terminals when a supported opener is available, or asks scripts to pass `--agent ` or `--editor`. - -Workspace skill installation is skills-only in this beta slice: even if global delivery is `commands` or `both`, workspace setup writes agent skill folders in the workspace root and does not create slash command files. The active global profile chooses which workflow skills are installed; `--tools` chooses which agents receive them. If `--tools` is omitted in non-interactive setup, no skills are installed and `workspace update --tools ` can add them later. +| `--path ` | Folder where the store should live (for example `~/openspec/`) | +| `--remote ` | Record the canonical remote in the new store's `store.yaml` | +| `--init-git` | Initialize a Git repository with an initial commit (default) | +| `--no-init-git` | Skip every Git action: no init, no initial commit | +| `--json` | Output JSON | -### `openspec workspace list` +Non-interactive runs (`--json`, scripts, agents) must pass both the store id and `--path`. In an interactive terminal, setup prompts for the location with an editable suggestion in a visible, user-owned place (for example `~/openspec/`); it never defaults to OpenSpec's managed data directory. -List known OpenSpec workspaces from the local registry. +Examples: ```bash -openspec workspace list [--json] -openspec workspace ls [--json] +openspec store setup +openspec store setup team-context +openspec store setup team-context --path ~/openspec/team-context --no-init-git +openspec store setup team-context --path ~/openspec/team-context --no-init-git --json ``` -The list shows each workspace location and linked repos or folders. Stale registry records are reported but not changed. +### `openspec store register` -### `openspec workspace link` - -Record an existing repo or folder for one workspace. +Register an existing local store folder. ```bash -openspec workspace link [name] [options] +openspec store register [path] [options] ``` **Options:** | Option | Description | |--------|-------------| -| `--workspace ` | Select a known workspace from the local registry | +| `--id ` | Store id; defaults to store metadata or folder name | +| `--yes` | Confirm creating store identity metadata for a healthy OpenSpec root | | `--json` | Output JSON | -| `--no-interactive` | Disable workspace picker prompts | -**Examples:** +### `openspec store unregister` + +Forget a local store registration without deleting files. ```bash -openspec workspace link /repos/api -openspec workspace link api-service /repos/api -openspec workspace link --workspace platform /repos/platform/apps/checkout +openspec store unregister [--json] ``` -The path must already exist. Relative paths are resolved against the command's current directory before OpenSpec stores the verified absolute path in machine-local workspace state. Linked paths can be full repos, packages, services, apps, or folders without repo-local `openspec/` state. +Use this when a store was moved, cloned somewhere else, or should no longer be +shown by OpenSpec on this machine. -### `openspec workspace relink` +### `openspec store remove` -Repair or change the local path for an existing link. +Forget a local store registration and delete its local folder. ```bash -openspec workspace relink [options] +openspec store remove [--yes] [--json] ``` -The path must already exist. Relink updates only the machine-local path for the stable link name. +`remove` shows the exact folder before deleting in an interactive terminal. +Agents, scripts, and JSON callers must pass `--yes` to confirm deletion. +OpenSpec refuses to delete a folder that does not contain matching +store metadata. -### `openspec workspace doctor` +### `openspec store list` -Check what one workspace can resolve on the current machine. +List locally registered stores. ```bash -openspec workspace doctor [options] +openspec store list [--json] +openspec store ls [--json] ``` -Doctor shows the workspace location, linked repos or folders, missing paths, repo-local specs paths when present, and suggested fixes. JSON output also includes the workspace planning path for compatibility. It reports issues only; it does not repair them automatically. - -Commands that need one workspace use the current workspace when run from inside a workspace folder or subdirectory. From elsewhere, pass `--workspace `, select from the picker in an interactive terminal, or rely on the only known workspace when exactly one exists. In `--json` or `--no-interactive` mode, ambiguous selection fails with a structured status error and suggests `--workspace `. - -JSON responses use typed objects plus `status` arrays. Primary data lives in `workspace`, `workspaces`, or `link`; warnings and errors live in `status`. +### `openspec store doctor` -### `openspec workspace update` - -Refresh workspace-local OpenSpec guidance and agent skills. +Check local store registration, metadata, and Git presence. ```bash -openspec workspace update [name] [options] +openspec store doctor [id] [--json] ``` -**Options:** +Doctor is diagnostic-only; it reports missing roots, metadata mismatches, and invalid local registry state without modifying the store. -| Option | Description | -|--------|-------------| -| `--workspace ` | Select a known workspace from the local registry | -| `--tools ` | Select agents for workspace skills. Use `all`, `none`, or comma-separated tool IDs | -| `--json` | Output JSON | -| `--no-interactive` | Disable workspace picker prompts | +### Referencing stores from a project -**Examples:** +A project repo can declare which stores its work draws on in `openspec/config.yaml`: -```bash -openspec workspace update -openspec workspace update platform -openspec workspace update --workspace platform --tools codex,claude -openspec workspace update --workspace platform --tools none +```yaml +schema: spec-driven +references: + - team-context ``` -`workspace update` refreshes the generated workspace guidance block and local open surface. For agent skills, it reuses the stored workspace skill agent selection when `--tools` is omitted. Passing `--tools` replaces that stored selection. It refreshes only OpenSpec-managed workflow skill directories in the workspace root, removes deselected managed workflow skills, and leaves linked repos and folders untouched. +From then on, `openspec instructions` output in that repo (both the per-artifact and `apply` surfaces, JSON and human modes) carries an index of each referenced store's specs — spec ids, a one-line summary from each spec's Purpose section, and the fetch command (`openspec show --type spec --store `). The index is built live from the registered checkout on every run; spec content is never copied into the output. -Running `openspec update` from inside a workspace does not update workspace-local files. Use `openspec workspace update` when you want workspace-local guidance and skills refreshed, and run `openspec update` inside repo-local projects when you want repo-owned tool files updated. +References are read-only context. They never change where commands act: work stays in the repo's own root, and writing to a referenced store remains an explicit `--store` action. A reference that cannot be resolved (for example, a store not registered on this machine) degrades to a warning in the index with the exact fix, and instructions still generate. `openspec doctor` reports reference health in one place. -### `openspec workspace open` +### Recording where a store is cloned from -Open a workspace working set through the stored preferred opener, a one-session agent override, or VS Code editor mode. +A store can record its canonical clone source in its committed identity file, so onboarding never dead-ends at "register the store": ```bash -openspec workspace open [name] [options] +openspec store setup team-context --path ~/openspec/team-context \ + --remote git@github.com:acme/team-context.git ``` -**Options:** +The remote lands in `.openspec-store/store.yaml` inside the initial commit, so every clone is born knowing it. For an existing store, edit `store.yaml` by hand and commit. `store doctor` shows the recorded remote (and the checkout's observed Git origin); setup/register sharing guidance names it; and register records the checkout's origin in the machine-local registry. -| Option | Description | -|--------|-------------| -| `--workspace ` | Alias for the positional workspace name | -| `--initiative ` | Open an initiative as a local workspace view. Accepts `` or `/` | -| `--store ` | Registered context store id for `--initiative` | -| `--store-path ` | Existing local context store root for `--initiative` | -| `--agent ` | One-session agent override: `codex-cli`, `claude`, or `github-copilot` | -| `--editor` | Open the maintained VS Code workspace file as a normal editor workspace | -| `--no-interactive` | Disable workspace and opener picker prompts | - -**Examples:** +A reference declaration can carry the clone source too, so a teammate who doesn't have the store yet gets a complete, pasteable fix (`git clone && openspec store register --id `): -```bash -openspec workspace open -openspec workspace open platform -openspec workspace open platform --agent github-copilot -openspec workspace open --agent codex-cli -openspec workspace open --editor -openspec workspace open --initiative billing-launch --store platform -openspec workspace open --initiative platform/billing-launch +```yaml +references: + - { id: team-context, remote: "git@github.com:acme/team-context.git" } ``` -`workspace open` uses the current workspace when run inside one, auto-selects the only known workspace when run elsewhere, and asks the user to choose when multiple workspaces are known. `--agent` and `--editor` do not change the stored preferred opener. Passing both opener overrides is an error; choose either `--agent ` or `--editor`. - -When `--initiative` is used, OpenSpec prepares or selects a private local workspace view for that initiative. Registry-selected stores are stored by id; `--store-path` stores a runtime-local path selector because workspace views are private local state. - -OpenSpec maintains `.code-workspace` at the workspace root for VS Code editor and GitHub Copilot-in-VS-Code opens. That file is machine-local workspace view state. - -The maintained VS Code workspace lists valid linked repos or folders first, then initiative context when attached, then the OpenSpec workspace files. VS Code displays those entries as a multi-root workspace. - -Root workspace open makes linked repos or folders visible for exploration and context. Implementation edits should start only after an explicit user request and a normal OpenSpec implementation workflow. - ---- - -## Shared Context Commands +Recording a remote is not sync: OpenSpec never clones, pulls, or pushes on its own. -Context stores and initiatives are beta coordination surfaces. A context store is a local registration for durable shared context, usually a Git-backed folder or clone. An initiative is shared coordination context inside a context store; repo-local changes can link to it without copying the shared plan into every repo. +### Declaring target repos -### `openspec context-store setup` +A planning repo can say, once, which code repos its work is about: -Create and register a local context store. With no arguments in a terminal, -OpenSpec guides the user through setup. Agents and scripts should pass explicit -inputs and use `--json`. - -```bash -openspec context-store setup [id] [options] +```yaml +# the store's openspec/config.yaml +targets: + - api-server + - { id: web-app, remote: "git@github.com:acme/web-app.git" } ``` -**Options:** +A change that only concerns some of them can narrow the set in its own `.openspec.yaml` with a plain `targets:` list. `openspec instructions` output then carries the effective set (with provenance: store default or change narrowing) so agents know which repos the work concerns. Targets name repos. (Legacy `affected_areas` metadata from old changes is still read but no longer authored by any command — use `targets:` for repo declarations.) -| Option | Description | -|--------|-------------| -| `--path ` | Context store folder path; defaults to OpenSpec's managed local data directory | -| `--init-git` | Initialize a Git repository in the context store | -| `--no-init-git` | Do not initialize a Git repository | -| `--json` | Output JSON | +Targets are declarations, not machinery: they never affect where commands act, nothing clones or syncs, and an unrecognized narrowed id degrades to a warning with a fix rather than failing. `openspec doctor` reports target health in one place. -When `--path` is omitted, setup creates the store under `getGlobalDataDir()/context-stores/`: `$XDG_DATA_HOME/openspec/context-stores/` when `XDG_DATA_HOME` is set, or `~/.local/share/openspec/context-stores/` on Unix-style fallbacks. Pass `--path` when you want the store in a visible clone or team-specific folder. +### Declaring a default store -Examples: +A repo whose planning is fully externalized — no local `openspec/specs/` or `openspec/changes/` — can declare its store once instead of passing `--store` on every command: -```bash -openspec context-store setup -openspec context-store setup team-context -openspec context-store setup team-context --path /repos/team-context --no-init-git -openspec context-store setup team-context --json --no-init-git +```yaml +# openspec/config.yaml (the only file under openspec/) +store: team-context ``` -### `openspec context-store register` +Normal commands then resolve to the declared store automatically; the root banner and JSON `root` block report `source: "declared"` with the store id, and printed hints still carry `--store `. The declaration is a fallback, never an override: explicit `--store` always wins, and a directory with real planning folders ignores the pointer (with a warning). To convert a pointer repo into a local OpenSpec root, remove the `store:` line and run `openspec init` — init refuses to scaffold while the declaration is present. -Register an existing local context store folder. +## Repo map (local checkout paths) -```bash -openspec context-store register [path] [options] -``` - -**Options:** - -| Option | Description | -|--------|-------------| -| `--id ` | Context store id; defaults to store metadata or folder name | -| `--json` | Output JSON | - -### `openspec context-store unregister` - -Forget a local context-store registration without deleting files. +Shared work names target repos by id; each developer maps those ids to local checkouts once. The map is local machine settings — never shared planning state, never committed: ```bash -openspec context-store unregister [--json] +openspec repo register ~/src/api-server # id defaults to the folder name +openspec repo register ~/work/checkout --id api-server +openspec repo list # id → path (or --json) +openspec repo unregister api-server # forgets the mapping; never touches the checkout ``` -Use this when a store was moved, cloned somewhere else, or should no longer be -shown by OpenSpec on this machine. +Once mapped, `openspec instructions` shows where each target lives on this machine (`- api-server → /Users/dev/src/api-server`). Store and repo ids share one namespace: registering either kind under an id (or path) the other holds fails with a typed conflict, and `--store ` rejects with a hint instead of a generic unknown-store error. `openspec doctor` reports unmapped and stale mappings in one place. -### `openspec context-store remove` +## Doctor (relationship health) -Forget a local context-store registration and delete its local folder. +One read-only question, one place: are the roots this work relates to — the OpenSpec root, the stores it references, and the target repos it names — available on this machine? ```bash -openspec context-store remove [--yes] [--json] +openspec doctor [--store ] [--json] ``` -`remove` shows the exact folder before deleting in an interactive terminal. -Agents, scripts, and JSON callers must pass `--yes` to confirm deletion. -OpenSpec refuses to delete a folder that does not contain matching -context-store metadata. - -### `openspec context-store list` +The report separates root health, store metadata health (including a note when the recorded remote and the checkout's origin diverge), reference health (the same diagnostics instructions show, with clone fixes for unresolved references), and target health (unmapped repos get the `repo register` fix). Health findings of any severity exit 0 — agents read the `status` arrays; only command failures (no root, unknown store) exit 1. Doctor never clones, syncs, or repairs. To get the assembled set itself rather than its health, use `openspec context`. -List locally registered context stores. +## Working context (the assembled set) -```bash -openspec context-store list [--json] -openspec context-store ls [--json] -``` - -### `openspec context-store doctor` - -Check local context-store registration, metadata, and Git presence. +Everything this work relates to, in one working set: the OpenSpec root, the stores it references, and the project repos it targets. ```bash -openspec context-store doctor [id] [--json] +openspec context [--store ] [--json] [--code-workspace [--force]] ``` -Doctor is diagnostic-only; it reports missing roots, metadata mismatches, and invalid local registry state without modifying the store. +The JSON brief is agent-consumable (each available referenced store carries its fetch recipe; unresolved members carry the same fixes instructions and doctor show). `--code-workspace` additionally writes a VS Code workspace file containing the root plus the available members (`ref:`, `repo:` folders) — the one write this command performs, refused without `--force` if the file exists. Unavailable members are reported, never guessed at. -### `openspec initiative create` - -Create an initiative in a context store. - -```bash -openspec initiative create --title --summary <summary> [options] -``` - -**Options:** +"Working context" is the assembled set; the `context:` field in `openspec/config.yaml` is project background injected into instructions — two different things. `openspec doctor` answers whether the set is healthy; `openspec context` answers what the set is. -| Option | Description | -|--------|-------------| -| `--store <id>` | Context store id from the local registry | -| `--store-path <path>` | Existing local context store root | -| `--title <title>` | Initiative title | -| `--summary <summary>` | Initiative summary | -| `--json` | Output JSON | +## Personal worksets -### `openspec initiative list` +> **Beta.** Worksets are part of the new beta surface; commands, flags, and file formats may change shape between releases. For the walkthrough, see the [stores guide](stores-beta/user-guide.md#worksets-reopen-the-folders-you-work-on-together). -List initiatives. Without a selector, this searches all registered context stores and reports partial-read warnings in `status`. +A workset is a personal, named view of the folders you work on together — a planning root plus whatever else you choose — kept on your machine and reopened by name in your tool. It is purely local: never committed, never shared, never derived from declarations, and removing one never touches a member folder. ```bash -openspec initiative list [options] -openspec initiative ls [options] +openspec workset create [name] [--member <path> | --member <name>=<path>]... [--tool <id>] [--json] +openspec workset list [--json] +openspec workset open <name> [--tool <id>] +openspec workset remove <name> [--yes] [--json] ``` -**Options:** +`create` runs a short guided flow (or takes `--member` flags non-interactively; the first member is the primary — sessions start there). `open` launches the chosen tool: editors (VS Code, Cursor) open a window with every member and return; CLI agents (Claude Code, codex) take over this terminal as a session with every member attached and no prompt pre-filled, ending when you exit. A member folder missing at open time is skipped with a note; the rest opens. The saved tool preference is overridable per open with `--tool`. -| Option | Description | -|--------|-------------| -| `--store <id>` | List one registered context store | -| `--store-path <path>` | List one existing local context store root | -| `--json` | Output JSON | +Supporting a new tool is configuration, not code. Every tool is one of two launch styles — `workspace-file` (launched with the generated `.code-workspace`) or `attach-dirs` (one attach flag per member) — and the `openers` key in the global `config.json` (open it with `openspec config edit`) adds tools or adjusts built-ins per field: -### `openspec initiative show` - -Resolve an initiative and print its canonical location. - -```bash -openspec initiative show <id> [options] -openspec initiative show <store>/<id> [options] +```json +{ + "openers": { + "zed": { "style": "workspace-file" }, + "claude": { "attach_flag": "--dir" } + } +} ``` -Without `--store`, OpenSpec searches registered context stores. If the same initiative id exists in multiple stores, pass `--store <id>` or use the `<store>/<id>` form. +All workset state lives under the global data dir's `worksets/` folder (the saved views plus the generated `<name>.code-workspace` files, regenerated on every open); deleting that folder removes every trace. --- @@ -527,9 +431,8 @@ openspec list --json **Output (text):** ``` -Active changes: - add-dark-mode UI theme switching support - fix-login-bug Session timeout handling +Changes: + add-dark-mode No tasks just now ``` --- @@ -738,7 +641,7 @@ These commands support the artifact-driven OPSX workflow. They're useful for bot ### `openspec new change` -Create a repo-local change directory and optional checked-in metadata. +Create a change directory and optional checked-in metadata in the resolved OpenSpec root. ```bash openspec new change <name> [options] @@ -749,40 +652,18 @@ openspec new change <name> [options] | Option | Description | |--------|-------------| | `--description <text>` | Description to add to `README.md` | -| `--goal <text>` | Workspace product goal to store with the change | -| `--areas <names>` | Comma-separated affected workspace link names | -| `--initiative <id>` | Link the repo-local change to an initiative | -| `--store <id>` | Context store id for `--initiative` | -| `--store-path <path>` | Existing local context store root for `--initiative` | +| `--goal <text>` | Optional goal metadata to store with the change | | `--schema <name>` | Workflow schema to use | +| `--store <id>` | Store id to use as the OpenSpec root (a store is a standalone OpenSpec repo you've registered) | | `--json` | Output JSON | Examples: ```bash -openspec new change add-billing-api --initiative billing-launch --store platform -openspec new change add-billing-api --initiative platform/billing-launch --json +openspec new change add-billing-api +openspec new change add-billing-api --store team-context --json ``` -### `openspec set change` - -Update checked-in repo-local change metadata without recreating the change. - -```bash -openspec set change <name> [options] -``` - -**Options:** - -| Option | Description | -|--------|-------------| -| `--initiative <id>` | Link the repo-local change to an initiative | -| `--store <id>` | Context store id for `--initiative` | -| `--store-path <path>` | Existing local context store root for `--initiative` | -| `--json` | Output JSON | - -`set change --initiative` is idempotent when the requested link already exists and refuses to replace a different existing initiative link. - ### `openspec status` Display artifact completion status for a change. @@ -1198,9 +1079,9 @@ openspec config profile core - Keep current settings (exit) If you keep current settings, no changes are written and no update prompt is shown. -If there are no config changes but the current project or workspace files are out of sync with your global profile/delivery, OpenSpec will show a warning and suggest `openspec update` for repo-local projects or `openspec workspace update` for workspace-local guidance and skills. +If there are no config changes but the current project files are out of sync with your global profile/delivery, OpenSpec will show a warning and suggest `openspec update`. Pressing `Ctrl+C` also cancels the flow cleanly (no stack trace) and exits with code `130`. -In the workflow checklist, `[x]` means the workflow is selected in global config. To apply those selections to project files, run `openspec update` (or choose `Apply changes to this project now?` when prompted inside a project). From inside a workspace, use `openspec workspace update` to refresh workspace-local guidance and skills; this remains skills-only for generated agent workflow files and does not generate workspace slash commands. +In the workflow checklist, `[x]` means the workflow is selected in global config. To apply those selections to project files, run `openspec update` (or choose `Apply changes to this project now?` when prompted inside a project). **Interactive examples:** diff --git a/docs/concepts.md b/docs/concepts.md index a04c65d81..b929a588a 100644 --- a/docs/concepts.md +++ b/docs/concepts.md @@ -49,150 +49,6 @@ OpenSpec organizes your work into two main areas: This separation is key. You can work on multiple changes in parallel without conflicts. You can review a change before it affects the main specs. And when you archive a change, its deltas merge cleanly into the source of truth. -## Coordination Workspaces - -Workspace support is in beta. The local-view model below is the current direction, but external automation, integrations, and long-lived workflows should still treat command behavior, state files, and JSON output as evolving. - -The commands below provide the first setup flow for opening local views over linked repos or folders. - -Repo-local OpenSpec projects are the right default when one repo owns the planning, implementation, and archive flow. Some work spans several repos or folders. For that case, an OpenSpec coordination workspace is a machine-local view that keeps linked paths, opener state, and agent setup together. - -The workspace mental model is: - -```text -workspace = private local view over context stores, initiatives, repos, and folders -context store = durable shared context container -initiative = durable coordination context inside a context store -link = a stable name for a repo or folder the workspace can resolve locally -change = one planned piece of work; implementation belongs in the owning repo -``` - -A workspace has a different shape from a repo-local project: - -```text -getGlobalDataDir()/workspaces/<workspace-name>/ -├── .openspec-workspace/ -│ └── view.yaml # Private local view record -├── AGENTS.md # Generated runtime guidance -└── <workspace-name>.code-workspace # Generated editor workspace file -``` - -Repo-local OpenSpec state keeps the existing shape: - -```text -repo-root/ -└── openspec/ - ├── specs/ - └── changes/ -``` - -Root-level `workspace.yaml` files are not OpenSpec workspace state. Workspace state is namespaced under `.openspec-workspace/`, so other tools can keep owning root-level files with the same name. - -That distinction matters. The workspace folder is a local coordination surface for opening and inspecting linked repos or folders. Each repo's `openspec/` directory remains the home for repo-owned specs, repo-local changes, and implementation planning. Users do not need to run repo-local `openspec init` inside a workspace folder. - -Stable link names are how a workspace refers to repos and folders. The private workspace record keeps names such as `api`, `web`, or `checkout` and maps them to this runtime's local paths. - -```yaml -# .openspec-workspace/view.yaml -version: 1 -name: platform -context: null -links: - api: /repos/api - web: /repos/web -``` - -When a workspace opens an initiative, `context` records the selected context-store binding and initiative id. Registry-selected stores stay portable by id; path-selected stores intentionally preserve the runtime-local path because `.openspec-workspace/view.yaml` is private local state. - -```yaml -context: - kind: initiative - store: - id: platform - selector: - kind: registry - id: platform - initiative: - id: billing-launch -``` - -Linked paths can be full repos, folders inside a large monorepo, or other existing folders. They do not need repo-local `openspec/` state before they can participate in workspace planning. Later implementation, verify, or archive workflows may require more repo readiness, but planning visibility starts with the link. - -```text -multi-repo: - api -> /repos/api - web -> /repos/web - -large monorepo: - billing -> /repos/platform/services/billing - checkout -> /repos/platform/apps/checkout -``` - -Managed workspaces live under the standard OpenSpec data directory: - -```text -getGlobalDataDir()/workspaces -``` - -That means `$XDG_DATA_HOME/openspec/workspaces` when `XDG_DATA_HOME` is set, `~/.local/share/openspec/workspaces` on Unix-style fallback, and `%LOCALAPPDATA%\openspec\workspaces` on native Windows fallback. Native Windows shells, PowerShell, and WSL2 each keep the path strings for the runtime running OpenSpec. This foundation does not translate between `D:\repo`, `/mnt/d/repo`, and UNC WSL paths. - -Managed workspaces use the namespaced private view record above. The workspace folder remains authoritative for its own private local view. - -Workspace visibility is not change commitment. Set up a workspace when OpenSpec should know which repos or folders are relevant; create a change later when you are ready to plan a feature, fix, project, or other piece of work. - -Useful commands: - -```bash -# Guided setup -openspec workspace setup - -# Automation-friendly setup -openspec workspace setup --no-interactive --name platform --link /repos/api --link web=/repos/web -openspec workspace setup --no-interactive --name platform --link /repos/api --opener codex-cli - -# See known workspaces from the local registry -openspec workspace list -openspec workspace ls - -# Add or repair links for the selected workspace -openspec workspace link /repos/api -openspec workspace link api-service /repos/api -openspec workspace relink api-service /new/path/to/api - -# Check what this machine can resolve -openspec workspace doctor -openspec workspace doctor --workspace platform - -# Refresh workspace-local guidance and agent skills -openspec workspace update -openspec workspace update --workspace platform --tools codex,claude - -# Open the linked working set -openspec workspace open -openspec workspace open platform --agent github-copilot -openspec workspace open --editor - -# Open an initiative as a local workspace view -openspec workspace open --initiative billing-launch --store platform -openspec workspace open --initiative billing-launch --store-path /repos/platform-context -``` - -`workspace setup` always creates the workspace in the standard workspace location, records it in the local registry, shows the workspace location, and requires at least one linked repo or folder. Interactive setup asks for a preferred opener and can install OpenSpec skills for selected agents. Non-interactive setup stores one only when `--opener codex-cli`, `--opener claude`, `--opener github-copilot`, or `--opener editor` is provided. - -Workspace skills are installed only in the workspace root. The active global profile selects which workflow skills are generated; `--tools` selects which agents receive them. Workspace setup and update do not create slash command files even when global delivery includes commands. Run `openspec workspace update` to refresh workspace-local guidance and add, refresh, or remove managed workspace-local skill directories without editing linked repos or folders. - -OpenSpec also maintains root workspace open files: an OpenSpec-managed guidance block in `AGENTS.md` and a machine-local `<workspace-name>.code-workspace` file for VS Code and GitHub Copilot-in-VS-Code opens. A managed workspace is not a repo, so OpenSpec does not create a default workspace `.gitignore` or a default workspace-level `changes/` directory. - -The maintained VS Code workspace lists valid linked repos or folders first, then initiative context when attached, then the OpenSpec workspace files. VS Code displays those entries as a multi-root workspace. - -`workspace open` opens the linked working set with the stored preferred opener unless `--agent <tool>` or `--editor` is passed for that one session. Passing both opener overrides is an error. Root workspace open makes linked repos and folders visible for exploration and context; implementation starts after the user explicitly asks for implementation work. - -`workspace link` and `workspace relink` record existing folders only; they do not create, copy, move, initialize, or edit the linked repo or folder. After a successful link or relink, OpenSpec refreshes the managed guidance and VS Code workspace file. - -Workspace commands that need one workspace can run from anywhere with `--workspace <name>`. If you run them inside a workspace folder or subdirectory, OpenSpec uses that current workspace. If several known workspaces are available and you do not pass `--workspace <name>`, human commands show a picker; `--json` and `--no-interactive` fail with a structured status error instead of prompting. - -Direct workspace commands support JSON output for scripts. JSON responses keep primary data in `workspace`, `workspaces`, or `link` objects and report warnings or errors in `status` arrays. Healthy objects use `status: []`. - ## Specs Specs describe your system's behavior using structured requirements and scenarios. diff --git a/docs/getting-started.md b/docs/getting-started.md index 3d0e9e95b..0f978d18b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -251,3 +251,4 @@ openspec view - [Commands](commands.md) - Full reference for all slash commands - [Concepts](concepts.md) - Deeper understanding of specs, changes, and schemas - [Customization](customization.md) - Make OpenSpec work your way +- [Stores](stores-beta/user-guide.md) - Planning that spans repos or teams? Keep it in its own repo (beta) diff --git a/docs/stores-beta/user-guide.md b/docs/stores-beta/user-guide.md new file mode 100644 index 000000000..e856b5653 --- /dev/null +++ b/docs/stores-beta/user-guide.md @@ -0,0 +1,359 @@ +# Stores: Plan in Its Own Repo + +> **Beta.** Stores, references, targets, working context, and worksets are +> new. Command names, flags, file formats, and JSON output may still change +> shape between releases. Every walkthrough below was run against the +> current build, but re-read this guide after upgrading. + +## The problem this solves + +OpenSpec normally lives inside one code repo: an `openspec/` folder next to +your code, holding specs and changes for that repo. + +That stops fitting the moment your planning is bigger than one repo: + +- Your work spans several repos — one feature touches the API server, the + web app, and a shared library. Whose `openspec/` folder does the plan + live in? +- Your team plans before code exists, or plans things that never become + code in *this* repo. +- Requirements are owned by one team and consumed by others. The wiki + version drifts, and your coding agent can't read it anyway. + +A **store** is the answer: a standalone repo whose whole job is planning. +It has the same `openspec/` shape you already know — specs and changes — +plus a small identity file. You register it on your machine once, by name, +and then every normal OpenSpec command can work in it from anywhere. + +## The shape + +``` + team-plans (a store: planning in its own repo) + ├── .openspec-store/store.yaml identity: "I am team-plans" + └── openspec/ + ├── specs/ what is true + └── changes/ what is in motion + ▲ + │ registered on each machine by name; + │ shared by pushing/cloning like any repo + ┌─────────────┼─────────────┐ + │ │ │ + web-app api-server mobile-app + (code repo) (code repo) (code repo) +``` + +Two rules keep this simple: + +1. **A store is just a git repo.** You commit, push, pull, and review it + yourself. OpenSpec never clones, syncs, or pushes anything on its own. +2. **Declarations, not machinery.** Repos can *declare* how they relate to + stores (shown below). Declarations change what OpenSpec can tell you — + never where your commands act. + +## Five minutes to your first store + +Two commands take you from nothing to a working, store-scoped change: + +```bash +openspec store setup team-plans --path ~/openspec/team-plans +``` + +``` +Store ready: team-plans +Location: /Users/you/openspec/team-plans +OpenSpec root: ready +Registry: registered + +Next: run normal OpenSpec commands against this store, for example: + openspec new change <change-id> --store team-plans +Share this store by committing and pushing it like any Git repo. +``` + +```bash +openspec new change add-login --store team-plans +``` + +``` +Using OpenSpec root: team-plans (/Users/you/openspec/team-plans) +Created change 'add-login' at /Users/you/openspec/team-plans/openspec/changes/add-login/ +Schema: spec-driven +Next: openspec status --change add-login --store team-plans +``` + +That's the whole model. From here the lifecycle is exactly what you know — +`status`, `instructions`, `validate`, `archive` — with `--store team-plans` +on each command, and every printed hint carries the flag for you. The +`Using OpenSpec root:` line always tells you where a command is acting. + +## Story: one team, one planning repo + +A team keeps its specs and changes in `team-plans` instead of scattering +them across code repos. + +**Day one (whoever sets it up):** + +```bash +openspec store setup team-plans --path ~/openspec/team-plans \ + --remote git@github.com:acme/team-plans.git +git -C ~/openspec/team-plans push -u origin main +``` + +Passing `--remote` records the clone URL inside the store's own identity +file (`.openspec-store/store.yaml`), in the initial commit. Every future +clone is born knowing where it came from, so health checks and error +messages can print a complete, pasteable fix for teammates who don't have +it yet. + +**Every teammate (once per machine):** + +```bash +git clone git@github.com:acme/team-plans.git ~/openspec/team-plans +openspec store register ~/openspec/team-plans +``` + +From then on, everyone works in the same planning repo by name: + +```bash +openspec status --store team-plans --change add-login +openspec show add-login --store team-plans +``` + +**Sharing work is git, on purpose.** A change you create exists only in +your checkout until you commit and push it — same as code. Plans get +branches, pull requests, and review for free, because a store is an +ordinary repo. + +**Connecting the team's code repos.** A code repo whose planning is fully +externalized needs exactly one line, in `openspec/config.yaml`: + +```yaml +# web-app/openspec/config.yaml +store: team-plans +``` + +Now every OpenSpec command run inside `web-app` acts on `team-plans` with +no flags at all: + +```bash +cd ~/src/web-app +openspec status --change add-login +``` + +``` +Using OpenSpec root: team-plans (/Users/you/openspec/team-plans) +... +``` + +The pointer is a fallback, never an override: an explicit `--store` always +wins, and if the repo grows real planning folders of its own, those win +(with a warning to remove the stale pointer). + +## Story: requirements that cross team lines + +A platform team owns the requirements. Product teams build against them, +in their own repos, with their own designs. Two declarations describe +that — without moving anyone's work. + +``` + platform-reqs (store) api-server (code repo) + owned by the platform team owned by a product team + ┌──────────────────────────┐ ┌──────────────────────────┐ + │ openspec/specs/ │ ◀────────│ openspec/config.yaml │ + │ payments/spec.md │ reads │ references: │ + │ auth/spec.md │ │ - platform-reqs │ + │ │ │ openspec/specs/ │ + │ openspec/config.yaml │────────▶ │ (their own designs) │ + │ targets: │ is about │ openspec/changes/ │ + │ - api-server │ │ (their own work) │ + │ - web-app │ └──────────────────────────┘ + └──────────────────────────┘ +``` + +**The product team declares what it draws on** in its repo's +`openspec/config.yaml`: + +```yaml +references: + - platform-reqs +``` + +References are read-only context. The repo keeps its own `openspec/` root; +work stays there. What changes: `openspec instructions` in that repo now +includes an index of the referenced store's specs — each with a one-line +summary and the exact fetch command (`openspec show <spec-id> --type spec +--store platform-reqs`). An agent working in `api-server` can find the +upstream payment requirements, cite them, and write its low-level design in +the repo's own root — without anyone pasting context around. + +A reference can carry its clone source, so teammates who don't have the +store yet get a complete fix instead of a dead end: + +```yaml +references: + - { id: platform-reqs, remote: "git@github.com:acme/platform-reqs.git" } +``` + +**The platform team declares what its work is about** in the store's +`openspec/config.yaml`: + +```yaml +targets: + - api-server + - web-app +``` + +Targets name the repos the planning concerns. They are pure declaration: +nothing is cloned, fenced, or enforced. A single change can narrow the set +in its own `.openspec.yaml` when it only concerns some of them. + +**Each person maps names to their machine, once.** Shared files name repos +by id; where a repo is checked out differs per person. The repo map is +local machine settings, never committed: + +```bash +openspec repo register ~/src/api-server # id defaults to folder name +openspec repo list +``` + +Store ids and repo ids share one namespace, so a typo'd +`--store api-server` tells you it's a repo, not a generic "unknown store." + +## Two questions you can always ask + +**"Is my setup healthy?"** — `openspec doctor` checks everything the +current root relates to, read-only, with a pasteable fix per finding: + +``` +Doctor + +Root + Location: /Users/you/src/api-server + OpenSpec root: ok + +References + - platform-reqs: ok (/Users/you/openspec/platform-reqs) + - design-system: Referenced store 'design-system' is not registered on this machine. + Fix: git clone -- git@github.com:acme/design-system.git '/Users/you/openspec/design-system' && openspec store register '/Users/you/openspec/design-system' --id design-system + +Targets + - api-server: mapped (/Users/you/src/api-server) +``` + +**"What am I working with?"** — `openspec context` assembles the working +set: the root, the stores it references, and the repos it targets. + +``` +Working context for api-server (/Users/you/src/api-server) + +OpenSpec root + api-server /Users/you/src/api-server + +Referenced stores + platform-reqs /Users/you/openspec/platform-reqs + Fetch: openspec show <spec-id> --type spec --store platform-reqs +``` + +Both support `--json` for agents. `openspec context --code-workspace +<path>` additionally writes a VS Code workspace file containing the whole +set — the only write this command performs. + +## Worksets: reopen the folders you work on together + +Separate from all of the above: most people open the same few folders +together every session — the planning repo plus two or three code repos. +A **workset** is a personal, named view of exactly that, reopened with one +command in your tool of choice. + +``` + workset "platform" openspec workset open platform + ├── team-plans ~/openspec/team-plans │ + ├── api-server ~/src/api-server ▼ + └── web-app ~/src/web-app all three open in your tool +``` + +```bash +openspec workset create platform \ + --member ~/openspec/team-plans --member ~/src/api-server \ + --tool claude +openspec workset list +``` + +``` +platform (opens in Claude Code) + team-plans /Users/you/openspec/team-plans + api-server /Users/you/src/api-server +``` + +`openspec workset open platform` then launches the saved tool: editors +(VS Code, Cursor) open one window with every member and return; terminal +agents (Claude Code, codex) take over your terminal with every member +attached and no prompt pre-filled, ending when you exit. The first member +is the primary — sessions start there. Override the tool any time with +`--tool <id>`. + +Worksets are deliberately *not* shared state. They live on your machine, +are never committed, and make no claims about the work — they only record +what you like open together. Removing one never touches the member +folders. New tools are configuration, not code: anything launched via a +workspace file or per-folder attach flags can be added under the `openers` +key in the global config (`openspec config edit`). + +## How commands decide where to act + +Every normal command resolves its root the same way, in this order: + +``` +1. --store <id> you said so explicitly → that store +2. nearest openspec/ a real planning root here → this repo + (walking up from cwd) +3. store: pointer config.yaml declares a store → that store +4. none of the above stores registered on this → error with a + machine? selection hint + no stores registered? → the current + directory + (classic behavior) +``` + +The `Using OpenSpec root:` line (and the `root` block in `--json` output) +tells you which case you're in. + +## Known limitations + +- **Beta shape.** Everything on this page may change between releases — + names, flags, file formats, JSON keys. +- **One checkout per store id per machine.** Registering a second checkout + under the same id fails with a hint to `store unregister` first. +- **No sync, ever — by design.** OpenSpec never clones, pulls, or pushes. + A stale checkout shows stale specs until *you* pull; references are + indexed live from whatever is on disk. +- **Some commands stay where they are.** `view`, `templates`, `schemas`, + and the deprecated noun forms (`openspec change show`, ...) act on the + current directory only — no `--store`. +- **Per-machine state is per-machine.** The store registry, the repo map, + and worksets are local settings. Nothing about your machine's layout is + ever committed to shared planning. +- **Two launch styles for worksets.** A tool that can't be launched with a + workspace file or per-folder attach flags can't be added as an opener. +- **Agent JSON has a known casing split** (store-family keys are + snake_case, workflow-family camelCase). Documented in the + [agent contract](../agent-contract.md); unifying it is deferred to a + versioned release. + +## Where things live + +| What | Where | Shared? | +|---|---|---| +| A store's planning | `<store>/openspec/` (specs, changes) | Yes — commit and push it | +| A store's identity | `<store>/.openspec-store/store.yaml` | Yes — committed with the store | +| The store registry and repo map | `<data dir>/openspec/stores/registry.yaml` | No — this machine only | +| Worksets | `<data dir>/openspec/worksets/` | No — this machine only | + +`<data dir>` is `~/.local/share/openspec` on macOS and Linux (or +`$XDG_DATA_HOME/openspec` when set), and `%LOCALAPPDATA%\openspec` on +Windows. + +## Reference + +Exact flags and JSON shapes for every command on this page: +[CLI reference](../cli.md) (Stores, Repo map, Doctor, Working context, +Personal worksets) and the [agent contract](../agent-contract.md). diff --git a/docs/workspaces-beta/agent-cli-playbook.md b/docs/workspaces-beta/agent-cli-playbook.md deleted file mode 100644 index 63e2d1975..000000000 --- a/docs/workspaces-beta/agent-cli-playbook.md +++ /dev/null @@ -1,96 +0,0 @@ -# OpenSpec CLI Playbook For Agents - -Beta note: workspace and initiative flows are usable, but still small. Prefer -plain commands, clear paths, and short status reports. - -## Start By Resolving Context - -Use JSON when you need exact paths. - -```bash -openspec context-store list --json -openspec initiative list --json -openspec initiative show <store>/<initiative> --json -openspec workspace doctor --json -``` - -When the user is working from an opened workspace, treat the workspace as the -local view. Use `workspace doctor --json` to read linked repos/folders and the -selected initiative. Do not assume the current directory is the repo that should -own implementation artifacts. - -## Set Up Context Stores Non-Interactively - -Humans can run `openspec context-store setup` and answer prompts. Agents should -pass the setup inputs explicitly. - -```bash -openspec context-store setup team-context --no-init-git --json -openspec context-store setup team-context --path /path/to/team-context --init-git --json -``` - -Use `context-store unregister <id> --json` to forget a local registration while -leaving files alone. Use `context-store remove <id> --yes --json` only when the -user explicitly asks to delete the local context-store folder. - -## Create Initiatives In Context Stores - -Create shared coordination context in a context store. - -```bash -openspec initiative create billing-launch --store team-context --title "Billing Launch" --summary "Get billing live without losing the plot." -``` - -Then edit the initiative files in the context store: - -- `requirements.md` -- `design.md` -- `decisions.md` -- `questions.md` -- `tasks.md` - -## Explore Or Propose From A Workspace - -When the user asks to explore or draft work from a workspace: - -1. Resolve the workspace with `openspec workspace doctor --json`. -2. Resolve the initiative with `openspec initiative show <store>/<initiative> --json`. -3. Inspect linked repos or folders and identify the likely owning repo. -4. If ownership is ambiguous, ask the user which linked repo should own the - repo-local OpenSpec change. -5. Run explore/propose workflow commands from the owning repo, not from the - workspace root. - -The workspace is the cockpit for the conversation. It is not the durable home -for implementation plans. - -## Create Changes From The Owning Repo - -Repo-local changes belong in the repo that owns the work. - -```bash -openspec new change add-billing-api --initiative team-context/billing-launch -``` - -Run this command with the owning repo as the current working directory. Do not -ask the user to type it and do not run initiative-linked change creation from a -workspace root. If you only know the workspace, resolve linked repo paths first. - -After creating a change, report the absolute paths of the created files and the -initiative link you used. - -## Use Doctor Before Guessing - -```bash -openspec workspace doctor --workspace billing-launch --json -openspec context-store doctor --json -``` - -## Do Not Promise Yet - -- Automatic sync, pull, push, or conflict handling. -- Cloning repos. -- Creating branches, worktrees, or submodules. -- Workspace apply, verify, or archive. -- Progress dashboards. -- Enforced edit boundaries. diff --git a/docs/workspaces-beta/user-guide.md b/docs/workspaces-beta/user-guide.md deleted file mode 100644 index e8cb50514..000000000 --- a/docs/workspaces-beta/user-guide.md +++ /dev/null @@ -1,76 +0,0 @@ -# Using OpenSpec With Your Coding Agent - -Beta note: this is the smallest useful path. You do the local setup. Your agent -manages the OpenSpec work. - -## 1. Create The Shared Place - -```bash -openspec context-store setup -``` - -OpenSpec asks for the context store name, where to put it, and whether to -initialize Git. Press Enter for the managed local data directory unless you -want the store somewhere specific. - -## 2. Ask Your Agent To Create The Initiative - -> Create an OpenSpec initiative called `billing-launch` in `team-context`. Keep -> it short and useful. - -## 3. Open Your Local Workbench - -```bash -openspec workspace open -``` - -Select the initiative from the picker. OpenSpec creates a local workspace view -for it if you do not already have one. When creating a new view, it also asks -which local repos or folders to include. - -The opened editor view shows linked repos and folders first, initiative context -when attached, and a small `OpenSpec workspace` folder last with `AGENTS.md`, -`.openspec-workspace/view.yaml`, and the generated `.code-workspace` file. - -Use `openspec workspace open --initiative team-context/billing-launch --editor` -when you want to skip the picker. Use `--agent codex-cli`, `--agent claude`, or -`--agent github-copilot` instead of `--editor` when you want to open an agent -directly. - -## 4. Check The Local Context - -Ask your agent to inspect the opened workspace before planning work: - -> Check this OpenSpec workspace. Resolve the selected initiative, list the -> linked repos or folders, and tell me if anything important is missing before -> we explore the work. - -If a repo or folder is missing, tell the agent which local path should be linked. -OpenSpec does not clone anything. - -## 5. Explore Before Creating Artifacts - -Use the workspace as the place where the conversation happens: - -> Using initiative `team-context/billing-launch`, explore the work in this -> workspace. Read the initiative context and linked repo context first. Do not -> create a change yet; help me decide what should be proposed and where the -> OpenSpec artifacts should live. - -## 6. Ask For A Draft When Ready - -When exploration has converged, ask the agent to create the right artifact in -the right place: - -> Create a draft repo-local OpenSpec proposal for the owning linked repo and -> link it to `team-context/billing-launch`. Resolve the workspace and initiative -> context yourself, run the needed OpenSpec commands from the correct repo, and -> report the files you created. - -## Tiny Caveat Box - -OpenSpec is not cloning, syncing, branching, or tracking progress dashboards in -this beta flow. It gives you shared initiative context, a local workspace view, -and repo-local plans tied back to the bigger mission. The workspace is where -you and the agent work together; durable plan artifacts should live in the -context store initiative or in the owning repo, not in the workspace root. diff --git a/openspec/changes/workspace-agent-guidance/.openspec.yaml b/openspec/changes/workspace-agent-guidance/.openspec.yaml deleted file mode 100644 index 66dd08a95..000000000 --- a/openspec/changes/workspace-agent-guidance/.openspec.yaml +++ /dev/null @@ -1,2 +0,0 @@ -schema: spec-driven -created: 2026-05-14 diff --git a/openspec/changes/workspace-agent-guidance/proposal.md b/openspec/changes/workspace-agent-guidance/proposal.md deleted file mode 100644 index b9cad3407..000000000 --- a/openspec/changes/workspace-agent-guidance/proposal.md +++ /dev/null @@ -1,100 +0,0 @@ -## Why - -Status: deferred by the context-store-and-initiatives direction. Generated -workspace guidance remains important, but the durable handoff should be designed -around initiatives linked to repo-local OpenSpec changes, not around a -workspace-owned cross-repo planning home. - -The remaining sections preserve the original workspace-agent-guidance direction -for later reference. This work is still expected to matter after initiatives and -initiative-linked repo-local changes exist; it is not the immediate next focus. - -OpenSpec workspaces let users create a planning home and link repos or folders -for cross-area exploration. After setup, the next user expectation is simple: - -> I opened the workspace with my agent. The agent should understand where it is, -> what it can safely inspect, and how to help me turn a product goal into a -> workspace proposal. - -Today that handoff is too thin. Workspace-local skills are installed, and the -CLI can create workspace-scoped changes, but agents still mostly behave like -they are in a normal repo-local OpenSpec project. They do not have a clear -workspace-native starting model before change creation. - -That creates avoidable confusion: - -- linked repos or folders may look like implementation targets instead of - read-only planning context -- agents may not know which registered link names are valid affected areas -- users may feel pressured to know every affected area before planning starts -- the product goal can be lost between workspace exploration and change - creation -- workspace planning can feel like a separate mode instead of normal OpenSpec - stretched across linked areas - -The principle this change should reinforce is: - -> Workspace visibility is not change commitment. - -Linked repos and folders are available for exploration. Creating a workspace -change captures a planning commitment. Implementation edits still require an -explicit implementation workflow with an allowed edit root. - -## Goal - -Make workspace-local planning skills give agents a small, reliable operating -model for starting workspace proposals. - -An agent opened in a workspace should be able to: - -1. recognize that it is operating from a workspace planning home -2. inspect registered workspace links as planning context -3. keep linked repos and folders read-only during planning -4. derive a concise workspace change name and product goal from the user request -5. pass known affected areas only when they match registered workspace link names -6. continue even when affected areas are unresolved, keeping those questions - visible in the normal planning artifacts - -This should feel to the user like the ordinary OpenSpec proposal flow, just with -workspace-aware context and safety. - -## Starting Scope - -Start with the smallest useful surface: - -- workspace-local generated skill guidance -- change-starting workflows used from a workspace planning home -- the relationship between user product goals, registered link names, and - workspace change metadata -- guardrails that keep planning separate from implementation edits - -The first implementation should prefer clear agent guidance over new workflow -machinery. If the existing CLI already exposes enough workspace context, the -skills should use it. If it does not, we should identify the missing context -explicitly before adding heavier behavior. - -## Non-Goals - -This change does not need to solve the full workspace lifecycle. - -Out of scope for this slice: - -- workspace apply semantics -- workspace verify or archive semantics -- branch or worktree orchestration -- creating repo-local changes for each affected area -- shared/team coordination repo behavior -- canonical shared-contract ownership flows -- forcing users to finalize all affected areas before creating a proposal - -## Questions To Work Through - -- What exact workspace context should an agent read before creating a change? -- Is the existing workspace/status/doctor output enough, or do we need a clearer - pre-change context command? -- How should generated skills decide when an affected area is confident enough - to pass as `--areas`? -- Should `--goal` be workspace-only metadata, or should repo-local behavior be - documented too? -- Where should unresolved affected-area questions appear so users and agents - continue from the same source of truth? diff --git a/openspec/changes/workspace-apply-repo-slice/proposal.md b/openspec/changes/workspace-apply-repo-slice/proposal.md deleted file mode 100644 index 3b98089d6..000000000 --- a/openspec/changes/workspace-apply-repo-slice/proposal.md +++ /dev/null @@ -1,58 +0,0 @@ -## Why - -Status: deferred by the context-store-and-initiatives direction. The principle -that apply means implementation is still useful, but the durable handoff should -be designed around initiatives linked to repo-local OpenSpec changes, not around -a workspace-owned cross-repo plan. Do not implement this as a first-class -workspace lifecycle command until that linkage exists. - -The remaining sections preserve the original workspace apply direction for -later reference. This work is still expected to matter after initiatives and -initiative-linked repo-local changes exist; it is not the immediate next focus. - -After a workspace proposal exists, users need a practical way to implement one repo slice at a time. - -In the proper workspace model, apply means implementation: - -```text -Take the selected workspace change. -Take the selected repo slice. -Open or use the right checkout. -Implement that slice while preserving the workspace plan. -``` - -It should not mean copying or materializing planning files into every repo as a user-facing workflow. - -## What Changes - -Add the repo-slice apply workflow for workspace changes: - -- select a workspace change -- select one target repo alias -- resolve the local checkout for that alias -- provide the agent with the workspace plan and repo-specific implementation context -- track progress without making the workspace lose ownership of the plan - -The workflow should support implementation across separate branches or sessions while keeping the workspace proposal as the continuity layer. - -Planning dependency: - -- Depends on `workspace-change-planning`. - -## Capabilities - -### New Capabilities - -- `workspace-repo-slice-apply`: Applies one repo slice of a workspace change as an implementation workflow. - -### Modified Capabilities - -- `cli-artifact-workflow`: Defines workspace apply as implementation rather than materialization. -- `context-injection`: Supplies repo-specific implementation context from a workspace change. - -## Impact - -- Workspace apply command behavior. -- Agent handoff text for repo-slice implementation. -- Local checkout resolution and branch/worktree assumptions. -- Tests that apply operates on one target repo slice and does not require copying workspace planning artifacts as the primary user contract. diff --git a/openspec/changes/workspace-reimplementation-roadmap/HISTORICAL_DIRECTION.md b/openspec/changes/workspace-reimplementation-roadmap/HISTORICAL_DIRECTION.md deleted file mode 100644 index d6319d293..000000000 --- a/openspec/changes/workspace-reimplementation-roadmap/HISTORICAL_DIRECTION.md +++ /dev/null @@ -1,511 +0,0 @@ -# Workspace Reimplementation Direction - -Date: 2026-04-30 - -## Status - -This document is historical product direction from the workspace POC follow-up. -It remains useful for preserved workspace setup, link, open, update, doctor, and -agent-visibility decisions. - -It no longer defines the durable coordination model. The current authority is -`openspec/initiatives/context-store-and-initiatives/direction.md`, which locks -this boundary: - -```text -Context stores sync truth. -Collections shape truth. -Initiatives coordinate work. -Workspaces open local views. -Changes implement repo-owned slices. -``` - -Superseded here: workspace as the durable planning home, workspace-level -planning artifacts as the canonical shared cross-repo plan, and workspace -apply/verify/archive as the next first-class lifecycle commands. - -Deferred here: apply, verify, archive, branch/worktree orchestration, -cross-repo validation, dependency graph enforcement, and governance flows until -initiative-linked repo-local changes exist. - -Fresh-agent entry point: read `openspec/changes/workspace-reimplementation-roadmap/START_HERE.md` first, then return to this document for the full product direction. - -This document captures the intended direction for reimplementing OpenSpec workspace support from scratch, based on what we learned from the workspace POC. - -The sections below are historical POC follow-up direction. Use them for lessons -and preserved local-view behavior only. Do not treat later workspace lifecycle -sections as active implementation guidance. - -The reimplementation should be ordered around the path a real user takes through OpenSpec: - -```text -set up workspace - -> link repos or folders - -> open workspace - -> explore across repos or folders - -> create proposal - -> apply one repo slice - -> verify - -> archive -``` - -The goal is not to rebuild every POC mechanism. The goal is to get one user-facing capability working at a time, in the same order a user would naturally create, implement, verify, and archive a change. - -## North Star - -A user should think: - -```text -I have a multi-repo product goal. -I set up an OpenSpec workspace. -I open it with my agent. -The agent can see the linked repos or folders. -We explore until the scope is clear. -Then we create a proposal. -Then we implement one repo slice at a time. -``` - -They should not think: - -```text -I need to create a change so repos become visible. -I need to materialize repo-local artifacts. -I need to understand implementation-specific workspace machinery. -I need to manage target metadata separately from proposal files. -``` - -The core product rule is: - -```text -Workspace visibility is not change commitment. -``` - -Linked repos or folders are planning context. Creating a change is a planning commitment. Applying a change is an implementation workflow. - -## Build Order - -### 1. Workspace Setup And Links - -First make workspace setup boring and solid. - -User goal: - -```text -Create a planning home and link the repos or folders OpenSpec should know about. -``` - -Expected surface: - -```bash -openspec workspace setup -openspec workspace setup --no-interactive --name platform --link /path/to/api --link web=/path/to/web -openspec workspace list -openspec workspace ls -openspec workspace link /path/to/api -openspec workspace link api-service /path/to/api -openspec workspace relink api /new/path/to/api -openspec workspace doctor -``` - -Expected outcome: - -```text -workspace-folder/ - changes/ - .openspec-workspace/ - workspace.yaml - local.yaml -``` - -Product decisions: - -- Use `.openspec-workspace/`, not `.openspec/`, for workspace metadata. -- Keep `changes/` visible in the workspace folder. -- Keep setup as the only public creation path for the first release; do not expose `workspace create`. -- Use `workspace link` and `workspace relink`, not POC-era `add-repo` or `update-repo`. -- Allow linked repos or folders without repo-local `openspec/` state. -- Keep stable link names in shared workspace state and local paths in machine-local state. -- Make `doctor` show link names, resolved paths, repo-local specs paths when present, and suggested fixes. - -Defer: - -- Agent launch and workspace open behavior. -- Preferred-agent prompts. -- Owner or handoff metadata. -- Workspace change creation or target selection. -- Branches. -- Worktrees. -- Apply. -- Archive. -- Complex target lifecycle. - -Done when a user can set up a workspace, link repos or folders, list known workspaces, relink local paths, and run `doctor` to see exactly what OpenSpec can resolve. - -### 2. Workspace Open - -Next make the workspace openable in the way users expect. - -User goal: - -```text -Open this multi-repo planning context with my coding agent. -``` - -Expected surface: - -```bash -openspec workspace open -openspec workspace open --agent codex -openspec workspace open --agent github-copilot -``` - -Product behavior: - -- `workspace open` opens the coordination workspace plus linked repos or folders. -- Repo visibility is default. -- Change selection is optional focus, not the mechanism for repo access. -- `--agent` should be a one-session override by default. Persisting the preferred agent should require an explicit preference-setting action. - -For GitHub Copilot, generate or open a `.code-workspace` file with: - -```text -workspace folder -linked repo or folder A -linked repo or folder B -``` - -For Claude and Codex, attach the linked repo or folder directories through the agent's supported mechanism. - -Defer: - -- `workspace open --change`. -- In-session upgrade flows. -- Per-change attachment restrictions. - -Done when opening a workspace gives the agent visibility into the coordination root and all linked repos or folders. - -### 3. Agent Guidance And Explore - -Then make exploration work. - -User goal: - -```text -Tell the agent a rough product goal and have it inspect the repos before creating a proposal. -``` - -Expected user prompt: - -```text -Explore how we should make the OpenSpec docs available on the landing page. -Look across the linked repos or folders, but do not implement yet. -``` - -Agent behavior: - -- Understand it is in workspace mode. -- Inspect linked repos or folders. -- Explain likely affected repos. -- Ask for clarification only when needed. -- Avoid implementation edits during explore. - -Build: - -- Workspace-level `AGENTS.md` guidance. -- Normal OpenSpec skills and commands in workspace sessions. -- Workspace-specific guidance layered on top of normal `/explore`, not replacing it. - -Defer: - -- Proposal artifact generation. -- Target confirmation commands. -- Apply context providers. - -Done when a user can open a workspace and run a useful cross-repo exploration without creating a dummy change. - -### 4. Proposal Creation - -Only after explore works, build proposal creation. - -User goal: - -```text -Now that we understand the scope, capture the plan. -``` - -Expected user prompt: - -```text -Create a proposal for this change. -Target the repos that are actually affected. -``` - -Preferred artifact shape: - -```text -changes/integrate-docs/ - proposal.md - design.md - tasks.md - specs/ - openspec/ - docs-conventions/spec.md - landing/ - docs-routing/spec.md -``` - -Key workflow rule: - -```text -/explore may leave targets unknown. -/propose may discover targets. -/propose must confirm targets before saying ready for apply. -``` - -Targets should be represented by the proposal artifacts themselves where possible. If there is `specs/landing/...`, then `landing` is in scope. Avoid a separate required `targets: [...]` metadata list as the active source of truth. - -Defer: - -- Repo-local materialization. -- Worktree selection. -- Multi-repo implementation. -- Archive. - -Done when a user can explore, then create a workspace proposal with repo-scoped specs and tasks. - -### 5. Status - -Before implementation, make status excellent. - -User goal: - -```text -Where are we, what repos are involved, and is this ready to implement? -``` - -Expected surface: - -```bash -openspec status -openspec status --change integrate-docs -``` - -Human output should answer: - -```text -Change: integrate-docs -Scope: openspec, landing -Proposal: present -Design: present -Tasks: present -Ready for apply: yes/no -``` - -Status should also catch structural mistakes: - -- Unknown repo folder under `specs/`. -- Missing tasks. -- No confirmed affected repo. -- Linked repo or folder path missing. - -Done when the agent and user can trust status before applying. - -### 6. Apply One Repo Slice - -Only now build `/apply`. - -User goal: - -```text -Implement the planned slice for one repo. -``` - -Expected user prompt: - -```text -/apply integrate-docs for landing -``` - -Product contract: - -```text -/apply means implement. -``` - -It does not mean: - -```text -copy planning files -materialize repo-local OpenSpec state -create the proposal files for the first time -``` - -Agent behavior: - -1. Ask OpenSpec for apply context. -2. Read proposal, design, tasks, and relevant specs. -3. Confirm the target repo checkout. -4. Edit only that repo. -5. Update workspace tasks. -6. Run relevant checks. - -This likely wants a normalized context command internally, but that is supporting machinery: - -```json -{ - "mode": "workspace", - "change": "integrate-docs", - "target": "landing", - "implementationRoot": "/repos/openspec-landing", - "contextFiles": [ - "changes/integrate-docs/proposal.md", - "changes/integrate-docs/design.md", - "changes/integrate-docs/tasks.md", - "changes/integrate-docs/specs/landing/docs-routing/spec.md" - ], - "allowedEditRoots": [ - "/repos/openspec-landing" - ], - "tasksFile": "changes/integrate-docs/tasks.md" -} -``` - -Defer: - -- Applying multiple repos at once. -- Automatic branch creation. -- Worktree management. -- Repo-local OpenSpec mirroring. - -Done when one repo slice can be implemented from the central workspace plan. - -### 7. Verify - -Then build verification. - -User goal: - -```text -Check whether the implemented repo slice satisfies the plan. -``` - -Expected prompt: - -```text -/verify integrate-docs for landing -``` - -Behavior: - -- Read the same normalized context as `/apply`. -- Inspect the implementation checkout. -- Check tasks and specs for that repo. -- Run repo validation. -- Report gaps clearly. - -Default behavior should verify one repo slice. Whole-workspace verification can come later. - -Done when a user can verify one implemented repo slice against the central workspace plan. - -### 8. Archive - -Archive comes last in the first complete loop. - -User goal: - -```text -The change is done. Move it out of active planning. -``` - -Expected prompt: - -```text -/archive integrate-docs -``` - -Behavior: - -- Require all targeted repo slices to be complete or explicitly accepted. -- Archive the workspace change. -- Do not require repo-local planning copies unless OpenSpec later decides that repo-local archival matters. - -Done when a user can complete the full lifecycle: - -```text -workspace setup - -> link repos or folders - -> open - -> explore - -> propose - -> apply repo A - -> apply repo B - -> verify - -> archive -``` - -## Implementation Discipline - -Build only the next user-visible step. - -The sequence should stay grounded in these questions: - -```text -1. Can I set up the workspace? -2. Can I see my linked repos or folders? -3. Can my agent explore them? -4. Can we capture a proposal? -5. Can status tell us if it is ready? -6. Can the agent implement one repo slice? -7. Can we verify it? -8. Can we archive it? -``` - -Avoid starting with internal abstractions unless they are required for the next user-visible capability. - -Do not start with: - -- Target metadata machinery. -- Materialization. -- Adapter abstractions. -- Branch orchestration. -- Worktree orchestration. -- Multi-repo apply. - -Those may matter later, but they should not define the first reimplementation path. - -## Historical Product Shape - -This was the older workspace product shape. It is preserved here so POC lessons -remain understandable, but it is superseded by the context-store-and-initiatives -direction for durable coordination. - -The historical durable product model was: - -```text -workspace = planning home -links = repos or folders visible for planning -proposal = scoped planning commitment -repo slice = one affected repo or folder in the plan -branch/worktree = implementation checkout -/apply = implement one selected repo slice -``` - -The current durable product model is: - -```text -context store = synced shared truth -initiative = durable coordination object -workspace = local opened view -repo change = repo-owned implementation plan -``` - -The historical user journey was: - -```text -Open the workspace. -Ask the agent to explore. -Create the proposal when scope is clear. -Implement one repo slice at a time. -Verify. -Archive. -``` diff --git a/openspec/changes/workspace-reimplementation-roadmap/POC_REFERENCE_GUIDE.md b/openspec/changes/workspace-reimplementation-roadmap/POC_REFERENCE_GUIDE.md deleted file mode 100644 index 5a3ed6836..000000000 --- a/openspec/changes/workspace-reimplementation-roadmap/POC_REFERENCE_GUIDE.md +++ /dev/null @@ -1,266 +0,0 @@ -# Workspace POC Reference Guide - -This guide is for a fresh agent starting a new session with no prior context about the workspace POC. - -Root entry point: `START_HERE.md`. - -The goal is not to continue the POC. The goal is to use it as research material -before preserving or replacing specific behavior from the current base. - -Current product authority lives in -`openspec/initiatives/context-store-and-initiatives/`. Under that direction, -workspace setup/open/update/doctor behavior remains useful local-view -infrastructure. Workspace-level apply, verify, and archive research is deferred -until initiative-linked repo-local changes exist. - -## Reference Point - -Use this exact commit as the stable reference: - -```text -workspace-poc @ 79a45ac043f414e63d13e08b9da83b135cb20a39 -``` - -Do not rely only on the moving branch name. Do not merge this commit into the implementation branch. Do not cherry-pick from it unless a later proposal explicitly decides that a small piece should be preserved. - -## What The POC Was Trying To Prove - -Start from the user journey: - -```text -create workspace - -> add repos - -> open workspace with an agent - -> explore across repos - -> create a proposal - -> apply one repo slice - -> verify - -> archive -``` - -The POC is useful if it helps answer: - -- What did the user experience feel like when workspace mode worked? -- Which CLI surfaces made the workflow easier to understand? -- Which tests captured real product expectations? -- Which implementation choices were shortcuts that should not survive? -- Which terminology became misleading once the desired product shape was clearer? - -## First Files To Read - -Read these from the POC commit before implementation: - -```text -WORKSPACE_REIMPLEMENTATION_DIRECTION.md -WORKSPACE_POC_FOLLOWUP_NOTES.md -docs/workspace.md -docs/workspace-demo.md -docs/cli.md -src/commands/workspace.ts -src/core/workspace/open.ts -test/commands/workspace/open.test.ts -test/core/workspace/open.test.ts -test/cli-e2e/workspace/workspace-open-cli.test.ts -``` - -Optional deeper context: - -```text -workspace-poc-explorer.html -workspace-poc-phase-playground.html -copilot-session-d4e9c61e-readable.md -copilot-session-d4e9c61e-timeline.md -``` - -The optional files are historical research aids. Use them to understand how the POC evolved, not as implementation requirements. - -## How To Inspect The POC Safely - -Preferred approach: use a separate worktree or read files directly from the pinned commit. - -Example direct reads: - -```bash -git show 79a45ac043f414e63d13e08b9da83b135cb20a39:WORKSPACE_REIMPLEMENTATION_DIRECTION.md -git show 79a45ac043f414e63d13e08b9da83b135cb20a39:src/commands/workspace.ts -git diff origin/main...79a45ac043f414e63d13e08b9da83b135cb20a39 --stat -``` - -Example separate worktree: - -```bash -git worktree add ../openspec-workspace-poc 79a45ac043f414e63d13e08b9da83b135cb20a39 -``` - -Keep the implementation branch based on the current target branch. The POC worktree is for reading and running tests only. - -## What To Bring Back - -Before implementing a slice, come back with a short POC findings note: - -```text -POC findings for <slice>: - -User behavior to preserve: -- ... - -Tests or examples worth translating: -- ... - -Implementation shortcuts to avoid: -- ... - -Open design questions: -- ... -``` - -Put durable findings in the relevant OpenSpec proposal or design artifact. Do not leave important decisions only in chat. - -## Slice-Specific Reading - -### `workspace-foundation` - -Focus on: - -- workspace folder shape -- metadata directory naming -- local versus committed state -- stable workspace name semantics - -Read: - -```text -WORKSPACE_REIMPLEMENTATION_DIRECTION.md -WORKSPACE_POC_FOLLOWUP_NOTES.md -docs/workspace.md -src/commands/workspace.ts -``` - -Bring back: - -- the storage model worth keeping -- the metadata naming decision -- any compatibility risks with repo-local `openspec/` - -### `workspace-create-and-register-repos` - -Focus on: - -- how a user creates a workspace -- how repos or folders are linked -- what `doctor` or equivalent status output should explain -- how POC `create`/`add-repo` behavior maps to the target `setup`/`link`/`relink`/`doctor` flow before change creation -- how planning-only repos and monorepo modules differ from implementation-ready repo-local OpenSpec projects - -Read: - -```text -docs/workspace.md -docs/workspace-demo.md -src/commands/workspace.ts -test/commands/workspace/setup.test.ts -``` - -Bring back: - -- expected commands -- expected files -- validation behavior for bad paths, duplicate workspace names, missing paths, planning-only links, and duplicate link names - -### `workspace-open-agent-context` - -Focus on: - -- what context the agent receives -- how linked repos or folders become visible -- how one-session agent selection should work -- what should be stable guidance versus dynamic launch context - -Read: - -```text -WORKSPACE_POC_FOLLOWUP_NOTES.md -src/commands/workspace.ts -src/core/workspace/open.ts -test/commands/workspace/open.test.ts -test/core/workspace/open.test.ts -test/cli-e2e/workspace/workspace-open-cli.test.ts -``` - -Bring back: - -- launch-context requirements -- agent-specific behavior to preserve -- prompt or guidance text that should become stable instructions - -### `workspace-change-planning` - -Focus on: - -- when repo scope becomes a planning commitment -- whether targets should be inferred from artifacts -- how proposal, design, tasks, and specs should be arranged - -Read: - -```text -WORKSPACE_REIMPLEMENTATION_DIRECTION.md -docs/workspace.md -docs/workspace-demo.md -``` - -Bring back: - -- the artifact shape to use -- how targets should be confirmed -- which POC target metadata ideas should be avoided or deferred - -### `workspace-apply-repo-slice` - -Focus on: - -- the terminology decision that apply means implementation -- what context the agent needs to implement one repo slice -- why materialization should not be the user-facing contract - -Read: - -```text -WORKSPACE_REIMPLEMENTATION_DIRECTION.md -WORKSPACE_POC_FOLLOWUP_NOTES.md -``` - -Bring back: - -- the normalized apply context shape -- the user-facing apply contract -- any POC materialization behavior that should be explicitly rejected - -### `workspace-verify-and-archive` - -Focus on: - -- partial repo completion versus full workspace completion -- how verification should report gaps -- how archive should avoid forcing repo-local planning copies - -Read: - -```text -WORKSPACE_REIMPLEMENTATION_DIRECTION.md -docs/workspace-demo.md -``` - -Bring back: - -- the minimum useful verify behavior -- the archive preconditions -- the distinction between repo-slice completion and workspace hard-done state - -## Ground Rules - -- Treat the POC as evidence, not inheritance. -- Preserve user-visible lessons before preserving code. -- Prefer current repo patterns over POC-only abstractions. -- Implement one user-visible step at a time. -- Update this roadmap when a POC lesson changes a later slice. diff --git a/openspec/changes/workspace-reimplementation-roadmap/README.md b/openspec/changes/workspace-reimplementation-roadmap/README.md deleted file mode 100644 index 65716de54..000000000 --- a/openspec/changes/workspace-reimplementation-roadmap/README.md +++ /dev/null @@ -1,107 +0,0 @@ -# Workspace Reimplementation Roadmap - -This change is the continuity layer for reimplementing workspace support across multiple sessions and branches. - -## Current Status - -This roadmap is historical and has been reframed by -`openspec/initiatives/context-store-and-initiatives/`. Fresh agents should use -the initiative direction as product authority and this roadmap as reference for -POC lessons and preserved local-view behavior. - -Keep: - -- workspace setup, link, relink, list, open, update, and doctor -- linked repos and folders as local planning context -- workspace-local skills as local agent guidance -- the POC as research material only - -Supersede: - -- workspace as the durable shared planning home -- workspace-level planning artifacts as the canonical cross-repo plan -- workspace change planning as the long-term source of truth - -Defer: - -- workspace apply, verify, and archive as first-class lifecycle commands -- branch/worktree orchestration, strong cross-repo validation, and dependency - graph enforcement - -Do not pick up the next unfinished flat sibling change from this roadmap unless -a later initiative-linked repo-change design explicitly reactivates it. - -Root entry point for fresh agents: `START_HERE.md`. - -The user journey this historical roadmap was implementing is: - -```text -create workspace - -> add repos - -> open workspace with agent context - -> plan a cross-repo change - -> implement one repo slice - -> verify and archive -``` - -The POC branch is reference material only: - -```text -workspace-poc @ 79a45ac043f414e63d13e08b9da83b135cb20a39 -``` - -Use it to understand behavior, tests, and lessons learned. Do not merge it or preserve its architecture by default. The full source direction document from that branch is captured in `HISTORICAL_DIRECTION.md`. - -Fresh agents should read `POC_REFERENCE_GUIDE.md` before implementing any slice. That guide explains how to inspect the pinned POC commit, which files to read for each slice, and what findings to bring back into the OpenSpec artifacts. - -## Historical Change Order - -The original flat sibling changes were: - -1. `workspace-foundation` -2. `workspace-create-and-register-repos` -3. `workspace-open-agent-context` -4. `workspace-change-planning` -5. `workspace-agent-guidance` -6. `workspace-apply-repo-slice` -7. `workspace-verify-and-archive` - -OpenSpec currently discovers active changes as immediate directories under `openspec/changes/`, and change names are kebab-case identifiers. These changes remain useful reference artifacts, but they are no longer a direct implementation queue. - -## Dependency Notes - -`workspace-foundation` establishes the storage, root detection, and naming model. Every later slice should build on that model instead of redefining workspace metadata. - -`workspace-create-and-register-repos` creates the workspace and makes linked repos or folders visible before a change exists. Linked items may be full repos, monorepo modules, or planning-only folders. This preserves the product rule that workspace visibility is not change commitment. - -`workspace-open-agent-context` gives the agent the workspace location, linked repos or folders, active changes, and selected change scope. - -`workspace-change-planning` created the beta workspace-level planning commitment and identified target repo slices. Under the initiative direction, this model is legacy or transitional rather than the durable shared plan. - -`workspace-agent-guidance` makes workspace-local workflow skills use the planning model deliberately: inspect linked context, seed workspace changes with goal and known affected areas, and preserve linked repos as read-only planning context until apply selects an edit root. - -`workspace-apply-repo-slice` is deferred until initiative-linked repo-local changes define the implementation handoff. - -`workspace-verify-and-archive` is deferred until initiative status and linked repo-local change lifecycle exist. - -## Session Handoff Prompt - -Use this prompt at the start of future implementation sessions: - -```text -Continue the context-store-and-initiatives direction. Read -openspec/initiatives/context-store-and-initiatives/direction.md and -openspec/initiatives/context-store-and-initiatives/roadmap.md first. Use -openspec/changes/workspace-reimplementation-roadmap/START_HERE.md, -openspec/changes/workspace-reimplementation-roadmap/README.md, -openspec/changes/workspace-reimplementation-roadmap/HISTORICAL_DIRECTION.md, -openspec/changes/workspace-reimplementation-roadmap/POC_REFERENCE_GUIDE.md, and -workspace-poc at 79a45ac043f414e63d13e08b9da83b135cb20a39 as historical -reference material only. Preserve useful local-view workspace behavior, but do -not implement workspace apply, verify, or archive until initiative-linked -repo-local changes exist. -``` - -## Branching Guidance - -Each sibling change may be implemented on its own branch or PR. Keep decisions that affect later slices in this README or in the relevant proposal so future sessions do not depend on chat history. diff --git a/openspec/changes/workspace-reimplementation-roadmap/START_HERE.md b/openspec/changes/workspace-reimplementation-roadmap/START_HERE.md deleted file mode 100644 index 9ffedc440..000000000 --- a/openspec/changes/workspace-reimplementation-roadmap/START_HERE.md +++ /dev/null @@ -1,105 +0,0 @@ -# Workspace Reimplementation Start Here - -This is the grep-friendly historical entry point for agents working on the -workspace reimplementation. - -## Current Status - -The original workspace lifecycle roadmap has been reframed by the context store -and initiatives direction. Fresh agents should treat this document and the POC -materials as reference for preserved local-view infrastructure, not as the next -implementation queue. - -Current product authority lives in: - -1. `openspec/initiatives/context-store-and-initiatives/direction.md` -2. `openspec/initiatives/context-store-and-initiatives/roadmap.md` - -The locked boundary is: - -```text -Context stores sync truth. -Collections shape truth. -Initiatives coordinate work. -Workspaces open local views. -Changes implement repo-owned slices. -``` - -Useful search terms: - -```text -workspace reimplementation -workspace poc -workspace-poc -workspace reference guide -workspace roadmap -fresh agent -start here -``` - -## Start Here - -Read these files in order: - -1. `openspec/initiatives/context-store-and-initiatives/direction.md` -2. `openspec/initiatives/context-store-and-initiatives/roadmap.md` -3. `openspec/changes/workspace-reimplementation-roadmap/HISTORICAL_DIRECTION.md` -4. `openspec/changes/workspace-reimplementation-roadmap/README.md` -5. `openspec/changes/workspace-reimplementation-roadmap/POC_REFERENCE_GUIDE.md` - -The POC reference commit is: - -```text -workspace-poc @ 79a45ac043f414e63d13e08b9da83b135cb20a39 -``` - -Use the POC as research material. Do not merge it into an implementation branch. -Do not preserve its architecture unless a later initiative or repo-local change -design explicitly decides to do so. - -## Historical Implementation Order - -The original flat OpenSpec order was: - -1. `workspace-foundation` -2. `workspace-create-and-register-repos` -3. `workspace-open-agent-context` -4. `workspace-change-planning` -5. `workspace-agent-guidance` -6. `workspace-apply-repo-slice` -7. `workspace-verify-and-archive` - -Current disposition: - -- Keep setup, link, relink, list, open, update, and doctor as beta local-view - infrastructure. -- Treat workspace planning as legacy or transitional behavior, not the durable - cross-repo source of truth. -- Do not implement `workspace-apply-repo-slice` or - `workspace-verify-and-archive` as first-class workspace lifecycle commands - until initiative-linked repo-local changes exist. -- Use `workspace-reimplementation-roadmap` as continuity and reference, not as - the active shipping sequence. - -## Before Editing - -For the slice you are about to implement, inspect the pinned POC commit using `POC_REFERENCE_GUIDE.md`, then write down: - -```text -POC findings for <slice>: - -User behavior to preserve: -- ... - -Tests or examples worth translating: -- ... - -Implementation shortcuts to avoid: -- ... - -Open design questions: -- ... -``` - -Capture durable findings in the relevant initiative, context-store, or -repo-local OpenSpec artifact so future sessions do not depend on chat history. diff --git a/openspec/changes/workspace-reimplementation-roadmap/proposal.md b/openspec/changes/workspace-reimplementation-roadmap/proposal.md deleted file mode 100644 index 99daf917d..000000000 --- a/openspec/changes/workspace-reimplementation-roadmap/proposal.md +++ /dev/null @@ -1,62 +0,0 @@ -## Why - -Workspace support needs to be reimplemented as a user-facing workflow, not carried forward as a direct port of the proof of concept. - -Status: this roadmap is now historical reference. The active product direction is -the context-store-and-initiatives initiative, where initiatives coordinate -durable cross-repo work, workspaces open local views, and repo-local changes own -implementation. Keep workspace setup/open/update/doctor infrastructure, but do -not treat workspace apply, verify, or archive as the next shipping sequence -until initiative-linked repo-local changes exist. - -A user should be able to say they have a multi-repo product goal, create a workspace, add the relevant repos, open that workspace with an agent, plan the change, implement one repo slice at a time, verify it, and archive it. The POC branch captured useful behavior and discovery, but its implementation should remain reference material rather than the base architecture. - -This roadmap also needs to survive multiple sessions and branches. Current OpenSpec change discovery treats active changes as flat immediate directories under `openspec/changes/`, and change names are kebab-case identifiers rather than nested paths. This change is therefore a flat planning container with sibling proposal changes instead of nested child changes. - -Reference material: - -- `workspace-poc` at `79a45ac043f414e63d13e08b9da83b135cb20a39` -- `WORKSPACE_REIMPLEMENTATION_DIRECTION.md` on that branch -- `WORKSPACE_POC_FOLLOWUP_NOTES.md` on that branch - -## What Changes - -Add a lightweight roadmap for reimplementing workspace support as a stack of flat sibling OpenSpec changes: - -- `workspace-foundation` -- `workspace-create-and-register-repos` -- `workspace-open-agent-context` -- `workspace-change-planning` -- `workspace-agent-guidance` -- `workspace-apply-repo-slice` -- `workspace-verify-and-archive` - -Each sibling change owns one step in the lived user journey. Dependencies are documented in proposal prose for now. When change stacking metadata lands, this roadmap can be migrated to explicit `parent` and `dependsOn` metadata. - -The intended order is: - -```text -workspace-foundation - -> workspace-create-and-register-repos - -> workspace-open-agent-context - -> workspace-change-planning - -> workspace-agent-guidance - -> workspace-apply-repo-slice - -> workspace-verify-and-archive -``` - -## Capabilities - -### New Capabilities - -- `workspace-reimplementation-roadmap`: Coordinates the workspace reimplementation plan across multiple flat OpenSpec changes. - -### Modified Capabilities - -- `openspec-conventions`: Clarifies that this workspace effort uses flat sibling changes until nested or stacked change metadata is supported. - -## Impact - -- Planning only in this PR. -- Future changes will affect workspace metadata, workspace CLI flows, agent context construction, workspace change planning, workspace-local agent guidance, repo-slice application, verification, and archive behavior. -- No runtime behavior changes are introduced by this roadmap proposal. diff --git a/openspec/changes/workspace-verify-and-archive/proposal.md b/openspec/changes/workspace-verify-and-archive/proposal.md deleted file mode 100644 index 8856a9583..000000000 --- a/openspec/changes/workspace-verify-and-archive/proposal.md +++ /dev/null @@ -1,57 +0,0 @@ -## Why - -Status: deferred by the context-store-and-initiatives direction. Per-repo -progress visibility remains important, but verify/archive should be redesigned -around initiative status and linked repo-local OpenSpec changes, not around -workspace-owned final archive state. Do not implement this as a first-class -workspace lifecycle command until that linkage exists. - -The remaining sections preserve the original workspace verify/archive direction -for later reference. This work is still expected to matter after initiatives and -initiative-linked repo-local changes exist; it is not the immediate next focus. - -Users need to know whether a cross-repo workspace change is complete without flattening all repo progress into one ambiguous done state. - -The desired lifecycle is: - -```text -Verify each repo slice. -See which slices are complete or still open. -Archive repo-local results when appropriate. -Archive the workspace change when the cross-repo goal is done. -``` - -Verification and archive should make the user's cross-repo status clearer, not force them to reason about internal artifact placement. - -## What Changes - -Add workspace-aware verify and archive behavior: - -- verify workspace-level change structure and target repo status -- show per-repo slice progress -- support repo-local archive work where needed -- support explicit workspace-level archive when the coordinated goal is complete -- avoid treating partial repo completion as full workspace completion - -Planning dependency: - -- Depends on `workspace-apply-repo-slice`. - -## Capabilities - -### New Capabilities - -- `workspace-verify-archive`: Verifies and archives workspace changes with per-repo progress visibility. - -### Modified Capabilities - -- `cli-archive`: Adds workspace-aware archive semantics. -- `opsx-verify-skill`: Adds workspace verification guidance. -- `opsx-archive-skill`: Adds workspace archive guidance. - -## Impact - -- Workspace status, verify, and archive behavior. -- Per-repo slice completion reporting. -- Workspace-level hard-done marker or equivalent archive state. -- Tests for partial completion, final workspace archive, and compatibility with standalone repo-local archive flows. diff --git a/openspec/initiatives/context-store-and-initiatives/README.md b/openspec/initiatives/context-store-and-initiatives/README.md index a31c8faf1..6574a7d4c 100644 --- a/openspec/initiatives/context-store-and-initiatives/README.md +++ b/openspec/initiatives/context-store-and-initiatives/README.md @@ -1,28 +1,44 @@ # Context Store And Initiatives -This initiative is the source of product intent for context stores, -collections, initiatives, workspaces, and repo-local changes. +Status: transition evidence / beta history. -Start here before continuing workspace or initiative work. +This folder preserves the beta context-store and workspace direction, the +decisions made while exploring it, and the evidence that led to the simpler +Git-native model. + +It is not the active product roadmap or implementation queue. For current +direction, start with: + +1. `openspec/work/simplify-context-and-workspace-model/goal.md` +2. `openspec/work/simplify-context-and-workspace-model/roadmap.md` + +The `direction-git-native-work.md` note is the transition note that led to the +current goal. If it conflicts with the current `goal.md`, the current `goal.md` +wins. ## Reading Order -1. `direction.md` explains the product model and principles. -2. `roadmap.md` lists the ordered roadmap. -3. `tasks.md` shows initiative-wide progress. -4. `decisions.md` records accepted decisions. -5. `questions.md` tracks unresolved questions. -6. `work-items/<id>/` contains execution notes for one roadmap item. +Use this reading order when researching the beta history: + +1. `direction-git-native-work.md` explains the transition from the old beta + model toward Git-native specs and work. +2. `direction.md` preserves the earlier context-store and initiative direction. +3. `roadmap.md` preserves the historical beta roadmap snapshot. +4. `tasks.md` preserves historical initiative-wide progress. +5. `decisions.md` records accepted decisions made during the beta. +6. `questions.md` tracks questions that were open at the time. +7. `work-items/<id>/` contains execution notes for one historical roadmap item. ## Boundary -Initiative artifacts carry product intent and roadmap decisions. OpenSpec specs -describe the current behavioral contract behind the code. +These artifacts preserve product intent, roadmap decisions, and beta evidence +from the old model. OpenSpec specs describe the current behavioral contract +behind the code. Do not rewrite specs for future intent until behavior changes with an implementation slice. -The current product boundary is: +The earlier product boundary was: ```text Context stores sync truth. @@ -31,3 +47,12 @@ Initiatives coordinate work. Workspaces open local views. Changes implement repo-owned slices. ``` + +The newer direction is: + +```text +OpenSpec is a Git-native artifact format for specs and work. + +Specs are what is true. +Work is what is in motion. +``` diff --git a/openspec/initiatives/context-store-and-initiatives/decisions.md b/openspec/initiatives/context-store-and-initiatives/decisions.md index d04e6726b..e2aa873d6 100644 --- a/openspec/initiatives/context-store-and-initiatives/decisions.md +++ b/openspec/initiatives/context-store-and-initiatives/decisions.md @@ -202,3 +202,24 @@ Implications: - Emit advisory edit boundaries only; do not enforce write restrictions. - Continue to open known existing local paths only. Do not clone, branch, create worktrees, use submodules, or infer local repos in Item 10. + +## 2026-05-30: Defer Hardcoded Agent Handoff Guidance + +Decision: Skip Item 13, agent handoff output and delivery polish, as an +implementation item for now. + +Why: The underlying beta pain is real: users and agents need better receipts +after setup, initiative creation, workspace opening, and repo-local change +creation. However, fixed "Next for your agent" guidance assumes a linear +workflow path and may not fit dynamic agentic work, where the agent should +inspect current state and choose the next move. + +Implications: + +- Do not implement hardcoded next-step blocks yet. +- Preserve Item 13 as research context for a future receipt or affordance model. +- Prefer future output that reports what exists, where it lives, and what + actions are available, rather than prescribing one next command. +- Deterministic receipt improvements such as direct `created_paths` fields may + be split into a smaller implementation slice if they remain clearly useful. +- Delivery terminology concerns may be handled separately from handoff output. diff --git a/openspec/initiatives/context-store-and-initiatives/direction-git-native-work.md b/openspec/initiatives/context-store-and-initiatives/direction-git-native-work.md new file mode 100644 index 000000000..a24b346d0 --- /dev/null +++ b/openspec/initiatives/context-store-and-initiatives/direction-git-native-work.md @@ -0,0 +1,472 @@ +# Git-Native Specs And Work Direction + +This note captures the current product direction after the initiative, +workspace, context-store, and multi-repo planning discussion. + +The positive shape is: + +```text +OpenSpec is a Git-native artifact format for specs and work. + +Specs are what is true. +Work is what is in motion. +``` + +OpenSpec artifacts live as files in Git. That Git repo may be the code repo, a +planning repo, or a contracts repo. OpenSpec should not introduce a separate +authoritative state system outside those files. + +## Core Shape + +The preferred future shape is: + +```text +openspec/ + README.md + openspec.yml + specs/ + work/ +``` + +- `specs/` describes accepted behavior. +- `work/` describes intended effort in motion. + +This shape should be the same whether the OpenSpec root lives beside code or in +a dedicated planning or contracts repo. + +```text +app-repo/ + openspec/ + specs/ + work/ + +planning-repo/ + openspec/ + specs/ + work/ +``` + +There is no separate product mode for "repo-local", "external", "workspace", +"context store", or "multi-repo" artifacts. The placement choice is simply +which Git repo contains the OpenSpec files. + +## Vocabulary + +Use a small vocabulary first: + +```text +Spec current accepted behavior +Work intended effort in motion +Change work that applies concrete deltas to targets +Initiative work that coordinates or decomposes other work +Target repo, service, package, path, or system where work lands +``` + +Users should not need to learn `context store`, `project`, `workspace`, +`artifact home`, or `index` as primary product nouns. + +## Domain Terms + +Use these terms when explaining the near-term product: + +```text +OpenSpec root + The `openspec/` directory that contains specs, changes, work, and config. + +In-project OpenSpec + OpenSpec initialized inside the project repo it helps describe. + +Standalone OpenSpec repo + A separate Git repo whose main purpose is to hold OpenSpec artifacts. + +Target project repo + A code repo that a change or work item applies to. + +Local repo map + Private local resolution from a target repo id to a checkout path. + +Workspace view + Legacy or beta local-view language. In the new direction, this should reduce + to a local repo map plus an optional focused OpenSpec root or work item. +``` + +Examples: + +```text +In-project OpenSpec: + +app-repo/ + openspec/ + specs/ + changes/ + +Standalone OpenSpec repo: + +app-openspec-repo/ + openspec/ + specs/ + changes/ + +Target project repo: + +app-repo/ + src/ + tests/ +``` + +The product should avoid the term `repo-local` for this distinction. It is too +easy to confuse "OpenSpec lives in this project repo" with "this work targets +this repo." + +The product should also avoid making `workspace` a primary user-facing noun. +The job that remains is simpler: map target repo ids to local checkout paths so +agents and commands can assemble the relevant Git repos on this machine. + +## Work Is The Primitive + +`work/` is one canonical area for units of work at different scales. + +```text +openspec/ + specs/ + auth/session-limits.md + work/ + add-login-rate-limit/ + work.yaml + proposal.md + tasks.md + deltas/ + checkout-modernization/ + work.yaml + README.md +``` + +A change is work with change capabilities: + +```yaml +id: add-login-rate-limit +kind: change +status: proposed +targets: + - repo: app +``` + +An initiative is also work: + +```yaml +id: checkout-modernization +kind: initiative +status: active +children: + - work: add-login-rate-limit + - work: add-checkout-tax +``` + +The distinction between a change and an initiative should not come from which +top-level folder the artifact lives in. It should come from metadata and +capabilities: + +- Work with targets and deltas can validate and archive those deltas into + `specs/`. +- Work with children, dependencies, and context can coordinate and roll up other + work. +- Some work may be both change-shaped and coordination-shaped. + +## Git Is The Source Of Truth + +OpenSpec should stay Git-native: + +- History comes from Git. +- Review uses normal Git and forge workflows. +- Diffs are normal file diffs. +- External planning means another Git repo, not another state system. +- Indexes, dashboards, status rollups, and orchestration are derived views. + +Forge-specific status such as pull request state, CI, review approvals, or +merge status may be read by adapters. That status should not become a competing +OpenSpec truth. + +## Targets + +Filesystem location should not imply implementation target. Work declares where +it lands. + +```yaml +targets: + - repo: api + - repo: web +``` + +Targets may later address repos, services, packages, paths, external systems, +or monorepo subtrees. Use plural `targets` in the format early, even if some MVP +lifecycle commands only support one target. + +## Nesting And References + +The rule is: + +```text +Nest within a repo. +Reference across repos. +``` + +Within one Git repo, work can nest when that is the real relationship: + +```text +app-repo/ + openspec/ + work/ + checkout-modernization/ + work.yaml + work/ + add-login-rate-limit/ +``` + +Across Git repo boundaries, work references other work by stable identity: + +```yaml +id: checkout-modernization +kind: initiative +children: + - repo: api + work: add-tax-api + - repo: web + work: update-checkout-ui +``` + +This keeps each repo's executable work close to the code it affects while still +allowing a planning or contracts repo to coordinate the larger effort. + +Work identity must come from metadata, not from the path. Folder paths can help +humans browse; they should not be the durable identity of the work. + +## Dependency And Sequencing + +Multi-repo complexity is mostly about sequencing, not folder placement. + +OpenSpec should be able to record dependency intent in Git: + +```yaml +depends_on: + - work: publish-tax-contract +``` + +Future views can answer: + +- How does this large effort decompose? +- What has to happen first? +- Which targets are affected? +- Which teams own the slices? +- What surrounding context does an agent need? + +The free artifact format should be able to describe ordering and dependencies. +Automation that enforces sequencing, gates merges, or rolls up live forge status +can remain a derived orchestration layer. + +## MVP Implication + +The immediate release path should keep the current OpenSpec baseline working: + +```text +openspec/ + README.md + openspec.yml + specs/ + changes/ +``` + +The first mental model is: + +```text +Specs = what is true. +Changes = what should change. +``` + +Near-term work should not require the future `work/` layout. `change` remains +important because a change applies deltas. The `work/` model is the future +layout direction, not a prerequisite for making standalone OpenSpec repos +useful. + +## Roadmap + +### 1. Preserve The Current Baseline + +Keep the existing in-project OpenSpec flow working and understandable: + +```text +app-repo/ + openspec/ + specs/ + changes/ +``` + +The first release goal is not to rename everything. It is to make the current +model boring and reliable. + +### 2. Make The Placement Choice Explicit + +Teach the product language: + +```text +OpenSpec can live inside your project repo, +or in its own Git repo. +``` + +Use: + +- `in-project OpenSpec` for `app-repo/openspec/` +- `standalone OpenSpec repo` for `app-openspec-repo/openspec/` + +Avoid `repo-local` as the user-facing term for this split. + +### 3. Support Standalone OpenSpec Repos + +Allow OpenSpec to be initialized and validated in a Git repo that does not hold +application code: + +```text +app-openspec-repo/ + openspec/ + specs/ + changes/ +``` + +This should use the same parser, templates, validation, and archive concepts as +in-project OpenSpec. A standalone repo is not a new state system. + +### 4. Add Target Project Repo Resolution + +Standalone OpenSpec repos need to describe where changes land: + +```yaml +targets: + - repo: app +``` + +The first slice can keep target resolution simple: + +- register local target repos +- validate that referenced targets exist +- report unresolved targets clearly +- let agents know which OpenSpec repo and target repos are involved + +Do not clone, branch, sync, orchestrate, or infer complex repo state yet. + +This is the simplified successor to the larger workspace-view concept. Existing +workspace beta behavior may remain as compatibility, but new direction should +use local repo mapping as the product shape. + +### 5. Add Cross-Repo Context And Doctoring + +Once standalone OpenSpec repos can target project repos, add read-oriented +support for relevant context: + +- doctor checks for missing target repo mappings +- local path mapping for agents +- read-only references to other OpenSpec repos when needed +- clear output showing which Git repo owns each artifact + +Remote Git URL support, pull/push helpers, status dashboards, and sequencing +enforcement can come later. + +### 6. Evolve Toward `work/` + +After the baseline and standalone repo flow are solid, introduce the future +layout direction: + +```text +openspec/ + specs/ + work/ +``` + +At that point: + +- existing `changes/` can be supported as legacy or migrated +- changes become change-shaped work +- initiatives become coordination-shaped work +- dependency and sequencing views can build on stable work identity + +Do not make `/work` block the standalone OpenSpec repo release. + +## Decisions Considered + +### Separate `changes/` And `initiatives/` + +Rejected as the preferred future shape: + +```text +openspec/ + changes/ + initiatives/ +``` + +This uses folders as the type system and makes changes and initiatives feel +artificially unrelated. The cleaner model is one `work/` tree where change and +initiative are shapes of work. + +### Initiative-Owned Change Folders + +Rejected as canonical storage: + +```text +openspec/ + initiatives/ + checkout-modernization/ + changes/ + add-tax-api/ +``` + +This makes initiative ownership look like lifecycle ownership. A larger unit of +work may coordinate a smaller one, but the smaller unit still has its own +identity, targets, deltas, and lifecycle. + +### Project Or Repo Buckets As Lifecycle Roots + +Rejected as the default: + +```text +projects/ + api/ + openspec/ + changes/ + web/ + openspec/ + changes/ +``` + +Repo buckets work when each artifact cleanly belongs to one repo, but they get +awkward for cross-repo work, shared contracts, monorepos, and initiatives that +span several targets. Repos should be targets, not mandatory lifecycle roots. + +### Stateful Context Store As Core Primitive + +Rejected as the core framing. + +A dedicated planning or contracts repo may hold OpenSpec artifacts, but it is +still a Git repo. OpenSpec should not create a separate authoritative store that +can disagree with Git. + +### Configurable Layout Modes + +Rejected as an MVP product shape. + +Custom layout modes force every tool, doc, and agent instruction to branch. +Prefer one opinionated layout and let users choose which Git repo contains it. + +### Workspace As A Primary Product Object + +Rejected as the new user-facing shape. + +The useful part of workspace-view behavior is local resolution: knowing where +the OpenSpec repo and target project repos are checked out on this machine. That +should be treated as a local repo map, not as a planning container, lifecycle +owner, or durable source of truth. + +## Supersession Note + +This direction supersedes the older product boundary that centered context +stores, collections, initiatives, workspaces, and repo-local changes as separate +primary nouns. Those artifacts remain useful historical context and describe +implemented beta behavior, but new product direction should start from the +Git-native `specs/` and `work/` shape. diff --git a/openspec/initiatives/context-store-and-initiatives/direction.md b/openspec/initiatives/context-store-and-initiatives/direction.md index ba863a7e8..c7bf117d0 100644 --- a/openspec/initiatives/context-store-and-initiatives/direction.md +++ b/openspec/initiatives/context-store-and-initiatives/direction.md @@ -1,11 +1,22 @@ # Context Store And Initiatives Direction -This document captures the suggested direction from the workspace/initiative -discussion. The main shift is that "workspace" should not be the durable shared -planning object. The durable shared object is a synced context store, and -initiatives are one opinionated collection inside it. +Status: historical beta direction. -## Core Model +This document preserves the earlier context-store and workspace direction from +the workspace/initiative discussion. It is useful transition evidence, but it +is not the current product authority for the simplification work. + +For current direction, start with: + +1. `openspec/work/simplify-context-and-workspace-model/goal.md` +2. `openspec/work/simplify-context-and-workspace-model/roadmap.md` + +The main historical shift captured here was that "workspace" should not be the +durable shared planning object. In this earlier model, the durable shared +object was a synced context store, and initiatives were one opinionated +collection inside it. + +## Historical Core Model ```text Context Store @@ -34,9 +45,9 @@ Workspaces open local views. Changes implement repo-owned slices. ``` -## Locked Product Boundary +## Historical Locked Product Boundary -The workspace-to-initiative pivot is now the product boundary for future +The workspace-to-initiative pivot was the product boundary for this beta coordination work: - A workspace is a regenerable, machine-local working view. It maps context diff --git a/openspec/initiatives/context-store-and-initiatives/roadmap.md b/openspec/initiatives/context-store-and-initiatives/roadmap.md index 1ac93f8d5..1f8d345ce 100644 --- a/openspec/initiatives/context-store-and-initiatives/roadmap.md +++ b/openspec/initiatives/context-store-and-initiatives/roadmap.md @@ -1,8 +1,17 @@ # Context Store And Initiatives Roadmap -This roadmap turns the direction in `direction.md` into shippable chunks. +Status: historical beta roadmap snapshot. -The product decision underneath every step is: +This roadmap preserves the implementation queue that existed while the +context-store and workspace model was being explored. It is not the active +roadmap for current simplification work. + +For current direction, start with: + +1. `openspec/work/simplify-context-and-workspace-model/goal.md` +2. `openspec/work/simplify-context-and-workspace-model/roadmap.md` + +The historical product decision underneath this roadmap was: ```text Context stores sync truth. @@ -12,17 +21,18 @@ Workspaces open local views. Changes implement repo-owned slices. ``` -## Current Beta Priority +## Historical Beta Priority Snapshot -The manual beta pass should pull first-run friction forward. Work in this order -before investing in deeper schema or lifecycle machinery: +At the time, the manual beta pass pulled first-run friction forward. This was +the historical working order before investing in deeper schema or lifecycle +machinery: 1. Finish the manual beta reality pass enough to keep the next slices grounded. 2. Item 12, context-store first-run and cleanup UX: interactive no-argument setup, target-path safety, and a supported unregister/remove path. -3. Item 13, agent handoff output and delivery polish: "Next for your agent" blocks, - direct JSON paths, and baseline OpenSpec guidance even when workflow - entrypoints are commands-oriented. +3. Skip Item 13 as an implementation item for now. Preserve the handoff + findings, but avoid hardcoding linear "next step" guidance until the agent + handoff model is clearer. 4. Item 14, workspaces beta guide split: make user docs match the interactive setup path and keep exact flags in the agent playbook. 5. Item 15, context store project roots and schema-led initiatives: sparse initiative @@ -72,10 +82,13 @@ Locked disposition: - Defer branch/worktree orchestration, strong cross-repo validation, dependency graph enforcement, and shared contract governance. -Fresh-agent rule: +Fresh-agent historical reading rule: -- Start from `openspec/initiatives/context-store-and-initiatives/direction.md` - for product authority. +- Start from `openspec/work/simplify-context-and-workspace-model/goal.md` and + `openspec/work/simplify-context-and-workspace-model/roadmap.md` for current + product authority. +- Use `openspec/initiatives/context-store-and-initiatives/direction.md` as + historical beta direction, not as the current product authority. - Treat `openspec/changes/workspace-reimplementation-roadmap/HISTORICAL_DIRECTION.md` and `openspec/changes/workspace-reimplementation-roadmap/` as historical reference material for preserved local-view behavior and POC lessons. @@ -492,27 +505,29 @@ Done when: ## 13. Agent Handoff Output And Delivery Polish -Goal: make existing command output and delivery choices enough for a fresh -agent to continue safely, before adding any broader `initiative next` command. +Status: deferred as an implementation item. + +Goal, if revisited: define an agent handoff receipt model that reports what +exists, where it lives, and which affordances are available without prescribing +one linear next step. Work item: `work-items/13-agent-handoff-output-and-delivery-polish/` Ship: -- "Next for your agent" handoff guidance in the command outputs where first-run - flow otherwise depends on pasted beta knowledge. -- JSON output with direct created artifact paths where agents need to write - files, while preserving existing relative fields for compatibility. -- Clear delivery wording that separates baseline OpenSpec guidance from - workflow entrypoints such as skills or slash commands. -- Warnings when a selected tool cannot receive workflow slash commands. +Do not ship fixed "Next for your agent" guidance yet. The current shape assumes +that users and agents move through the beta flow linearly, but real agentic +workflows may inspect, branch, skip steps, or start from existing context. -Done when: +Preserve for future exploration: -- A coding agent can continue from setup or initiative creation output without - guessing command names, reconstructing writable paths, or losing baseline - OpenSpec guidance because the user chose commands-oriented delivery. +- Whether command output should include context receipts, available affordances, + or nothing beyond deterministic paths. +- Whether direct path fields like `created_paths` are a small standalone receipt + improvement rather than part of a broader handoff model. +- How delivery wording should distinguish baseline OpenSpec guidance from + workflow entrypoints without coupling it to this handoff item. ## 14. Workspaces Beta Guide Split @@ -747,7 +762,8 @@ These are important, but should wait until the initiative model has real usage: 10. Let workspaces open initiatives. 11. Manual beta reality pass. 12. Context store first-run and cleanup UX. -13. Agent handoff output and delivery polish. +13. Skip agent handoff output and delivery polish until the handoff model is + clearer. 14. Workspaces beta guide split. 15. Context store project roots and schema-led initiatives. 16. Add local-to-initiative escalation UX. @@ -755,5 +771,5 @@ These are important, but should wait until the initiative model has real usage: 18. Explore initiative-hosted target-bound change artifacts. 19. Review workspace beta compatibility before public release. -Pending discussion: optionally add initiative next / agent handoff UX before or -alongside the handoff polish work. +Pending discussion: revisit handoff receipts after the beta guide and sparse +initiative model clarify what context agents actually need. diff --git a/openspec/initiatives/context-store-and-initiatives/tasks.md b/openspec/initiatives/context-store-and-initiatives/tasks.md index f49c4068d..1acbd6f60 100644 --- a/openspec/initiatives/context-store-and-initiatives/tasks.md +++ b/openspec/initiatives/context-store-and-initiatives/tasks.md @@ -1,18 +1,30 @@ # Context Store And Initiatives Tasks -This tracks roadmap execution for the initiative. Roadmap items live in -`roadmap.md`; detailed working notes live under `work-items/`. +Status: historical beta progress snapshot. -## Current Beta Priority +This file preserves the task state from the old context-store and workspace +initiative. It is not the active implementation queue for current +simplification work. -After the manual beta pass, prioritize the things a fresh user hits while +For current direction, start with: + +1. `openspec/work/simplify-context-and-workspace-model/goal.md` +2. `openspec/work/simplify-context-and-workspace-model/roadmap.md` + +Historical roadmap items live in `roadmap.md`; detailed working notes live +under `work-items/`. + +## Historical Beta Priority Snapshot + +At the time, the manual beta pass prioritized the things a fresh user hit while getting started before deeper model work: 1. Finish Item 11 observations enough to keep implementation grounded. 2. Item 12: no-argument context-store setup, path safety, and cleanup. -3. Item 13: "Next for your agent" output, direct JSON paths, - and baseline guidance/delivery polish. +3. Skip Item 13 as an implementation item for now. Preserve the findings, but + do not hardcode linear "next step" guidance until the agent handoff shape is + better understood. 4. Item 14: update the beta guide so it matches the improved first-run flow. 5. Item 15: context-store project roots and sparse schema-led initiatives. @@ -199,14 +211,15 @@ Work item: `work-items/12-context-store-first-run-and-cleanup-ux/` Work item: `work-items/13-agent-handoff-output-and-delivery-polish/` -- [ ] Decide which commands should print "Next for your agent" handoff guidance. -- [ ] Add direct created-path JSON fields where agents currently have to - reconstruct artifact paths. -- [ ] Clarify commands-oriented delivery so workflow slash commands are separate - from baseline OpenSpec guidance. -- [ ] Warn when a selected tool cannot receive workflow slash commands. -- [ ] Update docs, generated agent guidance, and tests for the polished handoff - and delivery output. +Status: deferred. Do not implement fixed "Next for your agent" output from this +item yet. + +- [ ] Revisit the handoff model after Item 14/15 clarify the beta guide and + sparse initiative flow. +- [ ] If needed, split deterministic receipt improvements such as direct + `created_paths` into a smaller future implementation slice. +- [ ] Avoid prescribing one linear workflow path; future handoff output should + report state, paths, and possible affordances that agents can compose. ## 14. Workspaces Beta Guide Split diff --git a/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/evidence.md b/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/evidence.md index f71d8dcc0..40b880851 100644 --- a/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/evidence.md +++ b/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/evidence.md @@ -23,3 +23,19 @@ Treat this as output polish, not a new workflow engine: - keep baseline OpenSpec literacy separate from workflow entrypoints; - leave the broader "what should I do next?" command to the proposed handoff work item. + +## Reassessment + +After reviewing practical examples, the proposed "Next for your agent" shape +looked too prescriptive. It assumes a fixed linear beta path, but agents may +inspect state, branch, skip setup, continue an existing change, or use context in +a different order. + +Conclusion on 2026-05-30: + +- Skip Item 13 as an implementation item for now. +- Preserve the evidence because the handoff pain is real. +- Do not hardcode fixed next-step guidance until the product has a clearer + receipt or affordance model. +- Consider splitting deterministic `created_paths` style receipt fields into a + smaller future slice if they remain useful. diff --git a/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/plan.md b/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/plan.md index 4aa24a8be..af578e2de 100644 --- a/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/plan.md +++ b/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/plan.md @@ -2,11 +2,14 @@ ## Status -Proposed from the manual beta reality pass. +Deferred as an implementation item. -This work item captures the remaining agent-handoff and delivery-output gaps -that are smaller than the broader `initiative next` discussion but still matter -for the beta flow. +This work item captures a real beta pain, but the current "Next for your agent" +shape should not be built yet. It assumes a linear workflow path and risks +hardcoding guidance that does not fit dynamic agentic work. + +Decision on 2026-05-30: skip this item for now. Keep the notes as research +input for a future handoff receipt model. ## Source Of Truth @@ -22,27 +25,31 @@ Related work: - `../14-workspaces-beta-guide-split/` - `../15-context-store-project-roots-and-schema-led-initiatives/` -## Why This Exists +## Why This Was Proposed The beta pass showed that agents can succeed if they know which command to run, but the first handoff is still too implicit. Setup output, JSON receipts, docs, and generated delivery artifacts should make the next move obvious without requiring the user to paste tribal knowledge. -This work item is deliberately narrower than an `initiative next` command. It -polishes existing command outputs and delivery semantics so a fresh agent can -continue safely. +That pain is still valid. The uncertain part is the product shape. A fixed next +step may be wrong when the agent can inspect current state, discover existing +initiatives, skip workspace setup, continue from a repo-local change, or choose +a different planning route. + +## Future Direction -## Goals +If this is revisited, frame it as a receipt or affordance model: -- Make setup and initiative creation output point to the next useful agent - action. -- Ensure agent-readable JSON returns paths that can be used directly without - path reconstruction when practical. -- Clarify commands-oriented delivery so "workflow commands" does not mean "the - agent receives no OpenSpec guidance." -- Warn clearly when the selected tool cannot receive workflow slash commands. -- Keep baseline OpenSpec literacy separate from workflow entrypoints. +- report what now exists; +- report where canonical context and created artifacts live; +- report relevant state and selected local bindings; +- optionally report available actions, not a required next command; +- avoid a single `next_command` unless the next action is genuinely + deterministic. + +Small deterministic output improvements, such as absolute `created_paths`, may +still be worth splitting into a narrower implementation slice. ## Non-Goals @@ -52,47 +59,48 @@ continue safely. setup output. - Do not make every relative path field disappear if existing compatibility requires it; add direct absolute path fields instead. +- Do not hardcode a single user or agent journey. -## Output Direction +## Deferred Output Sketch -Commands that create or prepare OpenSpec shared context should include a small -handoff block in human output: +Avoid this prescriptive shape for now: ```text Next for your agent: Ask your coding agent to create or update an initiative in team-context. ``` -JSON output should prefer both stable relative names and direct absolute paths -where agents need to write files: +If a future model exists, prefer contextual receipts: ```json { - "created_files": ["initiative.yaml", "brief.md"], + "created_files": ["brief.md"], "created_paths": [ - "/path/to/store/initiatives/billing-launch/initiative.yaml", "/path/to/store/initiatives/billing-launch/brief.md" ], - "next_commands": {} + "handoff_context": { + "store": "team-context", + "initiative": "billing-launch", + "workspace": null + }, + "available_actions": [ + "inspect_initiative", + "open_workspace_view", + "create_repo_local_change" + ] } ``` -Delivery copy should distinguish: +Delivery copy may still need separate work to distinguish: - baseline OpenSpec guidance or literacy; - workflow entrypoints such as skills or slash commands. -If a user selects commands-oriented delivery for a tool that has no command -adapter, output should warn that workflow slash commands are unavailable while -still installing or recommending baseline guidance when the tool supports it. - -## Done When +## Revisit When -- A fresh agent can continue after context-store setup or initiative creation - using command output and docs, without guessing paths or beta command names. -- JSON receipts expose direct paths for created initiative artifacts or explain - why only relative names are available. -- Commands-oriented delivery output clearly reports what guidance and workflow - entrypoints were installed, skipped, or unavailable. -- The broader `initiative next` proposal can build on these outputs instead of - solving first-run handoff from scratch. +- Item 14 clarifies the human guide versus agent playbook split. +- Item 15 clarifies sparse initiative artifacts and context-store project-root + behavior. +- There is enough beta evidence to decide whether command output should expose + state receipts, available affordances, direct paths only, or no special + handoff block. diff --git a/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/tasks.md b/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/tasks.md index d9049ccd0..243af6d17 100644 --- a/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/tasks.md +++ b/openspec/initiatives/context-store-and-initiatives/work-items/13-agent-handoff-output-and-delivery-polish/tasks.md @@ -1,5 +1,18 @@ # Agent Handoff Output And Delivery Polish Tasks +Status: deferred. Do not implement these tasks until the handoff model is +redesigned as contextual receipts or affordances rather than fixed linear +"next step" guidance. + +- [ ] Revisit after Item 14 and Item 15 clarify the beta docs, sparse + initiative flow, and context-store project-root model. +- [ ] Decide whether any deterministic receipt improvements, such as + `created_paths`, should be split into a smaller independent slice. +- [ ] Decide whether delivery terminology belongs in a separate + command-surface/delivery item. + +## Deferred Original Tasks + - [ ] Decide which existing commands should print a "Next for your agent" handoff block. - [ ] Define the minimal handoff content for context-store setup, initiative diff --git a/openspec/specs/artifact-graph/spec.md b/openspec/specs/artifact-graph/spec.md index fb9627ca3..6ffe393d6 100644 --- a/openspec/specs/artifact-graph/spec.md +++ b/openspec/specs/artifact-graph/spec.md @@ -128,38 +128,3 @@ The system SHALL support self-contained schema directories with co-located templ - **WHEN** listing schemas - **THEN** the system returns schema names from both user and package directories -### Requirement: Workspace planning schema -The artifact graph SHALL provide a built-in workspace planning schema for workspace-scoped changes. - -#### Scenario: Built-in workspace planning schema is available -- **WHEN** schemas are resolved from package built-ins -- **THEN** a schema named `workspace-planning` SHALL be available -- **AND** it SHALL describe the artifact structure for workspace-scoped planning - -#### Scenario: Workspace planning schema artifacts -- **WHEN** the `workspace-planning` schema is loaded -- **THEN** it SHALL include the normal planning artifacts for a shared proposal, workspace-scoped specs, cross-area design, and coordination tasks -- **AND** it SHALL not require an additional area manifest outside those normal planning artifacts - -#### Scenario: Workspace planning schema supports nested specs -- **WHEN** the `workspace-planning` schema defines its specs artifact -- **THEN** the specs artifact SHALL resolve workspace-scoped spec files under `specs/**/*.md` -- **AND** schema guidance SHALL describe `specs/<area-or-repo>/<capability>/spec.md` as the default convention for area-specific requirements - -#### Scenario: Workspace planning schema templates -- **WHEN** artifact instructions are requested for the `workspace-planning` schema -- **THEN** the schema SHALL provide templates that guide agents to write workspace-level planning content -- **AND** those templates SHALL avoid instructing agents to create repo-local implementation artifacts -- **AND** specs instructions SHALL support organizing area-specific requirements under workspace-scoped `specs/` paths - -#### Scenario: Workspace nested spec paths stay workspace-scoped -- **GIVEN** a workspace change has spec files under `specs/<area-or-repo>/<capability>/spec.md` -- **WHEN** OpenSpec reports status or artifact instructions for the workspace change -- **THEN** it SHALL preserve the concrete nested workspace spec paths -- **AND** it SHALL not treat those files as repo-local specs to sync or archive without an explicit affected-area implementation context - -#### Scenario: Workspace planning apply readiness -- **WHEN** the `workspace-planning` schema defines apply readiness -- **THEN** it SHALL require coordination tasks before implementation begins -- **AND** the apply guidance SHALL direct agents to select an affected area before making implementation edits - diff --git a/openspec/specs/change-creation/spec.md b/openspec/specs/change-creation/spec.md index 1e2cb1a9d..3e85719f5 100644 --- a/openspec/specs/change-creation/spec.md +++ b/openspec/specs/change-creation/spec.md @@ -65,44 +65,3 @@ The system SHALL validate change names follow kebab-case conventions. - **WHEN** a change name like `add--auth` is validated - **THEN** validation returns `{ valid: false, error: "..." }` -### Requirement: Workspace-aware change creation -Change creation SHALL support both repo-local and workspace planning homes. - -#### Scenario: Creating a change from a workspace root -- **GIVEN** the command runs from an OpenSpec workspace root -- **WHEN** the user creates a new change -- **THEN** OpenSpec SHALL create the change under the workspace planning path -- **AND** it SHALL not create the change under a linked repo's `openspec/changes/` directory -- **AND** it SHALL use the `workspace-planning` schema when no explicit schema is provided - -#### Scenario: Creating a change from inside a workspace -- **GIVEN** the command runs from a subdirectory of an OpenSpec workspace planning home -- **WHEN** the user creates a new change -- **THEN** OpenSpec SHALL resolve the current workspace as the planning home -- **AND** it SHALL create the change under that workspace's planning path -- **AND** it SHALL use the `workspace-planning` schema when no explicit schema is provided - -#### Scenario: Creating a change from inside a linked repo -- **GIVEN** a repo or folder is registered as a workspace link -- **AND** the command runs from inside that linked repo or folder rather than from the workspace planning home -- **WHEN** the user creates a new change without explicitly selecting a workspace -- **THEN** OpenSpec SHALL preserve repo-local change creation behavior for that location -- **AND** it SHALL not create a workspace-scoped change merely because the location is registered as a workspace link - -#### Scenario: Preserving repo-local change creation -- **GIVEN** the command runs outside an OpenSpec workspace -- **WHEN** the user creates a new change in a repo-local OpenSpec project -- **THEN** OpenSpec SHALL continue to create the change under `openspec/changes/` - -#### Scenario: Rejecting invalid workspace affected areas -- **GIVEN** a workspace change creation request includes affected area names -- **WHEN** one or more names are not registered workspace links -- **THEN** OpenSpec SHALL reject those invalid affected areas -- **AND** it SHALL list the valid workspace link names - -#### Scenario: Creating without affected areas -- **GIVEN** the user is still exploring scope -- **WHEN** the user creates a workspace change without affected areas -- **THEN** OpenSpec SHALL create the workspace change -- **AND** it SHALL allow affected areas to be identified later - diff --git a/openspec/specs/cli-artifact-workflow/spec.md b/openspec/specs/cli-artifact-workflow/spec.md index 60e43295d..c76ba7bed 100644 --- a/openspec/specs/cli-artifact-workflow/spec.md +++ b/openspec/specs/cli-artifact-workflow/spec.md @@ -135,29 +135,6 @@ The system SHALL create new change directories with validation. - **WHEN** user runs `openspec new change add-feature --description "Add new feature"` - **THEN** the system creates the change directory with description in README.md -### Requirement: Workspace Setup Commands -The CLI artifact workflow SHALL expose workspace setup commands before change creation. - -#### Scenario: Preparing workspace planning before a change -- **WHEN** a user needs to prepare workspace planning across repos or folders -- **THEN** the CLI SHALL provide commands to set up, list, link, relink, and doctor workspaces -- **AND** those commands SHALL not require an active workspace change - -#### Scenario: Listing workspaces with a short command -- **WHEN** a user wants a concise workspace list command -- **THEN** the CLI SHALL support `openspec workspace ls` -- **AND** it SHALL behave the same as `openspec workspace list` - -#### Scenario: Keeping setup separate from agent launch -- **WHEN** a user completes workspace setup -- **THEN** the setup workflow SHALL leave agent launch and workspace open behavior to a later workflow -- **AND** setup SHALL not require a preferred agent choice - -#### Scenario: Avoiding public direct creation -- **WHEN** users create a workspace in the first workspace setup flow -- **THEN** the CLI SHALL use `openspec workspace setup` -- **AND** it SHALL not expose `openspec workspace create` as the public creation path - ### Requirement: Schema Selection The system SHALL support custom schema selection for workflow commands. @@ -299,24 +276,7 @@ The setup command SHALL display clear output about what was generated. - **THEN** output includes message: "Command generation skipped - no adapter for <tool>" ### Requirement: Status JSON provides planning context -The status command SHALL provide machine-readable planning context for repo-local and workspace changes. - -#### Scenario: Reporting planning home -- **WHEN** a user runs `openspec status --change <id> --json` -- **THEN** the output SHALL identify whether the change is repo-local or workspace-scoped -- **AND** it SHALL include the planning home root and change root - -#### Scenario: Reporting concrete artifact paths -- **WHEN** a user runs `openspec status --change <id> --json` -- **THEN** the output SHALL include concrete paths for existing artifacts -- **AND** agents SHALL be able to read those paths without assuming `openspec/changes/<id>/` -- **AND** workspace-scoped nested spec paths SHALL be reported without flattening the area or capability path - -#### Scenario: Reporting workspace affected areas -- **GIVEN** the change is workspace-scoped -- **WHEN** a user runs `openspec status --change <id> --json` -- **THEN** the output SHALL include known affected areas -- **AND** it SHALL indicate when affected areas remain unresolved without requiring an additional area manifest artifact +The status command SHALL provide machine-readable planning context for changes. #### Scenario: Reporting next steps - **WHEN** a user runs `openspec status --change <id> --json` @@ -326,16 +286,6 @@ The status command SHALL provide machine-readable planning context for repo-loca ### Requirement: Status JSON action context The status command SHALL expose action context that lets agents act without hardcoded filesystem assumptions. -#### Scenario: Planning action context -- **WHEN** a workspace change is still in planning -- **THEN** status JSON SHALL identify the planning artifacts agents may read or update -- **AND** it SHALL indicate that linked repos and folders are context for exploration - -#### Scenario: Implementation action context -- **WHEN** a workspace change has a selected affected area for implementation -- **THEN** status JSON SHALL include the allowed edit root for that area -- **AND** it SHALL avoid authorizing edits outside that selected area - #### Scenario: Repo-local action context - **GIVEN** the change is repo-local - **WHEN** a user runs `openspec status --change <id> --json` @@ -345,12 +295,6 @@ The status command SHALL expose action context that lets agents act without hard ### Requirement: Instructions use resolved planning paths Artifact and apply instructions SHALL use resolved planning paths rather than hardcoded repo-local change paths. -#### Scenario: Workspace artifact instructions -- **GIVEN** the change is workspace-scoped -- **WHEN** a user runs `openspec instructions <artifact> --change <id> --json` -- **THEN** instruction output SHALL point to the artifact path under the workspace change root -- **AND** it SHALL not instruct the agent to write under a linked repo unless an explicit implementation context allows it - #### Scenario: Repo-local artifact instructions - **GIVEN** the change is repo-local - **WHEN** a user runs `openspec instructions <artifact> --change <id> --json` @@ -369,30 +313,3 @@ Generated workflow skills SHALL use OpenSpec CLI output as the source of truth f - **THEN** it SHALL instruct the agent to run `openspec instructions <artifact> --change <id> --json` - **AND** it SHALL write to the resolved artifact path returned by the command -#### Scenario: Skills avoid hardcoded repo-local paths -- **WHEN** generated workflow skills describe artifact locations -- **THEN** they SHALL avoid hardcoded examples that require changes to live under `openspec/changes/<id>/` -- **AND** any examples SHALL defer to CLI-reported paths for repo-local and workspace-scoped changes - -#### Scenario: Skills guard unsupported workspace workflows -- **GIVEN** a generated workflow skill is selected by the global profile -- **AND** the workflow does not yet have full workspace-scoped behavior in this slice -- **WHEN** the skill is used for a workspace-scoped change -- **THEN** it SHALL tell the agent that the workspace action is not supported yet -- **AND** it SHALL not instruct the agent to fall back to repo-local paths or edit linked repos without an explicit allowed edit root - -### Requirement: Workspace schema instructions -Workflow commands SHALL use the workspace planning schema instructions for workspace-scoped changes that use that schema. - -#### Scenario: Workspace planning artifact order -- **GIVEN** a workspace-scoped change uses schema `workspace-planning` -- **WHEN** a user runs `openspec status --change <id> --json` -- **THEN** the artifact list SHALL reflect the workspace planning schema -- **AND** it SHALL include the normal proposal, specs, design, and tasks artifacts - -#### Scenario: Workspace specs instructions -- **GIVEN** a workspace-scoped change uses schema `workspace-planning` -- **WHEN** a user requests instructions for the specs artifact -- **THEN** instruction output SHALL guide the agent to organize area-specific requirements under workspace-scoped `specs/` paths -- **AND** it SHALL not require all affected areas to be finalized before planning can continue -- **AND** it SHALL not instruct the agent to create repo-local spec files while the change is still in workspace planning diff --git a/openspec/specs/cli-config/spec.md b/openspec/specs/cli-config/spec.md index f3c9a11fa..78dc69143 100644 --- a/openspec/specs/cli-config/spec.md +++ b/openspec/specs/cli-config/spec.md @@ -263,56 +263,3 @@ The config command SHALL reserve the `--scope` flag for future extensibility. - **THEN** display error message: "Project-local config is not yet implemented" - **AND** exit with code 1 -### Requirement: Config profile applies to current workspace -The `openspec config profile` command SHALL remain global while offering an explicit workspace apply path when run from inside an OpenSpec workspace. - -#### Scenario: Config profile run inside a workspace -- **GIVEN** the command runs from inside an OpenSpec workspace -- **WHEN** the user changes profile or delivery settings with interactive `openspec config profile` -- **THEN** OpenSpec SHALL save the global config changes -- **AND** it SHALL prompt: `Apply changes to this workspace now?` - -#### Scenario: User confirms workspace apply -- **GIVEN** `openspec config profile` changed global profile or delivery settings inside a workspace -- **WHEN** the user confirms the workspace apply prompt -- **THEN** OpenSpec SHALL run `openspec workspace update` for the current workspace -- **AND** it SHALL not run repo-local `openspec update` unless the current planning home is repo-local - -#### Scenario: User declines workspace apply -- **GIVEN** `openspec config profile` changed global profile or delivery settings inside a workspace -- **WHEN** the user declines the workspace apply prompt -- **THEN** OpenSpec SHALL explain that global config was updated -- **AND** it SHALL tell the user to run `openspec workspace update` later to apply the profile to workspace-local skills -- **AND** it SHALL not modify workspace skill files - -#### Scenario: No-op inside workspace -- **GIVEN** the command runs from inside an OpenSpec workspace -- **WHEN** `openspec config profile` exits with no effective config changes -- **THEN** OpenSpec SHALL not prompt to apply changes -- **AND** it SHALL warn if workspace-local skills are out of sync with the current global profile -- **AND** the warning SHALL suggest `openspec workspace update` - -#### Scenario: Core preset shortcut inside a workspace -- **GIVEN** the command runs from inside an OpenSpec workspace -- **WHEN** the user runs `openspec config profile core` -- **THEN** OpenSpec SHALL save the global config change without prompting to apply immediately -- **AND** it SHALL tell the user to run `openspec workspace update` to apply the profile to workspace-local skills - -#### Scenario: Core preset shortcut inside a repo project -- **GIVEN** the command runs from inside a repo-local OpenSpec project -- **WHEN** the user runs `openspec config profile core` -- **THEN** OpenSpec SHALL preserve existing repo-local shortcut behavior -- **AND** it SHALL tell the user to run `openspec update` to apply the profile to project files - -#### Scenario: Workspace planning home wins over linked repo project -- **GIVEN** the command runs in a path under a workspace planning home where a repo-local OpenSpec project could also be detected -- **WHEN** OpenSpec decides which apply prompt to show -- **THEN** the nearest current planning home SHALL determine whether to offer `openspec workspace update` or repo-local `openspec update` -- **AND** OpenSpec SHALL not apply profile changes to a linked repo when the current planning home is the workspace - -#### Scenario: Linked repo keeps repo-local profile behavior -- **GIVEN** a repo-local OpenSpec project is registered as a workspace link -- **AND** the command runs from inside that linked repo rather than from the workspace planning home -- **WHEN** OpenSpec decides which apply prompt or guidance to show -- **THEN** OpenSpec SHALL preserve repo-local `openspec update` behavior for that repo -- **AND** it SHALL not offer `openspec workspace update` unless the workspace is explicitly selected diff --git a/openspec/specs/cli-update/spec.md b/openspec/specs/cli-update/spec.md index 34e32c91f..14cffe075 100644 --- a/openspec/specs/cli-update/spec.md +++ b/openspec/specs/cli-update/spec.md @@ -166,64 +166,3 @@ The archive slash command template SHALL support optional change ID arguments fo - **AND** wrap it in a clear structure like `<ChangeId>\n $ARGUMENTS\n</ChangeId>` to indicate the expected argument - **AND** include validation steps in the template body to check if the change ID is valid -### Requirement: Repo update stays separate from workspace planning homes -The repo-local `openspec update` command SHALL not silently treat a workspace planning home as a repo-local OpenSpec project. - -#### Scenario: Running update from a workspace root -- **GIVEN** the command runs from an OpenSpec workspace root -- **WHEN** the user runs `openspec update` -- **THEN** OpenSpec SHALL not generate repo-local project files in the workspace root -- **AND** it SHALL tell the user to run `openspec workspace update` - -#### Scenario: Running update from inside a workspace planning directory -- **GIVEN** the command runs from a subdirectory of an OpenSpec workspace planning home -- **WHEN** the user runs `openspec update` -- **THEN** OpenSpec SHALL not run repo-local update behavior -- **AND** it SHALL tell the user to run `openspec workspace update` - -#### Scenario: Running update from a repo-local project -- **GIVEN** the command runs from inside a repo-local OpenSpec project -- **WHEN** the user runs `openspec update` -- **THEN** OpenSpec SHALL preserve existing repo-local update behavior - -#### Scenario: Updating a repo-local project nested below a workspace folder -- **GIVEN** the target path contains repo-local OpenSpec state -- **AND** an ancestor is an OpenSpec workspace root -- **WHEN** the user runs `openspec update <path>` -- **THEN** OpenSpec SHALL preserve repo-local update behavior for the target path -- **AND** it SHALL not run workspace update behavior - -## Edge Cases - -### Error Handling - -The command SHALL handle edge cases gracefully. - -#### Scenario: File permission errors - -- **WHEN** file write fails -- **THEN** let the error bubble up naturally with file path - -#### Scenario: Missing AI tool files - -- **WHEN** an AI tool configuration file doesn't exist -- **THEN** skip updating that file -- **AND** do not create it - -#### Scenario: Custom directory names - -- **WHEN** considering custom directory names -- **THEN** not supported in this change -- **AND** the default directory name `openspec` SHALL be used - -## Success Criteria - -Users SHALL be able to: -- Update OpenSpec instructions with a single command -- Get the latest AI agent instructions -- See clear confirmation of the update - -The update process SHALL be: -- Simple and fast (no version checking) -- Predictable (same result every time) -- Self-contained (no network required) diff --git a/openspec/specs/openspec-conventions/spec.md b/openspec/specs/openspec-conventions/spec.md index f1360cf2e..e872a3463 100644 --- a/openspec/specs/openspec-conventions/spec.md +++ b/openspec/specs/openspec-conventions/spec.md @@ -245,312 +245,3 @@ OpenSpec CLI design SHALL use verbs as top-level commands with nouns provided as - **THEN** `openspec show` and `openspec validate` SHALL accept `--type spec|change` - **AND** the help text SHALL document this clearly -### Requirement: Workspace Product Language -OpenSpec conventions SHALL describe coordination workspaces in user-facing product terms. - -#### Scenario: Describing workspace structure -- **WHEN** OpenSpec documentation describes workspace support -- **THEN** it SHALL present a workspace as the planning home for work across linked repos or folders -- **AND** it SHALL describe `changes/` as the workspace planning area - -#### Scenario: Avoiding internal workspace vocabulary -- **WHEN** OpenSpec documentation explains what a workspace includes -- **THEN** it SHALL prefer plain product language such as "repos or folders" -- **AND** it SHALL avoid user-facing reliance on terms such as "working set", "code area", "entry", "alias", or "local overlay" - -#### Scenario: Distinguishing workspaces from changes -- **WHEN** OpenSpec documentation explains workspace planning -- **THEN** it SHALL describe a workspace as a durable planning home -- **AND** it SHALL describe individual features, fixes, and projects as changes inside the workspace - -#### Scenario: Distinguishing workspace and repo-local surfaces -- **WHEN** OpenSpec documentation compares workspace and repo-local flows -- **THEN** it SHALL explain that workspace planning lives in the workspace folder -- **AND** it SHALL explain that repo-local specs and changes continue to live under each repo's `openspec/` directory - -#### Scenario: Sequencing the workspace roadmap -- **WHEN** workspace reimplementation work is split across multiple active changes -- **THEN** conventions SHALL allow those changes to remain flat siblings under `openspec/changes/` -- **AND** dependency order MAY be documented in proposal prose until formal change stacking metadata is available - -### Requirement: Workspace planning vocabulary -OpenSpec conventions SHALL distinguish workspace planning concepts using user-facing product language. - -#### Scenario: Naming affected areas -- **WHEN** documentation or generated guidance refers to repos, folders, packages, services, apps, or docs sites touched by a workspace change -- **THEN** it SHALL call them affected areas -- **AND** it SHALL avoid using "target repo" or "repo slice" as the primary user-facing term - -#### Scenario: Naming delivery slices -- **WHEN** documentation or generated guidance refers to delivery increments inside a larger change -- **THEN** it SHALL call them slices or phases only when delivery sequencing is the subject -- **AND** it SHALL not use slice as a synonym for repo, folder, or affected area - -### Requirement: Workspace planning and implementation boundary -OpenSpec conventions SHALL distinguish workspace-level planning from repo-local implementation ownership. - -#### Scenario: Workspace as shared planning home -- **WHEN** a change spans linked repos or folders -- **THEN** conventions SHALL describe the workspace as the shared planning home -- **AND** repo-local implementation homes SHALL retain ownership of their code and canonical behavior - -#### Scenario: Avoiding materialization-first language -- **WHEN** documentation explains workspace change creation -- **THEN** it SHALL describe the user outcome in terms of shared planning and affected areas -- **AND** it SHALL avoid making users understand implementation terms such as materialization before they can plan - -#### Scenario: Preserving familiar workflow verbs -- **WHEN** workspace guidance describes OpenSpec workflows -- **THEN** it SHALL keep the familiar verbs explore, propose, apply, verify, and archive -- **AND** it SHALL explain that workspace context changes paths, scope, and allowed edit roots rather than creating a separate workflow family - -## Core Principles - -The system SHALL follow these principles: -- Specs reflect what IS currently built and deployed -- Changes contain proposals for what SHOULD be changed -- AI drives the documentation process -- Specs are living documentation kept in sync with deployed code - -## Directory Structure - -### Project Structure - -An OpenSpec project SHALL maintain a consistent directory structure for specifications and changes. - -#### Scenario: Initializing project structure - -- **WHEN** an OpenSpec project is initialized -- **THEN** it SHALL have this structure: -``` -openspec/ -├── project.md # Project-specific context -├── AGENTS.md # AI assistant instructions -├── specs/ # Current deployed capabilities -│ └── [capability]/ # Single, focused capability -│ ├── spec.md # WHAT and WHY -│ └── design.md # HOW (optional, for established patterns) -└── changes/ # Proposed changes - ├── [change-name]/ # Descriptive change identifier - │ ├── proposal.md # Why, what, and impact - │ ├── tasks.md # Implementation checklist - │ ├── design.md # Technical decisions (optional) - │ └── specs/ # Complete future state - │ └── [capability]/ - │ └── spec.md # Clean markdown (no diff syntax) - └── archive/ # Completed changes - └── YYYY-MM-DD-[name]/ -``` - -## Specification Format - -### Behavioral Spec Format - -Behavioral specifications SHALL use a structured format with consistent section headers and keywords to ensure visual consistency and parseability. - -#### Scenario: Writing requirement sections - -- **WHEN** documenting a requirement in a behavioral specification -- **THEN** use a level-3 heading with format `### Requirement: [Name]` -- **AND** immediately follow with a SHALL statement describing core behavior -- **AND** keep requirement names descriptive and under 50 characters - -#### Scenario: Documenting scenarios - -- **WHEN** documenting specific behaviors or use cases -- **THEN** use level-4 headings with format `#### Scenario: [Description]` -- **AND** use bullet points with bold keywords for steps: - - **GIVEN** for initial state (optional) - - **WHEN** for conditions or triggers - - **THEN** for expected outcomes - - **AND** for additional outcomes or conditions - -#### Scenario: Adding implementation details - -- **WHEN** a step requires additional detail -- **THEN** use sub-bullets under the main step -- **AND** maintain consistent indentation - - Sub-bullets provide examples or specifics - - Keep sub-bullets concise - -## Change Storage Convention - -### Header-Based Requirement Identification - -Requirement headers SHALL serve as unique identifiers for programmatic matching between current specs and proposed changes. - -#### Scenario: Matching requirements programmatically - -- **WHEN** processing delta changes -- **THEN** use the `### Requirement: [Name]` header as the unique identifier -- **AND** match using normalized headers: `normalize(header) = trim(header)` -- **AND** compare headers with case-sensitive equality after normalization - -#### Scenario: Handling requirement renames - -- **WHEN** renaming a requirement -- **THEN** use a special `## RENAMED Requirements` section -- **AND** specify both old and new names explicitly: - ```markdown - ## RENAMED Requirements - - FROM: `### Requirement: Old Name` - - TO: `### Requirement: New Name` - ``` -- **AND** if content also changes, include under MODIFIED using the NEW header - -#### Scenario: Validating header uniqueness - -- **WHEN** creating or modifying requirements -- **THEN** ensure no duplicate headers exist within a spec -- **AND** validation tools SHALL flag duplicate headers as errors - -### Change Storage Convention - -Change proposals SHALL store only the additions, modifications, and removals to specifications, not complete future states. - -#### Scenario: Creating change proposals with additions - -- **WHEN** creating a change proposal that adds new requirements -- **THEN** include only the new requirements under `## ADDED Requirements` -- **AND** each requirement SHALL include its complete content -- **AND** use the standard structured format for requirements and scenarios - -#### Scenario: Creating change proposals with modifications - -- **WHEN** creating a change proposal that modifies existing requirements -- **THEN** include the modified requirements under `## MODIFIED Requirements` -- **AND** use the same header text as in the current spec (normalized) -- **AND** include the complete modified requirement (not a diff) -- **AND** optionally annotate what changed with inline comments like `← (was X)` - -#### Scenario: Creating change proposals with removals - -- **WHEN** creating a change proposal that removes requirements -- **THEN** list them under `## REMOVED Requirements` -- **AND** use the normalized header text for identification -- **AND** include reason for removal -- **AND** document any migration path if applicable - -The `changes/[name]/specs/` directory SHALL contain: -- Delta files showing only what changes -- Sections for ADDED, MODIFIED, REMOVED, and RENAMED requirements -- Normalized header matching for requirement identification -- Complete requirements using the structured format -- Clear indication of change type for each requirement - -#### Scenario: Using standard output symbols - -- **WHEN** displaying delta operations in CLI output -- **THEN** use these standard symbols: - - `+` for ADDED (green) - - `~` for MODIFIED (yellow) - - `-` for REMOVED (red) - - `→` for RENAMED (cyan) - -### Archive Process Enhancement - -The archive process SHALL programmatically apply delta changes to current specifications using header-based matching. - -#### Scenario: Archiving changes with deltas - -- **WHEN** archiving a completed change -- **THEN** the archive command SHALL: - 1. Parse RENAMED sections first and apply renames - 2. Parse REMOVED sections and remove by normalized header match - 3. Parse MODIFIED sections and replace by normalized header match (using new names if renamed) - 4. Parse ADDED sections and append new requirements -- **AND** validate that all MODIFIED/REMOVED headers exist in current spec -- **AND** validate that ADDED headers don't already exist -- **AND** generate the updated spec in the main specs/ directory - -#### Scenario: Handling conflicts during archive - -- **WHEN** delta changes conflict with current spec state -- **THEN** the archive command SHALL report specific conflicts -- **AND** require manual resolution before proceeding -- **AND** provide clear guidance on resolving conflicts - -### Proposal Format - -Proposals SHALL explicitly document all changes with clear from/to comparisons. - -#### Scenario: Documenting changes - -- **WHEN** documenting what changes -- **THEN** the proposal SHALL explicitly describe each change: - -```markdown -**[Section or Behavior Name]** -- From: [current state/requirement] -- To: [future state/requirement] -- Reason: [why this change is needed] -- Impact: [breaking/non-breaking, who's affected] -``` - -This explicit format compensates for not having inline diffs and ensures reviewers understand exactly what will change. - -## Change Lifecycle - -The change process SHALL follow these states: - -1. **Propose**: AI creates change with future state specs and explicit proposal -2. **Review**: Humans review proposal and future state -3. **Approve**: Change is approved for implementation -4. **Implement**: Follow tasks.md checklist (can span multiple PRs) -5. **Deploy**: Changes are deployed to production -6. **Update**: Specs in `specs/` are updated to match deployed reality -7. **Archive**: Change is moved to `archive/YYYY-MM-DD-[name]/` - -## Viewing Changes - -### Change Review - -The system SHALL support multiple methods for reviewing proposed changes. - -#### Scenario: Reviewing changes - -- **WHEN** reviewing proposed changes -- **THEN** reviewers can compare using: -- GitHub PR diff view when changes are committed -- Command line: `diff -u specs/[capability]/spec.md changes/[name]/specs/[capability]/spec.md` -- Any visual diff tool comparing current vs future state - -The system relies on tools to generate diffs rather than storing them. - -## Capability Naming - -Capabilities SHALL use: -- Verb-noun patterns (e.g., `user-auth`, `payment-capture`) -- Hyphenated lowercase names -- Singular focus (one responsibility per capability) -- No nesting (flat structure under `specs/`) - -## When Changes Require Proposals - -A proposal SHALL be created for: -- New features or capabilities -- Breaking changes to existing behavior -- Architecture or pattern changes -- Performance optimizations that change behavior -- Security updates affecting access patterns - -A proposal is NOT required for: -- Bug fixes restoring intended behavior -- Typos or formatting fixes -- Non-breaking dependency updates -- Adding tests for existing behavior -- Documentation clarifications - -## Why This Approach - -Clean future state storage provides: -- **Readability**: No diff syntax pollution -- **AI-compatibility**: Standard markdown that AI tools understand -- **Simplicity**: No special parsing or processing needed -- **Tool-agnostic**: Any diff tool can show changes -- **Clear intent**: Explicit proposals document reasoning - -The structured format adds: -- **Visual Consistency**: Requirement and Scenario prefixes make sections instantly recognizable -- **Parseability**: Consistent structure enables tooling and automation -- **Gradual Adoption**: Existing specs can migrate incrementally diff --git a/openspec/specs/schema-resolution/spec.md b/openspec/specs/schema-resolution/spec.md index f243252ad..e7b76cac4 100644 --- a/openspec/specs/schema-resolution/spec.md +++ b/openspec/specs/schema-resolution/spec.md @@ -118,10 +118,6 @@ The system SHALL resolve the schema for a change using the following precedence - **WHEN** change has `.openspec.yaml` with `schema: bound` and config has `schema: tdd` - **THEN** system uses "bound" from change metadata -#### Scenario: Planning home default overrides project config -- **WHEN** no CLI flag or change metadata, the planning home provides default schema `workspace-planning`, and config has `schema: tdd` -- **THEN** system uses "workspace-planning" from the planning home default - #### Scenario: Only project config specifies schema - **WHEN** no CLI flag, change metadata, or planning-home default exists, but config has `schema: tdd` - **THEN** system uses "tdd" from project config @@ -174,28 +170,3 @@ The system SHALL continue to work with existing changes that do not have project - **WHEN** config file is added to project with existing changes - **THEN** existing changes continue to use their bound schema from `.openspec.yaml` -### Requirement: Workspace planning schema resolution -Schema resolution SHALL support the built-in workspace planning schema. - -#### Scenario: Listing workspace planning schema -- **WHEN** a user runs `openspec schemas` -- **THEN** the output SHALL include `workspace-planning` -- **AND** it SHALL identify it as a package-provided schema unless overridden by a higher-precedence schema - -#### Scenario: Resolving workspace planning schema by name -- **WHEN** a workflow command requests schema `workspace-planning` -- **THEN** schema resolution SHALL resolve it using the normal project, user, then package precedence order - -#### Scenario: Workspace default schema for new changes -- **GIVEN** the command creates a change in a workspace planning home -- **AND** the user did not pass an explicit `--schema` -- **AND** no change metadata schema applies to the new change -- **WHEN** OpenSpec resolves the schema for the new change -- **THEN** it SHALL use the planning-home default schema `workspace-planning` -- **AND** it SHALL use that planning-home default before any project or global config schema value - -#### Scenario: Explicit schema override for workspace change -- **GIVEN** the command creates a change in a workspace planning home -- **WHEN** the user passes an explicit `--schema <name>` -- **THEN** OpenSpec SHALL use the explicitly requested schema -- **AND** it SHALL validate that schema using normal schema resolution diff --git a/openspec/specs/workspace-change-planning/spec.md b/openspec/specs/workspace-change-planning/spec.md deleted file mode 100644 index 0d8ea674f..000000000 --- a/openspec/specs/workspace-change-planning/spec.md +++ /dev/null @@ -1,71 +0,0 @@ -# workspace-change-planning Specification - -## Purpose -Define how OpenSpec creates, tracks, and guides workspace-level changes whose planning artifacts coordinate multiple linked repos or folders before implementation ownership is finalized. - -## Requirements -### Requirement: Workspace change planning home -OpenSpec SHALL support workspace-level changes whose shared plan lives in the workspace planning home. - -#### Scenario: Creating a workspace change -- **GIVEN** the command runs from an OpenSpec workspace -- **WHEN** the user creates a change for workspace planning -- **THEN** OpenSpec SHALL create the change under the workspace planning path -- **AND** it SHALL treat the workspace as the planning home for that change -- **AND** it SHALL use the workspace planning schema when no explicit schema is provided - -#### Scenario: Workspace planning artifact structure -- **GIVEN** a workspace change uses the workspace planning schema -- **WHEN** OpenSpec reports or creates planning artifacts for that change -- **THEN** it SHALL use workspace-level artifacts for proposal, specs, cross-area design, and coordination tasks -- **AND** those artifacts SHALL live under the workspace change root -- **AND** it SHALL not require an additional area manifest outside those normal planning artifacts - -#### Scenario: Capturing the shared goal once -- **WHEN** a workspace change is proposed -- **THEN** OpenSpec SHALL capture the product goal at the workspace change level -- **AND** it SHALL avoid requiring separate repo-local proposals before the affected areas are understood - -#### Scenario: Preserving linked repos during change creation -- **WHEN** OpenSpec creates a workspace-level change -- **THEN** it SHALL not create repo-local OpenSpec change directories inside linked repos or folders -- **AND** it SHALL not edit implementation files in linked repos or folders - -### Requirement: Workspace affected areas -OpenSpec SHALL represent ownership or implementation boundaries in a workspace change as affected areas. - -#### Scenario: Using registered workspace links as areas -- **GIVEN** a workspace has linked repos or folders -- **WHEN** a workspace change identifies affected areas by registered link name -- **THEN** OpenSpec SHALL validate those area names against the workspace links -- **AND** it SHALL report invalid area names clearly - -#### Scenario: Planning before all areas are known -- **WHEN** a user is still exploring a workspace change -- **THEN** OpenSpec SHALL allow the shared plan to exist before all affected areas are finalized -- **AND** it SHALL keep unresolved affected area questions visible in the normal planning artifacts and status output - -#### Scenario: Organizing requirements by area -- **GIVEN** a workspace change has requirements owned by one or more affected areas -- **WHEN** OpenSpec reports or creates workspace-scoped specs -- **THEN** it SHALL allow area-specific requirements to be organized under `specs/<area-or-repo>/<capability>/spec.md` -- **AND** it SHALL not require separate area folders outside the normal `specs/` artifact tree -- **AND** it SHALL preserve the area-or-repo path segment as workspace planning context rather than flattening it into a repo-local capability name - -#### Scenario: Separating areas from delivery slices -- **WHEN** a workspace change reports affected areas -- **THEN** OpenSpec SHALL distinguish affected areas from delivery slices or phases -- **AND** it SHALL not require users to define delivery slices for a small cross-area change - -### Requirement: Workspace planning source of truth -OpenSpec SHALL keep the workspace change plan as the source of truth until implementation begins for a selected affected area. - -#### Scenario: Exploring before implementation -- **WHEN** an agent explores a workspace change -- **THEN** it SHALL use workspace-level planning artifacts as the shared planning source -- **AND** it SHALL treat linked repos and folders as available context rather than committed implementation targets - -#### Scenario: Deferring repo-local implementation -- **WHEN** repo-local implementation work is needed for a workspace change -- **THEN** OpenSpec SHALL require an explicit implementation workflow with a selected affected area -- **AND** it SHALL expose the allowed edit root for that selected area before implementation edits begin diff --git a/openspec/specs/workspace-foundation/spec.md b/openspec/specs/workspace-foundation/spec.md deleted file mode 100644 index 513ae3aa1..000000000 --- a/openspec/specs/workspace-foundation/spec.md +++ /dev/null @@ -1,279 +0,0 @@ -# workspace-foundation Specification - -## Purpose -Define the product and storage foundation for OpenSpec coordination workspaces, -including workspace identity, shared versus local state, managed storage, -registry behavior, stable link names, and repo ownership boundaries. -## Requirements -### Requirement: Recognizable Workspace Home -OpenSpec SHALL give users and agents a recognizable workspace home for cross-repo planning. - -#### Scenario: Planning across linked repos or folders -- **WHEN** a user creates an OpenSpec workspace for repos or folders they plan across -- **THEN** the workspace SHALL provide a durable planning home -- **AND** the workspace SHALL be able to hold multiple changes over time - -#### Scenario: Working from inside a workspace -- **GIVEN** a user runs OpenSpec from a workspace folder or one of its subdirectories -- **WHEN** OpenSpec resolves the current workspace -- **THEN** it SHALL identify the workspace location -- **AND** it SHALL use the workspace location's `changes/` directory as the workspace planning area - -#### Scenario: Avoiding accidental workspace mode -- **GIVEN** a directory has `changes/` but is not an OpenSpec workspace -- **WHEN** OpenSpec resolves the current workspace -- **THEN** it SHALL avoid treating that directory as a workspace -- **AND** it SHALL enter workspace mode only when the workspace identity file is present - -### Requirement: Stable Workspace Name -OpenSpec SHALL use one kebab-case workspace name across workspace identity, managed storage, and the local registry. - -#### Scenario: Using one workspace name -- **WHEN** OpenSpec creates or records a managed workspace -- **THEN** the workspace name SHALL be stored in `.openspec-workspace/view.yaml` -- **AND** the same name SHALL be used as the default managed workspace folder name -- **AND** the same name SHALL be used as the local registry name - -#### Scenario: Rejecting invalid workspace names -- **WHEN** OpenSpec accepts a workspace name -- **THEN** it SHALL require kebab-case names using lowercase letters, numbers, and single hyphen separators -- **AND** it SHALL reject empty names, dot names, names with leading or trailing hyphens, names with repeated hyphens, uppercase letters, spaces, underscores, dots, and path separators -- **AND** setup flows SHALL report OS-level folder creation failures clearly - -### Requirement: Dedicated Workspace Identity -OpenSpec SHALL distinguish a coordination workspace from a repo-local OpenSpec project. - -#### Scenario: Reading workspace identity -- **WHEN** OpenSpec reads or writes workspace identity and workspace state -- **THEN** it SHALL use `.openspec-workspace/` - -#### Scenario: Preserving repo-local OpenSpec projects -- **GIVEN** a repo-local OpenSpec project uses `openspec/` -- **WHEN** that repo is linked to a workspace -- **THEN** OpenSpec SHALL continue treating `openspec/` as that repo's local OpenSpec directory -- **AND** workspace planning SHALL remain anchored in the workspace folder - -#### Scenario: Avoiding repo-local initialization in the workspace folder -- **WHEN** a user is working from an OpenSpec workspace folder -- **THEN** OpenSpec SHALL treat that folder as a workspace coordination surface -- **AND** users SHALL not need to initialize a repo-local `openspec/` project inside the workspace folder - -### Requirement: Safe Workspace Sharing -OpenSpec SHALL keep shared workspace information separate from local machine paths. - -#### Scenario: Sharing workspace planning -- **WHEN** a workspace is shared with another user or machine -- **THEN** shared workspace information SHALL include portable workspace identity and stable link names -- **AND** it SHALL not require another user to reuse the original user's absolute checkout paths - -#### Scenario: Keeping checkout paths local -- **WHEN** OpenSpec stores local paths for a workspace -- **THEN** those paths SHALL be treated as local to the current machine and runtime -- **AND** another machine MAY map the same link names to different local paths - -#### Scenario: Preserving runtime-local paths -- **WHEN** OpenSpec reads or writes machine-local path state -- **THEN** it SHALL preserve path strings valid for the current runtime -- **AND** it SHALL support native Windows paths and WSL2/Linux paths as local state values - -#### Scenario: Keeping managed workspace view state local -- **WHEN** OpenSpec creates a managed workspace -- **THEN** it SHALL write `.openspec-workspace/view.yaml` as private local view state -- **AND** the file SHALL preserve stable link names and local path values for the current machine - -### Requirement: Standard Workspace Location -OpenSpec SHALL use a standard location for OpenSpec-managed workspaces without asking most users to choose one. - -#### Scenario: Using the standard workspace location -- **WHEN** OpenSpec needs the location for OpenSpec-managed workspaces -- **THEN** it SHALL use `<global-data-dir>/workspaces` -- **AND** `<global-data-dir>` SHALL follow existing OpenSpec XDG and platform data directory behavior - -#### Scenario: Avoiding workspace-specific storage overrides -- **WHEN** OpenSpec resolves the location for OpenSpec-managed workspaces -- **THEN** it SHALL not use a workspace-specific environment variable, command, or configuration setting in this slice -- **AND** managed workspace storage SHALL remain under `<global-data-dir>/workspaces` - -#### Scenario: Running from native Windows -- **WHEN** OpenSpec runs from native Windows shells such as PowerShell -- **AND** `XDG_DATA_HOME` is not set -- **THEN** OpenSpec SHALL store managed workspaces under the Windows global data location -- **AND** paths SHALL follow native Windows path behavior - -#### Scenario: Running from WSL2 -- **WHEN** OpenSpec runs from WSL2 -- **THEN** OpenSpec SHALL store managed workspaces under the Linux/XDG data location inside WSL -- **AND** paths SHALL follow Linux path behavior inside WSL - -#### Scenario: Using the workspace location automatically -- **WHEN** OpenSpec creates or resolves OpenSpec-managed workspaces in later workflows -- **THEN** it SHALL use the resolved workspace location by default -- **AND** users SHALL be able to follow the normal workspace flow without choosing a storage location - -#### Scenario: Showing the workspace location -- **WHEN** OpenSpec creates a workspace in the standard workspace location -- **THEN** it SHALL report the workspace location to the user -- **AND** it SHALL not hide where planning files were created - -#### Scenario: Staying in the current runtime -- **WHEN** OpenSpec resolves workspace locations or local repo paths -- **THEN** it SHALL interpret paths for the runtime running OpenSpec -- **AND** Windows, UNC WSL, and WSL mount paths SHALL remain explicit user-provided paths - -### Requirement: Local Workspace Registry -OpenSpec SHALL keep a lightweight local registry of known workspaces on the current machine. - -#### Scenario: Recording known workspaces -- **WHEN** OpenSpec creates or learns about a managed workspace -- **THEN** it SHALL be able to record the workspace name and location in a local registry -- **AND** the registry SHALL be machine-local state - -#### Scenario: Keeping workspace folders authoritative -- **WHEN** OpenSpec reads workspace details -- **THEN** each workspace folder's `.openspec-workspace/view.yaml` SHALL remain the source of truth for that workspace -- **AND** the local registry SHALL act only as an index of known workspace locations - -#### Scenario: Finding workspaces from anywhere -- **WHEN** a later workspace command runs outside a workspace directory -- **THEN** OpenSpec MAY use the local registry to find known workspaces -- **AND** commands that need one workspace MAY use the registry to support an interactive picker - -### Requirement: Stable Link Names -OpenSpec SHALL use stable folder-style link names to refer to repos and folders in workspace planning. - -#### Scenario: Referring to a repo or folder in workspace planning -- **WHEN** workspace state or later workspace planning artifacts refer to a linked repo or folder -- **THEN** they SHALL use the stable link name -- **AND** the same link name SHALL remain valid even when local checkout paths differ - -#### Scenario: Reusing link names across machines -- **WHEN** a workspace is used on another machine -- **THEN** link names SHALL remain stable -- **AND** local checkout paths MAY differ on that machine - -#### Scenario: Rejecting invalid link names -- **WHEN** OpenSpec accepts a workspace link name -- **THEN** it SHALL reject empty names, `.` or `..`, and names containing path separators -- **AND** link names SHALL be unique within the workspace -- **AND** link names SHALL not be required to use workspace-name kebab-case - -### Requirement: Linked Repos And Folders -OpenSpec SHALL allow workspace planning to include linked repos and folders before they have repo-local OpenSpec state. - -#### Scenario: Planning with a repo that has not adopted OpenSpec -- **WHEN** a workspace links a repo path that does not yet contain repo-local `openspec/` -- **THEN** the repo SHALL still be available for workspace-level planning -- **AND** implementation readiness MAY be handled by a later workflow - -#### Scenario: Planning across monorepo folders -- **WHEN** planning spans multiple packages, services, apps, or directories inside one monorepo -- **THEN** the workspace SHALL be able to link those folders separately -- **AND** each folder SHALL not need its own repo-local `openspec/` directory to participate in workspace planning - -#### Scenario: Treating repos and folders consistently -- **WHEN** a workspace plan includes both separate repos and folders inside a monorepo -- **THEN** OpenSpec SHALL use the same planning model for both -- **AND** users SHALL not need to create different kinds of workspace plans for multi-repo and monorepo changes - -#### Scenario: Recording links without changing targets -- **WHEN** OpenSpec records a link between a workspace and a local repo or folder -- **THEN** it SHALL store the link in workspace state -- **AND** it SHALL not create, copy, move, initialize, or edit files inside the linked repo or folder - -### Requirement: Planning Before Implementation -OpenSpec SHALL treat workspace creation and detection as planning setup, not implementation. - -#### Scenario: Creating or detecting a workspace -- **WHEN** a workspace exists -- **THEN** OpenSpec SHALL treat it as a place for workspace-level planning -- **AND** repo implementation files SHALL remain unchanged until an explicit implementation workflow runs - -#### Scenario: Deferring repo implementation -- **WHEN** repo-local implementation, apply, verify, or archive behavior is needed -- **THEN** that behavior SHALL require an explicit later workspace workflow - -### Requirement: Repo Ownership Boundaries -OpenSpec SHALL keep repo ownership legible when planning happens in a workspace. - -#### Scenario: Planning across owned repos -- **WHEN** a workspace plan refers to behavior owned by a repo or source area -- **THEN** that owner SHALL remain the home for canonical specs and implementation work -- **AND** the workspace SHALL make the cross-boundary plan visible without taking ownership away from that owner - -#### Scenario: Drafting before ownership is clear -- **WHEN** cross-repo behavior is still being explored and ownership is not clear -- **THEN** the workspace MAY hold planning notes or draft behavior -- **AND** those drafts SHALL remain distinguishable from canonical repo-owned specs - -### Requirement: Workspace Preferred Opener State -OpenSpec SHALL store a workspace's preferred opener in machine-local workspace state when the user explicitly chooses one. - -#### Scenario: Recording an interactive setup opener choice -- **WHEN** an interactive user chooses a preferred opener during `openspec workspace setup` -- **THEN** OpenSpec SHALL record the opener in `.openspec-workspace/view.yaml` -- **AND** the stored value SHALL use a structured `preferred_opener` object with `kind` and `id` - -#### Scenario: Recording a non-interactive setup opener choice -- **WHEN** a non-interactive user runs `openspec workspace setup --no-interactive --opener codex` -- **THEN** OpenSpec SHALL record `preferred_opener.kind` as `agent` -- **AND** it SHALL record `preferred_opener.id` as `codex` - -#### Scenario: Leaving opener unset during non-interactive setup -- **WHEN** a non-interactive user runs `openspec workspace setup --no-interactive` with opener selection omitted -- **THEN** OpenSpec SHALL leave the workspace preferred opener unset -- **AND** the unset state SHALL allow `workspace open` to prompt later - -#### Scenario: Supported preferred opener values -- **WHEN** OpenSpec accepts a preferred opener value -- **THEN** it SHALL accept `codex`, `claude`, `github-copilot`, and `editor` -- **AND** it SHALL map `editor` to `kind: editor` and `id: vscode` -- **AND** it SHALL map agent values to `kind: agent` and the matching agent `id` - -#### Scenario: Ordering setup opener choices -- **WHEN** interactive setup displays opener choices -- **THEN** OpenSpec SHALL show all supported openers -- **AND** it SHALL order openers with detected executables before unavailable openers -- **AND** unavailable openers SHALL remain visible with an availability note - -### Requirement: Maintained Workspace Open Surface -OpenSpec SHALL maintain files that make a workspace directly openable after setup and link changes. - -#### Scenario: Creating the open surface during setup -- **WHEN** `openspec workspace setup` creates a workspace -- **THEN** OpenSpec SHALL create or refresh `AGENTS.md` -- **AND** it SHALL create or refresh `<workspace-name>.code-workspace` -- **AND** it SHALL not create workspace ignore rules for machine-local open files by default - -#### Scenario: Refreshing the open surface after linking -- **WHEN** `openspec workspace link` succeeds -- **THEN** OpenSpec SHALL refresh `AGENTS.md` -- **AND** it SHALL refresh `<workspace-name>.code-workspace` - -#### Scenario: Refreshing the open surface after relinking -- **WHEN** `openspec workspace relink` succeeds -- **THEN** OpenSpec SHALL refresh `AGENTS.md` -- **AND** it SHALL refresh `<workspace-name>.code-workspace` - -#### Scenario: Building the VS Code workspace file -- **WHEN** OpenSpec refreshes `<workspace-name>.code-workspace` -- **THEN** the file SHALL include every linked repo or folder with a valid local path before workspace-local files -- **AND** it SHALL include attached initiative context when available -- **AND** it SHALL include the workspace root as `OpenSpec workspace` -- **AND** it SHALL omit linked repos or folders whose local paths are missing or invalid - -#### Scenario: Cleaning legacy workspace ignore rules -- **WHEN** OpenSpec refreshes the workspace open surface -- **THEN** it SHALL remove the legacy ignore rule for the maintained `<workspace-name>.code-workspace` file when present -- **AND** it SHALL preserve unrelated user-authored ignore rules - -#### Scenario: Preserving user-authored AGENTS content -- **GIVEN** `AGENTS.md` contains content outside the OpenSpec workspace guidance markers -- **WHEN** OpenSpec refreshes workspace guidance -- **THEN** it SHALL replace only the marked OpenSpec workspace guidance block -- **AND** it SHALL preserve content outside the markers - -#### Scenario: Appending AGENTS guidance when markers are missing -- **GIVEN** `AGENTS.md` exists and OpenSpec workspace guidance markers are absent -- **WHEN** OpenSpec refreshes workspace guidance -- **THEN** it SHALL append the marked OpenSpec workspace guidance block -- **AND** it SHALL preserve the existing file content diff --git a/openspec/specs/workspace-links/spec.md b/openspec/specs/workspace-links/spec.md deleted file mode 100644 index edbc2fea8..000000000 --- a/openspec/specs/workspace-links/spec.md +++ /dev/null @@ -1,529 +0,0 @@ -# workspace-links Specification - -## Purpose -Define the direct workspace setup, discovery, linking, relinking, health check, -and JSON-output behavior for managing OpenSpec workspaces across repos and -folders. -## Requirements -### Requirement: Guided Workspace Setup -OpenSpec SHALL provide a guided setup flow for users starting workspace planning. - -#### Scenario: Creating a workspace through setup -- **WHEN** a user runs `openspec workspace setup` -- **THEN** OpenSpec SHALL guide the user through creating an OpenSpec workspace -- **AND** the workspace SHALL use the standard workspace location from the workspace foundation - -#### Scenario: Asking for the workspace name first -- **WHEN** interactive setup starts -- **THEN** OpenSpec SHALL ask for the workspace name before asking for repos or folders -- **AND** workspace names SHALL use kebab-case with lowercase letters, numbers, and hyphens - -#### Scenario: Retrying an invalid workspace name during setup -- **WHEN** an interactive user enters an invalid workspace name -- **THEN** OpenSpec SHALL explain that workspace names must be kebab-case -- **AND** it SHALL let the user enter another workspace name before continuing setup - -#### Scenario: Linking a required first repo or folder -- **WHEN** setup asks for repos or folders -- **THEN** the user SHALL provide at least one existing repo or folder path -- **AND** setup SHALL not finish successfully until at least one path is linked - -#### Scenario: Inferring link names during setup -- **WHEN** the user provides a repo or folder path during setup -- **THEN** OpenSpec SHALL infer the link name from the folder basename -- **AND** it SHALL ask for a different name only when the inferred name conflicts - -#### Scenario: Handling inferred link name conflicts during setup -- **GIVEN** setup infers a link name that already exists in the workspace -- **WHEN** setup is interactive -- **THEN** OpenSpec SHALL show the conflicting link name and the existing path for that link -- **AND** it SHALL ask the user for a different link name before continuing - -#### Scenario: Preserving folder-style link names -- **WHEN** OpenSpec accepts a workspace link name -- **THEN** it SHALL allow folder-style names that are valid under the workspace foundation link-name rules -- **AND** it SHALL not require link names to use the stricter workspace-name kebab-case rule - -#### Scenario: Adding multiple repos or folders during setup -- **WHEN** setup links a repo or folder -- **THEN** OpenSpec SHALL let the user add another repo or folder with a simple repeated prompt -- **AND** each linked path SHALL be recorded without editing the target repo or folder - -#### Scenario: Storing verified absolute paths during setup -- **WHEN** setup links a repo or folder path -- **THEN** OpenSpec SHALL verify that the path resolves to an existing folder -- **AND** it SHALL store an absolute runtime-local path in machine-local state instead of the raw user input -- **AND** relative inputs SHALL be resolved against the command's current working directory - -#### Scenario: Preserving equals signs in setup link paths -- **WHEN** non-interactive setup receives a `--link` value that resolves to an existing folder and contains `=` -- **THEN** OpenSpec SHALL treat the full value as the path -- **AND** it SHALL infer the link name from the folder basename -- **AND** explicit `--link <name>=<path>` inputs SHALL preserve `=` characters inside `<path>` - -#### Scenario: Running setup with non-interactive inputs -- **WHEN** `openspec workspace setup --no-interactive` receives a workspace name and at least one valid link -- **THEN** OpenSpec SHALL create the workspace without prompts -- **AND** it SHALL support repeated `--link` values - -#### Scenario: Non-interactive setup duplicate link names -- **WHEN** `openspec workspace setup --no-interactive` receives two links with the same inferred or explicit name -- **THEN** OpenSpec SHALL fail with a clear duplicate link-name error -- **AND** the error SHALL show the conflicting link name and the first path using that name -- **AND** it SHALL suggest using explicit `--link <name>=<path>` values with different names - -#### Scenario: Missing non-interactive setup inputs -- **WHEN** `openspec workspace setup --no-interactive` is missing a workspace name or link -- **THEN** OpenSpec SHALL fail with a clear message -- **AND** it SHALL explain which flags are required - -#### Scenario: Finishing setup -- **WHEN** setup finishes -- **THEN** OpenSpec SHALL show the workspace location, planning path, and linked repos or folders -- **AND** it SHALL check what the current machine can resolve - -#### Scenario: Recording created workspaces locally -- **WHEN** setup creates a workspace -- **THEN** OpenSpec SHALL record it in the local workspace registry -- **AND** the workspace folder SHALL remain the source of truth for workspace state - -#### Scenario: Reusing an existing workspace name during setup -- **GIVEN** a managed workspace already exists with the requested name -- **WHEN** a user runs setup with that workspace name -- **THEN** OpenSpec SHALL explain that the workspace already exists -- **AND** it SHALL not overwrite the existing workspace - -### Requirement: Workspace Discovery -OpenSpec SHALL let users see the OpenSpec-managed workspaces available on the current machine. - -#### Scenario: Listing workspaces -- **WHEN** a user runs `openspec workspace list` -- **THEN** OpenSpec SHALL list known managed workspaces -- **AND** each workspace SHALL include the workspace name, workspace location, and linked repos or folders - -#### Scenario: Using the short list command -- **WHEN** a user runs `openspec workspace ls` -- **THEN** OpenSpec SHALL behave the same as `openspec workspace list` - -#### Scenario: Listing when no workspaces exist -- **WHEN** a user runs `openspec workspace list` -- **AND** no managed workspaces exist -- **THEN** OpenSpec SHALL say that no workspaces were found -- **AND** it SHALL show the user how to create one - -#### Scenario: Listing stale registry entries -- **WHEN** the local registry contains a workspace location that no longer exists -- **THEN** `workspace list` SHALL report the stale workspace entry -- **AND** it SHALL avoid silently deleting registry state -- **AND** it SHALL avoid rewriting or repairing registry state automatically - -#### Scenario: Avoiding registry cleanup commands -- **WHEN** users inspect stale workspace registry entries in this slice -- **THEN** OpenSpec SHALL treat stale entries as report-only diagnostics -- **AND** it SHALL not expose a registry cleanup command such as `workspace forget` - -### Requirement: Global Workspace Commands -OpenSpec SHALL let workspace commands run from outside workspace directories. - -#### Scenario: Selecting a workspace by flag -- **WHEN** a command that needs one workspace receives `--workspace <name>` -- **THEN** OpenSpec SHALL use that workspace from the local registry -- **AND** it SHALL fail clearly if the workspace name is unknown - -#### Scenario: Using the current workspace -- **GIVEN** the command runs from a workspace folder or subdirectory -- **WHEN** the command needs one workspace and no `--workspace` flag is provided -- **THEN** OpenSpec SHALL use the current workspace - -#### Scenario: Using an unregistered current workspace -- **GIVEN** the command runs from a valid workspace folder or subdirectory -- **AND** that workspace is not recorded in the local workspace registry -- **WHEN** the command needs one workspace and no `--workspace <name>` flag is provided -- **THEN** OpenSpec SHALL use the current workspace -- **AND** it SHALL include a non-fatal warning status with code `workspace_not_in_local_registry` -- **AND** the warning SHALL explain how the user can get the workspace recorded locally - -#### Scenario: Recording an unregistered current workspace after mutation -- **GIVEN** a mutating workspace command uses a valid current workspace that is not recorded in the local workspace registry -- **WHEN** `workspace link` or `workspace relink` succeeds -- **THEN** OpenSpec SHALL record the workspace name and location in the local workspace registry - -#### Scenario: Doctor does not register current workspaces -- **GIVEN** `workspace doctor` uses a valid current workspace that is not recorded in the local workspace registry -- **WHEN** doctor finishes -- **THEN** OpenSpec SHALL report the non-fatal registry warning -- **AND** it SHALL not write registry state - -#### Scenario: Picking from multiple workspaces -- **GIVEN** multiple known workspaces exist -- **WHEN** an interactive command needs one workspace and none is specified -- **THEN** OpenSpec SHALL show a workspace picker -- **AND** the picker SHALL include workspace names and paths - -#### Scenario: Ambiguous non-interactive workspace selection -- **GIVEN** multiple known workspaces exist -- **WHEN** a non-interactive command needs one workspace and none is specified -- **THEN** OpenSpec SHALL fail with a clear message -- **AND** it SHALL suggest passing `--workspace <name>` - -#### Scenario: Ambiguous JSON workspace selection -- **GIVEN** multiple known workspaces exist -- **WHEN** a command running with `--json` needs one workspace and none is specified -- **THEN** OpenSpec SHALL fail without showing a picker -- **AND** it SHALL emit a structured status error -- **AND** it SHALL suggest passing `--workspace <name>` - -#### Scenario: No known workspaces for a command that needs one -- **GIVEN** no known workspaces exist in the local registry -- **AND** the command is not running from a workspace folder or subdirectory -- **WHEN** `workspace link`, `workspace relink`, `workspace doctor`, or another command that needs one workspace runs without `--workspace <name>` -- **THEN** OpenSpec SHALL fail without showing a picker regardless of interactive mode -- **AND** it SHALL print `No known OpenSpec workspaces. Run 'openspec workspace setup' first.` -- **AND** it SHALL explain that `--workspace <name>` can be used after at least one workspace is known locally - -### Requirement: Workspace Links -OpenSpec SHALL let users link existing repos or folders to a workspace before creating a change. - -#### Scenario: Linking with an inferred name -- **WHEN** a user runs `openspec workspace link <path>` -- **THEN** OpenSpec SHALL infer the link name from the folder basename -- **AND** it SHALL store the verified absolute local path as machine-local state - -#### Scenario: Linking with an explicit name -- **WHEN** a user runs `openspec workspace link <name> <path>` -- **THEN** OpenSpec SHALL use the explicit link name for planning -- **AND** it SHALL store the verified absolute local path as machine-local state - -#### Scenario: Requiring an existing path -- **WHEN** a user links a repo or folder path -- **THEN** the path SHALL exist on the current machine -- **AND** OpenSpec SHALL reject missing paths with a clear message - -#### Scenario: Resolving linked paths before storage -- **WHEN** a user links a repo or folder path -- **THEN** OpenSpec SHALL store the verified absolute path for the current runtime -- **AND** relative inputs SHALL be resolved against the command's current working directory -- **AND** OpenSpec SHALL not translate paths between native Windows, WSL2, and Unix runtimes - -#### Scenario: Linking a monorepo folder -- **WHEN** a user links a package, service, app, or directory inside a monorepo -- **THEN** OpenSpec SHALL store it as a workspace link -- **AND** it SHALL not require that folder to have its own repo-local `openspec/` directory - -#### Scenario: Linking without repo-local OpenSpec -- **WHEN** a user links a path that does not contain repo-local OpenSpec state -- **THEN** OpenSpec SHALL keep that repo or folder available for workspace planning -- **AND** it SHALL not treat missing repo-local OpenSpec state as a link failure - -#### Scenario: Link records only -- **WHEN** a user links a repo or folder -- **THEN** OpenSpec SHALL record workspace state and local path state -- **AND** it SHALL not create, copy, move, initialize, or edit files in the linked repo or folder - -#### Scenario: Blocking link when local state is invalid -- **GIVEN** the workspace machine-local state file exists but cannot be parsed or validated -- **WHEN** a user runs `openspec workspace link` -- **THEN** OpenSpec SHALL fail with status code `workspace_local_state_invalid` -- **AND** it SHALL not rewrite shared workspace state or machine-local path state - -#### Scenario: Reusing a link name -- **GIVEN** a workspace already has a link with a given name -- **WHEN** a user tries to link another path with the same name -- **THEN** OpenSpec SHALL explain that the link name is already in use by another link -- **AND** it SHALL show the existing link name and existing path -- **AND** it SHALL suggest choosing a different link name -- **AND** it SHALL suggest `workspace relink <name> <path>` when the user intended to change the existing link path -- **AND** it SHALL preserve the existing link unless the user explicitly relinks it - -### Requirement: Workspace Relinks -OpenSpec SHALL let users update existing link paths without recreating the workspace. - -#### Scenario: Updating a local path -- **GIVEN** a workspace has a link -- **WHEN** a user runs `openspec workspace relink <name> <path>` -- **THEN** OpenSpec SHALL keep the stable link name -- **AND** it SHALL update the machine-local path for the current machine to the verified absolute path - -#### Scenario: Requiring an existing relink path -- **WHEN** a user relinks to a new path -- **THEN** the new path SHALL exist on the current machine -- **AND** OpenSpec SHALL reject missing paths with a clear message - -#### Scenario: Resolving relink paths before storage -- **WHEN** a user relinks to a new path -- **THEN** OpenSpec SHALL store the verified absolute path for the current runtime -- **AND** relative inputs SHALL be resolved against the command's current working directory - -#### Scenario: Blocking relink when local state is invalid -- **GIVEN** the workspace machine-local state file exists but cannot be parsed or validated -- **WHEN** a user runs `openspec workspace relink` -- **THEN** OpenSpec SHALL fail with status code `workspace_local_state_invalid` -- **AND** it SHALL not rewrite machine-local path state - -#### Scenario: Updating an unknown link -- **WHEN** a user tries to relink a link that does not exist -- **THEN** OpenSpec SHALL explain that the link name is unknown -- **AND** it SHALL preserve existing workspace state - -#### Scenario: Avoiding owner and handoff fields -- **WHEN** users link or relink repos or folders in this slice -- **THEN** OpenSpec SHALL not ask for owner or handoff metadata -- **AND** link maintenance SHALL focus on names and local paths - -### Requirement: Workspace Health Check -OpenSpec SHALL explain what the current machine can resolve for a workspace. - -#### Scenario: Doctor checks one selected workspace -- **WHEN** a user runs `openspec workspace doctor` -- **THEN** OpenSpec SHALL inspect one selected workspace -- **AND** it SHALL not scan every known workspace in the local registry by default - -#### Scenario: Doctor infers the current workspace -- **GIVEN** the command runs from a workspace folder or subdirectory -- **WHEN** the user runs `openspec workspace doctor` without `--workspace <name>` -- **THEN** OpenSpec SHALL inspect the current workspace - -#### Scenario: Checking a healthy workspace -- **WHEN** a user runs `openspec workspace doctor` -- **THEN** OpenSpec SHALL show the workspace location and workspace planning path -- **AND** it SHALL show linked repos or folders and which paths resolve on the current machine - -#### Scenario: Selected workspace location is missing -- **GIVEN** the selected workspace comes from the local registry -- **AND** the registered workspace location is missing or invalid -- **WHEN** a user runs `openspec workspace doctor` -- **THEN** OpenSpec SHALL report a selected-workspace status error -- **AND** it SHALL not attempt to inspect links for that workspace - -#### Scenario: Reporting repo-local specs paths -- **WHEN** a linked repo or folder resolves -- **THEN** doctor SHALL report `repo_specs_path` when repo-local `openspec/specs` exists -- **AND** it SHALL report `repo_specs_path: null` when repo-local specs are not present - -#### Scenario: Checking missing paths -- **WHEN** a link points to a path that is missing on the current machine -- **THEN** doctor SHALL identify the affected link name -- **AND** it SHALL include a suggested `workspace relink` fix - -#### Scenario: Checking shared and local state drift -- **WHEN** shared workspace state and machine-local path state do not agree -- **THEN** doctor SHALL explain which link names are affected -- **AND** it SHALL distinguish shared workspace links from local-only paths - -#### Scenario: Reporting invalid local state -- **WHEN** list or doctor reads a workspace whose machine-local state file cannot be parsed or validated -- **THEN** OpenSpec SHALL report status code `workspace_local_state_invalid` -- **AND** it SHALL avoid treating the invalid local state as an empty path map for mutation or repair suggestions -- **AND** it SHALL not rewrite workspace registry state or machine-local path state - -#### Scenario: Reporting without auto-repair -- **WHEN** doctor finds issues -- **THEN** it SHALL report all issues it can find -- **AND** it SHALL not automatically repair workspace state - -#### Scenario: Using readable human output -- **WHEN** doctor prints human output -- **THEN** it SHALL show a readable workspace summary, linked repos or folders, and issues when present -- **AND** it SHALL avoid printing raw JSON or relying on a rigid YAML dump as the default human experience - -### Requirement: Scriptable Workspace Setup Commands -OpenSpec SHALL provide JSON output for direct workspace setup commands. - -#### Scenario: Requesting JSON output -- **WHEN** a user passes `--json` to direct workspace setup commands -- **THEN** OpenSpec SHALL print machine-readable output -- **AND** the output SHALL avoid extra human-readable text -- **AND** the output SHALL separate primary objects from structured `status` entries - -#### Scenario: Setup JSON requires non-interactive setup -- **WHEN** a user runs `openspec workspace setup --json` without `--no-interactive` -- **THEN** OpenSpec SHALL fail clearly -- **AND** it SHALL explain that `workspace setup --json` requires `--no-interactive` - -#### Scenario: JSON output disables prompts -- **WHEN** a direct workspace setup command runs with `--json` -- **THEN** OpenSpec SHALL avoid interactive prompts -- **AND** it SHALL fail with structured status output when required choices are ambiguous - -#### Scenario: JSON status entry shape -- **WHEN** a direct workspace setup command reports warnings, errors, or suggested fixes in JSON output -- **THEN** each status entry SHALL include a stable `code`, a `severity`, and a human-readable `message` -- **AND** status entries MAY include `target` and `fix` fields when a specific object field or suggested command is useful - -#### Scenario: JSON object status shape -- **WHEN** a direct workspace setup command emits JSON for workspace, link, or list objects -- **THEN** each object MAY include a `status` array for object-specific warnings or errors -- **AND** the top-level response SHALL include a `status` array for command-level warnings or errors -- **AND** healthy objects and healthy responses SHALL use an empty `status` array - -#### Scenario: Commands with JSON output -- **WHEN** users run `workspace setup --no-interactive`, `workspace list`, `workspace link`, `workspace relink`, or `workspace doctor` -- **THEN** each command SHALL support JSON output - -### Requirement: Workspace setup installs agent skills -OpenSpec SHALL let users install OpenSpec agent skills into a workspace during workspace setup. - -#### Scenario: Prompting for workspace agent skills -- **WHEN** interactive workspace setup reaches agent skill installation -- **THEN** OpenSpec SHALL ask which agents should get OpenSpec skills in this workspace -- **AND** the prompt SHALL use agent-skill language rather than "AI tools" language - -#### Scenario: Preselecting the preferred opener -- **GIVEN** the user selected a preferred opener that supports OpenSpec skill generation -- **WHEN** interactive workspace setup asks which agents should get skills -- **THEN** OpenSpec SHALL preselect the matching agent -- **AND** the user SHALL be able to select additional agents or deselect the preselected agent - -#### Scenario: Installing selected workspace skills -- **WHEN** workspace setup completes with one or more selected agents -- **THEN** OpenSpec SHALL generate or refresh OpenSpec skill files under the workspace root for each selected agent -- **AND** it SHALL report which agents received skills -- **AND** it SHALL store the selected agents in workspace-local machine state - -#### Scenario: Installing profile-selected workflows -- **GIVEN** global config resolves to a workflow profile -- **WHEN** workspace setup installs agent skills -- **THEN** OpenSpec SHALL install workspace-local skills for the workflows selected by that profile -- **AND** it SHALL treat `--tools` as agent selection, not workflow selection -- **AND** it SHALL record the last applied workflow IDs for drift detection - -#### Scenario: Installing skills only during setup -- **WHEN** workspace setup installs agent skills -- **THEN** OpenSpec SHALL generate skill files only -- **AND** it SHALL not generate slash command files or global command files as part of workspace setup - -#### Scenario: Ignoring command delivery for workspace setup -- **GIVEN** global config delivery is `commands` or `both` -- **WHEN** workspace setup installs agent skills -- **THEN** OpenSpec SHALL still generate workspace-local skills only -- **AND** it SHALL report that workspace command generation is not part of this slice - -#### Scenario: Preserving linked repos during skill installation -- **WHEN** workspace setup installs agent skills -- **THEN** OpenSpec SHALL leave linked repos and folders unchanged -- **AND** generated skills SHALL be scoped to the workspace planning home - -#### Scenario: Non-interactive setup tool selection -- **WHEN** non-interactive workspace setup receives `--tools all`, `--tools none`, or `--tools <ids>` -- **THEN** OpenSpec SHALL use the selected tool set for workspace agent skill installation -- **AND** it SHALL validate tool IDs using the same supported tool IDs as skill generation for repo initialization - -#### Scenario: Non-interactive setup without tool selection -- **WHEN** non-interactive workspace setup omits `--tools` -- **THEN** OpenSpec SHALL create the workspace without installing agent skills -- **AND** it SHALL report that no workspace skills were installed -- **AND** it SHALL tell the user to run `openspec workspace update --tools <ids>` to install skills later - -#### Scenario: Reporting setup skills in JSON output -- **WHEN** non-interactive workspace setup installs agent skills with JSON output enabled -- **THEN** OpenSpec SHALL include generated, refreshed, skipped, or failed skill installation results in machine-readable output - -### Requirement: Workspace update manages agent skills -OpenSpec SHALL provide a workspace update flow for refreshing agent skills after setup. - -#### Scenario: Updating the current workspace -- **GIVEN** the command runs from inside an OpenSpec workspace -- **WHEN** the user runs `openspec workspace update` -- **THEN** OpenSpec SHALL update that current workspace - -#### Scenario: Updating a named workspace -- **GIVEN** a workspace named `platform` is known locally -- **WHEN** the user runs `openspec workspace update platform` -- **THEN** OpenSpec SHALL update the `platform` workspace - -#### Scenario: Updating a workspace selected by flag -- **GIVEN** a workspace named `platform` is known locally -- **WHEN** the user runs `openspec workspace update --workspace platform` -- **THEN** OpenSpec SHALL update the `platform` workspace - -#### Scenario: Updating selected workspace skills -- **WHEN** workspace update completes with selected agents -- **THEN** OpenSpec SHALL refresh OpenSpec skills for selected agents -- **AND** it SHALL add skills for newly selected agents -- **AND** it SHALL remove OpenSpec-managed workflow skill directories for agents that are no longer selected -- **AND** it SHALL update the stored workspace-local selected agent list - -#### Scenario: Identifying managed workflow skill directories -- **WHEN** workspace update evaluates a workflow skill directory for removal -- **THEN** OpenSpec SHALL treat it as OpenSpec-managed only when the directory name matches a known generated workflow skill directory and its `SKILL.md` contains OpenSpec generated metadata -- **AND** generated metadata SHALL include the `generatedBy` marker written by OpenSpec skill generation -- **AND** OpenSpec SHALL not remove directories that are missing the generated metadata, even when their names match known workflow skill directory names - -#### Scenario: Updating profile-selected workflows -- **GIVEN** global config resolves to a workflow profile -- **WHEN** workspace update refreshes workspace-local skills -- **THEN** OpenSpec SHALL sync the workspace-local skill workflow set to the workflows selected by that profile -- **AND** deselected workflow skill directories SHALL be removed only when they are known OpenSpec-managed workflow skill directories -- **AND** it SHALL update the last applied workflow IDs used for drift detection - -#### Scenario: Ignoring command delivery for workspace update -- **GIVEN** global config delivery is `commands` or `both` -- **WHEN** workspace update refreshes workspace-local skills -- **THEN** OpenSpec SHALL still update workspace-local skills only -- **AND** it SHALL not generate slash command files or global command files - -#### Scenario: Removing only managed skill directories -- **WHEN** workspace update removes skills for an unselected agent -- **THEN** OpenSpec SHALL remove only known OpenSpec-managed workflow skill directories -- **AND** it SHALL preserve unrelated files in the agent directory - -#### Scenario: Updating stored agent selection by flag -- **WHEN** workspace update receives `--tools <ids>` or `--tools none` -- **THEN** OpenSpec SHALL replace the stored workspace-local selected agent list with that selection -- **AND** future workspace updates without `--tools` SHALL use the stored selection - -#### Scenario: Non-interactive update tool selection -- **WHEN** workspace update receives `--tools all`, `--tools none`, or `--tools <ids>` -- **THEN** OpenSpec SHALL update workspace agent skills using that selected tool set -- **AND** it SHALL avoid prompting for agent selection - -#### Scenario: Non-interactive update without tool selection -- **GIVEN** workspace-local selected agents are stored -- **WHEN** non-interactive workspace update omits `--tools` -- **THEN** OpenSpec SHALL refresh the stored selected agents using the active global profile -- **AND** it SHALL avoid prompting for agent selection - -#### Scenario: Non-interactive update without stored selection -- **GIVEN** no workspace-local selected agents are stored -- **WHEN** non-interactive workspace update omits `--tools` -- **THEN** OpenSpec SHALL complete without installing agent skills -- **AND** it SHALL report a no-op with guidance to pass `--tools` - -#### Scenario: Reporting workspace skill drift -- **GIVEN** workspace-local skill state records last applied workflow IDs -- **AND** the active global profile resolves to a different workflow set -- **WHEN** OpenSpec reports workspace skill state -- **THEN** it SHALL report that workspace-local skills are out of sync with the global profile -- **AND** it SHALL suggest `openspec workspace update` - -#### Scenario: Reporting clean workspace skill sync -- **GIVEN** workspace-local skill state matches the active global profile and selected agents -- **WHEN** OpenSpec reports workspace skill state -- **THEN** it SHALL not report profile drift - -#### Scenario: Reporting workspace skill update results -- **WHEN** workspace update changes agent skill state -- **THEN** OpenSpec SHALL report which agents were refreshed, added, removed, skipped, or failed - -#### Scenario: Reporting workspace update results in JSON output -- **WHEN** workspace update runs with JSON output enabled -- **THEN** OpenSpec SHALL include refreshed, added, removed, skipped, or failed skill results in machine-readable output - -### Requirement: Workspace skill update surface is documented -OpenSpec SHALL expose workspace skill setup/update behavior in user-facing command surfaces. - -#### Scenario: Workspace update appears in help -- **WHEN** a user runs `openspec workspace --help` -- **THEN** OpenSpec SHALL list `workspace update` -- **AND** it SHALL describe it as refreshing workspace-local agent skills - -#### Scenario: Workspace update options appear in help -- **WHEN** a user runs `openspec workspace update --help` -- **THEN** OpenSpec SHALL document workspace selection options -- **AND** it SHALL document `--tools all|none|<ids>` -- **AND** it SHALL state that global profile selects workflows and `--tools` selects agents - -#### Scenario: Workspace update appears in completions -- **WHEN** shell completions are generated -- **THEN** the workspace command registry SHALL include `workspace update` -- **AND** it SHALL include relevant options such as `--workspace`, `--tools`, `--json`, and `--no-interactive` diff --git a/openspec/specs/workspace-open/spec.md b/openspec/specs/workspace-open/spec.md deleted file mode 100644 index 38263c814..000000000 --- a/openspec/specs/workspace-open/spec.md +++ /dev/null @@ -1,205 +0,0 @@ -# workspace-open Specification - -## Purpose -Define how OpenSpec opens a workspace working set through a selected agent or -VS Code editor, including workspace selection, opener resolution, launch -behavior, linked path visibility, and durable workspace guidance. - -## Requirements -### Requirement: Workspace Open Command -OpenSpec SHALL provide a `workspace open` command that opens an OpenSpec workspace working set through an agent or VS Code editor. - -#### Scenario: Opening the current workspace -- **GIVEN** the command runs from inside an OpenSpec workspace -- **WHEN** the user runs `openspec workspace open` -- **THEN** OpenSpec SHALL open that current workspace -- **AND** it SHALL use the selected opener for that workspace - -#### Scenario: Opening a named workspace -- **GIVEN** a workspace named `platform` is known locally -- **WHEN** the user runs `openspec workspace open platform` -- **THEN** OpenSpec SHALL open the `platform` workspace - -#### Scenario: Opening a named workspace with the selection flag -- **GIVEN** a workspace named `platform` is known locally -- **WHEN** the user runs `openspec workspace open --workspace platform` -- **THEN** OpenSpec SHALL open the `platform` workspace - -#### Scenario: Conflicting workspace selectors -- **GIVEN** workspaces named `platform` and `checkout` are known locally -- **WHEN** the user runs `openspec workspace open platform --workspace checkout` -- **THEN** OpenSpec SHALL fail with a clear conflict error -- **AND** the error SHALL name both conflicting selectors - -#### Scenario: Handling unsupported preview and JSON flags -- **WHEN** the user runs `openspec workspace open` with `--prepare-only` or `--json` -- **THEN** OpenSpec SHALL fail with a clear error that the root workspace open surface supports launching through a selected opener -- **AND** the error SHALL direct preview or machine-readable context needs to a future context/query surface - -#### Scenario: Handling change-scoped open before workspace planning -- **WHEN** the user runs `openspec workspace open --change <id>` -- **THEN** OpenSpec SHALL fail with a clear error that this slice supports root workspace open -- **AND** the error SHALL direct change-scoped open behavior to future workspace change planning - -### Requirement: Workspace Selection For Open -OpenSpec SHALL resolve the workspace to open using current workspace context, local registry state, and interactive selection. - -#### Scenario: Current workspace wins -- **GIVEN** the command runs from a workspace folder or one of its subdirectories -- **AND** no workspace name is provided -- **WHEN** the user runs `openspec workspace open` -- **THEN** OpenSpec SHALL open the current workspace - -#### Scenario: Auto-selecting the only known workspace -- **GIVEN** the command runs outside a workspace -- **AND** exactly one workspace is known locally -- **WHEN** the user runs `openspec workspace open` -- **THEN** OpenSpec SHALL open that known workspace directly - -#### Scenario: Picking from multiple workspaces -- **GIVEN** the command runs outside a workspace -- **AND** multiple workspaces are known locally -- **AND** the terminal is interactive -- **WHEN** the user runs `openspec workspace open` -- **THEN** OpenSpec SHALL present a picker with workspace names and locations -- **AND** it SHALL open the workspace the user selects - -#### Scenario: Non-interactive ambiguous selection -- **GIVEN** the command runs outside a workspace -- **AND** multiple workspaces are known locally -- **AND** the terminal is non-interactive -- **WHEN** the user runs `openspec workspace open` -- **THEN** OpenSpec SHALL fail with a clear message listing the known workspace names -- **AND** it SHALL ask the user to pass a workspace name - -#### Scenario: No known workspace -- **GIVEN** the command runs outside a workspace -- **AND** no workspaces are known locally -- **WHEN** the user runs `openspec workspace open` -- **THEN** OpenSpec SHALL fail with a clear message -- **AND** it SHALL suggest running `openspec workspace setup` - -### Requirement: Opener Resolution -OpenSpec SHALL resolve the opener from command overrides, workspace-local preference, or an interactive prompt. - -#### Scenario: Conflicting opener overrides -- **WHEN** the user runs `openspec workspace open --agent codex --editor` -- **THEN** OpenSpec SHALL fail with a clear conflict error naming `--agent` and `--editor` -- **AND** it SHALL avoid launching any opener -- **AND** it SHALL leave the stored preferred opener unchanged - -#### Scenario: Using the stored preferred opener -- **GIVEN** the workspace has a machine-local preferred opener -- **WHEN** the user runs `openspec workspace open` using default opener resolution -- **THEN** OpenSpec SHALL use the stored preferred opener - -#### Scenario: Overriding with an agent for one session -- **GIVEN** the workspace has a stored preferred opener -- **WHEN** the user runs `openspec workspace open --agent codex` -- **THEN** OpenSpec SHALL use Codex for that open command -- **AND** it SHALL leave the stored preferred opener unchanged - -#### Scenario: Overriding with VS Code editor for one session -- **GIVEN** the workspace has a stored preferred opener -- **WHEN** the user runs `openspec workspace open --editor` -- **THEN** OpenSpec SHALL open the workspace in VS Code editor mode -- **AND** it SHALL leave the stored preferred opener unchanged - -#### Scenario: Prompting when no opener is stored -- **GIVEN** the workspace has no stored preferred opener -- **AND** the terminal is interactive -- **WHEN** the user runs `openspec workspace open` using default opener resolution -- **THEN** OpenSpec SHALL prompt the user to choose an opener -- **AND** it SHALL only offer openers with detected executables - -#### Scenario: Failing when no opener can be prompted -- **GIVEN** the workspace has no stored preferred opener -- **AND** the terminal is interactive -- **AND** no supported opener executable is available on `PATH` -- **WHEN** the user runs `openspec workspace open` using default opener resolution -- **THEN** OpenSpec SHALL fail with a clear message that no supported opener is available -- **AND** it SHALL avoid prompting with unlaunchable choices - -#### Scenario: Failing when no opener is stored in non-interactive mode -- **GIVEN** the workspace has no stored preferred opener -- **AND** the terminal is non-interactive -- **WHEN** the user runs `openspec workspace open` using default opener resolution -- **THEN** OpenSpec SHALL fail with a clear message -- **AND** it SHALL ask the user to pass `--agent <tool>` or `--editor` - -### Requirement: Opener Launch Behavior -OpenSpec SHALL launch the selected opener using existing workspace files and linked path state. - -#### Scenario: Opening VS Code editor -- **GIVEN** the user selected the VS Code editor opener -- **WHEN** `code` is available on `PATH` -- **THEN** OpenSpec SHALL open the workspace's maintained `.code-workspace` file with VS Code - -#### Scenario: Opening GitHub Copilot in VS Code -- **GIVEN** the user selected `--agent github-copilot` -- **WHEN** `code` is available on `PATH` -- **THEN** OpenSpec SHALL open the workspace's maintained `.code-workspace` file with VS Code -- **AND** it SHALL treat this as the VS Code Copilot experience - -#### Scenario: Opening Codex -- **GIVEN** the user selected `--agent codex` -- **WHEN** `codex` is available on `PATH` -- **THEN** OpenSpec SHALL launch Codex from the workspace root -- **AND** it SHALL attach every linked repo or folder with a valid local path using Codex's supported directory attachment mechanism - -#### Scenario: Opening Claude -- **GIVEN** the user selected `--agent claude` -- **WHEN** `claude` is available on `PATH` -- **THEN** OpenSpec SHALL launch Claude from the workspace root -- **AND** it SHALL attach every linked repo or folder with a valid local path using Claude's supported directory attachment mechanism - -#### Scenario: Missing opener executable -- **GIVEN** the selected opener requires an executable that is not available on `PATH` -- **WHEN** the user runs `openspec workspace open` -- **THEN** OpenSpec SHALL fail with a clear error naming the missing executable -- **AND** it SHALL keep the selected opener as the required opener - -#### Scenario: Missing VS Code executable -- **GIVEN** the selected opener is VS Code editor or GitHub Copilot in VS Code -- **AND** `code` is not available on `PATH` -- **WHEN** the user runs `openspec workspace open` -- **THEN** OpenSpec SHALL fail with a clear error naming `code` -- **AND** it SHALL include the maintained `.code-workspace` path so the user can open it manually - -### Requirement: Linked Working Set Visibility -OpenSpec SHALL make linked repos and folders visible for workspace exploration and planning before change creation. - -#### Scenario: Attaching valid linked paths -- **GIVEN** a workspace has linked repos or folders with valid local paths -- **WHEN** the user opens the workspace through an opener that supports linked directory attachment -- **THEN** OpenSpec SHALL include every valid linked path in the opened working set -- **AND** it SHALL support opening before a workspace change exists - -#### Scenario: Skipping broken linked paths -- **GIVEN** a workspace has at least one linked path that is missing or not recorded locally -- **WHEN** the user opens the workspace -- **THEN** OpenSpec SHALL skip the broken linked path -- **AND** it SHALL report that the path was skipped with `openspec workspace doctor` as the repair path -- **AND** it SHALL continue opening the workspace when the selected opener itself is available - -#### Scenario: Opening links with repo-local OpenSpec state absent -- **GIVEN** a linked repo or folder has a valid local path and repo-local `openspec/` state is absent -- **WHEN** the user opens the workspace -- **THEN** OpenSpec SHALL include that link when its local path is valid -- **AND** it SHALL treat missing repo-local OpenSpec state as an implementation-readiness concern for later workflows while continuing open - -### Requirement: Workspace Open Guidance -OpenSpec SHALL use durable workspace guidance as the primary context source for root workspace open. - -#### Scenario: Launching with existing workspace guidance -- **GIVEN** the workspace has OpenSpec-managed guidance in `AGENTS.md` -- **WHEN** the user opens the workspace -- **THEN** OpenSpec SHALL refresh the maintained `.code-workspace` from current linked path state -- **AND** it SHALL launch the selected opener against refreshed workspace files -- **AND** it SHALL use durable workspace files as the primary workspace-open artifact - -#### Scenario: Minimal required launch prompt -- **GIVEN** an opener requires an initial prompt argument -- **WHEN** OpenSpec launches that opener -- **THEN** OpenSpec SHALL use a minimal prompt such as `Open this OpenSpec workspace.` -- **AND** durable workspace rules SHALL remain in workspace files diff --git a/openspec/work/AGENTS.md b/openspec/work/AGENTS.md new file mode 100644 index 000000000..01da7ee7f --- /dev/null +++ b/openspec/work/AGENTS.md @@ -0,0 +1,35 @@ +# Agent Guidance For `/work` + +When working in this directory, use a product-facing lens first. + +Start from how the work is experienced by users, not from the internal command +or file structure. In this product there are two users: + +- Humans: they usually do OpenSpec work by prompting agents. They may run shell + commands for interactive setup or one-off actions, but prompts are the normal + interface. +- Agents: they need clear intent, discoverable state, unambiguous next actions, + and enough structured output to act safely. + +Good human UX is usually good agent UX. A flow that is easy for a human to ask +for and understand is usually easier for an agent to execute, verify, and +explain. + +For roadmap or slice exploration: + +- Describe the user-facing flow before the internal implementation. +- Ask what the human sees, asks for, approves, or corrects. +- Ask what the agent must discover, decide, execute, and report back. +- Ground reasoning in the current repo behavior before proposing new shape. +- Treat shell commands as supporting mechanics, not the primary product story. +- Prefer concrete workflows over abstract model language. + +When an answer gets confusing, reframe it as: + +```text +What does the human want? +What does the agent need to know? +Where does the work live? +What changes on disk? +How does the user know it worked? +``` diff --git a/openspec/work/README.md b/openspec/work/README.md new file mode 100644 index 000000000..4fedc4864 --- /dev/null +++ b/openspec/work/README.md @@ -0,0 +1,87 @@ +# OpenSpec Work + +This directory is an experimental home for Git-native work artifacts. + +The current experiment separates the work model into four layers: + +```text +goal -> roadmap -> slice -> result +``` + +- `goal.md` describes the destination: what we are trying to make true and why. +- `roadmap.md` describes the current path toward that goal. It is expected to + change as implementation reveals better sequencing. +- `slices/<id>/spec.md` describes one small desired outcome. +- `slices/<id>/plan.md` describes how that slice will be implemented and + verified. +- `slices/<id>/result.md` records what actually happened and the evidence that + the slice passed, failed, or needs follow-up. +- `slices/<id>/log.md` is optional. Use it only when important changes need a + short explanation of what changed, why, and what downstream artifacts were + affected. + +The goal is to keep high-level work lightweight while still giving agents and +humans enough structure to move one slice at a time. + +Rule of thumb: + +```text +spec.md says what must be true. +plan.md says how we intend to get there. +result.md says what actually happened. +``` + +## Shape + +```text +openspec/work/ + README.md + <work-id>/ + goal.md + roadmap.md + slices/ + <slice-id>/ + spec.md + plan.md + result.md + log.md +``` + +## Workflow + +Start with the goal, then maintain a loose roadmap. The roadmap is a living +sequence of likely slices, not a promise to execute everything in order. + +For each slice: + +1. Explore and interview until the slice has a useful `spec.md`. +2. Generate `plan.md` only when the spec is clear enough to implement. +3. Execute the plan. +4. Record proof, verification output, and follow-ups in `result.md`. +5. Update `roadmap.md` when the result changes the path forward. + +## Revision Rules + +Edit `spec.md` when the desired slice outcome changes. + +Edit `plan.md` when the implementation path changes but the slice outcome is +still the same. + +Create or update `result.md` when implementation or verification has happened. +Do not use it as the source of truth for current intent. + +Add `log.md` entries when a meaningful pivot would be hard to understand from +the final files alone. + +Create a new slice when the new work can be accepted, scheduled, verified, or +shipped independently. + +## Compatibility + +This directory is experimental. Current OpenSpec CLI validation, archive, and +spec update behavior still centers on `openspec/changes/` and +`openspec/specs/`. + +Use `/work` to coordinate and learn. When a slice needs today's executable +OpenSpec lifecycle, project that slice into a normal `openspec/changes/<id>/` +artifact until `/work` has first-class CLI support. diff --git a/openspec/work/simplify-context-and-workspace-model/capstone/gauntlet.md b/openspec/work/simplify-context-and-workspace-model/capstone/gauntlet.md new file mode 100644 index 000000000..1e1eaae9d --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/capstone/gauntlet.md @@ -0,0 +1,77 @@ +# Capstone Whole-Delta Review Gauntlet (6.1) — Findings Ledger + +Run 2026-06-11 over `origin/main...HEAD` with four mechanisms: +`/code-review` at max effort (3 finder fan-outs + a 12-candidate +verification pass + gap sweep), a 32-agent adversarial Workflow +(six lenses × refute-style verification + completeness critic), a +codex whole-delta review, and the audits' queued items. Every finding +below was CONFIRMED (most live-reproduced). Status column tracks the +fix round. + +## P1 (2) + +| # | Finding | Status | +|---|---------|--------| +| G1 | The recommended `~/openspec/<id>` layout makes `$HOME` a "nearest" root: any `openspec/` DIRECTORY counts in the walk, so every lifecycle command under the home tree silently lands planning files in `$HOME/openspec/changes/` and the registered-store hint never fires. | **fixed** (37ad867; live re-verified) | +| G2 | `status`/`instructions` `--json` thrown errors emit NO JSON document (plus a stray blank line on stdout); part of the broader JSON-failure-contract family. | **fixed** (37ad867; live re-verified) | + +## P2 (13) + +| # | Finding | Status | +|---|---------|--------| +| G3 | The JSON failure contract family: `show`/`validate` unknown item, `list` (no failurePayload AND the changes-dir throw), `store <unknown subcommand>`, all exit 1 with zero JSON on stdout; agent-contract.md currently claims this fixed. | **fixed** (37ad867; live re-verified) | +| G4 | `doctor`/`context` miss the shared `--store-path` rejection seam (Commander unknown-option instead of the typed `store_path_not_supported`). | **fixed** (37ad867; live re-verified) | +| G5 | doctor's unguarded `gitOriginUrl(root.path)` walks UP: a non-repo store nested in another checkout reports the enclosing repo's origin + spurious `store_remote_divergence` (live-reproduced; violates operations.ts's own documented guard). | **fixed** (37ad867; live re-verified) | +| G6 | Stale registry lock = permanent `store_registry_busy` with a fix that can never work; Ctrl-C during `store remove` (which holds the lock across a recursive rm) orphans it; doctor is blind to it; EACCES also misreported as busy. | **fixed** (37ad867; live re-verified) | +| G7 | Config-only roots: `new change` creates the change but never completes the shape (the scaffold guard fires only when `openspec/` is wholly absent) — doctor immediately calls the root the tool just wrote to unhealthy. | **fixed** (37ad867; live re-verified) | +| G8 | Prompt-injection surface: target `remote` strings, referenced-store spec ids (raw directory names), and Purpose summaries render verbatim into `<referenced_stores>`/instruction output — newlines/control chars from a hostile clone can forge instruction lines. | **fixed** (37ad867; live re-verified) | +| G9 | Five more accepted specs REQUIRE deleted behavior (artifact-graph, schema-resolution, change-creation P2; cli-update, openspec-conventions P3) — the L2 excision covered only cli-config/cli-artifact-workflow. | **fixed** (37ad867; live re-verified) | +| G10 | Generated workflow skills still instruct agents to parse `planningHome` from status JSON surfaces that changed (archive-change template). | **fixed** (37ad867; live re-verified) | +| G11 | The generated zsh completion script is syntactically invalid — the `--store` description's apostrophe ("you've") breaks zsh quoting (completeness critic, live). | **fixed** (37ad867; live re-verified) | +| G12 | `store remove` deletes the store folder BEFORE the registry write commits — a failed commit leaves a phantom registration pointing at deleted files. | **fixed** (37ad867; live re-verified) | +| G13 | Setup's prepare/execute split: directory policy (non-empty, nested-git) is asserted only at prepare; the interactive confirm gap is unbounded, and the rollback's `kind === 'missing'` branch recursively deletes content setup never created (live-reproduced both sides). | **fixed** (37ad867; live re-verified) | +| G14 | Orphaned fresh `.git` after a failed initial commit (cleanup nested under `createdPaths.length > 0`); a rerun then registers a commitless store — the exact empty-clone state the slice exists to prevent. | **fixed** (37ad867; live re-verified) | +| G15 | Registry rollback race: `commitStoreRegistration`'s catch deletes store metadata outside the lock and can delete metadata a concurrently committed registration depends on (live-reproduced; P3→P2 borderline, queued with G12/G13). | **fixed** (37ad867; live re-verified) | + +## P3 (taken-cheap vs recorded) + +Queued for the fix round (cheap, mechanical): fence-marker desync in +purpose extraction; stat-EACCES-as-absent in `pathIsFile` (registered +stores reported unregistered with clone fixes); `existsSync` vs +`isDirectory` in the stale-target sweep (a FILE at a mapped path +presents available and lands in the code-workspace); the scaffolded +config baking a one-off `--schema` as the root default; `list --json` +compact-vs-pretty inconsistency; the declared-pointer repo-id fix text; +the root-relative "Created change at" print (absolute path instead); +write-side cross-section overlap check; docs fixes (affected_areas +wording, `--remote` in the setup options table, `vibe` in --tools, +the stale `list` output example); the dead-code P3 queue from the +technical audits (apply fallback + resolveCurrentPlanningHomeSync, +resolveRegisteredStore, references barrel line, PlanningHomeSummary, +parseJson consolidation). + +Recorded as known gaps for the report (not fixed this round, mapped to +Later Ideas / release notes): registry fsync durability; the reference +index byte budget growing linearly past 50KB at extreme reference +counts; Windows clone-recipe quoting (single quotes vs cmd.exe); +`view`/`templates`/`schemas`/deprecated noun forms remaining cwd-based +(documented in the agent contract); completions enumerating ids from +bare cwd; the cross-platform CI matrix not run on this branch; +semver/changeset planning for the deleted CLI surface; README not yet +describing the store model (L1 — public concept docs rewrite). + +## Verdicts + +- codex: FIX-FIRST (2 P2, 1 P3 — all in the table above). +- Workflow (32 agents, 6 lenses, refute-style verification): 25 + confirmed findings + 7 completeness gaps — all triaged above. +- /code-review max: 12/12 candidates CONFIRMED by the verification + pass (3 cross-finding violations of the code's own documented + invariants) + 6 gap-sweep finds — all triaged above. + +All 15 P1/P2 findings were fixed in commit 37ad867 and re-verified by +live probes (the JSON contract codes, the --store-path seam, the +stale-lock steal, the config-only scaffold completion, the phantom-root +regression test) plus the full suite (97 files, 1717 tests). The +queued-cheap P3 set landed in the same commit; the recorded-for-report +items appear in the release-readiness report's known gaps. diff --git a/openspec/work/simplify-context-and-workspace-model/capstone/journeys.md b/openspec/work/simplify-context-and-workspace-model/capstone/journeys.md new file mode 100644 index 000000000..fffff5b64 --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/capstone/journeys.md @@ -0,0 +1,57 @@ +# Capstone Persona Journeys (6.1) — Results + +Executed 2026-06-11 against the branch head. All four pass. + +## Journey 1 — Fresh team: PASS (standing e2e) + +`test/cli-e2e/store-lifecycle.test.ts` (the 1.3 journey, maintained +through the rename and deletions): machine A creates a store via +`store setup` (committed, clonable), works a change through archive +from a pointer project repo, the project repo stays byte-identical; +machine B clones, registers without ceremony, reads promoted specs. +Green in every full-suite run (part of the 1714). + +## Journey 2 — Layered PM-to-dev flow: PASS (new e2e) + +`test/cli-e2e/capstone-journeys.test.ts`: requirements live in a +`product-requirements` store; the app repo has its OWN root and a +`references:` declaration. The agent discovers the relationship from +config alone (`openspec context --json` surfaces the member with its +fetch recipe), follows the recipe verbatim to cite the upstream spec +(`openspec show billing-rules --type spec --store product-requirements`), +and the low-level design change lands in the app repo's root while the +store stays read-only throughout. + +## Journey 3 — Externalized planning: PASS (new e2e) + +Same file: a code repo with NO local root and only `store: team-planning` +in its config runs the entire lifecycle — new change, status, +instructions for every artifact, archive — with ZERO `--store` flags. +The change lives and archives in the store; the code repo never grows +planning state (its `openspec/` still holds only `config.yaml` at the +end). + +## Journey 4 — Cold-start agent: PASS (headless dogfood) + +A fresh codex headless session (gpt-5.5, medium reasoning) in a scratch +world: a `billing-app` TypeScript project, the `openspec` CLI on PATH, +isolated XDG state, and ONLY the vague prompt "set up planning in a +separate repo for this project... discover how it works from its +--help output." No insider knowledge. + +The agent produced the full intended topology unprompted: + +- `openspec store setup billing-app-planning` → a standalone planning + repo with specs/changes/config/store metadata, its own git history; +- `targets: [billing-app]` declared in the STORE config (the right + side); +- the pointer `store: billing-app-planning` written into the project + repo's `openspec/config.yaml`; +- `openspec repo register` mapping `billing-app` to its checkout; +- self-verified with `openspec doctor`, `openspec context`, and + `openspec validate --all --store billing-app-planning`. + +Independently verified after the run: `openspec context --json` from +inside `billing-app` resolves the declared root and lists the mapped +target with empty status. The guidance surfaces are sufficient for a +zero-context agent to assemble the model the roadmap intends. diff --git a/openspec/work/simplify-context-and-workspace-model/capstone/release-readiness.md b/openspec/work/simplify-context-and-workspace-model/capstone/release-readiness.md new file mode 100644 index 000000000..797421cf5 --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/capstone/release-readiness.md @@ -0,0 +1,124 @@ +# Release-Readiness Report — simplify-context-and-workspace-model + +Committed 2026-06-11 on `codex/store-root-parity` (merge to `main` +deliberately deferred per the run's standing instruction). This is the +6.1 capstone's final deliverable: the product, proven as one thing. + +**Verdict: release-ready, with the known gaps below mapped to Later +Ideas. No open P1/P2 findings anywhere in the capstone ledgers.** + +## The five-minute new-user story + +You install OpenSpec and run two commands: + +```bash +openspec store setup team-plans --path ~/openspec/team-plans +openspec new change my-first-change --store team-plans +``` + +That is the whole journey to a working, store-scoped change — two +commands, two concepts (a **store** is a standalone planning repo +registered on your machine; a **change** is the unit of work), and +every step's output prints the exact next command. From there the +lifecycle is `status` → `instructions` per artifact → `archive`, each +carrying `--store` in its own hints. Your code repos connect with one +line (`store: team-plans` in `openspec/config.yaml`) after which the +lifecycle works from inside them with zero flags; stores can declare +`references:` (read-only upstream context with fetch recipes) and +`targets:` (the repos the work is about, mapped to local checkouts via +`openspec repo register`). `openspec doctor` answers "is my setup +healthy"; `openspec context` answers "what is my working set" — and +can emit a `.code-workspace` file. Everything has `--json` with a +documented agent contract (`docs/agent-contract.md`). + +This story is not aspirational: journey 4 ran it cold — a headless +agent with no insider knowledge, a vague prompt, and `--help` output +assembled the full topology (store, pointer, targets, repo map) and +self-verified with doctor/context/validate. + +## What this roadmap shipped (the sum) + +- **One root model.** A single resolution precedence (explicit + `--store` → nearest qualifying root → declared pointer → + hint/implicit) implemented exactly once and verified hold across all + nine command entry points. Stores are standalone OpenSpec repos in a + typed local registry; store and repo ids share one kebab namespace + with bidirectional collision errors. +- **Declared relationships, no machinery.** `references:` and + `targets:` are declarations; nothing clones, syncs, or enforces + edit boundaries. Unresolvable pieces degrade to warnings with + pasteable fixes. +- **Two read-only composition surfaces.** `doctor` (relationship + health, four separated categories, findings exit 0) and `context` + (the working set as agent brief / human listing / editor view). +- **The old model deleted, not hidden.** The workspace/initiative + command groups, state model, schema, accepted specs, and template + guidance are gone (−12,903 lines in the first tranche; src is net + **−4,478** lines vs `origin/main` across the whole delta). + +## Audit results (full records in this folder) + +- **Persona journeys** (`journeys.md`): all four pass — fresh team + (standing e2e), layered PM-to-dev (new e2e), externalized planning + (new e2e, zero `--store` flags), cold-start agent (live headless + dogfood). +- **Usability** (`usability-audits.md`): 55-wrong-turn error catalog + (all failures fixed); vocabulary sweep clean across src, docs, and + generated guidance; time-to-first-success measured live at 2 + commands / 2 concepts. +- **Technical** (`technical-audits.md`): single-resolver and + dependency-direction invariants HOLD; module sizes bounded; the + agent contract documented and verified (`docs/agent-contract.md`); + dead code reduced to a recorded P3 queue. +- **Whole-delta gauntlet** (`gauntlet.md`): four mechanisms + (/code-review max, a 32-agent adversarial Workflow, codex, + completeness critic); 2 P1 + 13 P2 findings, **all fixed in 37ad867 + and live re-verified**, plus the cheap P3 set. Final suite: 97 + files, 1,717 tests green; all 36 accepted specs validate. + +## The autonomous-decision ledger + +Every `Decided autonomously (review me)` entry lives in the roadmap +changelog (18 marked entries plus per-slice recorded amendments). The +ones that shape the product: + +1. The store registry gained a typed `repos:` section (3.5) sharing the + store id namespace. +2. Declared-pointer roots resolve through the same store resolver as + `--store` (3.2); corrupt store metadata stays a resolution failure — + no doctor-only resolution fork (3.6 amendment). +3. `openspec doctor` is top-level and root-scoped; health findings of + any severity exit 0 (3.6). +4. 4.1's surface is `openspec context` (not `view`/`open`); opening is + REPLACED by emitted artifacts — no editor launching; `binding.ts` + and the template guards died with the state model (widened + carve-outs). +5. The Phase 5 remainder deleted the workspace-planning schema, the + four beta change folders, and the four wholly-workspace accepted + specs; mixed specs got bounded excisions (L2 decided). +6. Capstone fixes: the nearest walk now requires a QUALIFYING + `openspec/` (planning shape or config); every `--json` failure + emits one status document; `planningHome` was restored to status + JSON as a published agent contract (reversing a planned + dead-code collapse — `PlanningHomeSummary` is live again); + `store remove` commits the registry removal before deleting files; + prompt-render boundaries sanitize cloned content. + +## Known gaps, mapped + +| Gap | Disposition | +|---|---| +| README/public concept docs don't yet tell the store story | **L1** (rewrite public docs after behavior is solid) — the CLI reference (`docs/cli.md`) and agent contract are current | +| Richer cross-repo context (multi-store fetch ergonomics, reference index growth past ~150 references) | **L3** | +| `view`, `templates`, `schemas`, and deprecated noun forms remain cwd-based without `--store` | Documented in the agent contract; candidates for L9-grade fixes if they matter to the simple flow | +| JSON key-casing split (store-family snake_case vs workflow-family camelCase) and envelope-type unification | Recorded in the agent contract; renaming published keys is a product decision for the first versioned release | +| Registry fsync durability; Windows clone-recipe quoting; completions enumerating ids from bare cwd | Recorded engineering notes (gauntlet P3 ledger) — none block a first user on a POSIX machine | +| Cross-platform CI matrix not run on this branch; no semver/changeset plan for the deleted CLI surface | Release-process work for the merge-to-main moment, which this run deliberately does not perform | +| `parseJson` test-helper consolidation and sibling dead-code P3s | Recorded queue (`technical-audits.md`) | + +## What remains before users + +One action: merge `codex/store-root-parity` to `main` (every roadmap +box except "Merged to main" is ticked) and run the release process +(CI matrix, version, changelog). The branch holds 80+ commits, each +with a green full suite at commit time. diff --git a/openspec/work/simplify-context-and-workspace-model/capstone/technical-audits.md b/openspec/work/simplify-context-and-workspace-model/capstone/technical-audits.md new file mode 100644 index 000000000..6a535c029 --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/capstone/technical-audits.md @@ -0,0 +1,78 @@ +# Capstone Technical Audits (6.1) — Results + +Executed 2026-06-11 against the branch head. + +## Single-resolver invariant: HOLDS + +Root-selection precedence (explicit `--store` → nearest → declared +pointer → hint/implicit) has exactly one implementation +(`resolveOpenSpecRoot`, root-selection.ts). All nine resolution entry +points (list/show/validate/status/instructions×2/new-change/archive/ +doctor/context) route through it; doctor and init's extra walks are +post-resolution diagnostics and scaffold guards, never resolution. One +latent fork found and queued: `generateApplyInstructions`' unreachable +`resolveCurrentPlanningHomeSync` fallback (its only caller always +passes the resolved home) — deletion queued with the function itself. +Deprecated noun-forms (`change`/`spec`) are cwd-based with no walk — +documented, not forks. + +## Dependency direction: HOLDS + +Zero `core → commands/cli` imports; zero `commands → cli` imports; +templates reach nothing. The only cross-link is the package entry +(`src/index.ts`) re-exporting both — top-level composition. + +## Dead code: no P2s; five P3s and four notes, queued or recorded + +P3 queue (fixed in the gauntlet fix round where cheap): +1. The unreachable apply-instructions fallback + + `resolveCurrentPlanningHomeSync` (test-only after it). +2. `resolveRegisteredStore` (registry.ts) — test-only, subsumed by + root-selection, and its fix text references the removed + `--store-path` flag. +3. The references barrel line (`core/index.ts`) — zero consumers; the + sibling modules are deliberately not barreled. +4. `PlanningHomeSummary` — field-identical to `PlanningHome` post-4.1; + identity wrapper collapse. +5. `parseJson` test-helper ×11 — consolidate the enriched variant into + `run-cli.ts`. + +Notes (recorded, no action): `mkdir` fixture copies ×8 (marginal); +the `~/openspec/<id>` checkout convention is 1 computed + 5 prose +sites (constant would pin it); `ext::` transport — zero occurrences, +the shell-safe gate + `--` + trust boundary (team-committed +store.yaml) hold, a threat-model comment at the gate queued; +`registerStore`/`isStoreRoot` are test-only exports (sanctioned +fixture APIs, recorded). + +## Module sizes: bounded + +Largest src module is `store/operations.ts` at 1,160 lines; only four +files exceed 800 (operations, schema command, store command, init). +src total: 30,336 lines. + +## Agent-contract inventory: docs/agent-contract.md (committed) + +Every JSON shape, the diagnostic envelope, the failure payloads, the +exit-code contract, and a 100+-code catalog — verified against +emitting code. Fourteen consistency findings recorded in the document; +one is gauntlet-grade (P2): in `--json` mode, unknown/ambiguous-item +paths in `validate`/`show` and thrown errors in `status`/ +`instructions` print stderr only and exit 1 WITHOUT a JSON document — +agents parsing stdout get nothing. Queued for the gauntlet fix round. +The rest (severity low/medium: snake_case vs camelCase split between +store-family and workflow-family payloads, the four parallel envelope +type declarations, `status` key collision in `list`, fallback-code +suffix naming, unversioned payloads, schemas/templates ignoring root +selection) are recorded as known gaps for the report — renaming +published JSON keys is a product decision, not a capstone fix. + +## Net LOC delta vs origin/main: src is net-negative as expected + +- `src/`: **−4,478** net (+7,187 / −11,665) — the Phase 5 deletions + outweigh Phases 3–4's additions. +- `test/`: −325 net (+7,526 / −7,851). +- Whole delta: +27,560 / −22,842 across 213 files; the gross + insertions are dominated by `openspec/work/` planning artifacts + (specs, plans, the roadmap ledger) — process documentation, not + product code. diff --git a/openspec/work/simplify-context-and-workspace-model/capstone/usability-audits.md b/openspec/work/simplify-context-and-workspace-model/capstone/usability-audits.md new file mode 100644 index 000000000..d4367d04f --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/capstone/usability-audits.md @@ -0,0 +1,72 @@ +# Capstone Usability Audits (6.1) — Results + +Executed 2026-06-11 against the branch head. + +## Error-catalog walk: 55 wrong turns, 46 pass, 9 fail + +A live walk of every likely wrong turn on the new paths (13 walk +families, human + JSON surfaces), judged against the bar: actionable, +store-carrying, correct exit code, honest. The resolution-layer +taxonomy held up well — differentiated no-root hints, single-document +JSON failures with code/fix fields, shell-parseable clone fixes, +namespace-collision messages in both directions. + +Failures (fixed before the release-readiness report; the fix round is +the next capstone commit): + +- **F1 (P1)** Unparseable `openspec/config.yaml` in a real root dumps + a raw YAMLParseError with node_modules stack frames + (`project-config.ts` console.warn passes the error object). +- **F2 (P2)** The corrupt-registry fix never names the registry file — + "Repair or remove the store registry file" with no path, and the + suggested escalation (`store doctor`) dead-ends identically. +- **F3 (P2)** `instructions` under a corrupt registry drops the Fix + line entirely (the ✖ Error surface). +- **F4 (P2)** `validate` failure summaries offer no drill-down command + (nothing carries `--store`). +- **F5 (P2)** Implicit-root scaffolding (`new change` in a bare dir, + non-interactive init) creates a root that doctor immediately calls + unhealthy (no config.yaml/specs/archive) — the trap is the dishonest + half. +- **F6–F9 (P3)** A bare pathless duplicate warning for malformed + pointers on real roots; the pointer-to-unknown-store fix shaped for + the wrong mistake; store-register-at-code-repo fix assumes a store + clone; `archive <nonexistent>` lists no candidates while + `status --change` does. + +Full table preserved in the audit transcript (the gauntlet re-verifies +the fixes). + +## Vocabulary sweep (including docs/cli.md) + +- `context store` compound: zero hits anywhere (enforced by + `test/vocabulary-sweep.test.ts`). +- `workspace`: zero user-facing residue. Remaining src hits are the + `.code-workspace` FILE FORMAT name (the VS Code convention — correct + usage), the `context_file_exists` code, and historical comments. + Generated templates pinned residue-free by the parity test. +- `initiative`: one genuine finding — `ChangeStatus.initiative` + (instruction-loader) still passes a stored legacy initiative link + through to status JSON. Reading legacy metadata is user-data + tolerance (correct); RE-EMITTING it on a user-facing JSON surface is + residue. Queued in the fix round: drop the passthrough, keep the + schema parse tolerance. The `initiative_option_removed` rejection + string is deliberate (the ledger's recorded survivor). +- `docs/cli.md` and README: clean on all three families. + +## Time-to-first-success: 2 commands, 2 concepts + +Measured live from a clean machine state (isolated XDG, no +configuration): + +1. `openspec store setup team-plans --path ~/openspec/team-plans` — + creates the store, registers it, prints the next command. +2. `openspec new change my-first-change --store team-plans` — the + first store-scoped change exists; the output prints the next + command (`status`) with `--store` carried. + +Concepts a new user must hold: **store** (a standalone planning repo +registered on this machine) and **change** (the unit of work). The +root concept stays implicit until multi-root work begins. Every step's +output names the next step — the journey is self-guiding, which the +cold-start dogfood (journey 4) confirmed end-to-end. diff --git a/openspec/work/simplify-context-and-workspace-model/goal.md b/openspec/work/simplify-context-and-workspace-model/goal.md new file mode 100644 index 000000000..fe7c61795 --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/goal.md @@ -0,0 +1,72 @@ +# Simplify Context And Workspace Model Goal + +## Destination + +Reorient the current context-store, initiative, workspace, and repo-local change +direction into a simpler OpenSpec model that is easier to explain, implement, +and dogfood. + +The simplified direction is: + +```text +Specs are what is true. +Work is what is in motion. +``` + +OpenSpec artifacts should live in Git. That Git repo may be the project repo, +a standalone planning repo, or a contracts repo. The product should not require +context stores, workspaces, or another state system as primary user-facing +concepts. + +## Desired Experience + +A human should be able to say: + +```text +OpenSpec can live in this project repo or in its own Git repo. +This work targets these repos. +This project repo's work draws on these planning repos. +This local machine maps those target repos to these checkouts. +``` + +Agents and commands should be able to assemble the relevant OpenSpec root and +target project repos without asking users to understand context-store, +workspace, collection, and repo-local modes as separate product systems. + +## Product Direction + +- Preserve the current `specs/` and `changes/` baseline while the simpler model + is introduced. +- Make the placement choice explicit: in-project OpenSpec or standalone + OpenSpec repo. +- Support layered planning by reference, not redirection: high-level + requirements and design can live in a standalone repo while a project repo + keeps its own OpenSpec root for implementation-level work, drawing on the + standalone repo as declared context. +- Treat target repos as declared targets, not as mandatory lifecycle roots. +- Reduce workspace behavior to local repo mapping and optional focused views. +- Treat the future `work/` layout as a later evolution, not a prerequisite for + making standalone OpenSpec repos useful. + +## Constraints + +- Keep the current `openspec/changes/` and `openspec/specs/` lifecycle working. +- Treat this `/work` folder as an experiment for organizing the reorientation, + not as the implemented product model. +- Avoid reviving context stores or workspaces as primary product nouns. +- Avoid global `decisions.md` and `questions.md` files as the default planning + shape. +- Prefer small, reviewable slices over large roadmap items. +- Promote only the information that needs to guide future slices. + +## Success Signals + +- A fresh agent can understand the active goal and current roadmap by reading + the files in this work directory. +- The old context-store and workspace initiative becomes useful transition + history rather than the active product queue. +- The next product slices are about preserving the baseline, clarifying + placement, supporting standalone OpenSpec repos, and resolving target project + repos. +- The roadmap avoids making future `/work` support block the simpler standalone + OpenSpec repo path. diff --git a/openspec/work/simplify-context-and-workspace-model/roadmap.md b/openspec/work/simplify-context-and-workspace-model/roadmap.md new file mode 100644 index 000000000..d2bb7387f --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/roadmap.md @@ -0,0 +1,2445 @@ +# Simplify Context And Workspace Model Roadmap + +This roadmap is an internal plan for the work described in `goal.md`. + +The goal is simple: + +```text +Specs are what is true. +Work is what is in motion. +``` + +OpenSpec work should live in normal Git files. Those files can live inside the +project repo, or they can live in a separate OpenSpec repo that points at one or +more project repos. + +This roadmap should be readable by someone with no beta context. Each item says: + +- What the user can do. +- Why it matters. +- What changes in commands or files. +- How the user or agent knows it worked. + +This is not public product copy yet. Keep it practical, small, and honest about +what exists. + +## The Story In Plain English + +Today, too much of this area is explained through beta terms: context stores, +initiatives, workspaces, collections, and repo-local modes. + +The simpler product story should become: + +1. OpenSpec can live in this project repo or in its own Git repo. +2. If OpenSpec lives in its own repo, users can register that repo locally. +3. Normal OpenSpec commands can create, read, validate, and archive work in that + selected OpenSpec repo. +4. A project repo with its own OpenSpec root can reference standalone OpenSpec + repos its work draws on, such as high-level requirements from PMs and + architects, without those repos taking over where commands act. +5. Work can say which project repos it targets. +6. The local machine can map those target repos to local checkout paths. +7. An optional assembled context can bring the OpenSpec repo, referenced + repos, and target repos together for an agent session or editor. + +The product should not require users or agents to understand initiatives, +workspace-owned planning, or collection state as the main model. + +## Vocabulary For This Roadmap + +- **OpenSpec root**: the `openspec/` folder with `config.yaml`, `specs/`, and + `changes/`. +- **OpenSpec inside a project repo**: the `openspec/` folder lives inside the + code repo. +- **Standalone OpenSpec repo**: the `openspec/` folder lives in its own Git + repo. +- **Store**: a standalone OpenSpec repo registered on this machine. It has a + thin `.openspec-store/store.yaml` identity file, but the real planning work + lives in normal files under `openspec/`. (Renamed from the beta noun + "context store" on 2026-06-11; the CLI group rename lands in slice 1.4.) +- **Target project repo**: a code repo that the OpenSpec work is about. +- **Reference store**: a standalone OpenSpec repo that a project repo's work + draws on for context (for example PM/architect requirements). A reference + never changes where commands act; it is read as context. +- **Local repo map**: local machine settings that say where target project repos + are checked out. +- **View**: a local convenience for opening the OpenSpec repo and project repos + together. It is not the source of truth. + +## Rules We Should Not Forget + +- Keep the normal `openspec/specs/` and `openspec/changes/` lifecycle working. +- When context stores are used, treat them as standalone OpenSpec repos, not as + a separate planning system. +- References are repo-level config, never per-change lifecycle links. The + moment each change carries a managed link object with status coupling back + to a store, we have reinvented initiatives. +- One change lives in one root. Cross-root edits are two changes; the second + root is reached explicitly with `--store`. +- Do not create new initiative links in the simpler product path. +- Do not create workspace-owned planning state in the simpler product path. +- Do not promise clone, pull, push, sync, branch, worktree, dashboard, apply, + verify, or archive orchestration in these slices. +- Treat old beta files as history unless they block the simpler path. +- Do not rewrite public docs before the behavior is solid. + +## Progress At A Glance + +Use this as the quick "where are we?" view. + +Working branch: all roadmap implementation happens on the single +`codex/store-root-parity` branch (PR #1190), with each slice stacked on the +previous ones. Merge to `main` is deferred until the work is ready to land +as a whole; the "Merged to `main`" checkboxes in each slice stay open until +then and do not gate the next slice. + +Numbered labels are roadmap work item ids. Smaller `Progress` checkboxes inside +an item are status steps for that numbered work item. + +- [x] **Phase 0. Make the active direction easy to find.** + Old beta plans were marked as history, and this `/work` roadmap became the + active direction. +- [ ] **Phase 1. Make a standalone OpenSpec repo useful.** + Slices 1.1–1.4 are implemented with passing tests on the working branch; + only merge to `main` remains. The noun is "store" everywhere (CLI group, + machine tokens, guidance, docs), and a headless agent completes a + store-scoped change from one plain prompt (dogfood transcript in the 1.4 + slice folder). +- [x] **Phase 2. Stop putting new work through initiatives.** + Fully absorbed: 2.1 shipped inside slice 1.2, 2.2 folded into slice 1.4, + and 2.3 folded into item 4.1. No independent work remains here. +- [x] **Phase 3. Say how roots relate: references and targets.** + Complete (merge to `main` pending): references, the declared-store + fallback, canonical remotes, target declarations, the local repo + map, and the `openspec doctor` relationship-health roll-up are all + implemented and tested on the working branch. +- [ ] **Phase 4. Assemble the working context.** + Complete (merge to `main` pending): `openspec context` ships the + assembled working set; the old workspace opening machinery is + deleted (absorbed old 2.3). +- [ ] **Phase 5. Remove old surfaces only when they confuse the simple path.** + Criteria agreed (delete, sequenced). First tranche done: the + `workspace` and `initiative` command groups are deleted (−12.9k net + lines). The remainder runs after 4.1. +- [ ] **Phase 6. Prove the whole, ready for first users.** + The final acceptance capstone: persona journeys, usability and technical + audits, whole-delta review, release-readiness report. Runs last. +- [ ] **Phase 7. Keep and open personal worksets.** + Complete (merge to `main` pending): the `workset` command group + (compose/list/open/remove), the two-style opener table with local + config, the capstone dogfood transcript, and the pushed branch with + review comments addressed — all on the working branch. + +Next incomplete item: + +- (none) — every roadmap item is complete through its last + pre-merge box. The only open boxes across the roadmap are the + per-item "Merged to `main`" boxes, which close together when the + branch lands (PR #1190). + +## Phase 0. Make The Active Direction Easy To Find + +This phase is already done. It cleaned up old roadmap sources so agents and +humans do not follow the wrong plan. + +Phase checklist: + +- [x] **0.1** Point people away from the old context-store beta plan. +- [x] **0.2** Mark deferred workspace plans as not the current queue. +- [x] **0.3** Reframe local agent guidance around OpenSpec roots. + +### 0.1 Point People Away From The Old Context-Store Beta Plan + +Progress: + +- [x] Done. + +What the user or agent needs: + +- A clear place to find the current direction. +- Confidence that old initiative docs are history, not the active plan. + +What changed: + +- The old context-store initiative now points readers to this `goal.md` and + `roadmap.md`. +- Old beta notes remain discoverable as transition evidence. +- The old initiative roadmap is no longer treated as the implementation queue. + +How we know it worked: + +- A new reader can start from this `/work` folder instead of chasing the old + initiative roadmap. + +### 0.2 Mark Deferred Workspace Plans As Not The Current Queue + +Progress: + +- [x] Done. + +What the user or agent needs: + +- No accidental revival of old workspace apply, verify, archive, branch, + worktree, or dashboard plans. + +What changed: + +- The old workspace reimplementation artifacts were marked obsolete or pending + deletion review. +- Useful research can still be copied forward later. + +How we know it worked: + +- The old workspace changes no longer look like the next thing to implement. + +### 0.3 Reframe Local Agent Guidance Around OpenSpec Roots + +Progress: + +- [x] Done. + +What the user or agent needs: + +- Agent instructions that start with "where is the OpenSpec root?" instead of + "which beta workspace/context-store mode is this?" + +What changed: + +- Local guidance was reframed around OpenSpec roots, artifact placement, target + project repos, and local repo mapping. +- Beta shared-context guidance was described as old, non-default history. + +How we know it worked: + +- Agents are guided to inspect current files and commands, while avoiding + promises about clone, sync, branch, worktree, dashboard, or edit-boundary + behavior. + +## Phase 1. Make A Standalone OpenSpec Repo Useful + +The user-facing goal of this phase: + +```text +I can keep OpenSpec work in its own Git repo and still use normal OpenSpec +commands. +``` + +Phase checklist: + +- [x] **1.1** Create or register a standalone OpenSpec repo. + Implemented in draft PR #1190. +- [ ] **1.2** Let normal commands use a named standalone OpenSpec repo. + Implemented, tested, and review follow-up fixed on + `codex/store-root-selection`; merge remains. +- [ ] **1.3** Prove the standalone repo lifecycle end to end. + Spec and plan written 2026-06-11; implements on `codex/store-root-parity` + on top of 1.1 and 1.2. +- [ ] **1.4** One guidance pass: stores in, initiatives out. + Absorbs old item 2.2; gated on the context-store terminology decision; + carries the deferred guidance debt from slice 1.2. + +### 1.1 Create Or Register A Standalone OpenSpec Repo + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Implementation done in draft PR #1190. +- [x] Tests pass in draft PR #1190. +- [ ] Merged to `main`. + +Slice: `slices/store-root-parity/spec.md` + +What the user can do: + +- Run `context-store setup` and get a normal OpenSpec root in a standalone repo. +- Clone a teammate's standalone OpenSpec repo and register it locally. +- Run `context-store doctor` and see whether the OpenSpec root is healthy. + +Why it matters: + +- A context store should not feel like a special beta planning system. +- It should be a normal OpenSpec root plus a small identity file. + +What changes in commands or files: + +- Setup creates or preserves this shape: + +```text +context-store-root/ + .openspec-store/ + store.yaml + openspec/ + config.yaml + specs/ + changes/ + archive/ +``` + +- Register requires an existing healthy OpenSpec root. +- Register can add `.openspec-store/store.yaml` only after confirmation. +- Doctor reports OpenSpec-root health separately from metadata and Git health. +- Setup/register do not create initiatives, workspace planning files, generated + agent files, slash commands, or tool config. + +How the user or agent knows it worked: + +- `created_files` reports the exact files and folders created. +- Re-running setup/register for the same root reports nothing to change. +- `context-store doctor --json` includes a separate `openspec_root` section. +- Existing config, specs, changes, archived changes, and old beta files are not + overwritten. + +### 1.2 Let Normal Commands Use A Named Standalone OpenSpec Repo + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Plan reviewed with `claude -p`; actionable feedback folded into the + slice artifacts. +- [x] Implementation done on `codex/store-root-selection` (stacked on + `codex/store-root-parity`). +- [x] Tests pass. +- [x] Review follow-up fixed. +- [ ] Merged to `main`. + +Slice: `slices/store-root-selection/spec.md` + +Plain-English version of the next slice: + +```text +When I am in an app repo, I can tell OpenSpec to create or read work in my +registered standalone OpenSpec repo. +``` + +Example user flow: + +```bash +openspec new change add-billing --store team-context +openspec status --store team-context +openspec instructions apply --store team-context +``` + +What the user can do: + +- Stay in the project repo they are working on. +- Pick a registered standalone OpenSpec repo by name. +- Create, inspect, validate, and archive normal OpenSpec work in that selected + repo. + +Why it matters: + +- Without this, users can create/register a standalone OpenSpec repo, but normal + commands still mostly act on the nearest local `openspec/` folder. +- The user should not need initiative links or workspace planning state just to + put work in a standalone OpenSpec repo. + +What changes in commands or files: + +- Add `--store <id>` as the way to choose the OpenSpec root for normal + commands. +- First command set: `new change`, `status`, `instructions`, `list`, `show`, + `validate`, and `archive`, behind one shared root resolver. +- The selected command writes normal `openspec/changes/` and reads normal + `openspec/specs/`. +- The command does not create initiative metadata. +- The command does not create workspace planning files. + +Decisions locked on 2026-06-10 (details in the slice spec): + +- `--store` is repurposed as root selection with exactly one meaning. Phase + 2.1 is pulled forward into this slice: `new change` stops creating + initiative links, the old initiative meanings of `--store` and + `--store-path` are removed, and `openspec set change` is removed because + initiative linking was its only behavior. +- `--store <id>` (registry lookup) is the only selector. `--store-path` is + deferred; registering a clone is the answer for path access. +- Leftover workspace view state never wins root resolution on this path. The + workspace branch is demoted during this slice's resolver rework instead of + waiting for Phase 2.3/5.1. +- When the current directory has no OpenSpec root and registered stores + exist, commands error with a hint naming the registered stores instead of + silently scaffolding a local root. With no registered stores, current + behavior is unchanged. + +How the user or agent knows it worked: + +- Without `--store`, commands keep using the nearest/current OpenSpec root. +- With `--store team-context`, `openspec/changes/<id>` is created in the + registered store root. +- JSON output shows which OpenSpec root was used. +- No new initiative link is created. + +### 1.3 Prove The Standalone Repo Lifecycle End To End + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Smoke flow implemented. +- [x] Tests pass. +- [ ] Merged to `main`. + +Slice: `slices/store-lifecycle-proof/spec.md` + +Plain-English version: + +```text +Show that a registered standalone OpenSpec repo can do the same basic lifecycle +as an OpenSpec root inside a project repo — including cloning it and continuing +the work from a second checkout. +``` + +What the user can do: + +- Set up a standalone OpenSpec repo that is a real Git repo (initialized, with + an initial commit) at a path they chose. +- Create, inspect, validate, and archive a change there from their project + repo. +- Commit and push the store themselves, clone it on another machine, register + the clone, and continue the work. +- Ask doctor whether the store repo has commits, uncommitted changes, or a + remote. + +Why it matters: + +- This proves standalone OpenSpec repos are not just setup plumbing. +- The sharing path (clone, register, continue) is the reason standalone repos + exist, and it is where the hands-on walk on 2026-06-11 found the real gaps. +- It catches missing command support before more features are built on top. + +Decisions locked on 2026-06-11 (details in the slice spec): + +- The proof is a two-checkout journey test in the existing CLI e2e harness, + not a solo-machine smoke or a separate script harness. +- Setup finishes what it starts: Git on by default, an initial commit of + exactly the files setup created, and a user-chosen location (`--path` + required non-interactively; interactive runs prompt with a visible path + suggestion). Tracked placeholder files keep otherwise-empty store + directories alive in clones, and setup checks for a usable Git commit + identity up front instead of failing mid-operation or inventing one. +- The Git line is create-time and read-only: setup may init and commit once; + doctor reports commits/dirty/remote facts read-only; register never + commits; nothing clones, pulls, pushes, branches, or syncs. +- The loop never drops the thread: selected-store hints carry `--store <id>`, + the root banner prints on post-resolution failures, `new change` names the + next command, and `status` drops the workspace-era "Planning home" line. +- Register errors become terminal instead of circular, with the + one-checkout-per-id rule and `unregister` as the named escape hatch. +- `view` is explicitly out of this slice; opening things together is Phase 4. + +What changes in commands or files: + +- `context-store setup` Git and location defaults, plus sharing next-steps. +- Read-only Git facts in `context-store doctor` output. +- Reworked register error messages. +- Hint/banner continuity across the slice 1.2 command set. +- One chained two-checkout journey test covering setup/register, list, + doctor, root selection, change creation, status, instructions, list/show, + validate, and archive. + +How the user or agent knows it worked: + +- The journey passes against the built CLI with isolated global state, + without using old initiative collections or workspace-owned planning state. +- A clone of a freshly set-up store is immediately a healthy OpenSpec root. +- The final files are normal `openspec/specs/`, `openspec/changes/`, and + `openspec/changes/archive/` files in both checkouts. + +### 1.4 One Guidance Pass: Stores In, Initiatives Out + +This slice absorbed roadmap item 2.2 on 2026-06-11: teaching guidance that +stores exist and stopping the same surfaces from advertising initiatives and +workspaces are one job, and doing them separately would mean regenerating the +guidance twice. + +Progress: + +Slice: `slices/store-rename-and-guidance/spec.md` + +- [x] Terminology decided (2026-06-11): the noun is **store**, defined + everywhere as "a store — a standalone OpenSpec repo you've registered." + Command group renames `context-store` → `store`; the `--store` flag stays; + machine tokens rename in the same pass (`context_store_*` diagnostic codes + → `store_*`, JSON `context_store` keys → `store`, data dir + `context-stores/` → `stores/`); committed store-repo formats + (`.openspec-store/store.yaml`, registry shape) stay. "Planning repo" and + "contracts repo" are prose examples of what a store is for, never product + nouns. "Context" is retired from this concept (freed for Phase 4). + Runner-up considered and rejected: `openspec repo`/`--repo`, because + agents' `--repo` prior means the code repo being operated on, which + collides with target project repos in Phase 3. +- [x] Spec written. +- [x] Plan written. +- [x] Implementation done (four checkpoints on `codex/store-root-parity`: + mechanical rename, the two riders, guidance regeneration, guards and + the dogfood proof; post-implementation review and simplify rounds + folded). +- [x] Tests pass (full suite green, 95 files / 1745 tests; vocabulary + sweep, format pins, and the headless dogfood transcript committed). +- [ ] Merged to `main`. + +Plain-English version: + +```text +An agent prompted in a project repo can discover the registered standalone +OpenSpec repo and use it without the human spelling out flags — and is no +longer steered toward initiatives or workspaces. +``` + +What the user can do: + +- Prompt an agent with "create a change for X in our team store" and have the + agent find the registered store and use `--store` on its own. +- Read top-level help and recognize the context-store commands as the + standalone OpenSpec repo feature. +- Follow generated guidance without being pointed at `openspec initiative` or + workspace flows as normal workflow steps. + +Why it matters: + +- Prompts are the primary interface. Slice 1.2 shipped `--store`, but + generated agent guidance never mentions it, so the feature is invisible in + the product's main surface. +- If guidance and completions keep advertising initiatives and workspaces, + users and agents keep treating them as the product model. +- Phase 1 is not honestly done while agents cannot discover stores. + +What changes in commands or files (surface inventory from 2026-06-11 +research, about 13 surfaces): + +- The `context-store` → `store` rename pass (group, machine tokens, data + dir) lands first, before any guidance prose is written. +- Two renames riders: remove the second live meaning of `--store` (legacy + `workspace open --store` still describes it as an initiative selector in + the same completions metadata this slice regenerates), and add an + unknown-subcommand hint under the `store` group for the inevitable + `openspec store new change <id>` (pointing at + `openspec new change <id> --store <id>`). +- CLI help one-liners for the `store`, `workspace`, and `initiative` + command groups (`src/cli/index.ts`, command registration files). +- Completions metadata (`src/core/completions/command-registry.ts`, + `shared-flags.ts`): present `--store` and store discovery; stop presenting + initiative/workspace flows as normal steps. +- The seven generated workflow skill templates in + `src/core/templates/workflows/` that still carry workspace-planning guards + and initiative references. +- The checked-in `.codex/skills/use-openspec/` guidance, which still + advertises `initiative list` and `workspace list` as inspection commands. +- Explicitly out of scope: `schemas/workspace-planning/templates/` (content + of the legacy schema itself; Phase 5 decides its fate), and any command + behavior changes. + +How the user or agent knows it worked: + +- A fresh agent session in a project repo with a registered store completes a + store-scoped change from a single prompt, without hand-holding. +- Generated guidance names `--store`; help text matches the model being + shipped; a fresh user is guided toward specs and changes, not initiatives. +- Existing initiative data remains untouched. + +## Phase 2. Stop Putting New Work Through Initiatives + +The user-facing goal of this phase: + +```text +Normal OpenSpec work should not require an initiative. +``` + +Old initiative data can remain readable as legacy history, but the simpler path +should stop attaching new work to initiatives. + +As of 2026-06-11 every item in this phase has been absorbed by another slice; +this phase carries no independent work. The sections below say where each item +went. + +Phase checklist: + +- [x] **2.1** Stop creating new initiative links in normal change flows. + Pulled forward into slice 1.2 on 2026-06-10; implemented there. +- [x] **2.2** Hide or move initiative commands out of the main path. + Folded into slice 1.4 on 2026-06-11 (one guidance pass). +- [x] **2.3** Make workspace opening stop depending on initiatives. + Folded into roadmap item 4.1 on 2026-06-11 (opening is rebuilt there). + +### 2.1 Stop Creating New Initiative Links In Normal Change Flows + +This item was pulled forward into slice 1.2 (`slices/store-root-selection/`) +on 2026-06-10, because repurposing `--store` as root selection only works +cleanly if initiative-link creation stops in the same slice. Track progress +under 1.2. + +Progress: + +- [x] Folded into slice 1.2; see the 1.2 progress checklist. + +What the user can do: + +- Create normal changes without attaching them to an initiative. +- Still read old initiative metadata if it already exists. + +Why it matters: + +- Initiative links make the simple model harder to understand. +- They make users think the initiative system is required when it should not be + the normal path. + +What changes in commands or files: + +- `new change` stops creating new initiative links as part of the main product + path. +- `openspec set change` is removed because initiative linking was its only + behavior. +- Existing `.openspec.yaml` initiative metadata remains parseable if needed. +- Store/root selection points to normal OpenSpec roots, not initiative + collections. + +How the user or agent knows it worked: + +- New changes do not get initiative metadata by default. +- Old initiative-linked changes can still be displayed or handled as legacy. + +### 2.2 Hide Or Move Initiative Commands Out Of The Main Path + +This item was folded into slice 1.4 on 2026-06-11, because teaching guidance +that stores exist and stopping the same guidance surfaces from advertising +initiatives are one regeneration pass, not two. Track progress under 1.4. + +Progress: + +- [x] Folded into slice 1.4; see the 1.4 progress checklist. + +### 2.3 Make Workspace Opening Stop Depending On Initiatives + +This item was folded into roadmap item 4.1 on 2026-06-11. Research showed +initiative selection is hardcoded into roughly 5,500 lines of workspace +opening machinery (`WorkspaceContextState` is initiative-shaped at its core), +and 4.1 will rebuild opening around assembled context anyway — refactoring +the old path first would be wasted motion. Track progress under 4.1. + +Progress: + +- [x] Folded into roadmap item 4.1; see the 4.1 section. + +## Phase 3. Say How Roots Relate: References And Targets + +The user-facing goal of this phase: + +```text +This project repo's work draws on these planning repos, and this OpenSpec +work targets these project repos. +``` + +Two directions, one idea — declared relationships between roots: + +- A project repo can **reference** the standalone OpenSpec repos its work + draws on (PMs and architects keep high-level requirements and design in a + store; devs create lower-level design and tasks in the app repo's own + OpenSpec root, with the store as cited context). +- A store can declare which project repos its work **targets**, and the local + machine maps those targets to checkouts. + +Root resolution precedence is fixed and stated once: explicit `--store` wins, +then the nearest local `openspec/` root, then (only when no local root +exists) a declared default store, then today's error with a hint. A declared +store never overrides a local root, and references never change where +commands act. + +The reference items come first because they run on machinery that already +exists (the store registry resolves ids to paths); the target items need the +new local repo map. + +Decisions locked on 2026-06-11: + +- **Index, not inline (3.1).** Referenced-store content is never inlined + into generated instructions; instructions carry an index (what specs + exist, one-line summaries, the fetch recipe via `--store`) built live from + the registered checkout at assembly time, and the agent fetches what it + needs. Inlining would freeze upstream content at generation time — the + copy-paste failure this effort exists to kill. +- **Declarations live in `openspec/config.yaml` (3.1, 3.2).** Both + `references:` and the fallback `store:` pointer share one home. The + fallback case is a config-only `openspec/` directory (no `specs/` or + `changes/`): root detection keeps today's stat-only walk, two extra stats + distinguish a real root from a pointer, and doctor warns when a root has + both planning shape and a pointer (pointer ignored per precedence). A + top-level marker file was rejected: `.openspec.yaml` is already taken as + per-change metadata, and a dot-only filename collision is an agent hazard. +- **One id namespace, typed sections (3.4, 3.5).** The machine-local + registry becomes one file with typed sections (`stores:` and `repos:`), + cross-section id uniqueness enforced at write time, and the existing + kebab id grammar applies to every id kind before any `targets:` list is + committed. `--store` never resolves a target-repo id; it rejects with a + typed hint. +- **Relationships are location, declaration, or citation — never managed + artifact links.** Where work lives is a relationship (`--store` is root + selection, not a link); roots declare references/targets once at the + collection level; artifact-to-artifact derivation ("derives from + team-context/billing") is prose citation that agents follow via the + reference machinery. No per-change edge objects (see Rules We Should Not + Forget). + +Phase checklist: + +- [ ] **3.1** Let a project repo reference the stores its work draws on. + Spec and plan written and reviewed (`slices/store-references/`); + implementation is next. +- [ ] **3.2** Fall back to a declared store when no local root exists. +- [ ] **3.3** Record a canonical remote in store identity. +- [ ] **3.4** Let a store declare its target project repos. +- [ ] **3.5** Map target repo names to local checkout paths. +- [ ] **3.6** Report relationship health. + +### 3.1 Let A Project Repo Reference The Stores Its Work Draws On + +Slice: `slices/store-references/spec.md` + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Implementation done (config field, the index assembler with five + warning codes and the shared 50KB budget, both instruction surfaces + in both modes, docs subsection; three-mechanism post-implementation + review and a simplify pass folded). +- [x] Tests pass (full suite green, 88 files / 1641 tests; unit, + surface, and e2e layered-flow coverage). +- [ ] Merged to `main`. + +Plain-English version: + +```text +High-level requirements live in the team's planning repo. When I work in my +app repo, my agent reads them from there and cites them — without me naming +the store every session, and without my commands being redirected there. +``` + +What the user can do: + +- Declare in the project repo's `openspec/config.yaml` (for example a + `references:` list of store ids) which stores this repo's work draws on. +- Prompt an agent with "create a low-level design for billing" and have the + agent pull the store's billing requirement into context and cite it, while + writing the design in the app repo's own root. + +Why it matters: + +- This is the layered PM/architect-to-dev flow: upstream truth in the store, + downstream work in the repo, connected by reference instead of redirection + or copy-paste. +- A fresh agent discovers the relationship from config instead of being told + every session. + +What changes in commands or files: + +- A reference declaration shape in project config (config parsing is already + permissive; the existing `context:` injection in artifact instructions is + the mechanism to reuse for referenced store specs). +- Instructions/context assembly includes relevant referenced-store specs. +- Root resolution is untouched: references are read-only context. Writing to + a referenced store remains an explicit `--store` action and a separate + change in that store. +- No per-change link objects (see Rules We Should Not Forget). + +How the user or agent knows it worked: + +- Artifact instructions generated in the app repo cite referenced store + specs. +- An unresolvable reference (store not registered locally) is reported with a + clear next step, not silently ignored. + +### 3.2 Fall Back To A Declared Store When No Local Root Exists + +Slice: `slices/declared-store-fallback/spec.md` + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Implementation done (the resolver pointer branch with source + `declared`, the store-selected predicate across all eight consumers, + the init pointer guard with ancestor walk, the both-shapes warning; + three-mechanism post-implementation review and a simplify pass + folded). +- [x] Tests pass (full suite green, 89 files / 1656 tests; resolver + unit matrix plus the externalized-planning e2e journey). +- [ ] Merged to `main`. + +What the user can do: + +- In a repo whose planning is fully externalized (no local `openspec/`), + declare the store once and run normal commands without `--store` on every + invocation. + +Why it matters: + +- Slice 1.2 made `--store` the way to reach a root you are not standing in; + for people who are never standing in one, repeating it on every command is + a tax. The declaration records intent that agents otherwise rediscover each + session. + +What changes in commands or files: + +- A default-store declaration honored only when no local root exists + (fallback, never override), per the precedence rule above. +- The no-root error/hint from slice 1.2 remains for repos with no declaration. + +How the user or agent knows it worked: + +- With a local root present, behavior is byte-identical with or without the + declaration. +- Without a local root, commands resolve to the declared store and report it + through the existing root banner and JSON root block. + +### 3.3 Record A Canonical Remote In Store Identity + +Slice: `slices/store-canonical-remote/spec.md` + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Implementation done (the optional `remote` in store.yaml via + `setup --remote`; observed origins recorded machine-locally at + setup/register with rerun-safe refresh reporting; doctor and sharing + surfaces; `{id, remote}` reference declarations with shell-safe + verbatim clone fixes; three-mechanism review and a simplify pass + folded). +- [x] Tests pass (full suite green, 90 files / 1678 tests; the e2e + onboarding journey executes the printed fix verbatim). +- [ ] Merged to `main`. + +What the user can do: + +- Clone an app repo that references a store they do not have yet, and be told + where to clone the store from. + +Why it matters: + +- References and teammate onboarding both dead-end today at "register the + store" — nothing records where a store can be cloned from. The registry + already supports an optional remote but nothing populates it, and the + shared `store.yaml` identity has no remote field at all. + +What changes in commands or files: + +- Optional canonical remote in `.openspec-store/store.yaml` (the shared, + committed home), populated at setup/register when known. +- Doctor surfaces it; unresolved-reference and register guidance use it + ("clone from <remote>, then register"). +- Recording a remote is not sync: no clone, pull, push, or branch behavior. + +How the user or agent knows it worked: + +- A registered store's remote is visible in doctor output. +- Guidance for an unregistered referenced store names the clone source. + +### 3.4 Let A Store Declare Its Target Project Repos + +Slice: `slices/store-targets/spec.md` + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Implementation done (`targets:` in the root's config via the + shared declaration parser; per-change narrowing as ordinary metadata + with the kebab grammar's single source of truth; effective targets + with provenance and degradation warnings on both instruction + surfaces; three-mechanism review and a simplify pass folded). +- [x] Tests pass (full suite green, 92 files / 1696 tests). +- [ ] Merged to `main`. + +What the user can do: + +- Declare once, at the store level, which project repos this planning repo is + about; optionally narrow per change. + +Why it matters: + +- A standalone OpenSpec repo is separate from the code repos, and users and + agents need to know which code repos the work is about. +- Most stores target a stable set of repos; per-change declaration would be + repetitive ceremony, so store-level defaults are the primary shape and + per-change narrowing is the exception. + +What changes in commands or files: + +- A target declaration shape in the store's config, plus optional per-change + narrowing as ordinary metadata. +- Do not imply automatic clone, sync, branch, worktree, or edit-boundary + enforcement. + +How the user or agent knows it worked: + +- Given a store, OpenSpec can list the target repo ids its work is about. +- A change can narrow its targets, and the narrowing is visible in normal + OpenSpec files or metadata. + +### 3.5 Map Target Repo Names To Local Checkout Paths + +Slice: `slices/repo-map/spec.md` + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Implementation done (typed registry sections with pinned + preservation; the repo command group with pinned JSON contracts; + cross-section id+path uniqueness at write AND parse time; the + `store_id_is_repo` typed rejection; targets path enrichment incl. + change-only narrowing; three-mechanism review and a simplify pass + folded). +- [x] Tests pass (full suite green, 94 files / 1718 tests). +- [ ] Merged to `main`. + +What the user can do: + +- Tell OpenSpec where each target project repo lives on this machine. + +Why it matters: + +- Shared OpenSpec work can name a target repo, but each developer may have that + repo checked out in a different local path. + +What changes in commands or files: + +- Add local machine settings that map target repo ids to local checkout paths. +- Keep the map local; it is not shared planning state. +- Missing, duplicate, or invalid mappings fail clearly. + +How the user or agent knows it worked: + +- Given a target repo id, OpenSpec can resolve the local checkout path. +- If the path is missing or ambiguous, the error tells the user what to fix. + +### 3.6 Report Relationship Health + +Slice: `slices/relationship-health/spec.md` + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Implementation done (the root-scoped `openspec doctor` — pure + composition over the Phase 3 assemblers; every recorded deferral + landed; health-mode assembler options; the torn-snapshot + readRegistrySnapshot invariant; three-mechanism review and a + simplify pass folded). +- [x] Tests pass (full suite green, 96 files / 1739 tests). +- [ ] Merged to `main`. + +What the user can do: + +- Ask OpenSpec whether the roots this work relates to — referenced stores and + target project repos — are available on the current machine. + +Why it matters: + +- Agents need to know whether they can read the referenced context and + inspect or edit the relevant code repos. +- This should be diagnostic only; it should not clone or sync anything. + +What changes in commands or files: + +- Doctor or status output reports reference resolvability and target repo + mapping health. +- The report clearly separates OpenSpec root health, store metadata health, + reference health, and target checkout health. + +How the user or agent knows it worked: + +- Missing target repo mappings and unresolvable references are easy to see. +- The output does not attempt clone, pull, push, sync, branch, or worktree + behavior. + +## Phase 4. Assemble The Working Context + +The user-facing goal of this phase: + +```text +Give me — or my agent — everything this work relates to in one working set: +the OpenSpec root, the stores it references, and the project repos it +targets. +``` + +Phase checklist: + +- [x] **4.1** Assemble the working context from declared relationships. + (Merge to `main` pending.) + +### 4.1 Assemble The Working Context From Declared Relationships + +This item absorbed roadmap item 2.3 on 2026-06-11: the old workspace opening +machinery has initiative selection hardcoded into its state model across +roughly 5,500 lines, and this slice rebuilds opening around assembled +context, so de-initiative-ing the old path first would be wasted motion. + +Slice: `slices/assemble-working-context/spec.md` + +Progress: + +- [x] Spec written. +- [x] Plan written. +- [x] Implementation done (CP1 deleted the workspace machinery — + 27 files, −2,196 lines; CP2 added `openspec context` with the JSON + agent brief, human listing, and `--code-workspace` emitter; + three-mechanism review and a simplify pass folded). +- [x] Tests pass (full suite green, 96 files / 1714 tests). +- [ ] Merged to `main`. + +What the user can do: + +- From any root, get the full working set its declarations describe: the + OpenSpec root itself, its referenced stores, and its mapped target repos. +- Consume that set as an editor view (for example a code-workspace file) or + as an agent session brief — opening in an editor is one consumer of + assembly, not the feature itself. + +Why it matters: + +- Users usually need the plan, its upstream context, and the code together. +- Assembly is a local convenience computed from Phase 3's declared + relationships, not a new planning system; the primary interface is an agent + session, so the assembled set must be agent-consumable, not only + editor-shaped. + +What changes in commands or files: + +- Replace or rebuild workspace opening around assembled context (this is + where old item 2.3's initiative decoupling actually happens). +- Use the selected OpenSpec root as the durable planning source of truth, the + reference declarations for upstream stores, and the local repo map for + target checkouts. +- Do not create workspace-owned planning state. + +How the user or agent knows it worked: + +- The assembled set contains the OpenSpec root, resolvable referenced stores, + and mapped target repos, with unresolvable pieces reported, not guessed. +- Assembly does not create or require initiative planning state. +- The durable files remain normal OpenSpec artifacts. +- The result does not imply clone, pull, push, sync, branch, worktree, + dashboard, or edit-boundary enforcement. + +## Phase 5. Remove Old Surfaces Only When They Confuse The Simple Path + +The user-facing goal of this phase: + +```text +Remove or hide old beta surfaces only when they make the simple path harder to +use or understand. +``` + +Phase checklist: + +- [x] **5.1** Remove or hide old workspace and initiative paths when they block or + confuse the simple path. (Merge to `main` pending.) + +### 5.1 Remove Or Hide Old Workspace And Initiative Paths + +Progress: + +- [x] Criteria agreed (2026-06-11): **delete, don't hide — sequenced.** + With zero users, hiding keeps every cost (rot, grep noise, refactors + routing around dead code) and adds a hidden/visible distinction to + protect nobody. Sequence: guidance surfaces die in slice 1.4 (planned), + the `workspace` and `initiative` command groups become their own small + deletion slice soon after 1.4, and the workspace **state model** plus + the `workspace-planning` mode die when 4.1 replaces opening + (zero-consumer opening helpers go with the command groups — keeping + unreachable files would be hiding, which these criteria reject; wording + narrowed 2026-06-11 during the deletion-slice spec, recorded as a + reviewable autonomous decision). The inviolable carve-out stays: never + auto-delete user data files. "Hide now, delete later" is rejected + because later never comes. +- [x] Cleanup plan written (first tranche: the command-group deletion + slice, `slices/delete-legacy-command-groups/`; spec and plan both + through two adversarial review rounds). +- [x] Cleanup done. First tranche complete 2026-06-11: the `workspace` + and `initiative` command groups and everything only they consumed are + deleted (−12,903 net lines), with the deletion ledger committed. The + remainder executed 2026-06-11 after 4.1 + (`slices/delete-legacy-command-groups/remainder.md`): + `schemas/workspace-planning/` deleted (it was still advertised by + `openspec schemas`); the four `workspace-*` beta change folders + deleted (unimplemented relics — archiving would assert completion); + L2 decided — the four wholly-workspace accepted specs deleted + (capability gone = spec gone), the workspace requirements excised + from `cli-config` and `cli-artifact-workflow` (bounded, not a + rewrite), incidental mentions elsewhere recorded for the capstone + vocabulary audit. +- [x] Tests or review checks pass. First tranche green (85 files, 1616 + tests; three-mechanism review, no open P1/P2). Remainder green + (96 files, 1714 tests; all 36 accepted specs validate). +- [ ] Merged to `main`. + +What the user can do: + +- Follow the simple OpenSpec root path without being distracted by obsolete beta + workflows. + +Why it matters: + +- Cleanup is useful only when it reduces confusion or removes a blocker. +- It should not become a broad compatibility project or docs rewrite. + +What changes in commands or files: + +- Obsolete no-delta workspace changes can be deleted, archived, or moved out of + the active queue. +- Workspace-planning and initiative-collection code, docs, specs, and generated + guidance can be removed or moved out of the main path where they mislead + users or agents. +- Existing user data is not deleted automatically. + +How the user or agent knows it worked: + +- The active roadmap and generated guidance point to the simple path. +- Old surfaces no longer look like required workflow. + +## Phase 6. Prove The Whole, Ready For First Users + +The user-facing goal of this phase: + +```text +A person with zero context can start using this today: every persona +journey works cold, every error leads somewhere, and the codebase ended +leaner than it started. +``` + +Phase checklist: + +- [ ] **6.1** Final acceptance capstone. + +### 6.1 Final Acceptance Capstone + +The slices prove themselves; this proves the product — the sum of all +phases, reviewed and exercised as one thing. Full checklist in +`runbook.md` ("Final acceptance capstone"). + +Progress: + +- [x] Persona journeys pass (fresh team, layered PM-to-dev, externalized + planning, cold-start agent with no insider knowledge). Results: + `capstone/journeys.md` — journeys 1–3 as standing e2e + (store-lifecycle + capstone-journeys test files), journey 4 as a + live headless codex dogfood that assembled the full store/pointer/ + targets/repo-map topology from `--help` alone. +- [x] Usability audits done (error catalog, vocabulary sweep including + `docs/cli.md`, time-to-first-success documented). Results: + `capstone/usability-audits.md` — 55 wrong turns walked (46 pass; the + 9 failures are queued for the capstone fix round before the report); + vocabulary clean except one legacy initiative JSON passthrough + (queued); TTFS measured live at 2 commands / 2 concepts with every + step printing the next command. +- [x] Technical audits done (single-resolver invariant, dependency + direction, dead code, module sizes, agent-contract inventory, net LOC + delta reported). Results: `capstone/technical-audits.md` — both + invariants HOLD with zero violations; dead code yields five P3s + (queued) and no P2s; module sizes bounded (largest 1,160 lines); the + agent contract is documented in `docs/agent-contract.md` (every JSON + shape + 100+ diagnostic codes verified against emitting code, 14 + consistency findings recorded, one gauntlet-grade); src net LOC is + **−4,478** vs origin/main. +- [x] Whole-delta review gauntlet over `origin/main...HEAD` passed with no + open P1/P2 findings. Four mechanisms (`capstone/gauntlet.md`); the 2 + P1 + 13 P2 findings all fixed (37ad867) and live re-verified; full + suite green (97 files, 1,717 tests); all 36 accepted specs validate. +- [x] Release-readiness report committed + (`capstone/release-readiness.md`) — the five-minute story, all audit + results, the autonomous-decision ledger, known gaps mapped to Later + Ideas. No open P1/P2 findings. +- [ ] Merged to `main`. + +Why it matters: + +- Each slice was reviewed against its own base; nobody has reviewed or + exercised the sum. Cross-slice inconsistencies, vocabulary drift, and + cold-start failures live exactly there. +- "Could start using it straight away with no issues" is a product claim + that checkboxes cannot make; only journeys and audits can. + +How the user or agent knows it worked: + +- All four journeys run green as tests or headless dogfoods. +- The release-readiness report reads as a credible first-user story, with + known gaps mapped to Later Ideas rather than discovered by users. + +## Phase 7. Keep And Open Personal Worksets + +The user-facing goal of this phase: + +```text +Let me keep my own named view of the folders I work on together, and +open them all with one command in the tool I choose. +``` + +Phase checklist: + +- [ ] **7.1** Personal worksets: compose, keep, and open a local working + view. + +### 7.1 Personal Worksets: Compose, Keep, And Open A Local Working View + +User-directed follow-up (owner design review, 2026-06-12; supersedes the +change-anchored direction in `workset-direction.md` where they differ). A +workset is a purely local, personal, named working view: the user composes +it manually (a planning root plus whatever folders they choose), keeps it +on their machine, reopens it by name, and launches it into their tool of +choice. It is not committed, not shared, not derived from declarations, +and never a membership truth — it makes no claims about the work, only +about what this user likes open together. Declarations/targets may later +*suggest* members during composition; they are not load-bearing. The +repo map and `openspec context` remain unchanged and independent. + +Progress: + +- [x] Research done and spec written. +- [x] Plan written. +- [x] Implementation done. +- [x] Tests pass. +- [x] Capstone dogfood passes (end-to-end UX run; transcript in the + slice folder). +- [x] Branch pushed; code-review comments addressed. +- [ ] Merged to `main`. + +What the user can do: + +- Group the folders they work on together — a store checkout plus some + repos — under a name, in one short guided flow, with nothing to set + up beforehand. +- Reopen that grouping any time, by name, in their preferred tool, or + a different tool for a single open. +- List and remove their saved views; nothing they do here touches any + member folder or any shared state. + +Why it matters: + +- Multi-root work has a daily "get everything open again" cost; this + removes it without reintroducing managed workspace state. +- Agent sessions launched from a workset get real access to every + member (attach flags / sandbox roots), which a printed brief alone + cannot grant. + +What changes in commands or files: + +- A new `workset` command group (compose/list/open/remove shapes to be + settled in spec) and a machine-local saved-views file in the global + data dir, following the registry's lock/atomic-write idiom. +- An opener table (built-ins: `code`, `cursor`, `claude`, `codex`) + with user-extensible local config per the two-style pattern in FR2. +- No changes to `openspec context`, the repo map, project config + parsing, or any committed file format. + +How the user or agent knows it worked: + +- A first-time user composes and opens a view in under a minute, and + the same name reopens it tomorrow. +- An agent opened from a workset can read and edit every member folder + without asking where things are. +- Deleting all workset state loses nothing the user cannot recompose + in a minute; no member folder ever contains workset residue. + +Decisions locked (2026-06-12, owner-directed): + +- Local-only, manual composition; never committed, shared, or derived. +- **No starter prompt on agent opens** — reusing a grouping implies + nothing about intent; sessions open clean with directories attached. +- Tools-as-config via exactly two launch styles (`workspace-file`, + `attach-dirs`); no per-tool code paths. +- No `--print`/dry-run mode; fallback info lives in the failure path. +- Desktop apps unsupported until they expose a real launch interface. +- The retired noun "workspace" stays retired; the feature noun is + "workset". + +Research needed before the spec (the slice's first checkpoint): + +- Saved-views file shape and exact location; name validation rules. +- Opener config: file location, schema, override/merge semantics with + built-ins; verify the `cursor` CLI shim's `.code-workspace` handling. +- Terminal-handoff details for agent opens (signal handling, exit-code + propagation, `--json` interplay) — crib from `f858c19^` mechanics: + cross-spawn, stdio inherit, shell false, PATH/PATHEXT availability. +- Compose-flow prompt design against the house `@inquirer` idiom. + +Functional requirements (user perspective): + +**FR1 — Compose and keep a personal working view.** + +1. When a user regularly works across a planning repo and some code + repos together, they can compose that grouping by pointing at + folders, name it, and have it kept — one short guided flow, nothing + to set up beforehand. +2. The composition is entirely the user's choice: any folders, any + number, no requirement that they relate to declarations, teammates, + or anything else. +3. The saved view is private to the user's machine — never committed, + never shared, never written into any member folder. +4. Listing views shows each name with its members at a glance. +5. Removing a view deletes only the saved view, never a member folder. + +```gherkin +Scenario: First working view in under a minute + Given a user works on a store plus web-app and api-server together + When they create a workset, point at the three folders, and name it + Then it is saved on their machine and offered to open immediately + And nothing was created or changed inside any member folder + +Scenario: Composition is personal + Given a teammate works on the same store with different repos + When each composes their own workset + Then neither sees, affects, or needs the other's + +Scenario: Removing a view is safe + When a user deletes a workset + Then only the saved view is gone; member folders are untouched +``` + +**FR2 — Open the view in your tool.** + +1. Opening a workset launches the chosen tool with every member + attached and accessible. The open kind is stated plainly: editors + (VS Code, Cursor) open a window and return; CLI agents (Claude + Code, codex) take over this terminal as a session that ends when + they exit. +2. Only tools actually installed are offered; the preference saved at + composition is overridable per open without changing it. +3. Supporting a new tool is configuration, not code. Every tool is an + instance of one of two launch styles — `workspace-file` (invoke + with the generated `.code-workspace`) or `attach-dirs` (executable + + optional pre-args + one attach flag per member; no prompt is + passed — agent sessions open clean) — and users can add tools or + adjust parameters (command, attach flag) in local config, so a tool + renaming its flag is a one-line local fix. (The git + difftool/mergetool pattern.) +4. When a tool cannot be driven (desktop apps, for now) or a launch + fails, the user is shown the generated workspace file and the + member folders so they can open manually — never a bare error. + (Considered and dropped: a `--print` dry-run flag; the fallback + information lives in the failure path instead.) +5. A member folder missing at open time is skipped with a one-line + note; the rest of the view opens. + +Built-in opener table at v1: `code`, `cursor` (workspace-file style); +`claude`, `codex` (attach-dirs style; codex carries +`--sandbox workspace-write` pre-args). Availability via PATH scan. + +```gherkin +Scenario: Editor open returns, agent open takes over + When the user opens "platform" in VS Code + Then a window opens with all members and the prompt returns + When the user opens "platform" in Claude Code + Then a Claude session starts in this terminal with every member + granted as a working directory, no prompt pre-filled, and ends + when they exit it + +Scenario: Adding a new editor without a release + Given the user adds `zed: { style: workspace-file }` to local config + When they open a workset in zed + Then it launches with the generated workspace file + +Scenario: Flag drift is a local fix + Given a CLI agent renamed its attach flag + When the user overrides that tool's attach_flag in local config + Then opens work again immediately + +Scenario: Launch failure never strands + When a launch fails or the tool has no launch interface + Then the user sees the workspace file path and member folders to + open manually +``` + +Evidence base: the deleted `workspace` feature's guided setup, opener +availability sorting, graceful missing-path skips, and per-tool launch +recipes were its good bones (recoverable at `f858c19^`; launch +mechanics: cross-spawn, stdio inherit for agent handoff, shell false, +PATH/PATHEXT availability scan); its registry indirection, managed +directories, initiative binding, skills state, and repair subcommands +are explicitly not inherited. Current code provides the +`.code-workspace` builder (pure), the XDG storage idiom, and the +prompt library. + +## Later Ideas + +Keep these out of the main queue until the simpler standalone OpenSpec repo path +is working: + +- **L1** Rewrite public concept docs after behavior is solid. +- **L2** Decide how accepted workspace-planning specs should change once behavior has + changed. +- **L3** Add richer cross-repo context and doctoring after target repo mapping works. +- **L4** Consider first-class `work/` only after the baseline and standalone repo flow + are solid. +- **L5** Revisit whether `changes/` should evolve into change-shaped work under + `work/`. +- **L6** Add machine-readable `/work` metadata only after the manual shape proves + useful. +- **L7** The keep-or-rename *decision* for `context-store` terminology moved + into slice 1.4 on 2026-06-11 (guidance prose should not bake in a name we + have not chosen, and renaming is free while there are no users). Only the + execution of a rename, if chosen, may land here as its own slice. +- **L8** Review local `use-openspec` skill guidance and decide whether it should be an + ignored local skill, generated artifact, checked-in source, or productized + default. +- **L9** Fix small baseline quirks, such as JSON support for `openspec list --specs`, + only if they matter to the simple standalone repo flow. +- **L10** Reintroduce initiative-like behavior only as a Git-native work type if it + still proves useful later. +- **L11** Make archived changes browsable through commands (for example + `list --archived`) if filesystem and Git history prove insufficient. The + archive command's own confirmation line is the lifecycle's verification + signal for now. + +## Roadmap Change Log + +- 2026-06-07: Started the active reorientation experiment under + `openspec/work/` instead of continuing the context-store initiative roadmap. +- 2026-06-07: Renamed the active work from the abstract Git-native principle to + the concrete context/workspace model simplification. +- 2026-06-08: Removed the experimental `/work` folder shape from the roadmap; + it is the dogfood structure for this thinking, not a product slice. +- 2026-06-08: Preserved the old initiative reorientation item and expanded the + framing cleanup into separate roadmap slices. +- 2026-06-08: Completed the old initiative reorientation pass by rewriting the + opening sections of old initiative files as transition evidence and beta + history. +- 2026-06-09: Marked old workspace reimplementation artifacts obsolete or + pending deletion review. +- 2026-06-09: Reframed checked-in `use-openspec` guidance around OpenSpec roots, + artifact placement, and target project repos instead of beta shared-context + framing. +- 2026-06-09: Deferred public concept docs until the simplified model is more + solid. +- 2026-06-09: Reordered the roadmap around standalone OpenSpec repos, target + repo mapping, and local views. +- 2026-06-09: Added the store-root-parity slice spec. +- 2026-06-10: Rewrote this roadmap in user-facing language so each slice says + what the user can do, why it matters, what changes, and how success is + visible. +- 2026-06-10: Numbered phases, phase subitems, and later parking-lot ideas so + progress can be tracked unambiguously. +- 2026-06-10: Settled the model question behind 1.2: the OpenSpec root is the + planning home, a context store is registration/identity only, and workspace + "planning home" is legacy beta language. +- 2026-06-10: Locked the 1.2 decisions and added the store-root-selection + slice spec: repurpose `--store` as root selection and pull 2.1 forward, + defer `--store-path`, demote leftover workspace state during the resolver + rework, and replace the silent implicit-root scaffold with an error and + hint when registered stores exist. +- 2026-06-11: Walked the standalone-store lifecycle by hand against the + built CLI. The 1.1/1.2 command mechanics held up; the gaps were the + sharing path (commitless setup repos, empty clones, circular register + errors), guidance that drops the selected store, and leftover + workspace-era output language. +- 2026-06-11: Locked the 1.3 decisions and added the store-lifecycle-proof + slice spec: the proof is a two-checkout journey test; setup defaults to + Git with an initial commit and an explicit path; doctor reports read-only + Git facts; register errors become terminal; selected-store hints keep the + store; `view` stays out until Phase 4. +- 2026-06-11: Added slice 1.4 for agent and help-surface store + discoverability (the deferred guidance debt from slice 1.2) and parked + archive browsability as L11. +- 2026-06-11: Folded review findings into the store-lifecycle-proof spec + after reproducing the empty-clone failure against the built CLI: tracked + placeholder files so clones keep empty store directories, an up-front Git + identity check for setup, an explicit interactive location prompt, and an + enumerated second-checkout journey that reads promoted specs instead of + browsing the archive. +- 2026-06-11: Wrote the store-lifecycle-proof plan, grounded in a code map + of the setup/doctor/register internals, the hint and banner sites, and + the CLI e2e harness. +- 2026-06-11: Adopted a single working branch for the whole roadmap: all + slices implement on `codex/store-root-parity` (PR #1190), stacked in + order, with merge to `main` deferred until the work lands as a whole. +- 2026-06-11: Implemented slice 1.3 with the two-checkout journey test, then + ran two adversarial subagent reviews and folded all findings: hint + continuity extended to validate/show/archive/status-JSON next steps, + Windows-safe journey assertions and telemetry isolation, index-preserving + commit cleanup on failure, reruns no longer git-init registered stores, + corrupt repos are no longer reported as commitless, and the machine-B + journey now covers the full enumerated command set. Full suite green + (93 files, 1729 tests). +- 2026-06-11: Folded a code-quality review round: setup's initial commit is + now derived from the store shape rather than the rollback ledger, so + converting an existing non-Git root produces a clonable repo (the commit + carries config and specs, never unrelated beta files); identity-file + creation is owned by setup alone, with registration verifying instead of + writing; Git mechanics moved to `src/core/context-store/git.ts`; and the + Git lifecycle tests split into `context-store-git.test.ts` with shared + fixtures. +- 2026-06-11: Restructured the roadmap after a fresh-eyes review. The + PM/architect-to-dev layering use case (high-level requirements in a store, + implementation work in the app repo's own root) replaced the rejected + "project-to-store binding" idea with declared relationships between roots: + references never change where commands act, and root resolution precedence + is fixed (explicit `--store`, then nearest local root, then a declared + default only when no local root exists, then error with hint). +- 2026-06-11: Merged old item 2.2 into slice 1.4 (one guidance pass over the + ~13 surfaces inventoried by research) and gated 1.4 on the context-store + terminology decision promoted from L7. Folded old item 2.3 into item 4.1 + (initiative selection is hardcoded into ~5,500 lines of opening machinery + that 4.1 rebuilds). Phase 2 now carries no independent work. +- 2026-06-11: Rewrote Phase 3 around relationships in both directions — + references first (3.1 repo references stores, 3.2 declared-store fallback, + 3.3 canonical remote in store identity), then targets (3.4 store-level + target declarations with per-change narrowing, 3.5 local repo map, 3.6 + relationship health). Reframed Phase 4 as context assembly, with editor + opening as one consumer and an agent session brief as another. Added two + guardrails: references are repo-level config, never per-change lifecycle + links, and one change lives in one root. Updated goal.md with the layered + reference experience. +- 2026-06-11: Added Phase 6 (final acceptance capstone) and standing + quality bars to the runbook: the autonomous run cannot declare completion + on ticked boxes alone — four persona journeys (including a cold-start + agent with no insider knowledge), usability audits (error catalog, + vocabulary sweep, time-to-first-success), technical audits + (single-resolver invariant, dependency direction, dead code, module + sizes, agent-contract inventory, net LOC delta), a whole-delta review + gauntlet over `origin/main...HEAD`, and a committed release-readiness + report. +- 2026-06-11: Locked the open decisions after parallel product-level and + staff-engineer analyses. Naming: the noun is "store" with the + `context-store` → `store` group rename and machine-token rename landing + first in slice 1.4 (`--store` stays; `openspec repo`/`--repo` rejected for + the target-project-repo collision). Phase 3: index-not-inline injection, + declarations in `openspec/config.yaml`, one typed id namespace, and the + relationship altitude rule (location, declaration, or citation — never + managed per-artifact links, which is what initiative links were). Phase 5 + criteria agreed: delete rather than hide, sequenced across 1.4, a small + command-group deletion slice, and 4.1. Loop operating rules approved: + full slice discipline with adversarial subagent reviews plus codex CLI + reviews, stopping at undecided items, Phase 5 entry, and merges. +- 2026-06-11: Folded plan-review findings into the slice after checking + them against the code: `store.yaml` must be written before setup's + initial commit (today it is written during registration, after Git + init), the commit must be pathspec-limited to preserve the user's + staged index, the identity preflight uses `git var` so env-var identity + counts, converted roots get placeholders at first accept while doctor + warns on clone-fragile empty directories in older stores, and the + journey's `created_files` assertion runs setup in JSON mode. +- 2026-06-11: Wrote the store-rename-and-guidance slice spec (1.4) and + folded two parallel adversarial review rounds (subagent: + approve-with-fixes; codex CLI: reject). Both converged on the same flaw + — exempting the legacy initiative/workspace groups from the token + rename contradicted the locked machine-token decision, left + paste-broken hints, and kept a second live `--store` meaning — so the + spec now states one rule: the token rename is total and mechanical + everywhere (codes, JSON keys, dotted targets, hints, docs — legacy + groups included), the prose rewrite is surgical (enumerated guidance + surfaces only), and behavior changes are exactly the two riders. Also + folded: the corrected token inventory (45 codes pinned by sweep, plus + the dotted `context_store.*` target family), the missed guidance + surfaces (`artifact-placement.md`, `docs/workspaces-beta/`), the three + out-of-guard workspace-prose mentions in templates, a sweep-as-test + acceptance criterion, and a concrete delivery mechanism for the dogfood + proof (`openspec init` in the scratch repo). +- 2026-06-11: Decided autonomously (review me): the `context-store` group + gets no back-compat alias and the old `context-stores/` data dir is not + migrated — zero users on the unmerged branch, and 5.1 locked + delete-don't-hide. +- 2026-06-11: Decided autonomously (review me): internal identifiers + rename with the product noun (`src/core/context-store/` → + `src/core/store/`, `ContextStore*` → `Store*`, command/test/helper + files follow) — one concept, one token in the codebase; compiler-checked + and free with no users. +- 2026-06-11: Decided autonomously (review me): the legacy `initiative` + and `workspace` groups get token substitution and legacy-labeled + one-liners only, never restructuring; initiative's `--store`/ + `--store-path` selectors keep behavior under reworded descriptions as a + named, expiring inconsistency that the next slice deletes with the + group. +- 2026-06-11: Decided autonomously (review me): workflow-template + workspace guards stay (they quote the live `actionContext.mode: + "workspace-planning"` contract, reachable until 4.1, and refuse rather + than advertise); the three out-of-guard workspace-prose mentions + reword. Ground truth correction: five templates carry guards, zero + reference initiatives (roadmap had said seven with initiative refs). +- 2026-06-11: Decided autonomously (review me): docs get a mechanical + accuracy pass in 1.4 (`docs/cli.md` store section, removed + `workspace open` selector rows, stale default-XDG-path fix, token + renames in `docs/workspaces-beta/`) so no doc instructs a dead command; + deleting the beta docs belongs to the Phase 5 remainder and the L1 + rewrite stays deferred. +- 2026-06-11: Decided autonomously (review me): checked-in beta guidance + is cut, not updated — `shared-context-beta.md` deleted, `SKILL.md` + rewritten around store discovery, `artifact-placement.md` loses its + beta-flow routing — per the locked 5.1 sequencing that guidance + surfaces die in 1.4. +- 2026-06-11: Decided autonomously (review me): the dead + `getDefaultContextStoreRoot` export (orphaned when 1.3 made `--path` + required) is deleted in the rename pass, not renamed; and the + over-600-line modules the rename touches (`operations.ts`, + `commands/context-store.ts`) are not split in this slice because the + Phase 5 deletions and 4.1 rebuild are about to shrink them (recorded + module-size reason per the runbook bar). +- 2026-06-11: Decided autonomously (review me): discovered during 1.4 + implementation that `.codex/` is git-ignored (`.gitignore:158`) — the + use-openspec guidance the roadmap called "checked-in" is actually the + L8 ignored-local-skill. Its store-discovery rewrite (beta reference + deleted, SKILL.md and artifact-placement reworked) lands on disk for + local agents but cannot appear in commits; L8 keeps ownership of the + final disposition (ignored local skill vs generated vs checked-in). +- 2026-06-11: Wrote the delete-legacy-command-groups slice spec (the + Phase 5 command-group deletion) and folded two parallel adversarial + reviews (subagent: reject, three P1s; codex CLI: reject, one P1) — + every finding verified against code and folded: the `config` command's + workspace-profile integration (which even executes `npx openspec + workspace update`) is now in scope as the second included behavior + change; `src/core/store/binding.ts` is kept (the planning-home + carve-out depends on it through `workspace/foundation.ts`), with a + recorded dead-export carve-out ledger owned by 4.1; partial test edits + are named (`registry.test.ts`, `config-profile.test.ts`, + `foundation.test.ts`); `docs/concepts.md` loses its whole Coordination + Workspaces section; the "Use initiatives…" status constraint rewords + to read-only compatibility language; 39 diagnostic codes pinned for + the deletion ledger. +- 2026-06-11: Decided autonomously (review me): narrowed the locked 5.1 + sequencing wording — "opening machinery dies in 4.1" now reads "the + workspace state model and workspace-planning mode die in 4.1". The + zero-consumer opening helpers (`openers.ts`, `open-surface.ts`) are + deleted with the command groups, because once `workspace open` is gone + nothing can reach them and keeping them would be exactly the + hidden-not-deleted state the locked criteria reject. 4.1 builds new + assembly; it does not need the dead launchers. +- 2026-06-11: Decided autonomously (review me): orphan deletion is + transitive in the command-group deletion slice — the five + command-consumed core workspace modules, the whole + `src/core/collections/` tree, the `config` command's + workspace-profile integration, and the orphaned `path-env` test + helper go with the groups; `docs/workspaces-beta/` and the cli.md / + concepts.md legacy sections are deleted rather than updated + (superseding the 1.4 decision that parked the beta docs for the + Phase 5 remainder). +- 2026-06-11: Wrote the delete-legacy-command-groups plan (five + deletion waves, one commit, grep-before-delete discipline) and folded + two parallel plan reviews (subagent: approve-with-fixes; codex: + reject) — all verified and folded: two acceptance scenarios had no + implementing test (the planning-home mode pin — nothing in the suite + asserts `actionContext.mode` today — and the docs pointer grep gate), + `docs/cli.md` had dead-command references outside every cited range + (agent-table rows 51-56, the Stores summary cell, config-section + lines 1178/1180), the config map gained the interface and core-preset + call sites (49-52, 523-524) with the full test ranges (134-172, + 422-516), the parity test's initiative carve-out removal is named as + a deliberate fourth partial edit, and the spec's byte-stable clause + now allows the new removal-coverage tests. The reworded constraint + string gets its first-ever pin in the new test. +- 2026-06-11: Capstone (6.1) COMPLETE. The whole-delta gauntlet ran + four mechanisms (/code-review at max effort with all 12 verified + candidates confirmed, a 32-agent adversarial Workflow with six + lenses and refute-style verification, a codex whole-delta review, + and a completeness critic); the consolidated 2 P1 + 13 P2 findings + were all fixed in one round (37ad867) and re-verified live - the + highest-impact being the ~/openspec layout turning $HOME into a + phantom nearest root (the walk now requires a qualifying openspec/), + the --json failure contract (every failure path now emits exactly + one status document), prompt-render sanitization of cloned content, + and three store-lifecycle TOCTOU/ordering hazards. Decided + autonomously (review me): planningHome was RESTORED to status JSON + rather than rewriting eleven generated-skill references - it is a + published agent contract, which reverses the planned + PlanningHomeSummary dead-code collapse; store remove now commits the + registry removal before deleting files. The release-readiness report + is committed (capstone/release-readiness.md) with zero open P1/P2 + findings; every queue item's boxes are ticked except Merged to main, + per the run's standing instruction. +- 2026-06-11: Capstone (6.1) technical audits done + (`capstone/technical-audits.md`). Single-resolver invariant HOLDS + (one precedence implementation; nine entry points through it; one + latent unreachable fallback queued for deletion). Dependency + direction HOLDS (zero core→commands/cli imports). Dead-code sweep: + no P2s; five P3s queued (the unreachable apply fallback + + resolveCurrentPlanningHomeSync, test-only resolveRegisteredStore + with its stale --store-path fix text, the zero-consumer references + barrel line, the PlanningHomeSummary identity wrapper, the parseJson + test-helper x11); notes recorded (mkdir copies, the checkout-path + prose convention, ext:: threat-model comment, sanctioned test-only + exports). Module sizes bounded. docs/agent-contract.md committed: + the full agent contract verified against emitting code with 14 + consistency findings — one gauntlet-grade P2 (several --json + failure paths in validate/show/status/instructions print stderr + only, no JSON document) queued for the gauntlet fix round; key-casing + and envelope-unification findings recorded as known gaps (published + JSON renames are product decisions). Net LOC vs origin/main: src + −4,478 (deletions outweigh the rebuild), test −325; gross insertions + dominated by openspec/work planning artifacts. +- 2026-06-11: Capstone (6.1) usability audits done + (`capstone/usability-audits.md`). The error-catalog walk covered 55 + wrong turns live (human + JSON): 46 pass against the + actionable/store-carrying/honest bar; 9 fail (1 P1 - a raw + YAMLParseError stack trace for unparseable configs on real roots; + 4 P2 - the corrupt-registry fix never names the file, instructions + drops its Fix line, validate summaries offer no drill-down, and + implicit-root scaffolding creates roots doctor calls unhealthy; + 4 P3). All queued for the capstone fix round - the report cannot + commit with open P1/P2s. Vocabulary sweep: docs and src clean except + ChangeStatus.initiative re-emitting stored legacy links on status + JSON (queued; schema parse tolerance stays - user data). TTFS: 2 + commands, 2 concepts, measured live; every step prints the next + command. +- 2026-06-11: Capstone (6.1) persona journeys all pass + (`capstone/journeys.md`). Journeys 2 and 3 added as standing e2e + (`test/cli-e2e/capstone-journeys.test.ts`): the layered flow + (config-driven discovery, fetch-recipe citation, design in the app + repo's own root, store read-only) and externalized planning (full + lifecycle from a pointer repo, zero --store flags, no planning state + growth). Journey 4 ran as a live cold-start dogfood: a fresh codex + session with no insider knowledge built the entire intended topology + (store setup, targets declaration, pointer config, repo mapping, + doctor/context/validate self-verification) from --help output and + generated guidance alone. +- 2026-06-11: Executed the Phase 5 remainder, closing out 5.1 + (decision record: `slices/delete-legacy-command-groups/ + remainder.md`). Deleted `schemas/workspace-planning/` (no src code + named it after 4.1, but `openspec schemas` still ADVERTISED it — a + shipped invitation into a dead workflow); deleted the four + `workspace-*` beta change folders (unimplemented planning relics — + archiving would have asserted completion); decided L2: the four + wholly-workspace accepted specs (workspace-open, + workspace-foundation, workspace-change-planning, workspace-links) + deleted — an accepted-spec library that REQUIRES the impossible is + worse than one with a gap — and the workspace requirements excised + from cli-config (the profile-apply prompt flow) and + cli-artifact-workflow (the setup-commands and schema-instructions + requirements plus eight workspace-scoped scenarios), bounded + deliberately short of the broad docs rewrite the roadmap forbids. + Incidental workspace mentions in five other specs recorded as + capstone vocabulary-audit input. All 36 remaining accepted specs + validate; full suite green untouched (1714 tests). +- 2026-06-11: Implemented slice 4.1 in two checkpoints plus a + review-fix round and a simplify pass, completing Phase 4. CP1 + executed the deletion ledger's carve-outs widened to whole-module + deaths (src/core/workspace, store/binding.ts, getRepoPath, the + policy cascade, the ten template guards with the parity test flipped + to a no-residue assertion): 27 files, −2,196 lines. CP2 added + `openspec context` — the JSON agent brief, the human working-set + listing, and the --code-workspace emitter (available members only, + typed context_file_exists refusal) — as presentation over the 3.6 + composition through a new shared command gather (doctor refactored + onto it, behavior-identical). The review round (spec-compliance + + /code-review + codex, no P1s) fixed the --json write-failure + stdout contamination (the write now precedes the brief; exactly one + JSON document), the self-reference honesty gap, the + position-fragile registry-diagnostic coupling (now selected by + code), dead policy params, leftover binding imports, and added the + working-set unit matrix. Simplify extracted the shared stale-path + sweep into shared-gather, deleted the dead Windows-path machinery + and a stale workspace-kind test, and recorded the + context_output_dir_missing plan amendment. Recorded for the + capstone: the resolver's both-shapes stderr warning fires for every + command (per-command suppression would fragment the one-resolver + contract); PlanningHomeSummary is now field-identical to + PlanningHome (deliberate JSON insulation or collapse — capstone + judges); the npm export surface shrank (workspace/binding/ + getRepoPath gone from dist) — fine pre-release. +- 2026-06-11: Wrote the assemble-working-context plan (4.1, two + checkpoints: deletions leaves-first, then assembly) and folded two + plan reviews (both approve-with-fixes). The catch that mattered: the + spec's own `code_workspace_exists` diagnostic name collides with the + vocabulary sweep's `workspace_*` token ban — amended to + `context_file_exists` (the `--code-workspace` flag is hyphen-safe). + Also folded: the parity test's workspace-planning guard assertion + flips to an absence assertion (it currently pins the guards EXIST); + the change-status-policy tranche names `ChangeStatus.affectedAreas` + and the artifact-graph barrel re-export; the doctor-extraction claim + weakened to behavior-identical (the e2e asserts fields, not bytes); + the unresolved-members-on-stderr e2e mapped; the sweep guardrail + reworded to manual-grep honesty; stale hedges resolved (the + workspace test files named; the binding tests are two its, not a + block). Both reviewers verified the deletion order dependency-safe + (planning-home drops its workspace import before workspace/ dies; + binding dies after workspace/foundation) and every anchor accurate. +- 2026-06-11: Wrote the assemble-working-context slice spec (4.1) and + folded two adversarial reviews (both approve-with-fixes, converging, + one P1 pair). The deletion-grounding P1s: `binding.ts` dies WHOLE — + 5.1 kept it only because workspace/foundation imported it, so with + workspace/ gone the entire ~300-line module (plus its registry.test + binding tests and barrel line) would be exactly the hidden-not- + deleted state the 5.1 criteria reject; and the five workflow-template + workspace-planning guards that 5.1 explicitly deeded to 4.1 ("they + quote the library contract that 4.1 deletes") are now in the + deletion list with their parity-hash and .codex churn named. Also + folded: the change-status-policy cascade enumerated + (summarizeAffectedAreas et al.); the doctor/context shared data + gather made mandatory with doctor-only inputs staying doctor-side + (context recorded as deliberately silent on wrong turns); the + member-mapping table pinned (available = path AND empty status; + stale paths and invalid ids are not-available; registry-unreadable + bare members); code-workspace write semantics pinned + (code_workspace_exists + --force, no implicit mkdir, stderr + confirmation under --json); `getRepoPath` deleted rather than + re-hidden (its recorded consumers evaporated); fetchRecipe exported + for one recipe source; the naming paragraph recorded (context vs + view vs open; project-context disambiguation). +- 2026-06-11: Decided autonomously (review me): 4.1's surface is a new + top-level `openspec context` (JSON agent brief / human listing / + --code-workspace file emitter with --force); assembly is + presentation over inspectRelationships through a shared command-layer + gather; opening is REPLACED by emitted artifacts — no open verb, no + editor launching; the deletions follow the ledger carve-outs widened + to whole-module deaths where the keep-rationale collapsed. +- 2026-06-11: Implemented slice 3.6 (relationship health) in two + checkpoints plus a review-fix round, completing Phase 3: the + health-mode assembler options (`includeSpecs: false` skipping spec + reads and the byte budget; `registryEntries` injection with []/null + semantics), the pure `inspectRelationships` composition, and the + root-scoped `openspec doctor` (normal resolution with the additive + `allowImplicitRoot` pass-through; one registry read; every recorded + Phase 3 deferral landed — both-shapes and malformed pointers + structured, inert pointer declarations, unmapped AND stale-path + targets, remote divergence info, registry-unreadable suppression; + health findings exit 0, command failures exit 1 with the null-shape + payload). The review round fixed the human-mode stack-trace P1, + added target_path_missing (the lock's checkout health now stats + mapped paths), the honest self-reference empty-line, and unified + instructions' registry read through the new injection point. + Simplify extracted `readRegistrySnapshot` (the torn-snapshot + invariant in one place), routed doctor's catch through emitFailure + (fixing a --json inconsistency), taught shared asStatus to + duck-type the diagnostic envelope, and reused storePointerProblem. + Recorded: corrupt store.yaml on store-backed roots stays an exit-1 + resolution failure (one-resolver invariant); the test-helper + parseJson copies (now 10) go to the capstone sweep. Full suite + green (96 files, 1739 tests). +- 2026-06-11: Wrote the relationship-health plan (3.6, two + checkpoints) and folded two plan reviews (subagent: + approve-with-fixes with a P1; codex: reject with two P1s — + converging). The P1s: the registry-injection option would have + inverted the established null semantics (readStoreRegistryState + returns null for a healthy-ABSENT registry and throws for a corrupt + one — a naive null-for-unreadable injection would mark every fresh + machine unreadable; the option is now `registryEntries: [] | null` + mirroring the assembler's post-read variable); `resolveRootForCommand` + forwards only store/storePath, so `allowImplicitRoot` must be an + additive pass-through; and the targets assembly omitted + `storeConfigPath` while the invalid-id synthesis would have required + parsing ids out of message strings (the inspector now receives the + raw declarations and recovers ids with isKebabId). Also folded: the + inert-pointer re-walk named explicitly (the resolved declared root + is the STORE — `findRepoPlanningRootSync(process.cwd())` finds the + pointer dir, with a subdirectory e2e); the human-rendering + contradiction resolved (the spec transcript's three headings are + authoritative; JSON keeps the four-key separation); the + truncation-never and pass-through pins mapped; the failure payload + drops its dead status key; null-targets normalization owned by the + inspector; field-absence (not emptiness) pinned. +- 2026-06-11: Wrote the relationship-health slice spec (3.6) and + folded two adversarial reviews (both approve-with-fixes, two P1s + each, converging). The P1s: the exit-code rule cited a `store + doctor` contract that does not exist (store doctor exits 0 even on + error-severity health entries; the spec now mirrors the REAL + contract — health findings exit 0, only command failures exit 1); + and the JSON shape dropped the lock's separate store-metadata + category plus the 3.4-recorded inert-pointer deferral (the shape + gains a `store` section and `pointer_declarations_inert`). Also + folded: a real `includeSpecs: false` assembler mode (post-stripping + would pay the spec-file I/O and could leak the content-only + truncation diagnostic into a health report); the assembler accepts a + pre-read registry state so doctor's one read feeds everything + coherently; `target_unmapped` suppressed under an unreadable + registry (its register fix would be wrong); grammar-invalid targets + synthesize bare entries so doctor never loses them; the both-shapes + detection mechanism named (command-side classifyOpenSpecDir, stderr + duplication accepted and recorded); the guidance-pin consequence + scoped (doctor takes --store, so STORE_SELECTION_GUIDANCE and the + skill-template parity pins change deliberately); empty-vs-unreadable + registry and --store-repo-id scenarios added; the healthy scenario + split from the none-declared rendering. +- 2026-06-11: Decided autonomously (review me): 3.6's surface is a new + top-level root-scoped `openspec doctor` (store doctor is + machine-scoped and cannot see a project repo's references; status is + change-scoped); it is pure presentation over the existing assemblers + — no new health machinery; targets health is store-level only + (per-change narrowing stays on instructions); doctor never + scaffolds (allowImplicitRoot false); remote divergence is severity + info in the store section. +- 2026-06-11: Implemented slice 3.5 (repo map) in two checkpoints plus + a review-fix round: typed registry sections with the preservation + matrix pinned (the spec-review P1); the repo command group + (register/unregister/list, pinned JSON, folder-name default ids with + the --id rewrap); cross-section id+path uniqueness inside the shared + conflict asserts (early-reject pinned: store setup with a + repo-claimed id creates nothing) AND at parse time (a hand-edited + both-sections id fails clearly); `store_id_is_repo` before both + unknown-store branches with the non-looping zero-stores fix; targets + path enrichment with the unconditional repo-map read (the review + round's converged P2: change-only narrowing enriches too); the + library API hardened (path-then-id typed input errors; no-op reruns + never take the write lock). Simplify extracted the shared + commands/shared-output failure plumbing (third copy collapsed), + hoisted the same-mapping predicate, and single-sourced the kebab + grammar wording (KEBAB_ID_DESCRIPTION). Recorded: getRepoPath stays + exported as 4.1 groundwork (unit-tested, no production caller — the + 3.3 persisted-remote precedent); the registry-state builder + quadruplication judged mirror-territory and left. Full suite green + (94 files, 1718 tests). +- 2026-06-11: Wrote the repo-map plan (3.5, two checkpoints) and + folded two plan reviews (both approve-with-fixes): the cross-section + conflict check must live inside `assertNoRegisteredStoreConflict` + itself — it has FOUR call sites including three operations + preflights, and hooking only the write helper would let `store + setup` scaffold files before failing (an early-reject pin is now + planned); `getRepoPath` reconciled as a dumb id-keyed lookup after + one read, with `repo unregister` as its 3.5 caller (the enrichment + uses `listRepoEntries` on its own single read — recorded deviation + from the spec's wording); test mappings added for store list/doctor + against a both-sections registry, the empty-list verbatim contract, + `repo_not_found`, the mixed-registry positive resolution, the + directory-untouched unregister assert, and both-surfaces enrichment; + two code-map anchors corrected. +- 2026-06-11: Wrote the repo-map slice spec (3.5) and folded two + adversarial reviews (both approve-with-fixes, converging). The P1: a + schema-only `repos:` addition would pass round-trip tests while the + four registry state-rebuild sites (`parseStoreRegistryState`, + `serializeStoreRegistryState`, `withRegisteredStore`, + `withoutRegisteredStore`) silently erased every repo mapping on the + next store write — preservation is now a pinned scenario naming the + sites. Also folded: the repo check precedes BOTH unknown-store + branches (the zero-stores fix must not suggest claiming the repo's + id — the 1.3 no-error-loops lock); cross-section uniqueness covers + PATHS as well as ids (one checkout, one role; four claimant codes + with a recorded naming convention, `repo_path_conflict` within the + section); `invalid_repo_id` with repo wording and a `--id` hint when + the default folder name fails grammar; the kebab predicate moves to + its neutral `src/core/id.ts` home (the 3.4-recorded intention); + JSON contracts pinned for all three commands; the one-additional- + registry-read wiring stated honestly (the references assembler does + not expose its read); `TargetRepoEntry` keeps `path` off the shared + declaration type; the Unicode arrow recorded as deliberate; corrupt- + registry silence recorded as design. +- 2026-06-11: Decided autonomously (review me): 3.5 ships a minimal + `repo` command group (register/unregister/list — no setup, doctor, + or remove); the registry stays version 1 with an optional strict + `repos:` section (machine-local one-way story accepted); targets + enrichment is the resolution surface plus one `getRepoPath` library + accessor for 4.1; unmapped and corrupt-registry cases stay silent + by design (3.6 owns health). +- 2026-06-11: Implemented slice 3.4 (store targets) in two checkpoints + plus a review-fix round: `targets:` in the root's config through the + shared `parseDeclarationList` (references behavior byte-identical); + per-change narrowing as kebab-validated ordinary metadata + (`isKebabId` is now the grammar's single source — `validateStoreId` + delegates); the pure `src/core/targets.ts` assembly (narrowing + replaces with remote inheritance, set semantics, degradation codes + `target_invalid_id`/`target_not_declared`) and renderers; both + instruction surfaces carry `{source, repos, status}` with provenance + through ONE wiring shape (raw declarations in, assembly inside the + generator where change metadata lives — the review round unified an + accidental asymmetry a second caller would have tripped on). Also + from review: change-level duplicates dedup, the targets warning + names repo ids, `DeclarationEntry` replaces the references-flavored + type name, the loader falls back to the self-read config's targets, + and the spec's severity-cliff wording was amended to the real blast + radius. Recorded: the workspace kebab-regex copy dies with 4.1; an + `id.ts` home for the grammar is 3.5's natural move when repo ids + become resolvable. Full suite green (92 files, 1696 tests). +- 2026-06-11: Wrote the store-targets plan (3.4, two checkpoints) and + folded two plan reviews (both approve-with-fixes): the artifact + human rendering anchored to `printInstructionsText` (the original + anchor named instruction-loader, which renders nothing); the + `--store <target-repo-id>` unknown-store pin and root-resolution + byte-identity added to the test matrix; `validateStoreId` delegates + its grammar test to the new neutral `isKebabId` so exactly one kebab + regex remains (the spec's one-source-of-truth claim was otherwise + unimplemented); `KebabIdentifierSchema` is a label factory, so the + schema usage carries the label call; the apply options bag carries + the resolved config path (the fix text needs the real .yaml/.yml + file and the resolver is private); inline expected strings replace + the snapshot wording (the repo has no snapshot files); the e2e gains + a second non-narrowed change and exit-0 asserts on warning cases. +- 2026-06-11: Wrote the store-targets slice spec (3.4) and folded two + adversarial reviews (both approve-with-fixes, converging): the apply + surface loads change metadata inside `generateApplyInstructions`, so + targets assembly for apply runs inside with store targets passed via + the options bag (the spec's original wiring claim was wrong for that + surface); empty change-level `targets: []` is treated as undeclared; + the JSON shape carries `status` (always present, `[]` when clean) so + agents see degradation diagnostics; narrowed ids inherit the store + declaration's remote; the change-level grammar cliff is owned + explicitly (a bad id fails metadata reads everywhere, like any + metadata error); `KebabIdentifierSchema` is the named validator (not + `affected_areas`, which has no grammar); a neutral shared kebab + predicate replaces store-flavored naming at both config call sites; + declared-root/pointer sessions covered; pointer-dir targets recorded + as silently inert (3.6 surfaces that wrong turn); the inert-scenario + GIVEN narrowed to well-formed lists. +- 2026-06-11: Decided autonomously (review me): 3.4 puts `targets:` in + the root's `openspec/config.yaml` with the references entry shape + via one shared declaration-list parser; per-change narrowing is a + plain string array in `.openspec.yaml` that REPLACES the store list; + the display surface is instructions output (`{source, repos, + status}` + `<target_repos>`/`### Target Repos` blocks, no byte + budget); degradation codes `target_not_declared` and + `target_invalid_id` with envelope target 'targets'; no new commands; + targets never resolve (3.5 owns resolution, 3.6 health, 4.1 + assembly). +- 2026-06-11: Implemented slice 3.3 (store canonical remote) in two + checkpoints plus a review-fix round: the optional `remote` in + `store.yaml` (strict schema retained; `setup --remote` writes it + before the initial commit, refuses empty values and existing + identity files); observed origins probed read-only into the + machine-local registry at setup (both backend-resolution sites) and + register, with rerun-safe reporting (a same-checkout origin backfill + refreshes the entry but reports `already_registered`); doctor's + `metadata.remote` + `git.origin_url`; the sharing chain canonical → + observed → today's wording; `{id, remote}` reference declarations + normalized with fill-if-absent dedup; and the unresolved-reference + fix as a verbatim-pasteable absolute-path clone command. The review + round caught and fixed: the nested-repo origin leak (git -C walks + up — probes now guard with an at-root check), shell-quoting and + flag/metacharacter injection in the rendered clone fix (shell-inert + allowlist with teammate-wording fallback), the execute-phase TOCTOU + on --remote, and the rerun-reporting break. Simplify extracted the + duplicated hand-edit thrower and restructured registration around a + normalized `sameCheckout` predicate (fixing a symlinked-path + reporting edge). Capstone notes recorded: the `~/openspec/<id>` + convention lives in one computed + five prose sites; the remote + allowlist admits git's `ext::` transport (team-committed configs + only — harden to recognized URL shapes if remotes ever arrive from + less-trusted sources). Full suite green (90 files, 1678 tests). +- 2026-06-11: Wrote the store-canonical-remote plan (3.3, two + checkpoints) and folded two plan reviews (both approve-with-fixes): + the clone fix renders ABSOLUTE home paths (`~` never expands outside + a shell and agent JSON consumers execute argv directly — the spec's + `~/openspec/<id>` form is amended); setup's origin probe must reach + BOTH backend-resolution sites (`prepareSetupPlan` and + `setupPreparedStore`) or the rerun path re-introduces the erasure + P1, and it stays at call sites rather than inside + `resolveGitStoreBackendConfig` (hot read paths); the + sharing-guidance mechanism is concrete (`StoreMutationResult` gains + canonical/observed remotes, dropped from JSON, rendered by + `printMutationHuman` canonical → observed → today's wording); the + spec's setup-JSON contradiction resolved in favor of the unchanged + `StoreOutput` shape; `getOriginUrl` trims probe output; the + `--remote`-vs-existing refusal moves into `prepareStoreSetup` before + any prompt or write; dedup pins the fill-if-absent duplicate case; + registry persistence anchors corrected; TEST-NET fixtures use + `git remote add`, never clone. +- 2026-06-11: Wrote the store-canonical-remote slice spec (3.3) and + folded two adversarial reviews (subagent: approve-with-fixes with a + P1; codex: reject — converging). The P1: a setup rerun would have + silently erased the registry's observed remote because only register + probed the origin while `storeBackendsMatch` compares remotes; the + fix probes in both flows, preserving the 1.3 rerun-no-op contract. + Also folded: register's contract restated precisely (never commits, + never modifies an EXISTING store.yaml — the confirmed-conversion + path still creates `{version, id}` identity, without a remote); the + strict-schema compatibility claim corrected to its real one-way form + (old CLIs reject remote-bearing store.yaml; recorded as a standing + constraint that 3.4 must not add store.yaml fields without a version + bump or strictness revisit); mixed-shape references dedup defined + (normalize to `{id, remote?}[]`, dedup by id, first remote wins); + the clone fix made pasteable verbatim via the `~/openspec/<id>` + convention; `setup --remote` against an existing store.yaml fails + with the hand-edit fix instead of silently ignoring the flag; the + doctor UX example redrawn from the real layout; the no-network + clause made testable (TEST-NET URL pin). +- 2026-06-11: Decided autonomously (review me): 3.3 keeps two remotes + in two homes — team-authored canonical in committed `store.yaml` + (written only by `setup --remote` or hand-editing), observed origin + machine-local in the registry (probed read-only at setup/register, + refreshed by re-register, live-probed for display; the persisted + copy is 3.6 groundwork). The unresolved-reference clone source rides + the reference declaration (`{id, remote}` map entries) because no + local store state exists for an unregistered store. Resolved index + entries gain no remote field; `StoreOutput` stays unchanged (doctor + is the inspection surface); no new diagnostic codes. +- 2026-06-11: Implemented slice 3.2 (declared-store fallback) in two + checkpoints plus a review-fix round: the `store:` pointer in + `openspec/config.yaml`, the resolver classification (directory-typed + shape stats; warning-silent pointer read; `invalid_store_pointer` + with unparseable/non-string reasons; the declaration-origin rewrap; + `source: "declared"`), the `isStoreSelectedRoot` predicate across + all eight consumers, the both-shapes stderr warning, and the init + pointer guard (refuses malformed pointers and pointer-repo + subdirectories, anchored before any mutation). Three review + mechanisms found one real regression — empty/comments-only configs + briefly classified malformed, which would have stranded the + documented comment-out conversion path — fixed with regression tests + alongside the shared `classifyOpenSpecDir` (resolver and init can + never disagree), the shared config probe, and the consolidated + snapshot test helper. A simplify pass made the predicate a type + guard and single-sourced the malformed-reason strings. Full suite + green (89 files, 1656 tests); the e2e journey proves the full + lifecycle in a pointer repo with no `--store` anywhere, composing + with 3.1's references through the declared root. Process note: one + review-fix commit landed on a detached HEAD (an agent moved HEAD + during the fan-out) and was fast-forwarded back onto the branch. +- 2026-06-11: Wrote the declared-store-fallback plan (3.2, two + checkpoints) and folded two plan reviews (both approve-with-fixes): + an EIGHTH `source === 'store'` check surfaced + (`show.ts:160` `printNonInteractiveHint`) — the spec's seven-site + inventory is amended; the init guard moves to immediately after + `validate()` (legacy cleanup and the global-config migration write + run before `createDirectoryStructure`, so the original anchor would + have violated "creates nothing"); the declaration-origin prefix is a + call-site rewrap preserving codes and an unprefixed fix field (the + template-prefix idea missed the `fromStoreError` pass-throughs); the + targeted config read is a shared exported helper so init does not + duplicate it; the test matrix gained all five prefixed taxonomy + codes, the no-write malformed-pointer assertion, deterministic + byte-identity commands, and positive assertions for the config-only + no-pointer case. +- 2026-06-11: Wrote the declared-store-fallback slice spec (3.2) and + folded two adversarial reviews (subagent: approve-with-fixes with a + P1; codex: reject with two P1s — converging). The biggest catch: the + spec's own UX example used a relative path while its core decision + requires declared roots to behave exactly like `--store` roots; the + fix is one store-selected predicate (`storeId` set) adopted by all + seven `source === 'store'` consumers (banner, hints, new-change + display, status threading, validate/show suggestion suppression, + archive's absolute cross-root paths). Also folded: `openspec init` + refuses to scaffold a pointer directory (conversion requires + removing the `store:` line first); malformed pointers fail with + `invalid_store_pointer` instead of silently flipping the write + target; pointer resolution is one hop; the resolver's config read is + warning-silent; the two shape stats require directories; the + declaration-origin error is a true prefix via a `declaredOrigin` + parameter on the shared pipeline (no fork). +- 2026-06-11: Decided autonomously (review me): amended the locked 3.2 + wording "doctor warns when a root has both planning shape and a + pointer" — no project-level doctor command exists, so the warning + lives in resolution stderr (once per invocation, both modes), and + 3.6 owns the structured health surface. Also decided: a config-only + directory with no `store:` key keeps today's root behavior (freshly + initialized minimal roots keep working); hint continuity appends + `--store <id>` for declared roots so pasted hints work from any cwd. +- 2026-06-11: Implemented slice 3.1 (store references) in two + checkpoints plus a review-fix round: `references:` in + `openspec/config.yaml` (raw-string parsing), the + `src/core/references.ts` assembler (one registry read, the narrow + `inspectRegisteredStore` extraction shared with `resolveStoreRoot`, + fence-aware first-Purpose-line summaries, five warning codes, the + 50KB budget shared with the context cap and measured against the + real rendering in UTF-8 bytes), and the index wired into both + instruction surfaces in both modes with an omitted-not-empty JSON + contract. Three post-implementation review mechanisms found no P1s; + the six converged findings (fence-poisoned summaries, the + empty-vs-omitted contract, the orphan truncation fix line, budget + under-counting, corrupt-registry branch ordering, a throwing + inspection path) were fixed with regression tests, and a simplify + pass consolidated the new test fixtures into + `test/helpers/openspec-fixtures.ts`, deleted a dead defensive + wrapper, and single-sourced the 50KB cap. Full suite green + (88 files, 1641 tests); the e2e layered-flow test proves the + PM-to-dev journey against the built binary including the verbatim + fetch. +- 2026-06-11: Wrote the store-references plan (3.1, two checkpoints) + and folded two parallel plan reviews (both approve-with-fixes): pure + renderers live in core beside the assembler so the 50KB budget + measures the real output (truncation stops before the cap with the + warning line exempt); the `inspectRegisteredStore` extraction cut is + pinned narrow (metadata/health stages only — registry lookup stays in + `resolveStoreRoot`, whose seven error codes stay byte-identical); + config is read once in the command layer and suppresses the + generator's internal read; the Purpose-line scanner is self-contained + (the markdown parser's section methods are protected); and the test + matrix gained the symmetric `--store`, boundary byte-identity, + no-recursion, nothing-frozen, and not-inlined assertions. +- 2026-06-11: Wrote the store-references slice spec (3.1) and folded + two adversarial review rounds (subagent: approve-with-fixes with two + grounding P1s — `parseSpec()` throws on imperfect specs so summaries + extract tolerantly, and the apply human surface exists so the index + lives in both surfaces and both modes; codex: approve-with-fixes — + the assembler is async at the command boundary and passed into the + sync generators, the rendered index shares the 50KB context budget + with order-preserving truncation, and registry corruption degrades + to `reference_registry_unreadable`). Decided autonomously (review + me): five warning diagnostic codes (unresolved/invalid-id/ + root-unhealthy/registry-unreadable/index-truncated) that degrade + instructions instead of failing them; the parse-raw/ + validate-in-assembler split; the one-level no-recursion rule; + symmetric declarations (the resolved root's config, store or repo); + self-references silently omitted; summaries from the first Purpose + line with bare-id rendering when absent; zero-spec stores index as + empty entries; no workflow-template changes; the docs home is a new + "Referencing stores from a project" subsection in docs/cli.md. +- 2026-06-11: Implemented the delete-legacy-command-groups slice (the + Phase 5 first tranche) in one commit: the `workspace` and `initiative` + command groups, the five orphaned core workspace modules, the whole + collections tree, the completions entries, the config command's + workspace-profile integration, the update command's workspace + detection, and every doc that documented only them — net **−12,903 + lines** (+324/−13,227), with seven new removal-coverage tests, a + sweep pin on the surviving token allowlist, and `deletion-ledger.md` + (41 removed diagnostic codes; dead-export carve-outs owned by 4.1). + Side benefit: every CLI invocation loads ~25 fewer modules. Three + post-implementation review mechanisms found no P1s; all P2/P3 fixes + and a simplify pass landed (dead helper deleted, redundant fixtures + removed, byte-identity test hardened with directory markers and an + asserted update spawn, project-apply accept path regained coverage). + Full suite green (85 files, 1616 tests). +- 2026-06-11: Decided autonomously (review me): ground truth uncovered + during the deletion — `actionContext.mode: "workspace-planning"` has + been **unreachable from the CLI since slice 1.2**, whose resolver + rework routes every supported command through `toPlanningHome` + (hardcoded `kind: 'repo'`). The deletion spec's planning-home scenario + was corrected to pin the byte-stable `repo-local` CLI behavior plus + the library contract (`buildActionContext` unit pin); the template + guards stay as text quoting a contract that only the library can + still produce, and 4.1 deletes both. Also recorded: the accepted spec + library (`openspec/specs/cli-config`, `workspace-*`, + `cli-artifact-workflow`) still REQUIREs deleted behavior — that is + parked Later Idea L2, surfaced in the deletion ledger as a capstone + known-gap. +- 2026-06-11: Implemented slice 1.4 in four green checkpoints on + `codex/store-root-parity`: (1) the total mechanical rename — command + group `context-store` → `store`, 45 diagnostic codes, dotted targets, + JSON keys, data dir `stores/`, internal modules and symbols, every + help/error/hint string; (2) the two riders — `workspace open` lost its + legacy store selectors (persisted path-bound views still reopen), and + the store group gained an unknown-subcommand hint that owns the + Commander error path; (3) guidance regeneration via a three-stream + fan-out — store-selection teaching in all workflow templates, docs + accuracy pass (cli.md, concepts.md, workspaces-beta with `--path` + correctness fixes, all invocations smoke-run), legacy-beta labels; + (4) guards and proof — vocabulary sweep-as-test, committed-format + pins, old-data-dir negative fixtures, `--store` description equality, + telemetry path, and the headless dogfood (one plain prompt → agent + discovered the store via `--help` + `store list` and created the + change with `--store`; transcript committed). Post-implementation + review ran three parallel mechanisms (spec-compliance: compliant, all + 16 scenarios pass; /code-review high: 10 verified findings; codex CLI: + approve-with-fixes); both P2s fixed (the hint builder's invalid + suggestions; guidance over-claiming the flag surface and reaching the + storeless feedback workflow) plus the cheap P3s, then a simplify pass + made the presence guards registry-driven and tied the guidance's + taught command list to the live flag surface. Full suite green + (95 files, 1745 tests). +- 2026-06-11: Wrote the store-rename-and-guidance plan (four green + checkpoints: mechanical rename, riders, guidance regeneration with a + three-stream fan-out, sweep/guards/dogfood) and folded two parallel + plan reviews (subagent and codex CLI, both approve-with-fixes): the + rider-1 deletion list now names the unreachable guard branch and + preserves persisted path-bound view state; rider 2 owns the whole + Commander `command:*` error path; the docs pass gained + `docs/concepts.md` and runtime-correctness fixes for beta-doc examples + (`--path` since 1.3) plus a built-binary invocation smoke; the sweep's + roots exclude the `openspec/` planning history by design; old-data-dir + negative fixtures (valid and corrupt) and exact-equality + `--store`-description tests were added; the dogfood pins + `openspec init --tools claude --profile core`. Spec updated in the + same round for docs scope and sweep-root consistency. +- 2026-06-12: User-directed (owner review of the 4.1 autonomous + decisions; full direction in `workset-direction.md`): the 4.1 surface + renames `openspec context` → `openspec workset`, anchored on the work + item (`workset <change>`; bare form keeps the root union) with + change-named `.code-workspace` files as the durable, reopenable views; + a launch consumer joins scope because emitted paths do not cross agent + sandbox boundaries (the working set must shape the session boundary — + workspace file for IDE agents, boundary flags for CLI launch); the + 3.5 `repo` command group dissolves into a single plumbing command + (`openspec map`), with point-of-need prompts and diagnostic fix + strings as the primary fill paths and machine tokens kept as shipped; + no workspace-style grouping registry returns. Grammar guardrails + recorded: noun groups only for closed-set product objects, generic + verbs for collections (no per-collection groups, ever), "workspace" + permanently retired, lifecycle stays in skills/schemas. To be + implemented as a follow-up slice under the standard discipline. +- 2026-06-12: Ran the 7.1 research checkpoint and committed + `slices/personal-worksets/research.md`: the `f858c19^` opener + archaeology (the implicit two-style split, the PATH/PATHEXT scan, + cross-spawn handoff mechanics, the not-to-inherit ledger), the + current-tree idioms (registry lock/atomic-write, the pure + `.code-workspace` builder, `@inquirer` house rules, JSON contracts, + the recoverable fake-executable test helpers), and live verification + of all four built-in tools' flag spellings and hazards (the cursor + shim's `agent` first-arg hijack; both agent CLIs read a positional + as a starter prompt). +- 2026-06-12: Wrote the personal-worksets slice spec (7.1) and folded + two adversarial reviews (subagent: approve-with-fixes, every + citation verified; codex: reject — converging). The P1: the draft's + attach-dirs argv skipped the primary member and leaned on `cwd`, + contradicting the locked "one attach flag per member" — argv now + carries an attach pair for every member (primary included, + single-member shapes pinned). Also folded: the no-tool open path + (interactive prompt / typed `workset_tool_required`), the + stale-saved-tool rule (`tool` parses as a plain string; unknown ids + surface at open with the manual fallback), the signal exit contract + (`128 + n`, no banner), the hand-edit parse contract (absolute + paths, non-empty members, label rules, duplicates), pinned JSON + envelopes for all four subcommands including the `open --json` + typed rejection and the `command:*` handler, derived-file lock + semantics with ENOENT-tolerant remove, the teammate/arbitrary- + composition scenario, the win32 availability matrix, and the + opener-config touchpoints (hand-edit-only at v1; `config set` + rejects unknown keys; malformed-config degradation recorded). +- 2026-06-12: Decided autonomously (review me): the 7.1 spec's open + shapes — the group is `workset create/list/open/remove` (no edit at + v1; recompose or hand-edit); saved views live in one machine-local + `<dataDir>/worksets/worksets.yaml` on the store-registry idiom with + the generated `<name>.code-workspace` files beside it, regenerated + on every open (deleting `worksets/` removes every trace); workset + names use the one kebab grammar in their own namespace; opener + config is an `openers` key in global `config.json` (hand-edit-only + at v1) merged over built-ins per-field; `open` carries no `--json` + mode (typed one-document rejection instead — stdio-inherit handoff + cannot compose with the JSON contract); child exit codes and + signals propagate honestly (`code` / `128+n`, no banner); + `writeFileAtomically` and the lock loop extract to a shared + `src/core/file-state.ts` now that they have two call sites; agent + guidance does not teach worksets at v1 (human convenience; template + parity pins stay untouched). +- 2026-06-12: Ran the 7.1 capstone dogfood + (`slices/personal-worksets/capstone-dogfood.md`). Scripted walk in a + scratch env (isolated XDG, fake code/cursor/claude/codex on a + controlled PATH, built CLI): compose→list→open for both styles with + exact argv verified from the launch log — code got exactly the + generated workspace file, claude/codex got one --add-dir pair per + member with the primary included and codex's sandbox pre-args, no + positional anywhere; the unknown-tool strand test printed the + manual fallback; the missing-member skip, safe remove, and + byte-untouched member folders all held. The interactive wizard ran + from a real pty via expect (name → `.` default member → tool + select → open-now declined → reopen line; a stdin-EOF run + exercised Cancelled./130 live). Cold start: a fresh headless codex + session with no insider knowledge — told only that "the openspec + CLI can keep a named view of folders" — reached an opened workset + from `--help` alone (group discovery, subcommand help, repeatable + --member compose, open; launch log and saved yaml verified). No + product findings; the one defect surfaced was in the dogfood's own + first fake-tool shim. Full suite re-run green (101 files, 1799 + tests); capstone box ticked. +- 2026-06-12: Ran the 7.1 /simplify pass (four parallel cleanup + agents: reuse, simplification, efficiency, altitude) and applied + the converged fixes: the two textually-parallel lock-error + factories collapsed into a data-parameterized + `makeLockErrorFactory` in file-state (the altitude verdict — the + fix strings document the lock's own stale-steal/creation behavior, + so the templates belong with the mechanism; store shapes stay + byte-identical under their pins); the hand-rolled group-option + merge replaced by Commander's built-in `optsWithGlobals()`; the + prompt module's preview-helper ladder flattened with one + `assertKnownTool` spelling; `asErrorMessage` hoisted to + shared-output (store's private copy deleted, `asStatus` reuses it); + the member-row renderer deduped across list/fallback/remove-confirm + (`formatMemberRows`); open's `availabilityVerified` flag replaced + by per-branch opener resolution (deleting an unreachable branch and + the prompt path's redundant PATH re-scan); `serializeWorksetsState` + emits the schema-validated entries directly; a `toWorkset` helper + deduped the entry conversion; remove's `--yes` path skips the + duplicate pre-read; `KEBAB_ID_FIX` adopted at the two remaining + literal sites; dead exports unexported; the pathIsDirectory-vs- + FileSystemUtils choice documented in place. Skipped with notes: + converging the store group's `command:*` fallback onto the + group-action pattern (cross-slice; queued for the next store + touch), threading the create→open table/scan hints (low value, + adds coupling), and the predating change-metadata kebab literal. + Full suite green (101 files, 1799 tests). +- 2026-06-12: Ran the 7.1 post-implementation review — three parallel + mechanisms (spec-compliance agent: compliant-with-fixes, all locked + decisions hold; /code-review at high effort via a seven-angle + finder fan-out; codex 5.5 high: approve-with-fixes) — and fixed + every converged P2 plus the cheap P3s in one round. No P1s + anywhere. Behavioral fixes: the open fallback rule is structural + (every post-regeneration failure except prompt cancellation carries + "Open manually:" with the surviving members — the curated code set + had already drifted past invalid_opener_config and + workset_tool_required); the primary-reassignment note is printed; + zero-installed-tools interactive opens say so instead of + misreporting the table's first row; launch failures get a pasteable + --tool alternative; Ctrl-C at the post-save open-now offer declines + the offer instead of reporting a saved create as cancelled; the + parent ignores SIGINT/SIGTERM while a launched tool runs (the 128+n + contract was unreachable for tty Ctrl-C — the parent died first); + synchronous spawn throws map to workset_launch_failed; the + tool.cmd PATHEXT double-append is gone (the scan agrees with + spawn-time resolution); the bare `workset --json` probe keeps the + one-JSON-document contract via a group-level option + action + handler; the shared lock's stat-failure path is deadline-bounded + (was a pre-existing store busy-spin hazard); remove's derived-file + cleanup now follows the durable write; flag members validate + before the wizard spends the user's time; cross-spawn loads lazily + (~6ms off every CLI invocation, measured). Structure: the command + layer split into workset.ts / workset-prompts.ts / workset-input.ts + (the 838-line module had crossed the lean bar); formatZodIssues, + folderStyleNameProblem, KEBAB_ID_FIX, pathIsFile/pathIsDirectory/ + isNodeErrorCode each have one shared home; the prompt-cancellation + branch lifted into emitFailure with store's private copy collapsed. + Tests: the guided-flow success path, post-save-cancel, bare-group + probes, the launch-failure (ENOEXEC garbage executable), corrupt + open leg, and the win32 as-is matrix added; the in-process + interactive tests pin a controlled PATH (they silently depended on + the host's installed tools), and withPrependedPathEnv prefers the + base env's PATH key (a win32 duplicate-key hazard). Spec amended in + the same round (d2, d6, d8, d10-d14: the shipped contracts). + Recorded for /simplify: the two lock-error factories are + textually parallel (data-parameterizable); StoreError as the + envelope class for non-store domains is an accepted altitude + tradeoff (asStatus duck-types the envelope; a neutral + DiagnosticError rename is capstone-scale, not slice-scale). Full + suite green (101 files, 1799 tests). +- 2026-06-12: Implemented slice 7.1 in two checkpoints. CP1 (e8bf29b): + `src/core/file-state.ts` extracts the lock/atomic-write mechanics + from store foundation with caller-owned error factories (the store + shapes pinned byte-identical by new tests — the suite had never + covered the lock); `src/core/worksets.ts` (the saved-views file on + the registry idiom, hand-edit parse contract, `withWorksetsLock`, + the `.code-workspace` builder); `src/core/openers.ts` (the locked + built-in table, per-field config merge, the PATH/PATHEXT scan with + an injectable stat seam, the pure two-style argv builder). CP2 + (d6fb613): the `workset` command group (guided create, list, open + with regenerate-before-tool-resolution and honest exit/signal + propagation and the every-failure manual fallback, remove with + lock-scoped ENOENT-tolerant derived cleanup), registration + + completions, the docs/cli.md section, the resurrected fake-tool + test machinery, 34 command tests, and the two e2e journeys + (no-footprint with context/doctor byte-identity; two-machine + teammate isolation). Full suite green (101 files, 1795 tests). + Decided autonomously (review me): `workset_name_required` was added + for non-interactive create without a name (the spec family had no + missing-name code; mirrors `store_setup_id_required`); the + `--no-interactive` flag is not declared (parity with the store + group: the gate is `--json`/env/TTY); fake-tool tests pin a fully + controlled PATH after a first run launched the machine's real + cursor. +- 2026-06-12: Wrote the personal-worksets plan (7.1, two checkpoints: + core storage/openers with the file-state extraction, then the + command group with fakes-on-PATH tests) and folded two plan reviews + (subagent: approve-with-fixes — "one of the cleanest code maps + audited", three anchors drifted 2-3 lines; codex: reject — + converging). The shared P1: the open flow checked tool availability + BEFORE regenerating the `.code-workspace`, so the + unknown/unavailable-tool fallback could name a nonexistent file — + reordered to regenerate under the lock first, with the fallback + test now asserting file existence and currency. Also folded: the + false "foundation tests pin the extraction" claim corrected + (nothing in the suite covers the lock/atomic mechanics — CP1 adds + the two store busy-error byte-shape pins itself), the + `withWorksetsLock` read-without-write primitive open needs, the + real error-factory sites (`create-failed`/`timeout`; stale-steal is + silent), the cross-spawn `createRequire` import shape (ESM package, + no types), the Commander `--member` collector (repeated options + keep only the last value by default), launch mechanics fake + executables cannot reach moved to injectable-spawn units + (SIGINT-130, spawn-error → `workset_launch_failed`), interactive + cancellation covered in-process via a stubbed gate + mocked + prompts with the remaining interactive-only lines enumerated to + the capstone, the win32 fixture trap (injectable stat seam), the + lock-release→spawn TOCTOU recorded as accepted, and the spec's + d12 amended in the same round (`workset_create_cancelled` dropped — + create has no abort-confirm, so the code had no firing site). +- 2026-06-12: Continued owner design review replaced the change-anchored + workset direction with roadmap item 7.1 (briefly numbered 4.2 the + same day; moved to its own Phase 7), personal worksets: a purely + local, manually composed, named working view, opened via a two-style + extensible opener table (`workspace-file` / `attach-dirs`); FR1 + (compose and keep) and FR2 (open in your tool) recorded with locked + decisions — no starter prompt on agent opens, no `--print` mode, + desktop apps deferred, "workspace" stays retired. The 7.1 section + carries the research checklist; the runbook gained the 7.1 follow-up + run invocation. `openspec context` and the repo map are explicitly + untouched by 7.1. +- 2026-06-12: Closed the 7.1 pushed-branch box. The branch is pushed + through the capstone commit. PR #1190 review-comment disposition: + the two slice-touching comments that arrived mid-run were fixed and + pushed by the run itself (the Windows-compatibility pass and the + platform-aware clone-fix pin); the three remaining open inline + comments predate the run and touch nothing in the slice (spinner + early-return quick-wins in `workflow/instructions.ts` and + `workflow/status.ts`, and a keyboard-accessibility note on the + slice-1.2 decision-review HTML artifact) — parked for a cleanup + pass rather than expanded into 7.1 scope. 7.1 is complete through + every pre-merge box. diff --git a/openspec/work/simplify-context-and-workspace-model/runbook.md b/openspec/work/simplify-context-and-workspace-model/runbook.md new file mode 100644 index 000000000..284822dbb --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/runbook.md @@ -0,0 +1,256 @@ +# Roadmap Runbook + +Start the run from a fresh session in this repo on `codex/store-root-parity` +with exactly this command (interactive, or headless via +`claude -p '/goal ...'`): + +```text +/goal ROADMAP QUEUE COMPLETE: every item in the work queue defined in +openspec/work/simplify-context-and-workspace-model/runbook.md (slice 1.4, +the Phase 5 command-group deletion slice, 3.1-3.6, 4.1, the Phase 5 +remainder, and the 6.1 final acceptance capstone) has all of its roadmap.md +progress boxes ticked except "Merged to main", the full pnpm test suite +passes, all work is committed on codex/store-root-parity, and the +capstone's release-readiness report is committed with no open P1/P2 +findings. Work strictly per the runbook, one coherent unit per turn, never +waiting for the user; or stop after 300 turns. +``` + +This file is the contract for the autonomous run that works through the +`simplify-context-and-workspace-model` roadmap. The driver is `/goal` +(condition-based: turns fire back-to-back until the completion condition is +met — no schedule, no waiting for the user). Each turn does one coherent +unit of work and ends with an explicit status the goal evaluator can read. +All work happens on `codex/store-root-parity` (see the single-branch +workflow note in `roadmap.md`). + +Architecture: the goal-driven main loop is the sequential spine (one +judgment-bearing unit per turn, bookkeeping between phases); parallel review +phases run as multi-agent Workflows; the `/code-review` and `/simplify` +skills and the codex CLI provide independent review machinery — these skills +run from the main loop, never from inside workflow agents. + +## Follow-up run: 7.1 personal worksets (added 2026-06-12) + +The original queue is complete. Item 7.1 runs as its own goal, from a +fresh session on `codex/store-root-parity`: + +```text +/goal 7.1 COMPLETE: roadmap item 7.1 (personal worksets) in +openspec/work/simplify-context-and-workspace-model/roadmap.md has all +of its progress boxes ticked except "Merged to main" — including the +capstone dogfood and the pushed-branch box — the full pnpm test suite +passes, all work is committed on codex/store-root-parity, and the +branch is pushed to origin with code-review comments addressed. Work +per the runbook's per-slice discipline with the slice folder +slices/personal-worksets/; the 7.1 section's functional requirements, +locked decisions, and research checklist are the requirements baseline +and are owner-directed — do not relitigate them. Start with the +research checkpoint (the old launch mechanics at f858c19^ are the +evidence base). One coherent unit per turn, never waiting for the +user; or stop after 80 turns. +``` + +7.1 is a build slice: full review discipline (the deletion-slice trim +does not apply). Two run-specific amendments (owner-directed, +2026-06-12): + +- **Push allowed for this run — the working branch only.** After the + post-implementation review fixes land, and again at bookkeeping, + push `codex/store-root-parity` to origin. Then check PR #1190 for + code-review comments touching the slice and address each one (fix + it, or record a reply-with-rationale in the changelog). Merging to + or pushing `main` remains forbidden; the Hard boundaries section's + "never push at all" is superseded by this paragraph for this run + only. +- **7.1 capstone — after the simplify pass, before bookkeeping.** + Prove the feature end to end from the user's seat, headlessly: in a + scratch environment with isolated XDG state and fake `code` / + `cursor` / `claude` / `codex` executables on PATH, walk + compose → list → open for both launch styles, verifying the + generated `.code-workspace` contents and the exact launch argv per + tool (including the no-prompt rule for agent opens). Then a + cold-start UX walk: a fresh headless agent given only `--help` + output and no insider knowledge must reach an opened workset. + Record the transcript in the slice folder, fix what it surfaces, + re-run the full suite, and tick the capstone box. + +All other sections of this runbook apply unchanged. + +## Re-anchor (every turn) + +1. Read `roadmap.md` — Progress At A Glance, the next-incomplete-item + pointer, and the current slice's section. Read `goal.md` and `AGENTS.md` + if not already in context. Trust the files over conversation memory; + context may have been compacted. +2. The work queue, in order: slice 1.4 → the Phase 5 command-group deletion + slice → 3.1 → 3.2 → 3.3 → 3.4 → 3.5 → 3.6 → 4.1 → Phase 5 remainder → + **6.1 final acceptance capstone** (see roadmap Phase 6 and the section + below). All product decisions are locked in `roadmap.md` ("Decisions + locked" blocks, Rules We Should Not Forget, the 1.4 terminology + checkbox, the 5.1 criteria); do not re-open them. + +## Per-slice discipline (evolved from slice 1.3's) + +1. **Spec**: write `slices/<slice-name>/spec.md` in the established format + (Outcome, Locked Decisions, User Experience, Scope, Acceptance Criteria + with GIVEN/WHEN/THEN scenarios). Ground every claim in current code. +2. **Spec review** — run in parallel (Workflow for the agents, Bash for + codex): one adversarial review agent + one codex CLI review. Fold all + findings; record the round in the roadmap changelog. +3. **Plan**: write `slices/<slice-name>/plan.md` (Status, code map with + file:line anchors, implementation plan, test plan, risks, done + definition). +4. **Plan review**: same parallel shape as spec review. Fold findings. +5. **Implement** on this branch. Build clean; full `pnpm test` green before + any implementation commit. Update existing tests deliberately, never by + loosening contracts. +6. **Post-implementation review**, three independent mechanisms in + parallel (none of them edits): + - a spec-compliance agent (Workflow) checking the implementation against + the slice spec scenario by scenario; + - the `/code-review` skill at high effort for correctness findings; + - a codex CLI review of the commit range. + Fix all P1/P2 findings and cheap P3s; re-run the full suite. +7. **Quality pass**: run `/simplify` on the changed code — serial, after + correctness fixes land, because it edits the working tree. Re-run the + full suite; commit. +8. **Bookkeeping**: tick the slice's roadmap progress boxes, update the + next-item pointer and Progress At A Glance, add changelog entries, keep + the slice spec/plan consistent with what actually shipped, commit. + +## Standing quality bars (checked in every slice's reviews) + +- **Vocabulary**: new user-facing strings use only the locked nouns (store, + reference, target project repo, OpenSpec root). One concept, one token — + no synonym drift. +- **Error UX**: every new error or hint names the concrete next action, + carries `--store <id>` when a store is selected, and uses absolute paths + cross-root. A hint a user pastes must work verbatim. +- **Agent contracts**: new JSON fields and diagnostic codes follow the + existing shared shapes (the root block pattern; severity/code/message/fix + diagnostics). Additive, consistent, no parallel envelope styles. +- **Lean modules**: a touched module exceeding ~600 lines triggers a split + or a recorded reason. New abstractions need at least two real call sites + or a recorded reason — no speculative generality. +- **Dependency direction**: core never imports from commands; store Git + mechanics stay behind the single git module; config parsing and + instruction injection stay in their own modules. Root resolution remains + exactly one shared code path — no command-local forks of precedence. + +Codex review invocation: `codex exec` non-interactively with model 5.5 at +high reasoning (`-c model=...` and reasoning-effort overrides; confirm the +exact model id with `codex exec --help`/config on first use and then reuse +it). Give codex the commit range or artifact paths and ask for findings with +severity and file:line evidence. + +Deletion-slice review profile (Phase 5 remainder only): spec review keeps +the full dual shape (subagent + codex); plan review runs the adversarial +subagent alone, no codex; post-implementation review runs the +spec-compliance agent and `/code-review` at high effort, no per-slice +codex. Rationale: deletion slices are mechanical, their review-fix rounds +have been the smallest of the run, and the 6.1 whole-delta codex review +re-covers every deleted line anyway. Build slices (4.1) keep the full +discipline. + +Slice-specific acceptance: + +- **1.4**: after implementation, run the dogfood proof headlessly — in a + scratch project with isolated XDG state and a registered store, a fresh + headless agent session must complete a store-scoped change from a single + prompt without hand-holding. + +## Final acceptance capstone (6.1 — last queue item) + +The capstone proves the *product*, not the slices. It only passes when a +cold user could start using this today. Its checks: + +1. **Persona journeys**, each as an e2e test or headless dogfood: + - Fresh team: create a store, work a change through archive, commit and + push locally; second checkout clones, registers, continues (the 1.3 + journey must still pass after the rename and deletions, with new + names). + - Layered flow: requirements in a store; an agent in an app repo that + references it discovers the relationship from config, cites the + upstream spec, writes a low-level design in the app repo's own root. + - Externalized planning: a repo with no local root and a fallback + declaration runs the normal lifecycle without `--store` repetition. + - Cold start: a fresh headless agent, given only a vague human prompt + ("set up planning in a separate repo for this project") and no insider + knowledge, succeeds using only `--help` output and generated guidance. +2. **Usability audits**: an error-catalog walk (every likely wrong turn on + the new paths yields an actionable, store-carrying error); a vocabulary + sweep (zero "context store"/initiative/workspace residue in any + user-facing surface, including `docs/cli.md`); a documented + time-to-first-success count (commands and concepts from install to first + store-scoped change). +3. **Technical audits**: single-resolver invariant (one precedence + implementation, no command-local forks); dependency-direction check; + dead-code sweep over touched areas; module-size report; an agent-contract + inventory (all JSON shapes and diagnostic codes documented in one + reference file and verified consistent); net LOC delta vs `origin/main` + reported (expected net-negative given the Phase 5 deletions — justify if + not). +4. **Whole-delta review gauntlet** over `origin/main...HEAD` (the sum, not + the slices): `/code-review` at max effort, a codex CLI review, a + fan-out of adversarial Workflow reviewers, and a completeness critic + asking what is missing. Fix all P1/P2 findings. +5. **Release-readiness report** committed to this work folder: the + five-minute new-user story, audit results, the full + `Decided autonomously` ledger, and known gaps mapped to Later Ideas. + +## Autonomous decision protocol + +When a slice surfaces a decision the roadmap has not locked: + +1. Make the call most consistent with the locked decisions, the guardrails, + and the goal ("Specs are what is true. Work is what is in motion."). +2. Record it the same day in the roadmap changelog under a clearly marked + line: `Decided autonomously (review me): ...` with the rationale. +3. Continue. Do not stop to ask; do not silently decide either — the + changelog marker is the user's review surface. + +Phase 5 deletion slices proceed without confirmation: they delete code and +generated guidance only, never user data, and git history is the undo. + +## Hard boundaries (prohibitions, not gates) + +- **Never** merge, rebase onto, or push to `main`; never push at all — + commits stay local on `codex/store-root-parity`. +- Never delete user data files. +- Never re-open a locked decision; never rebuild per-change links + (relationships are location, declaration, or citation). +- One change lives in one root. + +## Parallelism policy + +- **Cross-slice work stays serial.** Every slice lands on the single branch; + the junction files (`src/cli/index.ts`, the completions registry, + `project-config.ts`, `foundation.ts`/`registry.ts`, and `roadmap.md` + bookkeeping) are shared by nearly every slice; and the queue's two largest + commits — the 1.4 mass rename and the Phase 5 mass deletion — are the + worst bases to rebase parallel tracks across. +- **Within-slice fan-outs are encouraged.** Mechanical sweeps over + partitioned file sets — the 1.4 rename and guidance surfaces, the Phase 5 + deletion sweep — run as Workflows, with worktree isolation when agents + edit concurrently. One integration point, one full-suite run. +- **Lookahead research is allowed.** During implementation turns, a + background read-only workflow may pre-build the next slice's code map + (file:line anchors for its plan). Never pre-write the next spec against + unlanded code or names. + +## Turn sizing and status (the evaluator reads this) + +- One coherent unit per turn: a spec with its reviews, a plan with its + reviews, an implementation checkpoint, or a review-and-fix cycle. +- End every turn with an explicit status block stating: current slice and + step, what was produced this turn, review verdicts, test-suite state, any + `Decided autonomously` entries, and what the next turn does. The goal + evaluator only sees what the transcript surfaces — state progress + plainly, never implicitly. +- The run is complete when every queue item's roadmap progress boxes are + ticked except "Merged to `main`", the full suite is green, all work is + committed, **and the 6.1 capstone passes with its release-readiness + report committed and no open P1/P2 findings**. When that is true, say so + explicitly in the final status: "ROADMAP QUEUE COMPLETE" plus the closing + summary including every `Decided autonomously` entry for review. diff --git a/openspec/work/simplify-context-and-workspace-model/slice-1.2-decision-review.html b/openspec/work/simplify-context-and-workspace-model/slice-1.2-decision-review.html new file mode 100644 index 000000000..d21a1c6fa --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slice-1.2-decision-review.html @@ -0,0 +1,436 @@ +<!DOCTYPE html> +<html lang="en"> +<head> +<meta charset="UTF-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title>Slice 1.2 — Decision Review + + + + +
+
+

Slice 1.2 decisions — store root selection

+ +
+ + + +
+
+
Already settled (not up for review): OpenSpec root is the planning home · context store is registration/identity only · workspace "planning home" is legacy beta language.
+
+ +
+
+
+
+ +
+
+
+ +
+
+ + + + diff --git a/openspec/work/simplify-context-and-workspace-model/slices/assemble-working-context/plan.md b/openspec/work/simplify-context-and-workspace-model/slices/assemble-working-context/plan.md new file mode 100644 index 000000000..f42b61a1a --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/assemble-working-context/plan.md @@ -0,0 +1,167 @@ +# Assemble Working Context Plan (4.1) + +## Status + +Spec locked 2026-06-11 after two adversarial rounds (binding.ts dies +whole; the five template guards join; the member-mapping table; pinned +write semantics; getRepoPath deleted). Plan drafted 2026-06-11. +Implementation not started. + +The main move: + +```text +Delete the dead opening machinery, extract doctor's gather, and the +working set becomes one thin command over data Phase 3 already built. +``` + +## Source Of Truth + +Start from `spec.md` (this folder). Keep nearby: `../../roadmap.md` +(Phase 4 + the absorbed 2.3), the deletion ledger +(`../delete-legacy-command-groups/deletion-ledger.md` — updated by this +slice), `../relationship-health/spec.md` (the consumed data shapes). + +## Current Code Map (verified during spec review) + +- **Deletion targets and their consumers**: + - `src/core/workspace/` (897 lines): imported by `planning-home.ts` + and the `src/core/index.ts` barrel only (plus its own tests). + - `src/core/store/binding.ts` (~300 lines): imported by + `workspace/foundation.ts` and the `store/index.ts` barrel; tests in + `test/core/store/registry.test.ts` (the binding describe block). + - `planning-home.ts`: `PlanningHomeKind = 'repo' | 'workspace'`, + the workspace state read, `WORKSPACE_DEFAULT_SCHEMA`; consumers of + `PlanningHome` never branch on `kind === 'workspace'` in production + (verified — `toPlanningHome` hardcodes repo; + `resolveCurrentPlanningHomeSync`'s workspace branch is the + CLI-unreachable carve-out). + - `change-status-policy.ts` cascade: `summarizeAffectedAreas`, + `AffectedAreasSummary`, `ChangeNextStepsInput.affectedAreas`, + workspace next-steps (~:130-135), `PlanningHomeSummary + .workspaceName`, the `'workspace-planning'` mode member; + instruction-loader plumbing at `instruction-loader.ts:465-496`. + - The five template guards: + `src/core/templates/workflows/{apply-change.ts:57, + verify-change.ts:41, archive-change.ts:40, + bulk-archive-change.ts:47, sync-specs.ts:39}` + mirrored second + occurrences — grep `workspace-planning` in templates to catch all. + - `getRepoPath` (`store/registry.ts`) + its unit tests. + - Test deletions: `planning-home.test.ts` workspace cases; the + legacy-groups workspace-planning LIBRARY pin (:185-203); + `test/core/workspace/{foundation,legacy-state}.test.ts`. + - Ledger updates: carve-outs → executed; Surviving tokens pruned; + `workspace_skills` allowlist entry removed + (`test/vocabulary-sweep.test.ts:79`). +- **Assembly anchors**: doctor's `gatherHealth` + (`src/commands/doctor.ts:37-141`) — the DATA half (snapshot + + health-mode index + targets + root inspection ≈ :42-75) extracts to + `src/commands/shared-gather.ts`; `readRegistrySnapshot` + (`store/registry.ts`); `inspectRelationships` + (`core/relationship-health.ts`); `fetchRecipe` (module-private at + `references.ts:133` — export); `RelationshipHealth.targets` = + `HealthTargetEntry[]`; the failure-payload mechanism + (`resolveRootForCommand` + `failurePayload`, doctor precedent). +- **Command registration**: doctor precedent + (`src/commands/doctor.ts:registerDoctorCommand`, `cli/index.ts`, + completions `command-registry.ts`, `STORE_SELECTION_GUIDANCE`, + parity hashes). + +## Implementation Plan + +### Checkpoint 1 — the deletions (commit) + +Order: leaves first, compiler-verified after each tranche. + +1. Templates: remove the five workspace-planning guards (grep-driven); + regenerate parity hashes + `.codex` guidance; FLIP the parity + test's guard assertion (skill-templates-parity.test.ts:179 asserts + the guards exist — it becomes an absence assertion: no generated + template contains `workspace-planning`). +2. `change-status-policy.ts` cascade + instruction-loader plumbing + (incl. `ChangeStatus.affectedAreas` and the + `artifact-graph/index.ts` barrel re-export of + `AffectedAreasSummary`); the legacy-groups library pin dies; the + six CLI-surface pins stay green untouched. +3. `planning-home.ts`: collapse to repo-only (keep the interface); + delete the workspace import; `planning-home.test.ts` workspace + cases die. +4. `src/core/workspace/` whole + `core/index.ts` barrel line + + `test/core/workspace/{foundation,legacy-state}.test.ts`. +5. `store/binding.ts` whole + `store/index.ts` barrel line + the two + binding `it` cases in `registry.test.ts` (:356, :414) and their + imports. +6. `getRepoPath` + its tests; amend the repo-map ledger note. +7. Ledger + vocabulary-allowlist updates; full suite green; net-LOC + note in the commit message. + +### Checkpoint 2 — assembly: core + command + e2e + docs (commit) + +1. `references.ts`: export `fetchRecipe`. +2. `src/commands/shared-gather.ts`: `gatherRelationshipData(root)` → + `{registrySnapshot, referenceEntries, effectiveTargets, + storeTargets, projectConfig, storeConfigPath, rootInspection}` — + doctor refactors onto it (behavior byte-identical; doctor e2e is + the net), context consumes it. +3. `src/core/working-set.ts` (pure): + - `WorkingSet {root: {path, source, store_id?, role}, members: + WorkingSetMember[], status: []}`; + `WorkingSetMember {role, id, path?, remote?, fetch?, status}`. + - `assembleWorkingSet({root, referenceEntries, targets, + registryUnreadable})`: the spec's member-mapping table + (available = path + empty status; references before targets; + synthesized entries appended; fetch recipes on available stores). + - `buildCodeWorkspaceJson(workingSet, rootName)`: pure string + builder, available members only, `ref:`/`repo:` prefixes. +4. `src/commands/context.ts`: resolution like doctor + (`allowImplicitRoot: false`, failure payload + `{root: null, members: []}`); the shared gather; + human listing (Available sections + "Not available on this + machine"); `--json`; `--code-workspace ` (+`--force`): refuse + `context_file_exists` exit 1 (spec-amended name — the draft's + `code_workspace_exists` trips the sweep's `workspace_*` ban); + missing parent dir → clear error, no mkdir; stderr confirmation + under `--json`; unresolved members reported on stderr during + writes; try/catch via `emitFailure` (the 3.6 lesson). CLI + + completions + `STORE_SELECTION_GUIDANCE` + parity hashes. +5. Tests: working-set unit (mapping table incl. stale-path, + invalid-id, registry-unreadable-bare members; ordering; fetch); + builder unit (folders, prefixes, omission); command e2e (three + session shapes; JSON exact shape; nothing-declared; the write + matrix — fresh/exists/force/nested-missing-parent/json-stderr; + read-only snapshot; failure payloads). +6. `docs/cli.md`: context section + summary row + doctor cross-link + + the project-context vs working-context disambiguation sentence. +7. Full suite; built-binary smoke of the UX transcript. + +## Risks And Guardrails + +- **Deletion order is the spine**: each tranche compiles and the full + suite passes before the next; never delete ahead of the compiler. +- **Doctor must not change**: the shared-gather extraction is a pure + refactor; doctor's e2e suite is the net (behavior-identical — the + suite asserts fields and sections, not full-output bytes). +- **The six legacy-groups CLI pins** and the user-data byte-identity + test stay untouched and green — they are the proof the deletions + changed no repo-local behavior. +- **Parity churn is deliberate twice** (template guards in CP1, + guidance in CP2) — update hashes from the test diff, never loosen. +- **No new diagnostic codes except `context_file_exists`** (the one + write needs the one refusal). Implementation amendment: the + missing-parent-directory failure also got a typed code + (`context_output_dir_missing`) — the spec pinned only "fails with a + clear error" with no code, and a typed code serves JSON consumers + better than the generic fallback; recorded here rather than left as + silent drift. +- **Vocabulary**: 'working context' in user-facing strings; never + 'workspace' — enforced by manual review + the Done-Definition grep + (the sweep bans only the compound and `workspace_*` tokens, not the + bare word). + +## Done Definition + +- Both checkpoints green on the full suite and committed. +- `grep -r workspace src/` returns only the `.code-workspace` file + format references and historical comments (verify; the sweep + allowlist reflects reality). +- Roadmap 4.1 boxes ticked through "Tests pass"; changelog updated; + pointer moved to the Phase 5 remainder. diff --git a/openspec/work/simplify-context-and-workspace-model/slices/assemble-working-context/spec.md b/openspec/work/simplify-context-and-workspace-model/slices/assemble-working-context/spec.md new file mode 100644 index 000000000..461a118b1 --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/assemble-working-context/spec.md @@ -0,0 +1,281 @@ +# Assemble Working Context Spec (4.1) + +## Outcome + +From any root, one command produces the full working set its +declarations describe — the OpenSpec root, its referenced stores, and +its mapped target repos — consumable as an agent session brief (JSON + +human) or as an editor view (a `.code-workspace` file, one consumer of +assembly, not the feature). Unresolvable pieces are reported, not +guessed. And the old workspace opening machinery this replaces — the +state model and workspace-planning mode that have been CLI-unreachable +since 1.2 — is deleted per the recorded ledger carve-outs (the absorbed +2.3: no de-initiative-ing of a path that dies). + +## Locked Decisions (roadmap) + +1. **Assembly is a local convenience computed from Phase 3's declared + relationships, not a new planning system.** The selected OpenSpec + root is the durable planning source of truth; references supply + upstream stores; the local repo map supplies target checkouts. No + workspace-owned planning state, ever. +2. **The primary interface is an agent session** — the assembled set + must be agent-consumable, not only editor-shaped. +3. **No machinery**: no clone, pull, push, sync, branch, worktree, + dashboard, or edit-boundary enforcement. +4. **Unresolvable pieces are reported, not guessed.** +5. **The absorbed 2.3**: opening is rebuilt around assembled context; + the old workspace state model (with initiative selection hardcoded + in) is deleted, not decoupled. + +## Decisions This Spec Makes (autonomous, recorded in the changelog) + +1. **The surface is a new top-level `openspec context`** — "print the + working context for the resolved root". Resolution is the normal + precedence (`--store`, nearest, declared pointer; no implicit + scaffold). JSON is the agent brief; human output is a readable set + listing; `--code-workspace ` additionally WRITES a VS Code + workspace file (the one write this feature performs, explicit and + user-requested — never implied). The name "context" is the phase's + own noun; the banned token is the compound "context store", not the + word. +2. **Assembly is presentation over `inspectRelationships`** — the 3.6 + spec recorded 4.1 as its consumer. The DATA gather (registry + snapshot, health-mode reference index, store-level targets, root + inspection) is EXTRACTED into a shared command-layer helper that + doctor and context both call; doctor-only inputs (store facts, the + wrong-turn detections, stale-path stats) stay in doctor — context + is deliberately SILENT on both-shapes/inert-pointer/divergence + (recorded: context answers "what is my working set", doctor answers + "is it healthy"; 3.6 pinned those diagnostics to doctor output and + ONLY there). Member mapping from the health data, pinned: + - AVAILABLE = `path` present AND per-entry `status` empty. Only + available members enter the `.code-workspace`. + - `target_path_missing` entries (path + warning) are NOT available + (the stale path appears in the message, never as a folder). + - Synthesized `target_invalid_id` entries are NOT-available members + carrying their status; they append after declared members + (declaration order holds for declared members only — recorded). + - Registry-unreadable bare targets are NOT-available members with + empty per-entry status; the top-level registry-unreadable entry + names the cause. + No new diagnostic codes; the unresolved members carry the existing + fixes verbatim. +3. **JSON shape**: + `{root: {path, source, store_id?, role: "openspec_root"}, + members: [{role: "referenced_store" | "target_repo", id, path?, + remote?, status: []}], status: []}` — `path` present iff resolved/ + mapped; members in declaration order, references before targets; + the top-level `status` carries the registry-unreadable degradation. + The fetch recipe for referenced stores rides each resolved member + (`fetch: openspec show --type spec --store `, reusing + the exported references `fetchRecipe` — one source for the string) + so an agent brief is self-contained. +4. **The `.code-workspace` emitter** writes + `{folders: [{name, path}...]}` — the root first (name: the root + dir basename or store id), then AVAILABLE referenced stores + (`ref:`), then AVAILABLE targets (`repo:`). Unavailable + members are omitted from the file and reported on stderr. Write + semantics, pinned: an existing path refuses with exit 1 and a typed + `context_file_exists` diagnostic whose fix names `--force` + (implementation amendment: the draft's `code_workspace_exists` + would trip the vocabulary sweep's `workspace_*` token ban — the + flag name `--code-workspace` is safe, hyphenated); + `--force` overwrites; a missing parent directory fails with a clear + error (NO implicit mkdir — the requested file is the only write); + with `--json`, the brief stays pure on stdout and the write + confirmation goes to stderr. 4.1 replaces opening with emitted + assembled-context artifacts; no `open` verb or editor process + launch is introduced. +5. **The deletions — the ledger's 4.1 carve-outs, widened where the + carve-out rationale collapses**: + - `src/core/workspace/` whole (foundation, state-io, legacy-state, + index — ~900 lines) plus the `src/core/index.ts` barrel line. + - `planning-home.ts`'s workspace branch: `PlanningHomeKind` + collapses to `'repo'`, the workspace state read and + `WORKSPACE_DEFAULT_SCHEMA` die; the `PlanningHome` interface + survives as the repo-shaped carrier. + - `binding.ts` WHOLE (review finding: 5.1 kept it only because + workspace/foundation imported it; with workspace/ gone it has + zero production consumers — the whole ~300-line module dies, with + its `registry.test.ts` binding tests and the `store/index.ts` + barrel line, exactly the hidden-not-deleted state the 5.1 + criteria reject). + - `change-status-policy.ts`'s workspace-planning branch AND its + cascade: `summarizeAffectedAreas` (constant-undefined + post-collapse), `AffectedAreasSummary`, + `ChangeNextStepsInput.affectedAreas`, the workspace next-steps, + `PlanningHomeSummary.workspaceName`, the `'workspace-planning'` + mode union member, and the instruction-loader plumbing that + threads them. + - The FIVE workflow-template workspace-planning guards + (apply/verify/archive/bulk-archive/sync-specs templates, two + occurrences each) — 5.1 explicitly deeded them here ("they quote + the library contract that 4.1 deletes"); their removal churns the + skill-template parity hashes and the checked-in `.codex` guidance + deliberately. + - `getRepoPath` (review finding: its recorded consumers + evaporated — the snapshot serves every caller; delete-don't-hide, + with the repo-map record amended). + Library pins that existed solely to freeze the carve-outs + (`planning-home.test.ts` workspace cases, the legacy-groups + workspace-planning library pin) are deleted WITH the behavior; the + six legacy-groups CLI-surface pins stay and must remain + byte-identical. The deletion ledger is updated throughout + (carve-outs marked executed, the Surviving-tokens section pruned, + the `workspace_skills` vocabulary-allowlist entry removed — its + referents die here, verified). The workspace-planning SCHEMA files + and the accepted-spec L2 question stay with the Phase 5 remainder + as recorded. +6. **Naming, recorded**: `openspec context` over `openspec view` (the + dashboard owns `view`) and over an `open` verb (no process + launching). The vocabulary sweep bans only the compound + `context store`; docs disambiguate "project context" (the config + field injected into instructions) from "working context" (this + set). Adding `context` to `STORE_SELECTION_GUIDANCE` rewrites the + generated workflow templates — the parity hashes and guidance walk + update deliberately, as in 3.6. + +## User Experience + +```text +$ openspec context +Working context for team-context (/Users/dev/src/team-context) + +OpenSpec root + team-context /Users/dev/src/team-context + +Referenced stores + upstream-context /Users/dev/openspec/upstream-context + Fetch: openspec show --type spec --store upstream-context + +Target repos + api-server /Users/dev/src/api-server + +Not available on this machine + - design-system: not registered + Fix: git clone -- https://github.com/acme/design-system.git /Users/dev/openspec/design-system && openspec store register '/Users/dev/openspec/design-system' --id design-system + - web-app: not mapped + Fix: Run: openspec repo register --id web-app +``` + +```text +$ openspec context --code-workspace team.code-workspace +Wrote team.code-workspace (3 folders; 2 members not available, see above) +``` + +The agent brief: + +```json +{ + "root": { "path": "/Users/dev/src/team-context", "source": "store", "store_id": "team-context", "role": "openspec_root" }, + "members": [ + { "role": "referenced_store", "id": "upstream-context", "path": "/Users/dev/openspec/upstream-context", "fetch": "openspec show --type spec --store upstream-context", "status": [] }, + { "role": "referenced_store", "id": "design-system", "status": [{ "code": "reference_unresolved", ... }] }, + { "role": "target_repo", "id": "api-server", "path": "/Users/dev/src/api-server", "status": [] }, + { "role": "target_repo", "id": "web-app", "status": [{ "code": "target_unmapped", ... }] } + ], + "status": [] +} +``` + +## Scope + +In scope: + +- **Core** (`src/core/working-set.ts`): `assembleWorkingSet` — pure + mapping from `RelationshipHealth` (or its inputs) to the members + shape; the `.code-workspace` content builder (pure: returns the JSON + string; the COMMAND writes it). +- **Command** (`src/commands/context.ts`) + the shared gather + (`src/commands/shared-gather.ts`): the data gather extracted from + doctor (snapshot + health-mode index + targets + root inspection), + consumed by both; doctor keeps its wrong-turn/store-facts/stale-path + additions; `--code-workspace ` + `--force`; CLI/completions + registration (friction pins; `STORE_SELECTION_GUIDANCE` gains + `context`). +- **Deletions** per decision 5, with `pnpm test` green after each + removal tranche; the deletion ledger updated (carve-outs marked + executed); the vocabulary-sweep allowlist pruned if the + workspace_skills entry's referent died (verify). +- **Docs**: `docs/cli.md` context section + summary row; the doctor + section cross-links ("doctor answers whether the set is healthy"). +- **Tests**: working-set unit (member mapping, ordering, fetch + recipes, unresolved members, registry-unreadable degradation); + code-workspace builder unit (folder list, name prefixes, omission of + unresolved); command e2e (the three session shapes; JSON shape; the + write + `--force` + refusal; stderr reporting; read-only except the + requested file); deletion regression (the full suite is the net — + workspace tests die with the code; `legacy-groups-removed.test.ts` + drops its workspace-planning library pins and keeps the + CLI-surface pins). + +Out of scope: + +- Editor integrations beyond the `.code-workspace` file; terminal + multiplexers; session managers. +- Any open/launch behavior (no spawning editors). +- The workspace-planning schema files, obsolete beta change folders, + and the L2 accepted-specs question (Phase 5 remainder, as recorded). +- Per-change context narrowing (instructions already shows per-change + targets). + +## Acceptance Criteria + +### The Working Set Is Complete And Honest + +#### Scenario: Assembly From Declarations + +- **GIVEN** a store-backed root with one resolvable + one unresolvable + reference and one mapped + one unmapped target +- **WHEN** `openspec context` runs (human + JSON; `--store`, nearest, + and declared-pointer sessions) +- **THEN** JSON matches decision 3 exactly: resolved members carry + absolute paths (and fetch recipes for stores), unresolved members + carry the existing diagnostics verbatim (the 3.3 clone fix, the + register fix), members in declaration order, references before + targets +- **AND** human output separates available from "Not available on this + machine" +- **AND** exit code is 0 (unresolved members are reported, not errors; + command failures keep the resolution taxonomy and the null-shape + payload `{root: null, members: [], status: [diagnostic]}`) + +#### Scenario: Nothing Declared + +- **GIVEN** a root with no references or targets +- **WHEN** context runs +- **THEN** the set contains only the root, `members: []`, and human + output says so plainly + +### The Editor View Is One Consumer + +#### Scenario: Code-Workspace Emission + +- **GIVEN** the mixed fixture above +- **WHEN** `openspec context --code-workspace out.code-workspace` runs +- **THEN** the file contains folders for the root + resolved members + only, named ``, `ref:`, `repo:`, in that order +- **AND** unresolved members are reported on stderr, not written +- **AND** rerunning without `--force` refuses with exit 1 and the + typed `context_file_exists` fix naming `--force`; with `--force` + it overwrites +- **AND** a missing parent directory fails clearly with no mkdir +- **AND** with `--json`, stdout stays the pure brief and the write + confirmation lands on stderr +- **AND** no other file or registry state changes (snapshot) + +### The Old Machinery Is Gone + +#### Scenario: The Carve-Outs Are Executed + +- **GIVEN** the post-4.1 tree +- **WHEN** the suite runs and the ledger is read +- **THEN** `src/core/workspace/` does not exist; `PlanningHomeKind` + is `'repo'` only; the change-status-policy and binding carve-outs + are gone; no `workspace_*` diagnostic codes or workspace state reads + remain in src (the vocabulary sweep's allowlist reflects reality) +- **AND** every repo-local CLI behavior pinned by + `legacy-groups-removed.test.ts`'s surface tests is byte-identical +- **AND** assembly works without any workspace/initiative state + (no `.openspec-workspace` reads anywhere in src) diff --git a/openspec/work/simplify-context-and-workspace-model/slices/declared-store-fallback/plan.md b/openspec/work/simplify-context-and-workspace-model/slices/declared-store-fallback/plan.md new file mode 100644 index 000000000..0df7342d6 --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/declared-store-fallback/plan.md @@ -0,0 +1,183 @@ +# Declared Store Fallback Plan (3.2) + +## Status + +Spec locked 2026-06-11 after two adversarial rounds (the store-selected +predicate adopted by all seven source-keyed consumers; init's pointer +guard; malformed-pointer errors; one-hop rule; warning-silent resolver +reads; the recorded doctor-wording amendment). Plan drafted 2026-06-11. +Implementation not started. + +The main move: + +```text +One predicate ("a store-selected root has storeId"), one pointer branch +in the resolver, one init guard — and externalized planning needs no +flags. +``` + +## Source Of Truth + +Start from `spec.md` (this folder). Keep nearby: `../../roadmap.md` +(Phase 3 precedence lock + the recorded amendment), +`../store-references/spec.md` (3.1 config patterns), +`../store-lifecycle-proof/spec.md` (hint-continuity contracts). + +## Current Code Map (verified during spec review) + +- **Resolver**: `resolveOpenSpecRoot` (`src/core/root-selection.ts:258-314`); + the nearest-root arm at 277-280 (`findRepoPlanningRootSync` returns + the project root whose `openspec/` exists and terminates at the + nearest ancestor — `planning-home.ts:52-77`); the stores-hint error + at 293-302; implicit at 305-313. `resolveStoreRoot` (134-218, module + private, same file) is the pipeline the pointer branch calls. +- **Source-keyed consumers to switch to the predicate** (all EIGHT + checks — plan review found the spec's "seven" missed one): + `emitStoreRootBanner` (`root-selection.ts:339`), `withStoreFlag` + (`root-selection.ts:349`), new-change path display + (`src/commands/workflow/new-change.ts:77`), status storeId + threading (`src/commands/workflow/status.ts:106` → `buildNextSteps` + appends `--store`), validate noun-suggestion suppression + (`src/commands/validate.ts:136`), show noun-suggestion suppression in + BOTH branches (`src/commands/show.ts:138` and + `printNonInteractiveHint` at `show.ts:160`), archive absolute display + paths (`src/core/archive.ts:446`). Spec amendment recorded in the + changelog: eight checks, not seven. +- **Config**: `ProjectConfigSchema`/`readProjectConfig` + (`src/core/project-config.ts`); the resolver does NOT reuse + `readProjectConfig` (it would re-emit field warnings) — it does a + targeted read. +- **Init**: `InitCommand.execute` → `createDirectoryStructure` + (`src/core/init.ts:144, 455-487`) unconditionally scaffolds under an + existing `openspec/`; the guard goes before that. +- **Tests**: `test/core/root-selection.test.ts` (resolver unit), + `test/commands/store-root-selection.test.ts` (CLI), + `test/core/init.test.ts`, `test/cli-e2e/` harness, + `test/helpers/openspec-fixtures.ts` (shared fixtures from 3.1). + +## Implementation Plan + +### Checkpoint 1 — resolver + predicate (commit) + +1. `src/core/project-config.ts`: add `store: z.string().optional()` to + the schema; resilient parse keeps a string, drops non-strings with + a warning (the parser's behavior is unchanged in spirit — the + RESOLVER, not the parser, owns the malformed-pointer error, and it + reads the file itself). +2. `src/core/root-selection.ts`: + - `OpenSpecRootSource` gains `'declared'`. + - New `isStoreSelectedRoot(root)` predicate (`storeId !== undefined`); + `emitStoreRootBanner` and `withStoreFlag` switch to it. + - In the nearest-root arm: stat `openspec/specs` and + `openspec/changes` as directories. Planning shape → today's path, + plus the both-shapes check: a targeted, warning-silent read of + `openspec/config.{yaml,yml}` (small local helper: read file, YAML + parse in try/catch, pluck `store`) and one stderr warning when a + `store` key exists ("openspec/config.yaml declares store 'x', but + this directory is a real OpenSpec root; the declaration is + ignored."). + - Config-only → targeted read: no config or no `store` key → today's + nearest behavior; unparseable config or non-string `store` → + `invalid_store_pointer` RootSelectionError naming the actual file + read; a string → call `resolveStoreRoot(id, globalDataDir, + 'declared')` inside a try/catch that **rewraps** any thrown + `RootSelectionError`/store error with the message prefix + "Declared in : " while preserving `code`, `target`, and + an UNPREFIXED `fix` — one wrapper covers all ~7 throw paths + including the `fromStoreError` pass-throughs + (`root-selection.ts:138,146`), no per-template surgery. + - `resolveStoreRoot` gains only a source parameter (default + `'store'`; `makeRoot` already takes source as its second arg). + - The targeted read is a small exported helper (host it next to + `readProjectConfig` in `project-config.ts`, reusing its + `.yaml`/`.yml` preference): read file, YAML parse in try/catch, + pluck `store` — returning `{value?, malformed?, filePath}`. The + both-shapes warning fires only for STRING values (a non-string in + a real root is not a pointer; the resilient parser's later + drop-warning covers it). +3. Command-layer predicate adoption: new-change display, status + threading, validate/show suppression, archive display paths — each + switched from `source === 'store'` to the shared predicate (import + from root-selection). +4. Tests (resolver unit + CLI): + - Pointer resolves: source `declared`, store_id set, banner, hints + carry `--store`, absolute paths in new-change/archive output, and + the show nothing-to-show hint suppresses noun-form suggestions + (the eighth consumer). + - `--store` beats the pointer, asserting `source === 'store'`. + - Real root + pointer: stdout byte-identical to a no-pointer run — + same directory, add/remove the line in place, using deterministic + commands (`status --json`, `list --json`; normalize or avoid + `durationMs`-bearing outputs like validate's) — plus exactly one + stderr warning per invocation in human AND JSON modes, JSON stdout + clean. + - Config-only without pointer (positive assertions — no "today" + binary exists to diff): `source === 'nearest'`, path is the + config-only dir, zero stderr warnings, registry never consulted. + - Malformed pointer (non-string, unparseable YAML) → + `invalid_store_pointer` with origin AND a no-write assertion (the + pointer dir is untouched); invalid grammar → `invalid_store_id` + with the declared prefix; ALL five taxonomy codes prefixed + (`unknown_store`, `no_registered_stores`, `unhealthy_store_root`, + `store_identity_mismatch`, `invalid_store_id`), each asserting + the prefixed `diagnostic.message` and an UNPREFIXED + `diagnostic.fix`. + - One hop: pointer → store whose config has `store:` → resolves to + the first store. + - `.yml` origin naming. + - No-pointer no-root: stores-hint error byte-identical. + +### Checkpoint 2 — init guard, e2e, docs (commit) + +1. `src/core/init.ts`: the guard goes **immediately after `validate()` + returns `extendMode`** (`init.ts:111`) — before legacy cleanup + (`:114`, which mutates project files), migration (`:121`, which + writes global config), and the interactive prompts — so the refusal + truly creates and changes nothing. Detection: `extendMode` and the + shared targeted-read helper reports a string `store:` in a + config-only `openspec/`. Test asserts: refusal with the conversion + guidance; NO filesystem changes (project tree snapshot identical; + global data dir untouched); after removing the line, a rerun + scaffolds `openspec/specs/` and `openspec/changes/` normally. +2. e2e externalized-planning journey (`test/cli-e2e/` or + `test/commands/`, runCLI): rootless app repo with pointer → + `new change`, `status`, `instructions` (+ references composition: + the store's own `references:` appear per 3.1 symmetry), artifact + writes, `validate`, `list`, `show`, `archive` — no `--store` + anywhere; work lands in the store; pointer dir never gains + `specs/`/`changes/` (snapshot); banner + JSON root block assert + `declared`. +3. `docs/cli.md`: "Declaring a default store" subsection next to the + references one (the pointer, precedence, the init conversion note). +4. Full suite; built-binary smoke of the UX transcript. + +## Risks And Guardrails + +- **Predicate adoption must not change `--store` behavior**: the + predicate is true for both sources; every switched site already + behaved this way for explicit stores — the suite's existing + store-root expectations are the net. +- **Resolver read cost**: the targeted read happens only when the + nearest root exists (one stat for the config file in the + planning-shape case; full read only in the config-only case or for + the both-shapes warning). Keep it synchronous-fs and tiny; no + `readProjectConfig` reuse (its warnings would double-fire — the + 3.1-recorded behavior). +- **`invalid_store_pointer` is a new code**: document it in the slice + artifacts; additive to the resolver taxonomy (the capstone + agent-contract inventory picks it up). +- **planning-home untouched**: `findRepoPlanningRootSync` semantics + stay; only `resolveOpenSpecRoot` classifies the found dir. The + legacy planning-home workspace branch is unaffected. +- **Byte-identity pins**: the no-pointer baseline assertions must run + the SAME fixture twice (with/without the line), not rely on + hand-written expectations. + +## Done Definition + +- All spec acceptance scenarios pass; both checkpoints green on the + full suite and committed. +- The e2e journey proves externalized planning end to end without + flags, including the 3.1 composition. +- Roadmap 3.2 boxes ticked through "Tests pass"; changelog updated; + pointer moved to 3.3. diff --git a/openspec/work/simplify-context-and-workspace-model/slices/declared-store-fallback/spec.md b/openspec/work/simplify-context-and-workspace-model/slices/declared-store-fallback/spec.md new file mode 100644 index 000000000..983ea51d2 --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/declared-store-fallback/spec.md @@ -0,0 +1,273 @@ +# Declared Store Fallback Spec (3.2) + +## Outcome + +A repo whose planning is fully externalized — no local OpenSpec root — +declares its store once, and every normal command works there without +`--store` on every invocation. The declaration is a fallback, never an +override: with any local root present, behavior is byte-identical to +today, declaration or not. The fixed precedence is finally complete: +explicit `--store` → nearest local root → declared store (only when no +local root exists) → today's error with the stores hint. + +## Locked Decisions (roadmap, 2026-06-11) + +1. **The declaration lives in `openspec/config.yaml`** — the fallback + `store:` pointer shares one home with `references:`. The fallback + case is a **config-only `openspec/` directory** (no `specs/`, no + `changes/`): root detection keeps today's stat-only walk, and two + extra stats distinguish a real root from a pointer. A top-level + marker file was rejected (`.openspec.yaml` is taken; dot-only + filename collisions are an agent hazard). +2. **Fallback, never override.** A declared store never overrides a + local root. With a local root present, behavior is byte-identical + with or without the declaration. +3. **A root with both planning shape and a pointer warns** (the pointer + is ignored per precedence). The locked wording said "doctor warns"; + no project-level doctor command exists, so this slice relocates the + warning to resolution stderr — recorded as a reviewed amendment in + the roadmap changelog; 3.6 owns the structured health surface. +4. **The no-root error/hint from slice 1.2 remains** for repos with no + declaration. +5. Without a local root, commands resolve to the declared store and + report it **through the existing root banner and JSON root block**. + +## Decisions This Spec Makes (autonomous, recorded in the changelog) + +1. **Detection mechanics.** The walk is unchanged: nearest ancestor + carrying `openspec/` wins and terminates the walk. On that one + directory, two stats (`openspec/specs`, `openspec/changes`, each + required to be a **directory**) classify it: either present → a + real root, today's `nearest` path, byte-identical. Both absent + (config-only) → a **warning-silent targeted read** of the config + (parse for the `store:` key only — never re-emitting the resilient + parser's field warnings during resolution); a `store:` key makes it + a pointer and resolution proceeds through the shared store pipeline; + **no `store:` key → today's behavior is preserved** (the config-only + directory is still a root — freshly initialized minimal roots keep + working). The walk never continues past the nearest `openspec/` + directory; nesting a pointer under a real root is pathological and + out of scope. +2. **A malformed pointer is an error, never a silent local root.** In a + config-only directory, a present-but-malformed `store:` value + (non-string, invalid id grammar) or an unparseable config file fails + resolution with an origin-naming error (`invalid_store_pointer` for + the malformed/unparseable cases; the grammar case flows into the + pipeline's `invalid_store_id`) — it must not degrade into scaffolding + work next to the pointer. (This deliberately differs from 3.1's + drop-with-warning references parsing: a dropped reference degrades + an index; a dropped pointer would silently flip the write target.) +3. **A declared root behaves exactly like a `--store` root except for + its `source` — enforced by one predicate.** "Store-selected" means + `root.storeId` is set; every consumer currently keyed on + `source === 'store'` switches to that predicate: the banner and + `withStoreFlag` (`root-selection.ts:339,349`), new-change's absolute + path display (`new-change.ts:77`), status's `storeId` threading + (`status.ts:106`), validate/show noun-form suggestion suppression — + both show branches, including `printNonInteractiveHint` + (`validate.ts:136`, `show.ts:138`, `show.ts:160` — the eighth check, + found in plan review), and archive's absolute cross-root display + paths (`archive.ts:446`). + Resolution runs the same `resolveStoreRoot` pipeline via an optional + `declaredOrigin` parameter; errors keep their codes and gain a true + prefix: "Declared in : " + + the existing message. The JSON root block carries + `source: "declared"` (additive enum value) plus `store_id`; hint + continuity appends `--store ` exactly as for explicit selection + (pasted hints work from any cwd). Explicit `--store` always wins and + never consults the pointer. +4. **Pointer resolution is one hop.** A resolved store's own `store:` + key is never consulted — no chaining, no recursion (a pointer chain + target that is itself config-only simply fails health as + `unhealthy_store_root`). +5. **The both-shapes warning lives in resolution, on stderr** (the + recorded amendment of the locked "doctor" wording). When the nearest + root has planning shape AND a `store:` pointer, commands emit + exactly one stderr warning per invocation — "Warning: declares store 'x', but this directory is a real + OpenSpec root; the declaration is ignored." (implementation + amendment: the absolute path replaces the spec draft's relative + `openspec/config.yaml`, per the absolute-paths quality bar) — in + both human and JSON modes (stderr keeps stdout payloads clean). + `references:` in the same config keeps working; only the `store:` + pointer is ignored. +6. **The pointer directory is never scaffolded by normal commands; only + `openspec init` may convert it, deliberately.** No lifecycle command + creates `specs/` or `changes/` inside a config-only pointer + directory; work lands in the declared store's root. `openspec init` + run in a pointer repo **refuses** with an actionable error ("this + repo's planning is externalized to store 'x' (openspec/config.yaml); + remove the store: line first to convert it to a local root") instead + of silently scaffolding a both-shapes directory. + +## User Experience + +A team keeps all planning in `team-context`. Their app repo carries +only a pointer: + +```yaml +# app-repo/openspec/config.yaml +store: team-context +``` + +Every normal command just works there, no flag: + +```text +$ openspec new change billing-rework +Using OpenSpec root: team-context (/Users/dev/src/team-context) +Created change 'billing-rework' at /Users/dev/src/team-context/openspec/changes/billing-rework/ +... +$ openspec status --change billing-rework --json +{ ..., "root": { "path": "/Users/dev/src/team-context", + "source": "declared", "store_id": "team-context" } } +``` + +(Note the absolute path: a declared root is cross-root, exactly like +`--store`, so every displayed path is absolute.) + +The pointer never hijacks a real root: in a repo that has its own +`openspec/specs/`, the same `store:` line changes nothing except one +stderr warning that it is being ignored. And a teammate without the +store registered gets the full store-error treatment, told exactly +where the requirement came from: + +```text +Error: Declared in /Users/dev/src/app-repo/openspec/config.yaml: Unknown store +'team-context'. No stores are registered. Run openspec store setup team-context +or openspec store register first. +``` + +## Scope + +In scope: + +- **Config**: `store:` (optional string) in `ProjectConfigSchema` and + the resilient parser (`src/core/project-config.ts`). +- **Resolver**: in `resolveOpenSpecRoot` + (`src/core/root-selection.ts:275-313`), after + `findRepoPlanningRootSync` returns a directory: the two + directory-shape stats; the pointer branch (warning-silent targeted + config read, malformed-pointer errors, resolve via the existing + `resolveStoreRoot` with the `declaredOrigin` prefix); `source: + 'declared'` added to `OpenSpecRootSource` and `RootOutput`; the + store-selected predicate (`storeId` set) adopted by all seven + source-keyed consumers (decision 3's list); the both-shapes stderr + warning. +- **Init guard**: `openspec init` refuses to scaffold a config-only + pointer directory (decision 6), with its own test. +- **Docs**: extend the `docs/cli.md` "Referencing stores from a + project" area with a sibling "Declaring a default store" subsection; + add the `store:` bullet to the config keys covered there. +- **Tests**: resolver unit coverage (pointer resolves; pointer + + explicit `--store` precedence; pointer ignored with planning shape + + warning; config-only without pointer unchanged; pointer to + unknown/unhealthy store errors with origin prefix; invalid pointer id + grammar); byte-identity pin (real root with and without `store:` — + identical stdout); an e2e externalized-planning journey (rootless app + repo with pointer → `new change`, `status`, `instructions`, artifact + writes, `validate`, `archive`, all without `--store`; work lands in + the store; the pointer dir gains no `specs/`/`changes/`; banner and + JSON root block report `declared`). + +Out of scope: + +- References behavior (3.1, shipped) beyond the natural composition: + the declared root's `references:` work exactly as for any resolved + root. +- Remotes (3.3), targets and the repo map (3.4/3.5), the structured + health surface (3.6), assembly (4.1). +- Any change to explicit `--store` behavior, the stores-hint error, or + the implicit-root scaffold for directories without `openspec/`. +- Multi-store pointers, per-command pointer overrides, or pointer + inheritance across the walk. + +## Acceptance Criteria + +### The Fallback Resolves + +#### Scenario: Externalized Planning Without Flags + +- **GIVEN** a repo whose `openspec/` contains only `config.yaml` with + `store: team-context`, and `team-context` registered and healthy +- **WHEN** `new change`, `status`, `instructions`, `validate`, `list`, + `show`, and `archive` run there without `--store` +- **THEN** every command acts on the store's root +- **AND** the banner prints `Using OpenSpec root: team-context (…)` +- **AND** JSON output's root block is + `{path: , source: "declared", store_id: "team-context"}` +- **AND** printed hints carry `--store team-context` +- **AND** the pointer directory never gains `specs/` or `changes/` + +#### Scenario: Explicit --store Still Wins + +- **GIVEN** the pointer declares `team-context` +- **WHEN** a command runs with `--store other-context` +- **THEN** it resolves `other-context` with `source: "store"`, the + pointer never consulted + +### The Fallback Never Overrides + +#### Scenario: Local Root Byte-Identity + +- **GIVEN** a repo with a real root (`openspec/specs/` or + `openspec/changes/` present) +- **WHEN** any command runs with and without a `store:` line in its + config +- **THEN** stdout is byte-identical in both runs (source stays + `nearest`) +- **AND** the runs with the pointer emit exactly one stderr warning per + invocation naming the ignored declaration, in human and JSON modes + alike, with JSON stdout payloads staying clean + +#### Scenario: Config-Only Roots Without Pointers Are Unchanged + +- **GIVEN** a config-only `openspec/` directory whose config has no + `store:` key +- **WHEN** commands run there +- **THEN** behavior is byte-identical to today (the directory is still + the root) + +### Failures Stay Actionable + +#### Scenario: Pointer To An Unavailable Store + +- **GIVEN** a pointer to an id that is unregistered, unhealthy, or + grammatically invalid +- **WHEN** a command runs +- **THEN** the existing store-error taxonomy fires (`unknown_store`, + `no_registered_stores`, `unhealthy_store_root`, + `store_identity_mismatch`, `invalid_store_id`) with the message + prefixed "Declared in : " +- **AND** the fix text is pasteable and unchanged in meaning +- **AND** a non-string `store:` value or an unparseable config in a + config-only directory fails with `invalid_store_pointer` naming the + origin — never a silent fall-through to local-root behavior, never a + write next to the pointer +- **AND** a pointer whose target store's own config carries `store:` + resolves to that target (one hop, no chaining) + +#### Scenario: Init Refuses To Bury A Pointer + +- **GIVEN** a config-only pointer directory +- **WHEN** the user runs `openspec init` +- **THEN** init fails with the conversion guidance (remove the + `store:` line first) and creates nothing +- **AND** after the user removes the line and reruns, init scaffolds a + normal local root + +#### Scenario: No Pointer, No Root — Nothing Changed + +- **GIVEN** a directory with no `openspec/` anywhere up the walk +- **WHEN** a command runs with registered stores present +- **THEN** the slice 1.2 stores-hint error appears, byte-identical to + today + +### The Composition Holds + +#### Scenario: Declared Root With References + +- **GIVEN** the declared store's own config carries `references:` +- **WHEN** `instructions` runs in the pointer repo +- **THEN** the index reflects the store's references (3.1 symmetric + behavior through the declared root) diff --git a/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/deletion-ledger.md b/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/deletion-ledger.md new file mode 100644 index 000000000..3ff337396 --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/deletion-ledger.md @@ -0,0 +1,111 @@ +# Deletion Ledger: Legacy Command Groups + +Generated 2026-06-11 by diffing +`rg -o "(workspace|initiative)_[a-z_]+" src/**/*.ts | sort -u` between the +pre-deletion commit (`ef45d5d`) and the deletion commit. For the +capstone's agent-contract inventory and dead-code sweep. + +## Surviving tokens (deliberate) + +- `initiative_option_removed` — the `new change --initiative` rejection, + locked in slice 1.2. Lives in `src/commands/workflow/new-change.ts`. + +## Removed diagnostic codes (emitted only by deleted command paths) + +Initiative group: + +- initiative_already_exists +- initiative_ambiguous +- initiative_collection_invalid +- initiative_collections_invalid +- initiative_collections_partially_invalid +- initiative_discovery_failed +- initiative_error +- initiative_id_required +- initiative_invalid +- initiative_lookup_incomplete +- initiative_not_found +- initiative_summary_required +- initiative_title_required + +Workspace group: + +- workspace_already_exists +- workspace_context_bind_required +- workspace_context_conflict +- workspace_create_failed +- workspace_error +- workspace_initiative_missing +- workspace_initiative_selection_ambiguous +- workspace_initiative_unavailable +- workspace_local_state_invalid +- workspace_name_collision +- workspace_no_available_openers +- workspace_not_found +- workspace_not_in_known_views +- workspace_open_change_unsupported +- workspace_open_link_skipped +- workspace_open_prepare_only_unsupported +- workspace_opener_conflict +- workspace_opener_launch_failed +- workspace_opener_unavailable +- workspace_opener_unset +- workspace_root_missing +- workspace_selection_ambiguous +- workspace_selection_conflict +- workspace_skills_out_of_sync +- workspace_state_invalid +- workspace_store_unavailable +- invalid_workspace_setup_tools (sweep fragment `workspace_setup_tools`) +- invalid_workspace_update_tools (sweep fragment `workspace_update_tools`) + +(`workspace_open_store_without_initiative` was already deleted by rider 1 +of slice 1.4 and is recorded in that slice's history.) + +## Removed non-code tokens (zod paths, JSON keys, target fragments) + +- initiative_id, initiative_reference (selector/zod field names) +- workspace_name, workspace_agent, workspace_opener (option/zod field + names in the deleted command layer) + +## Dead-export carve-outs (EXECUTED by 4.1 on 2026-06-11) + +Exports inside kept modules whose last consumer died with this slice. +They belonged to the workspace state model that 4.1 replaced; 4.1 +deleted every entry below, WIDENED to whole-module deaths where the +keep-rationale collapsed (`src/core/workspace/` whole, `binding.ts` +whole, `getRepoPath`, the five template guards, the planning-home and +change-status-policy workspace branches, the library pins that froze +them, and the `workspace_skills` vocabulary-allowlist entry). The +historical list: + +- `findWorkspaceRoot`, `isWorkspaceRoot` — + `src/core/workspace/state-io.ts` +- `resolveStoreBinding`, `createPathStoreBinding`, + `createRegisteredStoreBinding` — `src/core/store/binding.ts` +- `resolveCurrentPlanningHomeSync`'s workspace branch — + `src/core/planning-home.ts` (CLI-unreachable since slice 1.2's + resolver demotion; library behavior pinned by + `test/core/planning-home.test.ts`) +- `buildActionContext`'s workspace-planning branch — + `src/core/change-status-policy.ts` (same; pinned by + `test/commands/legacy-groups-removed.test.ts`) +- `readOptionalWorkspaceViewState`, `isWorkspaceRoot`, + `writeWorkspaceViewState`, `workspaceChangesDirExists` — + `src/core/workspace/state-io.ts` (production consumers died with the + commands; only planning-home's read path and tests remain) + +## Accepted collateral and known follow-ups + +- **Minor error-fidelity change in `openspec update`**: pre-deletion, an + unreadable `openspec/` entry (EACCES) surfaced the raw fs error via + the deleted detection helper; the unconditional path now reports the + standard no-project error. Accepted as part of decision 2a's behavior + change. +- **The accepted spec library still describes deleted behavior**: + `openspec/specs/cli-config`, `workspace-open`, `workspace-foundation`, + and `cli-artifact-workflow` specs REQUIRE workspace/initiative flows + that no longer exist. This is the roadmap's parked Later Idea **L2** + ("Decide how accepted workspace-planning specs should change once + behavior has changed") — deliberately not resolved by this slice; the + capstone should surface it under known gaps. diff --git a/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/plan.md b/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/plan.md new file mode 100644 index 000000000..3c88923cc --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/plan.md @@ -0,0 +1,216 @@ +# Delete Legacy Command Groups Plan + +## Status + +Spec locked 2026-06-11 after two parallel adversarial rounds (both +initially rejected; all findings verified against code and folded: the +config-command integration, the binding.ts carve-out, the narrowed 5.1 +wording, the concepts.md section, the constraint rewording). Plan +drafted 2026-06-11. Implementation not started. + +The main move: + +```text +Delete the workspace and initiative command groups and everything only +they consumed — about −13k lines — while the planning-home contract, +legacy metadata display, and all user data stay byte-identical. +``` + +## Source Of Truth + +Start from `spec.md` (this folder). Also keep nearby: + +- `../../roadmap.md` (5.1 criteria with the narrowed sequencing wording, + Rules We Should Not Forget) +- `../store-rename-and-guidance/spec.md` (the 1.4 surfaces this slice + must not regress: vocabulary sweep, store teaching, template guards) + +Sequencing: stacks on the 1.4 tip. Phase 3 slices assume these groups +are gone (no more second meanings to design around). + +## User-Facing Frame + +- "Show me only the product that exists: roots, stores, the lifecycle." +- "Don't touch my files — old initiative folders and workspace state + stay where they are." +- "If an old change carries initiative metadata, keep showing it to me." + +## Goals + +- Delete the command layer (15 files), the orphaned core (5 workspace + modules + the collections tree), the completions entries, the + workspace-profile integration in `config`, the dead docs, and the + tests of all of it. +- Keep planning-home, legacy display, `initiative_option_removed`, the + store group, and the 1.3/1.4 guarantees green and unchanged. +- Commit `deletion-ledger.md` (39 removed diagnostic codes + the + dead-export carve-outs owned by 4.1). +- Report the net LOC delta. + +## Non-Goals + +- No changes to `schemas/workspace-planning/`, the `workspace-planning` + mode value, planning-home behavior, or the template guards. +- No user-data deletion or migration; no doctor warnings about orphaned + view state (4.1's problem space). +- No behavior changes beyond the spec's three named ones (update + detection block; config workspace integration; the constraint-string + rewording). + +## Deletion Map (from the spec, re-verified at execution time) + +Every deletion below is executed with a grep-before-delete: list the +module's importers; if anything outside the deletion set imports it, +stop and re-plan rather than force. + +**Wave 1 — command layer and registrations** + +- `src/commands/workspace.ts`, `src/commands/workspace/` (11 files), + `src/commands/initiative.ts`. +- `src/cli/index.ts`: imports (~21, 23), registrations (~349, 351), the + `findWorkspaceRoot` update-detection block (~205-210) and its import + (~24). +- `src/commands/config.ts`: the `WorkspaceConfigProfileContext` + interface (49-52), workspace context resolution (199-211), + drift-warning workspace branch (228-252), apply-guidance workspace + branch (254-261), the core-preset call sites (523-524), the + apply-to-workspace exec flow (674-697), and the workspace imports + (25-29). + +**Wave 2 — orphaned core and barrels** + +- `src/core/workspace/{registry,openers,open-surface,skills,link-input}.ts`; + prune `src/core/workspace/index.ts` exports to the kept pair + (foundation, state-io — legacy-state is not barrel-exported; its + consumers import it directly). +- `src/core/collections/` whole tree; remove its barrel line from + `src/core/index.ts`. +- Keep: `binding.ts` (foundation depends on it), `foundation.ts`, + `state-io.ts`, `legacy-state.ts`, `planning-home.ts`. +- Reword the constraint string at `src/core/change-status-policy.ts:99`. + +**Wave 3 — completions and docs** + +- `src/core/completions/command-registry.ts`: delete the `workspace` + (~251-407) and `initiative` (~502-589) group entries (the parity test + enforces lockstep with Wave 1). +- `docs/cli.md`: workspace section (~179-349), the six + `openspec workspace ...` rows in the agent-compatible table (51-56), + initiative rows/sections (~63-64, ~444-491), summary-table rows (~10 + — and the kept Stores row's cell text, which lists + `initiative create/show/list`, gets an in-row edit), and the two + `openspec workspace update` instructions in the Configuration + Commands section (1178, 1180). +- `docs/workspaces-beta/` deleted; `docs/concepts.md` "Coordination + Workspaces" section (~52-194) deleted. + +**Wave 4 — tests** + +- Delete whole: `test/commands/workspace.test.ts`, + `workspace.interactive.test.ts`, `workspace-open.test.ts`, + `workspace-initiative-open.test.ts`, `initiative.test.ts`, + `test/core/workspace/skills.test.ts`, `test/core/collections/` (tree), + `test/helpers/path-env.ts`. +- Partial edits: `test/commands/config-profile.test.ts` (the + workspace-profile helper at 134-172 and the four workspace cases at + 422-516; keep the project-apply coverage at ~402), + `test/core/store/registry.test.ts` (initiatives-collection portions, + ~615-624 plus the import at line 11; binding tests stay), + `test/core/workspace/foundation.test.ts` (deleted-module portions + only; state-shape tests stay), and + `test/core/completions/command-registry.test.ts` (remove the + now-obsolete initiative carve-out at ~157-161 in the `--store` + description walk — a deliberate fourth partial edit named in the + spec). No expectations currently pin the reworded constraint string; + the new pin lives in the Wave 5 test, and + `change-initiative-link.test.ts` stays unchanged. +- Keep green unchanged: `change-initiative-link.test.ts`, + `test/core/planning-home.test.ts`, + `test/core/workspace/legacy-state.test.ts`, store suite, journey, + vocabulary sweep. + +**Wave 5 — new tests and the ledger** + +- New tests (in an existing suitable file or a small + `test/commands/legacy-groups-removed.test.ts`): + - `openspec workspace list` / `openspec initiative list` → unknown + command, exit 1 (runCLI, built binary). + - `--help` lists neither group (in-process registry/`program` checks + are already enforced by parity; the e2e check covers help output). + - Update fall-through: view-state dir, `openspec update` → standard + no-project error, no workspace mention. + - User-data survival: store with `initiatives/` + XDG view state; + run `store list`, `store doctor`, `store remove `, `update`, + `status`, `new change`; compare trees before/after with the + `snapshotDirectory` approach from + `test/cli-e2e/store-lifecycle.test.ts:62-80` (relpath→content map). + - Legacy display: the human-readable `Initiative: /` line + is pinned nowhere today — assert it here over a legacy-metadata + fixture (a plain `status` run). `change-initiative-link.test.ts` + stays unchanged (it pins the JSON field and the flag rejection). + - Planning-home mode pin: `status --json` over a + `.openspec-workspace/view.yaml` fixture asserts + `actionContext.mode === 'workspace-planning'` and the reworded + read-only constraint string. (Plan-review finding: no existing test + asserts the mode — `planning-home.test.ts` checks only + `PlanningHome.kind`.) +- `deletion-ledger.md`: the 39 codes, generated with a precise + `rg -o "(workspace|initiative)_[a-z_]+" src test | sort -u` inventory + before and after (classifying data fields like `workspace_skills` + separately from diagnostic codes), plus the dead-export carve-outs + (`findWorkspaceRoot`, `isWorkspaceRoot`, `resolveStoreBinding`, + `createPathStoreBinding`, `createRegisteredStoreBinding`) each with + owner 4.1. + +## Execution Order + +One checkpoint, one commit (the waves are not independently shippable — +the build only compiles with all of them done): + +1. Wave 1 + 2 together (compiler-driven: delete files, chase the import + errors through barrels and config.ts). +2. Wave 3 (parity test forces completions lockstep; docs mechanical). +3. Wave 4 + 5 (test deletions, partial edits, new tests, ledger). +4. `pnpm run build`, full `pnpm test`, built-binary smoke + (`workspace`/`initiative` unknown; `--help`; store group intact), + and the explicit pointer gate: + `grep -rn "openspec workspace\|openspec initiative" docs/ src/ .codex/` + must return nothing (the vocabulary sweep does not police these — + `workspace`/`initiative` are not retired tokens). +5. Capture net LOC delta (`git diff --shortstat HEAD~1`) for the + changelog; commit. + +If the suite reveals a consumer the grep missed, stop, record the +correction in the spec (ground truth), and re-run — never force a +deletion through by stubbing. + +## Risks And Guardrails + +- **Hidden consumers through barrels**: `src/core/index.ts` re-exports + everything; a kept module may import a deleted symbol via the barrel + rather than directly. The compiler catches imports; grep each deleted + *export name* too (string-based access or re-export chains). +- **The config command edit is behavior, not just deletion**: keep + `config profile` working globally; only the workspace branch goes. + Its tests define the kept behavior — edit them deliberately. +- **registry.test.ts surgery**: the initiatives-collection block sits + inside a kept file; delete only that describe/it scope and its + imports, keep binding coverage. +- **Vocabulary sweep stays green**: deleted docs can't regress it, but + the new test file must not introduce retired tokens (use the + established concatenation constants if needed — likely unnecessary + since `workspace`/`initiative` are not retired tokens). +- **User-data test isolation**: build the fixture store + view state in + temp XDG dirs; hash with a stable tree walk (reuse the journey test's + approach in `store-lifecycle.test.ts`). +- **LOC delta accuracy**: report `git diff --shortstat` of the single + implementation commit, splitting src/test/docs in the changelog note. + +## Done Definition + +- All spec acceptance scenarios pass; the implementation commit is on + `codex/store-root-parity` with the full suite green. +- `deletion-ledger.md` committed; net LOC delta recorded in the + changelog. +- Roadmap 5.1 first-tranche boxes ticked (cleanup plan written, cleanup + done, tests/review checks pass), pointer moved to 3.1. diff --git a/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/remainder.md b/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/remainder.md new file mode 100644 index 000000000..9cec9014d --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/remainder.md @@ -0,0 +1,53 @@ +# The Phase 5 Remainder (closing out 5.1) + +Decided and executed 2026-06-11, after 4.1, per the queue. The locked +5.1 criteria govern: delete, don't hide; never auto-delete user data. +Everything below is repo-owned project material in THIS repository +(schemas we ship, our own planning artifacts, our own accepted specs) — +not user data. + +## 1. `schemas/workspace-planning/` — DELETED + +After 4.1, no src code names the schema (`WORKSPACE_DEFAULT_SCHEMA` +died with planning-home's collapse), but `openspec schemas` still +ADVERTISED it — a shipped invitation into a workflow whose commands, +mode, and state model no longer exist. That is the precise "old surface +that misleads" the 5.1 criteria target. The directory (schema.yaml + +templates) is deleted; `openspec schemas` now lists `spec-driven` +alone. + +## 2. Obsolete beta change folders — the four `workspace-*` DELETED + +`openspec/changes/{workspace-agent-guidance, workspace-apply-repo-slice, +workspace-reimplementation-roadmap, workspace-verify-and-archive}` are +planning relics of the dead beta (mostly bare proposals; none +implemented). Archiving them would assert they were completed — a lie; +keeping them active advertises dead work. Deleted; git history +preserves them. The other change folders (add-*, fix-*, schema-*, etc.) +are NOT workspace-beta material and stay untouched. + +## 3. L2 — the accepted workspace-era specs + +The parked question: what happens to accepted specs that REQUIRE +deleted behavior. Decision in two grades: + +- **Wholly-workspace specs DELETED**: `workspace-open`, + `workspace-foundation`, `workspace-change-planning`, + `workspace-links`. Every requirement in them mandates commands and + state that no longer exist; an accepted-spec library that REQUIRES + the impossible is worse than one with a gap. Capability gone = + spec gone. +- **Mixed specs get a bounded excision, not a rewrite**: in + `cli-config`, the "Config profile applies to current workspace" + requirement dies (the prompt flow it mandates was deleted). In + `cli-artifact-workflow`, the "Workspace Setup Commands" and + "Workspace schema instructions" requirements die whole, and the + workspace-scoped scenarios/clauses inside the status-JSON and + planning-context requirements are removed (status JSON no longer + reports workspace anything). No other rewording. +- **Incidental mentions elsewhere are recorded, not rewritten**: + `change-creation`, `artifact-graph`, `cli-update`, + `openspec-conventions`, `schema-resolution` mention workspace + historically or peripherally; sweeping them is the broad docs + rewrite the roadmap forbids. Recorded as capstone + vocabulary-audit input. diff --git a/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/spec.md b/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/spec.md new file mode 100644 index 000000000..79a7ed13e --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/delete-legacy-command-groups/spec.md @@ -0,0 +1,332 @@ +# Delete Legacy Command Groups Spec + +## Outcome + +The `openspec workspace` and `openspec initiative` command groups no +longer exist, and everything that only they consumed goes with them — +command layer, orphaned core modules, completions entries, tests, and +docs. After this slice the CLI's visible surface is the simple path: +OpenSpec roots, stores, and the normal lifecycle commands. What survives +is exactly what other surfaces still need: the planning-home +workspace-mode contract (until 4.1 rebuilds opening), legacy change +metadata display, the `--initiative` rejection error, and every byte of +user data on disk. + +This is the "small command-group deletion slice" the locked 5.1 criteria +sequenced "soon after 1.4". Slice 1.4 already stopped guidance from +advertising these groups; this slice deletes the groups themselves. The +opening machinery's state model dies later, when 4.1 replaces it. + +## Locked Decisions (from roadmap 5.1, 2026-06-11) + +1. **Delete, don't hide.** With zero users, hiding keeps every cost and + protects nobody. No hidden aliases, no deprecation shims, no + redirect stubs for the deleted groups. +2. **Sequenced.** Guidance surfaces died in 1.4 (done); the command + groups die here; the opening machinery and the + `workspace-planning` mode die when 4.1 replaces opening. +3. **Never delete user data.** Initiative directories inside stores, + workspace view state under the XDG data dir, and workspace `changes/` + directories stay on disk untouched. Git history is the undo for + code; nothing is the undo for user data. +4. **Phase 5 deletion slices proceed without confirmation** (runbook): + they delete code and generated guidance only. + +## Decisions This Spec Makes (autonomous, recorded in the changelog) + +1. **Orphans go with the groups.** Delete-don't-hide applies transitively + to code whose last consumer is a deleted command: the five + command-consumed core workspace modules (`registry`, `openers`, + `open-surface`, `link-input`, and `skills` — the last also consumed by + the surviving `config` command, whose workspace-profile integration is + deleted with it, see decision 2) and the entire `src/core/collections/` + tree (the initiatives collection plus the collection runtime — its + only consumers are the dying commands). Leaving them would recreate + the hidden-not-deleted state 5.1 rejected. `src/core/store/binding.ts` + is **not** an orphan and stays: the kept `workspace/foundation.ts` + imports its types and normalization for the persisted view-state + shape (planning-home depends on it transitively). +2. **Surviving commands stop pointing at the dead groups — two included + behavior changes.** (a) `openspec update`'s workspace detection + (`src/cli/index.ts:~205-210` via `findWorkspaceRoot`) errors with + "Run `openspec workspace update`…", a dead command after this slice; + the block is deleted and `update` in a workspace view dir falls + through to the standard no-project error. (b) The `config` command's + workspace-profile integration — drift warnings naming + `openspec workspace update` (`src/commands/config.ts:228-261`), the + workspace context resolution (`:199-211`), and the interactive + apply-to-workspace flow that **executes** + `npx openspec workspace update` (`:674-697`) — is deleted whole. + `config profile` keeps working for global profile management with no + workspace awareness. +3. **The planning-home carve-out is exact.** `src/core/planning-home.ts` + keeps resolving workspace view state (`workspaceStateFileExistsSync`, + `readWorkspaceViewStateSync`, `getWorkspaceChangesDir`), so + `src/core/workspace/foundation.ts`, `state-io.ts`, `legacy-state.ts`, + and `src/core/store/binding.ts` (the view-state binding types) stay; + the `actionContext.mode: "workspace-planning"` contract value stays; + and the five workflow template guards stay. Existing on-disk view + state created before this slice still produces workspace-planning + mode. Precisely: the workspace **state model** and the + `workspace-planning` mode die in 4.1; the zero-consumer opening + helpers (`openers`, `open-surface`) die now because nothing can reach + them once `workspace open` is gone. This narrows the roadmap's + "opening machinery dies when 4.1 replaces it" wording — the + controlling locked criterion is delete-don't-hide, and keeping + unreachable files would recreate exactly the hidden state 5.1 + rejected; the narrowed wording is recorded in the roadmap changelog + as a reviewable autonomous decision. +4. **Deliberate dead-export carve-outs are recorded, not hidden.** Some + exports inside kept modules lose their last consumer with this slice + (`findWorkspaceRoot`/`isWorkspaceRoot` in `state-io.ts`; + `resolveStoreBinding` and the binding constructors in `binding.ts`). + They are kept because they belong to the state model 4.1 replaces; + the slice ledger lists them explicitly so the capstone's dead-code + sweep reads them as deliberate carve-outs with a named owner (4.1), + not as misses. +5. **Legacy display and rejection survive; one constraint string + rewords.** Old initiative-linked changes remain displayable: the + `InitiativeLink` change-metadata shape and the `status`/`instructions` + legacy display lines read from change metadata (artifact-graph), not + from the deleted collections code. `new change --initiative` keeps + failing with `initiative_option_removed` (locked in 1.2). + `test/commands/change-initiative-link.test.ts` covers exactly these + survivors and is kept, not deleted. One surviving workspace-planning + constraint string still steers toward the old model ("Use initiatives + for durable coordination when initiative context exists.", + `src/core/change-status-policy.ts:99`); it rewords to read-only + compatibility language ("Treat existing initiative context as + read-only coordination context.") — a string edit inside a kept + module, not a contract change. +6. **A deletion ledger is committed.** `deletion-ledger.md` in this slice + folder records (a) the 39 `workspace_*`/`initiative_*` diagnostic + codes removed with the commands (verified by sweep; the sole survivor + is `initiative_option_removed`), and (b) the dead-export carve-outs + from decision 4 — so the capstone's agent-contract inventory and + dead-code sweep can verify the surface shrank deliberately. +7. **Docs about nothing get deleted, not updated.** `docs/cli.md` loses + its workspace and initiative sections and summary-table rows; + `docs/workspaces-beta/` (which documents only the deleted groups) is + deleted whole; `docs/concepts.md` loses its entire "Coordination + Workspaces" section (the mental model, layout, and its ~17 dead + invocations — deleting only the command lines would strand the + prose). This supersedes the 1.4 decision that parked the beta docs + for the Phase 5 remainder — with the commands gone, every line in + them is a dead invocation. + +## User Experience + +A user (or agent) exploring the CLI sees roots, stores, and the +lifecycle — nothing else: + +```text +$ openspec --help + ... init, update, list, view, validate, show, archive, status, + instructions, templates, schemas, new, store, completion ... +$ openspec workspace list +error: unknown command 'workspace' +$ openspec initiative list +error: unknown command 'initiative' +``` + +Nothing points at the dead groups: no help text, no completions, no +docs, no generated guidance (1.4 already cleaned those), no error hint +anywhere in the surviving CLI names a `workspace` or `initiative` +command. + +A team with old beta data loses no files: initiative folders inside +their store and workspace view directories are still on disk, old +initiative-linked changes still show their `Initiative: /` +line in `status`/`instructions`, and an agent standing in a leftover +workspace view directory still gets the guarded workspace-planning +behavior until Phase 4 replaces opening. + +## Scope + +In scope — deletions: + +- **Command layer**: `src/commands/workspace.ts`, + `src/commands/workspace/` (all 11 files), `src/commands/initiative.ts`; + their imports and registrations in `src/cli/index.ts` (lines ~21, 23, + 349, 351) and the `findWorkspaceRoot` update-detection block + (~205-210). +- **The `config` command's workspace-profile integration** (decision 2b): + `src/commands/config.ts` workspace context resolution, drift warnings, + apply-to-workspace exec flow, and the corresponding tests in + `test/commands/config-profile.test.ts` (the drift checks and the + apply-to-workspace flow tests, ~lines 422-441 and related). +- **Orphaned core**: `src/core/workspace/{registry,openers,open-surface,skills,link-input}.ts`; + `src/core/collections/` (whole tree: `initiatives/`, `runtime.ts`, + `index.ts`); all barrel exports of the deleted modules + (`src/core/index.ts`, `src/core/workspace/index.ts`). `binding.ts` + stays (decision 1). Implementation must re-verify each orphan's + consumer list at deletion time (the compiler plus a grep for each + deleted export). +- **Completions**: the `workspace` and `initiative` group entries in + `src/core/completions/command-registry.ts` (~250-407, ~502-589). +- **Tests of deleted surfaces**: `test/commands/workspace.test.ts`, + `workspace.interactive.test.ts`, `workspace-open.test.ts`, + `workspace-initiative-open.test.ts`, `initiative.test.ts`; + `test/core/workspace/skills.test.ts`; + `test/core/collections/` (whole tree); the deleted-module portions of + `test/core/workspace/foundation.test.ts`; the initiatives-collection + portions of `test/core/store/registry.test.ts` (~615-623; its binding + tests stay with the kept module); the orphaned + `test/helpers/path-env.ts` (its only importers are deleted test + files). +- **Docs**: `docs/cli.md` workspace and initiative sections plus their + summary-table rows; `docs/workspaces-beta/` deleted; + `docs/concepts.md` "Coordination Workspaces" section deleted whole. +- **Constraint rewording** (decision 5): the "Use initiatives…" line in + `src/core/change-status-policy.ts:99` becomes read-only compatibility + language; its test expectations update. +- **Ledger**: commit `deletion-ledger.md` in this slice folder + (decisions 4 and 6). + +In scope — survivors that need deliberate care: + +- `src/core/planning-home.ts` and its workspace state dependencies + (`foundation.ts`, `state-io.ts`, `legacy-state.ts`) keep working; + `test/core/planning-home.test.ts` and + `test/core/workspace/legacy-state.test.ts` stay green. +- Legacy initiative display in `status`/`instructions` and the + `initiative_option_removed` rejection; `change-initiative-link.test.ts` + stays green unchanged. +- The store group, root selection, the 1.3 journey, and the 1.4 + vocabulary sweep stay green unchanged. + +Out of scope: + +- `schemas/workspace-planning/` content and the `workspace-planning` + schema name (Phase 5 remainder decides its fate). +- The `actionContext.mode` contract, planning-home behavior changes, or + any opening/assembly replacement (4.1). +- Deleting or migrating user data: initiative dirs, view state, + workspace changes dirs. +- Any change to surviving command behavior beyond the two named in + decision 2 (`openspec update` detection-block removal; `config` + workspace-profile integration removal) and the constraint-string + rewording in decision 5. +- The store feature, references, targets (Phase 3). + +## Acceptance Criteria + +### The Groups Are Gone + +#### Scenario: Unknown Commands, Everywhere + +- **WHEN** the user runs `openspec workspace ` or + `openspec initiative ` +- **THEN** the CLI fails with Commander's unknown-command error, exit 1, + no alias, no redirect stub +- **AND** `openspec --help` lists neither group +- **AND** the completions registry contains no `workspace` or + `initiative` entries (the registry/Commander parity test enforces both + sides) + +#### Scenario: Nothing Points At The Dead Groups + +- **WHEN** the surviving CLI prints any help, error, hint, or fix text, + and when `docs/` (and `.codex/` guidance on disk) are grepped for + `openspec workspace` and `openspec initiative` +- **THEN** no live surface instructs running a deleted command +- **AND** the only remaining `workspace` vocabulary in generated + guidance is the five template guards quoting the still-live + `actionContext.mode: "workspace-planning"` contract + +### The Orphans Went With Them + +#### Scenario: No Hidden-Not-Deleted Code + +- **WHEN** the deleted modules' former exports are grepped across `src/` +- **THEN** no consumer remains and no deleted-module file remains + (`src/core/collections/` and the five deleted workspace core modules: + `registry`, `openers`, `open-surface`, `skills`, `link-input`) +- **AND** the build compiles with no unused-import or missing-module + errors +- **AND** the barrel files export no deleted symbols + +#### Scenario: The Contract Surface Shrank Deliberately + +- **WHEN** the capstone's agent-contract inventory and dead-code sweep + run later +- **THEN** `deletion-ledger.md` in this slice folder lists the 39 + `workspace_*`/`initiative_*` diagnostic codes removed with the + commands (sole survivor: `initiative_option_removed`) and the + dead-export carve-outs kept for 4.1 +- **AND** no surviving code path emits any removed code + +### The Survivors Still Work + +#### Scenario: Planning-Home Behavior Is Byte-Stable + +Ground truth discovered during implementation: `workspace-planning` +mode has been **unreachable from the CLI since slice 1.2** — every +supported command derives its planning home via `toPlanningHome`, which +hardcodes `kind: 'repo'` (`src/core/root-selection.ts:320-327`), and the +one remaining `resolveCurrentPlanningHomeSync` reference is a default +parameter whose only caller always overrides it. The carve-out this +slice preserves is the planning-home **library** contract, which 4.1 +owns: + +- **GIVEN** a directory carrying pre-existing workspace view state +- **WHEN** `status --json` runs there +- **THEN** it reports `repo-local`, exactly as it did before this slice + (the 1.2 demotion already made the workspace branch CLI-unreachable) +- **AND** the planning-home library still resolves the view state to + `kind: 'workspace'` (existing `planning-home.test.ts` coverage) and + `buildActionContext` still maps that to `workspace-planning` with the + reworded read-only initiative-context constraint (pinned by a new + unit test) +- **AND** the five template guards stay byte-identical (they quote the + library contract that 4.1 deletes) + +#### Scenario: Legacy Initiative Links Still Display + +- **GIVEN** a change with legacy initiative metadata in + `.openspec.yaml` +- **WHEN** `status`/`instructions` run on it +- **THEN** the `Initiative: /` legacy display still appears +- **AND** `new change --initiative x` still fails with + `initiative_option_removed` + +#### Scenario: User Data Survives + +- **GIVEN** a store containing an `initiatives/` directory and an XDG + data dir containing workspace view state +- **WHEN** the representative surviving command set runs — `store list`, + `store doctor`, `store remove` of an *unrelated* store, + `openspec update`, `status`, and `new change` in that store +- **THEN** the initiative directory and the view state are + byte-identical afterward (hash the trees before and after) +- **AND** no surviving command offers to delete them + +#### Scenario: Update Falls Through Cleanly + +- **GIVEN** the working directory is a workspace view dir with no + OpenSpec project +- **WHEN** the user runs `openspec update` +- **THEN** the standard no-project error appears, with no mention of + workspace commands + +### Nothing Else Moves + +#### Scenario: The Rest Of The Suite Is Byte-Stable + +- **WHEN** the full suite runs after the deletion +- **THEN** every kept test passes unchanged — store group, root + selection, the 1.3 two-checkout journey, the 1.4 vocabulary sweep and + guards, `change-initiative-link` (unchanged — new assertions about the + legacy display live in the new test file, never here), planning-home, + legacy-state, and the binding tests in + `test/core/store/registry.test.ts` +- **AND** the only test diffs are whole-file deletions, the named + partial edits (`config-profile.test.ts` workspace-profile coverage + including its helper and the core-preset case, ~134-172 and 422-516; + `registry.test.ts` initiatives-collection removal; + `foundation.test.ts` deleted-module portions; + `command-registry.test.ts` removal of the now-obsolete initiative + carve-out in the `--store` description walk), and the **additions**: + the new removal-coverage test file and the planning-home mode pin +- **AND** the net LOC delta of the slice is reported in the changelog + (expected on the order of −13k lines including tests) diff --git a/openspec/work/simplify-context-and-workspace-model/slices/personal-worksets/capstone-dogfood.md b/openspec/work/simplify-context-and-workspace-model/slices/personal-worksets/capstone-dogfood.md new file mode 100644 index 000000000..df48702ad --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/personal-worksets/capstone-dogfood.md @@ -0,0 +1,162 @@ +# 7.1 Capstone Dogfood Transcript + +Date: 2026-06-12, after the simplify pass (567bb03). Environment: a +scratch dir with isolated XDG state (`XDG_DATA_HOME`/`XDG_CONFIG_HOME` +under `/tmp/openspec-7.1-dogfood/`), three member folders (a planning +root, a code repo, a plain notes folder), and fake `code`, `cursor`, +`claude`, `codex` executables on a fully controlled PATH — each shim +records its cwd and argv to a launch log and exits 0. The CLI under +test is the built `dist/cli/index.js` via an `openspec` wrapper on the +same PATH. Per the runbook's 7.1 amendment: the scripted +compose→list→open walk for both launch styles with exact-argv +verification, then a cold-start UX walk by a fresh headless agent. + +## Leg 1 — scripted walk (the user's seat, non-interactive) + +```text +$ openspec workset create platform --member src/team-context --member src/web-app --member notes --tool claude + +Saved workset 'platform' (3 members) to your machine. +Open it any time with: openspec workset open platform +exit=0 + +$ openspec workset list +platform (opens in Claude Code) + team-context /private/tmp/openspec-7.1-dogfood/src/team-context + web-app /private/tmp/openspec-7.1-dogfood/src/web-app + notes /private/tmp/openspec-7.1-dogfood/notes +exit=0 + +$ openspec workset open platform --tool code +Opening 'platform' in VS Code (a window opens; this command returns). +exit=0 + +$ openspec workset open platform # saved tool: claude (attach-dirs) +Handing this terminal to Claude Code for 'platform' (the session ends when you exit). +exit=0 + +$ openspec workset open platform --tool codex +Handing this terminal to codex for 'platform' (the session ends when you exit). +exit=0 +``` + +The generated `.code-workspace` (regenerated on every open): + +```json +{ + "folders": [ + { "name": "team-context", "path": "/private/tmp/openspec-7.1-dogfood/src/team-context" }, + { "name": "web-app", "path": "/private/tmp/openspec-7.1-dogfood/src/web-app" }, + { "name": "notes", "path": "/private/tmp/openspec-7.1-dogfood/notes" } + ] +} +``` + +The recorded launches — exact argv per tool, cwd at the primary +member, **no positional anywhere** (the no-prompt rule), one attach +pair per member with the primary included, codex's sandbox pre-args +first: + +```json +{"tool":"code","cwd":".../src/team-context","args":[".../data/openspec/worksets/platform.code-workspace"]} +{"tool":"claude","cwd":".../src/team-context","args":["--add-dir", ".../src/team-context", "--add-dir", ".../src/web-app", "--add-dir", ".../notes"]} +{"tool":"codex","cwd":".../src/team-context","args":["--sandbox", "workspace-write", "--add-dir", ".../src/team-context", "--add-dir", ".../src/web-app", "--add-dir", ".../notes"]} +``` + +The wrong turns: + +```text +$ openspec workset open platform --tool zed # unknown tool: the strand test +Error: Unknown tool 'zed'. +Fix: Known tools: code, cursor, claude, codex. Add new tools under "openers" in /tmp/openspec-7.1-dogfood/config/openspec/config.json. +Open manually: + Workspace file: /tmp/openspec-7.1-dogfood/data/openspec/worksets/platform.code-workspace + Members: + team-context /private/tmp/openspec-7.1-dogfood/src/team-context + web-app /private/tmp/openspec-7.1-dogfood/src/web-app + notes /private/tmp/openspec-7.1-dogfood/notes +exit=1 + +$ rm -rf notes && openspec workset open platform --tool code # missing member +Skipped 'notes' (/private/tmp/openspec-7.1-dogfood/notes is not available). +Opening 'platform' in VS Code (a window opens; this command returns). +exit=0 + +$ openspec workset remove platform --yes +Removed workset 'platform'. Member folders were not touched. +exit=0 + +$ openspec workset list +No worksets saved. Create one with: openspec workset create +exit=0 +``` + +Member folders verified byte-untouched after the whole walk (only the +original fixture files present). + +## Leg 1b — the interactive wizard (real pty, driven by expect) + +Answers: name typed, first folder accepted at the `.` default, Finish, +first tool in the select (VS Code — all four fakes available), open-now +declined. + +```text +[1/3] Name the workset +? Workset name: platform-two +[2/3] Add member folders (the first one is the primary - sessions start there) +? Folder path: . + Added 'openspec-7.1-dogfood' (/private/tmp/openspec-7.1-dogfood) +? Add another folder or finish: Finish +[3/3] Choose your tool +? Open with: VS Code + +Saved workset 'platform-two' (1 member) to your machine. +? Open it now in VS Code? No +Open it any time with: openspec workset open platform-two +exit=0 +``` + +Saved state confirmed (`tool: code`, basename-labeled member, absolute +path). A separate pty run where stdin hit EOF at the name prompt +exercised the cancellation path live: `Cancelled.`, exit 130, nothing +saved. + +## Leg 2 — cold start (fresh headless agent, no insider knowledge) + +A fresh `codex exec` session (gpt-5.5, medium) in the scratch dir with +fresh XDG state, given only this prompt: the user works across the +three folders daily, was told "the openspec CLI can keep a named view +of folders and open them together", knows no commands, and must start +from `openspec --help`. The agent's own report of its path: + +```sh +openspec --help +openspec workset --help +openspec workset create --help +openspec workset open --help +openspec workset list --help +openspec workset create daily-context --member ./src/team-context --member ./src/web-app --member ./notes --tool claude --json +openspec workset open daily-context --tool claude +``` + +It discovered the group from top-level help ("personal working +views"), drilled into subcommand help, composed non-interactively with +repeatable `--member` flags, and opened the view. Physical evidence: +the launch log shows claude invoked with cwd at the primary and one +`--add-dir` pair per member (no positional), and the fresh data dir +holds exactly the spec-shaped `worksets.yaml`. **An agent with zero +insider knowledge reached an opened workset from `--help` alone.** + +## Verdict + +Every runbook capstone check passes: compose→list→open for both launch +styles with exact argv verified (including the no-prompt rule), the +generated workspace-file contents, the failure fallback, the +missing-member skip, safe removal, member-folder isolation, the +interactive wizard from a real pty, live cancellation, and the +cold-start agent walk. No product findings surfaced — the only defect +found during the run was in the dogfood's own first fake-tool shim +(it routed argv through `node -e`, which ate `--add-dir` as a node +option; rewritten with printf). Raw transcripts in +`/tmp/openspec-7.1-dogfood/` during the run; the durable record is +this file. diff --git a/openspec/work/simplify-context-and-workspace-model/slices/personal-worksets/plan.md b/openspec/work/simplify-context-and-workspace-model/slices/personal-worksets/plan.md new file mode 100644 index 000000000..3462b3d5e --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/personal-worksets/plan.md @@ -0,0 +1,343 @@ +# Personal Worksets Plan (7.1) + +## Status + +- Research checkpoint committed (`research.md`, 980b056). +- Spec written and dual-reviewed (subagent approve-with-fixes, codex + reject → all findings folded; 6f4ca4a). The spec's 14 numbered + decisions are the contract this plan implements. +- This plan: two implementation checkpoints, each ending with a full + green `pnpm test` and a commit. + +## Source Of Truth + +- `slices/personal-worksets/spec.md` — decisions 1–14 + acceptance + criteria. +- Roadmap 7.1 FR1/FR2 and locked decisions (owner-directed). +- `slices/personal-worksets/research.md` — mechanics evidence + (`f858c19^` citations). + +## Current Code Map (anchors verified 2026-06-12) + +Storage idiom to copy / extract from: + +- `src/core/global-config.ts:78-102` — `getGlobalDataDir` with + injectable `{env, platform, homedir}`; `:35-56` `getGlobalConfigDir`; + `:116-170` `getGlobalConfig`/`saveGlobalConfig` (spread-parsed, so an + `openers` key survives round-trips); `:147-153` malformed-JSON → + stderr warning + defaults. +- `src/core/config-schema.ts:7-25` — `GlobalConfigSchema` is + `.passthrough()`; `:38-67` `KNOWN_TOP_LEVEL_KEYS` (config set rejects + unknown keys; worksets add nothing here — hand-edit-only at v1). +- `src/core/store/foundation.ts:188-194` — strict zod state schema with + `version: z.literal(1)`; `:259-292` parse; `:314-336` serialize + (re-validates); `:211-237` `invalidStoreStateError` ("Repair or + remove ."); `:391-406` **private** `writeFileAtomically`; + `:414-460` **private** `acquireStoreRegistryLock` (wx-open, 30s + stale-steal, 5s deadline, 25ms sleeps); `:462-480` + `updateStoreRegistryState` (lock → read → updater → write → unlock). + The extraction target: both privates move to `src/core/file-state.ts` + with the busy-error factory parameterized; foundation delegates. +- `src/core/store/registry.ts:210-229, 288-306` — pure + `withRegisteredStore`/`withoutRegisteredStore` rebuild pattern; + `:544-555` no-op pre-read before locking. +- `src/core/id.ts:5-13` — `isKebabId`, `KEBAB_ID_DESCRIPTION`. + +Command/output idiom: + +- `src/commands/repo.ts:149-181` — the minimal group registration + model (group description pulled from the completions registry). +- `src/commands/store.ts:222-227` — `isPromptCancellationError` + (duplicated at `src/commands/config.ts:91`; a third copy justifies + extraction — put the helper in `src/commands/shared-output.ts`); + `:243-381` prompt idioms (dynamic `@inquirer` imports, validate + wrappers, `prefill: 'editable'`, plan-then-confirm, `--yes`); + `:675-679` `Cancelled.` + exit 130; `:761-825` the `command:*` + unknown-subcommand handler emitting one JSON document. +- `src/commands/shared-output.ts:9-48` — `printJson`, `asStatus`, + `emitFailure`. +- `src/commands/context.ts:140-178` — write-guard + stderr + confirmation idiom; `:30` null-shape failure payload pattern. +- `src/utils/interactive.ts:17-28` — `resolveNoInteractive`, + `isInteractive`. +- `src/cli/index.ts:22-25, 348-351` — import + registration block; + `:49-54` hidden rejected `Option` pattern (for `open --json`); + `:60-61` the one-JSON-document failure comment; `:118-129` telemetry + preAction (generic; no per-command work). +- `src/core/completions/command-registry.ts:251-347` (store group with + subcommands), `:349-364` (context), `:374-405` (repo) — the + `workset` entry follows the store shape (group + four subcommands). +- `src/core/working-set.ts:93-107` — `buildCodeWorkspaceJson` + conventions to mirror (NOT generalized; recorded in spec d14). +- `package.json:77` — `"cross-spawn": "7.0.6"`, currently zero + importers. + +Old mechanics to port (all at `f858c19^`): + +- `src/core/workspace/openers.ts:48-108` — PATH scan (PATH/Path/path + keys, win32 PATHEXT default `.COM;.EXE;.BAT;.CMD`, posix X_OK, + separator-bearing commands stat directly, injectable + `{env, platform}`); `:144-172` available-first stable sort + + `( not found on PATH)` notes + first-available default. Spec + d14 sharpens: platform-keyed delimiter/join (`path.win32`/ + `path.posix`), extension-bearing commands match as-is. +- `src/commands/workspace/open.ts:21-22` — cross-spawn via + `createRequire`; `:175-218` launch promise (error event vs close); + spec d6/d7 replace the close handling (honest code/signal + propagation). +- `src/commands/workspace/prompt-theme.ts:3-26` — chalk prompt theme + (recoverable; reuse as `workset` prompt theme only if trivial — + optional polish, not a contract). +- `src/commands/workspace/setup-prompts.ts:29-160` — the member-loop + prompt shape. +- `test/helpers/path-env.ts` — `pathEnvKey`, `withPrependedPathEnv` + (resurrect verbatim). +- `test/commands/workspace-initiative-open.test.ts:~93-121` — + `createFakeExecutable` recorder pattern (posix shim + `.cmd` twin + + `OPENSPEC_FAKE_OPEN_LOG`); resurrect as a shared helper + `test/helpers/fake-tool.ts`. + +Test harness: + +- `test/helpers/run-cli.ts:56-91` — built-CLI runner, merges + `OPEN_SPEC_INTERACTIVE: '0'`. +- `test/commands/context.test.ts:20-27` — the XDG isolation block + (mkdtemp + realpath, `XDG_DATA_HOME`/`XDG_CONFIG_HOME`, + `OPENSPEC_TELEMETRY: '0'`, `getGlobalDataDir({env})`). +- `test/core/store/foundation.test.ts` / `registry.test.ts` — unit + homes; they pin the store behavior the file-state extraction must + not change. + +## Implementation Plan + +### Checkpoint 1 — core: file-state extraction, worksets storage, openers (commit) + +1. `src/core/file-state.ts` (new): move `writeFileAtomically` and the + lock-acquire loop out of store foundation verbatim, parameterizing + the two REAL error sites (plan-review correction — stale-steal is + silent rm-and-continue, `foundation.ts:441-448`; the sites are + lock-create failure at `:428-435` and deadline timeout at + `:451-454`): `errorFor: (kind: 'create-failed' | 'timeout', + info: { lockPath, cause? }) => Error`. Store foundation delegates; + its emitted errors stay byte-identical. **The existing suite does + NOT pin this** (plan-review correction: nothing in `test/` covers + the lock, stale steal, busy errors, or atomic-write failure) — so + CP1 adds the pins itself: the two store busy-error byte shapes + asserted *through the foundation path* (message + `Cannot create the registry lock file ().` + its fix; + `Store registry is busy.` + the stale-lock fix), alongside the + direct file-state units. +2. `src/core/worksets.ts` (new): spec d2/d3/d4/d12. + - Paths: `getWorksetsDir`, `getWorksetsFilePath`, + `getWorksetCodeWorkspacePath(name)` — all threading + `{ globalDataDir? }` like `StorePathOptions`. + - Schema (zod, strict): `{ version: 1, worksets: Record }`; + parse enforces kebab names via `isKebabId`, absolute member + paths, non-empty/separator-free/non-dot labels, intra-workset + label uniqueness; `tool` is a plain string. + - `parseWorksetsState` / `serializeWorksetsState` (re-validates); + `invalid_workset_file` / `workset_file_busy` via the shared + file-state helpers; absent file ⇒ empty state (the registry + precedent). + - Pure `withWorkset` (throws `workset_exists`) / `withoutWorkset` + (throws `workset_not_found` with saved names / create-command + fix); `updateWorksetsState(updater)`; **`withWorksetsLock(fn)`** + (lock → read → `fn(state)` → release, no yaml write-back — + plan-review fix: `open` needs a lock-scoped read plus + derived-file write without rewriting `worksets.yaml`, which the + store-pattern updater cannot express); read-only `listWorksets`, + `getWorkset`. + - Pure `buildWorksetCodeWorkspaceJson(members)` mirroring the + working-set builder's conventions (folders in member order, + saved names, absolute paths, 2-space JSON + newline). + - Errors: `WorksetError extends Error` with `.diagnostic` — reuse + `StoreError` directly instead if nothing workset-specific is + needed (`asStatus` duck-types `.diagnostic`, so either works; + prefer reusing `StoreError` to avoid a parallel class — decide + in code, record in the spec if it matters). +3. `src/core/openers.ts` (new): spec d5/d6. + - `BUILTIN_OPENERS` table (`code`, `cursor`, `claude`, `codex` rows + per the locked table); `OpenerDefinition { id, label, style, + command, args, attachFlag }`. + - `mergeOpenerConfig(builtins, raw)` — per-field override for known + ids, full rows for new ids (`style` required, `command` defaults + to id), typed `invalid_opener_config` on unknown style/malformed + row (strict per-row zod). + - `readOpenerConfig()` — reads the global config file's `openers` + key (via `getGlobalConfig`; malformed file already degrades with + the existing stderr warning). + - `isExecutableAvailable(command, {env, platform})` + + `listOpenerChoices(table, opts)` — the `f858c19^` scan with the + d14 sharpenings. + - `buildLaunchCommand(opener, { members, codeWorkspacePath })` — + pure (plan-review fix: the workspace-file style needs the + generated file's path as an input); workspace-file ⇒ + `{ executable, args: [codeWorkspacePath], cwd: primary }`; + attach-dirs ⇒ `{ executable, args: [...pre, ...members.flatMap( + m => [attachFlag, m.path])], cwd: primary }`; returns + `{ executable, args, cwd, label, style }`; never a positional. +4. Unit tests: `test/core/file-state.test.ts` (atomic write, lock + contention, stale steal, the two error kinds), + `test/core/worksets.test.ts` (parse/serialize round-trip, + hand-edit contract matrix, with/without, `withWorksetsLock`, lock + no-op reads, builder output), `test/core/openers.test.ts` (merge + matrix, availability incl. the win32 PATHEXT/`Path`/`tool.cmd` + matrix — fixture strategy recorded per plan review: the scan takes + an injectable `isExecutableFile` stat seam, since + `path.win32.join` output on a posix host produces + backslash-bearing filenames a naive fixture never matches; argv + builder incl. single-member, attach-pair-per-member, codex + pre-args, no-positional pin). Plus the store busy-error byte-shape + pins from item 1. +5. Full `pnpm test` green; commit. + +### Checkpoint 2 — command, registration, docs, e2e (commit) + +1. `src/commands/workset.ts` (+ `workset-prompts.ts` if the ~600-line + bar nears): the four subcommands per spec d1/d8/d9/d10/d11/d13. + - `create [name]`: interactive 3-step wizard / non-interactive + `--member` (+`name=path`) and `--tool` (validated against the + merged table); validation order: name → members → tool; write + under lock; offer-to-open (skipped when no tool saved; + suppressed non-interactive); JSON envelope `{ workset, status }`. + `--member` is repeatable via an explicit Commander collector + (`(value, prev) => [...prev, value]` with default `[]` — no repo + precedent exists and Commander keeps only the last value + otherwise; a parser test pins flag order). + - `list`: human at-a-glance + `{ worksets, status }` sorted by + name. + - `open [--tool ]`: **order fixed per the converged + plan-review P1** — resolve workset, then under the lock via + `withWorksetsLock`: re-read + regenerate `.code-workspace` + unconditionally (existing-and-directory members only; skip + notes; `workset_no_members_available` if none survive) → + release lock → resolve tool (`--tool` override → saved → + interactive select / typed `workset_tool_required`) → + availability check → pre-launch kind line → spawn (cross-spawn + via `createRequire(import.meta.url)` + `typeof nodeSpawn` cast, + the `f858c19^:open.ts:21-22` shape — no `@types/cross-spawn` + exists; `shell:false`, `stdio:'inherit'`, cwd = surviving + primary) → propagate exit code / `128+signal`. The + `workset_tool_unknown` / `workset_tool_unavailable` / + `workset_launch_failed` failures all fire AFTER regeneration, so + their "Open manually:" block always names an existing, current + file (the fallback test asserts the file's existence and + currency). `--json` registered as a hidden option + (`.hideHelp()`, the `cli/index.ts:49-54` precedent — parsed so + Commander never owns the error, kept out of help so a broken + mode is not advertised) and rejected in the action with the + one-document `workset_open_json_unsupported` payload. + - `remove `: plan-then-confirm / `--yes`; under the lock + delete entry + ENOENT-tolerant derived-file cleanup; + `{ removed, status }`. + - Group: description from the completions registry; `command:*` + handler (`unknown_workset_subcommand`); failure plumbing through + `emitFailure` with per-command null shapes; cancellation helper + extracted to shared-output (third copy). +2. Registration: `src/cli/index.ts` import + `registerWorksetCommand`; + `command-registry.ts` `workset` entry (group + 4 subcommands, + flags: `--member`, `--tool`, `--json`, `--yes`, + `--no-interactive`). +3. Docs: `docs/cli.md` — a "Personal worksets" section (concept + paragraph + command table rows + the opener-config example). +4. Tests: + - Resurrect `test/helpers/path-env.ts`; add + `test/helpers/fake-tool.ts` (recorder + posix/cmd shims). + - `test/commands/workset.test.ts`: non-interactive create + (+failure matrix: exists/members-required/member-invalid/name/ + unknown `--tool`), list (incl. the empty shape), remove + (+confirmation-required, not-found, never-opened), open per + fake tool (argv/cwd exact, exit code 7, missing-member skip, + primary fallback, no-members failure, open of an unknown name, + unknown/unavailable tool fallback block asserting the named + `.code-workspace` exists with current content AND the fix names + another installed tool, `--tool` override byte-unchanged yaml, + opener-config zed + attach_flag override + invalid style, + `open --json` rejection, unknown subcommand, command-level + corrupt `worksets.yaml` → `invalid_workset_file`). + - Launch mechanics that fake executables cannot exercise run as + in-process units through the d14 injectable-spawn seam + (plan-review fix): a fake ChildProcess emitting + `close(null, 'SIGINT')` pins the 130 path; an `error` event pins + `workset_launch_failed` (shell shims translate signals and a + PATH-absent tool can never reach the spawn-error branch). + - Interactive coverage (plan-review fix; `runCLI` forces + `OPEN_SPEC_INTERACTIVE=0`, so no CLI-spawned test can prompt): + in-process units with a stubbed TTY/env gate and + `vi.mock('@inquirer/prompts')` throwing `ExitPromptError` at + each compose boundary (name / member / tool / open-now confirm) + assert `Cancelled.`, exit 130, nothing saved. Typed cancellation + exists only on remove (`workset_remove_cancelled`, the declined + confirm — the spec's d12 was amended this round: create has no + abort-confirm, so `workset_create_cancelled` was dropped as a + dead code). If the gate stubbing proves brittle in + implementation, the fallback is recorded: cover the helper + + declined-confirm paths in-process and assign the Ctrl-C walk to + the capstone transcript explicitly. + - `test/cli-e2e/workset-journey.test.ts`: compose→list→open(both + styles)→remove with isolated XDG + fake tools; the two-data-dirs + teammate scenario; member-folder byte-untouched sweep + (fs-snapshot); `openspec context`/`doctor` byte-identical + before/after. +5. Full `pnpm test` green; commit. + +## Test Plan Summary + +Unit: file-state (3 areas), worksets storage (~10 cases), openers +(~12 cases). Command: ~20 cases over fake tools. E2e: 1 journey + the +teammate isolation + independence asserts. All hermetic (no real +editors/agents; PATH points at fakes; XDG isolated). Windows-specific +launch semantics are covered at the unit layer (injected +platform/env); the fake-tool `.cmd` twins keep command tests +OS-portable per the 1.3 precedent. + +## Risks And Guardrails + +- **Store-foundation extraction regression** — mitigated: mechanical + move, behavior-identical contract, foundation tests untouched and + green before/after; the new file-state tests cover the shared + mechanics directly. +- **Spawn behavior in tests** — recorder fakes exit 0 quickly; the + exit-7/SIGINT cases use dedicated fake scripts; no test inherits + the parent's stdio interactively (`stdio: 'inherit'` is fine under + vitest — the child writes nothing). +- **Interactive flows**: cancellation and declined confirms are + covered in-process (CP2 test item above); the remaining + interactive-only acceptance lines are enumerated to the capstone + transcript — the full wizard walk, the open-time tool select, the + offer-to-open decline next-step line, and the `create ` + step-echo. +- **`open --json` flag shape**: hidden `Option` (`.hideHelp()`) per + the `cli/index.ts:49-54` precedent — parsed so Commander never owns + the error, rejected in the action with the typed one-document + payload (plan review settled hidden over visible: help should not + advertise a mode that only rejects). +- **Lock-release → spawn TOCTOU, recorded**: a concurrent `remove` + can delete the regenerated `.code-workspace` between open's lock + release and the editor reading it. Spec d2 mandates + release-before-spawn; single-user machine-local state makes this + acceptable — recorded here so it is a decision, not a discovery. +- **Config plumbing**: `getGlobalConfig` reads `process.env` (not + injectable) — `readOpenerConfig` unit tests therefore test the pure + merge directly and route file-reading coverage through the CLI + layer's XDG env; the `GlobalConfig` interface gains an `openers?` + member (the schema is already `.passthrough()`). Diagnostic targets + follow spec d12's `workset.` convention. +- **Vocabulary**: all new strings say "workset"; the only + `workspace`-bearing token is the `.code-workspace` filename/flag + (the 4.1 precedent says hyphenated file references are sweep-safe); + diagnostic codes are all `workset_*`/`invalid_opener_config` — + no `workspace_*` tokens. +- **Module sizes**: worksets.ts and openers.ts each well under the + bar; workset.ts has the recorded split seam. + +## Done Definition + +- Both checkpoints committed; full suite green at each. +- Every spec acceptance scenario has an implementing test (or is the + capstone's recorded responsibility: the interactive wizard walk). +- No changes to `openspec context`, doctor, repo map, project config + parsing, or committed formats (e2e independence asserts prove it). +- Roadmap "Plan written" box ticked with changelog entries; spec kept + consistent with anything the plan round amended. diff --git a/openspec/work/simplify-context-and-workspace-model/slices/personal-worksets/research.md b/openspec/work/simplify-context-and-workspace-model/slices/personal-worksets/research.md new file mode 100644 index 000000000..3a309664a --- /dev/null +++ b/openspec/work/simplify-context-and-workspace-model/slices/personal-worksets/research.md @@ -0,0 +1,373 @@ +# Personal Worksets Research (7.1) + +Date: 2026-06-12. This is the slice's first checkpoint: the evidence base +for the spec. Sources: the deleted `workspace` opener machinery at +`f858c19^` (cited as `f858c19^:path:line`), the current tree at HEAD of +`codex/store-root-parity` (cited as `path:line`), and live verification of +the four built-in tools' CLIs on this machine (macOS; `code` 1.120.0, +`cursor` 3.5.1, `claude` 2.1.173, `codex` 0.128.0), supplemented by vendor +docs where a local check would have opened a window or session. + +Findings are evidence; decisions stay in the spec. Where the evidence +forces or strongly suggests a shape, it is marked **implication**. + +## R1 — Saved-views file: shape, location, name rules + +**Global data dir.** `getGlobalDataDir` (`src/core/global-config.ts:78-102`): +`$XDG_DATA_HOME/openspec` when set on any platform, else win32 +`%LOCALAPPDATA%/openspec` (with a homedir fallback), else +`~/.local/share/openspec`. Fully injectable via +`GlobalDataDirOptions { env?, platform?, homedir? }` (`:66-70`) — the test +seam every storage test uses. The store registry sits at +`/stores/registry.yaml` (`src/core/store/foundation.ts:13-16, +64-70`), with every read/write API threading +`StorePathOptions { globalDataDir? }`. A worksets file has an obvious +sibling slot in the same data dir. + +**The registry idiom is directly copyable.** The complete pattern: + +- Zod `.strict()` schema with `version: z.literal(1)` + (`foundation.ts:188-194`); parse = YAML → `safeParse` → + `formatZodIssues` → id-grammar check on keys (`:259-292`); serialize + re-validates before writing (`:314-336`). +- Atomic write: same-dir temp file + `fs.rename`, temp removed on error + (`writeFileAtomically`, `foundation.ts:391-406`). +- Lock: `${file}.lock` via `fs.open(..., 'wx')`, 30s stale-steal, 5s + deadline with 25ms sleeps, typed `store_registry_busy` on timeout + (`foundation.ts:412-460`); `updateStoreRegistryState(updater)` does + lock → read → update → write → unlock, and updaters may throw typed + errors from inside the lock (`:462-480`). +- Pure rebuilds `withRegisteredStore`/`withoutRegisteredStore` + (`src/core/store/registry.ts:208-229, 286-306`); no-op reruns never + take the write lock (`:544-555`). +- Corrupt file → typed diagnostic naming the file with a + "Repair or remove ." fix (`invalid_store_registry`, + `foundation.ts:211-237`). + +**Implication**: a separate `worksets.yaml` (not a new section in the +store registry) matches the feature's independence claims — the roadmap +pins "no changes to … the repo map" and worksets are not a Phase 3 +relationship, so they should not share the typed-sections file whose +cross-section uniqueness rules exist for stores/repos. Separate file, +same idiom. Deleting all workset state = deleting one file, which +satisfies "deleting all workset state loses nothing." + +**What the old workspace registry did wrong** (not inherited): it mapped +names to *managed* directories `/workspaces/` +(`f858c19^:src/core/workspace/registry.ts:13, 88-98`) and made each view a +directory lifecycle (rollback ceremony, `AGENTS.md` marker-fence sync, +`.gitignore` cleanup — `f858c19^:src/core/workspace/open-surface.ts:264-316`). +A saved view should be a record (name → ordered member paths + preferred +tool), not a directory. + +**Generated `.code-workspace` placement constraint.** FR1.3/FR1.5 and the +acceptance line "no member folder ever contains workset residue" mean the +generated workspace file cannot live in a member folder. The old code put +it in the managed workspace root. With no managed dirs, the natural home +is the data dir (e.g. `/worksets/.code-workspace`) — +machine-local, regenerable, deletable with the rest of workset state. +Counter-precedent: `store setup` deliberately suggests a *user-owned* +location, "never the managed XDG data dir" (`src/commands/store.ts:260-271`) +— but that comment is about the user's own repo, while this file is +derived state the user never edits. Spec decides. + +**Name validation.** One kebab grammar repo-wide: +`KEBAB_ID_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u`, `isKebabId`, +`KEBAB_ID_DESCRIPTION` (`src/core/id.ts:5-13`; header comment: "The one +kebab id grammar (Phase 3 lock: one id namespace)"). Error-wording idiom: +`` `Repo id '${id}' ${KEBAB_ID_DESCRIPTION}.` `` with a fix restating the +rule (`src/core/store/registry.ts:498-507`). Workset names live in their +own file, so no cross-section conflict checks with stores/repos apply — +but the grammar itself should be the same `isKebabId`. + +## R2 — Opener table and opener config + +**The two styles already existed implicitly.** The old opener model was a +`kind: 'agent' | 'editor'` discriminant +(`f858c19^:src/core/workspace/foundation.ts:16-43`): editor-style openers +received exactly `[codeWorkspacePath]` as argv; agent-style openers got +optional pre-args + `['--add-dir', path]` per attached path + cwd at the +root (`f858c19^:src/commands/workspace/open.ts:73-103`). That maps 1:1 to +FR2.3's `workspace-file` / `attach-dirs` styles. What 7.1 drops: the old +code appended `WORKSPACE_OPEN_MINIMAL_PROMPT = 'Open this OpenSpec +workspace.'` as a final positional on every agent launch +(`f858c19^:open.ts:19, 90-100`) — the locked no-starter-prompt decision +removes it; agent argv ends with the attach flags. + +**Identity was triple-keyed; collapse it.** Value strings (`'codex-cli'`), +structured `{kind, id}`, and label/executable lookups each re-switched on +raw ids including a `'codex'` alias +(`f858c19^:src/core/workspace/openers.ts:110-142`, +`foundation.ts:258-268`). **Implication**: one table row per tool — +`{ id, label, style, command, args?/attach_flag? }` — is the whole +identity, and user config rows are the same shape as built-in rows (the +git difftool/mergetool pattern FR2.3 names). + +**Availability scan (inherit nearly verbatim).** +`f858c19^:src/core/workspace/openers.ts:48-108`: PATH value from +`env.PATH ?? env.Path ?? env.path`; non-win32 extensions `['']`, win32 +`PATHEXT ?? '.COM;.EXE;.BAT;.CMD'`; candidate = `join(entry, exe + ext)` +must stat as a file, plus `X_OK` access on posix; executables containing a +path separator stat directly; all failures swallowed; injectable +`{ env?, platform? }`. Choices list available-first via a stable sort with +`( not found on PATH)` annotations (`:144-166`); default = first +available (`:168-172`). No caching — re-stats per call (fine at this call +frequency). + +**Built-in rows confirmed by live CLI verification** (details in the +per-tool section below): + +| id | style | launch shape | +| --- | --- | --- | +| `code` | workspace-file | `code .code-workspace` | +| `cursor` | workspace-file | `cursor .code-workspace` | +| `claude` | attach-dirs | cwd=primary, `claude --add-dir …` (repeatable flag also accepted) | +| `codex` | attach-dirs | cwd=primary, `codex --sandbox workspace-write --add-dir --add-dir …` | + +The old code's codex pre-args `['--sandbox', 'workspace-write']` +(`f858c19^:open.ts:57-60`) match the roadmap's pinned built-in table; it +applied them only when attach paths existed — simpler to apply always +(spec call). Per-member repeated `--add-dir ` pairs are the one +shape verified to parse for both agent CLIs (codex verified locally as +repeatable; claude's variadic `` also accepts the repeated +form, which is what the old shipped code emitted for it). + +**Opener config file: location candidates.** The repo splits homes by +kind: the global *config* dir holds user-edited JSON +(`/config.json`, permissive parse-with-defaults, +`src/core/global-config.ts:35-56, 116-170`); the global *data* dir holds +machine state YAML (registry). An opener table is user-edited +configuration → the config side fits. Candidates: a new top-level section +in `config.json` (cheapest; the file already has permissive parsing) or a +dedicated file. Merge semantics needed per FR2.3: built-ins exist without +any config; a user entry with a built-in id overrides that row's fields; a +new id adds a row; only the two known styles are accepted. + +**Cursor `.code-workspace` handling: verified.** The `cursor` shim +(`/usr/local/bin/cursor`, bash) resolves the app bundle and runs the stock +VS Code CLI entry (`ELECTRON_RUN_AS_NODE=1 "$CONTENTS/MacOS/Cursor" +"$CONTENTS/Resources/app/out/cli.js" "$@"`, args forwarded verbatim, no +eval). `cursor --help` mirrors `code --help` including the "folder or +workspace" wording on `--profile`; web evidence confirms +`cursor my.code-workspace` opens a multi-root workspace. Two shim +hazards recorded: + +- `cursor agent ...` routes to `~/.local/bin/cursor-agent` and + **auto-installs it via curl if missing**; `cursor editor ...` strips + `editor`. Mitigation: we pass exactly one argv entry, an absolute + workspace-file path, which can never equal a bare `agent`. +- A reported quirk in Cursor's "glass" multi-workbench mode can open + workspace files in the Agent Window (`--classic` is the community + workaround). Not locally reproducible without opening a window; do not + pre-add `--classic` — a user can add it in opener config if bitten + (exactly the FR2.3 escape hatch). + +## R3 — Launch and terminal-handoff mechanics + +**Spawn shape (inherit).** The old launcher used **cross-spawn** — still a +declared dependency at exactly `7.0.6` (`package.json:77`) with zero +importers in the current tree (residue of the deletion; 7.1 becomes its +importer again or drops it deliberately): + +```ts +const child = spawn(executable, args, { + cwd, // the primary root + stdio: 'inherit', // 'ignore' in --json mode + shell: false, +}); +``` + +(`f858c19^:src/commands/workspace/open.ts:21-22, 175-218`.) Not detached, +no `unref()`, no env manipulation. Editor opens also awaited child exit — +fine because `code`/`cursor` CLIs hand off to the running app and exit +immediately. + +**Signal handling: none existed, deliberately usable.** No +`SIGINT`/`SIGTERM` listeners anywhere in the old tree. With +`stdio: 'inherit'` and the child in the foreground process group, the +terminal delivers Ctrl-C to both processes; the parent just awaits +`'close'`. That shipped and worked. **Implication**: the new launcher +needs no signal plumbing either, but the spec should pin the observable +contract (Ctrl-C in an agent session must not produce a parent error +banner over the agent's own exit). + +**Exit-code propagation was lossy — fix it.** A nonzero child exit +rejected the launch promise; the command's `handleFailure` flattened it to +`process.exitCode = 1` and printed +`Error: