fix(presets): restore core skills instead of deleting them on preset remove - #3929
Conversation
…remove Skill restoration looked for core command templates under .specify/templates/commands, a directory specify init never populates in real projects. Since that lookup always missed, presets overriding a core command (e.g. speckit.plan) had their skill deleted outright on removal instead of restored — the actual core templates live in the bundled core_pack (wheel install) or the repo-root templates/ tree. Restoration now falls back to that bundled location, gated behind a restore_from_bundled_core flag so the existing "retire a stale skill superseded by a command-mode winner" path keeps deleting rather than resurrecting a duplicate skill. Fixes github#3928
There was a problem hiding this comment.
Pull request overview
Fixes preset removal so overridden core skills are restored from bundled templates.
Changes:
- Adds opt-in bundled-core restoration for preset cleanup paths.
- Adds regression coverage for projects without local core command templates.
Show a summary per file
| File | Description |
|---|---|
src/specify_cli/presets/__init__.py |
Adds bundled-core skill restoration fallback. |
tests/test_presets.py |
Tests restoration without local templates. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 2/2 changed files
- Comments generated: 1
- Review effort level: Balanced
read_text() defaults to the platform locale encoding, which is cp1252
("charmap") on Windows. The bundled specify.md core template contains a
UTF-8 multi-byte emoji whose bytes aren't valid cp1252, so the Windows CI
job failed decoding the restored SKILL.md with UnicodeDecodeError.
|
Fixed the Windows CI failure: the new test read the restored SKILL.md with |
The bundled-core fallback added for github#3928 ran before the extension_restore_index lookup, so a skill an installed extension owns could be silently replaced by lower-priority bundled core content on preset removal instead of preserving the extension's winning layer.
|
Addressed the inline review comment: the bundled-core fallback now skips itself when |
|
Thank you! |
Fixes #3928
Bug
When a preset that overrides core commands/skills is removed, the corresponding
core skills are deleted instead of being restored — the only way to get them
back is to re-scaffold with
specify init.Root cause
PresetManager._unregister_skills_in_dirlooks for the original core commandtemplate at
self.project_root / ".specify" / "templates" / "commands". Thatdirectory is never populated by
specify initin a real project — the actualcore command templates live in the bundled
core_pack/commands/directory(wheel install) or the repo-root
templates/commands/tree (source checkout),exactly like
PresetResolver.resolve_core()already falls back to via_locate_core_pack()/_repo_root().Because the lookup always missed, every preset-removal restoration fell
through to the "no core template found" branch and deleted the skill outright.
Fix
Restoration now falls back to the bundled core_pack/repo-root templates when
the project-local override directory doesn't have the file. This fallback is
gated behind a new
restore_from_bundled_coreflag so the pre-existing "retirea stale skill because its command now renders as a command file elsewhere"
path (triggered by
ai_skillsmode toggles) keeps its original deletebehavior — it must not resurrect a duplicate skill that a command file has
already superseded.
Repro (before the fix)
After the fix, all 10 skills are present again, restored with core content
after
specify preset remove lean.Testing
Added
test_skill_restored_on_preset_remove_without_project_core_templates,which (unlike the existing
test_skill_restored_on_preset_remove) does notpre-seed
.specify/templates/commandswith a fake core file, matching what areal
specify initproject looks like. Confirmed it fails without the fix(
AssertionError: Core skill must be restored, not deleted) viagit checkout HEAD~1 -- src/specify_cli/presets/__init__.py, then passes withthe fix restored.
AI disclosure
This PR was authored primarily by an AI coding agent (Claude Code): it
diagnosed the root cause, implemented the fix, and wrote/verified the
regression test. I reviewed the diff and test output before submitting.