fix(adapters): stop deleting the CoStrict and Junie commands on every run - #1492
Conversation
… 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.
📝 WalkthroughWalkthroughThe cleanup configuration now matches only legacy CoStrict ChangesLegacy command cleanup
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
.changeset/legacy-cleanup-live-command-files.mdsrc/core/legacy-cleanup.tstest/core/legacy-cleanup.test.ts
alfred-openspec
left a comment
There was a problem hiding this comment.
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.
LEGACY_SLASH_COMMAND_PATHSlists artifacts older OpenSpec versions left behind, soinitandupdatedelete 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 folderadapters/costrict.ts:20writesopsx-<id>.mdinto. Every run removes that directory and everything in it, including files the user keeps there, under a heading that readsNo user content to preserve.Junie —
.junie/commands/opsx-*.mdis exactly whatadapters/junie.ts:20writes. 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 beforemigrateIfNeeded, so on a config that has noprofilekey yet — the state after a firstinit— the missing command files makeinferDelivery(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
HOMEand fresh project:On
mainthe second run ends with zero command files for both tools, the user's file gone, and globaldeliveryflipped toskills.openspec updateruns 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 wereopenspec-proposal.md,openspec-apply.mdandopenspec-archive.md, written by the slash configurator added in #240 and dropped in #565 — the two revisions ofconfigurators/slash/costrict.tsare byte-identical on that point, andREADME_OLD.md:107documents 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-*.mdis a shape OpenSpec never produced. The same reasoning already keeps.devin/off the list two lines above, and the suite already asserts the principle forpi.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
detectLegacySlashCommandsreports nothing. Before the fix it reports the whole.cospec/openspec/commandsdirectory plus every.junie/commands/opsx-*.md; after, both lists are empty. It namescodex— 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 singularcommand/,qwen's.toml) would fire immediately if either drifted.pnpm lint,pnpm buildand the suite are clean; the one failure on my machine (test/commands/workset.test.ts:758) reproduces identically on unmodifiedmainand 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
Documentation