Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
cbb38f3
docs(stores): add initiatives guide + a real example
clay-good Jun 30, 2026
e179ae4
docs(stores): show an initiative as a collection of artifact types
clay-good Jun 30, 2026
8825ed2
docs(stores): add one-pager artifact type + a completed example graph
clay-good Jun 30, 2026
4aa2ebe
docs(stores): add a "Where this could go" section
clay-good Jun 30, 2026
9288424
docs(stores): rework initiatives prototype around the define-your-own…
clay-good Jul 1, 2026
2e1ce30
docs(stores): lock initiative precedence to canonical-store/local-shadow
clay-good Jul 1, 2026
7324d11
feat(stores): working prototype — store-aware schemas, initiative rol…
clay-good Jul 1, 2026
84681ba
feat(stores): surface a store's artifact types + initiatives in the a…
clay-good Jul 1, 2026
567456a
feat(stores): surface a store's artifact types + initiatives in `open…
clay-good Jul 1, 2026
44223bc
docs(stores): reflect that cross-repo artifact/initiative discovery n…
clay-good Jul 1, 2026
74e7360
feat(stores): add `openspec new initiative` thin scaffold
clay-good Jul 1, 2026
dec9652
docs(stores): use `openspec new initiative` in the guide's happy path
clay-good Jul 1, 2026
f3a5767
feat(stores): roll up initiative status across repos (cross-repo)
clay-good Jul 2, 2026
f0164d2
feat(stores): the plan folder — one clean prototype for work above a …
clay-good Jul 2, 2026
f6fe4c6
feat(stores): make the plan destination-first
clay-good Jul 2, 2026
66f6fed
fix(stores): surface destination-only plans in agent context
clay-good Jul 2, 2026
9cda627
feat(stores): the handoff — capture, decompose, hand off through changes
clay-good Jul 2, 2026
f90fb62
docs(stores): point the plan guide at worksets and schemas
clay-good Jul 2, 2026
8c7c25e
feat(stores): initiatives — a portfolio of finite work above evergree…
clay-good Jul 6, 2026
5ee8f92
Merge branch 'main' of https://github.com/Fission-AI/OpenSpec into do…
clay-good Jul 6, 2026
72c437b
fix(stores): pass projectRoot to task progress after merging main
clay-good Jul 6, 2026
bcf9c22
Merge branch 'main' of https://github.com/Fission-AI/OpenSpec into do…
clay-good Jul 8, 2026
a81ef23
fix(tests): reconcile workflow list and golden hashes after merging main
clay-good Jul 8, 2026
e9dff7d
feat(stores): encode PM → engineering handoffs as pull-based stage tr…
clay-good Jul 8, 2026
ba07f5a
fix(tests): bump skill-generation template counts to 13 after update-…
clay-good Jul 8, 2026
2f682ed
refactor(stores): make stage workflows fully persona-agnostic
clay-good Jul 8, 2026
74f2c17
feat(stores): close the dogfooded gaps — discovery on link, the upstr…
clay-good Jul 9, 2026
5dfdd78
feat(stores): teach the initiatives layout in the empty states
clay-good Jul 9, 2026
f588309
feat(stores): dead-simple team path — three commands, all wiring auto…
clay-good Jul 9, 2026
a6e2e30
fix(tests): set USERPROFILE alongside HOME in the default-path setup …
clay-good Jul 9, 2026
acb792d
refactor(stores): dissolve initiatives into typed changes + serves links
clay-good Jul 13, 2026
6c7ee0e
test(stores): retarget suite to serves links, downstream rollup, sche…
clay-good Jul 13, 2026
5b5b8f7
chore(stores): drop stale initiative-era exploration notes and update…
clay-good Jul 13, 2026
428b254
feat(stores): make the happy path one command deep
clay-good Jul 13, 2026
6d67c21
docs(stores): teach the happy path in the upstream-work guide and cha…
clay-good Jul 13, 2026
3b20c64
fix(stores): dogfood round findings — legacy string refs readable, ro…
clay-good Jul 13, 2026
4684edf
docs(stores): integrate upstream work into the beta guide, CLI refere…
clay-good Jul 13, 2026
e63ff98
chore: merge main (docs deploy workflow, release skill, v1.6.0 packag…
clay-good Jul 13, 2026
f4d35c5
refactor(schemas): fold scaffolding into schema init; seed spec Purpo…
clay-good Jul 13, 2026
1ec6c8f
docs(stores): state the any-format contract and the validate seam hon…
clay-good Jul 13, 2026
0948f02
fix(validate): stop demanding delta specs from spec-less workflow sch…
clay-good Jul 13, 2026
6737f23
feat(stores): stateless --scan for the rollup — the CI pattern now ru…
clay-good Jul 13, 2026
985d895
fix(stores): close the fresh-eyes findings from two independent sessions
clay-good Jul 13, 2026
f7c1d65
feat(schemas): sharpen the requirements workflow with deferrals, sign…
clay-good Jul 14, 2026
5ebeb86
feat(stores): divergence rule in the upstream block — intent stays ho…
clay-good Jul 14, 2026
714adad
feat(stores): executable structure + one door
clay-good Jul 14, 2026
981835a
docs(stores): bring the change record and every guide current with th…
clay-good Jul 14, 2026
a77d697
docs(stores): align the upstream-work guide vocabulary with the PR fr…
clay-good Jul 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .changeset/stores-upstream-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
"@fission-ai/openspec": minor
---

### New Features (stores beta)

- **Upstream links** — `openspec new change <name> --serves <store-id>/<change>` links a change to the store work it implements; the link wires context automatically (the store is referenced in the repo's config, the repo is recorded machine-locally for rollups), and the change's instructions carry the upstream artifacts to read first
- **Downstream rollup** — `openspec list --downstream [--store <id>]` shows each of the root's changes and every change on this machine that serves it, live from task checkboxes; archived upstream work stays visible and resolvable
- **Built-in `requirements` schema** — a documentation-only workflow (proposal → specs); fresh stores default to it, so a store is ready for shared planning with zero configuration
- **`openspec schema init` upgrades** — accepts `--store <id>` to scaffold into a registered store, and seeds per-stage `instructions/<artifact>.md` files (guidance agents receive, editable per stage) alongside templates
- **Schema inheritance** — repos that declare `references: [<store>]` resolve the store's schemas (project → referenced stores → user → built-in); `openspec schemas` labels inherited entries with their source store and accepts `--store <id>`
- **Schema `notes:` and instruction files** — a schema's top-level `notes:` are surfaced verbatim on every instruction surface; `instructions/<artifact>.md` beside the schema wins over inline `instruction:`
- **`structure:` config, now executable** — declare any folders or files in a root's layout (keys ending `/` are folders, other keys files); surfaced in `openspec context` and the references index, materialized by `store setup <id>` (markdown files seeded with their purpose), and drift is flagged by `store doctor`
- **`/opsx:store` — one door** — a generated skill (core profile) that reads machine state and routes the whole loop conversationally: set up, draft, link with `--serves`, show status, archive; every mechanic stays a deterministic CLI command underneath
- **One-command store setup** — `openspec store setup <id>` no longer requires `--path` (defaults to `~/openspec/<id>`) and its output teaches the draft → serve → rollup loop
- **Stateless CI rollups** — `openspec list --downstream --scan <dir>` scans a directory of checkouts with no per-machine state, so a CI job can publish team-wide status
- **Whole-change rollup status** — a serving change only counts as complete when its tasks AND its schema's artifacts are done; half-done work renders both counts
- **Custom stages in `schema init`** — `--artifacts` accepts custom kebab-case stage names in workflow order (each stage requires the previous); `schema validate` and `schema which` accept `--store <id>`

- **Intent stays honest** — a serving change's instructions carry a divergence rule (flag upstream instead of silently diverging), and the built-in `requirements` workflow now asks for explicit non-goals and measurable success signals, with requirement-traceability enforced in its specs guidance

### Fixes

- Legacy `initiative:` change metadata is tolerated in both the object and string forms (read-only, never re-emitted) instead of failing `status`
- `openspec new change` no longer names the wrong schema in its progress line when the root's config sets a default
- `openspec schema init --default` now writes the `schema:` key the config reader uses (previously wrote `defaultSchema:`, which nothing read), and its next-step hint prints a valid command
- `openspec validate` no longer demands delta specs from changes whose workflow schema defines no specs artifact (deliberately spec-less workflows validated with a spurious error)
- New specs created by archiving seed their Purpose from an explicit `## Purpose` in the delta spec, then the proposal's Why section, instead of a TBD placeholder
- Instruction output no longer ships an empty `<success_criteria>` placeholder; `status` prints a glyph legend when artifacts are blocked; archiving a change whose schema has no tasks phase says "not tracked by this schema" instead of "No tasks"
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ That second one matters more than it looks. OpenSpec has two halves: a command l
| Doc | What it gives you |
|-----|-------------------|
| [Stores: User Guide](stores-beta/user-guide.md) | Plan in its own repo when your work spans repos or teams |
| [Upstream Work](stores-beta/upstream-work.md) | Upstream work in a store: one door, your workflows, serves links, live status |
| [Agent Contract](agent-contract.md) | The machine-readable CLI surfaces agents drive |

## The thirty-second version
Expand Down
16 changes: 8 additions & 8 deletions docs/agent-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Successful JSON payloads embed the root:
## 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" }`.
`{ "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" }`. `--downstream`: `{ "downstream": { "path", "upstream": [ { "id", "exists", "archived", "changes": [ { "id", "store"?, "repo"?, "completedTasks", "totalTasks", "completedArtifacts"?, "totalArtifacts"?, "state": "complete"|"in-progress"|"no-tasks" } ], "changesComplete", "changesTotal", "tasksComplete", "tasksTotal" } ] } | null, "root" }` — `downstream` is null when the root has no `openspec/changes/` folder; each `upstream` entry is one of the root's changes plus every change on this machine that serves it. Serving changes are discovered by scanning `.openspec.yaml` files for `serves: <change>` (the root's own changes) and `serves: <store-id>/<change>` (changes in other registered roots and linked repos pointing at this store's change); `"exists": false` marks a ref whose target change is not on disk, `"archived": true` one whose target has been archived; `state: "complete"` requires tasks checked off AND (when the serving change's schema is readable) all artifacts present — `completedArtifacts`/`totalArtifacts` carry the artifact-graph progress and are absent when the schema cannot be resolved; `changes[].store`/`changes[].repo` name where a serving change lives, absent = the rolled-up root. `--scan <dir>` (repeatable, with `--downstream`) additionally scans the directory and its immediate subdirectories for serving changes — stateless, for machines with no linked-root records (CI). Outside any root (and without `--store`), the command answers with registered store rollups instead: `{ "downstream": null, "stores": [ { "store", "rollup": <same shape as downstream> } ], "root": null }`.

### 4.2 `show <item> --json`
Change: `{ "id", "title", "deltaCount", "deltas": [...], "root" }`. Spec: `{ "id", "title", "overview", "requirementCount", "requirements": [...], "metadata": { "version", "format", "sourcePath"? }, "root" }`.
Expand All @@ -57,15 +57,15 @@ Change: `{ "id", "title", "deltaCount", "deltas": [...], "root" }`. Spec: `{ "id
`{ "changeName", "schemaName", "planningHome"?: { "kind", "root", "changesDir", "defaultSchema" }, "changeRoot", "artifactPaths": { "<id>": {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 <artifact> --json`
`{ "changeName", "artifactId", "schemaName", "changeDir", "planningHome"?, "outputPath", "resolvedOutputPath", "existingOutputPaths", "description", "instruction"?, "context"?, "rules"?, "references"?: ReferenceIndexEntry[], "template", "dependencies": [{id,done,path,description}], "unlocks", "root" }`.
`{ "changeName", "artifactId", "schemaName", "changeDir", "planningHome"?, "outputPath", "resolvedOutputPath", "existingOutputPaths", "description", "instruction"?, "schemaNotes"?, "context"?, "rules"?, "references"?: ReferenceIndexEntry[], "upstream"?, "template", "dependencies": [{id,done,path,description}], "unlocks", "root" }` — `schemaNotes` carries the schema's top-level `notes:` verbatim; `upstream` is present when the change's metadata declares `serves:` and carries `{ "ref", "changeId", "store"?, "path": string|null, "archived" }` (path null = link is stale or the store is not registered here).

`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`).
`ReferenceIndexEntry`: `{ "store_id", "root"?, "specs"?: [{id,summary}], "schemas"?: [{id,summary,artifacts}], "changes"?: [string], "structure"?: {"<folder>": "<purpose>"}, "fetch"?, "status": [] }` — resolved entries carry root/specs/fetch; `schemas` (the store's own project-local artifact types), `changes` (its in-motion change ids), and `structure` (its config-declared folder purposes) are present only when the store defines them; unresolved carry store_id + warning status. Index capped at 50KB (`reference_index_truncated`).

### 4.6 `instructions apply --json`
`{ "changeName", "changeDir", "schemaName", "contextFiles": { "<artifactId>": ["/abs", ...] }, "progress": {total,complete,remaining}, "tasks": [{id,description,done}], "state": "blocked"|"all_done"|"ready", "missingArtifacts"?, "instruction", "references"?, "root" }`.
`{ "changeName", "changeDir", "schemaName", "contextFiles": { "<artifactId>": ["/abs", ...] }, "progress": {total,complete,remaining}, "tasks": [{id,description,done}], "state": "blocked"|"all_done"|"ready", "missingArtifacts"?, "instruction", "schemaNotes"?, "references"?, "upstream"?, "root" }` — `schemaNotes` and `upstream` as in 4.5.

### 4.7 `new change <name> --json`
Success: `{ "change": { "id", "path", "metadataPath", "schema" }, "root" }`. Failure: `{ "change": null, "status": [d] }`, exit 1.
Success: `{ "change": { "id", "path", "metadataPath", "schema" }, "serves"?, "root" }` — `serves` is present when `--serves <ref>` was passed and carries `{ "ref", "resolved", "reference_wiring"?: "added"|"already"|"skipped" }` (`resolved` = the upstream change was found on disk at link time; `reference_wiring` reports the automatic `references:` config edit). Failure: `{ "change": null, "status": [d] }`, exit 1.

### 4.8 `archive <name> --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.
Expand All @@ -74,13 +74,13 @@ Success: `{ "archive": { "change", "archivedAs": "YYYY-MM-DD-name", "path", "spe
`{ "root": { "path", "source", "store_id"?, "healthy", "status": [] }, "store": { "id", "metadata": {present,valid,remote?}, "origin_url"?, "status": [] } | null, "references": [...], "status": [] }`. Health findings of any severity exit 0. Failure payload: `{ "root": null, "store": null, "references": [], "status": [d] }`, exit 1.

### 4.10 `context --json`
`{ "root": { "path", "source", "store_id"?, "role": "openspec_root" }, "members": [ { "role": "referenced_store", "id", "path"?, "remote"?, "fetch"?, "status": [] } ], "status": [] }`. AVAILABLE = path present AND status empty. `--code-workspace <path>` writes `{folders:[{name,path}]}` (available referenced stores only, `ref:` 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.
`{ "root": { "path", "source", "store_id"?, "role": "openspec_root" }, "members": [ { "role": "referenced_store", "id", "path"?, "remote"?, "fetch"?, "artifactTypes"?: [string], "changes"?: [string], "structure"?: {"<folder>": "<purpose>"}, "status": [] } ], "status": [] }`. AVAILABLE = path present AND status empty. `artifactTypes` (the store's own project-local schema names), `changes` (its in-motion change ids), and `structure` (its config-declared layout — keys ending `/` are folders, other keys files) are present only on available members whose store defines them. `--code-workspace <path>` writes `{folders:[{name,path}]}` (available referenced stores only, `ref:` 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 `schemas --json` / `templates --json`
`schemas`: bare array `[ {name, description, artifacts, source} ]`. `templates`: keyed object `{ "<artifactId>": {path, source} }`. Both cwd-based, no root/status keys.
`schemas`: bare array `[ {name, description, artifacts, source, store?} ]` — `source` is `project|store|user|package`; `store` names the referenced store an inherited schema comes from. Supports `--store <id>` to list a store's schemas (success stays a bare array; a store-resolution failure emits `{status: [...]}` like other commands). `templates`: keyed object `{ "<artifactId>": {path, source} }`, cwd-based, no `--store`.

## 5. Exit-code contract

Expand Down Expand Up @@ -133,5 +133,5 @@ Recorded by the capstone audit; published-key renames are product decisions defe
4. Four parallel envelope type declarations exist in src; archive diagnostics never carry `target`.
5. `list --json` reuses the `status` key as a string enum per change.
6. Only `validate` output carries a `version` field.
7. `schemas`/`templates` ignore root selection (cwd-based, no `--store`).
7. `schemas` honors root selection (`--store <id>` lists a store's schemas); `templates` is still cwd-based (no `--store`).
8. Deprecated noun forms (`change`/`spec` subcommands) emit unenveloped payloads without `root`/`status`.
Loading
Loading