Queda una carrera abierta que el lock de script NO cierra: `build-farm.sh` corre `xargs -P2` y dos
recetas de la misma cola que compartan dep se pisan igual, porque `fetch` nombra el árbol de forma
determinista y sin nada del constructor.
El fondo no es "falta un lock", es que el árbol cumple DOS papeles que se contradicen bajo
concurrencia: caché direccionada por contenido (clave = nombre+sha, re-extraer es caro) y workspace
mutable de build (lib.rs lo parchea, aísla el workspace Cargo y vendorea EN EL SITIO, y después el
sandbox lo bind-montea). Por eso un lock alrededor de la extracción parece correcto y NO lo es: un
segundo proceso puede borrar un árbol que un bwrap ya está compilando.
El ADR deja las tres salidas (lock por árbol durante todo el build / árbol privado por build /
separar caché inmutable + copia privada) con su contra concreta cada una —incluida que el worker
corre ext4, sin reflink, así que la copia de la opción C es real— y los tres números que habría que
medir para elegir en vez de opinar.
Se ancla en los dos sitios donde alguien va a chocar: el ADR indexado en docs/README.md (de paso se
agregan 0009-0011, que faltaban) y un comentario en el propio `fetch_tarball` que dice explícitamente
que no se arregle a medias.
Va como PENDIENTE y no decidido a propósito: hay otro agente sobre este repo y esto es lo que evita
que la carrera se "arregle" de una forma que parece bien y deja el bug.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El Draft 1 estaba escrito contra el sandbox de nix. hammer no construye con
nix: construye con bubblewrap (ADR 0004, hammer-build/src/sandbox.rs). Eso
invalidaba por irrelevancia el argumento anti-nix (§0), las FOD y su red
abierta (§6 + D8), Q5, y la Fase 3 del fetcher mediado — que el propio doc
marcaba como el mayor riesgo de cronograma, de "meses".
Fase 0 ejecutada (5 minutos, no días):
- ABI Landlock = 10 en el laptop (techo: audit/7, TSYNC/8, quiet/10).
- Kernel de hammer 6.16.12-metal: CONFIG_SECURITY_LANDLOCK ausente, pero
CONFIG_LSM ya lista landlock ⇒ falta un flag, no una versión. Q2 cerrada.
- Cero FODs: download.rs baja con curl en el HOST y --unshare-all incluye
--unshare-net ⇒ el fetcher mediado de D8 ya está construido. Fase 3 se
elimina.
La brecha real es otra y paga mejor: el sandbox monta un rootfs Alpine ENTERO
como capa base ⇒ política ⊋ clausura ⇒ una receta usa Alpine sin declararlo y
pasa en verde. Es el bug que la campaña de de-Alpinización caza a mano. D1 +
audit de ABI 7 lo vuelve mecánico (path/dev/ino de lo no declarado).
Encaje en el modelo de confianza (recipe.rs:229): H1 garantiza que lo
declarado PASA; harkaq que lo declarado BASTA. Complementos simétricos.
Sobreviven intactos D1 (política derivada), D2 (evidencia negativa) y D3
(cero quieting). Nuevo Q1 = el riesgo técnico real: cómo llegan los registros
AUDIT_LANDLOCK_* al lector (CAP_AUDIT_READ, auditd, userns, flags de exec).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Procedimiento operativo end-to-end para de-riesgar lo construido: stage0 (ingerir
zig) → stage1 (build + ensamblar el rootfs) → empaquetar initramfs (cpio newc +
gzip) → bootear en QEMU con el kernel del host → criterios de éxito.
Fundamentado en los comandos reales del repo (scripts/bootstrap-devfs.sh, la URL
y sha256 de zig 0.16.0, hammer bootstrap stage0/stage1). La prueba clave: matar
hammerd y ver a arje-zero reiniciarlo con backoff — el CRASHED real.
Honesto sobre su estado: es el procedimiento, no un transcript verificado; el
cross-compile y el boot reales aún no se corrieron (ese es el punto). §7
anticipa los ajustes probables (init, /dev/console, getty/tty, -lgcc_s).
Enlazado desde SDD 12 §I2 y el índice de docs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diseña el último paso ◑ del Stage 1: reemplazar el init provisional de busybox
por arje-zero como PID 1, lo que entrega el CRASHED real (Fase 5 diferida).
Fundamentado en el código real de arje-zero (citas a tawasuyu):
- contrato de boot: arje monta los pseudo-FS él mismo (arje-kernel); lee
/ente/seed.card.json (Card Virtual + genesis); supervisa con Restart/Backoff
- qué debe proveer el rootfs (mount points, seed, /sbin/init→arje-zero, consola)
- la seed card de Stage 1: hammerd como Payload::Native Restart ⇒ on_death = el
CRASHED real; console-getty supervisada; ENTE_BUS_SOCK estable en /run
- generación de la seed: template JSON autocontenido v1 (no acoplar build a
tawasuyu), arje-packager al llegar la atestación A1
- fases I1(✅)→I2(PID 1)→I3(bus único B.2)→I4(overlay+atestación)
- relación agent.sock (API IA, encima) vs arje-bus (control del init)
Contraparte en hammer del plan tawasuyu PLAN-ATESTACION-Y-HAMMER §B.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Primer eslabón ejecutable del bootstrap from-scratch (SDD 11). Stage 0 ingiere un
toolchain semilla pinned al store sin compilarlo:
- SeedSpec{kind,version,url,sha256}. seed_hash() deriva el ArtifactHash de la
identidad pinned (clase+versión+sha256), no de rutas del host ni del url → el
mismo tarball desde otro espejo produce el mismo artefacto.
- stage0(seed, store): descarga (curl, file:// offline-ok), verifica sha256
ANTES de extraer, untar y seal en el store. Idempotente si ya está sellado;
un sha erróneo no sella nada.
- Reusa el lab: hammer_build::download (+ nuevo verify_sha256 público), Store::seal.
Staging bajo el root del store para que el rename de seal sea atómico.
ADR 0008 fija la decisión (3 stages, zig semilla primaria, musl-cross-make
escotilla, ingerir-no-compilar el Stage 0). Renumerado a 0008 porque el 0007 lo
tomó la decisión de adoptar arje como init; SDD 11 y refs alineados con arje
(init supervisado que entrega el CRASHED real, ADR 0007).
4 unit tests (hash por identidad, ingest+seal+readonly, idempotencia, rechazo de
sha) sin red (file://). Workspace 218 verde.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diseña el corte del cordón umbilical con Alpine en tres etapas, reusando el lab
de Fase 0 sin mecanismo nuevo:
- Stage 0: toolchain semilla (zig primario, musl-cross-make como escotilla)
ingerido al store como fuente pinned por sha256 — el único insumo del host.
- Stage 1: userland mínimo cross-compilado (musl, busybox/toybox, init propio,
hammerd) ensamblado en un rootfs sellado. El init propio habilita el CRASHED
real diferido de Fase 5.
- Stage 2: rebuild nativo dentro del rootfs y diff de hashes stage1 vs stage1'
⇒ auto-alojamiento bit-reproducible.
Cada etapa anota (stage, recipe_hash, artifact_hash) en bootstrap.json, embrión
del log de transparencia (SDD 09 §4). Propone el crate hammer-bootstrap y la CLI
`hammer bootstrap stage0|stage1|stage2|--all`. Decisiones abiertas marcadas para
un futuro ADR 0007. Enlazado desde el índice de docs y el roadmap.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Arranque del proyecto hammer (distro AI-nativa: laboratorio funcional en el
sótano, terminal mutable clásica arriba, integración de IA programadora).
- Workspace Rust (compila, tests verdes): hammer-core, hammer-build,
hammer-cli (bin `hammer`), hammerd.
- SDDs 00-10 + 6 ADRs en docs/ con toda la arquitectura.
- Esqueletos navegables mapeados a las fases del roadmap; Fase 0/1 listas
para implementar el sandbox real.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>