> 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…
drenaje: medía 5 de 7 imágenes y las otras 2 desaparecían en silencio
targets.toml declara 7 perfiles; drenaje.json listaba 5. escritorio-cosmic (cola
incoming-cosmic) y escritorio-sway (cola incoming-wlr) declaraban una cola que
ningún grafo de GRAFOS contenía, así que cargar() devolvía None y --todos hacía
`continue` pelado. Dos imágenes enteras fuera del artefacto sin dejar rastro, y
los dos números —5 medidos, 7 declarados— no se cruzaban en ningún lado.
Mismo olvido que ya costó 17 días de build-state-wlr.json congelado: la lista de
grafos crece a mano y se queda atrás cuando se abre una cola.
Tres cambios:
- GRAFOS suma los grafos de cosmic y wlr.
- Un perfil sin medir se anota en el artefacto (`sin_medir`), se grita por stdout
y drenar.py sale 1. Regla 3 del repo: un ausente falla ruidosamente.
- cosecha-cron deja de tragarse la salida; filtra las líneas ⚠ al log, que si no
llegaban como un "falló" mudo que no dice cuál imagen falta.
Medido: las 2 imágenes que nadie miraba estaban limpias, 0 en deuda. Ahora son
7/7 verificadas en vez de 5 verificadas y 2 supuestas.
diff --git a/docs/state/drenaje.json b/docs/state/drenaje.json
index 9426b856..f476c9a8 100644
--- a/docs/state/drenaje.json
+++ b/docs/state/drenaje.json
@@ -20,6 +20,15 @@
"construibles_ya": [],
"olas": []
},
+ "escritorio-cosmic": {
+ "grafo": "docs/state/build-state-cosmic.json",
+ "nodos": 134,
+ "en_deuda": 0,
+ "ondas": 0,
+ "profundidad": 0,
+ "construibles_ya": [],
+ "olas": []
+ },
"escritorio-gnome": {
"grafo": "docs/state/build-state-gnome.json",
"nodos": 158,
@@ -46,6 +55,16 @@
"profundidad": 0,
"construibles_ya": [],
"olas": []
+ },
+ "escritorio-sway": {
+ "grafo": "docs/state/build-state-wlr.json",
+ "nodos": 174,
+ "en_deuda": 0,
+ "ondas": 0,
+ "profundidad": 0,
+ "construibles_ya": [],
+ "olas": []
}
- }
+ },
+ "sin_medir": []
}
diff --git a/scripts/drenar.py b/scripts/drenar.py
index b4a2eb9f..1385a5ba 100755
--- a/scripts/drenar.py
+++ b/scripts/drenar.py
@@ -28,8 +28,15 @@ import json, sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
+# UNA LÍNEA POR GRAFO, Y SE OLVIDAN (2026-09-04). Esta lista nació con corpus+kde+gnome y se quedó
+# ahí mientras se abrían dos escritorios más: `escritorio-cosmic` (cola `incoming-cosmic`) y
+# `escritorio-sway` (cola `incoming-wlr`) declaraban una cola que ningún grafo de la lista contenía,
+# así que `cargar()` devolvía None y `--todos` los saltaba EN SILENCIO — 2 de las 7 imágenes fuera
+# del drenaje sin que nada lo dijera. Es el mismo olvido que ya costó 17 días de
+# build-state-wlr.json congelado (ver cosecha-cron.sh). Al abrir una cola nueva, se agrega ACÁ.
GRAFOS = ["docs/state/build-state.json", "docs/state/build-state-kde.json",
- "docs/state/build-state-gnome.json"]
+ "docs/state/build-state-gnome.json", "docs/state/build-state-cosmic.json",
+ "docs/state/build-state-wlr.json"]
SEMILLA = ROOT / "docs/state/seed-edges.json"
EN_DEUDA = ("debt", "never", "unhashable", "wanted")
@@ -109,9 +116,18 @@ def todos_los_perfiles():
doc = {"schema": "hammer-drenaje/1",
"nota": "DERIVADO del grafo de estado por scripts/drenar.py — no editar a mano.",
"perfiles": {}}
+ sin_medir = []
for p in sorted(targets_mod.load()):
ondas, deuda, ambito, cual = ondas_de(p)
+ # UN PERFIL QUE NO SE PUEDE MEDIR NO SE SALTA EN SILENCIO. Antes esto era un `continue` pelado
+ # y por eso dos imágenes enteras faltaban del artefacto sin dejar rastro: el JSON listaba 5
+ # perfiles, targets.toml declaraba 7, y los dos números no se cruzaban en ningún lado. Ahora
+ # queda escrito EN el artefacto (`sin_medir`) y gritado por stdout — regla 3 del repo: un
+ # ausente falla ruidosamente, un vacío llega hasta el final diciendo que todo fue bien.
if ondas is None:
+ sin_medir.append(p)
+ print(f" ⚠ {p:20} SIN MEDIR: ningún grafo de GRAFOS contiene su cola "
+ f"(¿falta build-state.py --<cola> o la línea en GRAFOS?)")
continue
doc["perfiles"][p] = dict(
grafo=cual, nodos=len(ambito), en_deuda=len(deuda), ondas=len(ondas),
@@ -119,9 +135,14 @@ def todos_los_perfiles():
olas=[{"n": i + 1, "recetas": o} for i, o in enumerate(ondas)])
print(f" {p:20} {len(deuda):3} en deuda · {len(ondas):2} ondas · "
f"onda 1 = {len(ondas[0]) if ondas else 0}")
+ doc["sin_medir"] = sin_medir
salida = ROOT / "docs/state/drenaje.json"
salida.write_text(json.dumps(doc, indent=1, ensure_ascii=False) + "\n")
print(f" → {salida.relative_to(ROOT)}")
+ if sin_medir:
+ print(f" ⚠ {len(sin_medir)} de {len(targets_mod.load())} imágenes SIN MEDIR: "
+ f"{' '.join(sin_medir)}")
+ return 1
return 0
diff --git a/scripts/farm/cosecha-cron.sh b/scripts/farm/cosecha-cron.sh
index c3b0f935..716d421a 100755
--- a/scripts/farm/cosecha-cron.sh
+++ b/scripts/farm/cosecha-cron.sh
@@ -202,7 +202,15 @@ if [ -x "$HAMMER" ]; then
# Cola DERIVADA del grafo (P4): el orden de masticado por imagen, en ondas topológicas. No lanza
# ningún build — sólo deja escrito qué conviene moler primero, para que la cola deje de ser una
# lista escrita a mano. Barato (lee JSON, no hashea).
- scripts/drenar.py --todos >/dev/null 2>&1 && echo " drenaje.json ✓" || echo " ⚠ drenar.py falló"
+ # NO se traga la salida entera: drenar.py sale 1 cuando alguna imagen quedó SIN MEDIR (una cola
+ # nueva sin su línea en GRAFOS), y con `>/dev/null 2>&1` eso llegaba al log como un "falló" mudo
+ # que no dice cuál. Se filtran las líneas ⚠, que son las únicas accionables.
+ if drenaje_out="$(scripts/drenar.py --todos 2>&1)"; then
+ echo " drenaje.json ✓"
+ else
+ echo " ⚠ drenar.py falló"
+ echo "$drenaje_out" | grep '⚠' | sed 's/^/ /'
+ fi
# VIGÍA DE FUENTES (ADR 0013 §3). Desde que el mirror propio se consulta ANTES que upstream,
# `hammer build` deja de avisar cuando una URL de terceros muere: sirve los bytes del mirror y
# sigue. Eso es lo correcto para construir y sería CIEGO sin este contrapeso — las URLs se irían