* fix(skills): stop workflows from adopting a project that never ran init Generated skills and commands are installed once per machine and offered in every repository the agent opens, including ones with no OpenSpec at all. Nothing stopped the workflow there: root resolution falls back to an implicit root at the current directory, so `openspec new change` quietly creates `openspec/` in whatever repo the agent happened to be standing in (#1645). Two changes, both in the generated instructions: - Every workflow now carries a shared project check. Before the first step that writes, the agent reads `root.source` from `openspec status --json`; `implicit` (or a `No OpenSpec root found` error) means the project is not set up, and the agent stops and asks the user whether to run `openspec init`, target a store, or drop OpenSpec for that request. It may not initialize the project on its own or let a command create the root as a side effect. - Every deployed skill description now names OpenSpec. Hosts pick skills by description, and "Enter explore mode - a thinking partner..." reads as a generic offer in a repository that has never heard of OpenSpec. Closes #1645 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(changeset): note the uninitialized-project guard Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): let onboarding run init once the user asks for it The guard read as an absolute ban on `openspec init`, which contradicts the option it offers one sentence earlier and the onboard workflow's job. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs(troubleshooting): explain an OpenSpec workflow starting in an unset-up project Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): check the root with a command that never fabricates one `openspec status --json` demands --change once a project has changes, so the guard's own check could fail in exactly the projects it should wave through. `openspec list --json` answers in one shape everywhere: a root object when the project is set up, `root: null` both when nothing is set up and when only stores are registered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(skills): pin the guard against every write, not the first fence CodeRabbit's point: checking only the first ```bash fence would miss a write outside a fence. Assert instead that nothing preceding the guard runs a command or writes, and that the guard sits directly under the store-selection guidance - both fail when the guard is moved down a workflow. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(new-change): say when the command had to create the root itself The generated workflows now check for a root before writing, but the guard is instructions - an agent that ignores it, or a human running the CLI directly, still turned an unset-up directory into an OpenSpec project without a word. Creating the root stays zero-config; it is no longer silent. Human output only: --json is unchanged, and `root.source` already carried the same fact for programmatic callers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): tell agents the root check's non-zero exit is the answer `openspec list --json` exits 1 when there is no root. An agent that reads that as a broken CLI is one step from hand-creating `openspec/` instead, which is the failure the guard exists to prevent. Also drops a vacuous assertion: the notice test now checks that the note names the directory it created and that the change really landed there. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * style(skills): read the guard back and untangle its two 'in that case' clauses Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): carry the store flag into the root check; pin the notice path exactly CodeRabbit, both valid: - With a store selected the store IS the root, so the check has to run as `openspec list --json --store <id>`. The store-selection paragraph above already says to append the flag to every command it lists, but leaving it implicit here invited a check against the wrong directory. - The notice assertion matched any `openspec/` suffix. It now pins the exact rendered path, and a new case runs the command from a subdirectory to show the note names the directory actually adopted (and that the repo above it is left alone). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: move the no-root contract to the canonical docs-lab pages alfred-openspec on #1787: docs-lab/README.md makes docs-lab/ canonical and the old docs/ tree legacy, and the canonical pages were stale in the two places the review named. - docs-lab/reference/cli.md, 'openspec new': documents the implicit-root notice after the 'Next:' line, with the exact output the CLI prints, that it goes to stdout and never appears with --json, and that JSON carries the same fact as root.source: implicit. Verified against a real run in an empty directory with an isolated HOME. - docs-lab/reference/skills.md: states the shared response and stop behavior once, above the index table, since it now holds for every skill: confirm the resolved root before the first write, stop when there is none, offer init, a store, or dropping OpenSpec, wait for the answer, never create openspec/ on its own. Drops the legacy docs/troubleshooting.md addition. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): make the no-root answer depend on how the workflow was reached alfred-openspec's product call on #1787. One answer could not serve both arrivals: #1645 asks the workflow to get out of the way ('it can go through the normal general propose not the openspec'), while a user who typed the skill's name is owed an answer about OpenSpec. The guard now branches after the same `openspec list --json` check: - Auto-selected: the model picked this workflow without the user naming OpenSpec, naming the skill, or running its command. Drop OpenSpec and answer the request normally, with no setup question and no mention of OpenSpec. - Explicit OpenSpec request: stop before writing and ask whether to run `openspec init`, target a store, or continue without OpenSpec, then wait. Neither branch may create the root as a side effect, stated once for both. One text serves both surfaces rather than a command-only variant, because apply-change and onboard render a single body into the skill and the command alike; a command-only constant would mean threading a surface flag through bodies that deliberately have none (#1515). The bullets scope themselves instead, and a slash command is an explicit invocation, so only the ask branch can apply there. A test pins that branch reaching every generated opsx command. Four regressions: the auto-selected branch (asserting it does not mention `openspec init` or `--store`), the explicit branch, the explicit branch's presence in every command file, and the shared no-side-effect rule. docs-lab/reference/skills.md said every no-root invocation asks. It now carries the same two branches as scan anchors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): keep the root guard off store-only projects and fix its docs A store-only project whose `store:` line names a store this machine has not registered reports `"root": null` from `openspec list --json`, so the guard read a real OpenSpec project as uninitialized. The guard now checks for the `Declared in` status message first and shows the store error instead. A stale global defaultStore reports the same codes in unrelated repositories, which is why the message prefix, not the code, decides. Propose's context step from #1657 offered `openspec init` on `no_openspec_root` regardless of how the workflow was reached. It now defers to the project check, so an auto-selected propose skill stays silent. The changeset names both no-root branches, and the `new change --json` docs separate the initialized example from the verified `implicit` one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(skills): keep the root guard off projects with a malformed store line A config-only project whose `store:` line is malformed reports `"root": null` with an `Invalid store declaration in` message, not `Declared in`, so the project check read it as never initialized and would drop OpenSpec or offer `openspec init` there. The guard now names both prefixes, and a root-selection test pins that every declaration failure starts with one of them while a stale global defaultStore starts with neither. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(skills): check guard ordering in the skill body, not its frontmatter A skill's YAML frontmatter is metadata a host reads to choose the skill, not instructions the agent runs, so a description that quotes a command name must not trip the ordering check. Scope the scan to the text after the closing frontmatter delimiter; a command injected into the body ahead of the guard still fails. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
docs-lab: parallel rebuild of the OpenSpec docs
Status: prose is landing page by page; the rest are skeletons (real headings plus a
one-line > job statement the site lifts into the page description). The live site
builds from this tree: website/docs.sync.config.mjs maps these files to published
pages, and the old docs/ tree is no longer used by the site.
This README owns the structure: which pages exist and which page teaches what. The
reverse view, from a job or message to the page that owns it, is
message-map.md. How to
write them (style, voice, formatting) is the write-openspec-docs skill's
writing.md.
The bar for every page
Every page in docs-lab is written by hand, from scratch. The old docs/ tree is source
material for facts, never text to carry over.
What we're after is that the reader gets the idea: every page reads well and makes sense to anyone, whatever their level of skill, and above all it is simple. The worst thing we can ship is documentation that is cognitively expensive to understand, and that cost comes from complicated words, metaphors that don't make sense, random terminology that isn't explained, and formatting that gets in the way of reading. Every sentence has a purpose and is easy to read and comprehend. If a sentence doesn't pass that test, rewrite it or cut it.
Structure rules
Folders are the areas. Every page lives in its area's folder (start/,
guides/, customize/, multi-repo/, reference/, help/); the root holds only this
README, message-map.md, and sources.md. Most folders publish as one
sidebar group; guides/ publishes as the Guides group, holding three collapsible
subgroups (Understanding OpenSpec, Using OpenSpec, Adopting OpenSpec), all expanded
by default (held back from the site until the pages are drafted: the whole section is
commented out in website/docs.sync.config.mjs, and links to a guide fall back to its
source on GitHub until it's re-listed). Reference holds three nested
folders (reference/architecture/, reference/schemas/, and
reference/configuration/), each publishing as a collapsible group with index.md as
its landing page; the spec-driven schema publishes as a single page
(reference/schemas/spec-driven/index.md) inside the Schemas group. Labels and URLs come from
website/docs.sync.config.mjs, so moving a file never moves a URL.
Teach once. The loop (propose, review, apply, archive) has one teacher; every other page links, never re-teaches:
start/quickstart.mdteaches it as UX: how a human moves a change through the lifecycle, including what archive does on disk.start/overview.mdshows it as pitch: copy only, no explanation.guides/concepts.mdstays out of it: the page explains the artifacts (specs, changes, the delta) and links to the quickstart for the loop. Disk paths appear inline with the concept that owns them, never as a layout section.start/installation.mdowns install;start/setup.mdowns init and what it writes. The quickstart opens with one prerequisite line linking both and starts at explore.
Guides vs reference. reference/skills.md holds each skill's contract: arguments,
what it creates, and what it responds with. Guide pages
(the Using and Adopting subgroups) own the human judgment for a task, including when
to reach for each skill, may span several skills, and never restate skill mechanics.
reference/architecture/ is the one exception to Reference's look-it-up bar: it's
explanation content, housed here as a pragmatic home while it's three pages. If it
grows (say, by absorbing contributor internals), consider giving it its own folder
and tab.
Reference is lookup, and named for it. reference/schemas/ and
reference/configuration/ are contracts: keys, values, types, defaults, and
locations, on tables and fences. Anything explanatory (what a schema is, what
to put in config.yaml) lives in Customize or Guides and is linked, never
restated. Naming follows three rules. A reference folder's landing
page is titled "Overview"; the folder label already names the group, and
repeating it double-nests the sidebar. A page documenting one file carries
concept and filename in the title, concept first, where the concept names the
file's use, never just its scope ("Project configuration (config.yaml)", "CLI
settings (config.json)"): the left edge is what the eye disambiguates in the
sidebar, and the filename keeps the title matching what readers search for and
see on disk. A file whose name is the term readers use keeps the filename
alone as the title (schema.yaml), and a page owning one product term takes
that term as the title (spec-driven), and
a page covering several files takes the concept alone (Stores), naming its
files in the job line.
FAQ is one-liners. Every FAQ entry is a short answer, a few lines at most, or a router link to the page that owns the topic. How-to content never lives in the FAQ: when an answer outgrows a one-liner, it moves to a guide or reference page and the FAQ entry becomes a pointer.
Page index: every page's job
Each goal below is the page's > blockquote verbatim, so the promise here is the promise
readers see. A page delivers exactly its goal: content that outgrows it means splitting
the page or rewriting the goal in both places, never letting them drift.
Start: from "what is this?" to your first archived change
| Page | Goal |
|---|---|
| Overview | TODO: emptied 2026-08-21 for a from-scratch rewrite and pulled from the site (/docs redirects to Installation meanwhile); the old goal line was dropped as too weak a pitch. Brief in Notes.md. |
| Installation | Install the openspec CLI on your machine, update it, and uninstall it. |
| Set up your project | Add OpenSpec to a project: run init, see what it wrote, and adjust it. |
| Quickstart | Your first change on your existing repo, from idea to archived. |
Guides: understand the system, use it well, bring it to your codebase and team
| Page | Goal |
|---|---|
| Understanding › Concepts | What the two artifacts are, and how a change describes a diff against current specs. |
| Using › Explore an idea | Think it through with the agent before you commit to a proposal. |
| Using › Review the plan | The two-minute pass that catches wrong turns before they're code. |
| Using › Apply a change | Run the plan: pacing, context windows, and picking up where you left off. |
| Using › Change course | Revise a change in flight, or decide it's cleaner to start fresh. |
| Adopting › Existing codebases | Bring OpenSpec to a codebase with a lot of code and no specs: where to start, what to backfill, and how specs grow from there. |
| Adopting › Teams | Run OpenSpec as a team: what to commit, how a change rides its PR, and when to archive. |
Customize: make the workflows fit your project
| Page | Goal |
|---|---|
| Overview | Your options for customizing OpenSpec. |
| Profiles | Choose which workflows are installed, and whether they install as skills, commands, or both. |
| Project configuration | Make the workflows plan changes the way you want with a few lines in config.yaml. |
| Schemas | Change what OpenSpec produces: the artifacts, their order, and their templates. |
Multi-repo (beta): plan across repository boundaries
| Page | Goal |
|---|---|
| Stores (beta) | Plan changes that span repositories: one store, many repos. |
| Worksets (beta) | Open the store and the repos that use it in one editor window, so your agent sees both. |
Reference: look it up, exact and complete
| Page | Goal |
|---|---|
| Skills | Every OpenSpec skill: arguments, what it creates, and what it responds with. |
| CLI | The openspec terminal commands. |
| Schemas | Every available workflow schema and the artifacts it defines. |
| Schemas › schema.yaml | Every field of a schema definition, for reading or writing one. |
| Schemas › spec-driven | The default workflow's artifacts: their order, their formats, and the change folder they produce. |
| Configuration | Every file and setting that changes how OpenSpec behaves, and where each lives. |
| Configuration › Project configuration (config.yaml) | Every field of openspec/config.yaml: the schema, context, and rules this project plans with. |
| Configuration › Change metadata (.openspec.yaml) | The supported fields and validation rules for the metadata stored with each change. |
| Configuration › CLI settings (config.json) | Every field of config.json: how the openspec CLI behaves on your machine. |
| Configuration › Environment variables | Every environment variable OpenSpec reads. |
| Configuration › Stores | The files behind multi-repo stores: registry.yaml and store.yaml, and which root a command uses. |
| Supported tools | Which AI coding tools OpenSpec supports, and each one's command syntax. |
| Glossary | Every OpenSpec term, one line each. |
| Architecture (held back from the site until drafted) | How OPSX is built: internals for the curious. |
| Architecture › Workflow runs | How a workflow run executes, from invocation to written artifacts. |
| Architecture › Design decisions | Why OPSX works the way it does. |
Help: get unstuck (held back from the site until drafted, see Open TODOs)
| Page | Goal |
|---|---|
| FAQ | Short answers to the questions that don't need a page. |
| Troubleshooting | When OpenSpec doesn't do what you expected: symptoms and their fixes. |
Legacy: land the old workflow safely (held back from the site until drafted, see Open TODOs)
| Page | Goal |
|---|---|
| Migrating from the legacy workflow | Moving from the legacy /openspec:* commands to OPSX. |
Old docs
The docs/ tree is legacy, and the plan is to remove it once docs-lab covers what it
owns. It has become a bit of an AI slop mess, so nothing from it is carried over as text
(see The bar for every page). Until it's removed it stays untouched: fixes land in
docs-lab, never in docs/.
sources.md maps every current docs/ page to its destination here: the
source material while drafting, the redirect list at cutover. Cutover steps are in that
file's Cutover section.
Open TODOs
-
Not started: the Architecture pages (
reference/architecture/index.md,workflow-runs.md,design-decisions.md). All three are headings only, so we hid the group from the site on 2026-08-21 (folder entry commented out inwebsite/docs.sync.config.mjs). The files stay on disk with a WIP comment. Published pages that link to them (reference/glossary.mdto the Overview,customize/project-config.mdto Workflow runs) fall back to the GitHub source until the group is re-listed. -
Not started: the Help and Legacy pages (
help/faq.md,help/troubleshooting.md,help/legacy/migration.md). FAQ has one answer and the other two are headings only, so we hid both sections from the site on 2026-08-21 (commented out inwebsite/docs.sync.config.mjs, same mechanism as Guides). The files stay on disk with a WIP comment. Published pages that link to them (start/setup.mdto FAQ,reference/glossary.mdto Migration) fall back to the GitHub source until the sections are re-listed. -
Not started:
start/overview.mdis empty on purpose. We cleared the skeleton (headings, narrative beats, diagram gallery) on 2026-08-21 to rewrite the landing page from scratch. The old pitch ("a shared, reviewable plan before code is written") undersells OpenSpec now that plan mode is everywhere; the rewrite should sell keeping larger features on track and aligned (teams, git-native, intended vs implemented behavior, control-loop framing). Brief inNotes.md("Start > Overview"); the diagram candidates went with the gallery and live in git history. Until the rewrite lands the page is off the site: its entry is commented out inwebsite/docs.sync.config.mjsand/docsredirects to Installation (website/public/_redirectsplus a fallback in the docs page route). Restoring it is one uncomment plus removing the two redirects. The Teach-once rule still applies: the loop appears here as pitch only. -
Product feedback, not a docs task: spec-driven's design
instructionlists six sections (including Migration Plan and Open Questions) butschemas/spec-driven/templates/design.mdcarries only four headers. The docs show both verbatim; the mismatch belongs upstream. Noted 2026-08-14 while consolidating the spec-driven page. -
Product feedback, not a docs task:
openspec store setup --remotewrites the URL intostore.yamlbut never configures a gitorigin, so "setup --remote, thengit push -u origin main" fails as written; the Stores page showsgit remote addinstead. The pasteable missing-store fix inopenspec doctoris powered byreferences:remotes, notstore.yaml. Noted 2026-08-21 while porting the Stores page. -
Style guide follow-up (
.agents/skills/write-openspec-docs/writing.md), from the Stores page's review rounds, 2026-08-21: never use a term the page hasn't shown (say "thestore:line", not "the pointer"; define by showing the artifact first); when behavior depends on the reader's starting state, enumerate the states and walk each to its outcome; sentence subjects are you, OpenSpec, or your agent, never an implementation unit ("the resolver picks") or a class of things ("store-only projects make..."); when a defined term is reused a section later, re-gloss it in one parenthetical at the point of use. -
Fence convention follow-up, 2026-08-21: the Stores page puts commands in
bashfences with a one-line#comment and OpenSpec output in a separateyamlfence.customize/schemas.mdstill usesconsolefences with$prompts (lines 78, 114, 137, 145; prompts at 24 and 115); the style guide should name the convention and that page should adopt it. -
Monorepo: message-map row 37 is still a Gap. "Packages treated as separate repos" may land on the Stores page later; not part of the current page.
-
reference/cli.mdis fully drafted: the command table plus one section per real command, facts captured from working-tree runs (2026-08-11). Thedeliverykey that start/setup.md's "Skills, commands, or both" section sets appears there only as command output; its field-level home,reference/configuration/config-json.md, is drafted (2026-08-14). -
Telemetry is undocumented.
OPENSPEC_TELEMETRY=0appears nowhere in the tree; the Deno install command grants--allow-net=edge.openspec.devwith no explanation (the telemetry gloss was deliberately pulled pending a real home). The home now exists: writereference/configuration/environment-variables.md(the env var, what's collected, the opt-out, the CI auto-disable), then have the Deno section link to it to explain the flag. Noted 2026-08-07; home settled 2026-08-10. -
Product feedback, not a docs task: init doesn't say when the global profile changed what it wrote. A machine with
profile: customsilently installs a different workflow set than a stock machine, and nothing in the init output names the profile that shaped it. Noted 2026-08-05 while verifying installation.md; track upstream, don't paper over in prose. -
Product feedback, not a docs task: drop the sync-specs skill from the default set; its job reads as reference content, not a workflow, and it pads the skill list every reader scans. Noted 2026-08-08 while writing start/setup.md's workflow tree.
-
Product feedback, not a docs task: make the shared
.agents/folder the default install target for every tool, with tool-specific folders (.claude/, ...) the exception. The docs already prefer.agents/in examples; the product should match. Noted 2026-08-08. -
help/troubleshooting.md's skeleton has no section for install-time failures (command not found, wrong Node version, PATH). Olddocs/troubleshooting.mdcovered them;start/installation.mdcarries caveats inline but there is no symptom-to-fix home. Add a section or an installation.md anchor. Noted 2026-08-10 during the old-docs message audit. -
Missing guide: the iterative flow. new/continue/fast-forward have no owner for the judgment: what the flow is, when to pick it over propose, and ff vs continue. Old
docs/workflows.mdcovered it (Two Modes, When to Use What);sources.mdroutes that page's mechanics toguides/apply.mdand contracts toreference/skills.md, so the choice itself landed nowhere. Likely a Guides › Using page slotted between Explore and Review the plan, with a pointer tocustomize/profiles.md(the skills are optional workflows outside the core set). Salvage only the ff-vs-continue rule of thumb; the rest of workflows.md is unverified. Noted 2026-08-11. Related: message-map row 29 words apply.md's pacing question as drafting-time pacing, the same creation-stage choice; fix that row's wording or owner when this guide lands. Noted 2026-08-14. -
Missing guide: working with git. OpenSpec never touches git, so every git decision lands on the reader with no page to answer it: do you branch before or after propose, does a task get its own commit, what goes in the PR, where does the archive commit land.
guides/teams.mdowns the archive-vs-PR ordering; the rest is unowned. Likely aguides/file in the Adoption group. Noted 2026-08-08. -
customize/skills.mdis parked: the skeleton stays on disk but is out of the page index, the sidebar, and the sync config. Editing installed skill prompts has no good answer yet (openspec updateoverwrites edits); the message map keeps the question as a Gap. Revive when the product has a real story for surviving updates. Parked 2026-08-14. -
guides/examples.mdis parked: the skeleton stays on disk but is out of the page index, the sidebar, and the sync config. Contrived examples teach the wrong lesson for this product; revive the page when real archived changes from actual usage can fill it. The content plan (weak-vs-reviewed pairs, archived-changes gallery) is in the file's comment. Parked 2026-08-11. -
Product feedback, not a docs task: "expanded" survives in product strings and the update workflow is unlabeled in the picker. The only stored profile values are core and custom, but
src/core/templates/workflows/update-change.tssays "expanded-profile workflow", andWORKFLOW_PROMPT_META(src/commands/config.ts) has noupdateentry, so theopenspec configworkflow picker renders a core workflow as rawupdate/ "Workflow: update". Docs standardized on core/custom with "expand the set" as a verb (2026-08-12). Noted 2026-08-12 during the glossary product sweep. -
Product feedback, not a docs task: converge on skills only, soon. A workflow's skill and command are the same instructions, Claude Code has already merged commands into skills upstream, and setup spends a whole subsection explaining why two forms exist. Every page gets simpler when commands go. Noted 2026-08-08 while writing start/setup.md.
-
Website QOL backlog, site build not prose: i18n; AI search layered on the stock keyword search (an ask-the-docs answer box, not just matching); proper light/dark themes that carry the Survey palette (DESIGN.md tokens) into both modes instead of a stock dark theme. Candidates to bundle in the same pass:
llms.txtplus a per-page "copy as Markdown" button so agents can ingest pages, copy buttons on code blocks, and "edit this page on GitHub" links. Noted 2026-08-11.