yupana: los 2 "bugs" de duplicados eran falsos positivos MÍOS — detector afinado, 0 reales
El usuario pidió arreglar itstool y la sombra de dbus. Apliqué el reflejo
(`yupana radio` + diff completo antes de tocar) y NINGUNO era un bug: ambos eran
patrones deliberados y documentados que mi detector, demasiado superficial, leyó
mal. Si los "arreglaba" a ciegas rompía dos builds.
· dbus: corpus/dbus (estático) e incoming-kde/dbus (dinámico) NO son redundantes
— qtbase linkea libdbus-1.so dinámicamente para Qt6DBus. El detector comparó
sólo versión+sha+deps, no el BUILD. Borrar la sombra rompía KDE.
· itstool: es un STUB documentado (`[source]=carrier`). El itstool real es
Python con libxml2-bindings ausentes en el lab; este genera un script inline y
sólo PRESTA el tarball de gettext-tiny. Apuntarlo al itstool real rompía
appstream (radio 5) con un source que ni compila acá.
Los 3 *-hello son el mismo patrón carrier; prison/prison-scanner una variante
cross-nombre (misma fuente, WITH_ZXING distinto).
FIX = el detector, no las recetas. Tres señales que le faltaban:
1. huella de BUILD en la firma de sombra (estático≠dinámico ⇒ no redundante).
2. CARRIER: ≤1 receta del grupo construye la fuente ⇒ el resto presta el tarball.
Señal: ¿invoca make/meson/cmake/ninja/cargo? (comentarios strippeados — la
prosa "invocación de meson" de un stub daba falso builder, lo cazó el guardián).
3. variante cross-nombre: ≥2 builders con BUILD distinto = deliberado, no bug.
Resultado: 0 colisiones reales (invariante c se cumple), 4 carrier + 14 variantes
+ 12 sombras justificadas, todas benignas.
Guardián extendido: ancla que el detector agrupa por FUENTE (itstool↔gettext-tiny)
y NO cría lobos (itstool=carrier, dbus=variante deliberada).
LECCIÓN: el reflejo del radio/diff antes de tocar evitó dos borrados destructivos
guiados por un falso positivo de mi propia herramienta. Medir antes de creer, aun
a la propia yupana.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,19 +38,31 @@ def main():
|
||||
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
|
||||
|
||||
# 3) INVARIANTE (c): el detector de duplicados debe seguir viendo la fuente, no el nombre. La
|
||||
# identidad de un nudo es lo que BAJA (sha), no cómo se llama. Si esto se rompe (p.ej. alguien
|
||||
# agrupa por nombre), las colisiones de fuente —shas copiados por error— dejan de verse.
|
||||
# 3) INVARIANTE (c): el detector debe AGRUPAR por fuente (sha), no por nombre — la identidad de un
|
||||
# nudo es lo que BAJA. Ancla: itstool y gettext-tiny comparten sha (itstool le presta el tarball
|
||||
# como carrier). Si el detector deja de ver ese grupo, está agrupando por nombre = invariante ciego.
|
||||
recs_f = yupana._recetas_con_fuente()
|
||||
por_sha = {}
|
||||
for k, r in recs_f.items():
|
||||
if r["sha"]:
|
||||
por_sha.setdefault(r["sha"], set()).add(k[1]) # nombres por sha
|
||||
colis = {s: ns for s, ns in por_sha.items()
|
||||
if len({yupana._base(n) for n in ns}) > 1} # familias distintas, misma fuente
|
||||
if not colis:
|
||||
fallos.append("el detector de duplicados no ve NINGUNA colisión de fuente — "
|
||||
"¿se está agrupando por nombre en vez de por sha? (invariante c ciego)")
|
||||
por_sha.setdefault(r["sha"], set()).add(k)
|
||||
sha_it = recs_f.get(("corpus", "itstool"), {}).get("sha")
|
||||
if not sha_it or ("corpus", "gettext-tiny") not in por_sha.get(sha_it, set()):
|
||||
fallos.append("el detector no agrupa itstool con gettext-tiny por sha — "
|
||||
"¿agrupa por nombre en vez de por fuente? (invariante c ciego)")
|
||||
|
||||
# 3b) NO crIar lobos: itstool NO es un bug — es un CARRIER (stub que presta el tarball, no
|
||||
# construye la fuente). Si el detector lo llama construye_fuente=True, lo reportaría como
|
||||
# colisión falsa (casi se "arregla" un stub apuntándolo al itstool real que no compila en el lab).
|
||||
if recs_f.get(("corpus", "itstool"), {}).get("construye_fuente"):
|
||||
fallos.append("itstool marcado como que CONSTRUYE su fuente — es un carrier/stub, "
|
||||
"el detector lo reportaría como colisión falsa")
|
||||
# 3c) dbus: corpus (estático) vs incoming-kde (dinámico, para Qt6DBus) — misma fuente, BUILD
|
||||
# distinto ⇒ variante deliberada, NO sombra redundante. Casi se borra load-bearing (2026-07-22).
|
||||
if ("corpus", "dbus") in recs_f and ("incoming-kde", "dbus") in recs_f:
|
||||
if recs_f[("corpus", "dbus")]["build_fp"] == recs_f[("incoming-kde", "dbus")]["build_fp"]:
|
||||
fallos.append("corpus/dbus e incoming-kde/dbus tienen build_fp IGUAL — el detector "
|
||||
"los marcaría redundantes y son variantes estático/dinámico deliberadas")
|
||||
|
||||
if fallos:
|
||||
print("✗ REGRESIÓN de yupana:")
|
||||
@@ -61,7 +73,8 @@ def main():
|
||||
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()))}")
|
||||
print(f"✓ detector de duplicados por fuente: {len(colis)} colisiones vistas (invariante c activo)")
|
||||
print(f"✓ detector agrupa por fuente (itstool↔gettext-tiny) y no cría lobos "
|
||||
f"(itstool=carrier, dbus=variante)")
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user