Skip to content

fix(extensions): scaffold new config templates on extension update - #5011

Open
yxshee wants to merge 1 commit into
github:mainfrom
yxshee:fix/extension-update-scaffold-config
Open

yxshee wants to merge 1 commit into
github:mainfrom
yxshee:fix/extension-update-scaffold-config

Conversation

@yxshee

@yxshee yxshee commented Oct 17, 2026

Copy link
Copy Markdown

Description

When a new extension version adds a provides.config template, specify extension update installs it but never creates the project's <ext>-config.yml. A fresh specify extension add of the same version does create it. Bundle install and refresh do too, since #4285 fixed the same gap there (#4283).

specify extension enable can't fill the gap either, because it exits early with "already enabled". So an updated project silently lacks a config file that the new version expects.

Root cause: run_update_command() in _command_update_transaction.py installs the new archive and copies the backed-up config files back, but never calls ExtensionManager.scaffold_config(). The other install paths all do: command_add.py, command_enable.py and bundles/primitives.py.

The fix calls manager.scaffold_config(extension_id) right after the backed-up configs are restored. Existing files are therefore skipped, so a user's customized config is never replaced. Deployed and failed templates are reported under the "Updated to" line, with the same wording as extension add. A rollback restores the extension directory, which also removes anything scaffolded.

Reproduction with the real CLI against a local catalog (SPECKIT_CATALOG_URL=http://localhost:8765/...) serving demo 1.0.0 (no config), then 1.1.0, which declares demo-config.yml from config-template.yml:

# main
$ specify extension add demo             # 1.0.0
$ echo y | specify extension update       # catalog now offers 1.1.0
   ✓ Updated to v1.1.0
✓ Successfully updated 1 extension(s)
$ ls .specify/extensions/demo
commands  config-template.yml  extension.yml

# this branch
$ echo y | specify extension update
   ✓ Updated to v1.1.0
   Config scaffolded: .specify/extensions/demo/demo-config.yml
✓ Successfully updated 1 extension(s)
$ ls .specify/extensions/demo
commands  config-template.yml  demo-config.yml  extension.yml

For comparison, specify extension add demo straight at 1.1.0 on main prints Config scaffolded: • .specify/extensions/demo/demo-config.yml.

Testing

  • Tested locally with uv run specify --help
  • Ran existing tests with uv sync && uv run pytest
  • Tested with a sample project (if applicable): the local-catalog reproduction above, on main and on this branch

New tests in tests/specify_cli/extensions/test_command_update.py. They use a real zip archive and the real install path; only the catalog lookup and download are patched.

  • test_update_scaffolds_config_declared_by_new_version
  • test_update_keeps_existing_config_over_new_template: a customized config isn't replaced
  • test_update_warns_when_declared_config_cannot_be_scaffolded

On main, the first and third fail and the second passes. The first fails with test-ext-config.yml missing. The third fails because "Config templates not scaffolded" is missing from the output. All three pass with the change, and the whole file gives 23 passed.

  • Focused suites (tests/specify_cli/extensions, tests/test_extensions.py, tests/specify_cli/bundles): 1589 passed.
  • Full suite (macOS, Python 3.13): 12333 passed, 376 skipped. 11 failures in the tests/test_*_python_parity.py files occur identically on unmodified main on this machine under full-suite load; those files pass when run on their own (257 passed, 112 skipped).
  • uvx ruff@0.15.0 check src tests: clean

Note: open PR #5005 edits a hunk just above this insertion point for a different concern (re-registering commands on update), so at most a trivial conflict.

AI Disclosure

  • I did not use AI assistance for this contribution
  • I did use AI assistance (fill in the disclosure below)

AI disclosure: Claude Code (model: Claude Opus 5.5, max reasoning effort, autonomous agent mode) opened this PR on behalf of @yxshee. A Claude Code subagent (same model, autonomous) found the bug and wrote the fix and tests. The lead agent independently re-ran the new tests against main and the branch, reproduced the before and after behavior with the real CLI, reviewed the diff, and wrote this description. No human reviewed the change line by line before it was opened.

🤖 Generated with Claude Code

`extension add` and `extension enable` deploy the templates an extension
declares in `provides.config`, and bundle install and refresh do the
same (github#4285), but `extension update` never called scaffold_config. When
a new version introduces a config template, the updated project has no
`<ext>-config.yml` while a fresh install of that version does, and
`extension enable` cannot deploy it either: it stops early because the
extension is already enabled.

Scaffold once the install has restored the project's existing configs,
so those are skipped exactly as before, and report deployed and failed
templates under the update line, as `extension add` does.

Assisted-by: Claude Code (model: Claude Opus 5.5, autonomous)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yxshee
yxshee requested a review from mnriem as a code owner October 17, 2026 19:11

This branch has not been deployed

No deployments
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.

1 participant