El run en la VM avanzó hasta `configure` y ahí bwrap falló: `Can't make overlay
mount … No such device`. Causa raíz: el kernel trae overlay como MÓDULO
(CONFIG_OVERLAY_FS=m) y el initramfs arranca sin módulos cargados ⇒ ENODEV. El
sandbox de hammer-build usa `bwrap --tmp-overlay` (raíz efímera sobre el
toolchain), así que necesita overlayfs.
Fix: rebuild-stage1 hace `insmod /toolchain/lib/overlay.ko` tras el shim del
loader (overlay no tiene deps; vermagic del kernel destino). El toolchain suma
`kmod` (insmod/modprobe). El `overlay.ko` es específico del kernel (no de Alpine):
se inyecta aparte en el builder (.dev-fs/alpine/lib/overlay.ko), documentado en
bootstrap-devfs.sh.
Progreso del run: shim OK ⇒ bwrap/git/curl corren ⇒ fetch OK ⇒ configure arranca;
overlay era el siguiente muro. 28 tests verdes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Correr el rebuild EN la VM destapó dos faltantes reales del builder (justo lo que
la verificación de Stage 2 debe cazar):
1. El toolchain (variante a, desde Alpine) traía compiladores (cargo/make/zig)
pero NO las herramientas de orquestación del lab — bwrap (anida el sandbox),
git (git archive del mirror) y curl (tarballs). En el host las aporta el
sistema; en la VM el toolchain es el único userland capaz, así que deben vivir
ahí. bootstrap-devfs.sh las añade (paquete `bubblewrap`, no `bwrap`).
2. Esos binarios son Alpine *dinámicos* y no corren desde el userland Stage 1
*estático* (musl --disable-shared ⇒ no hay loader en /lib). rebuild-stage1
ahora instala un shim del loader musl (cp a /lib) + LD_LIBRARY_PATH/PATH al
toolchain antes de invocar hammer; el sandbox de build sigue anidando en
/toolchain. El hammer estático corre por ruta absoluta.
El builder boot end-to-end ya estaba probado; esto lo hace además *capaz de
reconstruirse*. 28 tests verdes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
La verificación de reproducibilidad de Stage 2 (dos builds + diff de bytes) probó
que musl reconstruye bit-idéntico pero busybox NO, y cazó dos no-determinismos:
1. Config interactiva: tras editar .config, silentoldconfig prompea por opciones
NEW leyendo stdin → set de applets variable (build no-determinista y flaky).
Fix en busybox.toml: `yes '' | make oldconfig` (defaults, determinista;
1.36.1 no tiene olddefconfig).
2. Headers UAPI del kernel: el config determinista habilita console-tools, que
exige linux/kd.h (zig no la bundlea). Fix: linux-headers en bootstrap-devfs.sh
(zig cc nativo la halla en /usr/include).
Con ambos, musl y busybox reconstruyen bit-idéntico. Runbook §8b documenta la
verificación y deja pendiente: reproducibilidad de los componentes Rust y el
rebuild dentro de un builder rootfs (auto-alojamiento pleno).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Primera ejecución del runbook en el host. El userland C builda end-to-end; el
camino Rust queda bloqueado por una decisión de toolchain. Hallazgos verificados:
recipes/busybox.toml — tres incompatibilidades GNU-toolchain ↔ zig, resueltas:
- gcc hardcodeado (CC/HOSTCC = gcc con `=` ignora el env): pasar CC='zig cc'
HOSTCC='zig cc' en línea de comando de make
- flags GNU-ld que zig cc valida y rechaza (--warn-common, -Map, --verbose):
quitarlos de scripts/trylink con sed
Con esto musl 1.2.5 y busybox 1.36.1 compilan, linkean y se sellan.
scripts/bootstrap-devfs.sh — añade rust+cargo al rootfs (las recetas Cargo
corren cargo en el sandbox), con CAVEAT: el rust de Alpine es
x86_64-alpine-linux-musl pero BuildSys::Cargo pide x86_64-unknown-linux-musl
(no instalado, sin rustup) → bloquea hammerd/arje-zero. Decisión de toolchain
abierta (plan C.2).
docs/runbooks/stage1-vm-boot.md §8 — bitácora con la tabla de etapas, los fixes
y las opciones para resolver el triple Rust.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cierra el primer entregable del roadmap. Cambios:
- `Source`: ahora admite dos modos mutuamente excluyentes — `git` (repo+commit) o
`tarball` (url+sha256). El hash de entrada del artefacto usa el commit en modo
git y el sha256 en modo tarball; ambos son identificadores inmutables del
contenido fuente. Validación en `Source::kind()` con error claro.
- `fetch`: dispatch por modo. Tarball cacheado en `work/tarballs/<sha>.tar`,
descarga vía `curl -fL`, verificación sha256 con `sha2`, extracción con
`--strip-components` (default 1, GNU-style).
- `recipes/grep.toml`: GNU grep 3.12 desde el tarball release de gnu.org
(sha256 fijado, sin patches, `--disable-perl-regexp --disable-nls`).
- Bootstrap: añade `binutils` al rootfs Alpine — configure de autotools mira
ld/ar/ranlib incluso cuando el compilador real es zig cc.
- `.gitignore`: `/work/` (artefactos transitorios del build).
- `docs/10-roadmap.md`: Fase 0 y Fase 1 marcadas ✅; entregable cerrado.
Nota sobre v3.11 vs v3.12: probé primero v3.11 y el binario producido daba
"memory exhausted" en cualquier regex (bug conocido de grep+musl static al
inicializar DFA). v3.12 corrige y funciona limpio dentro del rootfs Alpine.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- `BuildConfig.cache_root` (override `HAMMER_CACHE`): bindea `/cache` al sandbox
y exporta `ZIG_GLOBAL_CACHE_DIR=/cache/zig`. Evita ~30s de recompilación de
musl en cada build.
- `scripts/bootstrap-devfs.sh`: idempotente, descarga+verifica alpine-minirootfs
3.23.4 y zig 0.16.0 con sha256 fijo, instala build tools en el rootfs vía apk
bajo bwrap.
- `make_tree_read_only`: solo archivos regulares pierden `w`; los directorios
conservan 0o755 para no estorbar GC ni `rm -rf` administrativo. La
inmutabilidad estricta del store se delega al mount RO de la distro propia.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>