Fase 2: anidación real de overlays con guard LIFO

Un `try` sobre un target ya cubierto apila un overlay nuevo: el kernel toma el
merged view de la capa inferior como lowerdir. Para que esto sea seguro y no un
footgun:

- fresh_id añade un `seq` atómico por proceso (`<ts>-<pid>-<seq>` zero-padded),
  eliminando la colisión de ids cuando un orquestador apila varios overlays en
  el mismo segundo — el caso real de la anidación.
- stack_key define un orden de apilamiento total y determinista (created_at,
  desempatado por id).
- blocking_overlays detecta capas más jóvenes que solapan targets (igualdad o
  ancestro de path). commit/discard fallan con Error::Shadowed si existen,
  exigiendo resolver LIFO de arriba hacia abajo — antes se desmontaba la capa
  equivocada del target compartido en silencio.

Tests: 6 unit del guard (disjuntos / solape / ancestro / desempate / commit y
discard rechazados) sin privilegios; e2e overlay_nested_stack_inside_userns
prueba el stack real (capa 2 ve la 1, rechazo LIFO, fusión arriba→abajo),
gated en HAMMER_OVERLAY_TESTS. Docs 04 y roadmap actualizados.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-06-10 20:39:32 +00:00
co-authored by Claude Opus 4.8
parent 303dfa2b58
commit fe9a2d50cf
4 changed files with 229 additions and 8 deletions
+9 -1
View File
@@ -44,7 +44,15 @@ pre-requisito de validación.
- [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).
- [x] Anidación real de overlays. Un `try` sobre un target ya cubierto apila un overlay nuevo
(el kernel usa el merged view inferior como `lowerdir`). Orden de apilamiento total y
determinista vía `stack_key` (`created_at` + `id`, con `seq` por proceso en `fresh_id`
para evitar colisiones en ráfaga). `commit`/`discard` exigen **LIFO**: `blocking_overlays`
detecta capas más jóvenes que solapan targets (igualdad o ancestro de path) y la operación
falla con `Error::Shadowed` si 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) por
`overlay_nested_stack_inside_userns`, gated en `HAMMER_OVERLAY_TESTS`. Pendiente menor:
unicidad de orden entre procesos concurrentes (track posterior).
### Fase 3 — Diario de mutaciones ▶ *en progreso*
- [x] Crate `hammer-journal`: `MutationEvent`, `Source` (HammerHydrate/HammerCommit/External),