gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1389Shell 137Markdown 80Rust 802602 Definiciones

selfhost-verify: módulos del kernel booteado + build a stderr (E2BIG)

Dos bugs latentes que sólo aparecían en un build real (in-VM); en el host
quedan tapados porque todo sale de caché.

1. scripts/selfhost-verify.sh: los módulos se sacaban de /lib/modules/$(uname -r)
   pero la VM bootea $KERNEL (/boot/vmlinuz-linux), que puede ser otra versión.
   Mismatch de version-magic ⇒ overlay.ko no carga ⇒ bwrap muere "No such device".
   Ahora la versión se deriva del bzImage booteado (file -bL "$KERNEL"; fallback uname -r).

2. crates/hammer-build/src/sandbox.rs: spawn_pump teeaba el stdout del build al
   stdout del padre. rebuild-stage1 hace PRIME=$(hammer ... bootstrap stage1); en
   un build real son miles de líneas de configure/make capturadas en $PRIME ⇒ el
   paso siguiente --rootfs "$PRIME" exec con un arg gigante ⇒ E2BIG (Argument list
   too long). Ahora el tee va a stderr; stdout queda para el hash legible-por-máquina.

Con esto el verify corre end-to-end in-VM: los 4 componentes reproducen bit a bit,
pero of_tree(stage1) diverge (host vs VM vs baseline) — no-determinismo del ensamblado
a cazar (SDD 09 §2).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

1ab53a650ffb00e83016acc17012f9324f222ba2 · por sergio · (hace 4 meses) · padre 4d8c1c754 · ver el árbol en este commit

Delta semántico · 2

crates/hammer-build/src/sandbox.rs

2 archivos · +22 −13

diff --git a/crates/hammer-build/src/sandbox.rs b/crates/hammer-build/src/sandbox.rs
index 7e385680..6c07098a 100644
--- a/crates/hammer-build/src/sandbox.rs
+++ b/crates/hammer-build/src/sandbox.rs
@@ -90,8 +90,8 @@ impl Sandbox {
         let tail = Arc::new(Mutex::new(VecDeque::<String>::with_capacity(TAIL_LINES)));
         let out = child.stdout.take().expect("stdout piped");
         let err = child.stderr.take().expect("stderr piped");
-        let h_out = spawn_pump(out, tail.clone(), false);
-        let h_err = spawn_pump(err, tail.clone(), true);
+        let h_out = spawn_pump(out, tail.clone());
+        let h_err = spawn_pump(err, tail.clone());
 
         let status = child
             .wait()
@@ -201,12 +201,19 @@ impl Sandbox {
     }
 }
 
-/// Lanza un thread que lee `reader` línea a línea, la reenvía al stream del padre
-/// (`to_stderr` decide cuál) y la acumula en el ring `tail`. Devuelve el handle para join.
+/// Lanza un thread que lee `reader` línea a línea, la reenvía a stderr del padre y la
+/// acumula en el ring `tail`. Devuelve el handle para join.
+///
+/// El tee va SIEMPRE a stderr (tanto el stdout como el stderr del build): stdout del proceso
+/// queda reservado para el resultado legible-por-máquina (el content-hash que imprime la CLI,
+/// p. ej. `bootstrap stage1`). Antes el stdout del build se teeaba al stdout del padre y se
+/// colaba en capturas tipo `PRIME=$(hammer … bootstrap stage1)`; con builds reales (no cacheados,
+/// p. ej. el rebuild in-VM) eso son miles de líneas de `configure`/`make` ⇒ el argumento crece y
+/// la fase de verificación siguiente (`--rootfs "$PRIME"`) revienta el exec con E2BIG
+/// (Argument list too long). Va a stderr, junto a los logs de tracing. Ver runbook §8c.
 fn spawn_pump<R: std::io::Read + Send + 'static>(
     reader: R,
     tail: Arc<Mutex<VecDeque<String>>>,
-    to_stderr: bool,
 ) -> std::thread::JoinHandle<()> {
     std::thread::spawn(move || {
         let buf = BufReader::new(reader);
@@ -215,12 +222,8 @@ fn spawn_pump<R: std::io::Read + Send + 'static>(
                 Ok(l) => l,
                 Err(_) => break,
             };
-            // Tee en vivo al padre (sin bufferizar de más: el build puede tardar).
-            if to_stderr {
-                let _ = writeln!(std::io::stderr(), "{line}");
-            } else {
-                let _ = writeln!(std::io::stdout(), "{line}");
-            }
+            // Tee en vivo al padre por stderr (sin bufferizar de más: el build puede tardar).
+            let _ = writeln!(std::io::stderr(), "{line}");
             if let Ok(mut g) = tail.lock() {
                 push_tail(&mut g, line, TAIL_LINES);
             }
diff --git a/scripts/selfhost-verify.sh b/scripts/selfhost-verify.sh
index dd255aa0..ff25ab83 100755
--- a/scripts/selfhost-verify.sh
+++ b/scripts/selfhost-verify.sh
@@ -83,8 +83,14 @@ fi
 
 # 3) Inyectar los módulos del kernel DE ESTA máquina al toolchain (el builder los carga: overlay para
 #    el sandbox bwrap, e1000 para la red del vendoring). Deben matchear el kernel que bootea la VM.
-say "inyectar overlay.ko + e1000.ko (kernel $(uname -r)) al toolchain"
-moddir="/lib/modules/$(uname -r)"
+# La versión de los módulos DEBE matchear el kernel que bootea la VM ($KERNEL), no el del host
+# corriendo el script ($(uname -r)) — en máquinas con varios kernels instalados difieren y el
+# insmod falla con "version magic mismatch" → sin overlayfs, bwrap revienta. Sacamos la versión
+# del propio bzImage; si no se puede, caemos a uname -r.
+KVER="$(file -bL "$KERNEL" 2>/dev/null | grep -oE 'version [0-9][^ ]*' | awk '{print $2}')"
+[[ -n "$KVER" && -d "/lib/modules/$KVER" ]] || KVER="$(uname -r)"
+say "inyectar overlay.ko + e1000.ko (kernel $KVER) al toolchain"
+moddir="/lib/modules/$KVER"
 for m in overlay e1000; do
     src="$(find "$moddir" -name "$m.ko*" 2>/dev/null | head -1)"
     if [[ -n "$src" ]]; then