# Plan de implementación del dominio musical ## Propósito Este documento convierte el análisis del catálogo en un programa de cambios ejecutable. La aplicación mantendrá una sola tabla `cancion` para la grabación principal y sus versiones musicales, pero dejará de confundir: - una obra y sus datos comunes; - una versión musical o grabación concreta; - un intérprete y su ficha pública; - una representación técnica de audio para escucha, descarga o archivo. > Estado a 11 de septiembre de 2026: 24 migraciones aplicadas y ninguna > pendiente. Están implementadas las guardas, transacciones, modelo de artistas, > streaming completo, ondas, fichas públicas, playlists, valoración y una parte > sustancial del panel. El auditor actual encuentra 11 principales, 11 másteres, > 11 escuchas y ninguna publicación sin stream. Quedan el crédito detallado en > el panel, las consultas específicas por ruta y la división de los editores > grandes. ### Seguimiento | Entrega | Estado | Pendiente principal | | ------- | --------- | --------------------------------------------------- | | PR 1–3 | Hechas | — | | PR 4 | Avanzada | Completar intérpretes y versiones reales | | PR 5 | Hecha | Operación y copias de seguridad del almacén | | PR 6 | En curso | Roles, instrumentos, orden y regeneración explícita | | PR 7–8 | Hechas | — | | PR 9 | Pendiente | Retirar el catálogo global del layout | | PR 10 | En curso | Retirar último rol legado y dividir UI monolítica | ### Punto de reanudación · 11 de septiembre de 2026 La rama de trabajo es `codex/redisenio-dominio-musical`. El último bloque funcional está en `bdec1e8`: el panel ya crea canciones como borrador y puede procesar un máster tanto durante el alta como desde una ficha antigua sin audio. La operación genera y enlaza el máster privado, la escucha MP3 completa a 96 kbps y la onda de 128 puntos; no sobrescribe archivos existentes. Estado verificado al cerrar la sesión: - `npm run check:sitio`: 0 errores y 0 avisos; - `npm run check:panel`: 0 errores y 0 avisos; - `npm run test:unit -- --run`: 34 archivos y 387 pruebas correctas; - builds de producción del sitio y del panel correctos; - transcodificación real comprobada con un WAV temporal; - 24 migraciones aplicadas, ninguna pendiente y auditoría musical íntegra. Para probar una subida en el entorno desplegado, el panel necesita `ffmpeg` y `ffprobe` en `PATH`, `BODY_SIZE_LIMIT=256M` y las mismas rutas `MEDIA_DIR`, `ESCUCHA_DIR` y `ONDA_DIR` que usa el sitio. Los valores y el proxy están documentados en `despliegue/con-proxy-manager.md`. Orden acordado para continuar: 1. incorporar la portada opcional al mismo alta, con validación, recorte cuadrado y registro en `medio`; 2. completar en el panel los créditos de interpretación —rol, instrumento, acreditación y orden— y el audio propio de cada versión musical; 3. abordar PR 9: sustituir el catálogo completo del layout raíz por consultas específicas de cada ruta; 4. dividir los editores monolíticos cuando esos contratos ya sean estables. El trabajo será incremental. Las migraciones serán aditivas hasta que las lecturas nuevas estén verificadas; ninguna columna ni relación actual se retirará en la misma entrega que introduce su sustituta. ## Decisiones cerradas ### Canción y versiones Cada fila de `cancion` es una versión musical reproducible. La principal tiene `version_de_id = null`; una versión alternativa apunta directamente a la principal. La base debe garantizar que: - una versión no apunta a sí misma; - una versión solo apunta a una principal; - no hay cadenas de versiones ni ciclos; - solo una principal puede ponerse a la venta; - comprar la principal da derecho a descargar sus versiones publicadas; - `version_codigo` es único dentro de una principal. Los datos comunes de la composición —ISWC, autoría y reparto— se asocian a la principal. Los datos de grabación —ISRC, intérpretes, audio, duración y fecha— pertenecen a cada fila concreta. ### Persona y artista `persona` seguirá representando personas físicas, autoría, datos legales e IPI. No representará grupos ni proyectos artísticos. Se añadirá `artista`, que representa la identidad pública acreditada. Puede ser una persona, un grupo o un proyecto. Una persona puede tener más de una identidad artística y un grupo puede relacionarse con varias personas. El identificador interno, el código de negocio y la URL tienen funciones distintas: - `id`: UUID opaco usado por claves foráneas; - `codigo`: identificador estable de negocio, por ejemplo `ART-000123`; - `slug`: dirección pública modificable. ### Versiones musicales y calidades de audio Una versión orquestal, una maqueta y una toma en directo son filas distintas de `cancion`. MP3 a 128 kbps, MP3 a 320 kbps y WAV son representaciones técnicas de la misma fila y no crean canciones nuevas. La escucha pública será completa en calidad web. La compra entregará archivos de mayor calidad y, cuando corresponda, todas las versiones incluidas. No se intentará presentar el streaming público como un archivo imposible de copiar: la autorización protege la descarga de alta calidad, no el contenido que el navegador necesariamente recibe para reproducir. ## Modelo de datos objetivo ### `artista` | Columna | Regla | | ------------------- | -------------------------------------------------------- | | `id` | UUID, clave primaria | | `codigo` | Único, inmutable | | `slug` | Único, usado en URL | | `tipo` | `persona`, `grupo` o `proyecto` | | `nombre` | Nombre artístico visible | | `descripcion_corta` | Texto para tarjetas y resultados | | `biografia` | Markdown para la ficha | | `imagen_id` | FK a `medio`, borrado con `set null` | | `pais_codigo` | FK opcional a `pais` | | `lugar_origen` | Texto opcional | | `fecha_inicio` | Fecha o año, por decidir tras inventariar los datos | | `fecha_fin` | Opcional | | `sitio_web` | URL opcional | | `estado` | `borrador`, `publicado` o `retirado` | | auditoría | procedencia, revisión, creación y actualización actuales | No se añadirá `nombre_legal` ni `ipi` a `artista`; esos datos pertenecen a `persona`. ### `artista_persona` Relaciona una identidad artística con personas físicas. | Columna | Regla | | ------------ | ----------------------------------- | | `artista_id` | FK a `artista` | | `persona_id` | FK a `persona` | | `relacion` | `identidad`, `miembro` o `fundador` | | `desde` | Fecha o año opcional | | `hasta` | Fecha o año opcional | | `orden` | Orden visible | La clave primaria incluirá artista, persona y relación. Una guarda comprobará que el período no termina antes de empezar. ### `artista_identificador` Guarda identificadores externos sin crear una columna nueva por plataforma. | Columna | Regla | | ------------ | ----------------------------------------------------- | | `artista_id` | FK a `artista` | | `sistema` | `spotify`, `apple_music`, `musicbrainz`, `isni`, etc. | | `valor` | Identificador en ese sistema | Serán únicos tanto `(artista_id, sistema)` como `(sistema, valor)` cuando el sistema garantice unicidad global. ### `cancion_interprete` Relaciona un artista con una versión musical concreta. | Columna | Regla | | ----------------- | --------------------------------------------------- | | `cancion_id` | FK a la fila concreta de `cancion` | | `artista_id` | FK a `artista` | | `rol` | `principal`, `invitado`, `coros` o `instrumentista` | | `instrumento` | Opcional | | `acreditado_como` | Nombre histórico de ese crédito, opcional | | `orden` | Orden del crédito | La relación actual `cancion_persona` conservará temporalmente letra, música, arreglos y producción. El rol `interprete` se retirará solo después de comprobar la paridad de datos. ### `cancion_audio` Relaciona cada versión con sus representaciones técnicas. | Columna | Regla | | --------------- | ----------------------------------------- | | `cancion_id` | FK a `cancion` | | `medio_id` | FK a `medio` | | `uso` | `stream`, `descarga` o `master` | | `formato` | `mp3`, `aac`, `opus`, `flac`, `wav`, etc. | | `bitrate_kbps` | Opcional para formatos con pérdida | | `frecuencia_hz` | Opcional | | `bits_muestra` | Opcional | | `canales` | Opcional | | `orden` | Preferencia dentro de un mismo uso | `medio.publico` y `uso` deben ser coherentes: - `stream`: medio privado, entregado completo y con autorización según `cancion.acceso`; - `descarga`: medio privado, entregado solo con autorización; - `master`: medio privado y nunca entregado por la web pública. Una restricción impedirá repetir la misma combinación de canción, uso y formato. Si se necesitan dos codificaciones equivalentes, el modelo se ampliará con un identificador de perfil explícito, no relajando la unicidad sin motivo. ### Ajustes en `cancion` Se añadirá `codigo`, único e inmutable. Se revisarán las columnas actuales con estas reglas: - `isrc` pertenece a cada versión; - `iswc`, autoría y reparto pertenecen a la principal; - `audio_id` y `muestra_id` quedan obsoletos tras poblar `cancion_audio`; - `ala_venta` solo puede ser cierto en una principal; - una versión puede tener intérpretes, audio, portada y duración propios; - una versión sin letra propia puede usar la de la principal en el read model, sin copiarla al guardar. No se moverá todavía `album_id` a una tabla puente. Se documenta como evolución posterior si una misma grabación debe aparecer en varias ediciones. ## Contratos de aplicación Los intérpretes dejan de ser `string[]`. Las lecturas públicas usarán objetos tipados: ```ts interface InterpreteResumen { id: string; codigo: string; slug: string; nombre: string; tipo: 'persona' | 'grupo' | 'proyecto'; imagen?: string; rol: 'principal' | 'invitado' | 'coros' | 'instrumentista'; instrumento?: string; } ``` Se crearán read models independientes: - `CancionPublicaReadModel`; - `FichaArtistaReadModel`; - `ListadoArtistasReadModel`; - `AudioReproducibleReadModel`; - `DescargaCompradaReadModel`. La ficha de canción no resolverá biografías completas. Solo recibe el resumen y enlaza a `/interpretes/[slug]`; la ficha del artista carga biografía, identificadores públicos y discografía. ## Programa de entregas ### PR 1 · Guardas de publicación 1. Centralizar el predicado de contenido público. 2. Aplicarlo a catálogo, canciones, álbumes, estilos, géneros, sitemap y SEO. 3. Separar lectura pública de lectura autorizada para canciones retiradas. 4. Añadir integración PostgreSQL para borrador, publicado y retirado. No depende del nuevo modelo y evita publicar accidentalmente datos durante la migración. ### PR 2 · Autenticación y comercio atómicos 1. Hacer atómicos el límite de peticiones y el consumo de OTP. 2. Añadir unicidad de carrito por usuario, fusionando duplicados existentes. 3. Crear pedido y líneas en una transacción. 4. Finalizar pedidos con transición condicional pendiente → pagado. 5. Registrar el inicio autorizado de una descarga o ajustar su semántica. ### PR 3 · Esquema aditivo de artistas y audio 1. Sincronizar primero el journal de Drizzle con `0011` y `0012`. 2. Añadir `artista`, `artista_persona`, `artista_identificador`, `cancion_interprete` y `cancion_audio`. 3. Añadir `cancion.codigo` y las restricciones de versiones. 4. Añadir `CHECK` reales para los vocabularios cerrados. 5. Mantener intactas las columnas y relaciones antiguas. La migración debe ejecutarse sobre una copia de producción antes de aplicarse al entorno real. ### PR 4 · Inventario y migración de datos El script de migración será repetible y producirá un informe antes de escribir. 1. Inventariar nombres usados como intérprete. 2. Normalizar mayúsculas, espacios y variantes obvias sin fusionar por heurística. 3. Presentar colisiones para decisión manual. 4. Crear un `artista` por identidad confirmada. 5. Relacionar personas físicas conocidas mediante `artista_persona`. 6. Migrar `cancion_persona(rol = interprete)` a `cancion_interprete`. 7. Crear códigos estables para canciones y artistas. 8. Convertir el audio completo actual en representación `master` o `descarga` según el formato real. 9. Marcar las muestras recortadas como legado; no confundirlas con el nuevo streaming completo. El informe debe comparar canciones, versiones, intérpretes y archivos antes y después, y detenerse ante cualquier huérfano. ### PR 5 · Pipeline de audio completo 1. Recibir o localizar el master privado. 2. Obtener duración, códec, frecuencia y canales mediante `ffprobe`. 3. Calcular la huella antes de generar derivados. 4. Generar la calidad web completa con un perfil versionado. 5. Generar las calidades de descarga acordadas. 6. Insertar todos los medios y relaciones en una transacción. 7. No permitir publicar una canción sin una representación `stream` válida. 8. Servir audio con `Range`, tipo MIME, `ETag` y caché coherentes. El perfil inicial se decidirá escuchando material real. Como punto de partida: stream MP3/AAC entre 96 y 128 kbps y descarga MP3 320 kbps; WAV o FLAC se conservarán como master y solo se ofrecerán si forman parte del producto. La primera automatización interactiva ya está disponible en el panel: crea la canción como borrador y, si recibe un máster, ejecuta validación, `ffprobe`, transcodificación MP3 a 96 kbps, forma de onda de 128 puntos y alta de medios. También puede completar canciones antiguas que aún no tienen audio. La operación no sobrescribe archivos existentes. ### PR 6 · Panel de artistas, créditos y audio 1. CRUD de artistas con imagen, descripción, biografía, país y estado. 2. Selector de intérpretes en cada versión musical. 3. Edición de rol, instrumento, acreditación y orden. 4. Vista de las representaciones de audio y su estado de procesamiento. 5. Acción explícita para regenerar derivados desde el master. 6. Bloqueo de publicación con errores accionables. El panel no aceptará nombres libres como intérpretes. Permitirá crear un artista desde el selector sin abandonar la canción. ### PR 7 · Lecturas públicas y fichas 1. Cambiar `Cancion.interpretes` de nombres a objetos. 2. Crear `/interpretes` y `/interpretes/[slug]`. 3. Mostrar imagen, descripción, biografía y discografía. 4. Enlazar los créditos de canciones y álbumes con la ficha correspondiente. 5. Incluir únicamente artistas publicados en rutas públicas y sitemap. 6. Mantener acreditaciones históricas aunque cambie el nombre actual del artista. ### PR 8 · Reproductor y compra 1. Sustituir la muestra recortada por la representación `stream` completa. 2. Actualizar cola, duración, textos y estados de error. 3. Autorizar descargas de representaciones `descarga` de la principal y sus versiones publicadas incluidas. 4. Impedir que URLs privadas viajen en los datos de página. 5. Actualizar condiciones de venta y privacidad antes de producción. ### PR 9 · Consultas, caché y layout 1. Eliminar el catálogo entero del layout raíz. 2. Crear read models específicos por ruta. 3. Paralelizar las consultas transversales restantes. 4. Retirar la invalidación de memoria entre panel y sitio. 5. Medir antes de introducir una caché compartida. ### PR 10 · Retirada del legado y división de UI 1. Retirar `cancion_persona.rol = interprete` tras comprobar paridad. 2. Retirar `audio_id` y `muestra_id` tras comprobar todos los medios. 3. Mover el código Markdown restante a importadores o eliminarlo. 4. Actualizar README, `.env.example` y modelo de datos. 5. Dividir `Capitulo.svelte` y los editores grandes del panel por responsabilidad. 6. Centralizar plataformas, formatos, etiquetas y políticas duplicadas. ## Estrategia de despliegue y reversión 1. Copia de seguridad y restauración ensayada antes de migrar datos. 2. Migraciones aditivas compatibles con la versión anterior del servidor. 3. Backfill separado, repetible y con modo de solo informe. 4. Activación de lecturas nuevas mediante una única configuración temporal. 5. Comparación de lecturas antigua y nueva en pruebas y entorno de ensayo. 6. Despliegue del código nuevo antes de retirar columnas antiguas. 7. Retirada destructiva en una migración posterior, con respaldo confirmado. La reversión durante la fase aditiva consiste en volver al servidor anterior; las tablas nuevas pueden permanecer. Después de la retirada destructiva, la reversión requiere restaurar la copia, por lo que esa entrega tendrá una ventana de despliegue explícita. ## Verificación mínima por entrega ```text npm run check npm run lint npm run test:unit -- --run npm run build npm run build:panel pruebas de integración contra PostgreSQL aislado E2E de los flujos modificados ``` Pruebas específicas obligatorias: - un borrador nunca aparece públicamente; - dos consumos simultáneos de OTP solo producen un éxito; - dos confirmaciones de Stripe solo finalizan un pedido una vez; - cada versión devuelve sus propios intérpretes; - una compra de la principal autoriza las versiones incluidas; - el streaming reproduce la duración completa; - el navegador nunca recibe la clave del master o de la descarga privada; - un artista retirado conserva los créditos históricos sin tener ficha pública; - el backfill conserva el número de canciones, versiones y relaciones válidas. ## Criterio de finalización El programa se considera cerrado cuando el catálogo público usa exclusivamente los nuevos read models, los intérpretes son entidades administrables y enlazables, el streaming completo se genera desde masters privados, las compras entregan representaciones de alta calidad, no quedan lecturas productivas del modelo legado y todos los checks, integración y E2E pasan en CI. ## Ampliación: comercio extensible La revisión del 12 de septiembre de 2026 detectó que el cierre por packs exactos impedía comprar libremente. La implementación asociada sustituye esa regla por packs de hasta diez, calculados automáticamente, y separa selección, líneas facturadas y derechos entregados. También queda preparada la base común para libros, partituras, MIDI, stems, servicios y niveles de suscripción. La decisión y los límites de esta fase se documentan en `docs/arquitectura-comercio-2026-09-12.md`.