SpecSync 6.0 is out, and check is still the product
Leif ·
SpecSync 6.0.0 is out. GitHub Latest points at it, and crates.io has specsync 6.0.0. The Homebrew tap is still on 5.2.0, so if you brew install today you do not get six yet. Install from cargo or a release binary until the formula catches up, and confirm with specsync --version.
Update, 2026-09-10. The tap has caught up.
Formula/spec-sync.rbnow serves 6.0.0, sobrew install CorvidLabs/tap/spec-syncgets you six. The paragraph above describes the state on the day this was published.
I am not rewriting the pitch. The first post about this tool was specs checked both ways, and that is still the whole idea. Spec and code can each drift from the other. So you check both ways, and CI fails when they disagree. 6.0 is the release where I finally made that the only thing you have to adopt.
Check is the product
specsync check is the product. Not the change lifecycle. Not the interview. Not the archive.
A fresh specsync init writes .specsync/sdd.json with the verified change workflow off. It does not start a first-change interview. It does not demand an active change before you can edit a source file. You scaffold a module contract, fill the Public API table, and run:
specsync check --strict
Ship an export the spec never named, and you get a warning that becomes an error under --strict. Write a spec for a symbol that is gone, and you get a hard error. Same tool, both directions, same asymmetry as before: a lying spec is worse than an incomplete one.
That is enough for a lot of repos. Specs as CI-enforced contracts, companion files for context, coverage and scoring when you want them. If that is all you came for, stop here. You are done.
SDD is opt-in
The verified change workflow is the other half of SpecSync, and in 6.0 it stays off until you ask for it.
specsync change adopt
That flips enabled in .specsync/sdd.json and leaves the rest alone. require_change_for_meaningful_files stays false unless you turn it on yourself. Upgrading the binary does not flip an existing policy off, and a fresh init does not force you into the lifecycle. Check and change are related, but they are not the same product. I kept getting that wrong in earlier releases by making init feel like a commitment to the whole machine.
I wrote about what happens when that machine and a squash-only repo fight each other in the explanation we never tested. 6.0 is the cleanup that came out of living in that mess: one path, fewer verbs that lie about the next step, and an on-switch you can see.
One path, slug ids
When you do turn SDD on, there is one user-facing path from scope to merge:
specsync change new "Add passkeys" --spec auth --path src/auth.rs
# answer the interview, write the deltas
specsync change approve add-passkeys --actor "Ada"
# implement
specsync change check add-passkeys --commit
# push the product tip, wait for CI, finish human review
specsync change review add-passkeys --reviewer "Ada"
specsync change ship add-passkeys
# no commit between review and ship; push the archive, wait, then merge
finalize is the same end as ship when you want the verb that says archive. Review and ship are one step: recording review binds a workspace digest, so committing between them stales the review. Finalize one change at a time. Do not merge a PR while any change on it is still active.
Change ids are slugs now, minted from the description. add-passkeys, not CHG-0042. Nothing allocates a CHG-NNNN ordinal anymore. Two clones that pick the same description mint the same slug and only meet at merge, so give each change a distinct description. change new refuses a slug already in use and names the existing change. That is the whole identity story.
change check compares the specs this change owns or maps to the code. It does not run your test suite. Product tests stay in CI, next to specsync check. Scoped structural verification is evidence that the contract held for what you claimed; it is not a substitute for cargo test or bun test.
Pin both halves of the Action
If you use the GitHub Action, pin the Action and the binary:
- uses: CorvidLabs/spec-sync@v6.0.0
with:
version: '6.0.0'
strict: 'true'
require-coverage: '100'
Both pins matter and they pin different things. The uses ref pins the Action code. The version input pins the binary it downloads. The tagged Action at @v6.0.0 still defaults version to 6.0.0-rc.14, leftover from the candidate window while stable was pending. Omit the input and CI quietly runs an RC. Do not float @v6 for a release gate either. Prefer the immutable @v6.0.0 pin with an explicit version: '6.0.0'.
Same rule for every lifecycle writer: developer machines, agents, hooks, and CI should be on the same 6.x binary. Older 5.x writers can reject slug-based changes or drop newer record fields. specsync --version in each environment before you resume active work.
Trust 1.2.0 follows
Trust 1.2.0 is out with SpecSync 6.0.0 as the default contract toolchain. Normal consumers leave specsync-version alone and get 6.0.0. If you need another exact GitHub-released SpecSync, set it explicitly. Trust installs that binary before lifecycle verification so a random specsync on PATH cannot win.
The gate is still the same shape it has been: contract, then risk, then provenance. SpecSync says the contract holds. augur scores the diff. attest records who signed off. 6.0 does not invent a fifth gate. It makes the first one honest about what you opted into.
What I want you to take
If you already run SpecSync for bidirectional check, upgrade the binary, pin the Action the way the README shows, and keep running specsync check --strict. That is the product. The companion files, the coverage number, the agent skill that runs the same CLI you do: those stay.
If you want the verified change workflow, turn it on with change adopt and drive one real change through new → approve → check --commit → review → ship before you trust the setup. Merge only after every active change on the PR is archived.
If you are installing for the first time: cargo, Homebrew, or a GitHub release binary all serve 6.0.0. Read ADOPTING.md if you are handing this to an agent. It is written to be pasted into a session.
I built SpecSync because I wanted specs that fail the build when they lie, in both directions, for a person and an agent the same way. 6.0 ships that without making you marry the lifecycle on day one. Check is the product. Everything else is optional, and now it says so.