Skip to content

fix(templates): stop the bulk archive when the user picks Cancel#1398

Open
clay-good wants to merge 1 commit into
mainfrom
fix/bulk-archive-honor-cancel
Open

fix(templates): stop the bulk archive when the user picks Cancel#1398
clay-good wants to merge 1 commit into
mainfrom
fix/bulk-archive-honor-cancel

Conversation

@clay-good

@clay-good clay-good commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Status: LGTM — ready for review.

What was wrong

The generated openspec-bulk-archive-change skill (and the matching opsx:bulk-archive command) offered you a Cancel option at the confirmation prompt:

7. **Confirm batch operation**
   - Options might include:
     - "Archive all N changes"
     - "Archive only N ready changes (skip incomplete)"
     - "Cancel"

8. **Execute archive for each confirmed change**
   Process changes in the determined order...

Step 7 collected your answer, but nothing ever told the agent what to do with it. Step 8 followed immediately and archived every selected change. An agent reading the skill literally would mv your changes into archive/ after you explicitly cancelled — and the only trace of the option was a vague "Skipped: user chose not to archive (if applicable)" bullet three steps later, after the move already happened.

The single-change archive skill had the same hole; it was fixed in #1357 (46a4d78) and reworded into its current routing block by #1394. The bulk variant was left behind. Neither has shipped yet — both are post-v1.6.0.

How it was fixed

Both template bodies (skill and opsx command) now route the answer:

Route on the answer by intent, not by exact label — you wrote these labels,
so match what the user picked rather than the wording above:
- "Cancel" — stop, do not archive. Report that nothing was archived and skip the remaining steps.
- The archive-everything option — proceed with every selected change
- 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.
- Anything else — ask again rather than archiving

Four deliberate details, each from an adversarial review finding:

  • Routing is by intent, not by literal label. Step 7 says options "might include" those labels, and they contain an N placeholder the agent fills in ("Archive all 3 changes"). Matching the literal strings would have sent every legitimate answer into "Anything else — ask again", trading a destructive bug for an infinite re-prompt.
  • "Ready" is bound to where it's decided — the step 6 status table, including the conflict-flagged Ready* row. Otherwise the agent can't tell which subset to archive.
  • A skipped conflict partner invalidates its recorded resolution. Ready-only can archive one side of a conflict while its partner stays active; without re-deriving, step 8a's "apply in resolved order" would sync the skipped change's deltas too.
  • The Cancel wording matches the single-change skill verbatim ("Cancel" — stop, do not archive), so one assertion guards all four templates — and "skip the remaining steps" also rules out printing a "Bulk Archive Complete" summary after a cancel.

A guardrail line was added where agents skim: Never archive after the user cancels the confirmation — a cancelled batch archives nothing.

Proof it works

Two regression tests in test/core/templates/skill-templates-parity.test.ts:

  • honors Cancel at every archive confirmation (#1381) — covers all four templates (bulk skill, bulk command, single skill, single command). The single-change routing has been correct since feat(skills): publish workflow skills to skills.sh #1357 but was never pinned by a test; this closes the whole issue rather than half of it.
  • routes the bulk archive confirmation by intent, not by literal label (#1381) — pins the intent-based wording, the ready-only binding, and the guardrail.

Both verified failing-then-passing. With the template change reverted:

× honors Cancel at every archive confirmation (#1381)
  → bulk skill: expected '---\nname: openspec-bulk-archive-chan…' to contain '"Cancel" — stop, do not archive'
× routes the bulk archive confirmation by intent, not by literal label (#1381)
  → bulk skill: expected '---\nname: openspec-bulk-archive-chan…' to contain 'Route on the answer by intent, not by…'

Full suite after npm run build: 2029 passed / 2046, 104 of 105 files green. The sole failing file is test/core/completions/installers/zsh-installer.test.ts (17 failures) — the known environment-only oh-my-zsh issue noted in CLAUDE.md and tracked in #1321, untouched by this branch and green in CI.

Notes / nits

Closes #1381

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Bulk archive now properly honors “Cancel”—no changes are archived.
    • Confirmation routing is now based on selected intent (not exact label text): Archive all archives everything; Archive only ready archives only Ready/Ready* items.
    • Non-matching items are recorded as Skipped; if a Ready* item’s conflict partner is skipped, conflict resolution is re-derived from the archived set.
    • If the confirmation response is unrecognized, the user is re-prompted instead of proceeding, consistently across bulk and single flows.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Bulk archive instructions now stop on “Cancel”, proceed for the two archive options, and re-prompt for other responses. Generated template hashes and parity tests are updated accordingly.

Changes

Bulk archive confirmation

Layer / File(s) Summary
Confirmation response routing
.changeset/bulk-archive-honors-cancel.md, skills/openspec-bulk-archive-change/SKILL.md, src/core/templates/workflows/bulk-archive-change.ts
Bulk archive guidance explicitly stops on “Cancel”, archives all or only ready changes for the corresponding options, and re-prompts for unrecognized responses.
Generated template parity validation
test/core/templates/skill-templates-parity.test.ts
Expected template hashes are updated, and parity tests verify cancellation, re-prompting, and intent-based routing text in generated variants.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: tabishb

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The linked issue targets openspec-archive-change and opsx/archive, but this PR only updates bulk-archive templates and tests. Update the archive-change skill and archive command templates so Cancel aborts before any archive step, or narrow the linked issue scope.
✅ Passed checks (4 passed)
Check name Status Explanation
Out of Scope Changes check ✅ Passed The changes stay focused on bulk-archive confirmation flow, template generation, hashes, and regression tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: bulk archive now stops when Cancel is selected.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/bulk-archive-honor-cancel

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@clay-good
clay-good force-pushed the fix/bulk-archive-honor-cancel branch from 0eaff77 to 3c6c3e8 Compare July 20, 2026 20:12
The bulk archive confirmation offered a "Cancel" option but never told
the agent what to do with it. Step 8 then archived every selected
change, so an agent following the skill literally moved the changes
even after the user cancelled.

Route each answer by intent rather than by literal label: the option
labels are written by the agent and carry an `N` placeholder, so
matching them verbatim would send every legitimate answer down the
"ask again" path. Cancel now stops without archiving and skips the
remaining steps, the ready-only option is bound to the status table
that decides what "ready" means (re-deriving conflict resolutions when
a Ready* partner is skipped), and a guardrail repeats that a cancelled
batch archives nothing.

Regression tests cover both archive paths, so the single-change
routing can no longer be silently reverted either.

Instruction text only — no CLI behavior changes.

Closes #1381

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@clay-good
clay-good force-pushed the fix/bulk-archive-honor-cancel branch from 3c6c3e8 to 078a2f5 Compare July 20, 2026 20:28

@alfred-openspec alfred-openspec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The routing closes the destructive fall-through in both bulk templates: Cancel exits before sync or move, unknown responses re-prompt, and ready-only selection re-derives conflicts without skipped changes. All nine focused parity tests pass locally at 078a2f5 and the exact-head CI/CodeQL matrix is green, so this is approved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generated openspec-archive-change skill archives even when the user selects Cancel

2 participants