Skip to content

fix(presets): reject duplicate provides.templates name+type entries - #4191

Open
Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/preset-duplicate-template-name-type
Open

fix(presets): reject duplicate provides.templates name+type entries#4191
Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/preset-duplicate-template-name-type

Conversation

@Noor-ul-ain001

Copy link
Copy Markdown
Contributor

Summary

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:

>>> PresetManifest(preset_yml_with_two_command_specify_entries)
# No error — accepted, both entries kept in .templates, but the resolver
# can only ever reach the first one.

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

  • Added test_duplicate_template_name_and_type_raises_validation_error and test_same_name_different_type_templates_allowed to tests/test_presets.py
  • Verified the duplicate-rejection test fails without the fix (DID NOT RAISE) and passes with it; verified the same-name-different-type test passes both before and after (no false positive)
  • pytest tests/test_presets.py — 602 passed, 8 pre-existing failures unrelated to this change (symlink-elevation tests that require admin rights on Windows, per this repo's documented Windows test-environment gaps), 2 skipped
  • ruff check on both changed files — clean

🤖 Generated with Claude Code

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>
@Noor-ul-ain001
Noor-ul-ain001 requested a review from mnriem as a code owner August 18, 2026 16:59
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