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.
This commit is contained in:
+37
-20
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user