Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
642aa4c
feat(cli): plan archive-and-sync-parity change
replygirl Oct 5, 2026
d8fb061
test(cli): add archive and sync-specs fixtures
replygirl Oct 5, 2026
5f0c0b7
test(cli): pin archive parity differentials as failing rows
replygirl Oct 5, 2026
400d36b
test(cli): pin sync-specs differentials as failing rows
replygirl Oct 5, 2026
a16f718
fix(validate): let REMOVED on a new capability pass as OpenSpec does
replygirl Oct 5, 2026
c12e66a
refactor(cli): share the scenario-preservation gate
replygirl Oct 5, 2026
6410745
feat(cli): answer every archive refusal with one JSON document
replygirl Oct 5, 2026
b08ec8b
feat(cli): refuse a namespace folder as OpenSpec archive does
replygirl Oct 5, 2026
5cb8c60
fix(cli): answer an unreadable archive directory as OpenSpec does
replygirl Oct 5, 2026
5be4f82
feat(cli): add OpenSpec's archive and root keys to archive's JSON
replygirl Oct 5, 2026
e54901c
fix(cli): spell relayed archive remedies as cospec
replygirl Oct 5, 2026
caa2f82
feat(cli): accept archive --no-validate and keep every hard gate
replygirl Oct 5, 2026
be6d5bb
feat(cli): copy a root's spec inputs to a scratch tree and back
replygirl Oct 5, 2026
ac78cea
feat(cli): add sync-specs, archive's merge without the archive
replygirl Oct 5, 2026
6f98772
feat(canon): sync specs through cospec sync-specs
replygirl Oct 5, 2026
e5f3da3
docs(cli): document archive parity and sync-specs
replygirl Oct 5, 2026
c3a0cdb
docs(agents): name sync-specs in the cospec workflow
replygirl Oct 5, 2026
8a9d405
docs(cli): record archive-and-sync-parity evidence
replygirl Oct 5, 2026
f484743
test(cli): pin sync-specs refusing a skip_specs change with deltas
replygirl Oct 5, 2026
4a180ff
fix(cli): refuse a skip_specs change with deltas in sync-specs
replygirl Oct 5, 2026
c4f0f08
docs(cli): fix post-rebase shas in archive-and-sync-parity ledger
replygirl Oct 5, 2026
87fadfb
fix(cli): revalidate before sync-specs answers nothing to sync
replygirl Oct 5, 2026
b1acdd8
fix(cli): fingerprint only the specs archive's merge can write
replygirl Oct 5, 2026
3b6969a
fix(cli): keep sync-specs' scratch run off the real tree through links
replygirl Oct 5, 2026
3dce4b8
test(cli): smoke-check the new archive-and-sync-parity fixtures
replygirl Oct 5, 2026
94bc763
docs(cli): close out archive-and-sync-parity before archive
replygirl Oct 5, 2026
1a2acb2
docs(cli): defer the advisory eval row for archive-and-sync-parity
replygirl Oct 5, 2026
2ef08a0
chore(archive): archive archive-and-sync-parity
replygirl Oct 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions .agents/shared.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,11 @@ through `cospec`:
`archive/scenario-preservation` — no `--force`), delegates to
`openspec archive`, verifies the move on disk, and fans out blocker sync.
Schemas with no specs artifact (`ci`, `chore`, `docs`, …) correctly produce
no spec-sync deltas here. **Timing is non-negotiable** — see "Branch, PR, and
merge flow" below.
no spec-sync deltas here. To land a change's main specs before it is done,
`mise run cospec -- sync-specs <slug>` runs archive's own merge on a scratch
copy and leaves the change active; its later archive is a no-op merge with
both hard gates still run. **Timing is non-negotiable** — see "Branch, PR,
and merge flow" below.

Steps 1–2 also have `/cospec:new` + `/cospec:ff`/`/cospec:continue` as
entry-point variants of `/cospec:propose`, and you can optionally dress-rehearse
Expand Down Expand Up @@ -240,17 +243,21 @@ fixture proving it reads the view it is registered under. A new rule or gate
takes the verbatim view unless no commented line can ever trigger it.

**JSON documents are additive** — a cospec `--json` document that mirrors an
OpenSpec command (`status`, `list`, `validate`, and `archive --json` next)
carries every key upstream's document does: computed natively where cospec owns
the fact (`validate`'s report keys), otherwise from one delegated call per
OpenSpec command (`status`, `list`, `validate`, `archive`) carries every key
upstream's document does: computed natively where cospec owns the fact
(`validate`'s report keys; `archive`'s `archive`/`root`, read from what the
wrapped human-mode archive reported and what cospec verified on disk, and its
failure documents, one per refusal), otherwise from one delegated call per
invocation — never one per change — merged by identity through `mergeUpstream`
(`apps/cli/src/core/upstream-keys.ts`). No cospec key is removed and no cospec
value changed; every envelope keeps `version: 1`. The key oracle
(`apps/cli/test/contract/support/key-oracle.ts`, rows in `cli-surface.test.ts`)
is the gate: it fails on a missing upstream key, an upstream value reported
differently, a changed pre-existing cospec key, and any collision outside its
named list (`NAMED_COLLISIONS`: `version`, validate's `items[].type`), each
entry stating why cospec's value wins.
value changed; every envelope that carried `version: 1` keeps it. The key oracle
(`apps/cli/test/contract/support/key-oracle.ts`, rows in `cli-surface.test.ts`
and `archive-no-validate.test.ts`) is the gate: it fails on a missing upstream
key, an upstream value reported differently, a changed pre-existing cospec key,
and any collision outside its named list (`NAMED_COLLISIONS`: `version`,
validate's `items[].type`, and archive's tasks-gate `status[].fix`, where
cospec's `--force-incomplete` wins because `--yes`, a no-op under cospec, does
not lift its stricter gate), each entry stating why cospec's value wins.

**Wrapped-call discipline** — every call into the wrapped binary declares its
expected exit codes, a stdout deny-list, and an observable post-condition. Trust
Expand Down
14 changes: 10 additions & 4 deletions .agents/skills/cospec-archive-change/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ compatibility: Requires the cospec CLI (@aligned-team/cospec).
metadata:
author: cospec
generatedBy: cospec@0.8.3
contentHash: sha256:5c738047656ddb62b491db73be4646970619cfe5f01aee6779924b5bd8ef3373
contentHash: sha256:06ced83cc52c3802650079fd3a3c303bdc97302d803b45574762e7f5bd67a1eb
---

Archive a completed change. `cospec archive` validates it, merges its spec
Expand All @@ -29,9 +29,15 @@ Relay the summary it prints verbatim: what was archived, which spec deltas were
applied (`+a ~m -r →n`) or skipped, which sibling changes had blocker boxes
checked, and which changes are now unblocked.

A change that introduces a brand-new capability (no living spec yet) may only
ADD requirements there — `cospec validate` refuses a MODIFIED, REMOVED, or
RENAMED op targeting it before archive ever runs the merge.
A change that introduces a brand-new capability (no living spec yet) may ADD
requirements there, and a REMOVED there is a no-op the merge warns about;
`cospec validate` refuses a MODIFIED or RENAMED op targeting it before archive
ever runs the merge.

A change whose specs were synced early with `$cospec-sync-specs (Codex) or /cospec-sync-specs (other agents)` archives as a
no-op merge: the summary says the specs were already in sync, and both hard
gates (`archive/verification-incomplete`, `archive/scenario-preservation`) still
run.

## 3. On failure

Expand Down
60 changes: 37 additions & 23 deletions .agents/skills/cospec-sync-specs/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
---
name: cospec-sync-specs
description: Explain how spec sync works (it runs inside archive) and preview what would merge. Also use when the user says "cospec sync specs", "sync the specs", or "openspec sync".
description: Merge a change's delta specs into the main specs without archiving it, exactly as archive would. Also use when the user says "cospec sync specs", "sync the specs", or "openspec sync".
license: MIT
compatibility: Requires the cospec CLI (@aligned-team/cospec).
metadata:
author: cospec
generatedBy: cospec@0.8.3
contentHash: sha256:1bfa89a12c71041a0dfa9dc59c5007a6cae904ca8a880cb87dbaad91fa4b4814
contentHash: sha256:629fe8bb821e930ca0b8c1ebb26381f8a60b6bc20453a4691a4df9f5d169e8c7
---

Explain and preview spec synchronization. Spec sync is not a standalone step in
cospec.
Merge a change's delta specs into the main specs under `openspec/specs/` without
archiving the change. `cospec sync-specs` runs the archive's own merge — the
pinned OpenSpec archive, on a scratch copy of the specs — and copies back only
the main-spec files it changed, so the result is byte-for-byte what
`cospec archive` would write. The change stays active, and its later archive is
a no-op merge.

Delta specs in a change are merged into the living specs under `openspec/specs/`
**only** by `cospec archive`, which applies the merge and then verifies it as
one coupled operation. There is no supported mid-flight "sync now without
archiving" path. This is deliberate: a partial merge would leave a tree that
neither validates nor archives cleanly.
## 1. Select the change

## Preview what would merge
If the user named one, use it. Otherwise run `cospec list --json`: if exactly
one active change exists, use it and announce `Using change: <slug>`; if more
than one is plausible, ask.

If the user did not name a change, run `cospec list --json`: if exactly one
active change exists, use it and announce `Using change: <slug>`; if more than
one is plausible, ask.
## 2. Preview the merge

```
cospec validate <slug>
Expand All @@ -31,13 +31,26 @@ cospec validate <slug>
This runs the archive-precondition checks (targets exist, no zero-op deltas, no
ADDED collisions, scenarios are well-formed) and reports anything that would
make the merge fail. Then read the delta files under
`openspec/changes/<slug>/specs/**/spec.md` to see the exact ADDED / MODIFIED /
REMOVED / RENAMED operations.
`openspec/changes/<slug>/specs/**/spec.md` and tell the user which main specs
the sync will create, change or delete: the ADDED / MODIFIED / REMOVED / RENAMED
operations per capability, and any retirement (below) with its marker.

A delta that targets a capability with no living spec yet may only ADD
requirements — any MODIFIED, REMOVED, or RENAMED op there is a validate-time
ERROR (`archive/new-spec-non-added`), not something that surfaces later at merge
time.
A delta that targets a capability with no living spec yet may ADD requirements
there, and a REMOVED there is a no-op the merge warns about; a MODIFIED or
RENAMED op targeting it is a validate-time ERROR (`archive/new-spec-non-added`).

## 3. Sync

```
cospec sync-specs <slug>
```

Relay what it prints: one `Synced:` line per main-spec file written or deleted
and the merge's totals, or that the specs were already in sync. The merge is the
archive's own, so a refusal here is the refusal archive would give — relay it
verbatim and fix what it names; never edit a main spec by hand to get past it.
Nothing is written when it refuses, and the change is never archived by this
step.

## Retiring a capability

Expand All @@ -49,8 +62,9 @@ the marker the merge refuses and reports the missing marker as the blocking
condition. Deleting the file also deletes its `## Purpose` — name both when you
report a retirement, and give the user a way to recover the file.

## Actually sync
## Afterwards

Run `$cospec-archive-change (Codex) or /cospec-archive-change (other agents)` when the change is complete. The merge happens there, is
verified, and blocker check-offs fan out automatically. To sanity-check the
living specs on their own, run `cospec validate --specs`.
The change is still active: finish its tasks and verification, then run
`$cospec-archive-change (Codex) or /cospec-archive-change (other agents)`. Its merge finds the specs already in sync, and both hard
archive gates still run. To sanity-check the living specs on their own, run
`cospec validate --specs`.
14 changes: 10 additions & 4 deletions .claude/commands/cospec/archive.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ tags:
metadata:
author: cospec
generatedBy: cospec@0.8.3
contentHash: sha256:d31ab736702e834b863f53218615046ce0d07111014acda12131333653f2a56a
contentHash: sha256:4ef8b0e5e83be85e5cf8f1c811e6947bbaeaec8a6df188fa96609d6a46aa220f
---

Archive a completed change. `cospec archive` validates it, merges its spec
Expand All @@ -31,9 +31,15 @@ Relay the summary it prints verbatim: what was archived, which spec deltas were
applied (`+a ~m -r →n`) or skipped, which sibling changes had blocker boxes
checked, and which changes are now unblocked.

A change that introduces a brand-new capability (no living spec yet) may only
ADD requirements there — `cospec validate` refuses a MODIFIED, REMOVED, or
RENAMED op targeting it before archive ever runs the merge.
A change that introduces a brand-new capability (no living spec yet) may ADD
requirements there, and a REMOVED there is a no-op the merge warns about;
`cospec validate` refuses a MODIFIED or RENAMED op targeting it before archive
ever runs the merge.

A change whose specs were synced early with `/cospec:sync-specs` archives as a
no-op merge: the summary says the specs were already in sync, and both hard
gates (`archive/verification-incomplete`, `archive/scenario-preservation`) still
run.

## 3. On failure

Expand Down
60 changes: 37 additions & 23 deletions .claude/commands/cospec/sync-specs.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
---
name: "COSPEC: Sync specs"
description: Explain how spec sync works (it runs inside archive) and preview what would merge. Also use when the user says "cospec sync specs", "sync the specs", or "openspec sync".
description: Merge a change's delta specs into the main specs without archiving it, exactly as archive would. Also use when the user says "cospec sync specs", "sync the specs", or "openspec sync".
category: Workflow
tags:
- cospec
- workflow
metadata:
author: cospec
generatedBy: cospec@0.8.3
contentHash: sha256:8a7fceb611f7097e7ba242b56cc99aa60a68727d1afdc9e9137146742657d282
contentHash: sha256:c6671819783b5458bb4ceb83e6c0ea0716c94d919a1b813d5ef25fef16307d21
---

Explain and preview spec synchronization. Spec sync is not a standalone step in
cospec.
Merge a change's delta specs into the main specs under `openspec/specs/` without
archiving the change. `cospec sync-specs` runs the archive's own merge — the
pinned OpenSpec archive, on a scratch copy of the specs — and copies back only
the main-spec files it changed, so the result is byte-for-byte what
`cospec archive` would write. The change stays active, and its later archive is
a no-op merge.

Delta specs in a change are merged into the living specs under `openspec/specs/`
**only** by `cospec archive`, which applies the merge and then verifies it as
one coupled operation. There is no supported mid-flight "sync now without
archiving" path. This is deliberate: a partial merge would leave a tree that
neither validates nor archives cleanly.
## 1. Select the change

## Preview what would merge
If the user named one, use it. Otherwise run `cospec list --json`: if exactly
one active change exists, use it and announce `Using change: <slug>`; if more
than one is plausible, ask.

If the user did not name a change, run `cospec list --json`: if exactly one
active change exists, use it and announce `Using change: <slug>`; if more than
one is plausible, ask.
## 2. Preview the merge

```
cospec validate <slug>
Expand All @@ -33,13 +33,26 @@ cospec validate <slug>
This runs the archive-precondition checks (targets exist, no zero-op deltas, no
ADDED collisions, scenarios are well-formed) and reports anything that would
make the merge fail. Then read the delta files under
`openspec/changes/<slug>/specs/**/spec.md` to see the exact ADDED / MODIFIED /
REMOVED / RENAMED operations.
`openspec/changes/<slug>/specs/**/spec.md` and tell the user which main specs
the sync will create, change or delete: the ADDED / MODIFIED / REMOVED / RENAMED
operations per capability, and any retirement (below) with its marker.

A delta that targets a capability with no living spec yet may only ADD
requirements — any MODIFIED, REMOVED, or RENAMED op there is a validate-time
ERROR (`archive/new-spec-non-added`), not something that surfaces later at merge
time.
A delta that targets a capability with no living spec yet may ADD requirements
there, and a REMOVED there is a no-op the merge warns about; a MODIFIED or
RENAMED op targeting it is a validate-time ERROR (`archive/new-spec-non-added`).

## 3. Sync

```
cospec sync-specs <slug>
```

Relay what it prints: one `Synced:` line per main-spec file written or deleted
and the merge's totals, or that the specs were already in sync. The merge is the
archive's own, so a refusal here is the refusal archive would give — relay it
verbatim and fix what it names; never edit a main spec by hand to get past it.
Nothing is written when it refuses, and the change is never archived by this
step.

## Retiring a capability

Expand All @@ -51,8 +64,9 @@ the marker the merge refuses and reports the missing marker as the blocking
condition. Deleting the file also deletes its `## Purpose` — name both when you
report a retirement, and give the user a way to recover the file.

## Actually sync
## Afterwards

Run `/cospec:archive` when the change is complete. The merge happens there, is
verified, and blocker check-offs fan out automatically. To sanity-check the
living specs on their own, run `cospec validate --specs`.
The change is still active: finish its tasks and verification, then run
`/cospec:archive`. Its merge finds the specs already in sync, and both hard
archive gates still run. To sanity-check the living specs on their own, run
`cospec validate --specs`.
14 changes: 10 additions & 4 deletions .claude/skills/cospec-archive-change/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ compatibility: Requires the cospec CLI (@aligned-team/cospec).
metadata:
author: cospec
generatedBy: cospec@0.8.3
contentHash: sha256:d31ab736702e834b863f53218615046ce0d07111014acda12131333653f2a56a
contentHash: sha256:4ef8b0e5e83be85e5cf8f1c811e6947bbaeaec8a6df188fa96609d6a46aa220f
---

Archive a completed change. `cospec archive` validates it, merges its spec
Expand All @@ -29,9 +29,15 @@ Relay the summary it prints verbatim: what was archived, which spec deltas were
applied (`+a ~m -r →n`) or skipped, which sibling changes had blocker boxes
checked, and which changes are now unblocked.

A change that introduces a brand-new capability (no living spec yet) may only
ADD requirements there — `cospec validate` refuses a MODIFIED, REMOVED, or
RENAMED op targeting it before archive ever runs the merge.
A change that introduces a brand-new capability (no living spec yet) may ADD
requirements there, and a REMOVED there is a no-op the merge warns about;
`cospec validate` refuses a MODIFIED or RENAMED op targeting it before archive
ever runs the merge.

A change whose specs were synced early with `/cospec:sync-specs` archives as a
no-op merge: the summary says the specs were already in sync, and both hard
gates (`archive/verification-incomplete`, `archive/scenario-preservation`) still
run.

## 3. On failure

Expand Down
Loading
Loading