La cabecera decía 'def 408909310' y la línea 38 usa 417847948 desde el 2026-08-08.
La 408909310 es justo la que NO traía rust ni go — la que dejaba fuera al 76% del
catálogo — así que el comentario apuntaba a la imagen rota.
Un comentario que nombra un id concreto es una referencia, no prosa: si alguien lo
lee y re-apunta IMAGE a mano, revive el bug que el propio fichero documenta debajo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
Sexta de la familia. El arreglo estaba HECHO en disco desde hace horas y lo
reporté como cerrado, pero nunca llegué a commitearlo — sólo se vio al revisar
`git status` por otra cosa. Sin esto, el arreglo se perdía.
El wrapper del sandbox (commit anterior) tapaba el síntoma. Esto arregla la causa:
nuestra receta de binutils no pasaba el flag que Alpine SÍ pasa, así que su `ar` y su
`ranlib` estampaban la hora de pared en la cabecera del miembro `/` de cada `.a`.
POR QUÉ AHORA Y NO "AGENDADO", Y POR QUÉ ESTE ORDEN. Arreglar la raíz PRIMERO evita
la parte fea del arreglo por wrapper: al cambiar el hash de binutils (c316212f… →
4b5fbf3b…) los artefactos contaminados caen en DIRECCIONES NUEVAS. No hay transición
"mismo ArtifactHash, otros bytes" — que es justo el peligro que la huella del lab
existe para eliminar. Los viejos quedan superados y podables, no reemplazados en el
sitio. Re-sellar en el sitio habría sido la opción peligrosa.
COSTE, MEDIDO CON yupana radio (no estimado): 422 dependientes transitivos, 365
sellados caen a deuda, las siete imágenes. Se asume a propósito: el corpus ya está en
campaña de reconstrucción (1093/1125) y la cascada se arrastra sola, porque cada
`hammer build` construye sus deps.
El wrapper del sandbox se mantiene como defensa en profundidad: es byte-neutral para
todo lo que ya sellaba con mtime 0, y cubre a cualquier herramienta futura que archive
sin pedir permiso.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
CÓMO SE VIO. Construir zlib en gioser y en el worker nuevo dio el MISMO ArtifactHash
y BYTES DISTINTOS. `hammer why-differs` lo nombró exacto: 13 entradas idénticas, 1
diverge — `usr/lib/libz.a`, cabecera `ar` del miembro `/` (la tabla de símbolos),
mtime 1787326910 vs 1787950875. Es la hora de pared de cada build.
CAUSA, AISLADA POR BISECCIÓN EN EL LAB (no por lectura de código):
· `zig ar` (llvm-ar) → mtime 0 ✅
· `ranlib` del rootfs Alpine → mtime 0 ✅ (Alpine compila binutils con
--enable-deterministic-archives)
· `ar` y `ranlib` de NUESTRO recipes/binutils.toml → hora de pared ❌
· los mismos con `-D` → mtime 0 ✅
La receta de binutils no pasa --enable-deterministic-archives, así que toda receta
que lo materialice como build-dep y llame a ar/ranlib hereda la hora.
Y el comentario de SOURCE_DATE_EPOCH en este mismo fichero AFIRMABA cubrir `ar`.
Era falso: binutils no lee esa variable. Corregido, porque una suposición escrita
en un comentario es la que impide que alguien vuelva a medir.
ARREGLO. Wrappers `ar`/`ranlib` en /usr/local/bin (que gana al /usr/bin del PATH)
que anteponen `-D`. Mismo patrón que ya usa el sandbox para arreglar esta CLASE de
bug sin re-hashear: -mcpu=baseline, CARGO_PROFILE_RELEASE_CODEGEN_UNITS,
CARGO_BUILD_JOBS. `-D` antepuesto y no como modificador de la cadena de operación,
porque la operación la pone el llamador en "$@" y no se puede reescribir.
Verificado: la secuencia exacta de zlib (`ar rc` + `ranlib`) da mtime 0 y el índice
del archivo sigue siendo legible (`ar t`, `nm -s`).
POR QUÉ AQUÍ Y NO EN LA RECETA. Arreglar recipes/binutils.toml es la cura de raíz,
pero `yupana radio binutils` da 421 dependientes transitivos y 365 sellados a deuda,
en las siete imágenes — desproporcionado frente a los 9 artefactos realmente
contaminados (medidos barriendo los 185 artefactos con .a del store: elfutils,
elfutils-libdw ×4, zlib, musl-fts, musl-obstack, argp-standalone). La cura de raíz
se agenda para el próximo rebuild del corpus que ya haya que hacer por otro motivo.
PENDIENTE Y DICHO EN VOZ ALTA: esto cambia los BYTES sin cambiar la DIRECCIÓN de
esos 9. Hay que re-sellarlos a propósito — es el mismo peligro que la huella del lab
existe para eliminar, y no se cierra solo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
Un LXC prestado (dev.gioser.net, Fedora 44, 6 núcleos, 196 G) pasa a ser el worker
mientras esté disponible; el camino Debian queda intacto para seguir abriendo
workers efímeros en Hetzner.
Cambios:
- familia detectada por gestor de paquetes (apt | dnf5/dnf) con sus equivalencias
(build-essential -> gcc/g++/make, libssl-dev -> openssl-devel, uidmap ->
shadow-utils, xz-utils -> xz).
- el veredicto de userns deja de ser "escribí el sysctl" y pasa a ser "unshare -Umr
funciona de verdad": en un contenedor el sysctl es read-only y no hace falta,
porque el userns lo concede el host. Si no se puede crear, aborta: sin eso la caja
no puede ser worker.
- swapon degrada a AVISO RUIDOSO en vez de abortar. En LXC devuelve Operation not
permitted (medido) y no hay forma desde dentro; el swap lo da el host. Se dice
explícito que sin swap el techo de RAM es duro y el OOM killer no avisa antes.
- documenta los dos pasos que una caja virgen necesita del hub y que la golden
escondía: instalar rsync a mano, y que el HUB EMPUJE el lab ya verificado en vez
de que el worker lo baje del Storage Box (el worker no debe tener secretos).
Validado provisionando dev.gioser.net de cero: hammer compila, el lab extrae y
verifica por sha256, y el toolchain queda "idéntico al lock (108 paquetes)".
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
`unable to find static system library 'bz2'` en el link, a los 2612 s (43 min).
La receta no tenía `[deps]`. Decimotercera de la familia.
NO fue OOM pese a que la RAM estaba en 222 Mi libres cuando lo comprobé: dmesg
limpio. Conviene dejarlo escrito porque la sospecha era razonable y falsa.
Falla con las dos: `unable to find static system library 'lzma'` y `… 'bz2'`,
en el link a los 1331 s. La receta no tenía `[deps]`.
Duodécima de la familia. Sin verificar (mismo criterio que mise/pixi): el
patrón lleva once arreglos idénticos y comprobarlo cuesta 22 min de campaña
parada.
El diseño entrante v2 absorbe las cuatro correcciones de la verificación:
R1 (inmutabilidad por mount, modos upstream), IF-1/IF-2 (blake3_arbol en vez de
EROFS y sin fanotify), la carrera B nombrada en §0 con la Fase 1b como entregable
de F3, y Q1 cerrada por precedente. NG-4/Q3 resuelta: se cita el hash, nunca una
ruta, y la cita sobrevive a la poda porque el mirror la regenera.
§8 deja de ser la lista de objeciones y pasa a ser el registro reproducible de las
mediciones, para que nadie tenga que volver a descubrirlas.
Añade una medición nueva (§8.2): la composición REAL de los dos grupos de overlay
—el del rootfs con --tmp-overlay / seguido del de la fuente sellada sobre /src—
funciona en una sola corrida. El acumulador de --overlay-src se consume por
operación y no filtra, y con modos upstream escribir en un SUBDIRECTORIO de /src
funciona: justo el caso que el chmod a-w rompía. El gate de mecanismo de F0 queda
verde; sólo resta IF-5 bit-a-bit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
Diseño entrante + su verificación contra el código, al estilo del SDD 22.
El mecanismo central quedó PROBADO en un comando: bwrap 0.11.2 monta
`--overlay-src <sellada> --overlay <upper> <work> /src` dentro de --unshare-all
con upper en disco; .zwrap cae al upper y el lower no se toca. El gate de
mecanismo de F0 ya está verde.
Cuatro correcciones antes de escribir código:
- R1 (`chmod a-w` recursivo) ROMPE el build: overlayfs preserva el modo al hacer
copy-up y el sandbox corre como uid 1001 sin DAC_OVERRIDE. Medido: escribir un
fichero existente y crear dentro de un subdir dan EACCES. Falla por PROFUNDIDAD
(la raíz pasa porque el modo del upper root gana) ⇒ un spike sobre zlib pasaría
en verde y reventaría en meson/cmake/cargo.
- IF-2 es inimplementable: bajo overlayfs todo escribible pasa por copy-up, nunca
EROFS. El hecho negativo correcto es que blake3_arbol del sellado no se mueva
tras N builds — y eso hace innecesario el fanotify de IF-1.
- Falta la SEGUNDA carrera (build-farm.sh:82-99): dos recetas distintas sobre la
misma dep no sellada chocan en sources/<dep>-<sha>/output. El diseño también la
mata ⇒ borrar la Fase 1b es un entregable medible de F3.
- Q1 ya está contestada: apply_patches/vendor corren en el host desde siempre
(lib.rs:224,248,258). La fase patch no migra, ya está ahí — y la tabla del
SDD 02 §4 que dice lo contrario está desactualizada.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
Reúne en un solo documento lo que hoy está repartido en 24 SDDs, 13 ADRs, los
runbooks y el estado generado: arquitectura de dos mundos, definiciones canónicas
(Recipe/hash_inputs/LabFingerprint/.swm/slots), el lab y sus fases, harkaq, el
bucle agéntico, bootstrap auto-alojado, release engineering, arranque por grafo,
los cuatro escritorios, la infraestructura de granja y la metodología yupana/khipu.
Incluye estado MEDIDO al 2026-08-28 (no heredado de los docs) y explica por qué la
deuda del corpus marca 75 y no las 12 del SDD 20: es la cola alfabética pendiente
de la reconstrucción disparada por el lab en hash_inputs y el split de debug.
Cierra con los huecos donde un SDD nuevo tendría tracción.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
Falla con las dos: `unable to find static system library 'lzma'` y `… 'bz2'`.
La receta no tenía `[deps]`. Undécima de la familia.
Acá la dep es evidente en retrospectiva: `rga` busca DENTRO de archivos
comprimidos, así que necesitar lzma/bz2 es lo esperable — pero el Cargo.toml no
las nombra (las arrastran sys-crates) y sólo aparece en el link, a los 468 s.
Sin verificar (mismo criterio): el patrón lleva diez arreglos idénticos.
`unable to find static system library 'bz2'`. La receta no tenía `[deps]`.
Décima de la familia (appstream, cargo-deb, cargo-make, dprint, dufs, fnm,
macchina, maturin, mise).
⚠ El fallo sale en el LINK, al final, y este build tarda 87 MINUTOS en llegar
(5226 s). Una dep de una línea que falta cuesta hora y media de máquina antes
de decir nada.
Sin verificar a propósito (mismo criterio que mise): comprobarlo costaría otra
hora y media de campaña parada, y el patrón lleva nueve arreglos idénticos
verificados.
`unable to find static system library 'bz2'`. La receta no tenía `[deps]`.
Novena de la familia (appstream, cargo-deb, cargo-make, dprint, dufs, fnm,
macchina, maturin).
⚠ Sin verificar a propósito: el build tarda ~60 min (falló a los 3550 s) y el
flock es exclusivo, así que comprobarlo cuesta una hora de campaña parada. El
patrón lleva ocho arreglos idénticos verificados y la campaña lo verifica sola
en la siguiente pasada.
`unable to find static system library 'lzma'`. La receta no tenía `[deps]`.
Octava de la familia (appstream, cargo-deb, cargo-make, dprint, dufs, fnm,
macchina). Verificado: sella, 0 librerías faltantes, binario en /usr/bin.
⚠ Nota de método: verificar ESTA costó ~20 min de campaña parada, porque el
build tarda más que su fallo (1033 s) y el flock es exclusivo. Para el resto de
esta familia —arreglo de UNA línea con el patrón ya verificado 8 veces— sale
más barato commitear y dejar que la campaña lo verifique en su siguiente
pasada.