11 Commits
Author SHA1 Message Date
sergioandClaude Opus 5 55dd36d97a perfil escritorio-sway: 121/121 CERRADO — la cuarta imagen de escritorio, y la más barata
Declarado en targets.toml y verificado con el grafo: **121 nodos, 121 sellados, falta 0**. Es
una imagen construible hoy, no una intención.

Nueve raíces (sway, yambar, fuzzel, swaybg, swaylock, swayidle, grim, slurp, wl-clipboard) más
`hereda = ["cli"]`, porque un WM sin userland debajo no se usa: hacen falta la terminal y las
herramientas. `foot` no se lista porque ya entra por la clausura de `cli`. El resto de la
imagen —wlroots, mesa, wayland, libinput, seatd, cairo, pango, fcft…— lo calcula el grafo
siguiendo las aristas: escribir la clausura a mano es justo lo que targets.toml existe para
evitar.

LA COMPARACIÓN QUE VALE: KDE y GNOME fueron campañas de semanas porque debajo tienen una torre
de C (Qt entero, GTK, la cascada de mesa). Esto es una decena de binarios pequeños sobre
wlroots y salió en una noche. Ya estaba escrito en el SDD 20 como hipótesis —«los WMs ligeros
son la mejor relación esfuerzo/resultado que queda»—; ahora está medido.

`--wlr` en build-state.py, con su flag propio por la misma razón que COSMIC: sin él el frente
es INVISIBLE para el khipu, y `yupana radio` sobre una receta compartida (wayland, libinput,
pixman, mesa, libxkbcommon…) no reportaría la imagen sway entre las afectadas — o sea que el
próximo que toque una de ésas mediría de menos y creería que no rompe nada.

Queda lo que no puede decidir el grafo: probarlo en QEMU CON PANTALLA. La regla ya pagada cara
es que las imágenes de escritorio no se validan con `-nographic`, porque el compositor puede
«arrancar» en los logs y no pintar nada.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 22:01:24 -04:00
sergioandClaude Opus 5 570fd3747e 🧮 cosmic: trazarle la yupana — el frente existía en el store y el ábaco no lo veía
Faltaba la mitad del método.  SÍ cruzaba incoming-cosmic (lo usé
antes de tocar libdisplay-info, pipewire y glib), pero la campaña no estaba en el
KHIPU: build-state.py sólo conocía --kde y --gnome, y targets.toml no declaraba
perfil.

Y eso no era cosmético. Antes de este commit,  decía
21 dependientes transitivos y dos imágenes; ahora dice 31 y TRES —
escritorio-cosmic entre ellas, con incoming-cosmic=10 en el reparto por cola. O
sea que el próximo que tocara libinput, libudev-zero o mesa habría MEDIDO DE
MENOS, que es exactamente el punto ciego que la metodología existe para cerrar.

Tres piezas:
- build-state.py --cosmic (y su build-state-cosmic.json).
- perfil.escritorio-cosmic en targets.toml, con las raíces que cosmic-session
  levanta MÁS los datos que ningún [deps] alcanza (iconos, xkb, fuentes, bash,
  dbus). Primera medición: 54/61 listo, faltan 7.
- el LATIDO lo regenera, por la misma razón por la que se le agregó GNOME en su
  momento: un frente que el cron no regenera envejece en silencio, y un grafo
  viejo miente con la misma cara que uno fresco.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 13:44:18 -04: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 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
sergioandClaude Opus 4.8 8da6ef6122 khipu: el motor de dependencias inversas que cruza TODAS las colas (y arregla la mentira de perfiles)
El 2026-07-22 un cambio a libdrm (default_library=both, para gtk4) invalidó 118
recetas KDE. El radio se midió contra recipes/*.toml y no contra
recipes/incoming-kde/ ⇒ 8 en vez de 118. Y build-state.py sin --kde cometía el
MISMO error: cargaba sólo el corpus, y su campo `perfiles` AFIRMABA que tocar
libdrm sólo afectaba a escritorio-mirada. Una mentira con confianza.

Causa estructural: las dependencias INVERSAS son un hecho del disco entero, no
de la vista que uno cargó. khipu.py las calcula SIEMPRE sobre todas las colas,
con resolución sibling-first (el nodo se identifica por (cola, nombre), porque
corpus/fontconfig y incoming-kde/fontconfig son nudos distintos).

  QUÉ NODOS PUNTÚO es decisión de vista. QUIÉN ME CONSUME es un hecho.

khipu es la PUERTA ÚNICA de la metodología: radio/perfiles nativos (cruzan todo
el repo) + delega estado/drenar/triaje/frontera/objetivo a sus órganos.

  khipu radio libdrm → 126 directos, 132 transitivos, [escritorio-kde,
                        escritorio-mirada], 11 sellados que caen. El número
                        honesto que habría frenado el cambio.

build-state.py ahora saca `perfiles` y el nuevo `dependientes_total` de khipu
(grafo entero), no de los nodos que cargó. Verificado: el grafo por defecto ya
atribuye libdrm a escritorio-kde; sin_perfil bajó 674→666 (8 recetas del corpus
que sólo KDE consume, ahora bien atribuidas). --check sigue en 0, grafo cierra.

Repo-wide por diseño, no KDE-céntrico: zlib toca las 4 imágenes (233 transitivos),
make 313.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 18:43:40 -04:00
sergioandClaude Opus 4.8 a27d9a94a4 catálogo objetivo P2: estado wanted + membresía de perfil en el grafo
El grafo cruza ahora el corpus con el manifiesto de objetivo:

· estado `wanted` — una raíz declarada sin receta se materializa como nodo de
  frontera. Se crea ANTES de buscar huérfanas a propósito: un objetivo declarado
  no es una arista colgando, así `--check` conserva su significado (verificado:
  exit 0, grafo CIERRA, topo OK). `wanted` ≠ `unhashable`.
· campo `perfiles[]` por nodo — qué imágenes lo alcanzan desde sus raíces.
· bloque `by_profile` — el "cuánto falta", POR IMAGEN.

Primeros números, derivados de raíces y no de listas a mano:
   base               51/51   listo
   cli                74/74   listo
   escritorio-mirada  27/31   (falta libinput, mesa-swrast, mirada-{compositor,greeter})
   escritorio-kde     85/162  faltan 77   ← de 7 raíces, antes ilegible
   674 recetas no las alcanza NINGUNA imagen (catálogo, no distro)

La clausura es COTA INFERIOR mientras haya nodos `wanted`: sus deps no se
conocen hasta construirlos (o sembrarlas, P3). Está documentado en el header.

`totals.recipes` sigue contando sólo recetas con fichero (los consumidores lo
leen como tamaño del corpus); `totals.nodes` incluye la frontera. La vista HTML
regenera sin cambios de contrato.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:43:40 -04:00
sergioandClaude Opus 4.8 4357cd7b0f estado: buscar el artefacto por el name INTERNO, no el basename del fichero
El grafo buscaba store/<hash>-<basename> pero el artefacto se sella por el name INTERNO de la receta
(qtbase.toml → name=qt6-qtbase → store/<hash>-qt6-qtbase). Las qt6-* KDE salían 'debt' aunque
estuvieran selladas. Fix: nodo lleva iname=d['name']; state_of busca por iname. Las aristas de deps
siguen por basename (así resuelve resolve_dep_path). qtbase ahora sale sealed correctamente.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:32:14 -04:00
sergioandClaude Opus 4.8 ff79dc5e28 estado+kde: el grafo ahora cubre la cola incoming-kde; base KDE desbloqueada en el laptop
MOTIVO: 'estábamos haciendo kde'. La cola recipes/incoming-kde/ (206 recetas) era punto ciego del
grafo (sólo miraba recipes/ top-level). Modo --kde: build-state.py/build-state-view.py cubren la cola
KDE (sombreando canónicas homónimas sibling-first, como el sandbox) → build-state-kde.{json,html}.

CAUSA de la deriva (193/206 en deuda): fui YO — esta sesión re-sellé pkgconf y samurai (arreglos
static), y 194 recetas KDE declaran pkgconf, 142 samurai ⇒ re-hash en cascada de todo el árbol. El
sistema determinista funcionando: cambia un input base, cascan los dependientes. Deuda legítima.

DESBLOQUEO (el 'GUI rompe en el laptop' era sólo cairo/pango, NO la base): construida en el laptop
toda la base KDE guiado por el orden de ataque del grafo — extra-cmake-modules, util-macros,
xorgproto, xcb-proto, wayland, libdrm, dbus, icu4c, util-linux, fontconfig, pixman, glib, freetype,
harfbuzz, fribidi, libXau/libXdmcp, la cadena X11 (xtrans/libxcb/libX11/libXext/libXrender/libXfixes/
libXi), la familia xcb-util, libxkbcommon, wayland-protocols, mesa (24.0.9 iris-only, sin LLVM), y
los -shared. KDE 9→39 selladas. qtbase (↑117, la raíz) quedó DESBLOQUEADO y compilando.

DISCO: el build llenó el disco (100%). Liberados 82G borrando work/sources (57G, se re-extrae solo)
y .farm-harvest (26G, verificado 100% redundante en el store, CAS).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:27:55 -04:00
sergioandClaude Opus 4.8 a267cf4f48 estado: clasificación Rust corregida (había ~180 Rust escondidas como C) + musl
Señal fuerte para Rust: la receta copia su binario de target/release/ en las fases. Medido 225
recetas, 0 con dep go ⇒ sin falsos positivos con Go (findutils entra bien: es uutils/findutils, find
en Rust). LÍMITE: un crate BuildSys::Cargo PURO sin fase install propia (zellij) no deja rastro en el
TOML y cae a 'c' — sólo se ve bajando la fuente, que el generador no hace.

Efecto: clases c 322→141, rust 45→226 (la mayoría eran Rust auto sin fase cargo explícita). Y la
LECTURA de la deuda cambia: la deuda C REAL es sólo 8 (casi toda saldada esta sesión), no 74. Lo que
queda es Rust (15, CLI pesados → granja), GUI (29 → granja), Go (5 → worker), kernel (4).

musl sellado (base). Avance: sealed 703→704, debt 53→52.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 12:36:23 -04:00
sergioandClaude Opus 4.8 a30e0d3146 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>
2026-07-17 11:48:28 -04:00
sergioandClaude Opus 4.8 7f89a635a4 estado: grafo de build persistido y versionado (scripts/build-state.py → docs/state/build-state.json)
El estado real del build vivía disperso —en mi cabeza, en docs que envejecen (matar-gcc decía 47,
eran 16), en el store (que guarda TODOS los sellados históricos, no el vigente)— y cada medición a
mano mentía distinto. Este generador lo deriva de la ÚNICA fuente de verdad (recetas + hammer hash
+ store) a un JSON firme. El git diff de ese fichero ES el avance entre dos corridas: qué se saldó,
qué se rompió, qué cambió de estado.

NODO = receta {name, class, link, compiler, deps[], hash, state}:
  sealed  — el artefacto del hash VIGENTE está en el store (al día)
  debt    — hay sellados históricos pero ninguno vigente (cambió, falta rebuild)
  never   — sin ningún sellado
  unhashable — hammer hash falló (hueco real)
ARISTA = dep de build. El grafo CIERRA (0 deps huérfanas) y topo-ordena sin ciclos.

Foto inicial (764 recetas): sealed 647 | debt 109 | never 8. Deuda por clase: c=74 go=5 gui=29
kernel=4 rust=5. Clases: go 362, c 322, rust 45, gui 30, kernel 5.

Validado contra lo que sé de esta sesión: samurai/which sealed, curl/openssl debt, helix sealed
(rust/gcc), naabu sealed (dynamic/go), mesa debt (gui), linux debt (kernel). Todos correctos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:30:41 -04:00