Skip to content

An extension alias can shadow a core command; _validate_install_conflicts only checks installed extensions #4555

Description

@Yash-Chindam

Problem Statement

_validate_install_conflicts is documented as rejecting installs that "would shadow core or installed extension commands", but it only checks installed extensions. Core command names are never in the map it consults, so an extension can claim a core command name as an alias and install successfully.

Split out of #4421 at a maintainer's request, so that stage 1 (#4488) stays additive and no core-validation change is smuggled into it.

Reproduction

main at d848fb4. A manifest whose command carries speckit.taskstoissues — a live core command — as an alias:

schema_version: "1.0"
extension:
  id: probe
  name: "Probe"
  version: "1.0.0"
  description: "probe"
requires:
  speckit_version: ">=0.2.0"
provides:
  commands:
    - name: speckit.probe.taskstoissues
      file: commands/cmd.md
      aliases: ["speckit.taskstoissues"]     # core command name

Result:

1) manifest validation:
   ACCEPTED — aliases: [['speckit.taskstoissues']]
2) install into a project (core taskstoissues exists):
   ACCEPTED — installed: probe

Both stages accept it, with the core command present in the same project.

Cause

Two gaps that line up:

  1. Aliases are not pattern-checked. EXTENSION_COMMAND_NAME_PATTERN and the core-namespace guard in src/specify_cli/extensions/__init__.py apply to primary command names only. A primary speckit.taskstoissues is correctly rejected (it fails the two-segment pattern, and an extension id equal to a core command name is rejected separately) — but an alias goes through neither check.

  2. The conflict check does not know about core. _get_installed_command_name_map walks self.registry — installed extensions — so core command names never appear in the map that _validate_install_conflicts compares against. Confirmed by inspection: the function references self.registry and nothing that loads core command names (_load_core_command_names / _FALLBACK_CORE_COMMAND_NAMES).

So the docstring describes a guarantee ("shadow core or installed extension commands") that is only half implemented.

Impact

A community extension can claim a core command's name as an alias and shadow it. This is most likely to bite during the #4421 migration, where the whole point of stage 1 is that speckit.taskstoissues and speckit.github.taskstoissues coexist without either shadowing the other — but it applies to any core command, not just this one.

The bundled github extension in #4488 deliberately registers no alias, and has a test pinning that. Worth being explicit that this is currently author discipline, not an enforced invariant — which is the reason to file this separately rather than rely on the convention holding.

Suggested Fix

Either:

  • extend the core-namespace guard to aliases as well as primary names, and include core command names in the map _validate_install_conflicts consults; or
  • if shadowing core via an alias is intended to be allowed (an override mechanism), narrow the docstring to describe what is actually checked.

Whichever way it goes, a test should pin the decision — an install that claims speckit.taskstoissues as an alias currently passes silently either way.

Component

Specify CLI (initialization, commands)


Disclosure: I used an AI assistant (Claude Code, model Claude Opus 5) to investigate this and draft this report. The results above came from running the probe manifest through ExtensionManifest and ExtensionManager.install_from_directory against main; I reviewed the findings before filing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions