Releases: developzoneio/specwright
Release list
v1.6.0
Added
## Quickstartsection inREADME.md(SW-8) - a numbered path (install ->/sd:setup->
/sd:feature <slug>, with the bundled fixture as the fallback for readers with no project handy)
so a new reader reaches their first spec-approval gate without piecing the flow together from
separate sections. See the README restructure under Changed below for where this section
finally landed - it absorbed the install commands outright. Also adds a star / "using this at
work" call-to-action to## Support, and nine GitHub topics plus a repo description fix (the
command count had drifted from what's on disk) viagh repo edit.## Spawned specsin the feature, bug, refactor, and perf spec templates (SW-42) - follow-up
work discovered mid-spec previously had nowhere to land except prose, where it evaporated. The
RCA template's reserved-ID table (Reserved ID | Type | Title | Owner) is now the one convention
across every spec type, not two. Each affected workflow's close-out prompts for the section
when the retro names deferred work - a prompt, not a gate: gate counts are unchanged, since
hard-gating hygiene would tax every spec for a minority's benefit.- The section ships with no
<<...>>token. It is filled at close-out, i.e. after
approved, so an author-fill placeholder there would be anSL010BLOCK on every spec that
deferred nothing. Header + separator is the empty state, and it is alsoSL090's trigger. SL090, the first 🟡 SUGGEST rule - adonespec whose body names deferred work with an
empty spawned-specs table. Advisory, never a failure, and its trigger vocabulary is a closed
phrase list rather than a judgement call, because an advisory that fires on a hunch is noise.
SL091-SL099open the close-out-hygiene band.- A reserved ID is not a registry entry. Documented alongside the index consistency rules:
it gets an.specs/index.mdrow only once its directory exists. Writing the row first
manufactures the ghost rowSL032exists to catch - the fourth of the four real-world
follow-ups that motivated this change was exactly that.
- The section ships with no
/sd:port- the fidelity-first port pipeline (SW-41) - the orchestration story that wires the
rest of the port epic (SW-37 skill, SW-38 template/snapshot layout, SW-39 extraction mode, SW-40
parity gate) into one command: bridge/extract -> freeze -> host survey -> fidelity tables -> pin
behavior -> plan -> execute batched -> justified-diff parity -> close-out. Ten phases, six gates,
four of them HARD (donor set frozen, fidelity tables complete, behavior pinned, justified-diff
parity) with no override path; the other two (plan approval, per-batch tests) are ordinary
approvals.--scopeis always explicit - Phase 0 asks when it is omitted rather than inferring
it, because scope selects the Phase 5 pinning mechanism.--fromselects topology (a bridged
cross-repo contract artifact vs an in-repo path/symbol) without changing anything downstream.- Port policy stays Layer 2. Phase 0 reads a
Port policyheading from the host's
.specs/constitution.mdand always states the effective policy in its output, including the
fallback (structural mirror, persd-port-fidelity) when the host declares nothing - the
engine supplies the mechanism, never a hardcoded posture. - Behavior pinning is scope-dependent and gate-verified.
endpointgets a contract test
suite runnable against both donor and host;modulegets characterization tests through an
interface-typed construction seam, so re-pointing donor -> host changes exactly one factory
method and assertion bodies stay byte-identical;featureuses whichever the surface allows;
patternskips pinning (no donor instance) but the gate still proves the host production tree
is unmodified via an emptygit diff/git status --porcelain, not a good-faith claim. - A port-specific complexity metric. The existing decompose thresholds count impacted files
and layers, which trip on nearly every port by construction (a port's file count equals the
donor's). Phase 6 instead counts deviation-table rows requiring adaptation - the quantity that
actually scales with how much judgment the work needs - and records the rationale in
01-plan.md. - The anti-drift mechanism lives in the task block, not the gate. Every port task's
Pattern refscites a snapshot member range (04-artifacts/source/<path>:<first>-<last>), never prose
and never a host sibling, andAcceptancecarries the licensed-deviation ID list. Anything not
on that list is reproduced as-is. A task missing either is a planning defect, refused before
execution rather than caught only at the parity gate. - Lifecycle divergence, deliberate. Unlike
/sd:featureand/sd:refactor,abortnever
jumps a port spec toarchived- it leaves the spec at its current state so re-invoking resumes
exactly there, since a partially-frozen or partially-pinned port has no clean "give up" shortcut
the way an unstarted feature does. WORKFLOW_TYPE = portadded tosd-implementer- neitherfeature(allows new public API
freely) norrefactor(forbids new public API, requiresINVARIANTS) was the right constraint
set for reproducing a donor's structure under a licensed-deviation list, so this is a genuine
fifth mode, not a reuse of an existing one.portjoins the prompt-router keyword map (backport,port from,port the,donor repo,mirror from,replicate from- deliberately multi-word phrases; a bare"port"would
fire on "support", "report", "portal"), shipped in both hook implementations plus the
project-config.template.jsondefault.contractLint.budgets.skillsBytesraised 12377 -> 12412 -skills/sd-port-fidelity/SKILL.md
picked up two small cross-references to/sd:port's phases (the freeze step, the fidelity-table
author) replacing prose that pointed at "a documented manual step until the port pipeline
lands"; the ratchet moves with it.- Deliberately NOT built:
--sync/ re-port drift detection, multi-donor ports, and editing a
host project's build/lint/coverage configuration to exclude the snapshot - the command warns
about tooling that globs.specs/, it never edits. - Known gaps carried forward: no
SL06xrule machine-checks the port task-block contract
(Pattern refs range + licensed-deviation list) - it is enforced by Phase 6 refusing to execute a
defective block, not by/sd:spec validate; andspec-gate/subagent-retrostill do not
recognize thePORT-prefix (onlyprompt-router's keyword routing landed this round) - see
docs/troubleshooting.md.PROJECT-SNAPSHOT.mddoes not exist in this repo and never has (see
the historical note below); nothing in this change introduces it.
- Port policy stays Layer 2. Phase 0 reads a
- Port parity adjudication:
port-parityTASK_TYPE onsd-reviewer, the parity diff artifact,
and the parity gate (SW-40) - the enforcement half of the port epic and the only mechanism in
it that can see logic drift, structural mismatch, or silent simplification; test-green and
contract compliance are blind to all three, which left the fidelity rules from SW-37 as
honour-system prose. The main thread writes04-artifacts/parity/: one unified diff per
non-omitpath mapping row, an all-deletion diff for a row whose host file is absent, an
all-addition diff for a changeset file with no row at all, plusINDEX.mdlisting them.
sd-reviewerconsumesINDEX.mdasDIFF_REFand classifies every hunk with
sd-port-fidelity's vocabulary, now five classes rather than four.overreachedis the new one -
a deviation row covers the hunk but the hunk changes more than that row'sHost formstates -
and it is the class a rubber stamp hides in. Two whole-artifact checks join it: member
completeness, reported as a count with each absent row named, and path conformance, one BLOCK per
unmapped changeset file. Ajustifiedhunk is a PASS and is deliberately NOT written up, so a
real BLOCK cannot drown in a list of accepted diffs.templates/specs/port.template.md's fixed
AC-1 gainsoverreachedand aHost form-covers-the-hunk clause: reworded to track the skill's
vocabulary, never renumbered, because the number is what every fidelity finding anchors to.- Diff generation stays on the main thread, enforced by the tool allowlist -
sd-reviewer
gains noBashand no write tool and stays incontractLint.readOnlyAgents(CL201). The
reviewer that cannot produce the diff also cannot fix what the diff shows; adjudicating from a
file it did not write is the entire structural guarantee. - The
/sd:verifyoverlap, decided before any checking logic was written - member
completeness and path conformance stay with the reviewer and/sd:verifyis untouched, no new
VF0xxrule. Rationale recorded indocs/architecture.md:/sd:verifydecides everything from
00-spec.mdand02-tasks.mdwith fixed regex shapes, and neither check can be decided that
way - one needs a member boundary recognized in an arbitrary host language, the other needs a
changeset input/sd:verifydoes not take. - Deliberately NOT built: semantic equivalence checking, which is the behavior-pinning
phase's job rather than the diff's, and auto-generation of deviation rows from unexplained
hunks, which would let the diff justify itself and turn the gate into a rubber stamp. The gate
stays HARD with exactly two resolutions - revert the host toward the snapshot, or add a
deviation row whose group and citation hold up and re-run - and no override. - Known gap: the pipeline command that would generate the parity artifacts and host the gate
is SW-41. Until it lands, diff generation ...
- Diff generation stays on the main thread, enforced by the tool allowlist -
v1.5.0
Fixed
- Three CI-only failures surfaced by PR #23, none reachable from a real install. (1)
scripts/validate.shCheck 7 useddeclare -Aandmapfile(both bash 4+), which crash on
macOS's stock/bin/bash3.2 (declare: -A: invalid option, thenmapfile: command not found
once the first was fixed) - rewritten as plain indexed arrays with linear-scan
q_get/q_set/fp_get/fp_appendlookup helpers and awhile readloop in place of
mapfile, no behavior change. (2) The
"Lesson validator (PowerShell)" CI step asserts the leaky fixture correctly FAILS validation,
but GitHub Actions appends an implicitexit $LASTEXITCODEto every pwsh step, so the
intentional non-zero exit code from the leaky-fixture check failed the step even though the
assertion itself passed - fixed with an explicitexit 0after the assertion. (3)
tests/hooks/run-conformance.ps1's-SelfTeststub bash script exits immediately without
reading stdin, and writing the JSON payload to its now-closed pipe raised an unhandled
IOException: Broken pipeon Linux runners -Invoke-HookProcessnow wraps the
StandardInput.Write/Closepair in a try/catch, since a child that never reads its input is
not a harness failure.
Added
-
/sd:status- a read-only reader for the metrics log (SW-16). SW-10 has been accumulating
.specs/_metrics/events.jsonlwith no consumer; the data existed and was invisible. The new
13th slash command summarises the live log plus.specs/index.md: specs in progress, gate
activity broken out by kind (verify/protected/code-edit) and decision
(allow/warn/block), lifecycle transitions, and a friction section ranking where the
operator is actually stuck - which specs are blocked most, which code-edit warns are being
ignored, which specs accumulate stale retros, and which in-progress specs are absent from the log
entirely. Read-only: no spec is created, no gate is evaluated, nothing is written.
Three decisions are worth recording because they diverge from a naive reading of the ticket.
(1)jqis an oracle, not a runtime dependency. The acceptance criterion "counts reconcile
againstjq" reads like a dependency; it is not. The schema is flat, metadata-only and written in
fixed key order, so exact substring counting is deterministic - andjqaborts on a
partially-written line, which would lose the whole report to one interrupted append, exactly what
the ticket forbids.jqverifies the numbers; it does not produce them.
(2) Counting is delegated to the shell, never to eyeballing. A capped log is ~8000 lines;
the command prescribes the exact count commands rather than asking for a summary, because a
number that was estimated cannot reconcile with an independent count.
(3) The live file only -events.jsonl.1is noted in one header line and never read, per the
read contract set in SW-15.
Every degrade path is a labelled state (ST001-ST005): no config, metrics disabled, log
absent, log empty. A blank report would read as "no friction", so an empty table is treated as a
defect rather than an edge case. Malformed lines are skipped and counted, and the skipped
count is always shown - a silent skip and a clean file are not the same fact.
Verification corpus attests/metrics/(populated / malformed / empty fixtures, expected numbers,
and thejqoracle procedure), pinned to LF in.gitattributes. It is documented as a manual
corpus:commands/status.mdis a prompt file and CI cannot execute it, so it is deliberately not
wired intoscripts/validate.*. -
Size cap and single-generation rotation for the metrics log (SW-15). A new
hooks.metrics.maxSizeKb
(default1024KB, ~1 MB) bounds.specs/_metrics/events.jsonl: before each append, if the live
file already meets or exceedsmaxSizeKb * 1024bytes, the hook rolls it toevents.jsonl.1
(single generation - any previous.1is overwritten) and starts fresh. Implemented in all four
metrics writers (spec-gateandsubagent-retro, PowerShell and bash) so the two platforms roll
at the same raw-byte boundary ((Get-Item).Length/wc -c). Inherits every SW-10 invariant:
rotation is best-effort and never stops the append (a silent stop would read as "metrics
working" while dropping data - worse than unbounded growth, per the ticket), a failed roll (locked
file on Windows, read-only dir) is a silent no-op, and it never alters a gate decision or the
hook's exit code. An absentmaxSizeKbis treated as1024, so aproject-config.json
written before this feature stays bounded with no edit; an explicit0/negative disables rotation,
and any non-number is invalid and also disables it (SW-22 type-strictness).events.jsonl.1is a
grace buffer, not part of any read contract - there is no consumer of the log today, and when
one exists it reads only the live file. Added totemplates/project-config.template.jsonand both
hooks' embedded default configs; documented indocs/architecture.mdanddocs/troubleshooting.md.
New conformance fixtures attests/hooks/fixtures/{spec-gate,subagent-retro}/metrics-rotates-at-cap
and.../metrics-rotation-failure-noopprove PS and bash rotate identically. -
Sanctioned mid-execution re-plan loop (SW-14). A new
sd-replan-loopskill defines a HARD Gate
Re-plan for the two workflows that produce a01-plan.md+02-tasks.mdpair -/sd:feature
and/sd:refactor- so a plan-invalidating discovery adapts the plan without violating
immutability or skipping a gate. The gate is reachable from both the Execute phase and the
batch/holistic review (the one real corpus failure surfaced at review, not mid-task). On
approval it appends anR<n>entry to an append-only## Revisionslog at the end of01-plan.md
(original plan prose left intact), regenerates only the affected task blocks in02-tasks.md
viasd-spec-architect(TASK = planwithREPLAN_SCOPE, no new architect mode), and marks each
regenerated taskRevised-by: R<n>(a conditional field insd-atomic-task-format, like refactor's
Parallel batch). Like Gate Complexity, it is a conditional gate that fires only on its trigger,
so/sd:featurestill advertises 3 hard gates and/sd:refactorstill 6. It never re-plans a
donespec. Scope was corrected from the ticket on evidence:/sd:bugand/sd:rcaproduce no
task list to re-plan, and/sd:perfalready carries its own revert-and-reselect loop, so all three
are left untouched. Seedocs/adr/0003-adaptive-replan-loop.md. -
SL070-SL073in/sd:spec validate: a new revision-log integrity band cross-checking the
## Revisionslog in01-plan.mdagainst theRevised-bymarkers in02-tasks.md.SL070
(dangling marker),SL071(one-sided/unreferenced revision), andSL072(broken append-only
history) are 🔴 BLOCK;SL073(malformed entry) is 🟠 WARN. The checks run only when a## Revisions
section or aRevised-bymarker exists, so a never-re-planned spec produces no finding.SL074-
SL079reserved. Honest boundary recorded in the ADR:validateis a static linter with no
Plan-phase snapshot, so it enforces the revision record's internal consistency but cannot detect an
unmarked silent edit by diffing - that is prevented by the gate, not the lint. -
Conformance fixtures at
tests/revision-log/fixtures/(SW-14): a valid revision record that passes
and a dangling-marker record that must BLOCK, pinned to LF via.gitattributes. They state the
contract; like the other fixture trees they have no runner (documented, not silently skipped). -
Complexity triage + forced decomposition in
/sd:feature(SW-13). The architect writes a
spec-levelcomplexityfrontmatter field (S|M|L, distinct from a task's
Estimated complexity) with a one-line rationale at create time. Gate 2 then measures the actual
plan against decompose thresholds - **> 8 tasks, > 2 production layers (Tests/Config excluded),8 impacted files, or an unresolved Open question** (the
> 8line set from the corpus canyon
between 3-4-task and 10-12-task specs; the Tests/Config exclusion keeps ordinary 2-layer mediums
under threshold).
Over threshold, Gate 2 becomes a HARD Gate Complexity that refuses one oversized plan and
forces a split into medium child specs (FEAT-<parent-arg>-<child-slug>, linked via existing
/sd:spec link spawns/depends-on; the parent becomes an immutablearchivedumbrella). Under
threshold it stays the normal plan approval with zero added friction - still 3 hard gates, not- A create-time
complexity: Lalso escalates models a tier (explorer ->sonnet, architect ->
opus, aliases only, per-invocation), deepening the impact map and plan for genuinely large work.
Task counts use the tolerantsd-atomic-task-formatheading grammar, not a naive### T<NN>
regex. Seedocs/adr/0002-complexity-triage-decomposition.md. Linting of the field + split
integrity is deferred to SW-4 (/sd:spec validate).
- A create-time
-
Field label grammar in
sd-atomic-task-format(SW-11). Task-block labels are now matched
case-insensitively, with**optional and the colon permitted inside or outside the emphasis -
all three forms found in live specs (- **Files**:,- Files:,- **Files:**) parse
identically. A field's value runs to the next field label, not the next newline, so multi-line
AcceptanceandPattern refsvalues are no longer truncated. The grammar is defined once and
applies to every field and every reader; per-field matchers are forbidden. -
SL060(WARN) in/sd:spec validate: a task block in02-tasks.mdwith noPattern refs
field.SL061-SL069reserved for further task-block content rules. This is the first rule
that reads inside a spec artifact rather than around it - see...
v1.4.0
Added
ROADMAP.md- published roadmap of near-term, planned, and exploratory work, linked from
README.md(new## Roadmapsection + Documentation entry). Migrated the forward-looking items
out of the non-standard### Plannedsubsection that sat under the1.2.0changelog entry into
this dedicated file./sd:setupcodebase scan (Phase 2.5) - samples the project tree to pre-fill detected facts
(stack,paths.{src,tests,docs},commands.*from the project manifest, and a new ordered
inside-outpaths.layersmap) intoCLAUDE.mdandproject-config.json, with a single batch
confirmation gate. Facts only - constitution rules are never auto-filled. Addspaths.layersto
templates/project-config.template.json./sd:adrcommand (11th) +sd-docs-writeragent (6th) - drafts a numbered, MADR-style Architecture
Decision Record under.specs/_adr/from a spec's03-decisions.md(or an ad-hoc decision), behind one
hard approval gate. The agent (modelsonnet, tools Read/Write/Glob/Grep, skillsd-evidence-citation)
writes only the ADR file and never invents decisions; the command owns numbering and supersession links.
Bumps command count 10 -> 11 and agent count 5 -> 6 across docs and the validators.scripts/smoke-hooks.sh+scripts/smoke-hooks.ps1- pipe fixture Claude Code hook JSON into
prompt-router,spec-gate, andsubagent-retroagainst a temp.specs/tree and assert exit
codes AND key output substrings, not just "did not crash": keyword-match routing (bash and
PowerShell must agree), spec-gate allow/warn/block across in-progress / header-only-marker /
docs-edit / malformed-stdin cases, and subagent-retro naming the real spec ID then debouncing a
second run..github/workflows/ci.ymladdsmacos-latestto the OS matrix (exercising the
BSD-specificstat -f %m/date -j -ffallback branches that only run there) and a smoke-test
step on every OS.
Changed
- Removed hardcoded MSSQL/C#/TS references from
agents/debugger.md,commands/perf.md,
commands/rca.md, andcommands/bug.md, per CLAUDE.md's stack-agnostic rule.sd-debugger's
tool allowlist no longer bakes inmcp__mssql__execute_sql; its "Database discipline" section
(renamed from "MSSQL discipline") now describes the same read-only SELECT/EXPLAIN discipline
generically, deferring to whatever database MCP tool or CLI client the project provides.
templates/project-config.template.json'smcp.mssqlentry is renamed tomcp.database.
perf.md/rca.mdgeneralize "MSSQL access (via MCP)" to "database access (via the project's
MCP tool or CLI)";perf.md's final-review check drops the C#/TS-specificdynamic/any
example in favor of "type-safety escapes for the project's language (as defined in
constitution.md)";bug.md's failing-test step now referencespaths.testsfrom
project-config instead of a hardcodedtests/<mirrored path>/with a C#-style example name. - De-duplicated rules that were copy-pasted from skills into agent bodies and commands (CLAUDE.md:
"a rule used by multiple agents lives in oneSKILL.md, never copy-pasted"), replacing each
copy with a reference to the owning skill:agents/debugger.md's andagents/reviewer.md's
Anti-patterns sections no longer restatesd-hypothesis-tree/sd-severity-taxonomy/
sd-evidence-citation(role-specific bullets are kept);agents/code-explorer.md's
Anti-patterns section no longer restatessd-evidence-citation.commands/feature.mdand
commands/refactor.mdno longer inline the atomic task-block format - both now point at
sd-atomic-task-format, which gains a documented "Refactor mode"Parallel batchfield (the
fieldrefactor.md's inline copy had already drifted to include whilefeature.md's copy
lacked it).commands/bug.mdandcommands/rca.mdno longer restate the 5-mental-models /
(Likelihood x Impact) / Cost-to-verifymethod inline - both now point atsd-hypothesis-tree. scripts/validate.shandscripts/validate.ps1now derive their expected install-target
counts (commands / agents / skills / hooks / templates) from the source tree instead of
hardcoding them as literals in both files - a new command/agent/skill/template only needs to
land in its source dir, never a constant bumped in two scripts (this already bit PR #12, which
had to bump both). Each derived count is asserted> 0so an empty or misnamed source dir
fails loudly instead of vacuously passing Check 5.
Fixed
hooks/bash/prompt-router.shemitted- /sd:0instead of- /sd:<workflow>under bash 3.2
(macOS system bash):declare -Ais a bash-4 feature, so the associative arrays silently
degraded to indexed arrays with all string subscripts arithmetic-evaluating to0. Caught by
the macOS CI smoke test (validate (macos-latest)was red since the matrix gained macOS).
Rewrote keyword matching with parallel indexed arrays; the hook is now bash-3.2 compatible.hooks/powershell/subagent-retro.ps1's debounce silently stopped persisting/reading state on
PowerShell 7+, found by writingscripts/smoke-hooks.ps1: (1)Save-State's
Split-Path -LiteralPath $StatePath -Parentthrows "Parameter set cannot be resolved" on some
PS7 builds (-LiteralPaththere has no-Parentparameter set) - the surroundingtry/catch
swallowed it, so the state directory/file were never written; switched toSplit-Path -Path
(safe here --Parentdoes no filesystem globbing, only-Resolvewould). (2) Even once the
state file wrote,Test-DebounceElapsedre-broke: PS7'sConvertFrom-Jsonauto-converts an
ISO-8601...Zstring to a[datetime](PS 5.1 leaves it as a string), and re-Parse-ing an
already-converted[datetime]stringifies it with the local culture - dropping the UTC marker -
so[datetimeoffset]::Parsesilently re-interpreted it as local time, skewing$ageby the
machine's UTC offset exactly like the bug fixed earlier in this file, just triggered a different
way. Both are PowerShell-only;hooks/bash/subagent-retro.shwas unaffected (no bash twin
change needed).install/install.shhardening: aligned toset -euo pipefail(wasset -eonly, so unset-
variable typos and mid-pipeline failures - e.g. asha256sum/shasumerror - passed silently;
those two pipelines now end|| truesince a hash-tool failure is expected-recoverable, not a
reason to abort); added the same--prefixsafety guarduninstall.shalready had (empty,
/,\, or..components rejected) toinstall/install.ps1too, so install and uninstall
accept the same set of prefixes on both platforms - previously only uninstall validated it, so
--prefix ../evilwould have written outside the intended tree; quoted the unquoted
rel="${f#$src_root/}"strip pattern (glob-interpreted$src_rootbroke on a repo path
containing[,*, or?); and added anERRtrap that reports how many files already
landed and the exactuninstall.shcommand to run if a copy fails mid-install (no full
transactional rollback - per-file.bak.*backups already protect overwritten files).- Post-1.3.0 docs drift:
README.md's tagline said "Ten slash commands, five specialized
subagents" (now eleven / six); the Commands table was missing/sd:adrand listed
/sd:featureat 4 hard gates (the merged review+integration gate makes it 3); the Agents table
was missingsd-docs-writerand listed a hardcodedMSSQLtool forsd-debugger; the.specs/
tree diagram omitted_explorations/,_reviews/,_adr/; the Roadmap highlights repeated two
items that already shipped.ROADMAP.md's## Plannedsection still listed the/sd:setup
codebase scan andsd-docs-writeragent, both shipped in 1.3.0+ (CHANGELOG is the source of
truth for shipped work).docs/usage.md's Utility commands section had no/sd:adrentry.
templates/project-config.template.json'sworkflow.gates.featurestill listed the pre-merge
4-gate sequence; collapsed to 3 and marked_comment-descriptive since no hook or command reads
the block.CONTRIBUTING.md's agent frontmatter example omitted the mandatedcolor:and
skills:fields.examples/README.mdgated a promised-features list on "not in v1.0.0", three
minor versions after v1.0.0; reworded to point atROADMAP.md. - Phase 0 of
/sd:feature,/sd:bug,/sd:refactor,/sd:perf, and/sd:rcanow guards
against missing or malformed Layer-2 context instead of silently readingCLAUDE.md,
.specs/constitution.md,.claude/project-config.json, and.specs/index.mdand letting
later phases fail on undefined config values. Missing.specs/,.specs/constitution.md, or
.specs/index.mdnow STOPs with "No.specs/found - run/sd:setupfirst." (matching
spec.md/release.md/adr.md); malformed.claude/project-config.jsonSTOPs naming the file;
a missingCLAUDE.mdonly WARNs and continues, since the constitution (notCLAUDE.md) is the
binding Layer-2 contract - matching the stance the four utility commands already took. sd-code-explorer'simpact-maptask no longer instructs the agent to APPEND to
OUTPUT_APPEND_TO- its tool allowlist has noWrite/Edit, so it physically could not
perform that write, silently starving03-decisions.md(and everything downstream that reads
it asIMPACT). The task now returns the structured analysis as final output; the informational
OUTPUT_TARGETinput names the file, and the calling command appends it.commands/feature.md
andcommands/refactor.mdeach gained an explicit main-thread append step after the impact-map
invocation.commands/perf.md,commands/bug.md, andcommands/rca.md's equivalent
"Append ... to03-decisions.md" steps aftersd-debuggerinvocations (also write-tool-less)
are now explicitly labeled as main-thread steps for the same reason./sd:bug, `/sd:rca...
v1.3.0
Release-tooling and CI hardening. Adds the /sd:release command (10th), a single-command repo invariant validator with a Windows + Ubuntu CI matrix, an uninstaller, and a batch of command/agent refinements.
Added
/sd:releasecommand (10th command) - generates release notes from completed specs: collects every spec indonestatus (feature / bug / refactor / perf; RCA excluded), groups them into Keep-a-Changelog sections (FEAT -> Added, BUG -> Fixed, REF/PERF -> Changed) under an inferred SemVer heading (any feature -> minor bump, else patch; major never auto-inferred), then transitions eachdone -> archived. One hard gate previews the notes and the archive plan before any write;--dry-runstops before writing. Pure file ops, no subagent (mirrors/sd:spec). Gives thedone(merged, unshipped) vsarchived(shipped) states a concrete meaning.scripts/validate.ps1+scripts/validate.sh- one command that runs every documented engine invariant: pure-ASCII scan of*.ps1,bash -non*.sh, hook-pair parity, agentmodel:alias-only check, install-target file counts (real install to a temp base), and a non-empty[Unreleased]CHANGELOG gate. Exit 1 on any failure..github/workflows/ci.yml- runsvalidateon push/PR across a Windows + Ubuntu matrix, plus an install -> uninstall round-trip per theCLAUDE.mdsandbox recipe.install/uninstall.ps1+install/uninstall.sh- removes the five<base>/<area>/sd/directories with dry-run preview, confirmation prompt (-Force/--forceto skip), and per-project cleanup reminders (.claude/settings.jsonhook wiring,.claude/.hookstate/).
Changed
scripts/validate.{ps1,sh}Check 6 now treats an empty[Unreleased]section as passing when the section immediately below it is a dated[x.y.z] - <date>release heading (the freshly cut version), so a clean post-release CHANGELOG no longer fails CI. A non-release-state empty[Unreleased]still fails, preserving the "every PR adds a changelog line" invariant./sd:perfGate 6 now structurally refuses a no-measurable-gain "keep" instead of merely warning about it in prose. The gate branches on the noise check: a measurable improvement still offerskeep/revert, but a within-noise result defaults torevertand allowskeeponly as an explicit logged constitution exception (decisionkept (exception)+ a reason recorded to05-retro.md). With no reason supplied, the change is reverted.docs/architecture.mdgains two reference sections: a "Command -> agent routing" tree showing the subagent fan-out per command (and the three file-ops commands that invoke none), and an "Artifact ownership" table mapping each.specs/<ID>/file to its producing phase/agent and downstream readers. Consolidates routing/ownership that previously lived only in scattered command files./sd:setupQ1 and thesd-spec-architectticket protocol now state explicitly that automatic ticket-context fetch is JIRA-only: GitHub Issues and Linear are still recorded as the project tracker (for prompt-hook ID recognition), but their ticket content is not auto-fetched - paste it into the prompt instead. The ticket snapshot protocol is documented as JIRA-specific. Closes the silent degradation where non-JIRA projects got no ticket fetch and no explanation.
Fixed
/sd:spec statusnow spells out the illegal-transition refusal instead of the vague "REFUSED with explanation": it prints the current state, the requested state, the valid next state(s) from the state machine, and the shortest legal path to the requested state when reachable (e.g.draft -> doneis rejected with the hintdraft -> approved -> in-progress -> done). No file is mutated on refusal./sd:bugPhase 3 no longer assumes a confirmed root cause always arrives. The investigation loop previously said "Continue until one is CONFIRMED" with no exit, so a bug whose every hypothesis is rejected/inconclusive had no defined stopping point. Added Gate 3a (hypothesis tree exhausted): the loop now terminates on a CONFIRMED hypothesis OR an exhausted tree, and the exhausted case STOPs and asks the user to re-enumerate (with new evidence), add observability, or abort as "root cause not found" - never guessing a fix from an unconfirmed tree.install/install.shmarked executable (mode100755, matchinguninstall.sh); it was100644, so the documented./install/install.shinvocation failed with "Permission denied" on a fresh Linux checkout. Surfaced by the new CI round-trip.install/README.md: total file count corrected (21 -> 32),skills/sd/added to the layout tree, install table, and manual uninstall commands (skills were missed since 1.1.0), and the-Prefix/--prefixoption documented.
v1.1.0 — Agent Skills + dual-format hooks
Spec-driven development workflows for Claude Code.
Nine slash commands, five specialized subagents, three guard-rail hooks, nine templates, five reusable skills — all under thesd:namespace, stack-agnostic, cross-platform, and ready to drop into any project.
This is the initial public release on the specwright name. It bundles the rebranded engine (originally ck-spec-system) with the v1.1.0 architecture refresh: a new Agent Skills layer and a forward-compatible upgrade to the spec-gate hook contract.
Highlights
- Agent Skills layer (new) — 5 reusable markdown rule packs that agents reference from YAML frontmatter. De-duplicates rules across agents, shrinks prompt bodies, and makes canonical rules auditable in one place.
- Dual-format spec-gate output — emits both the new
hookSpecificOutput.permissionDecisionand the legacydecisionfield. The same hook works across Claude Code CLI versions; no runtime version probing. - All assets namespaced under
sd:—/sd:feature,sd-reviewer,skills/sd/, etc. Removable without touching the rest of your~/.claude/. - Stack-agnostic — agents read
CLAUDE.mdandconstitution.mdat runtime; no hardcoded language, framework, or layer assumptions. - Cost-aware models — Sonnet for reasoning (spec, debug, review), Haiku for execution (implementer, explorer). Typical feature run lands around $2–$3.
What's included
| Component | Count | Names |
|---|---|---|
| Slash commands | 9 | /sd:feature, /sd:bug, /sd:rca, /sd:refactor, /sd:perf, /sd:spec, /sd:explore, /sd:review, /sd:setup |
| Subagents | 5 | sd-spec-architect, sd-code-explorer, sd-debugger, sd-implementer, sd-reviewer |
| Skills (new in 1.1.0) | 5 | sd-severity-taxonomy, sd-hypothesis-tree, sd-atomic-task-format, sd-evidence-citation, sd-spec-templates |
| Hooks | 3 | prompt-router, spec-gate (dual-format), subagent-retro |
| Templates | 9 | 4 setup + 5 spec (feature / bug / refactor / perf / rca) |
Cross-platform: matching PowerShell + bash pairs for every hook. Content-hash-deduped installer with timestamped backups and dry-run preview.
New in 1.1.0
Agent Skills (new skills/sd/ layer)
A skill is a markdown rule pack referenced by agents from YAML frontmatter:
---
name: sd-reviewer
model: sonnet
skills:
- sd-severity-taxonomy
- sd-evidence-citation
---| Skill | Used by | Purpose |
|---|---|---|
sd-severity-taxonomy |
sd-reviewer |
Severity rules (BLOCK / WARN / SUGGEST / PASS) and the mandatory review output format. |
sd-hypothesis-tree |
sd-debugger |
Enumerate / verify protocol with the 5 mental models, (L × I) / C score formula, and the proximate-vs-root "why" ladder. |
sd-atomic-task-format |
sd-spec-architect, sd-implementer |
The 9-field atomic task block plus canonical enums (Step type, Complexity, Reversibility). |
sd-evidence-citation |
sd-code-explorer, sd-debugger, sd-reviewer |
file:line citation discipline, snippet length, evidence taxonomy, grouping. |
sd-spec-templates |
sd-spec-architect |
Per-template authoring rules (feature / bug / refactor / perf / rca); which cross-phase fields to leave empty. |
A skill is not an agent. It cannot be invoked directly, has no tools of its own, and produces no output on its own. It is a context block the agent inherits.
Agent frontmatter
- All 5 agents declare a
skills: [...]list. - All 5 agents declare a
color:field for terminal rendering. - Agent prompt bodies shrank where content moved into a referenced skill (lookup discipline preserved).
Hooks — dual-format spec-gate block output
spec-gate.sh and spec-gate.ps1 now emit a single JSON object that carries both schemas:
{
"decision": "block",
"reason": "spec-gate: editing code file 'src/foo.cs' but no in-progress spec is recorded ...",
"hookSpecificOutput": {
"permissionDecision": "deny",
"reason": "spec-gate: editing code file 'src/foo.cs' but no in-progress spec is recorded ..."
}
}- New schema (
hookSpecificOutput.permissionDecision = "deny") is read by recent CLI builds. - Legacy schema (
decision = "block") is read by older CLI builds. - Both are harmless to the other reader. No version probing required.
Installer
- Both
install/install.shandinstall/install.ps1now wireskills/<prefix>/alongsidecommands/,agents/,hooks/,templates/. - Dry-run verified: 31 files planned on a fresh install.
--prefixflag (defaults tosd) lets you namespace under a different prefix if you collide with another tool.
Documentation
README.md— new Skills section + updated Layer 1 diagram.docs/architecture.md— new Agent skills section + dual-format hook schema note.docs/history/— archivedUPGRADE-PLAN.md+REMAINING-WORK.mdso future readers can see how this upgrade was scoped (6 phases, file-touch matrix, decision log).
Install
Windows (PowerShell 5.1+):
git clone https://github.com/developzoneio/specwright.git
cd specwright
.\install\install.ps1 -DryRun # preview
.\install\install.ps1 # install to $env:USERPROFILE\.claudemacOS / Linux (bash 4+):
git clone https://github.com/developzoneio/specwright.git
cd specwright
./install/install.sh --dry-run # preview
./install/install.sh # install to ~/.claudeThen in any project:
cd <your-project>
claude
> /sd:setup
/sd:setup is interactive (3 questions at most) and scaffolds CLAUDE.md, .specs/, and .claude/project-config.json. See install/README.md for advanced options.
Compatibility
| Component | Tested on | Notes |
|---|---|---|
| Claude Code CLI | Latest as of June 2026 | Dual-format spec-gate keeps older CLIs working too. |
| Windows 11 | PowerShell 5.1 and 7.x | Hooks are pure ASCII for CP1252 safety. |
| macOS 13+ | bash 4+ via Homebrew | stat -f %m syntax supported. |
| Ubuntu 22.04+ | bash 5 | stat -c %Y syntax supported. |
| jq | 1.6+ | Optional; bash hooks exit 0 if missing. |
Optional MCP servers (all fall back gracefully if missing): Atlassian, Context7, sequential-thinking, GitNexus, MSSQL, Playwright, Tavily.
Upgrading from a pre-rebrand ck-spec-system install
If you previously installed under the ck/ namespace:
- Run the new installer — it lays down
~/.claude/<*>/sd/alongside the oldck/folders (your old install is untouched). - In each project, re-run
/sd:setup(it regenerates.claude/settings.jsonwith hook paths pointing athooks/sd/*). - Once you're satisfied the
sd:install works, remove the old~/.claude/<*>/ck/folders manually.
The dual-format hook output means you don't need to pin a specific Claude Code CLI version.
Full changelog
See CHANGELOG.md — [1.1.0] for this release, [1.0.0] for the rebrand baseline.
Acknowledgements
Inspired by the spec-driven discipline of long-running software teams, and by the BMAD method for structuring AI-assisted workflows. Built on top of Claude Code by Anthropic.
Commits in this release carry a Co-Authored-By trailer crediting Claude Opus 4.7 (1M context).