[Bug]: Extension alias conflict check misses spellings that map to the same file, so one extension overwrites and then deletes another's alias

Abierto Apto para principiantes
#5,081 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

@chelsealong ya está trabajando en esto.

Desde el 26/10/2026.

  • #5134 de @chelsealong — abierto

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
78/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python
Área
cli

Línea de trabajo

Lee src/specify_cli/extensions/__init__.py, empezando por _normalize_shadow_name y _validate_install_conflicts, cerca de las líneas 1203–1240. Ejecuta los pasos de reproducción del issue para confirmar que shared.sync y shared-sync apuntan al mismo archivo de alias. Se considera terminado cuando añadir la segunda extensión informa de un conflicto y deja intacto el alias de la primera; añade una prueba de regresión específica junto a las pruebas de extensiones.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

triage-must-have
Bug Description

_validate_install_conflicts normalises declared names with _normalize_shadow_name only when comparing them with core commands. Against installed extensions it uses an exact spelling check (if name in installed_names, src/specify_cli/extensions/__init__.py:1240). Aliases such as shared.sync and shared-sync both render to .claude/skills/speckit-shared-sync/SKILL.md, so a second extension is installed without error and overwrites the first extension's alias file. Removing the second extension then deletes that file, although the first extension is still installed.

Steps to Reproduce

Save this as repro.py and run uv run python -I repro.py from the repository root.

import os, tempfile
from pathlib import Path
from typer.testing import CliRunner
from specify_cli import app

root = Path(tempfile.mkdtemp())
def make_ext(eid, alias):
    d = root / eid
    (d / "commands").mkdir(parents=True)
    (d / "commands/hello.md").write_text(f"---\ndescription: {eid}\n---\n\nHello from {eid}\n")
    (d / "extension.yml").write_text(f"""schema_version: "1.0"
extension: {{id: {eid}, name: {eid}, version: 1.0.0, description: d}}
requires: {{speckit_version: ">=0.1.0"}}
provides:
  commands:
    - name: speckit.{eid}.hello
      file: commands/hello.md
      description: x
      aliases: ["{alias}"]
""")
    return d
proj = root / "project"
(proj / ".specify").mkdir(parents=True)
(proj / ".claude/skills").mkdir(parents=True)
os.chdir(proj)
run = lambda *a: CliRunner().invoke(app, list(a))
skill = proj / ".claude/skills/speckit-shared-sync/SKILL.md"

print("add alpha:", run("extension", "add", "--dev", str(make_ext("alpha", "shared.sync"))).exit_code)
print("skill belongs to alpha:", "alpha" in skill.read_text())
print("add beta:", run("extension", "add", "--dev", str(make_ext("beta", "shared-sync"))).exit_code)
print("skill belongs to beta:", "beta" in skill.read_text())
print("remove beta:", run("extension", "remove", "beta", "--force").exit_code)
print("alpha still installed:", "alpha" in run("extension", "list").output)
print("alpha's alias skill exists:", skill.exists())
Expected Behavior

specify extension add --dev for beta fails with a conflict error. The _validate_install_conflicts docstring says "Reject installs that would shadow core or installed extension commands" (__init__.py:1220). The _normalize_shadow_name docstring (__init__.py:1203-1216) notes that dotted and hyphenated spellings "land on the same on-disk command name".

Actual Behavior
add alpha: 0
skill belongs to alpha: True
add beta: 0
skill belongs to beta: True
remove beta: 0
alpha still installed: True
alpha's alias skill exists: False
Specify CLI Version

1.2.4.dev0 (main at 27d14e2)

AI Agent

Claude Code

Operating System

Linux 6.18 (Ubuntu container)

Python Version

Python 3.12.3

Additional Context

Comparing _normalize_shadow_name(name) against the normalised installed names, as is already done for core commands, would catch this.

Also reproduced on macOS (Darwin 25.6, arm64) with Python 3.14.4 and uv run python -I repro.py against 27d14e2; the output matches the above.

AI Disclosure

Found and reproduced with Claude Code (model: Claude Opus 4.8), run autonomously under human supervision. The human reporter re-ran every reproduction above before filing.

Lenguaje dominante
Python
Estrellas
144k
Forks
12.9k
Merge medio
1 d 6 h
PR fusionados (30 d)
194

Preparar el entorno

Abrir en Codespaces

Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de github/spec-kit

Todos los issues de github/spec-kit

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.