250 líneas de comentario en 151 scripts. Control verificado: el diff no toca NI UNA línea que no empiece por #, y la sintaxis de los 151 pasa. El barrido saltea heredocs y cadenas triples, y el guardián DISPARÓ 3 veces: las tres eran el MOTD que el script escribe DENTRO de la imagen construida — texto del producto, no comentario del script. Se cambiaron aparte y a propósito, que es rebranding, no limpieza. Y el hallazgo caro: casaba contra , que es el TARGET de tracing — o sea el module_path!, o sea el nombre del crate. La etapa 4 lo movió a y el script quedó casando NADA. No fallaba: imprimía cero atribuciones, indistinguible de un log sin problemas. Comprobado con el binario (RUST_LOG=info sobre zlib), no deducido. Ahora acepta las dos, y tiene que seguir aceptándolas porque los logs viejos en disco dicen la vieja. Además 14 rutas de módulo en docs, que el barrido anterior no tocó porque no es frontera de palabra.
28 KiB
SDD 10 — Roadmap
Estrategia: validar la capa AI-nativa sobre Alpine antes de la distro propia
La innovación de takana no es la distro — es el substrato AI-nativo (build determinista +
mutable + diario + .swm). Construir bootstrap + init + userland de cero antes de validar esa
capa sería arriesgar meses contra una hipótesis no probada. Por eso:
Fase 0–6: montamos
takanasobre Alpine (musl + busybox + FHS mutable, ya cocidos). Validamos el bucle completo en semanas. Track posterior: una vez probado, bajamos a distro propia (init propio, bootstrap propio, userland propio) reusando todo el tooling sin retrabajo.
Ver ADR 0002.
Fases (sobre Alpine)
Fase 0 — Laboratorio de build ✅
takana-core: tiposRecipe,ArtifactHash,Store(BLAKE3, layout/store).takana-build: sandbox conbubblewrap+zig cc→ binario musl estático.- CAS: hashing de entrada, caché por hash, sellado en el store.
takana build <recipe.toml>→ imprime el hash del artefacto.- Fuente git y tarball (sha256 fijado), patches opcionales.
- Heurística de build system: autotools / cmake / meson / make plano.
- Caché persistente de zig entre builds (~30s/build ahorrados).
Fase 1 — Hidratación ✅
hydrate(hash, target, mode): hardlink a FHS,patchelfpara el caso dinámico.takana hydrate <hash> --into /.
🎯 Primer entregable (Fase 0 + 1) ✅
GNU grep 3.12 compilado estático desde el tarball upstream, sellado en el store
(b3:b81e893e…), hidratado por hardlink a /usr/bin/grep y ejecutado dentro del rootfs
Alpine vía bwrap — confirma el corazón "fábrica funcional → FHS mutable" sobre el
substrato Alpine. La VM/LXC dedicada queda como ejercicio de empaque, no como
pre-requisito de validación.
Fase 2 — Overlay de experimentación ✅
- Crate
takana-overlay:try/commit/discard/statussobreoverlayfs. - Manifiesto persistente en
<state_root>/<id>/state.json;statusenumera. commitpromociona archivos y procesa whiteouts (char dev 0/0) como remove.- Subcomandos del CLI:
takana try [targets…],commit <id>,discard <id>,status. - Tests E2E con bwrap+user-ns, gateados en
TAKANA_OVERLAY_TESTS=1(kernel-dependiente). - Hook al diario en
commit(Fase 3 lo añadió víacommit_with_journal). - Anidación real de overlays. Un
trysobre un target ya cubierto apila un overlay nuevo (el kernel usa el merged view inferior comolowerdir). Orden de apilamiento total y determinista víastack_key(created_at+id, conseqpor proceso enfresh_idpara evitar colisiones en ráfaga).commit/discardexigen LIFO:blocking_overlaysdetecta capas más jóvenes que solapan targets (igualdad o ancestro de path) y la operación falla conError::Shadowedsi las hay. Guard cubierto por 6 unit tests; el stack real (capa 2 ve la 1, LIFO rechaza commit de abajo, commit arriba→abajo fusiona) poroverlay_nested_stack_inside_userns, gated enTAKANA_OVERLAY_TESTS. Pendiente menor: unicidad de orden entre procesos concurrentes (track posterior).
Fase 3 — Diario de mutaciones ✅
- Crate
takana-journal:MutationEvent,Source(HammerHydrate/HammerCommit/External), append/read/tail/follow JSON-líneas. RFC 3339 sin chrono. hammerdconfanotify(FAN_CLOSE_WRITE) sobre/bin,/sbin,/usr/bin,/usr/sbin,/lib,/usr/lib,/etc. Filtra eventos bajo overlays activos. Fallback graceful sin CAP_SYS_ADMIN.takana journal [--tail N] [--follow] [--format pretty|json].takana commitregistra cada archivo promocionado víacommit_with_journal.- Refinar la
opdel watcher.watcher::classify_op(&journal, path, deleted)derivaDelete(el fd resuelve a" (deleted)", que ahora recortamos del path en vez de colarlo al diario),Edit(el diario ya tenía un evento previo no-Deletedel path) oCreate(primera mutación observada, o resurrección tras unDelete). Es la divergencia que el diario atestigua, no la verdad absoluta del FS. La atribución plena víaFAN_REPORT_DFID_NAME(nombre en eventos de directorio,rename-sobre-destino,open_by_handle_at+CAP_DAC_READ_SEARCH) queda para el track posterior:nix0.30 no parsea los info-records de FID y el modo FID perdería el fd que hoy hashea el contenido. - Hash del contenido tras la mutación (
content_hash) y de-dup idempotente.takana_journal::content_hash_of/hash_file(blake3 plano, estilob3sum, distinto delof_inputsde artefactos).Journal::record_dedupomite el evento si deja el archivo idéntico al último de ese path (o unDeletesobre algo ya borrado);last_for_pathlo resuelve. El watcher hashea cadaCLOSE_WRITEy usarecord_dedup: una reescritura sin cambios ni ensucia el diario ni despierta el bus.
Fase 4 — Formato y flujo .swm ✅
Swmde/serialización YAML estable (roundtrip).verify_schema(estructura + invariantes por mutación) yverify_base(distro_version + pins) conBaseRef/BaseCompat.apply_config_edit(hunks-/+con búsqueda exacta, no-fuzz) yapply_file_drop(base64 inline + verificación BLAKE3 vscontent_hash).- Bridge
Mutation::SourcePatch→Recipe+build+ hidratación. - CLI:
takana apply [--prefix DIR] [--base-ref base.json],takana swm-verify,takana export --journal DIR > out.swm. patch_url/content_urlremotos.takana_build::download(curl, sólo fuera del sandbox; aceptafile://para tests offline).build_source_patchdescargapatch_urlalswm-recipes/<commit>.patchantes de compilar;takana applydescargacontent_urly verifica BLAKE3 antes de escribir (hash erróneo ⇒ nada tocado). Su integridad la cubrecontent_hash(file_drop) y el build reproducible +expected_hash(patch).- Provenance en
export: mapa artefacto→receta vía sidecar.hammer/recipe.tomlquetakana-build::buildescribe dentro del artefacto antes de sellar.takana exportagrupa eventos porartifact_hashy emite UNsource_patchpor grupo cuya receta sea recuperable; lo demás cae al fallbackfile_drop. Sourcegitytarballmodelados:Mutation::SourcePatch(yRecipeInline) llevan camposrepo/commitxortarball/sha256(resueltos porswm::swm_source_kind, misma regla querecipe::Source::kind);takana exportreconstruye ambos sin caer afile_drop. - Firma
signature(ed25519) yTrustStorelocal.takana_core::sign:KeyPair(genera/carga/escribe claves),TrustStore::load(dir)(lee*.ed25519.pub),Swm::verify_signature(&trust) → SigStatus(trusted/unknown-key/bad-sig/unsigned) sobre bytes canónicos JSON del manifiesto sin la firma. CLI:takana keygen,takana swm-sign, ytakana swm-verify --trust DIR. La firma reporta autoría/integridad; NO autoriza promover (sigue mandando reproducir + commit). SDD 09 §3,§5. - Hecho cuando: exportas un cambio, lo aplicas en otra máquina y reproduce idéntico.
✅ Demostrado en
crates/hammer-cli/tests/swm_roundtrip.rsparaconfig_edit+file_drop.source_patchreusa el camino de Fase 0/1 (gated enTAKANA_NETWORK_TESTS).
Fase 5 — Bus de agente ✅ (queda CRASHED real, diferido al track posterior)
/run/init.control(FIFO humano):mkfifo, reader que loguea cada línea, ytakana ctl <line>que escribe al FIFO con error claro si no existe./run/agent.sock(JSON-líneas) con handshakeHello/Welcome, auth viaSO_PEERCREDyCapsPolicypor UID.- Comandos
Compile/Inject/Query/Init; eventosWelcome/BuildReady/BuildFailed/Injected/QueryResult/InitAck/Modified/Crashed/Error. - EventBus in-process: el watcher publica
Modifiedy todas las conexiones lo reciben. - Subsistemas independientes en
hammerd::main(FIFO/watcher/bus en threads); si uno falla en init, los demás siguen. - Política expresiva leída de
/etc/hammer/agent-caps.toml(hammerd --agent-caps).takana_core::AgentCapsConfig:default+[[rule]]poruid/gid(primera que casa gana),caps_for(uid,gid).bus::policy_from_configla enchufa; sin fichero o con fichero inválido, cae a la política por-UID por defecto (avisando). Ejemplo enexamples/agent-caps.toml. Eldefault_policyhardcoded queda como fallback. CRASHEDreal (requiere supervisión de servicios, que llega con el init propio del track posterior).BuildFailed.log_tailcon cola real del lab.Sandbox::runhace tee de stdout/stderr (sigue viéndose en vivo) y retiene las últimas 50 líneas; al fallar una fase devuelveBuildFailure { reason, log_tail }envuelto enError::Other. El bus lo recupera porBuildFailure::from_error(downcast) y separareasondel log textual del compilador, para que la IA reaccione al error real, no sólo al mensaje de Rust.- Hecho cuando: un cliente externo dispara un build y recibe el evento de fin por el
socket. ✅ Camino implementado (
Compile→BuildReady/BuildFailed) y la maquinaria alrededor cubierta porcrates/hammerd/tests/bus_e2e.rs: handshake con peer creds, gatingno_cap,Query,Modifiedfan-out,Init→FIFO. UnCompilereal reusa el camino de Fase 0/1 (gated enTAKANA_NETWORK_TESTS).
Fase 6 — Integración de la IA ✅
- Crate
takana-agentcon tres piezas: -AgentClient: cliente síncrono del bus (handshake,compile/inject/query/initbloqueantes, drenado de eventos asíncronos). -IntentTranslator(trait) +MockTranslatorcargado desde unIntentCatalogYAML (intent →.swmpre-armado). La integración con un LLM real se enchufa detrás del mismo trait sin tocar el bucle. -Orchestratorconrun(intent) → Proposal: plan → schema → base → try (overlay o prefix) → apply (mutaciones puras + source_patch vía bus opcional) → verify → propose. - CLI:
takana ai <intent> --catalog F [--prefix DIR --base-ref F --bus SOCK]. - Tipos del protocolo del bus movidos a
takana-core::protopara quehammerdytakana-agentlos compartan. - Tests:
- 10 unit en
takana-agent(translator + catalog + orchestrator). - 3 e2e del bucle agéntico (prefix tmp → archivos esperados en disco). - 1 e2e del cliente contra un stub del bus (handshake + Compile→BuildReady + Modified asíncrono). - Traductor LLM real (Claude API u otro), opcional vía feature flag o crate aparte.
ClaudeTranslatordetrás de la featurellm-claude, conureq + rustls. Modelo por defectoclaude-opus-4-8, adaptive thinking +effort=high. TraitHttpClientinyectable para tests sin red. CLI:takana ai --llm(mutuamente excluyente con--catalog). Sin la feature, el flag falla con mensaje claro. - Lenguaje de consulta del sistema (SDD 08 §6) para que la IA refiera servicios y
archivos sin rutas frágiles. Forma
kind:value(bin,file,pin,service,depends), evaluable local (takana query <expr>) y remoto vía bus (Command::Query{what:"expr"}). Parser ELF64 mínimo para extraerDT_NEEDED. - Bucle de auto-reparación (cliente reacciona a
Crashedcon un nuevo plan).Orchestrator::run_with_repair(intent, &RepairPolicy)drena async events del bus tras cada apply; si llegaCrashed, formatea una nueva intención y reentra hastamax_attempts. ElProposalfinal lleva elrepair_chaincompleto para que el humano vea la evolución antes de hacer commit. CLI:takana ai --repair-max-attempts. - Hecho cuando: una intención en lenguaje natural produce un cambio probado en overlay,
presentado para
commithumano. ✅ Demostrado portakana aiconMockTranslator: intent →.swm→ mutaciones aplicadas →Proposalconoverlay_id+ checks. El humano decidecommit/discard. El upgrade a LLM real reusa todo el bucle.
Track posterior — distro propia
Diseño completo en SDD 11 — Bootstrap from-scratch. Resumen:
- Bootstrap from-scratch en tres etapas (SDD 11 §3):
- Stage 0 ✅ — crate
takana-bootstrap+takana bootstrap stage0: ingiere el toolchain semilla pinned (SeedSpec, hash por identidad), verifica sha256 antes de sellar, idempotente. Cubierto por 4 unit + 1 e2e de CLI (offline,file://). - Stage 1 ✅ — userland mínimo, booteado end-to-end en QEMU. Recetas pinned
musl1.2.5 +busybox1.36.1 (estáticas, zig cc) +hammerd+arje-zero(recetas Cargo: repo pinned + deps vendoreadas en el fetch, build nativo crt-static víacargo rustc);takana bootstrap stage1los construye con la semilla, ensambla el rootfs FHS y lo sella. El init es arje-zero como PID 1 (/sbin/init→arje-zero) con su seed card/ente/seed.card.json; al bootear, arje-zero carga+valida la seed y supervisa hammerd + getty (elagent.socky el watcher fanotify de hammerd levantan). ElCRASHEDreal está demostrado:kill hammerd→ arje lo detecta (Killed(SIGTERM)), programa restart con backoff y lo re-encarna — la supervisión del init propio que la Fase 5 difirió (ADR 0007). Diseño en SDD 12; corrida y fixes en el runbook. Pendiente: bus único (B.2: exponer elCRASHEDa la capa de IA) y atestación (A1/A2). - Stage 2 ✅ — rebuild nativo dentro del rootfs y diff de hashes ⇒ auto-alojamiento
bit-reproducible CONFIRMADO end-to-end (host↔VM).
KVM=1 MEM=24576 ./scripts/selfhost-verify.shreconstruye los 4/4 dentro del rootfs Stage 1 con sólo su propio toolchain (arje-zero cu=1 compila en ~27 min in-VM), sella el rootfs y suof_tree(stage1') = b3:0039b2b9…iguala la referencia ⇒✓ REPRODUCIBLE: stage1' == stage1. El último no-determinismo (#3,arje-zero/codegen-units) se cazó y eliminó imponiendoCARGO_PROFILE_RELEASE_CODEGEN_UNITS=1en el sandbox (commit4c9bcc0). Corrida y veredicto en el runbook §8.
- Stage 0 ✅ — crate
- Reemplazar el init de Alpine por tu init (bus por pipes nativo) — habilita el
CRASHEDreal que la Fase 5 dejó diferido. Decisión: adoptararje(init from-scratch ya existente en el monorepotawasuyu, con PID 1 + supervisión real) en lugar de escribir uno nuevo — ver ADR 0007. Trae bus único, CAS BLAKE3 compartido y confianza en capas (procedencia takana + atestación arje). - Userland propio (busybox/toybox a elección),
/etc/service/*propios. - Decidir particionado/montaje de
/storey/var/lib/hammer. - Log de transparencia para compartir en comunidad (SDD 09 §4): el
bootstrap.jsonde SDD 11 §4 es su embrión. - Mini-lenguaje de consulta del sistema para la IA (SDD 08 §6).
Estado actual
- ✅ Fases 0–6 cerradas sobre Alpine: build determinista → hidratación → overlay
(con anidación LIFO) → diario (con
opCreate/Edit/Delete) →.swmfirmado → bus de agente → bucle agéntico con IA (mock + Claude real trasllm-claude). - ✅ El bucle completo está validado: una intención en lenguaje natural produce un cambio
probado en overlay, presentado para
commithumano. - ✅ 214 tests verdes en el workspace (1 e2e de overlayfs gated en
TAKANA_OVERLAY_TESTS; los de red enTAKANA_NETWORK_TESTS). - ⏳ Único ítem de las fases diferido:
CRASHEDreal (necesita supervisión de servicios → init propio del track posterior). - ✅ Track posterior arrancado y con su hito mayor cerrado: bootstrap from-scratch
Stage 0 → Stage 1 (booteado en QEMU, arje-zero como PID 1,
CRASHEDreal) → Stage 2 auto-alojamiento bit-a-bit confirmado (✓ REPRODUCIBLE, host↔VM). Baseline reproducible 4/4:of_tree(stage1)=b3:0039b2b9…(cu=1). - ✅ CERRADO — auto-alojamiento puro (variante b, SDD 11 §7.2b): que
takana construya el toolchain del builder desde fuente (no Alpine), reemplazando piezas una a una
con Stage 2 verificando cada paso. Pieza 1 hecha: GNU make
4.4.1(recipes/make.toml) se compila desde fuente con el lab — estática musl, selladab3:fbad44ac…, reproducible bit-a-bit. Swap implementado:BuilderSpec.swaps+ el flagtakana bootstrap builder --swap make=<hash>montan elmaketakana sobre/toolchain/usr/bin/make(pisando el de Alpine) y lo anclan en el hash lógico del builder ⇒ la procedencia deja de ser "todo Alpine" y se vuelve auditable (purab3:8a370f5d…vs swappedb3:e7e2282c…en el host). El verify lo expone conSWAP_MAKE=1. Stage 2 in-VM con el swap (2026-06-13):✗ DIVERGENTE→ causa raíz hallada y arreglada, make inocente. El swap dioof_tree(stage1')=9adefb82… ≠ 0039b2b9…, pero el control sin swap dio el mismo9adefb82…⇒ el make no era la causa. Bisección (descartando uno a uno: musl/busybox idénticos con takana-make incluso a-j1; hammerd reproduce): el único flaky era arje-zero, no-determinista incluso host-a-host (~62 KB de diferencia, mismo rustc/commit/flags) — el backend paralelo de rustc/LLVM (ThinLTO) quecodegen-units=1NO serializa. Builds seriales (la VM tiene 1 vCPU, otaskset -c 0) reproducían bit-a-bit a9adefb82…; los paralelos divergían. El viejo0039b2b9…era un build paralelo no-fiable. Fix:CARGO_BUILD_JOBS=1en el sandbox (serializa el jobserver ⇒ backend de 1 hilo, independiente del nº de CPUs) — verificado: arje-zero a todas las CPUs +JOBS=1da byte-idéntico al serial. Baseline del host refrescado yEXPECT_REFre-baseado a9adefb82…. Pieza 2 — busybox (sh+coreutils) validada en host: el/toolchainAlpine es mixto (busybox para sh/sed/grep/awk/tar/find, GNU coreutils para cp/mkdir/install); el swap monta el busybox estático de takana sobre/toolchain/bin/busybox(--swap busybox=<hash>:bin/busybox, sin código nuevo). Con make+busybox de takana los 4/4 (incl. busybox compilándose a sí mismo con takana-busybox de shell) reproducenof_tree=9adefb82…. Expuesto conSWAP_BUSYBOX=1. ✓ REPRODUCIBLE in-VM con ambos swaps (2026-06-13):KVM=1 MEM=24576 SWAP_MAKE=1 SWAP_BUSYBOX=1 ./scripts/selfhost-verify.shenlibrereconstruyó los 4/4 dentro de la VM con el/toolchainhammerizado (makefbad44ac…+ busybox56664d70…pisando los de Alpine) y elof_tree(stage1')igualó la referencia9adefb82…⇒✓ REPRODUCIBLE: stage1' == stage1,DRIVER_RC=0(~54 min, arje-zero cu=1 in-VM). La procedencia del builder ya no es "todo Alpine" y el auto-alojamiento sigue bit-a-bit. Pieza 3 — linux-headers (recipes/linux-headers.toml, kernel 6.16.12, validada en host): los headers UAPI que musl/busybox#include. Construidos conmake headers(NOheaders_install: sursyncfinal falta en el toolchain hermético) +unifdefvía HOSTCC=zig cc. El swap es de directorio, no de binario: se extendióassemble_builderpara reemplazar árboles enteros (los 13 subdirs kernel-ownedusr/include/{linux,asm, asm-generic,cxl,fwctl,misc,mtd,rdma,regulator,scsi,sound,video,xen}), dejando intactos los de musl (bits/sys/net…). Hallazgo clave: el paquete de Alpine NO es elmake headerscrudo — trae 5 archivos distintos a la salida vainilla; 3 de ellos (scsi/{scsi,scsi_ioctl,sg}.h, legacy userspace, NO UAPI) son requeridos porque el appletejectde busybox los incluye y sin ellos no compila. Se aportan content-pinned víarecipes/linux-headers-alpine-compat.patch(+ 2 cosméticos: kernel.h/if_tunnel.h) y se pisan sobre la salida. Criterio de éxito fuerte alcanzado:diff -rdel header-tree takana contra el de Alpine = vacío ⇒ el toolchain swapeado es byte-idéntico al baseline ⇒ todo lo que compile abajo reproduce de por sí (garantía por construcción, más fuerte que un rebuild). Expuesto conSWAP_LINUX_HEADERS=1; ensamblado end-to-end en host OK. Falta sólo la corrida in-VM para el sello. Pieza 4 — bwrap (recipes/bwrap.toml, bubblewrap 0.11.0, validada en host): EL sandbox del propio lab (takana-build/src/sandbox.rs). Binario estático ⇒ swap de archivo sobre/toolchain/usr/bin/bwrap. Como tool del toolchain (no input del 4/4) NO necesita casar byte-a-byte con Alpine, sólo aislar igual. Tres sub-hallazgos que la hicieron más jugosa que make/busybox: (1) el toolchain Alpine no trae meson/ ninja/python — bypaseamos meson compilando los 4.cde bubblewrap directo conzig cc(+config.htrivial generado a mano); (2) depende de libcap, cuyo -dev (libcap.a +sys/capability.h) tampoco está en el toolchain ⇒ nueva recetarecipes/libcap.toml; (3) para que el build de bwrap viera libcap se agregó materialización de build-deps al lab:deps.buildahora se construye recursivamente y cada artefacto sellado se apila como capa--overlay-srcBAJO el rootfs del sandbox, dejando sususr/{include,lib,lib/pkgconfig}en/usr— pkgconf y zig cc los hallan sin plumbing de flags (recetas sin deps quedan byte-iguales, baseline intacto). Validación host fuerte: takana-bwrap es estático, corre--version/sandboxea, y musl rebuildeó byte-idéntico usándolo de sandbox (bisección). Expuesto conSWAP_BWRAP=1; falta sólo correrla in-VM. Pieza 5 — coreutils (recipes/coreutils.toml, GNU 9.8, validada en host): cp/mkdir/ln/chmod/mv/ install que elmake installde musl/busybox usa. Empaquetada multicall (--enable-single-binary, igual layout que Alpine: un/bin/coreutils+ ~100 symlinks), así un solo swap del binario rutea todos los applets. Tool, no input ⇒ build vainilla sin patches. Bisección host fuerte: musl Y busybox rebuildearon byte-idéntico (57b66a2e,56664d70) con takana-coreutils pisando el de Alpine. Expuesto conSWAP_COREUTILS=1. Falta correrla in-VM. Herramientas de toolchain extra desde fuente (provenance, no en el camino del 4/4 mínimo):recipes/{patch,m4,pkgconf}.toml— GNU patch 2.8 (lo usaapply_patches), GNU m4 1.4.20 (base autotools) y pkgconf 2.5.1 (lee los.pcque materializa el lab). Construidas estáticas con zig cc y validadas (corren, versión correcta, funcionales). El 4/4 mínimo (musl/busybox/hammerd/arje-zero) no las invoca, así que no llevan flagSWAP_*dedicado: son swapeables con el escapeSWAPS="name=hash:rel". Avanzan el "builder reconstruible al completo, no todo-Alpine" aunque el verify no las ejercite hoy. ✅ CORRIDA in-VM acumulada (2026-06-14): los 5 swaps de sandbox juntos✓ REPRODUCIBLE.KVM=1 MEM=... SWAP_MAKE=1 SWAP_BUSYBOX=1 SWAP_LINUX_HEADERS=1 SWAP_BWRAP=1 SWAP_COREUTILS=1 ./scripts/selfhost-verify.shreconstruyó los 4/4 dentro de la VM con el/toolchainhammerizado y elof_tree(stage1')igualó9adefb82…bit a bit (~46 min, sobrevive presión de memoria). Recetas extra de toolchain desde fuente añadidas después:recipes/binutils.toml(GNU binutils 2.45.1, CC=gcc porque zig miscompila → segfault, link dinámico, inerte al of_tree porque zig ya provee as/ld/ar; swapeable víaSWAPS=). - ✅✅✅ CERRADO — frente rust/llvm (la última pieza grande del toolchain): cadena purista
mrustc → rustc 1.90.0 → 1.91.0 → 1.91.1conx.pyreal, reusando un LLVM 20.1.8 externo en todo (sin rebuild).rust-1.91.1-prefix/bin/{rustc,cargo}corren standalone (rpath, host musl), versión EXACTA de Alpine. Auto-consistencia VERIFICADA in-VM (2026-06-18):SWAP_RUST=1 KVM=1 PRESEED=hammerd ./scripts/selfhost-verify.sh→✓ REPRODUCIBLEbit a bit (host y VM danof_tree=7fa6cb4e…=RUST_EXPECT_REF, ≠9adefb82…Alpine). Para el rebuild bit-reproducible se committeó elCargo.lockde arje-zero en tawasuyu + se bumpeó la receta (--locked);PRESEED=hammerdes OBLIGATORIO (arje-zero vendorea ~1973 crates y un rebuild in-VM completo desborda RAM). Artefactos enscripts/rust-frontier/. - ✅✅✅ CAPSTONE (2026-06-19): LOS 6 SWAPS JUNTOS
✓ REPRODUCIBLEIN-VM — los 5 tools de sandbox +SWAP_RUSTa la vez,of_tree=7fa6cb4e…bit a bit (~7 min,MEM=12288 PRESEED=hammerd, sin swap en el host). El auto-alojamiento del toolchain del builder está COMPLETO; las piezas inertes al 4/4 (binutils/pkgconf) quedan salteadas por diseño. Capa de arranque restante (status libc, no vale de-Alpinizar): gcc, m4, libz/libzstd runtime. - ✅✅✅ CERRADO — kernel-from-source (SDD 11, último eslabón de soberanía): el
kernel Linux 6.16.12 construido por takana (
recipes/linux.toml,CC=gcc, monolítico e1000/overlay/userns=y) BOOTEA la VM del selfhost-verify y el rebuild in-VM da✓ REPRODUCIBLEbit a bit (TAKANA_KERNEL=1 KERNEL=<bzImage> KVM=1 MEM=16384 PRESEED=hammerd). Config lean (build ~35→~14 min, bzImage 13→8.7 MB). El muropivot_root(/= rootfs absoluto del mount-ns ⇒ bwrap no pivota; quirk que el kernel host enmascaraba) se resolvió con un/initwrapperswitch_roota tmpfs (condicional aTAKANA_KERNEL=1, no toca el camino del kernel host). Build-deps del kernel TODOS de-Alpinizados:recipes/{flex,bison}.toml(kconfig),recipes/openssl.toml(3.5.4, certs/extract-cert),recipes/elfutils.toml(sólo libelf 0.194 para objtool, con shims musl mínimos). Capa de arranque restante: m4, libz/libzstd, gcc (status libc). - ✅ Etapa A — orquestador
bootstrap all+ log de transparencia exportable:takana_bootstrap::allencadena stage0→stage1→stage2 en una corrida del host (takana bootstrap all --url … --sha256 … --version …) y deja elbootstrap.jsonpoblado;takana bootstrap manifestlo imprime (la superficie de publicación del log, SDD 11 §4). El veredicto✓ REPRODUCIBLElo sigue sellando el rebuild in-VM (selfhost-verify.sh), no el host —allancla la referencia para que ese rebuild la compare. Pendiente de la Etapa A: el smoke end-to-end de I3 (bus único) contra un init vivo (arje+hammerd corriendo) — no reproducible sin la VM, mismo status que cualquier sello in-VM. - ✅ B.2 —
CRASHEDreal a la capa de IA (cableado end-to-end): elEvent::Crasheddel bus de agente ya tiene fuente real. Fuente (arje):arje-busganóBusRequest::SubscribeBusPayload::Event(BusEvent); arje-zero difunde enon_deathEnteCrashed{id,label,status}/EnteRestarting{delay_ms}/EnteExiteda las conexiones suscritas, purgando las muertas (4 tests). Sink (takana):hammerd::crashestraduce la señal normalizada →Event::Crashed→EventBus→/run/agent.sock(3 tests). Transporte (takana):hammerd::arje_linkse suscribe al bus de arje ($ENTE_BUS_SOCK) y reenvía cadaBusEvent. Decisión de wire = relectura del frame postcard (mirror mínimo del subconjunto de suscriptor; no arrastra el crate-graph de arje ⇒ takana sigue standalone). El layout está verificado byte-a-byte contraarje-busreal (mismas versiones ulid 1.2 / postcard 1.1; frame Subscribe =[00,01,00,0d]) y cubierto por un round-trip local (2 tests). Resta sólo el smoke end-to-end contra un init vivo (no reproducible sin arje corriendo).
- ⏭️ También pendiente (Stage 1): atestación arje (A1/A2 — ya cableada en el repo arje, resta sólo enchufar su veredicto de boot a este roadmap).
Notas de entorno
- Desarrollo principal: laptop del autor.
- Host actual: Proxmox (
gioser.net). La VM/LXC Alpine de pruebas vivirá aquí o en la laptop. - Repo:
https://gitea.gioser.net/sergio/hammer.