Files
hammer/docs/10-roadmap.md
T
Sergio 5986200539 Fase 6 — traductor LLM real (Claude API) detrás de feature llm-claude
`ClaudeTranslator` implementa el trait `IntentTranslator` igual que el
`MockTranslator`, así que el `Orchestrator` no cambia: se traduce una
intención NL a un `.swm` válido vía `/v1/messages`.

Diseño:
- Síncrono (ureq + rustls), consistente con `AgentClient`. Tokio
  entraría sólo si el resto del crate lo pidiera.
- Trait `HttpClient` inyectable ⇒ tests sin red contra una fake que
  captura el request y devuelve un body pre-armado.
- Modelo por defecto: `claude-opus-4-8` (Opus 4.8, el más capaz al día
  de hoy). Adaptive thinking + `effort=high` por defecto. Override por
  env (`HAMMER_LLM_MODEL`, `HAMMER_LLM_EFFORT`, `HAMMER_LLM_BASE_URL`).
- System prompt documenta el shape exacto del `.swm` (4 variantes de
  mutación) y obliga JSON puro. Parseamos con `serde_json::from_str::
  <Swm>` + `verify_schema()` como gate adicional. Manejamos `refusal`,
  error envelopes de la API y code-fence markdown.

Activación:
- Sin feature: el módulo declara los tipos pero `ClaudeTranslator::new`
  está bajo cfg. El binario compila sin red.
- Con `--features llm-claude`: trae `ureq` con rustls, y la CLI activa
  `hammer ai --llm`.

CLI:
- `hammer ai` gana `--llm` (mutuamente excluyente con `--catalog`).
  Refactor de `run_ai` en helpers `run_with_mock_translator` /
  `run_with_llm_translator` para mantener legible el dispatch.
- Sin la feature, `--llm` falla con un mensaje claro pidiendo
  recompilar.

Tests (7 unit): happy path con verificación de URL/headers/body,
unwrap de markdown, `refusal`, error envelope, SWM mal formado,
verify_schema, helpers de strip_code_fence.
2026-06-10 16:29:27 +00:00

9.5 KiB
Raw Blame History

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 06: 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

  • hammer-core: tipos Recipe, ArtifactHash, Store (BLAKE3, layout /store).
  • hammer-build: sandbox con bubblewrap + zig cc → binario musl estático.
  • CAS: hashing de entrada, caché por hash, sellado en el store.
  • hammer 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, patchelf para el caso dinámico.
  • 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

  • Crate hammer-overlay: try / commit / discard / status sobre overlayfs.
  • Manifiesto persistente en <state_root>/<id>/state.json; status enumera.
  • commit promociona archivos y procesa whiteouts (char dev 0/0) como remove.
  • Subcomandos del CLI: hammer try [targets…], commit <id>, discard <id>, status.
  • Tests E2E con bwrap+user-ns, gateados en HAMMER_OVERLAY_TESTS=1 (kernel-dependiente).
  • 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

  • Crate hammer-journal: MutationEvent, Source (HammerHydrate/HammerCommit/External), append/read/tail/follow JSON-líneas. RFC 3339 sin chrono.
  • 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.
  • hammer journal [--tail N] [--follow] [--format pretty|json].
  • 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 .swmen progreso

  • Swm de/serialización YAML estable (roundtrip).
  • verify_schema (estructura + invariantes por mutación) y verify_base (distro_version + pins) con BaseRef / BaseCompat.
  • apply_config_edit (hunks -/+ con búsqueda exacta, no-fuzz) y apply_file_drop (base64 inline + verificación BLAKE3 vs content_hash).
  • Bridge Mutation::SourcePatchRecipe + build + hidratación.
  • 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 ▶ en progreso

  • /run/init.control (FIFO humano): mkfifo, reader que loguea cada línea, y hammer ctl <line> que escribe al FIFO con error claro si no existe.
  • /run/agent.sock (JSON-líneas) con handshake Hello/Welcome, auth via SO_PEERCRED y CapsPolicy por UID.
  • Comandos Compile/Inject/Query/Init; eventos Welcome/BuildReady/BuildFailed/Injected/QueryResult/InitAck/ Modified/Crashed/Error.
  • EventBus in-process: el watcher publica Modified y 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: hoy es código (default_policy), pendiente leerla de /etc/hammer/agent-caps.toml.
  • CRASHED real (requiere supervisión de servicios, que llega con el init propio del track posterior).
  • BuildFailed.log_tail con cola real del lab (hoy es None; el reason viene del Error de Rust pero no se conserva el log textual del sandbox).
  • Hecho cuando: un cliente externo dispara un build y recibe el evento de fin por el socket. Camino implementado (CompileBuildReady/BuildFailed) y la maquinaria alrededor cubierta por crates/hammerd/tests/bus_e2e.rs: handshake con peer creds, gating no_cap, Query, Modified fan-out, Init→FIFO. Un Compile real reusa el camino de Fase 0/1 (gated en HAMMER_NETWORK_TESTS).

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

  • Crate hammer-agent con tres piezas: - AgentClient: cliente síncrono del bus (handshake, compile/inject/query/init bloqueantes, drenado de eventos asíncronos). - IntentTranslator (trait) + MockTranslator cargado desde un IntentCatalog YAML (intent → .swm pre-armado). La integración con un LLM real se enchufa detrás del mismo trait sin tocar el bucle. - Orchestrator con run(intent) → Proposal: plan → schema → base → try (overlay o prefix) → apply (mutaciones puras + source_patch vía bus opcional) → verify → propose.
  • CLI: hammer ai <intent> --catalog F [--prefix DIR --base-ref F --bus SOCK].
  • Tipos del protocolo del bus movidos a hammer-core::proto para que hammerd y hammer-agent los compartan.
  • Tests: - 10 unit en hammer-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. ClaudeTranslator detrás de la feature llm-claude, con ureq + rustls. Modelo por defecto claude-opus-4-8, adaptive thinking + effort=high. Trait HttpClient inyectable para tests sin red. CLI: hammer 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 (hammer query <expr>) y remoto vía bus (Command::Query{what:"expr"}). Parser ELF64 mínimo para extraer DT_NEEDED.
  • Bucle de auto-reparación (cliente reacciona a Crashed con un nuevo plan). Orchestrator::run_with_repair(intent, &RepairPolicy) drena async events del bus tras cada apply; si llega Crashed, formatea una nueva intención y reentra hasta max_attempts. El Proposal final lleva el repair_chain completo para que el humano vea la evolución antes de hacer commit. CLI: hammer ai --repair-max-attempts.
  • Hecho cuando: una intención en lenguaje natural produce un cambio probado en overlay, presentado para commit humano. Demostrado por hammer ai con MockTranslator: intent → .swm → mutaciones aplicadas → Proposal con overlay_id + checks. El humano decide commit/discard. El upgrade a LLM real reusa todo el bucle.

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 §4).
  • Mini-lenguaje de consulta del sistema para la IA (SDD 08 §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.