Nuevo crate hammer-journal y watcher fanotify en hammerd. Cierra el contrato
del SDD 05 §3 ("toda mutación trazable/opaca registrada y consultable") y
liquida la deuda Fase 2 sobre el hook al diario en commit.
hammer-journal:
- MutationEvent (op, path, by, content_hash?, note?), MutationOp
Create/Replace/Edit/Delete, Source enum tagged HammerHydrate /
HammerCommit / External (distingue trazable de opaco para `export`).
- Journal: record append-only + sync_data, read_all/tail/follow (offset),
resilencia a líneas malformadas (kernel-panic mid-write se ignora).
- RFC 3339 UTC sin chrono (Hinnant civil_from_days, válido para todo i64).
- 7 unit tests cubren append/read/tail/follow/malformed/serde/timestamp.
Watcher (hammerd):
- nix::sys::fanotify con FAN_CLASS_NOTIF + FAN_CLOSE_WRITE +
FAN_EVENT_ON_CHILD sobre /bin /sbin /usr/bin /usr/sbin /lib /usr/lib /etc.
- Resuelve path por readlink(/proc/self/fd/<n>) — sin FAN_REPORT_DFID_NAME
para Fase 3 v1; el refinamiento Create/Edit con metadata viene después.
- Filtro: hammer_overlay::status(state_root) → ignora paths bajo un overlay
activo (el ruido del experimento no entra hasta el commit).
- Sin CAP_SYS_ADMIN, fanotify_init falla; hammerd lo reporta y sigue
durmiendo (preparado para que Fase 5 monte el bus encima).
- 3 unit tests sobre defaults/filtros/uid lookup.
CLI:
- hammer journal [--dir] [--tail N] [--follow] [--format pretty|json].
- hammer commit registra cada archivo promocionado/removido vía
commit_with_journal; --no-journal opt-out.
Hook overlay::commit:
- commit_with_journal(id, root, journal): para cada copied/removed emite un
MutationEvent con Source::HammerCommit { overlay }. Test directo del hook
sin overlayfs (lógica pura) más el e2e existente que ya cubre los mounts.
57 tests workspace, 0 fallos.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Nuevo crate hammer-overlay + integración al CLI. Diseño:
- API: try_overlay(targets, state_root) → OverlayId, status, discard, commit.
- Layout: <state>/<id>/{state.json, mounts/<slug>/{upper,work}}. El manifiesto
permite a `status` enumerar overlays sin estado en memoria.
- commit: desmonta primero, luego cp -a upper→lower por archivo, procesa
whiteouts (char dev 0/0) como remove en el lower. Sin diario aún (Fase 3).
- discard: umount LIFO + rm del state. Idempotente sobre `not mounted`.
- Privilegios: la librería no asume; emite `mount`/`umount` y deja que el
caller los tenga. CLI sin sudo falla con mensaje claro. El daemon de
Fase 5 los obtendrá desde contexto privilegiado.
Tests:
- 6 unit tests (slug, defaults, manifest, errores).
- E2E bajo bwrap+user-ns con CAP_SYS_ADMIN: ciclo try → escribir → commit →
verificar persiste; try → escribir → discard → verificar revertido;
remove en merged → commit → whiteout aplicado al lower. Gateado en
HAMMER_OVERLAY_TESTS=1: por defecto, los kernels deniegan el mount
overlayfs en user-ns no-privilegiado (sysctl/AppArmor); el dev habilita
cuando su entorno lo permite o corre con sudo.
CLI: hammer try [targets…] --state-root, commit <id>, discard <id>, status.
Refactor mínimo: las cmds que no tocan artefactos ya no abren (ni crean) el
store. `hammer status --state-root /tmp` no toca /store.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- hammer-core: Recipe::from_toml/load_from_path reales; Phases override;
hashing por *contenido* de patches; Store::seal con rename atómico + chmod
r/o recursivo; Store::find_by_hash por prefix.
- hammer-build: fetch (git mirror + archive al commit fijado), Sandbox::run
sobre bwrap con rootfs Alpine como tmp-overlay y zig cc inyectado,
orquestador build con caché por hash, hydrate con hardlinks atómicos
(link tmp + rename) que pisa el FHS sin tocar inodes del store.
- BuildConfig leído de HAMMER_{ROOTFS,ZIG,WORK} o defaults relativos al store.
- CLI: hammer build / hammer hydrate funcionales; logs a stderr para que
stdout sea pipeable (el hash y nada más).
- 20 unit + 1 integration test end-to-end (hello.c estático compilado bajo
bwrap, sellado, hidratado, ejecutado en sandbox limpio).
Cierra el primer entregable del roadmap salvo "X = grep" (falta heurística
autotools/cmake en resolve_phases).
Co-Authored-By: Claude Opus 4.7 <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>