Files
OpenSpec/docs-lab
Clay GoodandClaude Opus 5 9827762d2d fix(skills): stop workflows from adopting a project that never ran init (#1787)
* 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>
2026-09-16 23:05:28 +00:00
..
…
…

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.md teaches it as UX: how a human moves a change through the lifecycle, including what archive does on disk.
  • start/overview.md shows it as pitch: copy only, no explanation.
  • guides/concepts.md stays 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.md owns install; start/setup.md owns 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 in website/docs.sync.config.mjs). The files stay on disk with a WIP comment. Published pages that link to them (reference/glossary.md to the Overview, customize/project-config.md to 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 in website/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.md to FAQ, reference/glossary.md to Migration) fall back to the GitHub source until the sections are re-listed.

  • Not started: start/overview.md is 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 in Notes.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 in website/docs.sync.config.mjs and /docs redirects to Installation (website/public/_redirects plus 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 instruction lists six sections (including Migration Plan and Open Questions) but schemas/spec-driven/templates/design.md carries 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 --remote writes the URL into store.yaml but never configures a git origin, so "setup --remote, then git push -u origin main" fails as written; the Stores page shows git remote add instead. The pasteable missing-store fix in openspec doctor is powered by references: remotes, not store.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 "the store: 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 bash fences with a one-line # comment and OpenSpec output in a separate yaml fence. customize/schemas.md still uses console fences 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.md is fully drafted: the command table plus one section per real command, facts captured from working-tree runs (2026-08-11). The delivery key 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=0 appears nowhere in the tree; the Deno install command grants --allow-net=edge.openspec.dev with no explanation (the telemetry gloss was deliberately pulled pending a real home). The home now exists: write reference/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: custom silently 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). Old docs/troubleshooting.md covered them; start/installation.md carries 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.md covered it (Two Modes, When to Use What); sources.md routes that page's mechanics to guides/apply.md and contracts to reference/skills.md, so the choice itself landed nowhere. Likely a Guides › Using page slotted between Explore and Review the plan, with a pointer to customize/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.md owns the archive-vs-PR ordering; the rest is unowned. Likely a guides/ file in the Adoption group. Noted 2026-08-08.

  • customize/skills.md is 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 update overwrites 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.md is 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.ts says "expanded-profile workflow", and WORKFLOW_PROMPT_META (src/commands/config.ts) has no update entry, so the openspec config workflow picker renders a core workflow as raw update / "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.txt plus 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.