estado: orden de ataque por impacto de desbloqueo (lectura accionable del DAG)
El grafo ahora computa, sobre las dependencias, dos campos por receta: blocked_by — deps que están en deuda (lo que impide construirla ya) unblocks — cuántas recetas EN DEUDA la declaran como dep (su impacto de desbloqueo) Una receta en deuda sin blocked_by es construible YA; ordenadas por unblocks desc, dan el orden que libera el grafo más rápido. La vista muestra la pastilla ↑N en las filas listas y ordena por ahí. Top de impacto (todo el stack GUI concentrado, más zstd/perl de C-base): glib ↑14 wayland ↑13 pixman ↑12 libdrm ↑11 fontconfig ↑11 zstd ↑10 libxkbcommon ↑9 perl ↑8 Es la guía para cuando se levante la granja: construir esas primero desbloquea el grueso. De paso, diagnóstico de las 8 'never' (ninguna es cruft ni bug): dwarves BLOQUEADA-documentada (necesita libdw, elfutils da sólo libelf a propósito — sub-proyecto elfutils-libdw); llimphi-counter es un EJEMPLO/plantilla intencional; las otras 6 son imports Go/Rust que van al worker. Y confirmado parseando: 0 recetas con FIXME real en el campo sha256 (el grep decía 43, todas comentarios) — otra vez grep miente, el grafo (parsea) dice la verdad. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+19
-11
@@ -25,16 +25,18 @@ STATE_ES = {"sealed": "sellado al día", "debt": "deuda de rebuild",
|
||||
"never": "nunca construida", "unhashable": "ilegible"}
|
||||
CLASS_ES = {"c": "C", "go": "Go", "rust": "Rust", "gui": "GUI", "kernel": "kernel"}
|
||||
|
||||
# Para cada nodo, sus deps que están en deuda (lo que lo bloquea) — el sub-grafo accionable.
|
||||
# blocked_by / unblocks los computa build-state.py sobre el grafo; acá sólo los leemos.
|
||||
def blockers(name):
|
||||
r = nodes[name]
|
||||
return [d for d in r.get("deps", []) if d in nodes and nodes[d]["state"] in ("debt", "never", "unhashable")]
|
||||
return nodes[name].get("blocked_by", [])
|
||||
def unblocks(name):
|
||||
return nodes[name].get("unblocks", 0)
|
||||
|
||||
debt = {n: r for n, r in nodes.items() if r["state"] in ("debt", "never", "unhashable")}
|
||||
# Orden: primero las "listas" (sin bloqueadores), luego por nº de bloqueadores, luego alfabético.
|
||||
# Orden de ATAQUE: primero las "listas" (sin bloqueadores), y entre ellas la que DESBLOQUEA más
|
||||
# arriba; luego las bloqueadas por menos, luego alfabético. Es el orden que libera el grafo más rápido.
|
||||
def debt_key(n):
|
||||
b = blockers(n)
|
||||
return (len(b), nodes[n]["cls"], n)
|
||||
return (len(b), -unblocks(n), nodes[n]["cls"], n)
|
||||
debt_sorted = sorted(debt, key=debt_key)
|
||||
ready = [n for n in debt_sorted if not blockers(n)]
|
||||
|
||||
@@ -92,13 +94,16 @@ for c in sorted(classes, key=lambda c: -sum(classes[c].values())):
|
||||
|
||||
# ---- filas de deuda (agrupadas visualmente por "listas" vs "bloqueadas") ----
|
||||
def debt_row(n):
|
||||
r = nodes[n]; b = blockers(n)
|
||||
r = nodes[n]; b = blockers(n); u = unblocks(n)
|
||||
deps = r.get("deps", [])
|
||||
chips = "".join(chip(d) for d in deps) if deps else '<span class="chip empty">sin deps de build</span>'
|
||||
badge = ('<span class="ready">lista</span>' if not b
|
||||
else f'<span class="blocked">espera {len(b)}</span>')
|
||||
if not b:
|
||||
impact = f' <span class="impact" title="desbloquea {u} recetas en deuda">↑{u}</span>' if u else ""
|
||||
badge = f'<span class="ready">lista</span>{impact}'
|
||||
else:
|
||||
badge = f'<span class="blocked">espera {len(b)}</span>'
|
||||
ver = f'<span class="ver">{E(str(r.get("version") or ""))}</span>' if r.get("version") else ""
|
||||
return f'''<tr class="drow" data-cls="{r['cls']}" data-ready="{'1' if not b else '0'}" data-name="{E(n)}">
|
||||
return f'''<tr class="drow" data-cls="{r['cls']}" data-ready="{'1' if not b else '0'}" data-name="{E(n)}" data-unblocks="{u}">
|
||||
<td class="c-name"><span class="s-dot s-{r['state']}"></span>{E(n)} {ver}</td>
|
||||
<td class="c-cls"><span class="tag t-{r['cls']}">{E(CLASS_ES.get(r['cls'], r['cls']))}</span></td>
|
||||
<td class="c-badge">{badge}</td>
|
||||
@@ -205,6 +210,8 @@ td{padding:10px 14px;border-bottom:1px solid var(--line);vertical-align:top}
|
||||
.t-rust{background:#dd6b3d22;color:var(--ember)} .t-gui{background:#8f623422;color:var(--never)}
|
||||
.t-kernel{background:#bb443c22;color:var(--unhashable)}
|
||||
.ready{color:var(--sealed);font-weight:600;font-size:.8rem}
|
||||
.impact{display:inline-block;background:var(--ember);color:#fff;font-family:ui-monospace,monospace;
|
||||
font-size:.72rem;font-weight:650;padding:1px 6px;border-radius:999px;margin-left:4px}
|
||||
.blocked{color:var(--debt);font-weight:600;font-size:.8rem;font-family:ui-monospace,monospace}
|
||||
.c-deps{max-width:560px}
|
||||
.chip{display:inline-block;font-family:ui-monospace,monospace;font-size:.74rem;padding:2px 7px;
|
||||
@@ -281,8 +288,9 @@ HTML = f'''<style>{CSS}</style>
|
||||
<p class="eyebrow">huecos, en orden de ataque</p>
|
||||
<p class="hint">Cada receta en deuda con sus deps de build coloreadas por estado. Una fila
|
||||
<b style="color:var(--sealed)">lista</b> tiene todas sus deps al día ⇒ se puede construir ya;
|
||||
una <b style="color:var(--debt)">espera N</b> depende de N recetas que también están en deuda.
|
||||
{len(ready)} listas ahora.</p>
|
||||
el pastilla <span class="impact">↑N</span> es cuántas otras en deuda desbloquea (ordenadas por
|
||||
ahí: construí primero las de más impacto). Una <b style="color:var(--debt)">espera N</b> depende
|
||||
de N recetas que también están en deuda. {len(ready)} listas ahora.</p>
|
||||
<div class="controls">
|
||||
{cls_btns}
|
||||
<button class="fbtn" id="ready" data-ready aria-pressed="false">sólo listas</button>
|
||||
|
||||
@@ -126,6 +126,18 @@ def main():
|
||||
|
||||
order = toposort(recs)
|
||||
|
||||
# Lectura ACCIONABLE del grafo topológico: para cada receta, qué deps la BLOQUEAN (están en
|
||||
# deuda) y a cuántas otras en deuda DESBLOQUEA (la tienen como dep). Una receta en deuda sin
|
||||
# bloqueadores es construible YA; ordenar las listas por `unblocks` da el orden que libera más.
|
||||
DEBT_STATES = ("debt", "never", "unhashable")
|
||||
for n, r in recs.items():
|
||||
r["blocked_by"] = [d for d in r.get("deps", [])
|
||||
if d in recs and recs[d]["state"] in DEBT_STATES]
|
||||
for n, r in recs.items():
|
||||
# cuántas recetas EN DEUDA me declaran como dep directa (mi impacto de desbloqueo).
|
||||
r["unblocks"] = sum(1 for m, rm in recs.items()
|
||||
if rm["state"] in DEBT_STATES and n in rm.get("deps", []))
|
||||
|
||||
from collections import Counter
|
||||
by_state = Counter(r["state"] for r in recs.values())
|
||||
by_cls = Counter(r["cls"] for r in recs.values() if "cls" in r)
|
||||
|
||||
Reference in New Issue
Block a user