PresetResolver._manifest_declared_template returns the FIRST
'provides.templates' entry matching a given (name, type) pair:
for tmpl in manifest.templates:
if tmpl.get("name") == template_name and tmpl.get("type") == template_type:
...
return tmpl, ...
So a preset.yml declaring two templates with the same (name, type) --
e.g. two "command"/"specify" entries pointing at different files -- had
its second entry silently unreachable, while PresetManifest.templates
still counted and exposed both. PresetManifest._validate never checked
for this.
Reject the duplicate at manifest-validation time instead, matching the
sibling fix already applied to ExtensionManifest's provides.templates/
provides.scripts (commit 11e3176, PR github#4016): "The resolver returns the
first entry matching a declared name, so a later duplicate ... was
silently unreachable while still counted". Presets use a (name, type)
composite key rather than extensions' bare name, since the same name can
legitimately recur across different template types (e.g. a "specify"
template and a "specify" command); the fix only rejects a duplicate
within the exact same (name, type) pair.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary
PresetResolver._manifest_declared_template returns the first provides.templates entry matching a given (name, type) pair:
So a preset.yml declaring two templates with the same (name, type) — e.g. two command/specify entries pointing at different files — had its second entry silently unreachable, while PresetManifest.templates still counted and exposed both. PresetManifest._validate never checked for this:
Reject the duplicate at manifest-validation time instead, matching the sibling fix already applied to ExtensionManifest's provides.templates/provides.scripts (commit 11e3176, #4016): "The resolver returns the first entry matching a declared name, so a later duplicate ... was silently unreachable while still counted."
Presets use a (name, type) composite key rather than extensions' bare name, since the same name can legitimately recur across different template types (e.g. a template-type "specify" template and a command-type "specify" command) — the fix only rejects a duplicate within the exact same (name, type) pair.
Test plan
🤖 Generated with Claude Code