mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-13 05:09:32 +08:00
* 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>