Skip to content

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

Merged
mnriem merged 1 commit into
github:mainfrom
Noor-ul-ain001:fix/preset-duplicate-template-name-type
Aug 20, 2026
Merged

fix(presets): reject duplicate provides.templates name+type entries#4191
mnriem merged 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
@mnriem
mnriem requested a balanced review from Copilot August 20, 2026 17:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds preset manifest validation to reject unreachable duplicate template declarations sharing the same name and type.

Changes:

  • Tracks and rejects duplicate (name, type) template entries.
  • Adds regression coverage while allowing identical names across different types.
Show a summary per file
File Description
src/specify_cli/presets/__init__.py Validates template name/type uniqueness.
tests/test_presets.py Tests duplicate rejection and valid cross-type reuse.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Balanced

@mnriem
mnriem merged commit 58a7eda into github:main Aug 20, 2026
14 checks passed
@mnriem

mnriem commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Thank you!

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.

3 participants