Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 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
9544d36
feat(stores): encode PM → engineering handoffs as pull-based stage tr…
clay-good Jul 8, 2026
e9bae55
fix(tests): bump skill-generation template counts to 13 after update-…
clay-good Jul 8, 2026
9b1cd92
refactor(stores): make stage workflows fully persona-agnostic
clay-good Jul 8, 2026
a7e4246
feat(stores): close the dogfooded gaps — discovery on link, the upstr…
clay-good Jul 9, 2026
0ddec0d
feat(stores): teach the initiatives layout in the empty states
clay-good Jul 9, 2026
0841cf2
feat(stores): dead-simple team path — three commands, all wiring auto…
clay-good Jul 9, 2026
0de3263
fix(tests): set USERPROFILE alongside HOME in the default-path setup …
clay-good Jul 9, 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
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 |
| [Initiatives](stores-beta/initiatives.md) | The way in above a single change: your initiatives, your artifacts, live status |
| [Agent Contract](agent-contract.md) | The machine-readable CLI surfaces agents drive |

## The thirty-second version
Expand Down
10 changes: 5 additions & 5 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" }`. `--initiatives`: `{ "initiatives": { "path", "evergreen": [string], "initiatives": [ { "name", "exists", "stages": [{name,files}], "artifacts": [string], "changes": [ { "id", "store"?, "completedTasks", "totalTasks", "state": "complete"|"in-progress"|"no-tasks" } ], "changesComplete", "changesTotal", "tasksComplete", "tasksTotal" } ] } | null, "root" }` — `initiatives` is null when the root has no `openspec/initiatives/` folder; an entry with `"exists": false` is a name changes reference but no folder provides. Changes are discovered by scanning `.openspec.yaml` files for `initiative: <name>` (the root's own changes) and `initiative: <store-id>/<name>` (changes in other registered roots pointing at this store's initiative; the legacy object `{store, id}` reads as `<store>/<id>`); `changes[].store` names where a change lives, absent = the portfolio's own root. Outside any root (and without `--store`), the command answers with registered store portfolios instead: `{ "initiatives": null, "stores": [ { "store", "portfolio": <same shape as initiatives> } ], "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 @@ -59,7 +59,7 @@ Change: `{ "id", "title", "deltaCount", "deltas": [...], "root" }`. Spec: `{ "id
### 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" }`.

`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}], "initiatives"?: [string], "fetch"?, "status": [] }` — resolved entries carry root/specs/fetch; `schemas` (the store's own project-local artifact types) and `initiatives` (the store's initiative names — or, with none yet, its evergreen artifact names) 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" }`.
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], "initiatives"?: [string], "status": [] } ], "status": [] }`. AVAILABLE = path present AND status empty. `artifactTypes` (the store's own project-local schema names) and `initiatives` (the store's initiative names — or, with none yet, its evergreen artifact names) 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} ]` — 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`.
182 changes: 182 additions & 0 deletions docs/stores-beta/initiatives.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Initiatives

> **Beta.** Builds on [stores](user-guide.md). Names and shapes may change.

Big work starts above a single change — a roadmap, a product vision, a
quarter's effort — and today those artifacts have no home in OpenSpec, and no
connection to the changes carrying them out. `openspec/initiatives/` is that
home, and that connection.

## The whole idea in one picture

```text
openspec/initiatives/
roadmap.md evergreen — truths every initiative serves
smoother-setup/ one initiative = one folder; contents freeform
notes.md

openspec/changes/
add-search/
.openspec.yaml initiative: smoother-setup ← one line points UP
```

Two kinds of things live in the folder:

- **Evergreen artifacts** (unnumbered top-level files) are your standing
truths — the roadmap, the product, the architecture. Maintained forever,
the way specs are.
- **Initiatives** (subfolders) are finite: a piece of work above a single
change that runs to completion.

Point your in-flight changes at an initiative, and one command maps the
portfolio, live:

```bash
openspec list --initiatives
```

```text
Initiatives: /…/my-app/openspec/initiatives
Evergreen: roadmap.md

smoother-setup 1/2 changes complete
· add-search here 1/2 tasks
✓ fix-onboarding here 2/2 tasks
```

No manifest, no required artifact types, no new format. Status comes from
the changes' own task lists — nothing to keep in sync by hand.

## The same loop, one level up

```text
planning evergreen artifacts what is true initiatives what is in motion
code specs what is true changes what is in motion
```

Work flows down: an initiative decomposes into changes. Truth flows up:
finishing an initiative updates the evergreen artifacts, the way archiving a
change updates specs. Status flows back live — driven by structured facts on
disk (a task checked off, a change archived), never by prose.

## Stages: your workflow, in folder names

Number the folders inside an initiative and they become ordered stages —
and the names are your workflow. Any chain works, because none of them is
special:

```text
00_product/ 01_engineering/ a two-person team
00_product/ 01_design/ 02_engineering/ a product team
00_analysis/ 01_product/ 02_architecture/ 03_design/ 04_engineering/ a bigger org
00_idea/ 01_build/ a solo dev switching hats
```

Reconfiguring the workflow is renaming folders — no settings, no manifest,
no new skills. OpenSpec never learns your chain; it only knows *lower-numbered
is upstream*, and that is enough.

The handoff between stages is **pull-based, one rule**: whoever opens the
initiative reads everything lower-numbered first, then produces what their
stage owes the next one. Nothing pushes work downstream; the folder position
already says what is upstream and what comes next.

The documents themselves can be any format — a PRD, an RFC, a one-pager,
exported design notes. Position carries the meaning, not the format.
And stages are opt-in: an initiative with no numbers works exactly the same.

## Team: the portfolio lives in a store

Put the same folder in a [store](user-guide.md) — a planning repo the whole
team registers by name. The store is the parent; each code repo is a child
pointing up. The whole path is three commands:

```bash
openspec store setup team-plans # once — defaults to ~/openspec/team-plans
# put initiatives in team-plans/openspec/initiatives/ (or let /opsx:initiatives capture them)

# in any code repo — this one command does ALL the wiring:
openspec new change add-search --initiative team-plans/smoother-setup

openspec list --initiatives # from anywhere
```

```text
Initiatives: /…/team-plans/openspec/initiatives
Evergreen: roadmap.md

smoother-setup 2/3 changes complete
✓ add-payments-api api-server 2/2 tasks
· add-search my-app 1/2 tasks
✓ fix-onboarding my-app 2/2 tasks
```

Linking does all the wiring, and says so: it records the checkout so
rollups scan it (nothing written into the repo for that), and it adds
`references: [team-plans]` to the repo's `openspec/config.yaml` so agents
there see the store's context — announced in the output, one visible line
in your own config. One command then answers "where does everything
stand?" from anywhere — even outside any repo, where `list --initiatives`
shows the portfolios of your registered stores.

Going from solo to team is moving one folder into a store. The initiative
is untouched; its ref changes from `smoother-setup` to
`team-plans/smoother-setup`.

## The skill

One skill drives it: `openspec-initiatives` (`/opsx:initiatives`). It routes
by what is on disk — nothing there yet → it captures the conversation into a
first artifact (synthesizing what it already knows instead of
re-interviewing you); a portfolio exists → it opens with where everything
stands; inside an initiative → it ideates from what exists, decomposes into
self-contained changes born linked, and syncs the evergreen layer as work
completes. Every move ends in a short numbered menu of real next actions —
one marked recommended — and it is told to write *less*: one page per
artifact, tables over prose.

## The lifecycle, by position

Everyone talks to the same skill; the folder position — not the job title —
decides what happens. Whether your chain has two stages or five:

| Where you stand | Job to be done | What actually happens |
|---|---|---|
| nothing exists yet | capture the intent | ask the agent — `/opsx:initiatives` turns the conversation into the first stage's artifact, or drop in a doc you already have (a PRD, an RFC, a one-pager) |
| any middle stage | add your piece | open the initiative; the skill reads everything lower-numbered and drafts your stage — same move at `01_design/` as at `02_architecture/` |
| the last stage | turn plans into work | the skill decomposes it into changes born linked (`openspec new change <name> --initiative <ref>`), each tracing to something upstream |
| anywhere, anytime | know where it stands | `openspec list --initiatives` — live, from task lists, no bookkeeping |

**The handoff artifact between planning and building is the change itself** —
self-contained, worked with the normal change skills. The initiative travels
along: `openspec instructions` and `openspec instructions apply` for a linked
change open with the initiative it serves and where its upstream context
lives on disk, so intent reaches the working agent without anyone pasting
it. Status flows back with no meetings and no status updates written by
hand.

## What the pieces are

| Piece | What it is |
|---|---|
| `openspec/initiatives/` | evergreen truths + one folder per initiative |
| numbered folders inside an initiative | your workflow, as optional ordered stages |
| `initiative: <name>` / `<store-id>/<name>` | one line in a change's `.openspec.yaml` |
| `openspec new change <name> --initiative <ref>` | create a change already pointing up |
| `openspec list --initiatives [--store <id>]` | the portfolio + every linked change, live |
| `openspec-initiatives` skill | the guide through all of it |

## Honest limits

- Rollup scans checkouts this machine knows: registered stores, plus any
repo that has linked a change to a store's initiative. It never clones or
syncs.
- Stage order is a naming convention, not a gate. Nothing blocks working out
of order; the skill just knows what comes next.
- Reactions fire when the skill looks — reactive triggers beyond that (git
hooks, CI) are a natural next experiment, deliberately not this one.
- Want typed, checked artifacts someday? [Custom schemas](../customization.md)
already exist — initiative artifacts stay freeform until you want that.
- This repo dogfoods it: see
[openspec/initiatives/](../../openspec/initiatives/smoother-setup/goal.md)
— one initiative with two optional stages, grouping four real changes.
12 changes: 6 additions & 6 deletions docs/stores-beta/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Two rules keep this simple:
Two commands take you from nothing to a working, store-scoped change:

```bash
openspec store setup team-plans --path ~/openspec/team-plans
openspec store setup team-plans # defaults to ~/openspec/team-plans
```

```
Expand Down Expand Up @@ -93,8 +93,7 @@ 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
openspec store setup team-plans --remote git@github.com:acme/team-plans.git
git -C ~/openspec/team-plans push -u origin main
```

Expand Down Expand Up @@ -316,9 +315,10 @@ tells you which case you're in.
`openspec/config.yaml` declares `store: <id>` is treated as externalized
planning, not as a store checkout to register. Remove the `store:` line first
if you intentionally want to convert that repo into a local store root.
- **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`.
- **Some commands stay where they are.** `view`, `templates`, and the
deprecated noun forms (`openspec change show`, ...) act on the current
directory only — no `--store`. (`schemas` now accepts `--store` — see the
[initiatives guide](initiatives.md).)
- **Per-machine state is per-machine.** The store registry and worksets
are local settings. Nothing about your machine's layout is
ever committed to shared planning.
Expand Down
1 change: 1 addition & 0 deletions openspec/changes/add-global-install-scope/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
schema: spec-driven
created: 2026-02-21
initiative: smoother-setup
Loading
Loading