dec-0132

Vista generada de dec-0132: Styx multi-tipo: asset genérico, plugins de tipo de contenido, acciones y pipelines por kind

ImplementadoSin versión del tren todavía· generada desde docs/decisions/dec-0132-styx-multi-tipo.md
track/docsdec-0124track/docs:DC10

Vista generada desde docs/decisions/dec-0132-styx-multi-tipo.md. No se edita a mano: bun run docs:gen la regenera y bun run docs:check falla si difiere. El estado aquí es el del model: si discrepa con otra página, manda el model.

CampoValor
EstadoLOCKED
Fecha2026-10-02
Ficherodocs/decisions/dec-0132-styx-multi-tipo.md

Enmienda a: dec-0107, dec-0114

Por qué importa (del frontmatter del ADR):

Fija cómo Styx pasa de gestionar sólo películas y series a gestionar cualquier tipo de contenido finito: juegos de consola y de PC, homebrew, copias de PC, música y libros. Mantiene el catálogo Work → Edition → MediaAsset → SourceBinding y añade cuatro cosas. (1) Un asset puede ser un fichero, un árbol de ficheros o una imagen de sistema de ficheros. (2) El kind de una obra es un id registrado, no una unión cerrada de literales. (3) Plugins de tipo de contenido que aportan el esquema de metadatos, la estrategia de fuente de verdad (en el fichero, sidecar u overlay), la identificación, la presentación y las acciones. (4) Pipelines tipadas por kind, compuestas de etapas con un contrato por chunks en el data plane. Lo de vídeo (MediaIndex, CMF, planner, packaging) se queda como la faceta AV y la acción play de los kinds audiovisuales. Fija también la lista de cambios mínimos que tienen que entrar antes de que outcome/first-vertical cemente el catálogo. Sin ellos, cada tipo nuevo sería una migración y no un plugin.

Nodos del roadmap que lo citan en refs: track/content-kinds, track/pipelines, spike/archive-libs, spike/console-homebrew-protocols, track/kind-playstation, track/kind-pc-games, track/kind-backup

Páginas de la documentación que lo citan: Más allá de películas y series (especificado)

Texto del ADR

Leído de docs/decisions/dec-0132-styx-multi-tipo.md, el fichero canónico.

dec-0132 — Styx multi-tipo: asset genérico, plugins de tipo de contenido, acciones y pipelines por kind

  • Fecha: 2026-10-02
  • Estado: LOCKED (2026-10-02). waxin, vía AskUserQuestion: el core se abre antes de la vertical (M1, M2 y M4 como DM4 de track/domain-media; M3 ya va en el lock de dec-0127), el primer tipo no audiovisual son los juegos (PS5 primero, después PS3 y PS4, y PC), la instalación en consola se pospone y el desempaquetado empieza por RAR, zip y 7z. El resto de §14 se lockea con la recomendación del ADR como parámetros reversibles. Todo está en Lock (2026-10-02, waxin). Donde §1–§15 y el lock difieren, gana el lock. Las enmiendas a dec-0107 y dec-0114 (§13) entran en vigor hoy.
  • Propone: rama w12/multikind. Este ADR no crea nodos ni milestones en styx.model.yml: los propone en §12. La única excepción, por decisión de waxin en el lock, es el item de gate DM4 de track/domain-media.
  • Insumo: docs/track/plugin-seams/plans/multi-tipo-auditoria.md, la auditoría de acoplamiento con file:line. Los ids D-n, C-n, S-n, K-n, W-n y Z-n de este ADR remiten a ella.
  • Numeración: dec-0132 es el primer número libre en todas las ramas w*, ops/*, spike/* e integ/* el 2026-10-02 (el último ocupado es dec-0131, en w10/vision).
  • Dirección de waxin que cumple (2026-10-02):
    • "styx no debe ser sólo pelis y series". Tiene el mismo problema de media server y de transferencias con los backups de sus juegos físicos de PS4/PS5, el homebrew y el desarrollo (hoy todo a mano por FTP), las copias de PC (ficheros muy grandes, transferencias lentas), la música y los libros.
    • "Sólo cambiaríamos los modelos y ajustaríamos el resto, y styx podría, mediante plugins y los cambios que haya que hacer ahora para abstraer cosas del core y del SDK, pasar a ser de múltiples media types".
    • La pipeline de bytes (adquisición → desempaquetado/descompresión → verificación → normalización → compresión/almacenamiento por tiers → servir/entregar/instalar) tiene que ser agnóstica y plugineable, con varios tipos de pipeline. Los juegos tienen un proceso parecido desde la adquisición hasta el play, en PC o en consola.
  • Reparto con el spike paralelo: spike/media-storage-compression investiga el formato de bytes: troceado, deduplicación, compresión con acceso aleatorio y separación de contenedor. El 2026-10-02 la rama no tiene commits propios; su worktree tiene scripts sin commitear (dedup.py, container_split.py, seekable.py, packets.py). Este ADR no decide nada de eso. Define el hueco donde encaja: la etapa store de §7 y su contrato.
  • Cita:
    • r01: Bun decide, Zig ejecuta; los bytes no pasan por JS ni por NATS.
    • r04 / C14: SeekableMediaSource y cancel(requestId).
    • r08 / r20 §6: Work → Edition → MediaAsset → SourceBinding.
    • r16: frontier salvo blocker.
    • r23 / dec-0019: Canonical Media Factory; el artefacto adquirido no es la verdad.
    • r26: byte runtime en todas las direcciones.
    • r28 §3: anti-especulativo; r28 §5: el discriminante va antes de F1.
    • r48 / r49: seams, manifest, precedencia, MetadataPatch y sidecar conceptual (§3.4).
    • dec-0033: Plugin SDK como familia tipada; el core se queda los bytes y la seguridad.
    • dec-0034: Acquisition Runtime.
    • dec-0088 / r59: tracks condicionales de consola en experiments[].
    • dec-0106 (PROPOSED): ExtentMap.
    • dec-0107 (LOCKED en w11/locks): LiveService paralelo y WorkKind de media finita.
    • dec-0114 (LOCKED): scanner por contenido como plugin.
    • dec-0117 / dec-0118: I1–I12 y permisos <recurso>:<acción>.
    • dec-0119 / dec-0120: spire.
    • dec-0121: ingesta con conduit.
    • dec-0125, dec-0126, dec-0127, dec-0128, dec-0129, dec-0130 y dec-0131 (PROPOSED): cuentas, metadatos en el fichero, modelo de datos fichero-SSOT, dispositivos, transferencias, modo público y federación.

1. Contexto

La auditoría da tres hallazgos.

  1. El data plane ya es genérico. Caché por niveles, fuente de bytes, sesión de rangos, transporte H3 con SCT, ingesta conduit y seguridad (Z-1 a Z-6) no saben qué hay dentro de un fichero. Lo de vídeo (motor de medios, packaging CMAF, perfiles MoQT; Z-7, Z-8) está aislado detrás de qry.media.probe y cmd.media.openPackaging.
  2. El control plane tiene una pieza de vídeo en el camino obligatorio. scanAsset no persiste nada si el probe de medios falla (S-2, CatalogHandler.ts:164-190). registerIngested pasa por ahí (S-3). Hoy una subida de un .pkg, un EPUB o un .7z acaba en CATALOG_PROBE_FAILED: el fichero se queda en la raíz de ingesta, cuenta en la cuota y no entra en el catálogo.
  3. Hay dos sitios que van a cementar. Primero, el wire HTTP de obras ya cierra kind a 6 literales (ListWorksReplySchema y GetWorkReplySchema reutilizan WorkSchema; C-2), y dec-0107 pide cerrar también el reply del bus. Un cliente generado con un switch exhaustivo (web, Swift) rompe con el primer kind nuevo. Segundo, la jerarquía serie → temporada → episodio no existe en el dominio (sólo en mocks, W-6), y el plan vertical la va a añadir (ola B, B5). Si entra con columnas propias de series, cada tipo con jerarquía (disco → pista, juego → DLC, saga → libro) pedirá las suyas.

Todo lo demás acoplado a vídeo (planner, capabilities AV, resolvePlaybackSource, ficha y reproductor web, plugin NFO) es correcto que sea de vídeo: pasa a ser la faceta AV y la acción play de los kinds audiovisuales.

2. Vocabulario

En el repo ya hay cinco "kind" distintos. Este ADR no añade un sexto con el mismo nombre:

TérminoQué esDónde
Work.kind (tipo de contenido)Qué clase de obra es: movie, episode, game.ps5, music.album…Work.ts:20. Este ADR lo abre (§4.2)
PluginKindExtension point que cubre un plugin: metadata, source, scanner…manifest.ts:54
SourceKindProtocolo de la fuente: local, http, torrent…SourceBinding.ts:34
EditionKindClase de ediciónEdition.ts:17
MediaAsset.kindHoy siempre 'logical-composition'MediaAsset.ts:33
  • Tipo de contenido = el valor de Work.kind. El campo conserva su nombre, así que no hay migración.
  • Plugin de tipo de contenido = un plugin que cubre el extension point nuevo content-type (§5).
  • Faceta = un bloque de facts que el core sabe calcular sobre un asset. MediaIndex es la faceta av.
  • Colisión con dec-0088: track/ps4-homebrew y track/playstation-official (experiments[]) tratan la consola como cliente, es decir, Styx corriendo en la PS4. Este ADR trata los juegos de PlayStation como contenido. Los nodos de §12 usan otro nombre.

3. Decisión (resumen)

  1. El catálogo no cambia de forma. Work → Edition → MediaAsset → SourceBinding vale para todos los tipos: juego → región/versión → dump → dónde están los bytes; disco → edición → ficheros; backup → snapshot → árbol. Work sigue siendo media finita (dec-0107): live no entra por aquí.
  2. Asset genérico: un MediaAsset tiene layout: file | tree | image (§4.1). Un árbol tiene un manifiesto canónico, y su identidad es la raíz de ese manifiesto.
  3. Work.kind es un id registrado, no una unión de literales (§4.2). El core registra los kinds de hoy; los plugins de tipo de contenido registran los suyos. El registro rechaza cualquier kind que no sea finito. Los clientes tratan un kind desconocido con una presentación genérica.
  4. Facetas en vez de un índice obligatorio (§4.3). El alta en el catálogo no exige el probe AV: cada kind declara qué facetas calcula, y un asset sin facetas reconocidas entra igualmente, como "no identificado".
  5. Relación genérica entre obras (§4.4): WorkRelation { parent, child, rel, ordinal }, la misma para serie → episodio, disco → pista, juego → DLC o actualización, y saga → volumen.
  6. Plugins de tipo de contenido (§5): aportan el esquema de metadatos (un bloque con espacio de nombres dentro de styx.json, dec-0127), la estrategia de fuente de verdad (en el fichero, sidecar u overlay), las señales de identificación para el scanner (dec-0114), la presentación y la lista de acciones y pipelines del kind.
  7. Acciones (§6): extension point action. El plugin produce un plan declarativo, y el core lo valida y lo ejecuta con sus piezas: el planner para play, transferencias dec-0129 para download y sync, y el puente de dispositivos para install/send. Ningún plugin mueve bytes.
  8. Pipelines tipadas por kind (§7): una composición de etapas acquire → unpack → verify → normalize → store → deliver. La política la decide Bun; los kernels de bytes son Zig, con un contrato por chunks reanudable por ExtentMap y verificable por el árbol Merkle de dec-0129. La CMF de vídeo (dec-0019) pasa a ser una instancia de este modelo, sin cambiar sus locks.
  9. Seguridad (§8): las acciones que hablan con consolas en la LAN, los desempaquetadores de archivos y las etapas nativas son superficie nueva, con reglas propias sobre I4, I5, I7, I8 e I11.
  10. Cambios mínimos antes de outcome/first-vertical (§11): cinco cambios pequeños. Con ellos, añadir un tipo es aditivo y no una migración.

4. Núcleo de asset genérico

4.1 MediaAsset.layout: fichero, árbol o imagen

layoutQué esEjemplosIdentidad (huella de asset)
file (default, lo de hoy)Un fichero.mkv, .flac, .epub, .pkg de PS4, .isoLa huella que defina el plugin del kind. Para AV, la huella de payload de dec-0126 §3.3; si el kind no la define, la raíz de transferencia del fichero (dec-0129 §3.2)
treeUn directorio con N ficherosDump de PS5 en carpeta (sce_sys/param.json, eboot.bin…), juego de PC instalado, copia de PC, disco en FLAC por pistasRaíz del manifiesto: BLAKE3 de JCS (RFC 8785) de [{relPath, size, mode, transferRoot}], ordenado por relPath en bytes UTF-8
imageUn fichero que contiene un sistema de ficherosImagen exFAT de un dump de PS5, .iso de PCLa raíz de transferencia del fichero. Si una etapa unpack deriva el árbol interior (§7), ese manifiesto es una faceta y no la identidad
  • Manifiesto (AssetManifest): lista canónica de entradas con relPath (normalizado NFC, sin .. ni absolutos, separador /), size, mode (sólo file/dir/exec; los symlinks no se siguen ni se materializan, §8) y transferRoot (la raíz Merkle de 16 KiB de dec-0129 de cada fichero). Se guarda como derivado regenerable en L2, igual que el outboard de dec-0129.
  • Exclusión del sidecar: el directorio .styx/ de la raíz de un árbol (§5.2) no entra en el manifiesto. Escribir metadatos no cambia la identidad del asset, que es la misma regla que dec-0126 §3.3 aplica a los metadatos incrustados.
  • Transferencia: un árbol se transfiere como N transferencias de fichero (dec-0129 §3.3.2) más el manifiesto, que las agrupa y fija el estado final (bundleId, §9). Deduplicar entre árboles sale gratis: dos actualizaciones de un juego que comparten el 95 % de los ficheros comparten sus transferRoot.
  • MediaAsset.path y sizeBytes siguen: en un árbol, path es la raíz lógica y sizeBytes la suma. container sólo tiene sentido en file e image.

4.2 Work.kind como id registrado

  • Forma del id: ^[a-z][a-z0-9-]*(\.[a-z0-9-]+)*$, como mucho 64 caracteres. Los 6 kinds de hoy conservan su id sin prefijo (movie, series, episode, concert, extra, collection), así que no hay migración de filas. Los nuevos van con espacio de nombres: game.ps4, game.ps5, game.pc, homebrew.app, homebrew.payload, music.album, music.track, book, book.comic, backup.tree.
  • Registro (WorkKindRegistry, en el core de catalog-svc): el core siembra los 6 kinds AV y cada plugin content-type registra los suyos al inicializarse. Cada entrada declara {id, finite: true, facets, layouts, editionKinds, relations, actions, pipelines}.
    • El registro rechaza una entrada con finite: false o cualquier intento de registrar channel/live. Así se conserva el invariante de dec-0107 por construcción, no por convención.
    • Dos plugins no pueden registrar el mismo id: se aplica la precedencia de r48 §3.2 y el conflicto aparece en el init report, sin elegir en silencio.
  • Contrato: en @styx/api-contracts, kind es Type.String({ pattern, maxLength: 64 }). El servidor valida contra el registro en cada frontera de escritura (createWork, la aplicación de propuestas del scanner, registerIngested). El reply puede llevar cualquier id registrado.
    • Regla de cliente (web, Swift, CLI): un kind desconocido se pinta con la presentación genérica (§5.4) y nunca rompe un switch. Va con un test por cliente.
    • El registro se publica por qry.catalog.listKinds, con su descriptor de presentación, para que el cliente sepa qué filtros y qué fichas ofrecer.
  • Relación con dec-0107: el lock dice "WorkKind es desde hoy un conjunto cerrado de media finita" y pide cerrar kind del reply con un Type.Union de literales. Este ADR conserva el invariante (sólo media finita; live por LiveService) y cambia el mecanismo: el conjunto se cierra en un registro validado y no en una unión estática. Es una enmienda a la letra de dec-0107 y necesita su propio AskUserQuestion (P1). La alternativa barata (añadir un literal por kind, como la enmienda de SourceKind de dec-0107 P4) está en §13 y §14.

4.3 Facetas: el probe AV deja de ser obligatorio

  • Una faceta es un bloque de facts versionado que una etapa sabe extraer:
    • av: el MediaIndex de hoy, por qry.media.probe;
    • ps.sfo: param.sfo de PS4 (TITLE_ID, CONTENT_ID, APP_VER, TITLE…);
    • ps.paramjson: sce_sys/param.json de PS5;
    • audio.tags: Vorbis comments, ID3v2, ilst;
    • book.opf: metadatos OPF de un EPUB;
    • tree.manifest: el manifiesto de §4.1.
  • Cada kind declara qué facetas necesita para estar identificado y cuáles son opcionales. La extracción es un parser de entrada no confiable, así que vive en el daemon (I5, §8) y devuelve facts, nunca bytes (el patrón de dec-0114 §1).
  • El alta queda así:
    1. Persistir el asset con su layout y su identidad. Esto no puede fallar por facetas.
    2. Pedir las facetas que el reconocimiento de layout o extensión sugiere.
    3. Pasar las propuestas a los scanners (dec-0114).
    4. Un asset sin facetas reconocidas queda unidentified y visible en la cola de revisión. Nunca se pierde.
  • media_indexes sigue siendo la tabla de la faceta av (ya está separada, S-6). El resto de facetas van en una tabla asset_facets(asset_id, facet, version, facts jsonb, digest).

4.4 Relaciones entre obras

  • WorkRelation { parentWorkId, childWorkId, rel, ordinal? } con rel registrado por el kind padre: season/episode (serie), track (disco), dlc/update/patch (juego), volume (saga, cómic) y snapshot (backup, si P3 elige obra por snapshot).
  • ordinal es una ruta numérica ordenable ([1, 3] = temporada 1, episodio 3; [2, 7] = disco 2, pista 7). Una sola columna sirve para todo tipo.
  • Es lo que tendría que implementar B5 del plan vertical (series) en vez de columnas seriesId/season/episode (§11, M4).

5. Plugins de tipo de contenido (extension point content-type)

5.1 Qué aporta un plugin

PluginExportsByKind gana 'content-type': IContentTypeProvider, en el mismo cambio que su primer consumidor real (r28 §3, manifest.ts:25-30). Un plugin de tipo de contenido declara:

PiezaQué esQuién la ejecuta
kinds[]Los descriptores de §4.2Registro del core
metadataSchemaEsquema TypeBox del bloque kinds.<id> de styx.json (versionado)Core: valida y fusiona con la precedencia de r48 §3.2
truthEstrategia de fuente de verdad (§5.2)Core + daemon (escritura)
recognizePistas baratas: extensiones, ficheros centinela (sce_sys/param.json, META-INF/container.xml) y facetas a pedirCore, en el discovery de dec-0114
presentationDescriptor de UI (§5.4)Cliente
actions[]Ids de acciones aplicables (§6)Core
pipelines[]Pipelines por intención (acquire, canonicalize, install…) (§7)Orquestador
  • La identificación sigue siendo del extension point scanner de dec-0114. Un plugin de tipo de contenido puede traer su scanner en el mismo paquete (dos kinds en el manifest), pero el contrato es el de dec-0114. Lo que este ADR enmienda de dec-0114 está en §13.
  • No hay plugins de "tipo de contenido genérico" (dec-0033): cada pieza es un extension point tipado con su interfaz.

5.2 Fuente de verdad: en el fichero, sidecar u overlay

Generaliza dec-0127 §3 ("el fichero manda; el catálogo es un índice derivado") a formatos que no son MKV/MP4:

truthCuándoDónde vive styx.json
embedEl formato tiene sitio y Styx sabe escribirlo sin romperloDentro del fichero. MKV y MP4 (dec-0126/0127); FLAC (bloque VORBIS_COMMENT + un bloque APPLICATION), MP3 (ID3v2 TXXX/GEOB), M4A (ilst, como MP4), EPUB (OPF + un fichero en META-INF/). Cada writer es una etapa del daemon con journal (dec-0126 §5) y llega con el kind que lo necesita
sidecarÁrboles escribibles, o ficheros que no admiten escritura segura.styx/styx.json en la raíz del árbol (excluido del manifiesto, §4.1), o <nombre>.styx.json junto a un fichero. Es el SidecarDescriptor de r48 §3.4 hecho concreto
overlayLo que no se puede tocar: fuente remota, raíz ro, fichero firmado (.pkg de PS4), seeding, nlink > 1En el catálogo, con fileState: overlay (dec-0127 §6.3). Se materializa en un sidecar en cuanto el asset pase a una raíz escribible
  • Un .pkg de PS4 está firmado: escribir dentro lo invalida. Su kind declara truth: overlay, o sidecar cuando la raíz es escribible. El param.sfo interior es autoridad local de sólo lectura (faceta), con la misma precedencia que un NFO (local-authoritative).
  • Un dump de PS5 en carpeta: sidecar en .styx/. Un dump en imagen exFAT: sidecar junto a la imagen, porque Styx no escribe dentro de la imagen (§8).
  • La precedencia es la de r48 §3.2 para todos los tipos: lock manual > autoridad local (facetas del propio contenido, sidecar del usuario) > orden configurado > confianza > fallback.

5.3 Esquema de styx.json multi-tipo

  • La cabecera y el bloque común de dec-0127 §4.1 (schema, workUid, kind, edition, títulos, fecha, ids externos, artwork, curación) valen para todos los kinds.
  • Lo propio de cada tipo va en kinds.<id> y lo valida el esquema que registró su plugin. Por ejemplo, kinds["game.ps5"] = {titleId, contentId, region, appVersion, requiredFirmware, dlc[]} o kinds["book"] = {isbn, authors[], series, volume}.
  • Los ids externos van por espacio de nombres, como ya prevé dec-0127: igdb, ps.titleId, isbn, openlibrary y musicbrainz.

5.4 Presentación

  • El descriptor de presentación es datos, no código: aspecto de la tarjeta (2:3 póster, 1:1 portada de disco, 3:4 libro, 16:9 juego de PC), campos primarios y secundarios, agrupación (temporadas, discos, DLC), facetas de filtro y la acción primaria.
  • El cliente tiene una ficha por familia (av, audio, book, game, generic) y elige según el descriptor. Un kind sin familia conocida usa generic: título, tamaño, árbol de ficheros y acciones. Un plugin de terceros nunca inyecta UI ejecutable (dec-0118 §6, CSP).
  • /library?tipo= pasa a filtrar por ids del registro, y /work/$workId sirve a todas las familias (W-1 a W-4).

6. Acciones (extension point action)

6.1 Forma

  • IActionProvider { id, appliesTo: kindIds[], targets: TargetClass[], plan(asset, target, params) → Result<ActionPlan> }.
  • ActionPlan es declarativo y validable, el patrón del EncoderProvider de r48 §3.3: una lista de pasos de un vocabulario cerrado del core. El core lo valida contra los permisos del actor y del dispositivo y lo ejecuta. Un plugin nunca abre un socket ni lee un byte.
  • Vocabulario de pasos (cerrado; ampliarlo es un cambio del core, no de un plugin):
PasoQué hacePieza del core que lo ejecuta
playReproducirEl planner de hoy (r05/r20), sólo para kinds con faceta av
readServir un libro o cómic por rangos a un lectorEgress del daemon (http_range), SCT de lectura
downloadFichero original o árbol a un clienteTransferencias de dec-0129 (scope download)
syncMantener una réplica de un árbol en un nodo o dispositivo, por diferencias del manifiestodec-0129 (nodo ↔ nodo) + manifiesto (§4.1)
offer-urlDar a un dispositivo una URL firmada para que él tire de los bytesEgress del daemon + SCT download atada al dispositivo
device-callLlamada de control a la API del dispositivo (p. ej. "instala desde esta URL")Puente de dispositivos (§8.1), sin bytes de contenido
pushEmpujar ficheros al dispositivo por su protocolo (FTP…)Puente de dispositivos, con un egress sink Zig
run-pipelineLanzar una pipeline del kind (p. ej. normalizar carpeta → imagen antes de instalar)Orquestador de §7

6.2 Acciones de los casos de waxin

AcciónKindsPlan
playmovie, episode, concert, music.*play (el de hoy; música es el subconjunto audio del mismo planner)
readbook, book.comicread
downloadtodosdownload
install.pullgame.ps4, game.ps5, homebrew.apprun-pipeline(normalize-for-target)? → offer-url → device-call(install-from-url). La consola descarga del daemon con un SCT de un solo dispositivo y tiempo acotado. Recomendado por defecto: no abre egress nuevo del daemon
install.pushídem, cuando la herramienta homebrew sólo tiene FTPpush(ftp) por el puente. Más lento y en claro (§8)
deploy.devhomebrew.payloadpush del binario recién construido al cargador de payloads del dispositivo. Es el bucle de desarrollo que waxin hoy hace a mano por FTP. Exige el dispositivo en modo desarrollo y confirmación (§8)
syncbackup.tree, game.pcsync con delta por manifiesto (sólo viajan los transferRoot nuevos)
restorebackup.treedownload del árbol en un snapshot dado
  • Los puertos, protocolos y APIs concretos de las herramientas homebrew (servidor FTP, instalador remoto de paquetes, cargador de payloads) no se fijan aquí. Cambian con la herramienta y su versión. Los mide el spike de §12 con lo que waxin usa de verdad, y cada uno entra como un IActionProvider con su ficha de dispositivo.
  • La consola como destino es un dispositivo de dec-0128 (emparejado, con dueño y con clase console.ps4/console.ps5). Este ADR no crea un registro de dispositivos paralelo.

7. Pipelines tipadas por kind

7.1 Modelo

  • Una pipeline es un DAG (casi siempre una cadena) de etapas con un tipo de entrada y uno de salida (file | tree | image | facts), declarada por un kind para una intención: acquire, canonicalize, install, archive…
  • Seis clases de etapa, que son las de waxin:
ClaseEntrada → salidaEjemplos
acquirefuente → file/treeSubida conduit (dec-0121), swarm, HTTP, otro Styx (dec-0131), volcado desde la consola (el dispositivo como fuente: FTP de lectura), agente de copia de PC
unpackfile/image → tree.zip, .7z, .rar (si la licencia lo permite, P10), imagen exFAT → árbol, .pkg → árbol (sólo para facetas)
verifycualquiera → mismo + veredictoÁrbol Merkle (dec-0129), consistencia de param.json y del manifiesto, comparación opcional con una base de dumps conocidos
normalizecualquiera → cualquieraCarpeta ↔ imagen según el destino, retag de música, CMF para vídeo (dec-0019), metadatos al fichero (dec-0126)
storecualquiera → referencia en un tierTroceado, dedup, compresión, tier caliente o frío. El formato es del spike media-storage-compression
deliverreferencia → destinoLos pasos download, offer-url, push y sync de §6
  • Contrato por chunks (data plane, Zig). Cada etapa de bytes implementa:
    • pull(ctx, out) → Chunk | EntryBegin | EntryEnd | Eof. Chunk = {entry, offset, bytes}. Un árbol viaja como una secuencia de entradas, nunca como ficheros temporales completos si la etapa siguiente puede consumir en streaming;
    • backpressure por waker (dec-0053) y cancel(requestId) por request (r04/C14);
    • reanudación por ExtentMap (dec-0106) por entrada. Una etapa declara si es determinista e idempotente. Sólo las que lo son se reanudan a mitad; las demás reempiezan su entrada;
    • facts laterales: la etapa emite facts pequeños (faceta, veredicto, progreso) por el socket de control. Nunca bytes de contenido (r01).
  • Orquestación (control plane). La pipeline es un job durable: FSM en Postgres + JetStream, el patrón que dec-0019 lockeó para la CMF ("SIN BullMQ"). Vive en workers-svc, que hoy sólo tiene /health y cuya responsabilidad prevista es justo el scheduler durable (P7). La decide Bun (qué pipeline, con qué parámetros, cuándo); la ejecuta el daemon. Las clases de scheduler son las de dec-0129 §3.8: un canonicalize o un archive nunca degrada una reproducción.
  • Plugins de etapa (pipeline-stage): un plugin TS aporta política (parámetros, cuándo aplicar, cómo interpretar facts). El kernel que toca bytes es Zig y se elige por trust level (I11): builtin/trusted-native en el proceso del daemon; sandboxed/community fuera de proceso detrás de spire, con sólo el fd del recurso. Ningún kernel es "shell arbitrario desde plugin" (r48 §3.3).

7.2 Encaje con lo que ya existe

PiezaRelación
CMF (dec-0019, r23)Es la pipeline canonicalize de los kinds AV: acquire → verify → normalize(AV2/AV1/HEVC según CanonicalCodecPolicy) → store. Sus locks no cambian. Cambia que deja de ser el único tipo de pipeline
Transferencias (dec-0129)Son acquire (entrada) y deliver (salida) de toda pipeline, con su modelo Transfer, su árbol Merkle y su entrada única registerIngested. El origin de dec-0129 §3.7 gana device (volcado desde una consola)
conduit (dec-0121)La subida es una etapa acquire. Una carpeta = N sesiones + bundleId (§9)
swarm (track/swarm-core, track/swarm-progressive)acquire desde BitTorrent y deliver como siembra. Un árbol es un torrent v2 multi-fichero: sus transferRoot son los pieces root de BEP 52 (dec-0129 §3.2)
Acquisition Runtime (track/acquisition-core, track/acquisition-native, dec-0034)Decide qué adquirir y de dónde, y lanza la pipeline acquire del kind. Las policies por kind de dec-0034 (Movies/Series/Live/EPG/Music) se amplían con los kinds registrados
plugin-seams / plugins-sdkcontent-type, action y pipeline-stage son extension points nuevos del mismo seam (manifest, registry, precedencia, health). Su forma pública la fija track/plugins-sdk con dec-0130 §8
spike media-storage-compressionRellena store: formato de troceado y dedup, compresión con acceso aleatorio, tiers. Restricción que este ADR le pasa: un kind con acción play/read/install.pull necesita servir rangos desde store, así que su formato caliente debe ser seekable. Un tier frío de backup.tree puede no serlo

7.3 Pipelines de los casos de waxin

  • Juego de PS5 (dump en carpeta o imagen):
    1. acquire (subida conduit desde el PC, o volcado desde la consola);
    2. unpack (imagen → árbol, sólo para facetas);
    3. verify (Merkle + param.json + manifiesto completo);
    4. normalize (opcional: carpeta ↔ imagen según lo que espere el cargador del destino);
    5. store (dedup entre versiones y actualizaciones del mismo titleId);
    6. deliver = install.pull.
  • Juego de PS4: .pkg como file; verify con la faceta ps.sfo; deliver = install.pull o install.push.
  • Copia de PC (backup.tree): acquire (agente de copia: sólo sube los transferRoot que el nodo no tiene); verify; store (dedup + compresión, tier frío); deliver = restore/sync. Esto ataca "ficheros muy grandes, transferencias lentas": no se mueve lo que ya está.
  • Música: acquire; verify; normalize (tags + styx.json incrustado); store; deliver = play/download.
  • Libros: acquire; normalize (OPF + styx.json); deliver = read/download.
  • Homebrew en desarrollo: no hay store. acquire (salida del build, por el CLI o un watcher) → deliver = deploy.dev. El valor es quitar el FTP a mano del bucle editar-compilar-probar.

8. Seguridad (dec-0117 / dec-0118)

Las acciones hacia la LAN son superficie nueva. Hoy el daemon no tiene ninguna salida (dec-0117 I8, DS5-01), y las herramientas homebrew de consola exponen servicios sin autenticación y en claro.

8.1 Puente de dispositivos

  • Proceso aparte (styx-device-bridge, Zig): un contenedor propio con la única red de salida hacia la LAN, deny-by-default, con la lista de ip:puerto de los dispositivos emparejados (dec-0128) en la tabla del firewall de check:deploy. El daemon no gana egress a la LAN: I8 se cumple sin reabrir styx-edge.
  • El puente recibe del daemon el fd del recurso concreto (I11), nunca una raíz. Se habla con él por spire (dec-0119), con presupuestos (I4).
  • install.pull es el modo por defecto porque no necesita el puente para los bytes: la consola tira del egress que ya existe, con un SCT de scope download (dec-0129) atado al dispositivo, de un solo asset y con caducidad corta. El puente sólo hace la device-call.

8.2 Reglas

  • Destinos sólo emparejados: un push o una device-call sólo van a dispositivos de dec-0128 del mismo dueño u hogar, en rangos privados (RFC 1918, ULA). Nunca a una IP que traiga la petición.
  • Permisos (dec-0125 <recurso>:<acción>): asset:install, device:control y device:deploy-code. deploy.dev exige además que el dispositivo esté en modo desarrollo y una confirmación explícita en el cliente por sesión de despliegue: enviar código ejecutable a una consola es la acción más peligrosa de este ADR.
  • Modo público (dec-0130): los kinds de consola y homebrew, y todas sus acciones de dispositivo, están desactivados en una instancia pública, y un principal anónimo o federado (dec-0131) nunca las ve. Los backups de juegos son de uso personal; la retirada de dec-0130 §7.4 aplica igual.
  • Desempaquetado = parser de entrada no confiable (I5): se escribe como módulo styx:parser (sin unreachable, .? ni catch {}; fuzz largo en fuzz_long.zig). Además:
    • path traversal / zip-slip: toda entrada se resuelve fd-relativa bajo la raíz de staging (I7) y se rechaza cualquier relPath que salga, sea absoluto o tenga .. tras normalizar;
    • symlinks y hardlinks dentro de un archivo o un árbol: no se materializan. Se registran en el manifiesto como entrada link con su destino como dato, nunca como ruta a seguir;
    • bombas de descompresión: presupuesto por etapa en bytes de salida y en ratio salida/entrada, más el número de entradas (I4). Al pasarse, error tipado, no OOM;
    • sistemas de ficheros en imagen (exFAT): parser de sólo lectura en el daemon, sin montar nada en el host. Styx no escribe dentro de una imagen.
  • Facetas de consola (param.sfo, param.json): son parsers de entrada no confiable con la misma regla styx:parser.
  • Etapas y acciones de terceros: I11 sin excepciones. sandboxed/community fuera de proceso, y su ActionPlan pasa por la validación del core como cualquier otro.
  • Audit (dec-0118 §9): cada push, device-call, deploy.dev e install.* deja un evento de seguridad con actor, dispositivo, asset y resultado. Nunca con el SCT ni la URL firmada (I12).

9. Qué cambia en contratos y SDK

SitioCambioCuándo
@styx/domain Work.tsWorkKindId (string con patrón) + CORE_WORK_KINDS (los 6 de hoy) + WorkKindDescriptorPre-vertical (M1)
@styx/domain MediaAsset.tslayout: 'file' | 'tree' | 'image' (default file)Con el primer kind de árbol; la regla de identidad, pre-lock de dec-0127 (M3)
@styx/domain nuevoAssetManifest, AssetFacet, WorkRelationWorkRelation pre-vertical (M4); el resto, con su consumidor
@styx/domain Edition.tsEditionKind también registrado por el kind (region, version, snapshot, translation…)Con el primer kind no AV
@styx/api-contracts catalog.tskind como string validado (C-1, C-2); qry.catalog.listKinds; ScanAssetHttpReply.index opcional (C-5); indexed → facets[] en WorkAssetSummary (C-4)kind: pre-vertical (M1). Índice opcional: M2
@styx/api-contracts ingest.tsbundleId? + manifestRoot? para agrupar una carpetaCon el primer kind de árbol
@styx/api-contracts metadata.tsIMetadataPatchFields + kinds?: Record<KindId, FieldPatch…>Con el primer kind no AV
@styx/api-contracts nuevoactions.ts (ActionPlan, cmd.<dominio>.runAction), pipelines.ts (job, estado, evt.pipeline.*)Con su consumidor
@styx/plugin-sdkIMetadataProvider.enrich(assetId, facets) en vez de (assetId, index: IMediaIndex) (K-2); ISearchProvider.kind → string con espacio de nombres (K-3); extension points content-type, action y pipeline-stageenrich: pre-SDK-público (M5). Extension points: con su consumidor
@styx/source-sdkReadPurpose + transfer, verify, install (K-7); capability opcional list() para fuentes que enumeran (K-8, la "capability de enumeración" de dec-0114 §1)Con su consumidor
protocols/session-ipcOps de pipeline (cmd.media.runStage, pollStage, cancelStage) y de facetas (qry.media.facets)Con su consumidor
catalog-svcRegistro de kinds; alta sin facetas obligatorias (S-2, S-3, S-4); asset_facets; work_relations; validar row.kind al leer (S-7); operations/registry.ts:57 al registro (S-8)M1, M2 y M4 pre-vertical
workers-svcOrquestador de pipelinesCon la primera pipeline no AV
webFiltro por registro (W-1 a W-3); fichas por familia + generic; regla de kind desconocidoLa regla, pre-vertical (M1); las fichas, con su kind

10. Encaje con r16 y r28

  • r28 §3 (anti-especulativo): ningún kind, acción ni etapa entra sin su consumidor real en el mismo cambio. Los consumidores reales son los de waxin: sus backups de PS4/PS5, su bucle de homebrew, sus copias de PC, su música y sus libros. Este ADR no pide implementar los seis kinds de golpe. Pide que el núcleo no impida añadirlos (§11) y ordena su entrada (§12).
  • r28 §5: igual que el discriminante live, la apertura del kind tiene que estar antes de que outcome/first-vertical cemente el catálogo y los clientes generen tipos. Después costaría una migración de clientes.
  • r16 (frontier): identidad por contenido de árboles enteros, dedup por Merkle entre versiones de un juego, instalación por pull con SCT en vez de FTP, y la misma pipeline para ocho tipos de contenido. No hay referente que lo haga junto: Jellyfin no gestiona juegos, y los gestores de backups de consola no tienen catálogo, ni dedup, ni capabilities.
  • dec-0033: el core se queda bytes, seguridad, estado autoritativo y el vocabulario de pasos; los plugins aportan política, esquema, reconocimiento y presentación. No hay un plugin genérico.

11. Cambios mínimos antes de outcome/first-vertical

Sólo lo que no sería aditivo si se hace después. Nada de esto implementa un kind nuevo.

#CambioPor qué antesCoste (tamaño, no fecha)
M1Work.kind abierto en el wire + registro con los 6 kinds de hoy + regla de kind desconocido en webDespués del vertical, web y Swift tendrán switch exhaustivos sobre 6 literales y el wire HTTP cerrado (C-2). Abrirlo luego rompe clientes. La base de datos no cambia (S-5)S: Work.ts (tipo + schema), WorkKindRegistry en catalog-svc (~1 fichero), catalog.ts (C-1, C-2), operations/registry.ts:57, catalog-repo.ts (validar en vez de castear, S-7), blender-catalog.ts:38, web (router.tsx:124-127, library-fetch.ts, library-queries.mock.ts, home-rails.ts, lib/operations/registry.ts:73) + un test por cliente con un kind desconocido. Necesita la enmienda a dec-0107 (P1)
M2El alta no exige faceta AV: saveIndexedAsset con índice opcional, ScanAssetHttpReply.index opcional y un fallo de probe = asset unidentified, no errorHoy toda subida no AV se pierde para el catálogo (S-3). Cuando el vertical cierre la vía "scan + probe en la misma transacción" con su contrato y sus tests, separarlas será romperloS: CatalogHandler.ts:164-190 y :366-375, core/ports/index.ts:25-46, catalog-repo (escritura sin índice), catalog.ts:304-308 y la semántica de indexed (C-4). La tabla ya está separada (S-6)
M3En dec-0126/0127 (PROPOSED), antes de su lock: la huella de asset y el AssetId se definen por layout y por kind, no sólo como payload mdat/Clusters, y el styx.json reserva kinds.<id>dec-0127 §6.4 deriva el AssetId de la huella AV. Lockeado así, un árbol o un .pkg no tienen AssetId estable sin reabrir el lockXS: un párrafo en dec-0127 §6.4 y otro en §4.1, más una línea en dec-0126 §3.3. Sin código
M4La jerarquía de series de B5 (plan vertical, ola B) se modela como WorkRelation {parent, child, rel, ordinal} y no con columnas de serieB5 es el primer código con jerarquía. Con columnas propias, música, juegos y libros pedirían las suyas, y migrar las de series después toca datos reales0 extra: es la misma tarea de B5 con otra forma. Una tabla work_relations en lugar de 3 columnas en works
M5IMetadataProvider.enrich(assetId, facets) en vez de (assetId, index: IMediaIndex)track/plugins-sdk (después del vertical) lo hace público y dec-0130 §8 lo versiona. Después sería un semver major del SDKXS: providers.ts:15 + metadata-local-nfo (no usa el índice para leer el NFO) + enrichment-service.ts

Lo que no hace falta antes (es aditivo en cualquier momento): layout en MediaAsset (un campo con default), AssetManifest, asset_facets, extension points nuevos, ReadPurpose, bundleId en ingesta, EditionKind registrado, acciones, pipelines y el puente de dispositivos.

12. Nodos y milestones propuestos (NO están en styx.model.yml)

PropuestaTipoDepende deContenidoGate (borrador)
track/domain-media DM4 open-content-kinditem de gate nuevo en un track que ya existe—M1, M2 y M4Un kind desconocido llega por el wire sin romper web; una subida no AV entra como unidentified por el production path; B5 usa work_relations
track/content-kindstrack nuevo, zona servicestrack/domain-media (DM4), track/plugin-seams w2 (scanner, dec-0114)K1 registro + extension point content-type + asset_facets; K2 layout: tree + manifiesto + bundleId en ingesta; K3 extension point action + vocabulario de pasos + download/syncK1 con el primer kind no AV de verdad (P4); K2 con un árbol real de waxin re-escaneado sin cambios en < 5 s (la vara de SCAN-01)
track/pipelinestrack nuevo, zonas dataplane + servicestrack/byte-runtime/ingest, dec-0129 lockeado, el spike de compresión para storePL1 contrato de etapa Zig + orquestador en workers-svc; PL2 unpack (zip/7z/exFAT) como styx:parser con fuzz; PL3 la CMF expresada como pipelinePL2: fuzz largo de cada parser + test de zip-slip, symlink y bomba que fallan contra un desempaquetador ingenuo (mutante)
spike/console-homebrew-protocolsspike—Medir con las herramientas y versiones que usa waxin: servidor FTP, instalador remoto por URL, cargador de payloads, formatos de dump (carpeta e imagen)Produce las fichas de dispositivo y fija si install.pull es viable en PS4 y en PS5
track/kind-playstationtrack nuevo, zona interop (no ps4-homebrew, §2)track/content-kinds (K1-K3), track/pipelines (PL1-PL2), dec-0128 (dispositivos), el spike de arribaPS1 catálogo de dumps PS4/PS5 (facetas ps.sfo/ps.paramjson, sidecar/overlay); PS2 install.pull; PS3 puente de dispositivos + install.push; PS4 deploy.devPS2: un juego de waxin instalado en su consola desde Styx sin FTP a mano; PS4: bucle editar → compilar → ejecutar en consola sin pasos manuales
track/kind-backuptrack nuevo, zona dataplanetrack/content-kinds K2, track/pipelines PL1, el formato store del spikeBK1 backup.tree con sync incremental por manifiesto; BK2 snapshots con dedup entre ellosSegunda copia de un árbol de waxin con < 1 % de cambios: transfiere sólo los ficheros cambiados (bytes medidos), restaura bit a bit
Música y libroskinds dentro de track/content-kindsK1 + writers embedKinds music.* (acción play por el planner audio) y book (read)Con su consumidor, sin fecha

El orden recomendado está en P4. Ninguno de estos nodos entra en el camino crítico del vertical de vídeo salvo DM4, que es pequeño a propósito.

13. Alternativas rechazadas y enmiendas propuestas

Alternativas rechazadas

  • Un agregado paralelo por tipo (Game, Book, Album, al estilo de LiveService). Rechazada: dec-0107 separó live porque no es media finita (sin duración, sin ediciones, con ventana rodante). Un juego o un libro sí son media finita con ediciones y fuentes. Duplicar el catálogo por tipo duplicaría selector, bindings, transferencias y federación.
  • Mantener WorkKind como unión y añadir un literal por kind (la vía de la enmienda de SourceKind en dec-0107 P4). Es la alternativa barata y no se descarta del todo (P1): sirve mientras todos los kinds sean first-party. Se rechaza como diseño final porque impide que un plugin registre un tipo (dec-0130 quiere un SDK público) y obliga a recompilar los clientes por cada tipo nuevo.
  • kind como string libre sin registro. Rechazada: pierde el invariante de dec-0107 (alguien registraría channel) y la presentación quedaría sin descriptor.
  • Asset de árbol como N assets de fichero sueltos. Rechazada: un dump de PS5 no es instalable fichero a fichero, su identidad es el conjunto y la dedup entre versiones necesita el manifiesto.
  • Plugins que mueven bytes (un plugin de FTP en TS). Rechazada por r01 y por dec-0033: los bytes son del core. El plugin produce un plan; el core ejecuta.
  • Egress a la LAN desde el daemon. Rechazada: rompe I8 (DS5-01) para todo el data plane por una sola clase de acción. El puente aislado la contiene.
  • Montar imágenes exFAT en el host. Rechazada: privilegios (I8 cap_drop: ALL) y superficie del kernel. Parser de sólo lectura en el daemon.

Enmiendas propuestas (efectivas sólo en el lock)

  • dec-0107: el "conjunto cerrado de media finita" se implementa como registro validado (§4.2), con finite: true obligatorio y channel/live vetados. El reply cierra kind con pattern + validación contra el registro, no con Type.Union. El resto del lock (B, LiveService, P1-P6) no cambia.
  • dec-0114: "No aborda libros, cómics ni música como tipos de Work" pasa a "los aborda a través de plugins content-type (dec-0132)". La huella de §3 se generaliza por layout (§4.1). El IdentificationProposal.kind es un WorkKindId registrado.
  • dec-0126 / dec-0127 (PROPOSED, se proponen como texto para su lock, no como enmienda a un lock): M3 de §11.
  • dec-0129 (PROPOSED): origin gana device; registerIngested gana bundleId y manifestRoot para árboles.

14. Preguntas para waxin (bloquean el lock)

Contestadas o diferidas en el lock. Se conservan como registro.

#PreguntaRecomendación
P1¿Se enmienda la letra de dec-0107 para que WorkKind sea un registro validado (finito por construcción) en vez de una unión de literales?Sí. Conserva lo que dec-0107 protege (live fuera de Work) y es lo único que permite kinds por plugin. Si se prefiere no tocar un lock de ayer, la alternativa es la unión con un literal por kind first-party, y el registro llega con track/plugins-sdk (pagando entonces el cambio de clientes)
P2¿Ids con espacio de nombres (game.ps5, music.album) y los 6 de hoy sin prefijo?Sí. Cero migración y legible
P3Un backup de PC, ¿es una obra con un snapshot por edición, o una obra por snapshot?Una obra, un snapshot por edición (EditionKind snapshot): la dedup entre snapshots cae dentro de la obra, y la UI la lista como "Copia del PC de waxin" con su historial
P4¿Cuál es el primer tipo no de vídeo?Juegos de PS4/PS5 (track/kind-playstation PS1-PS2). Es el dolor que más se repite, y ejercita todo el núcleo: árbol, imagen, facetas, sidecar/overlay, verificación, acción install. backup.tree viene casi gratis después. Música y libros, cuando haya writer embed
P5¿Instalación en consola por pull (la consola descarga de Styx con un SCT) por defecto, y push por FTP sólo cuando la herramienta no permita otra cosa?Sí. Pull no abre egress del daemon y va por el egress rápido que ya existe. El spike confirma la viabilidad en cada consola
P6¿Puente de dispositivos como contenedor aparte (styx-device-bridge) con la única salida a la LAN?Sí. Mantiene I8 intacto para el daemon. La alternativa (egress LAN en el daemon) amplía la superficie de todo el data plane
P7¿El orquestador de pipelines vive en workers-svc?Sí. Es el servicio que r17 reservó para jobs durables y hoy no tiene responsabilidad. dec-0019 ya fijó el patrón (Postgres FSM + JetStream)
P8¿Entran M1, M2 y M4 como DM4 en el gate de track/domain-media antes de outcome/first-vertical, y M3 en el texto de dec-0126/0127 antes de su lock?Sí. Son pequeños, y después cuestan migraciones de clientes o reabrir un lock. M5 puede ir en cualquier momento antes de track/plugins-sdk
P9¿Los kinds de consola y homebrew quedan desactivados en modo público (dec-0130)?Sí, sin opción de activarlos en una instancia pública. Son backups personales
P10¿Qué formatos de archivo se desempaquetan? .zip y .7z tienen especificación abierta; RAR no (el descompresor de referencia tiene licencia propia).zip, .7z y exFAT primero, con parsers propios styx:parser. RAR sólo si waxin lo necesita, tras revisar su licencia
P11¿deploy.dev (enviar binarios a la consola para desarrollo) entra en el alcance?Sí, como último milestone (PS4), detrás del modo desarrollo del dispositivo y de la confirmación por sesión

15. Lo que este ADR NO decide

  • El formato de bytes de store (troceado, dedup, compresión, tiers): es del spike media-storage-compression.
  • Los protocolos concretos de cada herramienta homebrew: es del spike de §12.
  • Emulación o ejecución de juegos de PC desde Styx: fuera de alcance. "Play" de un juego de PC es descargar o sincronizar a un dispositivo de waxin.
  • El registro de dispositivos: es dec-0128.
  • Nodos y milestones: §12 es una propuesta; los crea la gobernanza tras el lock.
  • Nombres exactos de interfaces, subjects y tablas: los fija cada ticket con su consumidor.

Lock (2026-10-02, waxin)

Decisión de waxin vía AskUserQuestion (2026-10-02). Respuestas a §14:

  1. P8 y P1 — El core se abre antes de la vertical. Sí. M1, M2 y M4 de §11 entran como item de gate DM4 open-content-kind de track/domain-media, que es dependencia de outcome/first-vertical:

    • M1: Work.kind registrable por plugin y abierto en el wire, con registro validado. Es la enmienda a la letra de dec-0107 (P1: sí);
    • M2: el alta no exige el probe de vídeo; si falla, el asset entra como unidentified;
    • M4: las series como WorkRelation genérica, no como columnas de serie.

    M3 ya va en el texto del lock de dec-0127 (2026-10-02, rama w10/adr-datos): huella y AssetId por layout y por kind, y kinds.<id> reservado en styx.json. M5 puede ir en cualquier momento antes de track/plugins-sdk (recomendación del ADR, reversible).

  2. P4 — Primer tipo no audiovisual: juegos. Cambia el orden que recomendaba el ADR:

    • PS5 primero, después PS3 y PS4. PS3 no estaba en la propuesta: entra con sus propios formatos de dump, que identifica el spike de §12;
    • juegos de PC: repacks, DVD, mods de Steam Workshop y ports de mods. En resumen, ficheros comprimidos o en árbol (layout: tree e image, §4.1, más unpack, §7);
    • los ids siguen P2 (game.ps5, game.ps4, game.ps3, game.pc; nombres exactos en el ticket);
    • música y libros siguen detrás, cuando haya writer embed.
  3. P5 — Instalación en consola: pospuesta. No se decide entre pull y push. install.pull, install.push y el puente de dispositivos no tienen milestone hasta que waxin la reabra. La primera vertical de juegos es catálogo, verificación, desempaquetado y transferencia (download/sync, dec-0129), sin instalar en consola.

  4. P10 — Desempaquetado en la primera versión de la pipeline: RAR, zip y 7z. RAR sobre todo, porque es el formato de los repacks:

    • RAR exige revisar licencias antes de elegir librería. unrar tiene licencia propia que sólo permite descomprimir (no recrear el compresor). libarchive es BSD, y su soporte de RAR5 lo verifica el ticket. Nada GPL (como dec-0129 §3.4.3);
    • zip y 7z, con especificación abierta, con parsers styx:parser o la librería que salga de la misma revisión;
    • exFAT (imágenes de consola) sigue con la recomendación del ADR, como parámetro reversible, porque los dumps de PS5 lo necesitan;
    • las reglas de §8.2 no cambian: parser de entrada no confiable, fuzz largo, y tests de zip-slip, symlink y bomba que fallan contra un desempaquetador ingenuo (gate de PL2).
  5. Sin respuesta separada: recomendación del ADR como parámetro reversible. Se cambian sin ADR nuevo:

    • P2: ids con espacio de nombres (game.ps5, music.album), y los 6 de hoy sin prefijo;
    • P3: un backup de PC es una obra con un snapshot por edición (EditionKind snapshot);
    • P6: puente de dispositivos como contenedor aparte (styx-device-bridge), con la única salida a la LAN. Sólo aplica cuando se reabra la instalación (punto 3);
    • P7: el orquestador de pipelines vive en workers-svc;
    • P9: los kinds de consola y homebrew quedan desactivados en modo público (dec-0130), sin opción de activarlos;
    • P11: deploy.dev entra como último milestone de consola, detrás de la instalación (punto 3).
  6. Enmiendas que entran en vigor hoy (§13), con banner en cada ADR:

    • dec-0107: el "conjunto cerrado de media finita" se implementa como registro validado (§4.2), con finite: true obligatorio y channel/live vetados. El reply cierra kind con pattern + validación contra el registro, no con Type.Union. El resto de su lock (opción B, LiveService, P1–P6) no cambia. dec-0107 está LOCKED en w11/locks; en esta rama todavía figura como PROPOSED y el banner se integra con ese lock;
    • dec-0114: libros, cómics y música se abordan con plugins content-type; la huella de §3 se generaliza por layout (§4.1); IdentificationProposal.kind es un WorkKindId registrado.
  7. Enmienda que este lock no aplica: origin: device, bundleId y manifestRoot en registerIngested (§13, para dec-0129). dec-0129 se lockeó el mismo día en w10/adr-transferencias sin recogerlas. Quedan propuestas para cuando exista su consumidor (árboles en ingesta, K2 de track/content-kinds), con su propio AskUserQuestion.

  8. Nodos. Este lock sólo añade DM4 al gate de track/domain-media. El resto de §12 (track/content-kinds, track/pipelines, el spike de protocolos de consola, track/kind-playstation, track/kind-backup) sigue siendo una propuesta que crea la gobernanza tras el lock, ahora con el orden del punto 2: PS5 → PS3/PS4, juegos de PC con unpack de RAR/zip/7z, y la instalación fuera hasta nuevo aviso.

Back-refs

  • Insumo: docs/track/plugin-seams/plans/multi-tipo-auditoria.md.
  • Enmienda (en vigor desde el lock) a: dec-0107 (§4.2) y dec-0114 (§13). Los dos llevan banner.
  • Propone texto para el lock de: dec-0126 y dec-0127 (M3), y dec-0129 (origin: device, bundleId).
  • Relacionados: dec-0019 (la CMF como instancia), dec-0033 (familia tipada), dec-0088 (colisión de vocabulario), dec-0117/dec-0118 (§8), dec-0128 (dispositivos), dec-0130 (modo público, SDK público), dec-0131 (federación).