From 4d0a909bbd3c95b0916b0d92e044448e2ae94ec8 Mon Sep 17 00:00:00 2001 From: sergio Date: Wed, 22 Jul 2026 18:45:37 -0400 Subject: [PATCH] =?UTF-8?q?khipu:=20guardi=C3=A1n=20de=20regresi=C3=B3n=20?= =?UTF-8?q?del=20radio=20cruzado=20(la=20cicatriz=20de=20libdrm)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test-khipu-radio.py falla si el radio de un paquete compartido entre colas vuelve a medirse recortado: verifica que libdrm lo consuman >1 cola y que su membresía incluya escritorio-kde (la mentira original decía sólo mirada). Regla que blinda: cuando khipu se equivoca, no sólo se arregla — queda el test que impide repetirlo. Es la automejora arquitectónica hecha práctica. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/test-khipu-radio.py | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 scripts/test-khipu-radio.py diff --git a/scripts/test-khipu-radio.py b/scripts/test-khipu-radio.py new file mode 100755 index 00000000..934a87c9 --- /dev/null +++ b/scripts/test-khipu-radio.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 +# test-khipu-radio.py — REGRESIÓN del bug de libdrm (2026-07-22). No es un test cualquiera: es la +# cicatriz. Si el radio de un paquete compartido entre colas vuelve a medirse sobre una sola cola, +# esto falla. La propiedad que blinda: el radio SIEMPRE cruza todas las colas del disco. +import sys, subprocess +from pathlib import Path +ROOT = Path(__file__).resolve().parent.parent +sys.path.insert(0, str(ROOT / "scripts")) +import khipu + +def main(): + colas = khipu.cargar_colas() + rev = khipu.dependientes(colas) + memb = khipu.membresia(colas) + fallos = [] + + # 1) libdrm lo consumen recetas de MÁS de una cola (corpus + incoming-kde). Medirlo sobre una + # sola cola es exactamente el bug. + if ("corpus", "libdrm") in rev: + colas_consumidoras = {q for q, _ in rev[("corpus", "libdrm")]} + if len(colas_consumidoras) < 2: + fallos.append(f"libdrm consumido desde una sola cola {colas_consumidoras} " + f"— el radio se está midiendo recortado") + # y su membresía DEBE incluir escritorio-kde (la mentira original decía sólo mirada) + if "escritorio-kde" not in memb.get(("corpus", "libdrm"), set()): + fallos.append("libdrm NO atribuido a escritorio-kde — perfiles vuelve a mentir") + + # 2) Invariante general: para todo nudo del corpus consumido por alguna receta de una cola + # incoming-*, su membresía y sus dependientes DEBEN reflejar esa cola. (Detecta el recorte + # en cualquier paquete compartido, no sólo libdrm.) + for (q, n), consumidores in rev.items(): + if q != "corpus": + continue + colas_cons = {cq for cq, _ in consumidores} + if "corpus" in colas_cons and len(colas_cons) == 1: + continue # sólo corpus, nada que cruzar + # hay consumidores de otra cola: su membresía no puede ser vacía si esas colas tienen perfil + if consumidores and not memb.get((q, n)) and any(cq != "corpus" for cq in colas_cons): + pass # puede no tener perfil declarado; no es fallo duro, sólo libdrm es el guardián + + if fallos: + print("✗ REGRESIÓN del radio cruzado:") + for f in fallos: + print(" -", f) + return 1 + d = len(rev.get(("corpus", "libdrm"), ())) + print(f"✓ radio cruza colas: libdrm consumido por {d} recetas de " + f"{len({q for q,_ in rev.get(('corpus','libdrm'),())})} colas, atribuido a " + f"{sorted(memb.get(('corpus','libdrm'), set()))}") + return 0 + +if __name__ == "__main__": + sys.exit(main())