16 tasas de Linux medidas en momento (KVM, 4 vCPU, bajo carga): piso de
syscall, /proc como API de texto, fork vs espacio de direcciones del padre,
enlazado dinámico, VFS, io_uring, THP, namespaces. Programas en C y salidas
crudas en docs/evidencia/tasas-kernel-2026-08-29/.
Lo que sale de medir y después verificar contra las recetas: los kernels de
hammer se construyen sin CONFIG_MEMCG (no está en x86_64_defconfig y no tiene
default y), y arje/sandokan escriben memory.max descartando el error. Un tope
de memoria pedido por una Card no se aplica y sólo deja un warn.
Tres resultados que contradicen el consejo de manual y quedan documentados:
THP 3x más lento que 4K con defrag=madvise, io_uring 2,8x más lento que pread
sobre tmpfs, y stat cuesta lo mismo en tmpfs que en ext4 (es tasa del VFS).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ACUcwo9mZsE5ocYVE9npih
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