Vista generada de dec-0057: r43 — D5-01: migrar a zig 0.17 en vez de vendorizar el dev build muerto
docs/decisions/dec-0057-zig-017-migration-over-vendorize.mdVista generada desde
docs/decisions/dec-0057-zig-017-migration-over-vendorize.md. No se edita a mano:bun run docs:genla regenera ybun run docs:checkfalla si difiere. El estado aquí es el del model: si discrepa con otra página, manda el model.
| Campo | Valor |
|---|---|
| Estado | LOCKED |
| Fecha | 2026-07-02 |
| Referencia legada | r43 |
| Fichero | docs/decisions/dec-0057-zig-017-migration-over-vendorize.md |
Nodos del roadmap que lo citan en refs: ninguno.
Páginas de la documentación que lo citan: ninguna todavía.
Leído de docs/decisions/dec-0057-zig-017-migration-over-vendorize.md, el fichero canónico.
El data plane Zig (native/zig/media-daemon/ → quic-zig → libxev) sólo compilaba junto
contra zig 0.16.0-dev.3133+5ec8e45f3, que zig purgó del upstream (404 en ziglang.org).
Stable 0.16.0 rompe el daemon; master 0.17-dev rompe las deps. Un docker build que resuelva
zig del upstream aborta → M2.F.4 (dockerizar) bloqueado, y el zig job del CI degradado a
informativo (continue-on-error, no hard-gate) desde el episodio 2026-06-27.
El brief docs/handoffs/D5-01-zig-toolchain-decision-brief.md recomendó Opción A — vendorizar
el dev.3133 como GitHub Release asset, y estimó Opción B (migrar/forkear) en "semanas".
waxin cuestionó el band-aid: "no podemos actualizar al 17? o algo? o buscar mejor?" — y pidió
diligencia real antes de lockear.
3 Explore agents en paralelo + un build REAL con zig 0.17.0-dev.978 (que waxin ya tenía
local vía zv) + micro-tests. Hallazgos verificados:
StringArrayHashMap / AutoHashMapUnmanaged / ArrayListUnmanaged.empty / DebugAllocator
/ std.Thread.spawn / std.fmt siguen vivos en 0.17 (verificado vs master GitHub). No
usa std.Io/std.fs (usa std.c FFI directo) → esquiva el churn del Writergate. Único
posible ajuste: @bitCast(std.posix.O) (semántica "logical bit layout" 0.17), transparente en
little-endian.@ptrCast/@intCast/@bitCast single-arg = FINE en 0.17. El agente de quic-zig afirmó
una migración de "300+ sitios @ptrCast" citando "precedente 0.12→0.13" → REFUTADO compilando
un test real con 0.17 (.o generado). Era confusión con una migración VIEJA (0.11→0.12); el
single-arg result-location es la forma actual desde 0.12. Esa claim inflaba el estimate ~3-4h.docs/references/quic-zig/build.zig: b.args removido en 0.17 (~13 sitios en run-steps de
dev tooling — interop clients/benchmarks, no la librería que importa el daemon).libxev/build.zig: findProgram() cambió de firma (2-arg → 1-arg).addModule/createModule/b.path = probablemente fine: el build.zig del daemon usa las
mismas APIs y pasó en el build real (el primer error fue en la build.zig de la DEP, no del
daemon).endel/quic-zig HEAD == el pin 344c0e4 (0 commits ahead, migración
"Phase 2 in-progress" detenida). mitchellh/libxev pin a82a04e ya es prácticamente HEAD
(2 commits triviales después) y no soporta 0.17 aún. → ambos requieren fork/patch local.Veredicto de esfuerzo (calibrado LLM executor, ÷8 vs humano): MEDIO (~2-5h), NO DURO.
Lock D5-01 = Opción B — migrar el data plane a zig 0.17. Se rechaza vendorizar el dev.3133 muerto como lock (queda como fallback documentado).
Alcance del lock:
MKS2508/quic-zig (extendiendo r36, que ya prevé esta fork para el leak fix
c4c9069): patchear build.zig (b.args → nueva API de run-args) + bump minimum_zig_version
a 0.17.0. El fork ahora carga DOS cosas: el leak fix P1 (r36) + los patches de Build API 0.17.libxev (pin maxed, upstream no ha migrado): findProgram 1-arg + cualquier
shim residual de std.posix que 0.17 rompa. Preferir PR upstream a mitchellh; fork local si tarda..gitmodules / build.zig.zon: apuntar a los forks + pin a un 0.17-dev vivo
(0.17.0-dev.1158+1d1193aa7, disponible en ziglang.org hoy).minimum_zig; posible ajuste @bitCast si el build lo pide).guard.yml zig job): pasa de continue-on-error informativo a hard-gate (resuelve
0.17-dev del index oficial, que ahora compila).build.zig de las deps
más allá de los 2 errores confirmados es mecánica pero no 100% enumerada. El handoff arranca por
un spike de build iterativo (patch → rebuild → siguiente error) para cuantificar antes del fork
formal. Si aparece un blocker DURO real (unknown-unknown), reabrir esta decisión (fallback A sigue
disponible).NO autónoma por axon. Handoff → task-executor:
docs/handoffs/next-prompt-d5-01-zig-017-migration.md. axon verifica (build 0.17 verde +
146/146 tests + smoke e2e re-run + CI zig-job hard-gate green) antes de cerrar.
El spike M0 del handoff corrió (task-executor a406db9e + verificación independiente axon).
La decisión B (migrar a 0.17) se mantiene, pero la diligencia original no escaneó el
source de quic-zig más allá de su build.zig y erró el scope. Correcciones al record:
** fue eliminado del lenguaje en 0.17** (repetición de arrays/tuplas, ej. [_]u8{0} ** 12) fue removido del grammar de Zig 0.17,
reemplazado por @splat. Verificado en tres capas (tokenizer sin asterisk_asterisk, Parse.zig
sin array_mult en la tabla de precedencia, AST sin nodo .array_mult) contra dos fuentes
independientes:
0.17.0-dev.978+a078d55a2 (vía zv): [_]u8{0} ** 8 → error: binary operator '*' has whitespace on one side; @splat(0) da bytes idénticos.codeberg.org/ziglang/zig, el trayecto VIVO de Zig): confirma la remoción.⚠️ Nota infra crítica: github.com/ziglang/zig es un mirror CONGELADO (HEAD == "README:
migrated to codeberg") y todavía muestra ** fósil. No grepear github para estado actual de Zig.
Referencia local canónica = clone de codeberg en ../zig-lang (sibling de styx, live master).
| Componente | r43 original | Realidad verificada |
|---|---|---|
Daemon source (media-daemon+media-core) | trivial | ✅ 0 sitios **-repeat (los 55 hits de grep crudo son markdown-bold en doc-comments) |
| Build.zig de deps | 2 patches | 4 — quic-zig b.args→addPassthruArgs (13 sitios) + libxev findProgram 1-arg + libxev install_prefix removido (idiom pkg-config relocatable) + libxev build_root→root (×3). Todos mecánicos, fixeados en el working-tree del spike |
| quic-zig source | no escaneado | ~160 sitios **→@splat (36 tls13.zig, 35 connection.zig, 28 crypto.zig — el core del handshake) |
Mecánicamente sigue MEDIO (~1-1.5h LLM): @splat es reemplazo 1:1 (mismos bytes, cero cambio de
runtime), verificado en sandbox para todas las formas (byte escalar, char, struct-literal, longitud por
const, default de campo). Edge cases acotados: 3 string-repeat ("x" ** N → cambio de tipo + & en
call-site) + 1 multi-elemento en un test{} de libxev (comptime loop). NO es DURO, pero el SCOPE es
más ancho de lo que r43 vendió: parchea source TLS/QUIC de la dep, no solo su build.zig. Fallback A
(vendorizar el 0.16-dev.3133 muerto) sigue disponible per este ADR si el scope creciera.
r43 asumió "fork GitHub". Refinado tras confirmar el terreno:
MKS2508/quic-zig (fork de endel/quic-zig, 8 ramas vivas). Ya carga el
leak fix r36 como PR #29 (pr/disposal-on-ack-lifecycle) + issue #30, ambos OPEN sin respuesta
upstream. La migración 0.17 (@splat + 4 build.zig) se apila en una rama nueva encima → cero carga
nueva (la fork ya existe; era la incomodidad de waxin — resuelta: reusar ≠ crear). NO vendor-in-tree.mitchellh/libxev no tiene issue/PR de 0.17 (seríamos los primeros).
Abrir issue + PR con los 3 deltas de build.zig; fork MKS2508/libxev solo si no responden.endel/quic-zig) — "PTO does not rescue unidirectional send streams". Bug de
corrección relevante para Styx (exposure = WebTransport/bare-QUIC empujando bulk data por uni
streams = MoQ media delivery). Se arregla en la fork como commit atómico SEPARADO, NO bundle con la
migración toolchain (mantiene la migración verificable-limpia). PR upstream separado (o stacked), no
mezclado con #29.Clone shallow de codeberg ziglang/zig → ../zig-lang (sibling de styx, fuera del tree). Fuente de
verdad local para verificación de APIs/grammar de Zig — evita round-trips web y el mirror github stale.
.decls gap closedDrift detectado durante M3 verification (Lane B, commit 9da4452): el spec note v36 schemaNote
documentó .decls→std.meta.declarations como hecho, pero la migración quedó incompleta en el
libxev fork (MKS2508/libxev@2db65bf, rama feat/zig-0.17). 6 sitios con .decls (API removida
en zig 0.17 std.Type.Struct) sobrevivieron al push inicial:
| Archivo | Línea | Contexto |
|---|---|---|
src/main.zig | 47 | comptime validation block en xev |
src/watcher/tcp.zig | 523 | test "TCP: Stream decls" |
src/watcher/file.zig | 638 | test "File: Stream decls" |
src/watcher/udp.zig | 918 | test "UDP: Stream decls" |
src/watcher/stream.zig | 148 | comptime de Pollable |
src/watcher/stream.zig | 1136 | test "Stream decls" |
Symptom: zig build test (test-only) pasaba 146/146 pero zig build (install binaries
styx-media-daemon + styx-wt-client) fallaba con "no field named 'decls' in struct
'lang.Type.Struct'". Smoke D5-01 M5 funcionó porque usó binario pre-built (Jul 2), no rebuild fresco.
Fix ejecutado en fork (workflow multi-repo, 3 pushes):
MKS2508/libxev@7f067af — .decls→.decl_names (zig 0.17 std.Type.Struct API change). Loop
variable cambia tipo de Declaration struct (con .name) a [:0]const u8 (string directo);
refs downstream decl.name → solo decl. Mismo patrón canónico que styx ya tenía migrado en
docs/references/quic-zig/src/event_loop.zig:261,1359.MKS2508/quic-zig@e1a034e — submodule bump: build.zig.zon .libxev.url SHA 99e0031 →
7f067af + nuevo content hash libxev-0.0.0-86vtc6gUFADXrA3PX99k2xjY0Nsyf6qx08pmEUOO4G6S.styx@7776291 — submodule pin bump registrado en el repo parent.Verificación: zig build exit 0 (binarios styx-media-daemon 6.7M + styx-wt-client 5.2M),
zig build test 13/13 + 146/146, bun run check:roadmap 28/28.
Implicación doctrinal: la lista exhaustiva de olas migradas en r43 (→@splat (159), .decls→std.meta.declarations, params→param_types, std.meta.Tuple removido, etc) era correcta en
styx-owned code + quic-zig fork, pero incompleta en libxev fork. Doctrina "upstream-first →
fork mientras tanto" sigue válida; lo que faltó fue un sweep comprehensivo del fork en push-time. Fix
de proceso para futuras migraciones: pre-push grep exhaustivo de todos los símbolos documentados
como migrados contra el fork entero (no solo el path principal del código migrado). Aplicar cuando
migración similar toque forks adicionales.
Ref: docs/handoffs/archive/M2-integration-report.md §8.