Skip to content

fix(adapters): stop deleting the CoStrict and Junie commands on every run - #1492

Merged
clay-good merged 1 commit into
Fission-AI:mainfrom
mc856:fix/legacy-cleanup-current-output-collision
Aug 4, 2026
Merged

fix(adapters): stop deleting the CoStrict and Junie commands on every run#1492
clay-good merged 1 commit into
Fission-AI:mainfrom
mc856:fix/legacy-cleanup-current-output-collision

Conversation

@mc856

@mc856 mc856 commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

LEGACY_SLASH_COMMAND_PATHS lists artifacts older OpenSpec versions left behind, so init and update delete whatever matches. Two of its 24 entries name paths the current adapters still write to.

CoStrict — the entry was { type: 'directory', path: '.cospec/openspec/commands' }, the folder adapters/costrict.ts:20 writes opsx-<id>.md into. Every run removes that directory and everything in it, including files the user keeps there, under a heading that reads No user content to preserve.

Junie.junie/commands/opsx-*.md is exactly what adapters/junie.ts:20 writes. Both halves of the entry arrived in #853 one file apart, so it has collided with itself since day one. The consequence is worse than churn: cleanup runs before migrateIfNeeded, so on a config that has no profile key yet — the state after a first init — the missing command files make inferDelivery (core/migration.ts:422) read the project as skills-only and persist that to the global config. The files are not regenerated, and the delivery preference changes for every other project too.

Repro, fresh HOME and fresh project:

openspec init --tools costrict,junie --force    # 6 skills and 6 commands
# put a file of your own in .cospec/openspec/commands/
openspec init --tools costrict,junie --force    # 6 skills, no commands

On main the second run ends with zero command files for both tools, the user's file gone, and global delivery flipped to skills. openspec update runs the same migrate-then-cleanup pair and is equally exposed.

Fix

CoStrict becomes a file pattern, .cospec/openspec/commands/openspec-*.md. The only files that folder held before the opsx rename were openspec-proposal.md, openspec-apply.md and openspec-archive.md, written by the slash configurator added in #240 and dropped in #565 — the two revisions of configurators/slash/costrict.ts are byte-identical on that point, and README_OLD.md:107 documents the same three.

Junie's entry is removed rather than narrowed. Junie support landed in #853, months after #565 deleted the configurators that wrote openspec-* files, and no junie configurator ever existed — .junie/commands/openspec-*.md is a shape OpenSpec never produced. The same reasoning already keeps .devin/ off the list two lines above, and the suite already asserts the principle for pi.

Test

The regression test is an invariant rather than a fixture: it writes every registered adapter's output for every workflow into a temp project and asserts detectLegacySlashCommands reports nothing. Before the fix it reports the whole .cospec/openspec/commands directory plus every .junie/commands/opsx-*.md; after, both lists are empty. It names codex — the one legacy id with no adapter — instead of silently skipping it, and asserts no adapter path is absolute so a future global-scoped adapter fails loudly rather than being filtered out. Genuinely legacy files are still detected and removed, and the near-misses already in the table (opencode's singular command/, qwen's .toml) would fire immediately if either drifted.

pnpm lint, pnpm build and the suite are clean; the one failure on my machine (test/commands/workset.test.ts:758) reproduces identically on unmodified main and is environmental.

No other tool's patterns change — they never overlapped their adapter's current output.


Written and reviewed with Claude Code using claude-opus-5.

Summary by CodeRabbit

  • Bug Fixes

    • Legacy cleanup now removes only outdated CoStrict command files, preserving current and user-created files.
    • Prevented cleanup from incorrectly treating the entire CoStrict commands directory as legacy.
    • Removed ineffective Junie cleanup handling.
    • Improved detection to avoid misidentifying current command adapter outputs as legacy artifacts.
  • Documentation

    • Added release documentation for the legacy cleanup corrections.

… run

LEGACY_SLASH_COMMAND_PATHS lists artifacts older OpenSpec versions left
behind, so init and update remove whatever matches. Two entries named
paths the current adapters still write to.

`costrict` was a whole-directory entry for `.cospec/openspec/commands`,
the folder its adapter writes `opsx-<id>.md` into, so every run deleted
the directory and everything in it — including files the user put there
— under a heading reading 'No user content to preserve'. It is now a
file pattern for `.cospec/openspec/commands/openspec-*.md`: the only
files that folder ever held before the opsx rename were
openspec-proposal.md, openspec-apply.md and openspec-archive.md, written
by the slash configurator added in Fission-AI#240 and dropped in Fission-AI#565.

`junie` listed `.junie/commands/opsx-*.md`, its adapter's own output,
next to `openspec-*.md`. Both halves arrived in Fission-AI#853 one file apart, so
the entry has collided with itself since day one. Cleanup runs before
migrateIfNeeded, so on a config with no `profile` key yet — the state
after a first init — the deleted command files make inferDelivery read
the project as skills-only and write that to the global config. The
files are not regenerated, and the delivery preference changes for every
other project too.

The entry is removed rather than narrowed. Junie support landed in Fission-AI#853,
months after Fission-AI#565 deleted the slash configurators that wrote
`openspec-*` files, and no junie configurator ever existed — so
`.junie/commands/openspec-*.md` is a shape OpenSpec never produced. The
same reasoning already keeps `.devin/` off the list two lines above.

The regression test is an invariant rather than a fixture — it writes
every registered adapter's output for every workflow into a temp project
and asserts detection reports nothing — and it names codex, the one
legacy id with no adapter, instead of silently skipping it.

Nothing else changes. The surviving `openspec-*` globs stay as broad as
they have always been, since narrowing them to the three ids the pre-opsx
configurators actually wrote is a separate, uniform change, and qwen's
`opsx-*.toml` pair stays because its adapter emits Markdown now.
@mc856
mc856 requested a review from a team as a code owner August 2, 2026 16:41
@mc856
mc856 requested review from clay-good and removed request for a team August 2, 2026 16:41
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The cleanup configuration now matches only legacy CoStrict openspec-*.md files and excludes Junie. Tests verify detection, selective deletion, preservation of current and user files, and exclusion of adapter-generated command paths.

Changes

Legacy command cleanup

Layer / File(s) Summary
Cleanup path configuration
.changeset/legacy-cleanup-live-command-files.md, src/core/legacy-cleanup.ts
CoStrict cleanup uses a file-scoped openspec-*.md glob. The Junie legacy path is removed.
Detection and cleanup validation
test/core/legacy-cleanup.test.ts
Tests verify legacy file detection, selective deletion, preservation of current and user files, adapter output exclusion, and Junie absence.

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

Possibly related PRs

Suggested reviewers: clay-good, alfred-openspec

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: preventing cleanup from deleting current CoStrict and Junie command files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/core/legacy-cleanup.test.ts`:
- Around line 401-404: Use platform-neutral path expectations in
test/core/legacy-cleanup.test.ts at lines 401-404 and 651-652 by constructing
the CoStrict file, directory, and cleanup result paths with path.join(). If
detectLegacySlashCommands() returns POSIX paths, normalize both actual and
expected values before comparison, and add Windows coverage verifying the path
contract.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 70dc6368-ecee-4bd9-80f7-bf325f2d40b2

📥 Commits

Reviewing files that changed from the base of the PR and between 45cca5d and fad5962.

📒 Files selected for processing (3)
  • .changeset/legacy-cleanup-live-command-files.md
  • src/core/legacy-cleanup.ts
  • test/core/legacy-cleanup.test.ts

Comment thread test/core/legacy-cleanup.test.ts

@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.

Verified at fad5962: the CoStrict cleanup is now file-scoped, Junie no longer self-matches current output, and repeated init/update runs preserve current commands, a user-owned CoStrict file, and delivery: both. Isolated build plus 1,378 focused tests and the hosted CI/security matrix all pass.

@clay-good
clay-good added this pull request to the merge queue Aug 4, 2026
Merged via the queue into Fission-AI:main with commit 690a27e Aug 4, 2026
13 checks passed
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.

3 participants