> 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…
harkaq: el clasificador nunca propone runtime base como declarable (fix de raíz de busybox)
El bug que declaró `busybox` en 29 recetas y rompió binutils: harkaq-suggest veía
`/bin/busybox` denegado, encontraba `recipes/busybox.toml` en el store y decía
"declarar dep: busybox" — sin mirar que la política YA lo concede (`ro /bin/busybox`:
el sandbox corre `sh -c` y /bin/sh→busybox ⇒ es CONTRATO, no dep).
Fix: harkaq-suggest ahora lee los paths CONCEDIDOS de la política (`ro`/`rw`/`list`),
no sólo los `# expect`. Si un path está concedido ⇒ categoría RUNTIME BASE: no se
declara, aunque el store lo provea.
Las 4 clasificaciones verificadas tras el cambio:
/bin/busybox → RUNTIME BASE (ya concedido; NO declarar) ← el fix
/usr/bin/make → DECLARABLE (declarar dep: make)
/usr/lib/libstdc++.so → IRREDUCIBLE (nadie lo provee)
/usr/bin/gcc en broot → DEUDA DE COMPILADOR (compiler=gcc)
/usr/bin/gcc en zlib → sin deuda (sonda esperada; zlib compila con zig)
Con esto puesto, busybox JAMÁS se habría declarado. Cierra el TODO que dejó el
revert: la campaña automática ya no puede cimentar contrato como dependencia.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
diff --git a/scripts/harkaq/harkaq-suggest.py b/scripts/harkaq/harkaq-suggest.py
index 98af5226..937c3a42 100755
--- a/scripts/harkaq/harkaq-suggest.py
+++ b/scripts/harkaq/harkaq-suggest.py
@@ -101,11 +101,22 @@ def main():
usa_gcc = receta_toml and compiler_de(receta_toml) == "gcc"
esperadas = set()
+ # RUNTIME BASE: lo que la política ya CONCEDE (`ro`/`rw`/`list`). Si un path está acá, NO es
+ # deuda declarable aunque el store lo provea — es contrato del sandbox.
+ #
+ # Este bug costó caro: `/bin/busybox` está concedido (el sandbox corre `sh -c` y /bin/sh→
+ # busybox), pero como `recipes/busybox.toml` existe, el clasificador propuso "declarar dep:
+ # busybox" y la cosecha lo declaró en 29 recetas. Declararlo apila el busybox de hammer sobre
+ # el de Alpine y ROMPE el build (binutils: "cannot run C compiled programs"). Además va contra
+ # la Etapa C, que reemplaza busybox por uutils. Medir bien y accionar mal.
+ concedidos = set()
with open(politica) as f:
for l in f:
l = l.strip()
if l.startswith("# expect "):
esperadas.add(l[len("# expect "):].strip())
+ elif l[:3] in ("ro ", "rw ") or l.startswith("list "):
+ concedidos.add(l.split(None, 1)[1].strip())
# Si la receta compila con gcc, los frontends NO se perdonan: son la deuda de compilador.
if usa_gcc:
esperadas -= FRONTENDS_GCC
@@ -117,11 +128,15 @@ def main():
idx = indice_del_store(store)
recetas = catalogo(recipes_dir)
- declarable, irreducible, compilador = {}, [], []
+ declarable, irreducible, compilador, base = {}, [], [], []
for p in deuda:
if usa_gcc and p in FRONTENDS_GCC:
compilador.append(p)
continue
+ # ¿Ya concedido por la política? ⇒ runtime base, NO se declara (ver arriba: busybox).
+ if p in concedidos or any(p.startswith(c.rstrip("/") + "/") for c in concedidos if c != "/"):
+ base.append(p)
+ continue
provs = idx.get(p)
if provs:
# El store es autoritativo: sabe qué ficheros aporta cada artefacto.
@@ -144,6 +159,10 @@ def main():
else:
irreducible.append(p)
+ if base:
+ print("RUNTIME BASE — ya concedido por la política; NO declarar (es contrato del sandbox):")
+ for p in base:
+ print(f" {p}")
if compilador:
print("DEUDA DE COMPILADOR (matar-gcc) — la receta usa el gcc de Alpine (compiler=gcc):")
for p in compilador: