CreaRack-SL

Stencils duplicados en paleta (bug restore, ronda funcional 13-08-2026)

Descripción

Durante la ronda funcional de mantenimiento del 13-08-2026, se descubrió que la paleta de stencils del editor de racks mostraba ~148 entradas cuando el catálogo real son 57 stencils.

La causa: la función apply_full_restore_from_zip() en racks/api/export/restore.py creaba stencils sin comprobar si ya existían. Cada restauración repetida de un backup duplicaba el catálogo completo.

Síntomas en PROD

  • Pack global de stencils de sistema: ×2 (ids 150–174, calco exacto del seed de migración 0011).
  • Stencils de la org principal (org-1): hasta ×4 (98 filas cuando debería haber 32 distintos).
  • UX: paleta del editor con 91 entradas sobrantes, haciendo difícil localizar el stencil correcto.

Root cause

Línea 150 en restore.py usaba Stencil.objects.create() incondicionalmente:

Stencil.objects.create(
    organization=org,
    name=s["name"],
    category=s.get("category", ""),
    image_path=s.get("image_path", ""),
    ...
)

Contrasta con las BoxCategory (3 líneas más arriba) que ya usaban get_or_create().

Fix aplicado (v1.66.8)

Cambio a get_or_create() con unique lógico organization + name + image_path:

_, created = Stencil.objects.get_or_create(
    organization=org,
    name=s["name"],
    image_path=s.get("image_path", ""),
    defaults={
        "category": s.get("category", ""),
        "default_u_height": s.get("default_u_height", 1),
        "manufacturer": s.get("manufacturer", ""),
        "extra_data": s.get("extra_data", ""),
    },
)
if created:
    counts["stencils"] += 1

Contrato de idempotencia: test nuevo en tests/api/test_backup_restore_async.py::test_stencils_no_se_duplican_al_restaurar_dos_veces verifica que restaurar el mismo backup dos veces genera exactamente 1 Stencil en la BD.

Remediación de datos

  • Purga verificada: eliminadas 91 filas sobrantes en PROD en el mismo ciclo.
  • Validación pre-purga: audit previo confirmó que 0 registros en suggested_stencil_id apuntaban a duplicados → sin remapeos necesarios.
  • Resultado: 58 stencils, 0 grupos duplicados.

Deuda técnica declarada

La hipótesis del duplicado del pack global (ids 150–174, que aparecieron 2 veces) es razonada pero no observada directamente:

  • Teoría: la migración 0011 (seed del catálogo global) fue re-ejecutada con aislamiento de filas (RLS y GUC) pero viendo 0 filas, lo que activó un CREATE incondicionalmente.
  • Anotada en task #224 por si reaparece en una futura migración con seed.

Impacto usuario

Ninguno visible: mismo comportamiento, pero catálogo limpio. El editor sigue funcionando igual, solo con la paleta correcta (57 stencils en lugar de 148).

Véase también

  • [[entity—racks—model—stencil]]
  • [[entity—racks—endpoint—list-stencils]]
  • [[concept—saas—multi-tenancy]]
  • [[feature—racks—backup-restore]]