gioser

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 188Rust 55Shell 30Markdown 281211 Definiciones

yupana: el khipu se descubre, no se escribe a mano — cosmic y wlr eran invisibles

Dos sitios llevaban la lista de grafos codificada como `(kde, gnome, corpus)` y **omitían
`build-state-cosmic.json` y `build-state-wlr.json`**, que son dos de los cuatro escritorios. Es
exactamente la forma del fallo que motivó este fichero y que su cabecera describe: medir contra un
subconjunto de las colas y que el número salga chico sin que nada avise.

Medido: `_estado()` devolvía `?` para los 36 nudos propios de cosmic y para 12 de los 14 de wlr.

Pero el daño peor no era el `?`. `_estado()` elegía el fichero por la cola —gnome si
`incoming-gnome`, corpus si `corpus`, **KDE para todo lo demás**— así que un nudo de `incoming-wlr`
se buscaba en el grafo de KDE. `gdk-pixbuf` existe en `corpus` Y en `incoming-wlr` con recetas y
hashes distintos, y la consulta por el de wlr devolvía el estado del de corpus, con toda confianza.
Coincidía en `sealed`, así que nunca se notó. Un `?` se ve; una respuesta de la receta equivocada,
no.

Ahora `_khipus()` los descubre por glob (un grafo nuevo entra solo; ninguno se puede olvidar) y
`_estados_por_cola()` indexa por **(cola, nombre)**, que es la clave real: el mismo nombre vive en
varias colas siendo recetas distintas. Si la cola no tiene ese nudo, la respuesta es `?` — que es la
verdad, y no el estado de su homónimo.

`keystones` compartía el mismo índice por nombre suelto: pasa a `(cola, nombre)` en las tres
consultas.

Los tres invariantes de `test-yupana-radio.py` siguen verdes, y las 50 discrepancias de cosmic+wlr
bajan a 0.

748b2f604c601133349062cbf8f6baefb9a5dfaf · por Sergio · (hace 1 mes) · padre 66db079b6 · ver el árbol en este commit

Delta semántico · 4

scripts/yupana.py

1 archivos · +37 −20

diff --git a/scripts/yupana.py b/scripts/yupana.py
index 5804477e..0e957079 100755
--- a/scripts/yupana.py
+++ b/scripts/yupana.py
@@ -358,6 +358,37 @@ def _dominador_retencion(colas, perfiles):
     return ret
 
 
+def _khipus():
+    """TODOS los grafos de estado del disco, descubiertos y no escritos a mano.
+
+    La lista estaba codificada como `(kde, gnome, corpus)` en dos sitios y **omitía cosmic y wlr**,
+    que son dos de los cuatro escritorios. Es la misma forma del fallo que motivó este fichero (ver
+    la cabecera): medir contra un subconjunto de las colas y que el número salga chico sin que nada
+    avise. Un grafo nuevo entra solo; ninguno se puede olvidar.
+    """
+    return sorted(glob.glob(str(ROOT / "docs/state/build-state*.json")))
+
+
+def _estados_por_cola():
+    """`{(cola, nombre): estado}` sobre todos los khipus.
+
+    Va por (COLA, nombre) y no por nombre: el mismo nombre vive en varias colas con recetas y hashes
+    DISTINTOS —`gdk-pixbuf` está en corpus, en incoming-kde y en incoming-wlr— así que un índice por
+    nombre devuelve el estado de otra receta con toda confianza. Eso es peor que no saber.
+    """
+    estados = {}
+    for f in _khipus():
+        try:
+            nodos = json.loads(Path(f).read_text())["nodes"]
+        except Exception:
+            continue
+        for n, r in nodos.items():
+            q = r.get("queue")
+            if q:
+                estados.setdefault((q, n), r.get("state", "?"))
+    return estados
+
+
 def keystones(como_json=False):
     """Los nudos que GATEAN más trabajo. Métrica accionable: cierre inverso transitivo restringido a
     la DEUDA (cuánto bloqueado libero al sellar esto). Segunda columna: RETENCIÓN por dominadores (lo
@@ -366,23 +397,17 @@ def keystones(como_json=False):
     colas = cargar_colas()
     rev = dependientes(colas)
     perfiles = targets_mod.load()
-    # estados desde el khipu más completo
-    estados = {}
-    for f in ("docs/state/build-state-kde.json", "docs/state/build-state-gnome.json",
-              "docs/state/build-state.json"):
-        p = ROOT / f
-        if p.exists():
-            for n, r in json.loads(p.read_text())["nodes"].items():
-                estados.setdefault(n, r.get("state", "?"))
+    # estados desde TODOS los khipus, indexados por (cola, nombre)
+    estados = _estados_por_cola()
     DEUDA = ("debt", "never", "unhashable", "wanted")
     ret = _dominador_retencion(colas, perfiles)
 
     filas = []
     for nodo in list(rev):
         cerr = _cierre_inverso(nodo, rev)
-        en_deuda = sum(1 for (q, n) in cerr if estados.get(n) in DEUDA)
+        en_deuda = sum(1 for nd in cerr if estados.get(nd) in DEUDA)
         q, n = nodo
-        yo_deuda = estados.get(n) in DEUDA
+        yo_deuda = estados.get(nodo) in DEUDA
         filas.append(dict(nudo=f"{q}/{n}", gateo_total=len(cerr), gateo_en_deuda=en_deuda,
                           exclusivo=ret.get(nodo, 0), en_deuda=yo_deuda))
     # KEYSTONE ACCIONABLE: un nodo SELLADO ya está disponible ⇒ construirlo no libera nada (make gatea
@@ -397,7 +422,7 @@ def keystones(como_json=False):
     # camino crítico = nº de pasos (lo que ya daban las ondas). Con datos, es la ETA real en segundos.
     tiempos = _tiempos()
     idx = {q: set(m) for q, m in colas.items()}
-    deuda_set = {(q, n) for q in colas for n in colas[q] if estados.get(n) in DEUDA}
+    deuda_set = {(q, n) for q in colas for n in colas[q] if estados.get((q, n)) in DEUDA}
     deps_debt = {}
     for (q, n) in deuda_set:
         dd = set()
@@ -640,15 +665,7 @@ def membresia(colas=None, perfiles=None):
 
 def _estado(nodo):
     """Estado del nudo desde el grafo firme, si está regenerado. Best-effort: el radio funciona sin él."""
-    q, nombre = nodo
-    fichero = ("docs/state/build-state-gnome.json" if q == "incoming-gnome"
-               else "docs/state/build-state-kde.json" if q != "corpus"
-               else "docs/state/build-state.json")
-    try:
-        d = json.loads((ROOT / fichero).read_text())
-        return d["nodes"].get(nombre, {}).get("state", "?")
-    except Exception:
-        return "?"
+    return _estados_por_cola().get(nodo, "?")
 
 
 def radio(nombre, como_json=False):