SDD 28 §6.10: el respaldo desde otro hub destapó CINCO bloqueos, uno de ellos destructivo

El más grave: el paso del repo va con `--delete`, y el `/opt/takana` de la caja llegó por
`rsync --exclude=.git`. Una corrida real desde ahí habría BORRADO `.git` del Storage Box — el
historial entero — en silencio, porque rsync haría exactamente lo que se le pidió.

La regla que sale y que vale más allá de este script: **un `--delete` convierte «respaldar» en
«sincronizar», y sincronizar desde un origen incompleto no sube menos: BORRA.** Cualquier respaldo
con `--delete` necesita una guarda de completitud del ORIGEN, no sólo del destino.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x
This commit is contained in:
Sergio
2026-09-11 03:05:12 +00:00
co-authored by Claude Opus 5
parent d1d6645bd0
commit fb8bbba0b9
+20 -3
View File
@@ -777,14 +777,31 @@ Box** por el puerto 23 y lista su contenido. El manifiesto se refresca desde all
**3140 artefactos**, y de paso el script detecta **2 VACÍOS en el respaldo** (`gnome-desktop` ×2) que
excluye del manifiesto — su propia guarda, funcionando.
Para llegar ahí hubo que arreglar tres cosas, y las tres son de la misma familia: *el instrumental
asume que el hub es gioser*.
Para llegar ahí hubo que arreglar **cinco** cosas, y todas son de la misma familia: *el instrumental
asume que el hub es gioser*. El `--seco` ahora completa los tres pasos desde la caja y lee la
ocupación del box (111 G de 1 T).
1. **La raíz cableada.** `RAIZ="${RAIZ:-/mnt/vvv/takana}"` ⇒ desde cualquier otra máquina el script
moría con `cd: /mnt/vvv/takana: No such file or directory`. Ahora se deriva de dónde está el
script, como el resto de `scripts/`.
2. **El binario de desarrollo.** `HAMMER = ROOT/target/release/takana` (§6.7).
3. **El rsync del corpus no puede correr el respaldo del corpus.** El script exige
3. **El store no estaba donde el script creía.** Usaba `$RAIZ/store` cableado: en gioser es un
bind-mount DENTRO del repo, pero en una caja instalada es una partición en `/store`
`change_dir "/opt/takana/store" failed`. Y rsync devuelve **23**, que está en la lista de
reintentables, así que el bucle insistía — el cuadro que la propia cabecera del script documenta
(«un error reintentable que se repite 40 veces no es un corte de red: es algo estructural»).
Ahora `STORE` es env, con una guarda que aborta ANTES del bucle si la ruta no existe.
4. **🚨 Y una que habría costado el historial.** El paso [2/3] sube el repo **con `--delete`**
correcto, para eso está git. Pero el `/opt/takana` de la caja llegó por `rsync --exclude=.git`:
es una COPIA, no un clon. **Una corrida real desde ahí habría borrado `.git` del Storage Box**, o
sea el historial entero, y en silencio: rsync haría exactamente lo que se le pidió. Añadida una
guarda que aborta si la raíz no tiene `.git` (escotilla `REPO_INCOMPLETO=1`), probada en los tres
sentidos.
**La regla que sale**: un `--delete` convierte «respaldar» en «sincronizar», y sincronizar desde
un origen incompleto no sube menos — BORRA. Cualquier respaldo con `--delete` necesita una guarda
de completitud del ORIGEN, no sólo del destino.
5. **El rsync del corpus no puede correr el respaldo del corpus.** El script exige
`--compress-choice=zstd` y `recipes/rsync.toml` lo construía con `--disable-zstd` — porque cuando
se escribió, `zstd` no estaba en el catálogo. Hoy sí. Peor aún: el error 4 que devuelve rsync el
script lo clasifica como «no de red» y **no reintenta**, así que el respaldo simplemente no se