* docs: add beginner workflow guide
* docs(start): guide the first change in a new project
* docs(start): make greenfield guidance tool-neutral
* docs(start): use portable first-change prompts
---------
Co-authored-by: Clay Good <hi@claygood.com>
* feat(skills): match natural "openspec <verb>" phrasing to its workflow
Users and agents say "openspec propose" / "openspec apply", but no workflow
skill description contained that phrasing, so an agent hearing it had nothing
to match and routinely hand-built the artifacts with the CLI instead of
running the workflow.
Each workflow skill's description now names the phrasings that should route
to it. `openspec update` is deliberately left unclaimed: it is a real CLI
command that refreshes generated files, unrelated to the update-change
workflow, so that skill claims "openspec update change" instead.
Descriptions are emitted as unquoted YAML plain scalars, so the new tests also
pin that the generated frontmatter still parses and the description round-trips.
Closes#1221
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(skills): derive the CLI-collision guard instead of hardcoding it
Review found the guard codified the one exception rather than the rule, so it
could never catch the next collision. It now reads every command name the CLI
registers and fails on any claimed phrase that shadows one, unless the phrase
is listed in DELIBERATE_CLI_PHRASE_CLAIMS with a reason.
Two routing fixes fall out of stating the rule:
- bulk-archive also claims "openspec archive all", so an exact-phrase match on
"openspec archive" no longer pulls a multi-change request to the
single-change skill.
- update-change now disclaims the openspec update CLI command in prose, not
only by avoiding the string.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(skills): drop the trailing clause and close the plural-archive hole
Review found the "- follow this skill rather than doing the work by hand"
trailer was decoration that contradicted two of the skills it was appended to:
sync-specs opens "This is an agent-driven operation - you will read delta specs
and directly edit main specs", and explore says "This is a stance, not a
workflow. There are no fixed steps." A description is read at selection time,
so the clause could not reach the hand-building it targeted anyway; the bodies
already carry that guidance. Removing it from all 12 also drops ~800 chars of
identical boilerplate that made update-change's CLI redirect read as filler.
Routing fixes:
- bulk-archive claims the plural phrasings that do not contain "all", so
"openspec archive these three changes" no longer loses to the single-change
skill on the bare literal.
- update-change redirects to the CLI command positively instead of negating
("run that command instead"), which routers honor far better than "not for".
- apply also claims "openspec implement", the natural English verb for it,
which shadows no CLI command.
Corrects the recorded reason for claiming "openspec archive": the CLI command
does merge delta specs (docs/cli.md:631, src/core/archive.ts:1402). The real
reason is that the workflow confirms and verifies the merge before anything
moves, where the bare command does it in one shot.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(quickstart): name the verb phrasing that now routes to a workflow
Also rewrites the changeset to house style: links the issue, names the
commands-only scope limit, and tells a reader they need `openspec update`
to pick it up.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(skills): walk the real command tree instead of scanning the entrypoint
Mutation testing found the collision guard was a strict subset of reality,
not the superset its comment claimed. It scanned src/cli/index.ts for
`.command('…')`, but seven groups — spec, config, schema, store, doctor,
context, workset — are registered from their own modules, so 23 real command
names were invisible. A description claiming "openspec doctor" or
"openspec spec" passed 18/18 green.
It now walks the commander tree from the exported `program` (importing it does
not parse argv; runCli does that), and a sanity test pins the seven delegated
groups so the blind spot cannot come back.
Three more holes the same pass found, all confirmed by re-running the
mutations that previously slipped through:
- phrase extraction was case-sensitive and double-quote-only, so
"Openspec update" and `openspec update` in backticks both evaded every
guard. Matching is now case-insensitive and accepts either delimiter.
Unquoted prose stays excluded on purpose: the update-change redirect names
the CLI command in prose, and prose is not a routing trigger.
- prefix shadowing was unguarded, which is the exact shape of the
archive/bulk-archive tension. A shorter phrase contained in another skill's
longer phrase must now be declared in DELIBERATE_PHRASE_SHADOWING.
- both allowlists accepted an empty reason and never flagged stale entries.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(skills): let the CLI-collision guard ignore hidden workflow-verb hints
PR #1776 registers the workflow verbs (explore, propose, apply, ...) as
hidden CLI commands that only point the user at the workflow. Walking the
commander tree then saw "openspec explore" as a real command and failed
the collision guard for every skill trigger.
Skip a subcommand only when it is hidden AND named after a workflow.
Visible commands and hidden non-workflow commands are still guarded,
pinned by a synthetic commander tree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(changeset): drop em dashes from the release note
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(skills): drop the generic by-hand clause from the explore description
The other eleven descriptions dropped it; explore is a stance, not a
workflow, so telling the agent to follow it instead of doing the work
contradicts it. Adds a regression over every workflow description.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* fix(nix): install shell completions with the flake package
The flake exposed `openspec completion generate SHELL` but installed no
completion scripts, so a Nix install had no completions at the standard
locations. Generate the Bash, Fish, and Zsh scripts during postInstall and
place them with installShellCompletion.
Closes#1740
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: note that cross-compiled Nix builds omit completions
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* ci: run the Nix job when the completion generator changes
The Nix build now runs `openspec completion generate`, so a change under
src/core/completions can break packaging without touching flake.nix.
Also drops the cross-compilation caveat from the Nix install docs: every
package this flake exposes is native (`legacyPackages.<system>` has
buildPlatform == hostPlatform), so `canExecute` is always true and the
completions are never omitted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs: move the Nix completions note to the canonical pages
docs-lab/README.md makes docs-lab/ canonical and the old docs/ tree legacy, so
the fact is recorded in the two docs-lab pages that own it and docs/cli.md and
docs/installation.md are back to their state on main.
- docs-lab/start/installation.md, Nix: the package ships the scripts at the
standard locations, so completion install is not needed.
- docs-lab/reference/cli.md, openspec completion: the same exception, stated
where a reader looking up the command will hit it, linking to the Nix
section rather than repeating the paths.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* docs(explore): stop claiming explore never writes files
Sixteen lines across both documentation trees told users that
`/opsx:explore` creates no artifacts and writes no files, full stop.
That has been false since explore shipped (#467): its capture branch
writes the planning artifacts the user asked for, and can edit an
existing change's artifacts. #1503 later made it scaffold with
`openspec new change` first, closing #668 and #720.
The claim appeared in two shapes. Six lines denied the capability
outright ("Explore creates no artifacts and writes no code"). Ten more
said the same thing as a timing claim ("before any artifact exists"),
which reads as ordinary pitch copy and is what escaped the first pass.
Every site now carries one guarantee, worded the same way: explore
never writes code, and writes nothing else unless you ask, or say yes
when it offers. Four sites described only the user-initiated trigger,
which left the offer path - the one a reader actually hits - looking
like it did not exist.
docs/explore.md and docs/commands.md also gain a positive description
of capture where the denial used to sit, including what scaffolding
creates beyond the artifacts you named, and how capture differs from
handing off to propose (propose writes the set your schema requires;
capture writes only what you named).
Closes#1833
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(docs): keep the retired explore wording retired
A flat list of the phrasings that actually carried the claim, swept
over the eleven pages that pitch explore. Fails on main with all
sixteen offenders; clean on this branch.
Modeled on test/vocabulary-sweep.test.ts, and deliberately a list
rather than a grammar. An earlier draft built the grammar - section
splitting, code-fence tracking, a conditional-marker exemption so
"creates no artifacts unless you ask" would pass - and measured
against realistic prose it was imprecise in both directions while
returning the same verdict on the real input. The list has no
exemption logic to get wrong, and any maintainer can extend it.
Phrasings that are only wrong in the absolute ("writes nothing",
"creates nothing") are left to review, since the conditional form of
each is the wording the failure message recommends.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(docs): pin the explore capture contract, not the word
The guide check matched any "capture", so "explore automatically captures
every artifact" passed. Both explore.md and commands.md now must name the
user trigger, `openspec new change`, and the named-artifacts scope, with no
capture line claiming it happens unprompted, and keep "never writes code".
Also scope the explore.md guarantee to the setup files a new change needs,
and make the commands.md offer name the change and its scope, which the
template asks for on main and after #1832.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(docs): accept negated unprompted-capture wording, catch non-capture verbs
The unprompted check matched "automatically" on any capture line, so the
correct "Explore does not automatically capture artifacts" failed, while
"Explore automatically writes planning artifacts" was never scanned because
it lacks the word capture. Check each clause of lines naming explore or
capture for an unprompted write verb with no preceding negation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* docs: rebuild docs site from docs-lab
Replace the docs site's source tree with docs-lab, a page-by-page rebuild
of the OpenSpec docs (40 pages: Start / Guides / Customize / Multi-repo /
Reference / Help).
- Point website/docs.sync.config.mjs at ../docs-lab and restructure the
sidebar into nested groups; sync script gains nested meta.json emission,
leading-quote descriptions, idempotent writes, and diagram asset copying
- Remove the marketing landing page; / now redirects to /docs
(meta-refresh page + Cloudflare _redirects)
- Add remark plugins (faq, file-steps, gfm-alert) and the FileSteps
component backing the new page formats
- Add install.md at the repo root, curled by docs-lab/start/installation.md
as an agent-executable install prompt
- Add the docs authoring skills (.agents/skills/{write,draft,verify}-
openspec-docs); docs-lab/README.md links into write-openspec-docs
The old docs/ tree is now unused by the site and left for a follow-up.
Claude-Session: https://claude.ai/code/session_01BMMLYNJQPKXx1QHpnDn4ho
* docs: hold back unwritten pages, add worksets, drop diagram drafts
- website: comment out Overview, Guides, Architecture, Help, Legacy in
docs.sync.config.mjs until those pages are written; temporary
/docs -> /docs/installation redirect (Cloudflare _redirects + static
export meta-refresh fallback in page.tsx)
- docs-lab: new multi-repo/worksets.md page, published under Multi-repo
- docs-lab: content revisions across start/, customize/, reference/,
help/, multi-repo/; add review notes (Notes.md)
- remove docs-lab/diagrams option-* drafts and their website copies
- write-openspec-docs skill: add spoken-flow sentence rule
* docs: address review on PR #1649
- sync-docs: read the existing output directly instead of exists-then-read
(CodeQL TOCTOU alert)
- hold back the headings-only Environment variables and Stores reference
pages until written; links to them fall back to their GitHub source
- sources.md: cutover keeps docs/ in place and points at public/_redirects
- setup.md: label the workflow tree as the default set plus two optional ones
* docs: two review nits (spoken-flow rule, XDG_DATA_HOME note)