fix(granja): «0 fallan» mentía cuando la caja no podía escribir el veredicto
`ok` y `fail` se cuentan LISTANDO ficheros en `status/`, y los escribe cada worker con `echo … > status/$n.ok|.fail`. Si la caja no puede escribir —disco lleno, inodos agotados— **no se escribe ninguno de los dos**, así que la receta no entra en ninguna cuenta: se evapora. Y entonces la línea del yield miente de la peor forma, que es sonando bien. Medido en el worker el 2026-10-07: **«BUILD-YIELD: 37/57 construyen (0 fallan)»** con 63.000 `No space left on device` en el journal y la granja sin sellar nada desde nueve días antes. Las 20 que no construyeron no se contaron como fallo **porque el mecanismo que registra el fallo lo rompía la misma causa que el fallo**. Lo anoté ese día y arreglé el watchdog —que miraba bytes cuando morían los inodos—, pero no esto; y hoy volvió a pasar por el otro eje: 7 ENOSPC en veinte minutos con el disco al 100 %. Un ✓ falso es peor que un ✗ falso: el ✗ falso manda a leer una receta sana, el ✓ falso dice que la granja va bien durante nueve días. El arreglo no es escribir el fichero de otra manera —si no hay disco, no hay disco— sino **contar lo que falta**: `n_total` es la cola ya filtrada, así que `n_total - ok - fail` es exactamente lo que desapareció. Y se imprime la presión de los DOS medidores al lado, porque la causa casi siempre es ésa y buscarla a mano es el rato que se perdió la otra vez. Control: con 5 en la cola y sólo 3 ficheros de status (2 .ok, 1 .fail) ⇒ «2 receta(s) SIN VEREDICTO» más la presión de bytes e inodos. Sin huecos no imprime nada. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Delta semántico
Ninguna definición cambió: el cambio es de texto, documentación o datos.
1 archivos · +22 −0
- scripts/build-farm.sh +22 −0
diff --git a/scripts/build-farm.sh b/scripts/build-farm.sh
index 3e7ad811..8c0e8ca1 100755
--- a/scripts/build-farm.sh
+++ b/scripts/build-farm.sh
@@ -142,7 +142,29 @@ done
# ── Fase 2: PROMOTE + PUBLISH en serie (índice del repo = recurso mutable compartido).
ok=$(ls "$FARM"/status/*.ok 2>/dev/null | wc -l | tr -d ' ')
fail=$(ls "$FARM"/status/*.fail 2>/dev/null | wc -l | tr -d ' ')
+# ── ⚠ LO QUE NO DEJÓ VEREDICTO NO ES UN ÉXITO NI UN FALLO: ES UN HUECO, Y HAY QUE DECIRLO ────
+# `ok` y `fail` se cuentan LISTANDO ficheros en `status/`, y los escribe el worker con
+# `echo … > status/$n.ok|.fail`. Si la caja no puede escribir —disco lleno, inodos agotados— **no
+# se escribe NINGUNO de los dos**, así que la receta no aparece en ninguna cuenta: desaparece.
+#
+# Y entonces la línea de abajo MIENTE de la peor forma posible, que es sonando bien. Medido en el
+# worker el 2026-10-07: **«BUILD-YIELD: 37/57 construyen (0 fallan)»** con 63.000 `No space left on
+# device` en el journal y la granja sin sellar nada desde nueve días antes. Las 20 que no
+# construyeron no se contaron como fallo **porque el mecanismo que registra el fallo lo rompía la
+# misma causa que el fallo**. Un ✓ falso es peor que un ✗ falso: el ✗ falso manda a leer una receta
+# sana, el ✓ falso dice que la granja va bien durante nueve días.
+#
+# Arreglarlo no es escribir el fichero de otra manera —si no hay disco, no hay disco— sino **contar
+# lo que falta**: `n_total` es la cola ya filtrada, así que `n_total - ok - fail` es exactamente lo
+# que se evaporó. Y se imprime la presión de los dos medidores al lado, porque la causa casi siempre
+# es ésa y buscarla a mano es el rato que se perdió la otra vez.
+mudas=$(( n_total - ok - fail ))
echo "==> BUILD-YIELD: $ok/$n_total construyen ($fail fallan)"
+if [ "$mudas" -gt 0 ]; then
+ echo "==> ⚠ $mudas receta(s) SIN VEREDICTO: ni .ok ni .fail en $FARM/status/."
+ echo " eso NO es «0 fallan» — es que no se pudo registrar el resultado. Presión del disco:"
+ echo " bytes: $(df --output=pcent "$FARM" 2>/dev/null | tr -dc '0-9')% inodos: $(df --output=ipcent "$FARM" 2>/dev/null | tr -dc '0-9')%"
+fi
if [ "$PROMOTE" = 1 ] && [ "$ok" -gt 0 ]; then
echo "==> promoviendo + publicando $ok al repo firmado…"