tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Sutichaykuna

feat(granja): el disco del worker, que ningún log miraba — y el GC que nunca se había corrido

El latido informaba del worker sólo por su manifiesto (cuántos artefactos tiene) y jamás por su
disco, así que la caja se llenó en silencio: medido hoy, **90 % — 20 G libres de 196** con una tanda
de seis recetas en vuelo. El operador lo notó antes que cualquier log.

Censo de los 196 G, que cambia el diagnóstico:

    60 G  /root/tawasuyu/target   target de cargo de builds a mano · EN USO (mtime de hoy)
    62 G  store de takana         de los cuales 33 G eran versiones superadas
    23 G  work/                   repos 13 · tarballs 6,3 · sources 4 · sway-rootfs 3,8
    13 G  cachés de root          .rustup 4,6 · .cargo 4,3 · .cache 4,0

O sea que **el mayor consumidor no es la granja**: es un `target/` interactivo que ocupa el 31 % de
la caja. Y «el worker no se poda nunca» es una regla real, pero su motivo es que la COSECHA no
deshaga la poda —su rsync devolvía el store entero—, no que el store no deba podarse.
`scripts/store-gc.sh` existe desde el 2026-08-05 para esto y trae el mecanismo que evita ese churn
(`work/store-gc-superados.txt`, registro acumulado de lo que NO debe volver). **Nunca se había
corrido en el worker.** Al correrlo: 465 artefactos superados, **18 G → 43 G libres** (los 4,6 G que
faltan para los 29,6 medidos son hardlinks del rootfs hidratado, que el propio guion advierte). Los
77 huérfanos (2,9 G) no se tocaron: son la última copia de su receta.

Lo que entra acá es un número por ciclo y el comando sólo cuando hace falta. Suelo de 25 G porque un
árbol de fuentes del monorepo cuesta ~2,7 G y una tanda de seis encadenadas no puede quedarse sin
sitio a mitad: **el disco lleno no se lee como disco lleno, se lee como receta rota** — eso ya costó
ocho recetas KDE marcadas «deuda» sin tener nada roto.

⚠ El valor se valida como entero antes de compararlo, y no es estilo: con basura ahí el `[ -lt ]`
falla, el `elif` sale falso y la ejecución cae en el `else`, o sea que un `df` ilegible se
informaría como «✓ libres». Probado en los cuatro caminos: 38 G ✓ · 10 G ⚠⚠ con el comando · `df`
ilegible ⚠ · caja ausente ⚠.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

9e50ea43a3e29095e81ddcbd7418e15e5e4b844e · ruwaq Sergio · (6 p'unchaw ñawpaq) · tayta 6adfadb0f · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

1 qillqakuna · +29 −0

diff --git a/scripts/farm/cosecha-cron.sh b/scripts/farm/cosecha-cron.sh
index 0ad19454..01eb97fb 100755
--- a/scripts/farm/cosecha-cron.sh
+++ b/scripts/farm/cosecha-cron.sh
@@ -227,6 +227,35 @@ else
     else
       echo "   ⚠ no pude leer el manifiesto del worker — sigo"
     fi
+    # ── ⚠ EL DISCO DEL WORKER, QUE NADIE MIRABA ─────────────────────────────────────────────────
+    # El latido informaba del worker sólo por su manifiesto —cuántos artefactos tiene— y nunca por
+    # su disco, así que la caja se llenó sin que ningún log lo dijera: medido el 2026-10-15, **90 %
+    # (20 G libres de 196)** con una tanda en vuelo. Y «el worker no se poda nunca» es una regla
+    # real, pero su motivo es que la COSECHA no deshaga la poda (su rsync devolvía el store entero),
+    # no que el store no deba podarse: `scripts/store-gc.sh` existe para eso y trae el mecanismo que
+    # evita ese churn —`work/store-gc-superados.txt`, registro acumulado de lo que NO debe volver—.
+    # Nunca se había corrido allá. Al hacerlo: 465 artefactos superados, **18 G → 43 G libres**.
+    #
+    # ⇒ un número por ciclo, y el comando sólo cuando hace falta. El suelo son 25 G porque un árbol
+    # de fuentes del monorepo cuesta ~2,7 G y una tanda de seis encadenadas no puede quedarse sin
+    # sitio a mitad: el disco lleno NO se lee como disco lleno, se lee como receta rota (y eso ya
+    # costó ocho recetas KDE marcadas «deuda» sin tener nada roto).
+    libres_w=$($SSH "root@$ip" "df -Pm /opt/takana 2>/dev/null | awk 'NR==2{print \$4}'" 2>/dev/null || echo "")
+    # ⚠ Se valida que sea un ENTERO, y no es paranoia de estilo: con basura ahí el `[ -lt ]` falla,
+    # el `elif` sale falso y la ejecución cae en el `else`, o sea que un `df` ilegible se informaría
+    # como «✓ libres». Un control que falla hacia «todo bien» es exactamente el que no sirve.
+    case "$libres_w" in (*[!0-9]*|"") libres_w="" ;; esac
+    if [ -z "$libres_w" ]; then
+      echo "   ⚠ no pude leer el disco del worker (df ilegible o caja ausente)"
+    elif [ "$libres_w" -lt 25600 ]; then
+      echo "   ⚠⚠ disco del worker: $((libres_w/1024)) G libres — bajo el suelo de 25 G."
+      echo "      Un árbol de fuentes del monorepo cuesta ~2,7 G: una tanda se queda sin sitio a"
+      echo "      mitad y el fallo se lee como receta rota. El GC del store mide antes de borrar:"
+      echo "        ssh root@$ip 'cd /opt/takana && STORE=./store bash scripts/store-gc.sh'"
+      echo "        …y con --aplicar borra SÓLO los superados (los huérfanos hay que quererlos)."
+    else
+      echo "   disco del worker: $((libres_w/1024)) G libres ✓"
+    fi
     # ⚠ Lo que NO cubre esto: los artefactos nuevos quedan SÓLO en el volumen hasta que alguien
     # haga una pasada de respaldo. El volumen tiene borrado protegido, pero es UNA copia.
   done < "$FLEET"