Hardening round. Four defects, all reproduced before being fixed. **Two shipped changes touching one capability lost a fold.** `applyFolds` evaluated every change against the pre-write baseline and then wrote them all, so each rebuilt body was a whole file derived from the original spec and the second write erased the first — silently, while the console reported both as applied. The changes did not conflict; the batch read a stale baseline. Archive never had this because it takes one change per invocation. Sync now folds one change at a time, re-deriving each against the specs as they are at that moment. **Two capability ids resolving to one file overwrote each other.** A capability directory may deliberately be a symlink, so this is a shape the trust model allows rather than an accident. Archive refuses it outright; sync wrote both and lost one. Archive's check is now shared by both, so they cannot disagree about which trees they will write. **`sync --check` was green for a change whose delta the writer refuses.** The check only asked "is what was discovered folded?", and `discoverSpecFiles` does not walk `specs/spec.md`, so a change whose only delta sat there certified as clean while archive and the sync writer both refused the same tree (#1385). Delta validation now runs inside the evaluation, so it runs on the check path too — and it asks archive's own question about whether a change has deltas at all, so a zero-delta change gets the same answer from both commands. **`--ship` could fold and then fail forever.** A change with no `.openspec.yaml` had its specs written and its stamp refused, and the rerun failed in the same place, so the ordering's usual self-correction did not apply. Checked up front now. Also: `--no-validate` requires `--yes`, matching archive's refusal to skip validation without an explicit answer; the rollback no longer "restores" targets it never wrote (a false data-loss alarm) and refuses to clobber a file something else changed mid-run; and `test/core/sync.test.ts` restores the process working directory before removing its temp tree, which Windows locks. Nineteen tests added, each mutation-verified. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1.2 KiB
@fission-ai/openspec
| @fission-ai/openspec |
|---|
| minor |
Add openspec sync, which folds a change's delta specs into the main specs without archiving it, and an optional status: proposed | shipped field in a change's .openspec.yaml.
openspec sync --check gates on one property: a change that claims to be shipped has its deltas in specs/. A proposed change passes for free, so the check is green as its resting state and red only on a real mistake — unlike a check for "is everything archived?", which is red for the whole life of every open pull request. It reads only files on disk, so a pre-commit hook, a pre-push hook and CI run the same command and agree.
openspec list --status <state> filters changes by that field.
Everything here is opt-in and inert by default. The status field is absent unless a project writes it, nothing generates it, and archive is unchanged.
Designed by @ixxie in #1683 — the diagnosis that archive welds a state transition to a text merge, shipped ⇒ folded as a predicate over the working tree, and the standalone sync that makes it checkable. This ships a smaller, additive subset of that proposal.