Skip to content

Commit 206522d

Browse files
committed
fix(archive): make command and bulk archive paths root-aware, synchronous, and verified
1 parent 456f006 commit 206522d

4 files changed

Lines changed: 105 additions & 43 deletions

File tree

skills/openspec-bulk-archive-change/SKILL.md

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -122,19 +122,28 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig
122122
so match what the user picked rather than the wording above:
123123
- "Cancel" — stop, do not archive. Report that nothing was archived and skip the remaining steps.
124124
- The archive-everything option — proceed with every selected change
125-
- The ready-only option — proceed with only the changes the step 6 table marks `Ready` or `Ready*`, and record the rest as Skipped in step 8c. If a `Ready*` change's conflict partner is skipped, re-derive that conflict's resolution using only the changes being archived.
125+
- The ready-only option — proceed with only the changes the step 6 table marks `Ready` or `Ready*`, and record the rest as Skipped in step 8d. If a `Ready*` change's conflict partner is skipped, re-derive that conflict's resolution using only the changes being archived.
126126
- Anything else — ask again rather than archiving
127127

128128
8. **Execute archive for each confirmed change**
129129

130130
Process changes in the determined order (respecting conflict resolution):
131131

132132
a. **Sync specs** if delta specs exist:
133-
- Use the openspec-sync-specs approach (agent-driven intelligent merge)
134-
- For conflicts, apply in resolved order
135-
- Track if sync was done
133+
- Run the `openspec-sync-specs` workflow inline (agent-driven intelligent merge) for each change, passing the delta spec analysis, and wait for it to finish.
134+
- For conflicts, apply in resolved order.
135+
- Do not delegate to a background task — step 8c would move `changeRoot` out from under a sync that is still reading it.
136136

137-
b. **Perform the archive**:
137+
b. **Verify main specs before moving changeRoot**:
138+
- Re-run the comparison against every capability that has a delta spec in `artifactPaths.specs.existingOutputPaths` against main spec at `<planningHome.root>/openspec/specs/<capability>/spec.md` (use the store-aware `planningHome.root` from step 3 status JSON, not a hardcoded repo path).
139+
- Verify that main specs are updated:
140+
- ADDED requirements present
141+
- MODIFIED requirements carrying scenario and description changes named in the delta, with their other scenarios intact
142+
- REMOVED requirements gone
143+
- RENAMED requirements present under the new name and absent under the old one
144+
- If sync failed or any capability does not match verification, report what differs and fail/skip moving that change's `changeRoot` — do not archive that change. `changeRoot` remains intact.
145+
146+
c. **Perform the archive**:
138147

139148
Target name: use the change name as-is when it already starts with a `YYYY-MM-DD-` prefix; otherwise prepend the current date as `YYYY-MM-DD-<name>` (same rule as `openspec archive`).
140149

@@ -143,9 +152,9 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig
143152
mv "<changeRoot>" "<planningHome.changesDir>/archive/<target-name>"
144153
```
145154

146-
c. **Track outcome** for each change:
155+
d. **Track outcome** for each change:
147156
- Success: archived successfully
148-
- Failed: error during archive (record error)
157+
- Failed: error during archive or spec verification (record error)
149158
- Skipped: user chose not to archive (if applicable)
150159

151160
9. **Display summary**
@@ -178,7 +187,7 @@ This skill allows you to batch-archive changes, handling spec conflicts intellig
178187

179188
Example 1: Only one implemented
180189
```text
181-
Conflict: specs/auth/spec.md touched by [add-oauth, add-jwt]
190+
Conflict: <planningHome.root>/openspec/specs/auth/spec.md touched by [add-oauth, add-jwt]
182191
183192
Checking add-oauth:
184193
- Delta adds "OAuth Provider Integration" requirement
@@ -193,7 +202,7 @@ Resolution: Only add-oauth is implemented. Will sync add-oauth specs only.
193202

194203
Example 2: Both implemented
195204
```text
196-
Conflict: specs/api/spec.md touched by [add-rest-api, add-graphql]
205+
Conflict: <planningHome.root>/openspec/specs/api/spec.md touched by [add-rest-api, add-graphql]
197206
198207
Checking add-rest-api (created 2026-01-10):
199208
- Delta adds "REST Endpoints" requirement
@@ -257,3 +266,5 @@ No active changes found. Create a new change to get started.
257266
- Preserve .openspec.yaml when moving to archive
258267
- Archive directory target uses current date: YYYY-MM-DD-<name>; a name that already starts with a `YYYY-MM-DD-` prefix is used as-is (never stack a second date)
259268
- If archive target exists, fail that change but continue with others
269+
- If sync is requested, run the `openspec-sync-specs` workflow inline (agent-driven) for each change
270+
- Never archive a change while a spec sync is still in flight — run the sync inline and verify main specs at `<planningHome.root>/openspec/specs/<capability>/spec.md` before moving `changeRoot`

src/core/templates/workflows/archive-change.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ ${STORE_SELECTION_GUIDANCE}
207207
- "Sync now" or "Sync anyway" — sync, then verify (below)
208208
- Anything else — ask again rather than archiving
209209
210-
To sync, run the \`openspec-sync-specs\` workflow inline (agent-driven intelligent merge) for change '<name>', passing the delta spec analysis from above, and wait for it to finish. Do not delegate it to a background task — step 5 would move \`changeRoot\` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
210+
To sync, run the \`/opsx:sync\` workflow inline (agent-driven intelligent merge) for change '<name>', passing the delta spec analysis from above, and wait for it to finish. Do not delegate it to a background task — step 5 would move \`changeRoot\` out from under a sync that is still reading it, leaving the change archived and the main specs never updated. If your agent can only run it by delegation, delegate synchronously and wait for the result.
211211
212212
Then re-run the comparison from the top of this step against every capability that has a delta spec in \`artifactPaths.specs.existingOutputPaths\` — not only the ones the sync reports it touched. A successful sync leaves nothing left to apply, so each capability must now read as already synced:
213213
- ADDED requirements present
@@ -309,7 +309,7 @@ Target archive directory already exists.
309309
- Don't block archive on warnings - just inform and confirm
310310
- Preserve .openspec.yaml when moving to archive (it moves with the directory)
311311
- Show clear summary of what happened
312-
- If sync is requested, run the \`openspec-sync-specs\` workflow inline (agent-driven)
312+
- If sync is requested, run the \`/opsx:sync\` workflow inline (agent-driven)
313313
- Never archive while a spec sync is still in flight — run the sync inline and verify the main specs before moving \`changeRoot\`
314314
- If delta specs exist, always run the sync assessment and show the combined summary before prompting`
315315
};

src/core/templates/workflows/bulk-archive-change.ts

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -124,19 +124,28 @@ ${STORE_SELECTION_GUIDANCE}
124124
so match what the user picked rather than the wording above:
125125
- "Cancel" — stop, do not archive. Report that nothing was archived and skip the remaining steps.
126126
- The archive-everything option — proceed with every selected change
127-
- The ready-only option — proceed with only the changes the step 6 table marks \`Ready\` or \`Ready*\`, and record the rest as Skipped in step 8c. If a \`Ready*\` change's conflict partner is skipped, re-derive that conflict's resolution using only the changes being archived.
127+
- The ready-only option — proceed with only the changes the step 6 table marks \`Ready\` or \`Ready*\`, and record the rest as Skipped in step 8d. If a \`Ready*\` change's conflict partner is skipped, re-derive that conflict's resolution using only the changes being archived.
128128
- Anything else — ask again rather than archiving
129129
130130
8. **Execute archive for each confirmed change**
131131
132132
Process changes in the determined order (respecting conflict resolution):
133133
134134
a. **Sync specs** if delta specs exist:
135-
- Use the openspec-sync-specs approach (agent-driven intelligent merge)
136-
- For conflicts, apply in resolved order
137-
- Track if sync was done
135+
- Run the \`openspec-sync-specs\` workflow inline (agent-driven intelligent merge) for each change, passing the delta spec analysis, and wait for it to finish.
136+
- For conflicts, apply in resolved order.
137+
- Do not delegate to a background task — step 8c would move \`changeRoot\` out from under a sync that is still reading it.
138138
139-
b. **Perform the archive**:
139+
b. **Verify main specs before moving changeRoot**:
140+
- Re-run the comparison against every capability that has a delta spec in \`artifactPaths.specs.existingOutputPaths\` against main spec at \`<planningHome.root>/openspec/specs/<capability>/spec.md\` (use the store-aware \`planningHome.root\` from step 3 status JSON, not a hardcoded repo path).
141+
- Verify that main specs are updated:
142+
- ADDED requirements present
143+
- MODIFIED requirements carrying scenario and description changes named in the delta, with their other scenarios intact
144+
- REMOVED requirements gone
145+
- RENAMED requirements present under the new name and absent under the old one
146+
- If sync failed or any capability does not match verification, report what differs and fail/skip moving that change's \`changeRoot\` — do not archive that change. \`changeRoot\` remains intact.
147+
148+
c. **Perform the archive**:
140149
141150
Target name: use the change name as-is when it already starts with a \`YYYY-MM-DD-\` prefix; otherwise prepend the current date as \`YYYY-MM-DD-<name>\` (same rule as \`openspec archive\`).
142151
@@ -145,9 +154,9 @@ ${STORE_SELECTION_GUIDANCE}
145154
mv "<changeRoot>" "<planningHome.changesDir>/archive/<target-name>"
146155
\`\`\`
147156
148-
c. **Track outcome** for each change:
157+
d. **Track outcome** for each change:
149158
- Success: archived successfully
150-
- Failed: error during archive (record error)
159+
- Failed: error during archive or spec verification (record error)
151160
- Skipped: user chose not to archive (if applicable)
152161
153162
9. **Display summary**
@@ -180,7 +189,7 @@ ${STORE_SELECTION_GUIDANCE}
180189
181190
Example 1: Only one implemented
182191
\`\`\`text
183-
Conflict: specs/auth/spec.md touched by [add-oauth, add-jwt]
192+
Conflict: <planningHome.root>/openspec/specs/auth/spec.md touched by [add-oauth, add-jwt]
184193
185194
Checking add-oauth:
186195
- Delta adds "OAuth Provider Integration" requirement
@@ -195,7 +204,7 @@ Resolution: Only add-oauth is implemented. Will sync add-oauth specs only.
195204
196205
Example 2: Both implemented
197206
\`\`\`text
198-
Conflict: specs/api/spec.md touched by [add-rest-api, add-graphql]
207+
Conflict: <planningHome.root>/openspec/specs/api/spec.md touched by [add-rest-api, add-graphql]
199208
200209
Checking add-rest-api (created 2026-01-10):
201210
- Delta adds "REST Endpoints" requirement
@@ -258,7 +267,9 @@ No active changes found. Create a new change to get started.
258267
- Track and report all outcomes (success/skip/fail)
259268
- Preserve .openspec.yaml when moving to archive
260269
- Archive directory target uses current date: YYYY-MM-DD-<name>; a name that already starts with a \`YYYY-MM-DD-\` prefix is used as-is (never stack a second date)
261-
- If archive target exists, fail that change but continue with others`,
270+
- If archive target exists, fail that change but continue with others
271+
- If sync is requested, run the \`openspec-sync-specs\` workflow inline (agent-driven) for each change
272+
- Never archive a change while a spec sync is still in flight — run the sync inline and verify main specs at \`<planningHome.root>/openspec/specs/<capability>/spec.md\` before moving \`changeRoot\``,
262273
license: 'MIT',
263274
compatibility: 'Requires openspec CLI.',
264275
metadata: { author: 'openspec', version: '1.0' },
@@ -384,19 +395,28 @@ ${STORE_SELECTION_GUIDANCE}
384395
so match what the user picked rather than the wording above:
385396
- "Cancel" — stop, do not archive. Report that nothing was archived and skip the remaining steps.
386397
- The archive-everything option — proceed with every selected change
387-
- The ready-only option — proceed with only the changes the step 6 table marks \`Ready\` or \`Ready*\`, and record the rest as Skipped in step 8c. If a \`Ready*\` change's conflict partner is skipped, re-derive that conflict's resolution using only the changes being archived.
398+
- The ready-only option — proceed with only the changes the step 6 table marks \`Ready\` or \`Ready*\`, and record the rest as Skipped in step 8d. If a \`Ready*\` change's conflict partner is skipped, re-derive that conflict's resolution using only the changes being archived.
388399
- Anything else — ask again rather than archiving
389400
390401
8. **Execute archive for each confirmed change**
391402
392403
Process changes in the determined order (respecting conflict resolution):
393404
394405
a. **Sync specs** if delta specs exist:
395-
- Use the openspec-sync-specs approach (agent-driven intelligent merge)
396-
- For conflicts, apply in resolved order
397-
- Track if sync was done
406+
- Run the \`/opsx:sync\` workflow inline (agent-driven intelligent merge) for each change, passing the delta spec analysis, and wait for it to finish.
407+
- For conflicts, apply in resolved order.
408+
- Do not delegate to a background task — step 8c would move \`changeRoot\` out from under a sync that is still reading it.
409+
410+
b. **Verify main specs before moving changeRoot**:
411+
- Re-run the comparison against every capability that has a delta spec in \`artifactPaths.specs.existingOutputPaths\` against main spec at \`<planningHome.root>/openspec/specs/<capability>/spec.md\` (use the store-aware \`planningHome.root\` from step 3 status JSON, not a hardcoded repo path).
412+
- Verify that main specs are updated:
413+
- ADDED requirements present
414+
- MODIFIED requirements carrying scenario and description changes named in the delta, with their other scenarios intact
415+
- REMOVED requirements gone
416+
- RENAMED requirements present under the new name and absent under the old one
417+
- If sync failed or any capability does not match verification, report what differs and fail/skip moving that change's \`changeRoot\` — do not archive that change. \`changeRoot\` remains intact.
398418
399-
b. **Perform the archive**:
419+
c. **Perform the archive**:
400420
401421
Target name: use the change name as-is when it already starts with a \`YYYY-MM-DD-\` prefix; otherwise prepend the current date as \`YYYY-MM-DD-<name>\` (same rule as \`openspec archive\`).
402422
@@ -405,9 +425,9 @@ ${STORE_SELECTION_GUIDANCE}
405425
mv "<changeRoot>" "<planningHome.changesDir>/archive/<target-name>"
406426
\`\`\`
407427
408-
c. **Track outcome** for each change:
428+
d. **Track outcome** for each change:
409429
- Success: archived successfully
410-
- Failed: error during archive (record error)
430+
- Failed: error during archive or spec verification (record error)
411431
- Skipped: user chose not to archive (if applicable)
412432
413433
9. **Display summary**
@@ -440,7 +460,7 @@ ${STORE_SELECTION_GUIDANCE}
440460
441461
Example 1: Only one implemented
442462
\`\`\`text
443-
Conflict: specs/auth/spec.md touched by [add-oauth, add-jwt]
463+
Conflict: <planningHome.root>/openspec/specs/auth/spec.md touched by [add-oauth, add-jwt]
444464
445465
Checking add-oauth:
446466
- Delta adds "OAuth Provider Integration" requirement
@@ -455,7 +475,7 @@ Resolution: Only add-oauth is implemented. Will sync add-oauth specs only.
455475
456476
Example 2: Both implemented
457477
\`\`\`text
458-
Conflict: specs/api/spec.md touched by [add-rest-api, add-graphql]
478+
Conflict: <planningHome.root>/openspec/specs/api/spec.md touched by [add-rest-api, add-graphql]
459479
460480
Checking add-rest-api (created 2026-01-10):
461481
- Delta adds "REST Endpoints" requirement
@@ -518,6 +538,8 @@ No active changes found. Create a new change to get started.
518538
- Track and report all outcomes (success/skip/fail)
519539
- Preserve .openspec.yaml when moving to archive
520540
- Archive directory target uses current date: YYYY-MM-DD-<name>; a name that already starts with a \`YYYY-MM-DD-\` prefix is used as-is (never stack a second date)
521-
- If archive target exists, fail that change but continue with others`
541+
- If archive target exists, fail that change but continue with others
542+
- If sync is requested, run the \`/opsx:sync\` workflow inline (agent-driven) for each change
543+
- Never archive a change while a spec sync is still in flight — run the sync inline and verify main specs at \`<planningHome.root>/openspec/specs/<capability>/spec.md\` before moving \`changeRoot\``
522544
};
523545
}

0 commit comments

Comments
 (0)