- hammer-core::swm: verify_schema (invariantes por mutación) + verify_base con BaseRef/BaseCompat/PinDiff (distro_version + pins). FileDrop.content_b64 para .swm autocontenidos. - hammer-core::apply: primitivas puras apply_config_edit (hunks -/+ con búsqueda exacta de bloque, ambiguo => error), apply_file_drop (base64 + verify BLAKE3), rebase_path para tests con prefix. - hammer-build::swm_bridge: Mutation::SourcePatch -> Recipe sintética + patch inline materializado + build, con comparación opcional contra expected_hash. - hammer-cli: nuevos subcomandos apply [--prefix --base-ref --skip-source-patch --state-root], swm-verify, export --journal --base-ref --since. - Tests: 18 unit nuevos en hammer-core (apply + verify), 5 en swm_bridge, 4 e2e en hammer-cli (roundtrip yaml -> apply bajo prefix reproduce los archivos). - Roadmap y SDD 06 actualizados con lo cerrado y lo pendiente (URLs remotos, provenance en export vía mapa artefacto->receta, firma ed25519).
111 lines
5.9 KiB
Markdown
111 lines
5.9 KiB
Markdown
# 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](adr/0002-alpine-first.md).
|
||
|
||
---
|
||
|
||
## Fases (sobre Alpine)
|
||
|
||
### Fase 0 — Laboratorio de build ✅
|
||
- [x] `hammer-core`: tipos `Recipe`, `ArtifactHash`, `Store` (BLAKE3, layout `/store`).
|
||
- [x] `hammer-build`: sandbox con `bubblewrap` + `zig cc` → binario musl estático.
|
||
- [x] CAS: hashing de entrada, caché por hash, sellado en el store.
|
||
- [x] `hammer build <recipe.toml>` → imprime el hash del artefacto.
|
||
- [x] Fuente git **y** tarball (sha256 fijado), patches opcionales.
|
||
- [x] Heurística de build system: autotools / cmake / meson / make plano.
|
||
- [x] Caché persistente de zig entre builds (~30s/build ahorrados).
|
||
|
||
### Fase 1 — Hidratación ✅
|
||
- [x] `hydrate(hash, target, mode)`: hardlink a FHS, `patchelf` para el caso dinámico.
|
||
- [x] `hammer 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 ✅
|
||
- [x] Crate `hammer-overlay`: `try` / `commit` / `discard` / `status` sobre `overlayfs`.
|
||
- [x] Manifiesto persistente en `<state_root>/<id>/state.json`; `status` enumera.
|
||
- [x] `commit` promociona archivos y procesa whiteouts (char dev 0/0) como remove.
|
||
- [x] Subcomandos del CLI: `hammer try [targets…]`, `commit <id>`, `discard <id>`, `status`.
|
||
- [x] Tests E2E con bwrap+user-ns, gateados en `HAMMER_OVERLAY_TESTS=1` (kernel-dependiente).
|
||
- [x] Hook al diario en `commit` (Fase 3 lo añadió vía `commit_with_journal`).
|
||
- [ ] Anidación real de overlays (un solo overlay activo por target hoy).
|
||
|
||
### Fase 3 — Diario de mutaciones ▶ *en progreso*
|
||
- [x] Crate `hammer-journal`: `MutationEvent`, `Source` (HammerHydrate/HammerCommit/External),
|
||
append/read/tail/follow JSON-líneas. RFC 3339 sin chrono.
|
||
- [x] `hammerd` con `fanotify` (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.
|
||
- [x] `hammer journal [--tail N] [--follow] [--format pretty|json]`.
|
||
- [x] `hammer commit` registra cada archivo promocionado vía `commit_with_journal`.
|
||
- [ ] Refinar op del watcher (Create vs Replace vs Edit con FAN_REPORT_DFID_NAME).
|
||
- [ ] Hash del contenido tras la mutación (`content_hash`) y de-dup idempotente.
|
||
|
||
### Fase 4 — Formato y flujo `.swm` ▶ *en progreso*
|
||
- [x] `Swm` de/serialización YAML estable (roundtrip).
|
||
- [x] `verify_schema` (estructura + invariantes por mutación) y `verify_base` (distro_version
|
||
+ pins) con `BaseRef` / `BaseCompat`.
|
||
- [x] `apply_config_edit` (hunks `-/+` con búsqueda exacta, no-fuzz) y `apply_file_drop`
|
||
(base64 inline + verificación BLAKE3 vs `content_hash`).
|
||
- [x] Bridge `Mutation::SourcePatch` → `Recipe` + `build` + hidratación.
|
||
- [x] CLI: `hammer apply [--prefix DIR] [--base-ref base.json]`,
|
||
`hammer swm-verify`, `hammer export --journal DIR > out.swm`.
|
||
- [ ] `patch_url` / `content_url` remotos (hoy sólo inline).
|
||
- [ ] Provenance en `export`: mapa artefacto→receta para emitir `source_patch` en vez de
|
||
`file_drop`. Hoy se emiten file_drops con `content_b64`, lo que reproduce byte-a-byte
|
||
pero pierde la receta original.
|
||
- [ ] Firma `signature` (ed25519) y `TrustStore` local.
|
||
- **Hecho cuando:** exportas un cambio, lo aplicas en otra máquina y reproduce idéntico.
|
||
✅ Demostrado en `crates/hammer-cli/tests/swm_roundtrip.rs` para
|
||
`config_edit` + `file_drop`. `source_patch` reusa el camino de Fase 0/1 (gated en
|
||
`HAMMER_NETWORK_TESTS`).
|
||
|
||
### Fase 5 — Bus de agente
|
||
- [ ] `/run/init.control` (FIFO humano) + `/run/agent.sock` (JSON-líneas, `SO_PEERCRED`).
|
||
- [ ] Comandos `COMPILE`/`INJECT`/`QUERY`/`INIT`; eventos `BUILD_*`/`CRASHED`/`MODIFIED`.
|
||
- **Hecho cuando:** un cliente externo dispara un build y recibe el evento de fin por el socket.
|
||
|
||
### Fase 6 — Integración de la IA
|
||
- [ ] Cliente de agente; traductor intención NL → `.swm`; bucle plan→build→try→verify→propose.
|
||
- **Hecho cuando:** una intención en lenguaje natural produce un cambio probado en overlay,
|
||
presentado para `commit` humano.
|
||
|
||
---
|
||
|
||
## Track posterior — distro propia
|
||
- Reemplazar el init de Alpine por **tu init** (bus por pipes nativo).
|
||
- **Bootstrap from-scratch** con `zig`/`musl-cross-make` (Stage 0/1/2 → imagen destino).
|
||
- Userland propio (busybox/toybox a elección), `/etc/service/*` propios.
|
||
- Decidir particionado/montaje de `/store` y `/var/lib/hammer`.
|
||
- Log de transparencia para compartir en comunidad ([SDD 09](09-trust-model.md) §4).
|
||
- Mini-lenguaje de consulta del sistema para la IA ([SDD 08](08-ai-integration.md) §6).
|
||
|
||
---
|
||
|
||
## Estado actual
|
||
- ✅ Repo y workspace Rust inicializados.
|
||
- ✅ SDDs y ADRs redactados.
|
||
- ✅ Esqueletos de crates que compilan (`hammer build` stub).
|
||
- ⏭️ Siguiente: implementar el sandbox de build real (Fase 0) y montar la VM/LXC Alpine.
|
||
|
||
## 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`.
|