tawasuyu

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 1572Shell 203Rust 109Markdown 993728 Definiciones

⎇ 303dfa2b58dbdfb6b648a241ac6841f1a3542885 docs/10-roadmap.md Markdown · 189 líneas · 12.3 KB Historia crudo

Fase 3: op del watcher (Create/Edit/Delete) derivada del diario 303dfa2b5 · Sergio ·

1# SDD 10 — Roadmap
2
3## Estrategia: validar la capa AI-nativa sobre Alpine antes de la distro propia
4
5La innovación de `hammer` no es la distro — es el substrato AI-nativo (build determinista +
6mutable + diario + `.swm`). Construir bootstrap + init + userland de cero antes de validar esa
7capa sería arriesgar meses contra una hipótesis no probada. Por eso:
8
9> **Fase 0–6:** montamos `hammer` **sobre Alpine** (musl + busybox + FHS mutable, ya cocidos).
10> Validamos el bucle completo en semanas.
11> **Track posterior:** una vez probado, bajamos a distro propia (init propio, bootstrap propio,
12> userland propio) reusando todo el tooling sin retrabajo.
13
14Ver [ADR 0002](adr/0002-alpine-first.md).
15
16---
17
18## Fases (sobre Alpine)
19
20### Fase 0 — Laboratorio de build  ✅
21- [x] `hammer-core`: tipos `Recipe`, `ArtifactHash`, `Store` (BLAKE3, layout `/store`).
22- [x] `hammer-build`: sandbox con `bubblewrap` + `zig cc` → binario musl estático.
23- [x] CAS: hashing de entrada, caché por hash, sellado en el store.
24- [x] `hammer build <recipe.toml>` → imprime el hash del artefacto.
25- [x] Fuente git **y** tarball (sha256 fijado), patches opcionales.
26- [x] Heurística de build system: autotools / cmake / meson / make plano.
27- [x] Caché persistente de zig entre builds (~30s/build ahorrados).
28
29### Fase 1 — Hidratación ✅
30- [x] `hydrate(hash, target, mode)`: hardlink a FHS, `patchelf` para el caso dinámico.
31- [x] `hammer hydrate <hash> --into /`.
32
33### 🎯 Primer entregable (Fase 0 + 1) ✅
34**GNU grep 3.12 compilado estático desde el tarball upstream, sellado en el store
35(`b3:b81e893e…`), hidratado por hardlink a `/usr/bin/grep` y ejecutado dentro del rootfs
36Alpine vía `bwrap`** — confirma el corazón "fábrica funcional → FHS mutable" sobre el
37substrato Alpine. La VM/LXC dedicada queda como ejercicio de empaque, no como
38pre-requisito de validación.
39
40### Fase 2 — Overlay de experimentación  ✅
41- [x] Crate `hammer-overlay`: `try` / `commit` / `discard` / `status` sobre `overlayfs`.
42- [x] Manifiesto persistente en `<state_root>/<id>/state.json`; `status` enumera.
43- [x] `commit` promociona archivos y procesa whiteouts (char dev 0/0) como remove.
44- [x] Subcomandos del CLI: `hammer try [targets…]`, `commit <id>`, `discard <id>`, `status`.
45- [x] Tests E2E con bwrap+user-ns, gateados en `HAMMER_OVERLAY_TESTS=1` (kernel-dependiente).
46- [x] Hook al diario en `commit` (Fase 3 lo añadió vía `commit_with_journal`).
47- [ ] Anidación real de overlays (un solo overlay activo por target hoy).
48
49### Fase 3 — Diario de mutaciones  ▶ *en progreso*
50- [x] Crate `hammer-journal`: `MutationEvent`, `Source` (HammerHydrate/HammerCommit/External),
51      append/read/tail/follow JSON-líneas. RFC 3339 sin chrono.
52- [x] `hammerd` con `fanotify` (FAN_CLOSE_WRITE) sobre `/bin`,`/sbin`,`/usr/bin`,`/usr/sbin`,
53      `/lib`,`/usr/lib`,`/etc`. Filtra eventos bajo overlays activos. Fallback graceful sin
54      CAP_SYS_ADMIN.
55- [x] `hammer journal [--tail N] [--follow] [--format pretty|json]`.
56- [x] `hammer commit` registra cada archivo promocionado vía `commit_with_journal`.
57- [x] Refinar la `op` del watcher. `watcher::classify_op(&journal, path, deleted)` deriva
58      `Delete` (el fd resuelve a `" (deleted)"`, que ahora recortamos del path en vez de
59      colarlo al diario), `Edit` (el diario ya tenía un evento previo no-`Delete` del path) o
60      `Create` (primera mutación observada, o resurrección tras un `Delete`). Es la divergencia
61      que el diario atestigua, no la verdad absoluta del FS. La atribución plena vía
62      `FAN_REPORT_DFID_NAME` (nombre en eventos de directorio, `rename`-sobre-destino,
63      `open_by_handle_at`+`CAP_DAC_READ_SEARCH`) queda para el track posterior: `nix` 0.30 no
64      parsea los info-records de FID y el modo FID perdería el fd que hoy hashea el contenido.
65- [x] Hash del contenido tras la mutación (`content_hash`) y de-dup idempotente.
66      `hammer_journal::content_hash_of`/`hash_file` (blake3 plano, estilo `b3sum`, distinto del
67      `of_inputs` de artefactos). `Journal::record_dedup` omite el evento si deja el archivo
68      idéntico al último de ese path (o un `Delete` sobre algo ya borrado); `last_for_path` lo
69      resuelve. El watcher hashea cada `CLOSE_WRITE` y usa `record_dedup`: una reescritura sin
70      cambios ni ensucia el diario ni despierta el bus.
71
72### Fase 4 — Formato y flujo `.swm`  ▶ *en progreso*
73- [x] `Swm` de/serialización YAML estable (roundtrip).
74- [x] `verify_schema` (estructura + invariantes por mutación) y `verify_base` (distro_version
75      + pins) con `BaseRef` / `BaseCompat`.
76- [x] `apply_config_edit` (hunks `-/+` con búsqueda exacta, no-fuzz) y `apply_file_drop`
77      (base64 inline + verificación BLAKE3 vs `content_hash`).
78- [x] Bridge `Mutation::SourcePatch` → `Recipe` + `build` + hidratación.
79- [x] CLI: `hammer apply [--prefix DIR] [--base-ref base.json]`,
80      `hammer swm-verify`, `hammer export --journal DIR > out.swm`.
81- [x] `patch_url` / `content_url` remotos. `hammer_build::download` (curl, sólo fuera del
82      sandbox; acepta `file://` para tests offline). `build_source_patch` descarga `patch_url`
83      al `swm-recipes/<commit>.patch` antes de compilar; `hammer apply` descarga `content_url`
84      y **verifica BLAKE3 antes de escribir** (hash erróneo ⇒ nada tocado). Su integridad la
85      cubre `content_hash` (file_drop) y el build reproducible + `expected_hash` (patch).
86- [x] Provenance en `export`: mapa artefacto→receta vía sidecar `.hammer/recipe.toml` que
87      `hammer-build::build` escribe dentro del artefacto antes de sellar. `hammer export`
88      agrupa eventos por `artifact_hash` y emite UN `source_patch` por grupo cuya receta
89      sea recuperable; lo demás cae al fallback `file_drop`. Source `git` modelado;
90      `tarball` cae a file_drop con warning (pendiente extender SourcePatch).
91- [x] Firma `signature` (ed25519) y `TrustStore` local. `hammer_core::sign`: `KeyPair`
92      (genera/carga/escribe claves), `TrustStore::load(dir)` (lee `*.ed25519.pub`),
93      `Swm::verify_signature(&trust) → SigStatus` (`trusted`/`unknown-key`/`bad-sig`/
94      `unsigned`) sobre bytes canónicos JSON del manifiesto sin la firma. CLI:
95      `hammer keygen`, `hammer swm-sign`, y `hammer swm-verify --trust DIR`. La firma reporta
96      autoría/integridad; NO autoriza promover (sigue mandando reproducir + commit). SDD 09 §3,§5.
97- **Hecho cuando:** exportas un cambio, lo aplicas en otra máquina y reproduce idéntico.
98      ✅ Demostrado en `crates/hammer-cli/tests/swm_roundtrip.rs` para
99      `config_edit` + `file_drop`. `source_patch` reusa el camino de Fase 0/1 (gated en
100      `HAMMER_NETWORK_TESTS`).
101
102### Fase 5 — Bus de agente  ▶ *en progreso*
103- [x] `/run/init.control` (FIFO humano): `mkfifo`, reader que loguea cada línea, y
104      `hammer ctl <line>` que escribe al FIFO con error claro si no existe.
105- [x] `/run/agent.sock` (JSON-líneas) con handshake `Hello/Welcome`, auth via
106      `SO_PEERCRED` y `CapsPolicy` por UID.
107- [x] Comandos `Compile`/`Inject`/`Query`/`Init`; eventos
108      `Welcome`/`BuildReady`/`BuildFailed`/`Injected`/`QueryResult`/`InitAck`/
109      `Modified`/`Crashed`/`Error`.
110- [x] EventBus in-process: el watcher publica `Modified` y todas las conexiones lo reciben.
111- [x] Subsistemas independientes en `hammerd::main` (FIFO/watcher/bus en threads); si uno
112      falla en init, los demás siguen.
113- [x] Política expresiva leída de `/etc/hammer/agent-caps.toml` (`hammerd --agent-caps`).
114      `hammer_core::AgentCapsConfig`: `default` + `[[rule]]` por `uid`/`gid` (primera que casa
115      gana), `caps_for(uid,gid)`. `bus::policy_from_config` la enchufa; sin fichero o con
116      fichero inválido, cae a la política por-UID por defecto (avisando). Ejemplo en
117      `examples/agent-caps.toml`. El `default_policy` hardcoded queda como fallback.
118- [ ] `CRASHED` real (requiere supervisión de servicios, que llega con el init propio del
119      track posterior).
120- [x] `BuildFailed.log_tail` con cola real del lab. `Sandbox::run` hace *tee* de
121      stdout/stderr (sigue viéndose en vivo) y retiene las últimas 50 líneas; al fallar una
122      fase devuelve `BuildFailure { reason, log_tail }` envuelto en `Error::Other`. El bus lo
123      recupera por `BuildFailure::from_error` (downcast) y separa `reason` del log textual del
124      compilador, para que la IA reaccione al error real, no sólo al mensaje de Rust.
125- **Hecho cuando:** un cliente externo dispara un build y recibe el evento de fin por el
126  socket. ✅ Camino implementado (`Compile` → `BuildReady`/`BuildFailed`) y la maquinaria
127  alrededor cubierta por `crates/hammerd/tests/bus_e2e.rs`: handshake con peer creds,
128  gating `no_cap`, `Query`, `Modified` fan-out, `Init`→FIFO. Un `Compile` real reusa el
129  camino de Fase 0/1 (gated en `HAMMER_NETWORK_TESTS`).
130
131### Fase 6 — Integración de la IA  ▶ *en progreso*
132- [x] Crate `hammer-agent` con tres piezas:
133      - `AgentClient`: cliente síncrono del bus (handshake, `compile`/`inject`/`query`/`init`
134        bloqueantes, drenado de eventos asíncronos).
135      - `IntentTranslator` (trait) + `MockTranslator` cargado desde un `IntentCatalog` YAML
136        (intent → `.swm` pre-armado). La integración con un LLM real se enchufa detrás del
137        mismo trait sin tocar el bucle.
138      - `Orchestrator` con `run(intent) → Proposal`: plan → schema → base → try (overlay
139        o prefix) → apply (mutaciones puras + source_patch vía bus opcional) → verify →
140        propose.
141- [x] CLI: `hammer ai <intent> --catalog F [--prefix DIR --base-ref F --bus SOCK]`.
142- [x] Tipos del protocolo del bus movidos a `hammer-core::proto` para que `hammerd` y
143      `hammer-agent` los compartan.
144- [x] Tests:
145      - 10 unit en `hammer-agent` (translator + catalog + orchestrator).
146      - 3 e2e del bucle agéntico (prefix tmp → archivos esperados en disco).
147      - 1 e2e del cliente contra un *stub* del bus (handshake + Compile→BuildReady + Modified
148        asíncrono).
149- [x] Traductor LLM real (Claude API u otro), opcional vía feature flag o crate aparte.
150      `ClaudeTranslator` detrás de la feature `llm-claude`, con `ureq + rustls`. Modelo
151      por defecto `claude-opus-4-8`, adaptive thinking + `effort=high`. Trait `HttpClient`
152      inyectable para tests sin red. CLI: `hammer ai --llm` (mutuamente excluyente con
153      `--catalog`). Sin la feature, el flag falla con mensaje claro.
154- [x] Lenguaje de consulta del sistema (SDD 08 §6) para que la IA refiera servicios y
155      archivos sin rutas frágiles. Forma `kind:value` (`bin`, `file`, `pin`, `service`,
156      `depends`), evaluable local (`hammer query <expr>`) y remoto vía bus
157      (`Command::Query{what:"expr"}`). Parser ELF64 mínimo para extraer `DT_NEEDED`.
158- [x] Bucle de auto-reparación (cliente reacciona a `Crashed` con un nuevo plan).
159      `Orchestrator::run_with_repair(intent, &RepairPolicy)` drena async events del bus
160      tras cada apply; si llega `Crashed`, formatea una nueva intención y reentra hasta
161      `max_attempts`. El `Proposal` final lleva el `repair_chain` completo para que el
162      humano vea la evolución antes de hacer commit. CLI: `hammer ai --repair-max-attempts`.
163- **Hecho cuando:** una intención en lenguaje natural produce un cambio probado en overlay,
164  presentado para `commit` humano. ✅ Demostrado por `hammer ai` con `MockTranslator`:
165  intent → `.swm` → mutaciones aplicadas → `Proposal` con `overlay_id` + checks. El humano
166  decide `commit`/`discard`. El upgrade a LLM real reusa todo el bucle.
167
168---
169
170## Track posterior — distro propia
171- Reemplazar el init de Alpine por **tu init** (bus por pipes nativo).
172- **Bootstrap from-scratch** con `zig`/`musl-cross-make` (Stage 0/1/2 → imagen destino).
173- Userland propio (busybox/toybox a elección), `/etc/service/*` propios.
174- Decidir particionado/montaje de `/store` y `/var/lib/hammer`.
175- Log de transparencia para compartir en comunidad ([SDD 09](09-trust-model.md) §4).
176- Mini-lenguaje de consulta del sistema para la IA ([SDD 08](08-ai-integration.md) §6).
177
178---
179
180## Estado actual
181- ✅ Repo y workspace Rust inicializados.
182- ✅ SDDs y ADRs redactados.
183- ✅ Esqueletos de crates que compilan (`hammer build` stub).
184- ⏭️ Siguiente: implementar el sandbox de build real (Fase 0) y montar la VM/LXC Alpine.
185
186## Notas de entorno
187- Desarrollo principal: laptop del autor.
188- Host actual: Proxmox (`gioser.net`). La VM/LXC Alpine de pruebas vivirá aquí o en la laptop.
189- Repo: `https://gitea.gioser.net/sergio/hammer`.

10-roadmap.md

SDD 10 — Roadmap

Estrategia: validar la capa AI-nativa sobre Alpine antes de la distro propia

La innovación de hammer 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 hammer sobre 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 ✅

Fase 1 — Hidratación ✅

🎯 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 ✅

Fase 3 — Diario de mutaciones ▶ en progreso

Fase 4 — Formato y flujo .swm ▶ en progreso

Fase 5 — Bus de agente ▶ en progreso

Fase 6 — Integración de la IA ▶ en progreso


Track posterior — distro propia


Estado actual

Notas de entorno