Commit Graph
15 Commits
Author SHA1 Message Date
Sergio 24bcf1783c takana etapa 4: los 10 crates de librería y el CLI pasan a takana-*
hammer-{core,build,bootstrap,overlay,journal,mirror,upgrade,agent,recover,cli}
→ takana-*, con sus deps de workspace, sus identificadores en el fuente y las
referencias -p de los scripts.

VERIFICADO que no mueve nada del corpus: `takana hash recipes/zlib.toml`
devuelve b3:dc363f26… , idéntico a antes del renombre. Los nombres de crate no
entran en hash_inputs, pero eso se comprueba, no se supone. 600 tests en verde.

DOS BINARIOS SE CONGELAN, y no por prolijidad:

- `hammerd` — paquete Y binario. Es componente de Stage 1 de la distro (musl,
  busybox, hammerd, arje-zero), lo supervisa arje-zero en el sistema arrancado,
  `PRESEED=hammerd` lo nombra en selfhost-verify y sus bytes anclan el baseline
  of_tree. El nombre del crate va en los símbolos ⇒ renombrarlo mueve los bytes.

- `hammer-recover` — el PAQUETE se renombra a takana-recover, el BINARIO no.
  hammer-live-install.sh lo copia a /usr/sbin/hammer-recover en sistemas ya
  instalados y hornea un hook de arranque que lo invoca por ese nombre:
  renombrarlo rompe máquinas instaladas, no el repo.

Consecuencia que hay que anotar igual: al renombrar hammer-core, los bytes de
hammerd cambian de todos modos porque linkea contra un crate con otro nombre.
El baseline of_tree del selfhost hay que rehacerlo — es efecto de la etapa 4,
no de un cambio de hammerd.

Las referencias en comentarios de recetas y docs (rutas hammer-core/src/…)
quedan para la etapa 5: son texto, no mueven hash.
2026-09-09 18:46:41 +00:00
sergioandClaude Opus 4.8 10961ce47f kernel-from-source: ✓ REPRODUCIBLE con kernel hammer (switch_root wrapper)
CIERRA el frente kernel-from-source: el bzImage hammer-built (Linux 6.16.12,
recipes/linux.toml) bootea la VM del selfhost-verify y el rebuild in-VM reproduce
el of_tree bit a bit (✓ REPRODUCIBLE, DRIVER_RC=0, ~3min).

El muro era bwrap `pivot_root: Invalid argument`: con un kernel hammer el / del
initramfs es el rootfs absoluto del mount-namespace (sin mount padre movible), y
bwrap del sandbox no puede pivotar de ahí (el kernel host lo permitía — quirk
suyo; diagnosticado con un debug-loop de initramfs mínimo, boot ~10s).

Fix portable (HAMMER_KERNEL=1): un /init wrapper PID1 que copia el rootfs a un
tmpfs y hace switch_root, dejando / como mount tmpfs real (pivotable). bwrap
pivota en CUALQUIER kernel. Condicional ⇒ el camino del kernel host (rdinit=
/sbin/init directo) queda intacto. drive-rebuild.py: APPEND env override para
pasar rdinit=/init.

Uso: HAMMER_KERNEL=1 KERNEL=<bzImage> KVM=1 MEM=16384 PRESEED=hammerd \
  ./scripts/selfhost-verify.sh

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:21:35 -04:00
sergioandClaude Opus 4.8 a4777d5d64 selfhost-verify: auto-consistencia rust VERIFICADA in-VM + PRESEED=hammerd requerido
✓ REPRODUCIBLE in-VM (2026-06-18): SWAP_RUST=1 KVM=1 PRESEED=hammerd selfhost-verify.sh
→ el host construye stage1 con hammer-rust (of_tree b3:7fa6cb4e), la VM lo reconstruye con
hammer-rust y reproduce 7fa6cb4e BIT A BIT (stage1' == stage1, DRIVER_RC=0). hammerd
reconstruido in-VM = byte-idéntico al host (d08fd273). Cierra el frente rust del
auto-alojamiento: el compilador (rustc 1.91.1 hammer-built, mrustc→1.90→1.91.0→1.91.1) se
auto-aloja bit a bit, con su propio EXPECT_REF (≠ 9adefb82 de Alpine; rustc emite los bytes).

PRESEED=hammerd es OBLIGATORIO con SWAP_RUST: arje-zero (monorepo tawasuyu) vendorea ~1973
crates; un rebuild in-VM completo (PRESEED=all) desborda el rootfs en RAM (ENOSPC). Con
PRESEED=hammerd se preseedea el arje-zero host-built (hammer-rust, locked desde 9967b02c) y
sólo hammerd se reconstruye in-VM. Documentado en el bloque SWAP_RUST y en rust-frontier/README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 09:47:28 -04:00
sergioandClaude Opus 4.8 5549330c94 selfhost-verify: RUST_EXPECT_REF=7fa6cb4e + swap --restore deja el toolchain prístino
Auto-consistencia de hammer-rust VERIFICADA en host (2026-06-17): of_tree(stage1)
construido con hammer-rust 1.91.1 = b3:7fa6cb4e… reproducido 2× (store-rust y store-rust2,
builds frescos independientes). Es el nuevo EXPECT_REF para SWAP_RUST (criterio elegido:
auto-consistencia, ≠ el 9adefb82 de Alpine). Cableado como default de RUST_EXPECT_REF.

swap-rust-into-toolchain.sh --restore: ahora también elimina lo AÑADIDO por el swap
(rustlib x86_64-unknown-linux-musl + librustc_driver de hammer), no sólo restaura
rustc/cargo. Round-trip apply→restore deja el devfs prístino (verificado: sólo
x86_64-alpine-linux-musl + driver de Alpine). Antes dejaba ~350 MB de cruft.

Nota: los leftovers NO afectaban el of_tree (un build Alpine con/ sin ellos diverge igual
del baseline Jun13 — ver hallazgo de drift abajo); la limpieza es higiene/reversibilidad.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 01:17:24 -04:00
sergioandClaude Opus 4.8 f06bfc7ee4 selfhost-verify: SWAP_RUST usa store dedicado (el input-hash no incluye el rustc)
recipe.hash_inputs = source+compiler+target+link+patches+flags+phases+deps; NO incluye
el binario rustc del toolchain. En el ./store compartido, arje-zero/hammerd quedarían
cacheados con los bytes de Alpine ⇒ el swap de rust sería un no-op (REF seguiría 9adefb82).
SWAP_RUST=1 ahora usa un store dedicado (store-rust, override RUST_STORE) para forzar el
rebuild de los 4/4 con hammer-rust, sin tocar el ./store baseline. Así of_tree refleja el
compilador nuevo (auto-consistencia).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 22:03:18 -04:00
sergioandClaude Opus 4.8 fb71aca4be selfhost-verify: swap rust hammer-built 1.91.1 en /toolchain (SWAP_RUST, auto-consistencia)
Pieza rust del auto-alojamiento "variante b" (la última del frente). A DIFERENCIA de
make/busybox/bwrap/coreutils/linux-headers (herramientas que orquestan/copian ⇒ bytes
idénticos ⇒ of_tree 9adefb82 por construcción), **rustc EMITE los binarios del 4/4**
(arje-zero, hammerd) ⇒ un rustc distinto diverge of_tree. Criterio elegido: AUTO-
CONSISTENCIA (REF recomputado con hammer-rust = nuevo EXPECT_REF; la VM reproduce ESE),
no igualdad byte-a-byte con el baseline Alpine.

- swap-rust-into-toolchain.sh: overlay aditivo y reversible de .scratch/rust-1.91.1-prefix
  sobre /toolchain. El rustlib x86_64-unknown-linux-musl de hammer y sus .so con hash propio
  NO chocan con el x86_64-alpine-linux-musl de Alpine ⇒ sólo se reemplazan /usr/bin/{rustc,
  cargo} (Alpine guardado en *.alpine; --restore deshace). Los 4/4 compilan NATIVO (target
  x86_64-linux-musl == SANDBOX_NATIVE_TARGET, sin --target) ⇒ hammer-rustc emite para su
  triple nativo x86_64-unknown-linux-musl.
- selfhost-verify.sh: SWAP_RUST=1 aplica el swap en $TOOLCHAIN antes del build de stage1
  (así el REF host se computa con hammer-rust) y restaura al salir (trap). Sirve TANTO al
  REF host COMO al toolchain in-VM (--toolchain $TOOLCHAIN). EXPECT_REF pasa a RUST_EXPECT_REF
  (no el 9adefb82 de Alpine). Vars: RUST_PREFIX, RUST_EXPECT_REF.

VALIDADO: el swap aplica/restaura limpio; hammer-rust 1.91.1 (host x86_64-unknown-linux-musl)
compila+corre un crate NATIVO (sin --target) en el devfs — el camino exacto de los 4/4.
NEXT: computar el nuevo RUST_EXPECT_REF (bootstrap stage1+stage2 con SWAP_RUST) y verify in-VM.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 21:59:38 -04:00
sergioandClaude Opus 4.8 a774552ead selfhost-verify: pieza 5 (coreutils swap) — SWAP_COREUTILS=1, musl+busybox byte-idénticos en host
Quinta pieza del toolchain hammer-from-source (variante b, SDD 11 §7.2b): GNU coreutils 9.8
(cp/mkdir/ln/chmod/mv/install…), que el `make install` de musl y busybox invocan.

- recipes/coreutils.toml: empaquetada multicall (--enable-single-binary), mismo layout que el
  paquete de Alpine (un /bin/coreutils + ~100 symlinks que despachan por argv[0]). Estática musl
  con zig cc, vainilla sin patches (es tool del toolchain, no input compilado del 4/4).
- scripts/selfhost-verify.sh: SWAP_COREUTILS=1 construye la receta y monta el único binario sobre
  /toolchain/bin/coreutils — los symlinks del toolchain (cp/mkdir/install/…) lo siguen, un solo --swap.

Bisección host fuerte: con hammer-coreutils pisando el de Alpine (trap-restore en .dev-fs), musl Y
busybox rebuildearon BYTE-IDÉNTICO (57b66a2e, 56664d70). Pendiente: corrida in-VM acumulando swaps.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 02:23:28 -04:00
sergioandClaude Opus 4.8 e9f535a1bb selfhost-verify: pieza 4 (bwrap swap) + materialización de build-deps en el lab
Cuarta pieza del toolchain hammer-from-source (variante b, SDD 11 §7.2b): bubblewrap, el
sandbox del propio lab. Binario estático ⇒ swap de archivo sobre /toolchain/usr/bin/bwrap.
Como herramienta del toolchain (no input del 4/4) no necesita casar byte-a-byte con Alpine,
sólo aislar igual.

Tres piezas:
- recipes/libcap.toml (2.78): dep obligatoria de bwrap; el toolchain Alpine no trae el -dev
  (libcap.a / sys/capability.h). Build estático musl con zig cc, sin patches (es tool, no input).
- materialización de build-deps (hammer-build): deps.build ahora se CONSTRUYE recursivamente
  (build() llama build() por cada dep) y cada artefacto sellado se apila como capa --overlay-src
  bajo el rootfs del sandbox, dejando usr/{include,lib,lib/pkgconfig} en /usr. pkgconf y zig cc
  las hallan sin plumbing de flags. Recetas sin deps: sandbox byte-igual (baseline intacto).
  Tests nuevos: no_deps_emits_single_overlay_src, deps_stack_as_overlay_layers_under_rootfs.
- recipes/bwrap.toml (0.11.0): el toolchain no trae meson/ninja/python, así que bypaseamos meson
  compilando los 4 .c de bubblewrap directo con zig cc (+config.h trivial). deps.build=["libcap"].

Validación host fuerte: hammer-bwrap es estático, corre --version y sandboxea, y musl rebuildeó
BYTE-IDÉNTICO usándolo de sandbox (bisección). Expuesto con SWAP_BWRAP=1. Tests verdes.
Pendiente: corrida in-VM acumulando swaps para el sello ✓ REPRODUCIBLE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 20:05:55 -04:00
sergioandClaude Opus 4.8 b05badee23 selfhost-verify: pieza 3 (linux-headers swap) — SWAP_LINUX_HEADERS=1, byte-idéntico a Alpine en host
Tercera pieza del toolchain hammer-from-source (variante b, SDD 11 §7.2b): los headers
UAPI del kernel 6.16.12 que musl/busybox #include, hoy tomados del paquete linux-headers
de Alpine.

- recipes/linux-headers.toml: `make headers` (no `headers_install` — su rsync final falta
  en el toolchain hermético) + unifdef vía HOSTCC=zig cc; .tar.gz (busybox-tar lo
  descomprime sin xz). Sella los 13 subdirs kernel-owned de usr/include.
- assemble_builder: el swap ahora soporta rel_path de DIRECTORIO (reemplaza el árbol
  entero: remove + copy), no sólo binarios. Cubierto por test nuevo
  (builder_rootfs_swaps_toolchain_header_tree_from_source, incl. borrado de huérfanos).
- recipes/linux-headers-alpine-compat.patch: el paquete de Alpine no es el `make headers`
  crudo. Aporta content-pinned 5 archivos que difieren de la salida vainilla; 3 son
  REQUERIDOS (scsi/{scsi,scsi_ioctl,sg}.h — legacy userspace, NO UAPI) porque el applet
  `eject` de busybox los incluye y sin ellos no compila. install los pisa sobre /out y
  limpia el junk (.cmd/Makefile/headers_check.pl/drm) que `cp -a` arrastra.
- scripts/selfhost-verify.sh: SWAP_LINUX_HEADERS=1 construye la receta y emite un --swap
  por subdir kernel-owned (auto-derivado del artefacto sellado).

Criterio de éxito fuerte: `diff -r` del header-tree de hammer contra el de Alpine = VACÍO
⇒ el toolchain swapeado es byte-idéntico al baseline ⇒ reproducibilidad por construcción.
Ensamblado end-to-end en host OK (13 swaps, musl bits/sys intactos). 133 tests verdes.
Pendiente: corrida in-VM para el sello ✓ REPRODUCIBLE.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 19:35:01 -04:00
sergioandClaude Opus 4.8 0b97a85356 selfhost-verify: pieza 2 (busybox swap) — SWAP_BUSYBOX=1, validada en host
Variante (b) pieza 2: el sh+coreutils que el build usa del /toolchain. El /toolchain
de Alpine es mixto — busybox para sh/sed/grep/awk/tar/find (symlinks → /bin/busybox),
GNU coreutils para cp/mkdir/install. El swap monta el busybox estático de hammer
sobre /toolchain/bin/busybox: los applets busybox-backed pasan a usarlo, coreutils
GNU queda intacto. Sin código nuevo — el mecanismo --swap ya soporta rel_path
(--swap busybox=<hash>:bin/busybox).

Validado en host (sin VM): con make+busybox de hammer swapeados, los 4/4 componentes
construyen — incl. busybox compilándose a sí mismo con hammer-busybox de shell — y el
of_tree del rootfs ensamblado da 9adefb82 (el baseline determinista). Las piezas 1 y 2
son reproducibilidad-neutrales: el toolchain es intercambiable sin perturbar el byte-output.

selfhost-verify.sh: SWAP_BUSYBOX=1 (paralelo a SWAP_MAKE). Docs 10/11 actualizadas.
Pendiente: verify in-VM con los swaps para el ✓ REPRODUCIBLE end-to-end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 06:40:59 -04:00
sergioandClaude Opus 4.8 004a895829 selfhost-verify: fix CONFIRMADO + re-base EXPECT_REF a 9adefb82 (determinista)
Verificado: arje-zero con CARGO_BUILD_JOBS=1 y TODAS las CPUs del host sale
byte-idéntico al build serial ⇒ el jobserver serializa el backend paralelo de
rustc/LLVM (ThinLTO) independientemente del nº de CPUs. La pieza que faltaba para
la reproducibilidad host↔VM (codegen-units=1 no bastaba).

- EXPECT_REF re-baseado: 0039b2b9 (build paralelo no-fiable) → 9adefb82 (serial,
  determinista, = lo que produce la VM con 1 vCPU).
- Baseline del host refrescado: store/ re-sellado con el arje-zero determinista.
- docs/10-roadmap.md: causa raíz documentada (make inocente; arje-zero/ThinLTO).

Pendiente: re-correr el verify in-VM con el fix para cerrar el ✓ REPRODUCIBLE y
validar de paso la pieza 1 (swap del make).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 05:05:25 -04:00
sergioandClaude Opus 4.8 7bc2ee6019 builder: --swap del toolchain — montar make hammer sobre Alpine (variante b, pieza 1)
Segundo paso del auto-alojamiento *puro* (SDD 11 §7.2b): tras sellar GNU make
4.4.1 desde fuente (commit 749ea9e), ahora el builder puede *usarlo*. El swap
reemplaza una a una las piezas que el builder toma de Alpine por recetas hammer,
con Stage 2 reverificando que el byte-output no cambia.

- `BuilderSpec.swaps: Vec<ToolchainSwap{name,artifact,rel_path}>`: monta el binario
  sellado sobre el path Alpine en /toolchain (estático musl ⇒ sin shim del loader).
- `swaps_digest` (ordenado por nombre) entra al hash lógico del builder ⇒ la
  procedencia deja de ser "todo Alpine" y se vuelve auditable para el log de
  transparencia. Vacío ⇒ digest "" (compat hacia atrás: builder pura-Alpine
  conserva su hash previo).
- CLI: `hammer bootstrap builder --swap make=<hash>[:rel_path]` (repetible;
  rel_path por defecto usr/bin/<name>).
- selfhost-verify.sh: opt-in `SWAP_MAKE=1` (construye recipes/make.toml y lo
  swapea) + `SWAPS="name=hash …"` para swaps extra. Default off ⇒ corrida
  pura-Alpine idéntica a la baseline conocida-buena.

Validado en el host contra el store real: pura `b3:8a370f5d…` vs swapped
`b3:e7e2282c…`, y /toolchain/usr/bin/make queda hardlinkeado al artefacto
fbad44ac… (ELF estático, no el dinámico de Alpine). 3 tests nuevos
(swap aplica, hash cambia, error si falta el binario). 37 tests verdes.

Pendiente: correr Stage 2 in-VM con el swap y confirmar que of_tree(stage1') == ref
(el make hammer compila los 4/4 idéntico al de Alpine ⇒ toolchain intercambiable).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 21:38:04 -04:00
sergioandClaude Opus 4.8 4c9bcc016b sandbox: CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 — arje-zero reproducible (Stage 2 no-det #3)
El rebuild in-VM daba ✗ DIVERGENTE (of_tree host 984e002f ≠ VM 94b93585) pese a
que los 4 componentes sellaban bajo el mismo key of_inputs. Bisección por componente
en el host: musl, busybox y hammerd reconstruyen byte-idéntico, y la ensambladura del
rootfs (of_tree) es determinista e independiente del entorno. El culpable era arje-zero:
dos builds del MISMO host daban binarios distintos (Δ ~9.6 KB por .text/.rodata/.eh_frame/
.gcc_except_table) — firma del codegen paralelo de rustc.

Raíz: el workspace de hammer pinea [profile.release] codegen-units=1 (por eso hammerd
reproducía), pero el monorepo tawasuyu no declara perfil → cargo default codegen-units=16,
cuyo reparto del crate en N objetos varía build-a-build.

Fix: el sandbox impone codegen-units=1 para TODAS las crates Cargo (el var de cargo gana
sobre el profile del repo fuente). El lab elimina el no-determinismo en vez de confiar en
upstream (SDD 09 §2). Verificado: dos builds cu=1 de arje-zero → byte-idénticos.

Nueva referencia reproducible 4/4: of_tree(stage1)=b3:0039b2b9… (reemplaza 198f209f… de
cu=16). Actualizados EXPECT_REF (selfhost-verify.sh) y runbook §8b/§8c.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 04:31:51 -04:00
sergioandClaude Opus 4.8 1ab53a650f 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>
2026-06-12 03:04:45 -04:00
SergioandClaude Opus 4.8 7025f93bcd scripts: tarea selfhost-verify end-to-end (correr el 4/4 in-VM en KVM)
El veredicto pleno 4/4 in-VM pide KVM + RAM holgada; el host de dev (7.6 GB, sin KVM)
colgó por presión de RAM bajo TCG a los ~17 min del make de musl. Esta tarea traslada
la verificación a una máquina capaz (laptop) en un solo comando.

- scripts/selfhost-verify.sh: pipeline completo — stage0 → stage1 baseline → stage2
  (ancla la ref of_tree) → inyecta overlay.ko/e1000.ko del kernel local al toolchain →
  ensambla el builder con la ref embebida → empaqueta (cpio --owner=root:root) →
  bootea + driver no-interactivo → veredicto. KVM auto-detect; PRESEED=hammerd para el
  camino barato (preseed C+arje, sólo hammerd in-VM). Cross-check entre máquinas:
  compara su of_tree(stage1) contra EXPECT_REF (198f209f… conocida-buena del dev).
- scripts/drive-rebuild.py: driver no-interactivo parametrizado (env: BUILDER_CPIO,
  KERNEL, MEM, CPU, KVM, NET, DEADLINE, LOG). Bootea, espera la shell de arje-zero,
  manda rebuild-stage1 y sale 0 si REPRODUCIBLE / 1 si DIVERGENTE. (Versión de repo del
  driver ad-hoc que vivía en work/.)
- scripts/boot-builder-vm.sh: añade la NIC e1000 (NET=1 por defecto) — el vendoring Rust
  necesita red.
- runbook §8c: documenta la tarea y el muro de RAM del host de dev.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 00:57:07 +00:00