Commit Graph
10 Commits
Author SHA1 Message Date
Sergio 02facebe28 deps.runtime: que ALGUIEN las lea — de 29 huecos de soname a 6
Tres arreglos que son el mismo: un campo que nadie lee es un campo que miente.

1. yupana._deps() leía SÓLO `deps.build`. Como yupana es la base de todas las
   herramientas de grafo, una dep de EJECUCIÓN no existía para ninguna: ni el
   vigía de sonames, ni la membresía de perfiles, ni el rootfs hidratado.
   Medido: firefox declaraba `runtime = ["gcc-libs"]` y el cierre de las cuatro
   imágenes seguía sin libstdc++.so.6, así que el navegador no arrancaba y el
   vigía lo seguía reportando como hueco DESPUÉS de haberlo arreglado.
   `deps.runtime` está en el esquema de hammer desde siempre. La unión es la
   definición de cierre: para CORRER hacen falta las dos.

2. build-state.py, lo mismo y por lo mismo.

3. El vigía entra en el LATIDO y deja docs/state/sonames.txt. Existía desde
   antes y contesta la pregunta que el grafo no contesta —no «¿está sellado?»
   sino «¿arranca?»— pero NADIE LO CORRÍA: no estaba en cosecha-cron y no dejaba
   fichero de estado. Por eso libstdc++.so.6, que rompía el navegador en los
   CUATRO perfiles, estuvo en su salida sin que nadie lo leyera, y se
   redescubrió arrancando atuq a mano. Un vigía que hay que acordarse de invocar
   no se distingue de no tenerlo.

Y se BORRA scripts/audit-needed.sh, que escribí ayer sin ver que vigia-sonames.py
ya hacía exactamente esto, con la misma frase en la cabecera. Dos herramientas
que miden lo mismo divergen y la que nadie mira es la que miente; la que se
queda es la que ya existía, que además reporta POR PERFIL y encontró más cosas.

python3 declara sus cuatro deps de ejecución (readline/sqlite/lzma/bz2): son
módulos de la stdlib que se cargan por dlopen, así que no rompen el arranque
sino un `import` — un fallo que aparece lejos y no menciona a python.

Resultado, con todo aplicado: 29 huecos -> 6, y los que quedan son otra clase.
`libperl.so` es empaquetado de la receta perl; `libc.so.6` lo pide el `go`
prebuilt y es un soname de GLIBC en una distro musl, que es un síntoma distinto.
Los dos quedan anotados en docs/state/sonames.txt, que ahora se regenera solo.
2026-09-06 22:28:17 +00:00
Sergio 748b2f604c 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.
2026-09-05 16:31:31 +00:00
Sergio 9834f57052 yupana: --help imprimía literalmente None
Todo el encabezado del fichero era comentario `#`, así que __doc__ era None y
`main()` hace `print(__doc__)` en dos caminos: `--help` y verbo desconocido. La
puerta única de la metodología no sabía explicarse, y el error de verbo tampoco
decía cuáles son los verbos válidos.

La ayuda operativa pasa a docstring; el diseño y el porqué se quedan arriba como
comentario. Cotejada contra el despacho real: los 11 verbos de la ayuda existen y
los 11 que existen están en la ayuda (faltaba `sembrar`).
2026-09-04 14:27:34 +00:00
sergioandClaude Opus 4.8 d33fc94fc5 gnome: enciende la CAPA GRAFO del yupana-gnome
build-state.py gana --gnome (análogo a --kde): carga recipes/incoming-gnome/ y sale
a build-state-gnome.json. drenar/yupana/seed-graph aprenden ese grafo (keystones,
drenar --perfil escritorio-gnome, radio de nodos incoming-gnome, frontera). Hoy la
cola está vacía ⇒ el grafo trae 0 nodos gnome; se poblará al aterrizar recetas y ahí
drenar/keystones reckonan solos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 16:05:40 -04:00
sergioandClaude Opus 4.8 8c10c0a908 yupana: modelo EXTRA-GRAFO — preflight aprende los patrones de hueco de entorno
El grafo modela deps (R1: Receta×Receta). El entorno es OTRA capa: R2 (Receta×
Capacidad, qué exige un build) y R3 (Host×Capacidad, qué ofrece la máquina).
Construible = clausura(R1) ∧ R2⊆R3. Esos huecos viven FUERA del grafo (infra del
sandbox, toolchain, layout de fs) y una matemática de grafos sola no los ve —
pero SÍ se aprenden, como patrones con predictor, calibrados contra fallos reales.

`yupana preflight <receta>` corre los patrones ANTES de construir. Primer patrón:
overlay-lowerdir, la cicatriz de kio. Habría marcado kio antes de quemar la
campaña descubriéndolo a los golpes:

  kio: 52 deps ⇒ lowerdir ~5346B > 4096B (una página) ⇒ depende del merge

CALIBRADO, no adivinado: el estimador daba corto (2974B "ok") hasta usar el
prefijo que bwrap VE en runtime (/oldroot/…, ruta del worker, no la del hub).
Con eso: kio estimado 5346B vs real 5315B — 31B de error, sin fudge. El primer
predictor "obvio" mentía con confianza; la lección de siempre.

preflight --perfil escritorio-kde destapa lo sistémico: las 40 recetas KDE
profundas CUELGAN del merge (plasma-desktop: 114 deps → 11708B, casi 3× el
límite). Todo el escritorio dependía del fix 6a0dc40. El pre-flight vuelve
VISIBLE y contable una fragilidad que antes sólo se manifestaba fallando.

Registro extensible (PATRONES_ENTORNO): futuros = capacidad-de-host (rootfs
laptop≠worker, techo MSRV — acumular observaciones), skew de toolchain (zig-skew
— fingerprint por artefacto). harkaq ya MIDE R2 (evidencia negativa); esto lo
vuelve consultable + pre-vuela. Guardián: kio marcado + estimador calibrado.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 21:42:42 -04:00
sergioandClaude Opus 4.8 85b5a9bc46 yupana: instrumentar duración de build en el worker → camino crítico PESADO
El peso que le faltaba a las ondas: sin él, critical-path = nº de pasos; con él,
ETA real en segundos.

INSTRUMENTACIÓN (worker):
  scripts/farm/build-timed.sh envuelve `hammer build` midiendo la pared y la
  registra en $STORE/.times/<hash>-<host>.json — un SIDECAR del store, NUNCA
  dentro del artefacto. La duración es no determinista (varía por máquina/carga)
  ⇒ no puede tocar el ArtifactHash. Keyed por hash (CAS-safe: dos workers no se
  pisan) + host (varias muestras por receta). Viaja al hub con el rsync de store
  que ya hace la cosecha; ninguna herramienta del store lo confunde con artefacto.
  Sólo builds REALES (pared ≥ UMBRAL 3s) — los cache-hits no envenenan la mediana.
  campana-deuda.sh ahora construye vía build-timed.sh (transparente, mismo exit).

CONSUMO (hub): yupana._tiempos() carga name→mediana de segundos; keystones
computa el CAMINO CRÍTICO pesado = longest weighted path del subgrafo de deuda
(peso = segundos de build). La cadena más larga hay que construirla en SERIE
aunque haya ∞ workers ⇒ es la ETA con paralelismo infinito. Degrada con gracia:
sin datos, peso=1 y el camino crítico = nº de pasos (lo que ya daban las ondas).

Verificado: con muestras sintéticas da "ETA 19 min sobre 5 pasos, kio → kparts →
frameworkintegration → breeze → plasma-integration"; sin datos degrada a "5 pasos
(SIN datos)". store/.times está git-ignorado (metadata de máquina, no se commitea).

LÍMITE honesto (en la cabecera de build-timed.sh): `hammer build` arrastra deps ⇒
la pared incluye deps no selladas. En orden topológico (drenar) las deps ya están
selladas y la pared mide sobre todo ESTA receta — cota superior buena para pesar.
La precisión exacta pediría instrumentar el sellado dentro de hammer-build (Rust).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 20:25:09 -04:00
sergioandClaude Opus 4.8 4f609080df 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>
2026-07-22 20:14:10 -04:00
sergioandClaude Opus 4.8 619a30187c yupana: keystones — el camino crítico real, con la matemática correcta (AND, no OR)
`yupana keystones` rankea los nudos EN DEUDA por cuánto trabajo BLOQUEADO libera
su sellado (cierre inverso transitivo restringido a la deuda). Sellá de arriba
hacia abajo y la cascada cae lo antes posible.

CORRIGE UN ERROR MÍO: dije "árbol de dominadores = keystones". Lo medí y es
FALSO. El dominador ingenuo asume alcanzabilidad OR (basta un camino), pero
construir es AND (hacen falta TODAS las deps): kcoreaddons depende de dbus/libdrm
/mesa además de qtbase ⇒ hay un camino de desbloqueo que evita qtbase y el
dominador-OR lo pierde. La métrica correcta bajo AND es el cierre inverso
transitivo. Y el crudo lo gana el toolchain (make gatea 313, inútil) ⇒ el
keystone accionable es el cierre restringido a la deuda, y sólo cuenta si el
nudo mismo está en deuda (un sellado ya está disponible, no gatea nada).

Estado actual (qtbase ya sellado, era EL keystone con 117 gateados):
   kio        36   ← sellarlo libera 36 recetas KDE bloqueadas
   kparts     19
   kcmutils   17
   ksvg       14
   libplasma  13
Ése es el camino crítico ahora.

El dominador SÍ entra, en su uso legítimo: la columna `exclusivo` = retención
estilo GC (nodos que sólo dependen de mí). kwin=9 (sus plugins privados),
libksysguard=3. Responde "si suelto esta feature, cuánto más puedo soltar".

Sin datos nuevos (no necesita duración de build). Cuando instrumentemos tiempos,
el mismo cierre se vuelve camino crítico PESADO (ETA real).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 20:03:56 -04:00
sergioandClaude Opus 4.8 0f3a1dc394 yupana: invariante (c) CANÓNICO — detector de duplicados por evidencia de fuente
`yupana duplicados` caza nudos que son el MISMO paquete bajo nombres/colas
distintos, agrupando por lo que BAJA (sha/url), no por el nombre. Clasifica:

  colisión-fuente — mismo sha, familias de nombre DISTINTAS ⇒ casi seguro un
                    sha copiado por error. ACCIONABLE.
  variante        — mismo sha, misma familia (zlib/zlib-shared, mesa-*) ⇒
                    diversificación deliberada estático/shared, OK (14 vistas).
  sombra          — mismo nombre en >1 cola; redundante si versión+sha+deps
                    coinciden (una sobra), justificada si difieren.

HALLAZGO INMEDIATO — el detector encontró un bug real en su primera corrida:
  itstool.toml dice versión 2.0.7 pero su url+sha apuntan al tarball de
  gettext-tiny (29cc165e…). Construiría la fuente equivocada. radio itstool=1,
  gettext-tiny=78 ⇒ el sha copiado es el de itstool.
Otras 3 colisiones son demos (adwaita-hello/libadwaita, sourceview-hello/
gtksourceview, radio 0) o subcomponentes (prison/prison-scanner) — a revisar,
no urgentes. Y 1 sombra redundante: incoming-kde/dbus es byte-idéntica a
corpus/dbus (mismo hash de artefacto) ⇒ los 114 consumidores KDE podrían
apuntar a corpus/dbus sin rebuild.

NO funde nada solo: reporta a docs/state/duplicados.json y deja el juicio al
humano (como el triaje). El radio de cada nudo dice cuál es el canónico (el de
mayor radio). Guardián extendido: test-yupana-radio.py falla si el detector
deja de ver colisiones de fuente (agrupar por nombre en vez de sha = invariante
c ciego).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:59:20 -04:00
sergioandClaude Opus 4.8 e4a0237b6d yupana: rename khipu→yupana — el registro es el khipu, la yupana es el ábaco que reckona sobre él
`khipu` colisionaba con una app de tawasuyu. En vez de un nombre a dedo, el
significado más profundo RESUELVE la colisión: en los Andes el khipu GUARDABA
(el registro de nudos) y la yupana CALCULABA sobre él (el ábaco). Acá igual, y
la distinción es arquitectura:

  docs/state/build-state.json = el KHIPU  (el registro firme: nudos y cuerdas)
  scripts/yupana.py           = la YUPANA (el motor que reckona: radio, ondas, clausura)

Nunca fue del todo un khipu lo que construimos; era la yupana. La colisión
empujó al nombre más exacto.

git mv preserva historia. Verificado tras el rename: `yupana radio libdrm` da
126/132/[kde,mirada]; el guardián de regresión pasa; build-state importa yupana
y --check sigue en 0. Cero referencias `khipu` colgadas en código.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 19:53:53 -04:00