dec-0103

Vista generada de dec-0103: La promoción de Candidate H consume zkit como dependencia real

ImplementadoSin versión del tren todavía· generada desde docs/decisions/dec-0103-byte-runtime-consume-zkit.md
track/docsdec-0124track/docs:DC10

Vista generada desde docs/decisions/dec-0103-byte-runtime-consume-zkit.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-08-27
Ficherodocs/decisions/dec-0103-byte-runtime-consume-zkit.md

Enmienda a: r44

Por qué importa (del frontmatter del ADR):

Gobierna CÓMO se ejecuta la promoción de Candidate H en track/byte-runtime ahora que las primitivas rescatadas viven en un repo externo (MKS2508/zkit). Sin este ADR, un executor de M6 leería r44 y reimplementaría en native/zig/ primitivas que ya existen — deshaciendo la promoción de la condición #5 de r44 ejecutada el 2026-08-27. También resuelve la colisión entre las condiciones #1 y #5 de r44, que se volvieron mutuamente inejecutables al retirarse el spike.

Nodos del roadmap que lo citan en refs: track/byte-runtime, track/byte-runtime/m5, track/byte-runtime/m6, track/byte-runtime/m7, track/byte-runtime/sec, track/byte-runtime/zkit, track/byte-runtime/ingest

Páginas de la documentación que lo citan: Puesta en marcha de un nodo (implementado), Generadores de la documentación (especificado), Guardas del repositorio (implementado), Arquitectura (implementado), Seguridad del data plane (implementado), Byte runtime (implementado), zkit, spire y conduit (implementado)

Texto del ADR

Leído de docs/decisions/dec-0103-byte-runtime-consume-zkit.md, el fichero canónico.

dec-0103 — La promoción de Candidate H consume zkit como dependencia real

  • Fecha: 2026-08-27
  • Estado: LOCKED
  • Decisor: waxin — "quiero la mejor arquitectura, da igual el coste" (instrucción directa, sesión de coordinación cross-repo styx↔zkit). Aplicado sin AskUserQuestion por el corolario anti-trivialidad del criterio de recomendación: la respuesta arquitectónica no admitía tensión real.
  • Enmienda: r44 (condiciones #1, #2, #5)
  • Depende de: dec-0102 (sin el levantamiento del freeze, track/byte-runtime no avanza)

Contexto — el hecho nuevo que r44 no podía prever

r44 (2026-07-12) seleccionó Candidate H como la arquitectura del byte-runtime, y su condición #5 mandaba: "el código del spike se elimina tras promover (regla spikes)".

Esa condición se ejecutó el 2026-08-27, seis semanas después, pero no hacia native/zig/media-core/ como r44 asumía implícitamente: las primitivas genéricas se rescataron a un repo nuevo standalone, MKS2508/zkit, dentro del programa de convergencia del toolchain Zig (zkit.model.yml, nodo zkit/rescue → zkit/green, mergeado en a3ed91f).

Superficie pública de zkit hoy (src/root.zig, verificado):

SímboloOrigenUso esperado en byte-runtime
SubscriberQueuespikes/candidate-hfanout ready-driven (r46 §3)
ReorderBuffer / SequenceNumberspikes/candidate-hdelivery pump
HungWorkerWatchdog / WatchdogStatusspikes/candidate-hstall/detach (r44 #8)
HandleSlabhyperdiff + candidate-hHandlePool de producción (r44 #2)
TrackingAllocatorhyperdiffinstrumentación de memoria

El resultado es que r44 quedó descrita contra un layout que ya no existe. Este ADR la re-ancla.

Qué se decide

1. track/byte-runtime consume zkit como dependencia .zon real. No se reimplementa.

Prohibido copiar, vendorizar o re-derivar SubscriberQueue, ReorderBuffer, HungWorkerWatchdog, HandleSlab o TrackingAllocator dentro de native/zig/. Ya se promovieron una vez; duplicarlas sería des-ejecutar la condición #5 de r44 y crear exactamente el par divergente que la regla de spikes existe para evitar.

Coste asumido y aceptado: styx gana una arista de dependencia externa hacia un repo que hoy no tiene release ni versionado semántico. Es más barato que dos copias que derivan.

2. El arreglo de thread-safety (r44 #2 y #9) se hace aguas arriba, en zkit.

Verificado 2026-08-27: zkit/src/handle.zig tiene cero atomics y documenta literalmente "Not thread-safe. Callers must synchronize externally... (typical FFI pattern: single-threaded consumer)". subscriber_queue.zig idem. El rescate preservó fielmente el contrato single-threaded del spike — que es correcto como rescate, e insuficiente para el delivery plane de styx, que es multi-thread (las 4 races TSAN que r44 #2 nombra).

Por tanto la condición #2 de r44 no está satisfecha por el rescate, y su trabajo no vive en styx: una variante "styx-atómica" en native/zig/ sería el fork que el punto 1 prohíbe. El trabajo es un nodo del roadmap de zkit, y track/byte-runtime lo consume cuando aterrice.

3. Colisión r44 #1 ↔ #5 — resuelta: la gauntlet gatea el merge, no precede al port.

r44 #1 pide que la gauntlet compuesta H-F5 corra antes de promover a media-core/, sobre el spike. r44 #5 mandó borrar el spike. Ejecutada #5, la letra de #1 es inejecutable: no queda código del spike sobre el que correrla (EVIDENCE-REPORT.md §5 confirma que nunca llegó a correrse ahí). No es una reinterpretación de conveniencia — son dos condiciones de la misma ADR que se volvieron incompatibles al ejecutarse una de ellas.

Resolución que preserva la intención de #1 (que nada sin validar llegue al árbol de producción):

La promoción de M6 se materializa en rama. M7 (gauntlet) es el gate del merge de esa rama, no un sello posterior. track/byte-runtime/m6 NO puede declararse done sin que M7 haya pasado.

M6 → M7 en el DAG es el orden de ejecución; M7 bloquea el cierre de M6 es el contrato.

Efecto sobre los artefactos

  • zkit.model.yml: trans/wire-consumers se parte en dos — trans/wire-styx (esta arista, owner axon2) y trans/wire-hyperdiff (territorio de axon1). Ambos blocked por la misma pasada de convergencia. La diferencia real es de método, no de blocker: hyperdiff se wirea con parity gate (lock/bindings-parity) porque tiene paquetes publicados; styx no lo necesita (es el origen del rescate, cero consumidores publicados). Corrección de waxin sobre mi primer borrador, que daba «está en producción» como motivo de aplazamiento: "da igual que mc esté en prod, no lo rompes en prod" — el parity gate es la tarea que impide romperlo, no una razón para no hacerlo. Cola-perro, y la había invertido yo.
  • docs/track/byte-runtime/plans/byte-runtime-decompose.plan.md: addendum — M5/M6 nombran los símbolos de zkit; M8 registra que el borrado del spike ya existe en rama.
  • styx.model.yml: track/byte-runtime cita dec-0103.

Lo que este ADR NO decide

  • No enmienda lock/standalone (zkit.model.yml). Ese lock dice "CERO aristas .zon ... en esta pasada" y su razón es que el camino verde del toolchain no dependa de que zkit exista. Sigue intacto: la arista de este ADR es posterior a que la convergencia de styx a 0.17.0-dev.1884 aterrice en master (rama chore/zig-017-convergence, hoy sin mergear y en review de waxin). El nodo nace bloqueado, no accionable.
  • No toca hyperdiff. El parity gate de @mks2508/hyperdiff-* es territorio de axon1.
  • No fija release ni versionado de zkit. Cómo se declara la dep (path vs tarball con hash) es decisión de implementación de trans/wire-styx.
  • No reabre la selección de Candidate H (r44 sigue siendo el lock de arquitectura) ni el veredicto de "dos BackingStore" (entregable de M1).
  • No cierra C12-e2e (r44 #3), que sigue bloqueado por TKT-027/U4 — cerrarlo por scope creep sigue explícitamente prohibido por r44.

Enmienda 2026-09-29 — lo que styx consume de zkit (rama w4/zkit)

Registro, no decisión nueva: §1 se cumple y queda vigilado. La arista .zon apunta a MKS2508/zkit@c6195d3 (rama feat/styx-extraction; el hash va en native/zig/build.zig.zon). Cada símbolo tiene un consumidor en un camino de producción de styx, salvo los marcados como «tests»:

Símbolo de zkitConsumidor de producción en styx
time.{monotonicNs,monotonicMs,sleepNs,ns_per_ms,Stopwatch}todo el daemon (relojes, esperas, métricas)
sync.{Mutex,Condition}packager (transmux), backpressure_waker
safety.Mutextodos los locks del daemon y de media-core (antes pthread_mutex_t)
fs.{File,createFile,openRead,readFileAlloc,makeDir,openRaw,max_path_bytes}, os.{getenv,getenvInt,randomBytes}caches L2/L3, certificados TLS, config por entorno, ids de sesión
safety.fs (Root, openFile)LocalFileSource: todo acceso a medios bajo STYX_MEDIA_ROOT
errors.ErrorSpaceWithSourceErrorSpace de local_file.zig → TS generado en @styx/source-sdk
ZeroCopyBuffer, BufferGuardobjetos MoQT, caché L1
HandleSlab (+ encodeHandle/decodeSlot/decodeGeneration)tiered_cache (BufferHandle{slot, generation} de C7)
PriorityQueuescheduler del daemon
LatestValueTransportSignalsChannel
AtomicHistogramhistograma de rangos del transporte
BoundedQueuePendingDeliveryQueue (MoQT)
CancelTokencontrato del motor de medios (probe/demux)
safety.BoundedReader, safety.BitReaderparsers de contenedor (ISOBMFF, Matroska, codec configs, frames)
safety.BudgetAllocatorpresupuesto de recepción por sesión MoQT y del daemon (SEC-Z08)
TrackingAllocatorcontabilidad de memoria del daemon (main.zig)
testing.Fixture, safety.fuzz, safety.LeakReporttests

Sin consumidor en styx hoy, así que no se consumen (nada que wirear sin workload, r28): SubscriberQueue, ReorderBuffer/SequenceNumber, HungWorkerWatchdog, WakeupPipe, ConcurrentHandleSlab, safety.{Handle,TypedSlab}, safety.checked, log.

Las copias locales que existían se borraron en la misma rama (reloj, mutex/cond pthread, shims de fs/env/random, test_fixture.zig, ZeroCopyBuffer/lifecycle, PriorityQueue, el canal de señales, el histograma, la cola de entrega, los lectores de bytes/bits de los parsers y el reparto del handle del slab).

Guard: zig build audit:safety (regla Z1, bun run check:zkit-no-reimpl) falla si un fichero de media-daemon/ o media-core/ declara un símbolo con nombre de zkit que no es un alias de zkit, llama a la libc que zkit envuelve o escribe un extern "c" a mano. Los nombres los lee del propio zkit fijado, así que un símbolo nuevo de zkit queda protegido sin tocar el guard. Corre en zig build y zig build test.