Repository navigation
0.4.0 makes every write command preview by default: spec new and spec set now write only with --write, like the rest, so add --write where scripts and CI create or change documents. It also adds coverage audits, decision-maker import, [check] link_ignore and safer --write undo for files with uncommitted edits, and fixes about 100 issues across checks, hints and refusals.
Behavior changes
What an existing command does differs from 0.3.0; check scripts and CI that call these.
spec new and spec set now only preview without --write: add --write to every script, CI job and agent instruction that creates or changes documents with them. Like every other write command, a run without --write (or with --dry-run, which does the same) writes nothing, shows the change with its diff, and exits 0 with next: naming the same command with --write; --json reports written: false. Their hints, help, docs and agent skills now give the --write form. The 0.3 stderr warning is gone.
spec check --write-baseline with --rule or --skip is refused with exit 2: a baseline is recorded from a full run only (C181).
Two or more [[guards]] with the same baseline file are refused when the config loads (exit 2, naming both guards): each guard's --write-baseline used to overwrite the other's entries (C184).
A --write (fix, edit, set and the rest) over a tracked file that already has uncommitted edits first copies it to .specfold/trash/<timestamp>/, and its undo: copies that back instead of git stash, which also took the user's own edits; the preview names such files. Files without uncommitted edits keep the git stash undo (C183).
-C <dir> now resolves relative path arguments against <dir>, and next commands include -C <dir>.
Write previews show an update's changed lines as unified-diff hunks (@@ -old,n +new,m @@): changes with an unchanged line between them, such as the refreshed updated: line and an edited section, are separate hunks instead of one block. In --json, update items carry hunks (each with line, old_line, before, after) in place of the top-level before and after; line is still the first changed line.
spec sync --prune previews name the trash folder .specfold/trash/<timestamp>/ instead of the current second, so a preview and the refusal of the same write agree; the write itself uses the second it runs in and prints the real path. The pruned-files note names .specfold/trash/.
spec check --rule/--skip refuse (rc 2) a rule id that does not exist, as --fail-on does; a lower-case id is pointed at its upper-case form (C132).
spec fix --pick refuses (rc 2) an id that is not one of the finding's choices, listing them (C146).
spec log --since must be a real YYYY-MM-DD date, and spec log --type, spec find --type and spec find --status must name a known type or status; others are refused with rc 2 (C148, C153).
--limit 0 is refused (rc 2) by every command that takes --limit; it showed nothing and its --offset hint pointed at itself (C152).
spec new --governs and spec context refuse (rc 2) an empty path or one outside the repository, instead of recording it or suggesting spec new --governs for it (C130).
A guard whose range is not staged, push, all or a git range (a..b) is refused by every command that loads spec.toml, naming the guard; spec doctor reports it as a config finding (C141).
- An id held by two files (I1, e.g. a
.spec/and a.spec.local/copy) is refused (exit 2) by every command that takes a document, listing both paths; name one by its path.spec doctor's fix names the path (C139). spec edit --section/--new-sectionrefuses (exit 2) text holding a heading at the section's level or above, which would start or duplicate a section; deeper headings and fenced code are fine (C125).spec import --apply --writerefuses (exit 4) while rows marked!still need a decision, lists them and suggests--force; the new--forceimports anyway. Previews are unchanged (C155).spec attach --intorefuses (exit 2) an absolute folder or one with..; it used to write outside the bundle (C142).spec new skill|rule|agentrefuses (exit 2) a title with no ASCII words unless--slugis given, and a--slugthat is not kebab-case (C147).spec initrefuses (exit 2) an--owner-kindwith an empty pattern or template, a{n}beyond the pattern's*s, or a suffix other than:manifest, and a--rootthat is not an owner id (C140).spec set <id> title=refuses (exit 2) emptying the required title (C128).spec export … --dry-runpreviews and writes nothing (it exported before): it listsfilesand, over an earlier export,replaceswith exit 0; a plugin exporter is not called.spec check -qnow shows config warnings too (C114, C115, C116).spec set <plan> status=archived --writerefuses with exit 4 while the plan has open tasks and lists them;--forcearchives anyway, and--dry-runreports them with exit 0.- Exit code
4(CLI-17): a write flag was given but a safeguard refused it, so nothing was
written (--writeover[write] max_files,sync --prunewithout--yes, a hand-edited
copy without--force,exportover an earlier export orattachto the store without
--yes). These exited0before. spec set <id> key=[a, b]stores a list, not the string"[a, b]".spec sync --prune --write --yesno longer deletes: each pruned file moves to.specfold/trash/<stamp>/(ignored by git), and the undo line copies it back (cp -Rp .specfold/trash/<stamp>/. .), so a file never committed can be restored. The undo of a sync or copy lists only the files written, not their sources; a successful sync ends withnext: git status --short(C100, C101, C106).spec check --write-baselinewithout[check] baselinesays what to add to spec.toml before previewing, withnext: spec explain config(C102).--dry-runis accepted on every command guarded by--yesor--writeand writes nothing: check and guard ignore it, import previews even with--apply(C120), and export previews instead (C103, C114). An export whose--outis outside the repository suggestsls <out>instead ofgit status(C104).- An invalid spec.toml points to
spec explain config(C106). - Overwrites need ownership or
--force(CLI-4):spec sync assets --writeoverwrites a copy file only when its content is a version its source has had (git history or now); a hand-edited copy needs--force.spec fetchleaves a different existing file alone and fetches nothing unless--force.initupgrades andexportare unchanged. - Bulk writes are bounded (CLI-7):
[write] max_files = 50(0 = no bound). Whenspec mv,fix,syncorimportwould change more files, the preview says how many and--writealone writes nothing; narrow the scope or add--yes(new on mv, fix and import). - One meaning per shared flag (CLI-11):
spec guard --tree(was--all),spec find --body(was--text),spec init --owner-kind(was--owner). The old names keep working as hidden aliases with a stderr warning; help JSON lists them under each flag'sdeprecated_aliases. spec --helpandspec helplist the 12 core commands (context, find, show, graph, new, set, edit, check, guard, fix, explain, init);spec --help --all/spec help --alllist every command.spec help --json,--markdown,--llmsand MCP still describe every command; JSON addscore.spec guard --write-baselinewithout--yespreviews the hit counts it would record.- CLI conventions CLI-1–CLI-16 (AGENTS.md,
tests/cli_contract.rs). Actions that delete,
overwrite or send files out now need--yesbesides their write flag; without it they write
nothing andnextshows the command with--yes:spec sync assets --prune --write,
spec attach … --writewhen a file goes to the store,spec export markdownreplacing an
earlier export in--out, andspec check/spec guard --write-baseline.
Added
-
spec audit --kind coverage: with[audit] coverage_types = ["sot"], lists each{#ANCHOR}heading of a current document of those types that no other document cites as[[<id>#ANCHOR]]. Off by default. -
Import keeps who decided: MADR
decision-makers/deciders,consulted,informedand MADR 2/Log4brains* Deciders:body lines becomedecision_makers,consultedandinformedlists, now declaredadrfields (no S1 warning). -
[check] link_ignore: globs of link targets rule L8 skips. -
A config warning when
require_whennames a status the type does not have. -
A config warning when
[audit] coverage_typesnames a type that does not exist. -
Rules L8 and L9 (warn). L8 checks plain Markdown links and images offline: a relative path
must exist and a#fragmentmust match a heading's GitHub slug (repeats get-1,-2) or a
{#anchor}in the target file; URLs and ids are skipped. L9 flags an image without alt text. -
New rule S8 (warning): a field that
require_whenrequires for the current status must hold a usable value — not a placeholder ([types.<t>] placeholders, defaultTBD,Pending,TODO,?), of the kind declared infields = { approvers = "people", resolution = "url", review_by = "date", decision = "link" }, and not shared within adistinct = [["authors", "approvers"]]group.fieldsas a list works as before. -
spec help --jsonmarks each command'sdeprecated_aliases(name,use,since). A deprecated name keeps running the new command and warns on stderr (none yet). -
spec set ID key=with an empty value deletes the key (an emptiedrelis removed too). -
D1 also counts staged deletes and the old paths of renames (
p (removed)). Its fix is a runnablespec new change ... --depends-on ID --write;spec explain D1points tospec check --staged --rule D1. A staged/changed run without documents says so in its summary. -
spec new --depends-on ID(repeatable) writesrel.depends_on. -
spec exportreports attachments it did not scan for secrets (not_scanned); binary files no longer get thescan_max_byteshint. Successnextisgit status --short -- <out>. -
--dry-runon every write command (edit,mv,fix,init,attach,sync,audit;
import --plantakes it as an alias;newandsetkeep theirs). -
spec help --jsongives each command arisk: read, write, destructive or external. MCP tools
carry the matchingannotations(readOnlyHint, destructiveHint, idempotentHint, openWorldHint). -
Writes report an
undocommand (git stash push --include-untracked -- <paths>). -
Rule D1 (warning,
spec check --stagedand--changedonly): code changed under an active
document'srel.governswhile neither the document nor achangenote naming it changed. -
Rule U2 runs the built-in secrets check (the one
spec exportalways applies) over every
document file, so the commit hook (spec check --staged) refuses what export would refuse.
spec guard secretsruns it over any range, andspec guardwith no secrets guard configured
includes it. A configuredsecrets = trueguard keeps reporting through U1 instead.
Hits in local (git-ignored) documents are warnings; in committed documents, errors.
U2 also reads each document's text attachments kept in the repository (up to the new
[files] scan_max_bytes, 1 MiB by default; binary files are skipped), so a secret attached
with a warning keeps being reported byspec checkandspec guard secrets.spec export
reads the same attachments of the documents it exports and refuses on a hit.
Attachments it skips are named in one U2 warning (and anot_scannedline in
spec guard secrets) that points at raising[files] scan_max_bytes. -
spec attachruns the built-in secrets check on every file, with no[[guards]]needed: a hit
refuses a store upload (--allow-hit secretslets it through) and warns for a repository file. -
A slug id (
plan:root/payment-rolloutfor a timestamp-keyed plan) resolves in[[…]]
references, Markdown links, export andspec find; L1 suggests the canonical id. -
spec help <command>shows the command's notes (limits, settings, what a refusal means)
after its arguments and examples;--markdownand--json(notes) carry them too. -
The attachments guide, the config reference and
spec help attachshow a[files.store]example. -
Built-in secrets (
secrets = truein a guard) now find connection strings with a password
(scheme://user:password@host, any scheme: postgres, mysql, mongodb, redis, amqp …) and
three-part JWTs. Not flagged: example and loopback hosts (example.com|org|net,*.example,
*.test,*.invalid,localhost,127.*,::1,0.0.0.0); placeholder passwords
(${VAR},$VAR,%VAR%,{{var}},<var>, one repeated character such asxxxx, words
such aspasswordorchangeme, a password equal to the user name such as
postgres:postgres, fewer than 4 characters); and the jwt.io sample token. -
spec attachruns every guard that reads content (secrets, denied terms or patterns, denied
files) over files bound for the store before uploading; an error-level hit refuses, a warn
hit is shown, and--allow-hit <guard>lets one guard's hits through. Matched values are
never printed. -
spec exportruns the built-in secrets check, and the guards markedpublic = true, over
what leaves (the built-in tree, or the source files of the documents a plugin receives) and
writes nothing, nor calls the plugin, on an error-level hit. The secrets check runs even when
no guard is configured;--no-guardskips the check and the guards. -
Rule L1 warns about a Markdown link whose target is a document id (
[text](plan:root/…))
and offers[[id|text]](spec fix --rule L1 --write); the built-in export rewrites the
ones that resolve into the target's link. -
A timestamp-keyed document resolves by its id with the slug in place of the timestamp:
plan:root/payment-rolloutnamesplan:root/20261030020644. -
spec export markdownends with a next command, and its missing--target/--outand
non-empty--outerrors offer a runnable one. The export guide documents how same-named
files get-2,-3. -
The Markdown source of the export guide is published at
/guides/export/index.md. -
spec mcpreplaces built-in secret matches with[redacted: <pattern>]in tool results and
reports the count asredacted;[mcp] redact = falseturns it off.spec editand
spec setrefuse text holding a redaction marker the document does not already hold. -
spec export markdown --target obsidian|mkdocs|docusaurus|github-wiki --out <dir>: a
built-in exporter that writes plain Markdown with each target's links, flattened
frontmatter, a.specfold-export.jsonmanifest, and a report of references written as text
(--fail-on unresolvedto fail instead).--localadds.spec.localdocuments;--banner
names each page's source. The namemarkdownis reserved for it. -
Exporter plugins can declare
wants: ["body", "links"]to receive each document's body,
source_revhash and resolved references with byte spans; past--max-bytes, bodies come as
body_path. -
spec graph consumersprints a header, a count and scope footer, andsearchedin JSON. -
spec help lsptakes--dirand points to the editor setup guide. -
spec newwarns when the slug leaves out words in another script ("결제 비교 Gateway" gives
gateway), not only when most of the title is lost.
Fixed
-
spec import --apply --writeof a CRLF source writes the new document CRLF throughout, its generated frontmatter included, instead of LF frontmatter over a CRLF body; LF sources stay LF (C243). -
A usage error missing some other required option (
spec edit <id> --cell S=x --text xwithout --row) no longer suggestsspec edit <id> <id> ...; it points tospec help <command>.--format jsonnext to an option left without its value is no longer rewritten into a rerun that fails the same way (C241). -
spec attachrefuses (exit 2, nothing written) the document's own file, or a file that would land on its bundle index name, which used to record a hash that could never match (C239). -
A heading's optional closing
#run (## Context ##) is dropped from its text, so the outline shows "Context" and[x](#context)links pass L8 (C240). -
A line indented four or more spaces, or starting with seven or more
#, is no longer read as a heading: outline,show/edit --section, edit's heading guard, required sections (S7) and anchors share one CommonMark heading parser (C238). -
spec.tomlregexes and globs ([verify] serial,full_run_on,[forbid],[cites], guard and asset paths) are checked at load: a bad one stops with its line (exit 2) instead of being dropped. An empty or missing[verify] shellis reported byspec doctor, andspec verifynames the shell and points tospec doctor(C237). -
Language tags compare case-insensitively in
spec census --translationsand R1's one-translation-per-language check, asspec new --translationdoes;--langthat is not a language tag exits 2 (C236). -
spec doctoron a spec.toml that does not load says to fix it at the line TOML names;spec init --upgradeon such a file fails with exit 2 instead of calling it current (C235). -
spec mvof a bundle document: the undo stashes the old and new folder and runs cleanly, and relative links to the bundle's attachments follow the move (C233, C234). -
Release notes come from the CHANGELOG section for the tag, falling back to the commit subjects since the previous tag.
-
spec help newexamples and thespec agentslines that start documents carry--write(C232). -
spec guard --range A..Breads files atB(HEAD when empty) instead of the checkout, and also reads every version a commit in the range wrote, so a value added and later removed inside the range still fails (C230, C231). -
Suggested next commands write option values starting with
-as--opt=value, put such words after--, and a usage error's next keeps-C <dir>(C229). -
C227: every
spec setthat a fix, next, rule explanation or help example suggests carries--write, including both steps of the T1 fix for a draft, so it still writes oncesetpreviews by default -
C226: "section '…' matches N headings (line …)" from
spec editandspec show --sectionnames file lines, not body lines -
C223:
spec set <doc> updated=takes only a date, an RFC 3339 time ortoday(rc 2 otherwise, preview included), refusesupdated=as it doestitle=, and its next runs S5 -
C224:
spec mvcounts a referring document once when both its links and its rel lines change, in the preview, the write and --json -
C225: the undo of
spec attach --write --yesover other content also puts the document file back, so onecp -Rprestores the files list and the replaced files -
C220:
spec guard --rangerefuses an empty value and..Xwith their own message (rc 2, next<base>..HEAD); the<rev>..HEADsuggestion is built only for a single revision -
C222:
spec export markdownin a repository with no documents creates the folder with the manifest alone (rc 0, nextspec new) instead of a raw os error -
C221:
spec export markdown --outresolves the nearest existing folder through symbolic links, so a link into.spec,.spec.localor.git(or one containing the repository) is refused like the plain path -
C216:
spec guard <unknown>listssecrets (built-in)among the guards it has, with nextspec guard secrets -
C214:
spec fixresolves its targets likespec check(a missing file, a directory, a non-document or:abcis rc 2; ids are accepted) -
C204: T1 on a draft names the two-step fix (
status=active --write && status=superseded), and T1 andspec set status=supersededignore a cross-typerel.supersedesthat L3 rejects -
C213:
spec graph chainwalks a supersede cycle once, reportscurrent: null("a supersede cycle, nothing is current") and points atspec check --rule T1 -
C212:
spec census --translationsrows are paged items (--jsonl, --limit, --offset), andspec graph --jsonlstreams one row per node, edge and governed path -
C211:
spec importwalks sources in path order, so records sharing a date get ids in file-name order on every filesystem -
C210:
spec init --owner-kindand spec.toml refuse an owner pattern segment that mixes glob characters (**,svc-*) with rc 2, and {N} counts whole*segments -
C207:
contextandgraph governson a folder also list documents whose governs path or glob lies inside it, and no longer suggestspec new sotthen -
C206: concurrent
spec newruns reserve their timestamp id with create-new and take the next second on a clash; I1's explain gives a repair path for timestamp ids -
C201: inline code spans close only at a backtick run of the same length (CommonMark), so a link after
a ` bis checked and counted -
C200: S1 reports a title that is not a non-empty string and S4 a status that is not a string;
spec set title=[x, y]writes quoted text, not a list -
C199: set, edit and mv keep a document's leading UTF-8 BOM; the preview no longer shows line 1 changing
-
C198: a section name picks an exact heading or {#anchor} before a heading that only starts with it; several prefix matches or duplicate headings are rc 2 listing them
-
C215: an argument that is not UTF-8 is rc 2 with the argument named, instead of a panic
-
C218:
spec help --markdown/--llmsput--flagwords in prose in code spans, so the docs site no longer renders--writeas an en dash -
spec guard --range <rev>without..is refused rc 2 (next:--range <rev>..HEAD), as the config already did; it used to scan the history of<rev>and pass the new commits. -
spec edit --text/--filerefuses text that opens a code fence and never closes it (rc 2), which used to turn every later heading into code and hide sections and open tasks. -
Undo lines work for git-ignored paths (the local tier:
rm -finstead of a stash git cannot reach) and for documents never committed (set/editcopy the old file to the trash instead of a stash that removes the whole document). -
spec attachno longer silently overwrites a bundle file with other content (repo or store): the preview marks itreplace, the write needs--yes(and--forcefor a file the document does not list, asspec fetch), and the old file is copied to the trash with anundo:line. -
spec mcprefuses a tool argument whose type does not match the schema (a string for a boolean, a non-integer limit) and a positional value starting with-, so a value can no longer become a flag such as--writeor--yes. -
spec audit recordandspec audit --recordaccept--slug(validated likespec new), so a title with no ASCII words can be recorded;--writenow prints anundo:line. -
A guard baseline that passes through a symbolic link into a folder that does not exist yet and back out (
lnk/sub/../.guard-base.json) is compared where the write would land: the deepest existing part of the path is resolved before..is applied. Such baselines are refused as shared instead of overwriting another guard's baseline and creatingsub/(C195). -
Two guards whose baselines reach one file through a symbolic link folder (
lnk/.guard-base.jsonand./.guard-base.jsonwithlnk -> .) are refused as sharing it: an existing folder is resolved before the paths are compared (C194). -
spec graph --format dotnames nodesd0/p0with the text aslabel, like mermaid, so a governs path spelled like a document id no longer redeclares that document as a folder or lands its edge on it. Scripts that matched quoted id node names must match labels now (C190). -
spec init --upgradereads a spec.toml that starts with a UTF-8 BOM (and keeps the BOM when it writes), so the upgradecheckasks for no longer reports "is current" and loops (C191). -
spec edit --new-sectionrefuses (exit 2) a name that is empty or blank, holds a line break, or starts with#, asnew --titledoes, instead of writing an empty, doubled or## ##heading (C188). -
-C <file>is refused (exit 3) instead of running against no repository, and an empty-Cvalue points at the help of the command typed rather than an unrelated one (C193). -
-C <subfolder>now acts likecd: relative path arguments are read from it and rebased to the repository root (context,new --governs, …), and undo/fix shell commands run from the root (git -C <root>), so the printed undo works (C192). -
spec export --local --no-localis refused (exit 2) in either order instead of silently dropping the local documents the preview counted (C189). -
spec contextandspec checkhint--governswith glob characters escaped (src/[[]a]/z.rs), andspec new --governsescapes an existing literal path holding[ * ? {, so the document governs that file and not the ones a glob reading would match (C185). -
The shared-baseline check resolves
.and..and paths under the repository root before comparing, sox/../b.jsonand./b.jsoncount as one file (C186). -
Two writes in the same second no longer share a
.specfold/trash/<timestamp>/folder: each write claims a fresh one (<timestamp>-1,-2, … when taken), so an earlier trash copy is never overwritten. This applies to edit, fix, set, mv, import and sync --prune (C187). -
spec edit --rowwith a key that several rows share now says so, lists their line numbers and points at editing the whole section; it asks for --section only when the rows sit in different sections (C182). -
spec mvrewrites reference definitions, angle-bracket and percent-encoded relative links too, and no longer touches links inside code (C180). -
spec import --apply --writenow rewrites reference definitions, angle-bracket links and percent-encoded links (keeping the encoding), still skipping code; L8 also checks reference definitions (C180). -
spec show <id> --bodycut by--max-bytesnow continues withspec show <id> --section …instead of putting the body text where the id goes (C179). -
spec newwith an option it does not have (--tier local,--bogus x) now points atspec help newinstead of a next that repeats the same error (C178). -
spec export markdown --dry-run(and the replace refusal) now print a next that keeps--banner,--localand--fail-on, so following it writes what was previewed;--no-guardis still left for you to type (C177). -
Next steps and fix commands shell-quote every path and id through one function, so
spec contextandauditgap suggestions run as printed for paths with spaces, quotes, or non-ASCII names. -
Audit:
--kind staleshell-quotes governed paths in itsgit diffnext step. -
Audit:
--kind stalesuggests onlyspec set <id> verified_at=HEADwhenverified_atis not a commit, instead of agit diffthat cannot run. -
Help:
showandeditexamples name sections new documents have (rule,Steps). -
spec context: the next names--section ruleonly for a sot; other types getspec show <id>. -
spec check --format sarif:artifactLocation.uripercent-encodes each path segment (a space is%20). -
spec init --upgrade: a spec.toml from a newer version is refused with the same message and exit code asspec check, not reported as current. -
spec init --upgrade: a table header with a trailing comment ([spec] # main) is read as TOML reads it, so an old schema under it is raised asspec checkasks. -
spec fix --writewith nothing to fix says "no change" and reportswritten: false, with no empty undo line (asmvandset). -
spec fix --rule: an unknown or lower-case rule is refused (exit 2, "no such rule", upper-case hint) as inspec check --rule. -
spec export: a refused run's next keeps--dry-run,--local,--bannerand--fail-on, so following it from a preview no longer writes. -
spec new: the draft-supersede refusal now namesspec new adr --title '…' --from <id>, and the missing---titlenext keeps--fromand the other options given. -
C164:
spec mvto the document's own id and path reports "no change" withwritten: falseand no undo, assetdoes, instead of0 change(s) written. -
C163: with
-C <dir>, shell commands innext:andundo:run from where you typed them: git ones getgit -C <dir>, otherscd <dir> &&; checkfix:hints getspec -C <dir>. Output without-Cis unchanged. -
C162:
new --titleandset title=also refuse U+0085, U+2028 and U+2029 (YAML 1.1 line breaks), as for\nand\r. -
C161: with
-C <dir>, relative path arguments (attachfiles,edit --file,import --map,export --out,audit --from) are read from<dir>instead of the shell's directory, andnext:commands keep-C <dir>. -
spec new --titleandspec set title=refuse a title with a line break (exit 2, with a next
command to give it on one line); it split the body heading and one-row-per-document lists. -
spec export markdownlooks up relation targets by id instead of scanning every document for
each one: 10,000 documents export in about 1 s instead of 4.8 s, with the same files.
A write preview of a very large change no longer runs out of memory: hunks are computed only while the compared region stays under about four million line pairs, and a larger change is shown as one hunk; 30,000 replaced lines had aborted (C158).
Preview hunk headers matchgit diff -U0: an empty side names the line before the change, a count of 1 is left out, and an insertion or deletion among repeated lines sits where git puts it; JSONlineandold_linefollow the same rule (C159).
The pre-push snippet fromspec hook printno longer fails on a branch without an upstream: it compares with the merge-base of the default branch (origin/HEAD), and when there is neither it prints a note and skips (C126).
Thespec editpreview diff on a CRLF document compares lines with their original endings, so it shows only the changed lines instead of the whole document (C157).
A refused--slug(e.g.spec new skill --slug 'Bad Slug') is replaced in the suggested rerun instead of given twice (C156).
A failingspec verifysuggestsspec showwith the failing document's id instead of a literal<id>(C129).
spec logsuggestsspec showwith a real id, and no next when nothing was listed (C149).
spec graph --format boxwrites a cycle note on one line with ids only (C131).
spec check --write-baselinewithout[check] baselinenow carries the reason in JSONerror(C134).
LSP diagnostic messages keep a real line break beforefix:instead of\u{000A}(C144).
Thespec fixpreview's next keeps--staged,--pickand the targets, so following it writes what was previewed (C145).
The usage-error next ofspec newno longer takes option values as type or title, and keeps--title/--governs(C133).
A plugin that writes a lot to stderr no longer stalls until its timeout; stdout and stderr are read together and the error shows the last stderr line (C150).
A plugin that leaves a background child holding its pipes now stops at itstimeout_ms;spec pluginsreports a bad action before starting any plugin (C151). -
Writes keep a file's CRLF line endings instead of mixing in LF (C154).
-
spec mvrewrites relative Markdown links to the moved file (fragment kept), and its next includes L8 (C137). -
spec audit --recordcites findings as[[id]]so the record passes L2, and audit records are no longer reported as orphans (C136). -
spec attachpreviews and refusals keep--intoin their next (C143). -
Code fences close only with the same character and at least the same length, so a ```` fence holding ``` lines or a
~~~fence is read alike by every command (C135). -
spec importcounts the alias table toward[write] max_files(C138). -
spec fetchdownloads to a temporary file and replaces the target only after the checksum matches; on a mismatch it removes the temporary file, leaves the target alone and exits 3 (C127). -
spec sync assets: a path that is a folder on one side and a file on the other is named in the preview and the write alike (write refused, exit 4);--prunemoves the copy's entry to the trash first. Folder errors name the path (C124). -
spec import --apply --write's undo brings back source documents that were never committed: they are copied to.specfold/trash/<stamp>/before the write and copied back by the undo, alongside thegit stashfor committed sources (C123). -
spec import --apply --dry-runpreviews (rc 0, nothing written) instead of failing with a clash against--plan;--dry-runis listed inspec help import, and still refuses--yes(C120). -
spec import --apply --writelists the files it created, moved and updated (alias table included) and printsundo:; JSON gainswritten,undoandchanges. With no source documents it writes nothing, not even the alias table (C121). -
spec mv --writeon a document not yet committed offers an undo that works: a copy of the uncommitted file is kept under.specfold/trash/and the undo removes the new path and copies it back, instead of agit stashthat failed on the old path and left a stray stash (C122). -
spec setwith values the document already has writes nothing: it reportsno change, exits 0, and neither bumpsupdatednor offers an undo. -
spec importplans note body lines (* Status:,* Deciders:,* Date:…) that repeat values moved into frontmatter, and an H1 that keeps a number the title dropped. -
spec auditsuggests checking open tasks (or--force) before archiving an active plan that still has them;spec check's summary counts spec.toml warnings (+N in spec.toml, fieldconfig_warnings); a mistyped[audit] coverage_typesentry gets a did-you-mean, andspec doctorpoints at the type name to fix. -
spec mvpreviews keep--owner,--slug,--tierand--cited-pathsin the next command. -
After
spec setchanges a status or a required field, the next check includes S6 and S8. -
Nexts add
--force(sync over a hand-edited copy) and--yes(fix overmax_files); the S6
fix names every missing field. -
Docs say
--owner-kindwhere they still said--owner. -
An attachment that changed because a secret was added to it lists U2 before E1, and E1
points atspec explain U2instead of suggesting to attach the leaky file again. Secret
findings now come first among findings of their level. -
spec help <command>gives the first example asnextwithout its column padding and
# ...comment (spec help guardended with the padded example line). -
spec exportsecret refusals name the source document file and line (not the exported copy)
and suggestspec guard secrets. -
spec findwith no current match suggests the same search with--all, runnable as written. -
spec attachwritesupdatedas an RFC 3339 timestamp, likespec newandspec set. -
Refusing text that holds the mcp redaction marker now suggests
spec edit <id> --append.
Changed
spec newandspec setaccept--write(they always write).- A missing-section error from
spec editandspec showno longer lists the H1 title. spec new --translationno longer suggests--titlewhen the title comes from the source.- An error that ends with a runnable
spec …command offers it asnext. - Exporter plugins no longer receive
.spec.localdocuments unless--localis given; edges to
them are left out and references to them arrive unresolved (reason: "not exported"). spec edit --text,--new-section,--rowandspec new --titletake values that start with
-, such as--text "- [ ] wire gateway".spec edit --text -is refused and points to--file -instead of writing a lone-.- A file that is not UTF-8 is reported by
check(S1),fixandshowwith its path and line,
instead of "no frontmatter block". - After
spec set <id> aliases=…, the suggested check includes I2 (duplicate aliases). spec edit --sectionon a missing section offers the--new-sectioncommand asnext;
spec graph --format jsonsays JSON output is--jsonand offers that command.