Update dependency npm:@fission-ai/openspec to v1.13.2 - #56
Open
renovate[bot] wants to merge 1 commit into
Open
renovate[bot] wants to merge 1 commit into
renovate[bot] wants to merge 1 commit into
Conversation
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
1.5.0→1.13.2Release Notes
Fission-AI/OpenSpec (npm:@fission-ai/openspec)
v1.13.2Compare Source
Patch Changes
#1940
0b5ce44Thanks @clay-good! - Keep fast-forward clarification guidance and onboarding task approval consistent across generated skills and commands. Fast-forward now asks only when context is critically unclear, while onboarding asks users to approve the task breakdown before saving it and separately asks whether to begin implementation.#1926
f2812f6Thanks @kevin9327! - ### Bug FixesArchive — When Windows
EPERMblocks renaming a change directory that still has children, copy from the original source instead of requiring a staging rename that fails the same way. That lets archive finish instead of rolling back the spec write and leaving an empty capability directory git cannot see. A staging failure that is notEPERM/EXDEVstill leaves the source untouched.The source of that unstaged copy is still the live change directory, which the archive claim does not cover, so cleanup removes only the entries it copied and verified rather than whatever is present when it runs. A file written in that window is left alone and the complete destination is retained for recovery, instead of being deleted without ever reaching the archive.
An edit to a file that was already verified is covered too. Cleanup claims each entry with an atomic rename before reading it, then compares what it claimed against the copy. A rewrite that lands first is caught by that comparison and the file is put back; one that lands after creates a new file at the original path, which is never deleted. Either way the newer bytes stay on disk and archive reports the move as incomplete rather than succeeding with the older copy.
Rollback of a newly created spec now also prunes the capability directory it created — and only that one. An empty capability directory that was already there is left in place with its own permissions.
#1795
fb1b876Thanks @runsonmypc! - Archive workflows now use schema-aware task progress fromopenspec list --json, so custom task files and globs still trigger incomplete-task warnings.#1885
fd56e12Thanks @philo-x! - Fix artifact output resolution to recognize brace expansion and extglob patterns while preserving literal output filenames and confining brace-expanded paths to the change directory.#1964
7ac58dcThanks @clay-good! - Continue commands now open with an instruction to follow the active OpenSpec workflow directly, so local models no longer try to call a tool named after it (#1944).#1964
7ac58dcThanks @clay-good! - Generate Kilo Code commands in.kilo/command/, the directory Kilo Code reads, instead of.kilocode/workflows/(#1938).openspec initand legacy cleanup remove the workflow files OpenSpec generated there, matched by their known file names (including copies you edited), and leave files with other names in place.#1958
1d35e90Thanks @clay-good! - Preserve a file's existing line endings when rewriting it, so Windows users no longer get whole-file diffs. Applying a delta to a CRLF spec (the default on a Windows checkout withcore.autocrlf=true) rewrote the file to LF, turning a one-requirement change into a diff that touched every line.openspec archivenow writes the spec back with the convention it already used; a spec that does not exist yet is still written with LF.The same fix covers marker-managed files: installing or updating shell completions in a CRLF
.bashrcor.zshrcno longer leaves the file with mixed endings, whichbashreports as$'\r': command not found.Removing a managed block is fixed the same way: the blank-line collapse in
removeMarkerBlockrebuilt its separator as a bare LF, so cleaning up legacy artifacts left a lone LF inside an otherwise-CRLFCLAUDE.mdor rc file. Both write paths now read the file the same way, by dominant ending, so one stray CRLF in an otherwise-LF file no longer pulls the whole rewrite to CRLF.scripts/pack-version-check.mjsnow spawnsnpmthroughcross-spawn, so the release guard can run on Windows, wherenpmisnpm.cmdand cannot be resolved byexecFile.#1912
8826c0cThanks @Tyagiquamar! - Fixvalidate --strictreportingPURPOSE_IS_PLACEHOLDERfor a Purpose that opens with the ordinary word "Todo" followed by prose, as in Spanish ("Todo el…") and Portuguese ("Todo o…") specs (#1897).TBD/TODOin capitals is still a placeholder marker whatever follows it, soTODO write this lateris still reported.todo -,tbd.), so an authored Spanish or Portuguese sentence is not reported.#1744
5b55263Thanks @javigomez! - Clarify the Codex setup hint for CLI, IDE, and desktop app users.#1809
a5ceea3Thanks @ryandemelo! - Say what aMODIFIEDblock adds when the scenario-loss guard fires (#1809).openspec validateandopenspec archivealready named the scenarios a block omits. They now also print how many scenarios each side has and which ones the block introduces, capped at three names, so a rename and a truncation read differently without opening either file. The guard catches exactly what it did before, and no exit code changes.#1731
d6bdef6Thanks @runsonmypc! - Stop workflows from displaying schema names thatopenspec list --jsondoes not return. Update and continue no longer fabricate aspec-drivenpicker label, while bulk archive and explore describe only the change fields the list command actually provides.#1955
ed5d386Thanks @clay-good! - Task guidance now requires each task group to land its own tests and documentation updates instead of deferring them to a trailing group. The onboarding walkthrough teaches the same rule, and the published schema reference no longer quotes stale instruction text.#1939
a64303fThanks @clay-good! - Return a nonzero exit status whenopenspec update --forcecannot replace a legacy-only Codex installation.#1733
72fbe4cThanks @runsonmypc! - Let/opsx:updatefill a missing file under an already-satisfied glob artifact. A glob artifact is complete once one file matches it, and/opsx:continueonly picks upreadyartifacts, so the previous "point the user to/opsx:continue" handoff was unreachable and the missing file could never be created through the documented flow.#1962
3364146Thanks @ryandemelo! - Stop/opsx:verifyfrom reporting a correctly removed requirement as missing. Verify now reads which delta section each requirement sits under: ADDED and MODIFIED requirements are checked for an implementation as before, a REMOVED requirement passes once its behavior is gone and is flagged only while it is still present, and the old name of a RENAMED requirement is no longer reported as missing.#1732
072de6bThanks @runsonmypc! - Stop/opsx:verifyfrom reporting skipped checks as passing. Task completion now uses the schema-awaretasksandprogressfields returned by apply instructions, while absent spec or design inputs are mapped to every check they prevent. Apply instructions aggregate every file matched by the configured task path or glob, regardless of the tracked artifact ID. Verification stays advisory and does not require optional or intentionally omitted artifacts. The scorecard identifies each skipped check, and the final assessment does not claim archive readiness when any check did not run.#1769
d3d7707Thanks @kikeprzn! - Fixopenspec archiveleaving.openspec-archive.lockbehind on Windows. Node can reportdev: 0nfrom a path stat while the open file handle reports the real volume id, so the claim-ownership check never matched and the stale lock blocked every later archive. The check now treats an absent device id as unavailable while still requiring the inode and the claim's contents to match before unlinking.v1.13.1Compare Source
Patch Changes
#1864
767d63cThanks @dwin-gharibi! - Stop archive adding a second copy of an existing requirement under a name that differs only in case or spacing. ADDED and the RENAMED target compared requirement names exactly, while REMOVED and the RENAMED source already treated a case or whitespace variant as a mistyped header, so an ADDEDlate feesbeside an existingLate Fees, or a rename toLATE FEES, archived cleanly and left two contradicting requirements in the main spec, whichvalidatethen accepted. Both now refuse with an error naming the existing requirement, in the same form REMOVED already used. The exact-duplicate error is unchanged, a case-only rename of a requirement to its own name still works, and a variant of a requirement the same delta removes or renames away is still allowed, because ADDED is checked against the spec as it stands after the earlier operations, as the exact check already was.#1872
72bf760Thanks @dwin-gharibi! - Makeopenspec completion uninstall bashhand.bashrcback exactly ascompletion install bashfound it. Install adds the OpenSpec block at the top of the file followed by a blank separator line; uninstall removed the block but kept that blank line at the top, then stripped every trailing blank line and wrote the file back without its final newline. The byte count happened to come out unchanged, but the next tool to append to.bashrcwith>>(the nvm, conda and rustup installers all do) merged its first line into the user's last line and broke both. Uninstall now also drops the separator line install added when the block sits at the top of the file, and leaves the rest untouched: the final newline, trailing blank lines and CRLF line endings all survive the round trip. A block the user moved elsewhere in the file is still removed, and the zsh, fish and PowerShell installers are unchanged.#1829
e67ac47Thanks @choi138! - Fix bulk archive nesting a change inside an existing archive target. The workflow now checks every archive target before it writes any main spec, the same orderopenspec archiveuses. A change whose target already exists, or that shares a target with another selected change, is reported as failed and is never synced or moved, while the rest of the batch continues. The check runs again just before each move.#1878
2ef6fbdThanks @dwin-gharibi! - Letopenspec config editrun anEDITORorVISUALthat carries arguments. The whole value was passed tospawnas the program name, so common settings such ascode --wait,subl -woremacsclient -tfailed withspawn code --wait ENOENT, and because that error was never caught the command died with a raw Node stack trace. The value is now split into a program and its arguments, honoring quoted paths with spaces, and the config path is appended as its own argument. No shell is involved, so shell metacharacters in the value are passed through literally. On Windows,.cmdshims such ascode.cmdare found. A value that is itself the absolute path of an existing file is still run as-is, so an unquoted editor path containing spaces keeps working. An editor that cannot be started, exits non-zero or is killed is now reported as a one-line error naming the editor, with an install hint when the program was not found, and the command exits 1 instead of throwing.EDITORstill takes precedence overVISUAL, and the file is still validated after the editor closes.#1773
11a9691Thanks @clay-good! - Stop dropping checkbox lines whose marker the task parser does not recognise. Atasks.mdwhose remaining work used a marker other than[ ]/[x]/[X], for example- [~] 1.2 Deferred, reported✓ Completeinopenspec list/statusand archived with no incomplete-task warning, because unmatched lines counted toward neither the numerator nor the denominator. An empty[]and a padded[ x]were lost the same way. Only a box holdingxorXmeans done (spacing inside the brackets is ignored, so[ x]is done), and every other marker now reads as unfinished, across progress, the apply task list, archive's gate and validate's task-numbering check. The archive, bulk-archive and verify workflows now tell agents the same rule, so a hand-counted tally cannot disagree with the CLI, and thetasksinstruction in thespec-drivenschema states it where agents author the file. Markdown link bullets stay out of the count:- [Some doc](./doc.md)and the one-character- [A](https://example.com)are not tasks.#1701
92fb72dThanks @clay-good! - Agent-driven archive and sync workflows now create a missing main spec fromADDEDrequirements instead of treating it as already synced. They block sync rather than inventingMODIFIEDorRENAMEDrequirements or writing an empty spec for aREMOVED-only delta, while preserving the user's explicit choice to archive without syncing. A REMOVED-only delta withretire_capabilities: trueremains already synced when its main spec is gone. Fixes #1222 and #1264.#1804
a5bf5c6Thanks @dwin-gharibi! - Say so when a requirement in a delta sits outside every delta section. A well-formed### Requirement:block written under## Notes, under a misspelled header such as## Add Requirements, or above the first##header was dropped with no diagnostic:openspec validatereported the change valid andopenspec archiveexited 0 without applying it.openspec validatenow reports each one as a WARNING naming the section and line, and archive prints the same warning. Nothing else changes: the block is still not applied, the verdict stays valid outside--strict, and requirements shown inside a code fence are not reported. Fixes #1803.#1832
4c369e0Thanks @clay-good! - Resolve the contradiction that left explore mode's capture branch without a governing rule. Explore states twice that the agent must ask a direct yes/no question and wait for confirmation in a separate user message before its first write-capable action, namingopenspec new changeas an example, while the capture branch tells the agent to transition "seamlessly" into runningopenspec new changeand creating artifacts with no confirmation step. Both readings were defensible from the text, so the same "capture this as a change" request either wrote.openspec.yamlplus several artifacts immediately or stopped and asked, depending on which passage the agent weighed, which made the #1715 guarantee unenforceable in the one explore path that writes files. An explicit capture request is now stated to be that confirmation, covering the change and the artifacts the request names and nothing else. The guardrail keeps its teeth for the case #1715 actually reported: when the agent is the one proposing the capture, or when the work would go beyond the requested scope, it still asks first, and answers to design or clarifying questions are still never consent to write. Both explore delivery surfaces and the committed skill carry the same wording. Fixes #1828.#1788
62106f4Thanks @clay-good! - Name the workflow where explore hands off. Explore mode refuses to implement, but every place it said what to do instead described the next step as prose ("create a change proposal") without naming the workflow that does it: the refusal itself, the "flow into a proposal" ending, the closing summary, and the do-not-implement guardrail. Its seamless capture path was worse: it scaffolded a change, wrote artifacts, and then said nothing at all about what came next. With no named exit, agents finished the discovery questions and started writing code, which is the failure reported through GitHub Copilot in #869, and which the docs already promised would not happen ("when the picture is clear, it hands off to/opsx:propose").The explore skill and command now name
/opsx:proposeat all four prose handoffs, and the capture path ends by naming/opsx:proposefor the remaining planning artifacts and/opsx:applyfor implementation, with an explicit note that capturing artifacts is not permission to implement them. The references are written in the canonical/opsx:<id>form so each tool renders the invocation it actually registers (/openspec-proposefor skills-only delivery,/opsx-propose,/opsx:propose, or@opsx-proposefor command surfaces). The handoffs follow the installed workflow set: a custom profile withoutproposeorapplygets explore's own capture path and theopenspec instructions applyCLI instead of a command it never installed. Fixes #869.#1787
9827762Thanks @clay-good! - ### Bug Fixesopenspec init. Every workflow now checksrootfromopenspec list --jsonbefore its first write, and"root": nullmeans the project is not set up. What happens next depends on how the workflow was reached. A skill the agent picked on its own drops OpenSpec and answers the request normally, without asking about setup. A workflow the user asked for by name, or ran as a slash command, stops and asks whether to initialize the project, target a store, or handle the request without OpenSpec. A project whoseopenspec/config.yamlnames a store this machine cannot resolve (not registered, or a malformedstore:line) is not mistaken for an uninitialized one: the workflow stops and shows the store error. Neither path letsopenspec new changecreateopenspec/in the current directory as a side effect. Skill descriptions now name OpenSpec so hosts stop offering these workflows in unrelated repositories.openspec new changealso says when it had to create the root itself, so a directory that was never set up no longer picks up anopenspec/directory in silence (human output only;--jsonis unchanged).#1902
eb03b9eThanks @clay-good! - Harden the CLI against repositories you have cloned but not yet read (#1835).config.yamlvalue can no longer close the project context block and inject its own directives into the instructions an agent receives.openspec updateoropenspec archivewith catastrophic regex backtracking..npmrccan no longer point the update check at a cleartext or attacker-controlled registry; a rejected registry now disables the check instead of falling back.openspec updatenow notices a generatedSKILL.mdthat was edited by hand and restores it, instead of reporting every tool as up to date.DO_NOT_TRACK=trueand other common spellings of an opt-out now turn telemetry off, and nothing is sent until the first-run notice has been shown.#1874
388d344Thanks @dwin-gharibi! - Stop legacy cleanup deleting the user's own files. The six pre-skills tools that kept their commands in a<tool>/commands/openspec/folder (Claude Code, CodeBuddy, Qoder, Lingma, Crush and Gemini CLI) had that whole folder removed recursively whenever it existed, so a command the user kept there, such as a team review checklist, was deleted along with OpenSpec's files, and the summary named only the folder. Becauseopenspec initcleans up automatically when there is no TTY, an agent or CI running plainopenspec initdid this without--forceand without a prompt, andopenspec update --forcedid the same. Cleanup now deletes only the files OpenSpec wrote there:proposal,applyandarchivefiles that still carry the OpenSpec markers every legacy command was generated with, so a same-named file the user wrote is kept. It never follows a symlinked command folder, removes the folder only once nothing else is left in it, and lists each thing it kept. A folder holding nothing OpenSpec wrote is no longer reported as legacy at all. A folder holding only OpenSpec's files, or nothing, is still removed exactly as before, with the same summary line.#1866
8146be5Thanks @dwin-gharibi! - Stop one unresolvable file from breakingopenspec list. To sort changes by recency,liststats every file inside each change, and any entry it could not stat failed the whole command: a dangling symlink, such as the.#tasks.mdlock Emacs keeps beside every file with unsaved edits, or a symlink loop madelistexit 1 andlist --jsonreport"changes": [], so agents discovering work through it saw no changes at all. An entry that no longer resolves (removed mid-walk, a dangling symlink, or a loop) is now skipped when computing a change's last-modified time. Valid symlinks are dated as before, and any other error, such as a permission failure, still fails the listing.#1849
09a999bThanks @clay-good! - Report a change directory nested in a namespace folder instead of silently listing the folder around it as a change. Specs can be nested by domain (specs/mobile/tutorial-videos/spec.md), so it looks reasonable to lay changes out the same way, but a change is only ever a directory directly underchanges/:changes/mobile/refresh-token/left the real change invisible whilemobilewas reported as a task-less change everywhere.openspec archive mobilethen moved the unfinished change into the archive under the namespace's name and applied none of its deltas.openspec listnow marks the foldernot a changeand names the nested directories and a flat alternative,openspec show,openspec status --changeandopenspec status --allsay the same instead of reporting a missing proposal or a full artifact plan,openspec validatereports it instead of "must have at least one delta",openspec list --jsoncarries awarningsentry, andopenspec archiverefuses the folder outright. Detection looks up to three directory levels belowchanges/, which covers every namespace layout seen in practice; a change buried deeper than that behaves as it did before. Fixes #1846.#1902
eb03b9eThanks @clay-good! - Install shell completions with the Nix flake package (#1785). The package now ships bash, zsh and fish completions in their standardshare/locations, so Nix users get tab completion without runningopenspec completion installagainst their home directory.#1775
626269eThanks @clay-good! - Generated skills and commands no longer point at workflows the active profile does not install. On the defaultcoreprofile, the update workflow told agents to hand off to/opsx:continuefor missing artifacts and to/opsx:newfor a change of intent, neither of whichcoregenerates. Every cross-workflow handoff is now decided at generation time against the installed workflow set, and renders a concrete CLI fallback (openspec status,openspec instructions,openspec archive) when the workflow it would name is absent, rather than relying on a runtime availability check the agent had to perform. The onboarding tutorial's command tables are likewise built from the workflows you actually have.Also folds in #1735, which fixed the same issue (#1734) by removing the optional handoffs outright. The CLI's own runtime instructions no longer name the
openspec-continue-changeskill either, since those strings are chosen at run time and cannot be resolved against a profile; and the blocked-state fallback now carries the full CLI recovery (select the nextreadyartifact fromopenspec status, read its rules withopenspec instructions, keep the selected--store) rather than a one-line pointer.#1870
e01ed07Thanks @dwin-gharibi! - Stop archiving a change whose delta was written somewherearchivenever reads.validateandarchiveread a change's deltas only fromspecs/<capability-path>/spec.md, but the spec-driven artifact graph counts any markdown file underspecs/as the specs being written, so a delta atspecs/user-auth.md, or in a second file beside a capability'sspec.md, was reported done bystatusand ready byinstructions applywith no warning, rejected byvalidateonly as "no deltas found", and then archived with exit 0 and nothing merged intoopenspec/specs/. A markdown file that carries delta sections but is not a capability'sspec.mdis now a validation error naming the file and thespec.mdits requirements belong in;archiveruns that validation and refuses the change instead of archiving it unmerged, andinstructions applylists each such file in itswarnings.--no-validatestill archives as before, a change with no spec files still archives, and notes without delta sections underspecs/are not affected.#1806
6e62b1dThanks @dwin-gharibi! - Refuse a## RENAMED Requirementssection whoseFROM:andTO:lines do not pair up, instead of guessing. The reader kept one pending pair and dropped whatever did not fit: aTO:before itsFROM:, aFROM:displaced by a secondFROM:, or a trailingFROM:vanished with no diagnostic. Listing the old names and then the new ones paired the secondFROM:with the firstTO:, soopenspec archiverenamed a requirement the delta never named, under a name written for a different one, and exited 0.openspec validatenow reports each unpaired line as an ERROR with its line number, and archive refuses the change until the pairing is fixed. Well-formed renames, including several consecutive pairs, are unchanged. A change that used to archive with a malformed RENAMED section is now rejected. Fixes #1805.#1860
4b5c07aThanks @dwin-gharibi! - Read a requirement heading written with a CommonMark closing sequence, such as### Requirement: Late Fees ###, as the requirement it renders as. The trailing#run stayed in the name, so a REMOVED written that way looked for "Late Fees ###", missed the requirement, and archive exited 0 with a false "treating it as already removed" warning while the requirement stayed in the spec; a closed MODIFIED or RENAMED heading failed as "not found", and a closed and an open heading of one requirement were not reported as duplicates. Requirement names now drop the closing run wherever they are read, exactly as scenario names already did: only a run preceded by a space or tab counts, so a name such asC#keeps its#. Headings without a closing run are unaffected.#1868
7090e16Thanks @dwin-gharibi! - Reject a schema whoseapply.requiresnames an artifact that does not exist.parseSchemachecked every artifact'srequiresbut neverapply.requires, soopenspec schema validatepassed a one-character typo there, and apply then skipped the unknown id:apply.requires: [desgin]turned the apply gate off and told the agent "Proceed with implementation" with only a proposal written. That is now a schema error, raised wherever the schema is loaded, exactly like an unknown artifactrequires, and it names the bad id and the artifacts the schema declares.openspec schema validatealso warns, without failing, whenapply.tracksisn't exactly equal to some artifact'sgeneratesvalue, because OpenSpec finds the tracked artifact by comparing those two strings and can otherwise not tell which artifact's progress the file belongs to. That covers a typo such astask.mdand alsotracks: tasks/main.mdagainstgenerates: tasks/*.md, where the glob does produce the file but the strings still differ. Apply reads that path as written either way, so schemas that track a hand-written file keep loading and working. Every built-in schema parses as before.#1856
46ff91fThanks @dwin-gharibi! - Makeopenspec show --json --deltas-onlyreport the deltas archive applies.ChangeParser, which backsshow --json, thechange listdelta counts and archive's proposal warnings, read delta specs with its own section lookup instead ofparseDeltaSpec, the reader archive uses, and the two disagreed. A REMOVED written in the bullet form (- `### Requirement: X`) was invisible to it, so it fell back to the proposal's "What Changes" prose and reported an invented MODIFIED while archive deleted the requirement; a repeated section header was read only once; and a RENAMED line written with*or+was dropped. The inspection command OpenSpec's own error text recommends therefore misreported a deletion.ChangeParsernow derives every operation fromparseDeltaSpec, and a change whose delta spec files carry a delta section is described by them alone, so proposal prose is never reported in place of what archive applies. Requirement text and scenarios are read exactly as before, header-form deltas produce the same output, and a change with no delta spec files, or a legacy change whose spec files carry no delta section, still falls back to the "What Changes" bullets.#1786
8b99c07Thanks @clay-good! -openspec statusnow names the command that moves the change forward.The text output reported state and stopped there, so picking a change back up (after a lost session, or on a change you did not start) meant already knowing which command came next. The JSON surface had carried that command all along in
nextSteps; the text surface never printed it.Status now ends with a
Next:line: the next ready artifact'sopenspec instructionscommand while planning is unfinished, andopenspec instructions applyonce every planning artifact exists. It carries--store <id>when the resolved root is a store, and it is built from the same source as the JSONnextStepssentence, so the two surfaces cannot name different commands.#1882
208b5b5Thanks @dwin-gharibi! - Stop a store namedspecsorchangesfrom taking over root selection. Stores are placed at~/openspec/<id>, so a store with one of those ids is itself~/openspec/specsor~/openspec/changes, and that made$HOMElook like a planning root. Every command run anywhere under the home directory then resolved$HOMEas the nearest root: the globaldefaultStorewas never consulted, andnew changewrote into~/openspec/changes, outside any store. Aspecs/orchanges/directory that carries store metadata no longer counts as planning content of the directory above it, so these stores resolve like any other. A real project'sopenspec/specs/andopenspec/changes/are unaffected.#1880
9f8dec5Thanks @dwin-gharibi! - Stopopenspec store removedeleting a store the user did not name. Remove deletes the target's folder recursively, but it checked only the target's own metadata, so any other registered store living inside that folder was deleted with it, uncommitted planning work included, while its registry entry was left pointing at a path that no longer existed. The natural way to get there is a shared store vendored into another as a git submodule, a layoutstore registeraccepts. Remove now refuses when another registration points inside the folder, checked under the same registry lock that commits the removal, and the error names each nested store with theopenspec store unregistercommand to run first. Removing a store whose other registrations are siblings is unchanged, andstore registerstill accepts nested checkouts.#1884
5d22145Thanks @dwin-gharibi! - Letopenspec store setup --no-init-gitcreate a store inside an existing Git repository. Setup refuses a path inside another repository because initializing the store there would nest one repository in another, but it ran that check even with--no-init-git, which creates no repository at all. Users who keep their home directory as a dotfiles repository therefore could not set up a store at the recommended~/openspec/<id>path with any flag. With--no-init-gitthe check is now skipped, and the store never records the enclosing repository's remote. The default setup and an explicit--init-gitstill refuse a path inside another repository.#1862
8fc65b7Thanks @dwin-gharibi! - Count task checkboxes under every CommonMark list marker. The task counter shared bylist,status,view,instructions apply,validate --archivedand archive's incomplete-task check recognized only-and*bullets, so a task written as an ordered item (1. [ ],1) [ ]) or under a+bullet was invisible to all of them: a change with unfinished ordered tasks reported "✓ Complete", andopenspec archivearchived it without its incomplete-task warning. Task lines under+and ordered markers (.or), up to nine digits, as CommonMark allows) now count exactly like-and*ones, including nested sub-tasks, CRLF files and the existing tolerance of a missing space after the marker, and task-numbering checks now see them too. Ordered and+items without a checkbox are still ignored, and-and*tasks count as before.#1777
3312af4Thanks @clay-good! - Start generated proposal, spec, design, and tasks files with a top-level heading, so artifacts are complete markdown documents instead of files whose first line is a section header. Editors that run markdownlint no longer flag every OpenSpec artifact with MD041.openspec schema initscaffolds custom templates the same way.openspec show --jsonandopenspec change list --jsonkeep naming a change by its id when its proposal opens with the template's bare# Proposaltitle.#1778
7de2404Thanks @clay-good! - Make the vendor-neutral tool target findable when your assistant is not on the list.openspec initnow shows it as "Other / Universal (shared .agents skills)"; the picker's search box matches it onuniversal,other,generic,custom,proprietary,unlisted,unsupported,vendor-neutralandagents.md; a search that matches nothing points at it instead of ending at "No matches"; and--tools <unknown>names it in the error. The search box also accepts punctuation, so.agentsandamazon-qfilter instead of silently dropping their.and-.#1876
605d9e7Thanks @dwin-gharibi! - Stop OpenSpec rewriting a global config file it cannot parse. After a hand edit left a typo such as a trailing comma inconfig.json, the next command of any kind, including read-only ones likeopenspec list, read the fallback defaults as telemetry consent, minted a new anonymous ID and wrote it back, replacing the whole file: atelemetry.enabled falseopt-out, the chosen profile and the workflow list were all lost, and usage events were sent. A config file that exists but does not hold a JSON object, whether it failed to parse or its root is something else such asnull, an array or a string, is now never written implicitly, and telemetry and the update check treat it as opted out.config set,config unsetandconfig profilerefuse with an error that names the file and points toopenspec config edit, andopenspec config reset --allstill replaces it. The existing "Invalid JSON" warning is unchanged, and valid or missing config files behave exactly as before.#1840
fede536Thanks @clay-good! - Resolve the contradiction that left/opsx:update's only write path without a governing rule. Step 4 told the agent to "Apply the requested edit", while step 5 and the guardrails told it to write only after the user confirms each revision, so the same/opsx:update "the design now uses X"either wrote immediately or stopped and showed the proposed revision first, depending on which passage the agent weighed. Step 4 now drafts the edit in the conversation and step 5 owns every artifact write, matching the workflow's own specified behavior: propose each revision and apply it only after user confirmation. Fixes #1836.#1858
db560aeThanks @dwin-gharibi! - Stopvalidateaccepting a requirement whose only scenario is a bare header. The delta scenario counter counted every####header, while the spec path that archive uses to validate the rebuilt spec keeps a scenario only when its body has content, sovalidatecalled such a change valid andarchivethen refused it with a generic "Requirement must have at least one scenario" that did not name the requirement. Both paths now share one rule,hasScenarioBody, and read a scenario's body up to the same boundary, sovalidaterejects exactly what archive rejects, naming the requirement and saying that a header with no body under it does not count. A scenario whose body is only a fenced block or a deeper header still counts, a requirement with one real scenario is still accepted even when another is empty, and main-spec validation is unchanged.#1774
09984b8Thanks @clay-good! - ### Bug Fixestasks.mdwritten as plain bullets or a numbered list counts as zero tasks, soopenspec listandopenspec statusreported "No tasks" andopenspec archivehad no unfinished work to warn about.openspec validatenow warns when a change's tracked task files contain list items but no checkbox at all, and points at the first offending line.#1852
5f5914eThanks @clay-good! - Match the natural "openspec " phrasing to the workflow it names. Users and agents say "openspec propose" or "do an openspec apply", but no workflow skill's description contained that phrasing (and a skill's description is what an agent matches on), so the phrase read as an invitation to hand-build the artifacts with the CLI instead of running the workflow. Every workflow skill's description now names the phrasings a user actually types ("openspec propose", "opsx apply", and so on). Runopenspec updateto pick it up.openspec updateitself is deliberately left unclaimed: it is a real CLI command that refreshes generated files, unrelated to the update-change workflow, which claims "openspec update change" instead. Commands-only installs write no skills and are unchanged. Fixes #1221.v1.13.0Compare Source
Minor Changes
#1783
8ba4ac1Thanks @clay-good! - Apply now says when a change has no delta specs. Apply gates on the schema'sapply.requiresalone, so a change whosetasks.mdwas written ahead of its specs read as ready to implement even though it had no spec deltas at all — the stateopenspec validaterejects.openspec instructions applynow reports that gap as a warning (text and--json), naming both ways out: write the specs, or declareskip_specs: true. Changes that have specs, declareskip_specs, or are still blocked on their own required artifacts are unaffected.A blocked apply also names the whole chain now, not just the first hop: a change holding only a proposal reported
Missing artifacts: taskswhile the specs thattasksdepends on were missing too, which reads as an instruction to write the tracking file straight from the proposal. The full build order is reported asmissingPrerequisitesin--json. The remedies these messages give are CLI commands (openspec instructions <artifact> --change <name>) rather than theopenspec-continue-changeskill, which thecoreprofile never installs.Patch Changes
#1798
aedf4d0Thanks @dwin-gharibi! - Stop archive rewriting the inside of fenced code blocks. The final assembly inbuildUpdatedSpeccollapsed runs of blank lines across the whole rebuilt document to tidy the seams between the slices it rejoins, but the pass was not fence-aware, so a requirement documenting a sample with two or more consecutive blank lines had that sample silently edited on archive, and edited again on every later archive. That matters wherever whitespace carries meaning: YAML block scalars, Python, expected-output fixtures, Markdown inside Markdown. Blank runs are now collapsed only outside fenced blocks, using the samebuildCodeFenceMaskevery other structural pass in the module already used. Behavior outside fences is unchanged, including that only a truly empty line counts as blank, so a line of spaces is still never a collapse boundary. Fixes #1797.#1800
fadac3eThanks @dwin-gharibi! - Read a removal or rename written with*or+as the operation it is. CommonMark opens a bullet list with-,*or+, but the bullet form of## REMOVED Requirementsand theFROM:/TO:lines of## RENAMED Requirementsboth hardcoded-, so either other marker matched nothing at all. The operation then silently never happened:openspec validatereported the change valid,openspec archiveexited 0 with "Specs updated successfully", and the requirement that was supposed to be deleted or renamed stayed exactly as it was. The change archived as complete, leaving the spec quietly disagreeing with the delta that was meant to update it. Both forms now accept[-*+], theFROM:/TO:bullet stays optional, and the plain### Requirement:header form is unchanged. Fixes #1799.#1802
8251763Thanks @dwin-gharibi! - Apply every delta section, not just one copy of each. A delta file that wrote the same header twice, two## ADDED Requirementssections say, silently kept one of them: sections were collected into a record keyed by title, so a repeated title overwrote the earlier body, and the case-insensitive lookup returned only the first entry that folded to the target, so## ADDED Requirementsbeside## Added Requirementsleft the second unread. Every requirement under the discarded copy was gone before validation or the merge could see it, soopenspec validatereported zero issues andopenspec archiveexited 0 having applied less than the author wrote, then moved the change to the archive with the live spec quietly diverging from what was reviewed. Sections are now kept as a list and every body whose title matches is read, each keeping its own line numbers so diagnostics still point at the right copy. Rename pairs are read per section, so aFROM:in one copy of the header can never pair with aTO:in another. An author does not have to repeat a header on purpose to hit this: a delta documenting OpenSpec's own syntax inside a fenced example produces the duplicate on its own. Fixes #1801.#1657
6d2dbe6Thanks @clay-good! - Load project context before proposal planning, using the selected project or store root and honoring config precedence and validation limits. When no root exists, stop without writing files and offer initializatioConfiguration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR was generated by Mend Renovate. View the repository job log.