53de4e259aa48337c4aefa61e005d5d2198035fc
57
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
13ce9b16f1 |
targets: la IA local entra en las CUATRO imágenes de escritorio
Decisión del usuario (2026-09-12): `llama-cpp` + `ia-modelo-chat` de raíz en escritorio-gnome, -kde, -cosmic y -sway. El motivo es que el panel de la barra lateral está en las cuatro —viene con `atuq`— y una función del navegador que sólo existe en una imagen es una trampa: en las otras el usuario ve el panel y lee «esta imagen no trae modelo». Cuesta ~1,25 GiB por imagen (199 M el motor + 1,04 GiB el modelo) y está escrito en el comentario, al lado de las líneas, para que bajarlo sea una decisión y no un descuido. Verificado como manda la lección de `foot` que este fichero ya aprendió: las dos raíces expanden en los cuatro perfiles (`scripts/targets.py`), las dos están SELLADAS al hash vigente, y el grafo las ve con sus cuatro perfiles encima — una raíz que no resuelve quedaría `wanted` y ninguna métrica lo diría. El grafo CIERRA y el topo-sort sigue OK. |
||
|
|
d3b42f892d |
zstd-cli: la distro comprime todo con zstd y no traía con qué descomprimirlo
`recipes/zstd.toml` construye **sólo `lib/`** —lo dice su propia cabecera, «build de lib/ (no CLI)»— porque lo que necesitan sus 13 consumidores (mesa, libadwaita, appstream, rsync, los `zstd-sys`) es `libzstd.a`. El binario nunca se construyó, y el agujero sólo se ve al instalar la distro en una máquina de verdad: **un takana recién instalado no puede desempacar su propio laboratorio**, porque el `tar` del rootfs es el de busybox (`tar: unrecognized option: zstd`) y `zstd` no existe. Hubo que extraer por tubería desde otro hub — y un hub que se instala solo no puede depender de eso. ⚠ **Y mi arreglo anterior estaba mal**: declaré `zstd` en `perfil.base` dando por hecho que traía el binario. Se destapó aplicándolo con `takana upgrade` en la caja: «✓ generación 1 aplicada, + 8 añadidos» y `zstd` seguía ausente, porque los 8 ficheros eran headers y `libzstd.a`. **El upgrade hizo exactamente lo que debía; lo que estaba mal era lo que le pedí que aplicara.** Corregido a `zstd-cli`. Variante y no ampliación de la canónica: `zstd` es dep de 13 recetas y `mesa` está entre ellas; re-hashearla arrastraría esa torre por un binario de 1 M que ninguna usa. Mismo criterio que las 23 variantes `*-shared`, con el eje en librería/herramienta en vez de estático/dinámico. Radio cero. `HAVE_ZLIB/LZMA/LZ4=0` a propósito: sin eso el Makefile las detecta del sysroot del LAB y el binario sale pidiendo sonames que el artefacto no publica. Verificado: estático, 0 intérpretes requeridos, `Zstandard CLI v1.5.7`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x |
||
|
|
9a31857eaa |
targets: zstd en base — un takana recién instalado no podía desempacar su propio laboratorio
La distro comprime TODO con zstd: la imagen del lab (`lab-image.tar.zst`), el respaldo al Storage Box, el `dd` remoto de una instalación. Y la imagen no traía el binario. Medido sembrando el lab en la caja de producción: el `tar` del rootfs es el de busybox y contesta `tar: unrecognized option: zstd`, así que hubo que extraer por tubería desde el hub (`zstd -dc … | ssh caja 'tar -xf -'`). Un hub que se instala solo no puede depender de que otro hub le descomprima las cosas. La receta ya existía y está sellada (`b3:1ceb6215…`): sólo faltaba declararla. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x |
||
|
|
7e41ffd0e6 |
SDD 28 §6.5: el muro derribado — de 23 binarios inertes a 1, y sin mover un ArtifactHash
Elegidas las salidas 1 y 2 del §6.4; hecha la 1, que resultó más barata de lo que parecía y desbloquea la 2 en vez de competir con ella. `musl-shared` publica `/usr/lib/libc.so` + `/lib/ld-musl-x86_64.so.1`. Como variante y no tocando la canónica: `musl` es Stage 1 y su `of_tree` es el baseline del selfhost. Control: `takana hash recipes/musl.toml` sigue en `b3:ce952f72…`. Y `musl` no es dep de nadie ⇒ cero re-hasheo del corpus. En la caja: `sh: python3: not found` → `Python 3.12.10`. De 23 inertes quedó **1**, `sqlite3`, que pedía `libz.so.1`; `zlib-shared` ya estaba sellado y sólo faltaba declararla en `base`. La salida 2 (python/perl estáticos) sigue viva pero deja de ser urgente: con el cargador publicado el sistema ya no cuelga del rootfs del lab. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x |
||
|
|
7e86459738 |
musl-shared: el corpus publica su propio CARGADOR — 23 binarios dejan de ser inertes
La imagen del perfil servidor traía 23 binarios de 726 que no podían ejecutarse:
$ python3 -c "print(1)"
sh: python3: not found <- y `command -v python3` decía /usr/bin/python3
No faltaban: eran ELF dinámicos pidiendo `/lib/ld-musl-x86_64.so.1`, y **ningún artefacto del corpus
publicaba ese fichero**. Funcionaban en el hub sólo porque el rootfs Alpine del LAB lo presta — la
misma fuga que documentan `zlib-shared` y `expat-shared`, un piso más abajo, y peor: el lab no entra
en `hash_inputs`, así que la dependencia era invisible para el store.
Los 23 son la suite binutils entera + perl, python3, sqlite3, flex y nft. Entre ellos, TODO el
instrumental del proyecto, que es Python.
**Variante y no `--enable-shared` en la canónica**: `musl` es componente de Stage 1 y su `of_tree` es
el baseline de `selfhost-verify`. Re-hashearlo obliga a rehacer ese baseline a propósito, y eso es su
propia unidad de trabajo. Con el patrón `*-shared` (ya hay 23 en el corpus) la canónica no se mueve —
control: `takana hash recipes/musl.toml` sigue dando `b3:ce952f72…`, el mismo sellado de Stage 1. Y
además `musl` no es dep de NADIE (medido: sólo de sí misma; el enlace estático lo resuelve el musl de
zig), así que esto suma sin mover un solo ArtifactHash del corpus.
**⚠ `compiler = "gcc"` y no `zig-cc`, medido.** Con zig cc el `libc.so` sale con 1586 símbolos
dinámicos contra los 1654 de Alpine, y los 68 que faltan son EXACTAMENTE los que musl implementa en
ensamblador x86_64 (`memset memcpy memmove memcmp strlen` y toda la familia matemática) más los
`__stack_chk_*`. No es que no se compilen —en el `libc.a` canónico están—: zig los resuelve con su
propio musl y los deja `FUNC LOCAL HIDDEN` de tamaño 0, fuera de la tabla dinámica. El síntoma es un
cargador que arranca, reloca y muere con `memset: symbol not found`, que se lee como "el binario está
roto" y no como "a la libc le faltan símbolos". Con gcc: **1651 símbolos**, `memset` y `ceil`
presentes, y `python3 3.12.10` CORRE con el cargador del corpus.
De paso: el comentario de `musl.toml` decía que el artefacto aportaba «el loader, para uso dinámico
futuro». Era falso —`--disable-shared` no construye ninguno— y es justo la etiqueta que hizo que
nadie buscara el agujero. Corregido (los comentarios no entran en `hash_inputs`; verificado).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x
|
||
|
|
2613fe3951 |
servidor-image.sh: la imagen del perfil servidor, armada por script — y netup pasa a ser raíz
El ensamblado dejaba de ser reproducible en cuanto se cerraba la terminal: cuatro pasos, tres de ellos con una trampa que no se ve. Ahora es un script, con las trampas escritas en su cabecera: 1. **El kernel.** `linux.toml` NO sirve para hcloud (apaga SCSI y el disco de Hetzner es virtio-SCSI). Va `linux-generic`, que sí trae `CONFIG_SCSI_VIRTIO=y` — dato que no está en la receta, lo pone `make defconfig`, y sólo se ve en el `.config` que el artefacto publica. 2. **El init se pisa.** La clausura del perfil trae busybox y su `/sbin/init` gana por hidratarse después. El script lo restaura e IMPRIME la reparación (`../bin/busybox → /usr/bin/arje-zero`), que es la diferencia entre arreglarlo y creer que estaba bien. 3. **EXDEV.** Comprueba con `findmnt` que STORE y WORKDIR estén en el MISMO montaje y aborta con un mensaje que dice qué hacer, en vez de fallar fichero por fichero a mitad de un `cp -al`. 4. **La clave es obligatoria.** Sin `AUTHKEYS` no arma nada: una instalación remota sin `authorized_keys` deja una máquina viva e INALCANZABLE, que es peor que una que no arrancó. Y el cmdline va SIN `init=`, a propósito: así corre el wrapper que escribe `install-image.sh`, que es quien monta `/store` y `/var/lib/hammer`. **`netup` entra como raíz de `perfil.servidor`** aunque su binario ya venga dentro del `product-rootfs`. La razón es concreta y se pagó hoy: el del producto está congelado en el artefacto sellado del bootstrap, así que el arreglo de la ruta on-link NO llegaba a la imagen. Declarado como raíz, la hidratación lo proyecta encima y la imagen lleva el vigente — verificado por sha256: la imagen trae `a23df20e…` y el product-rootfs `a84b0a0d…`. `recipes/netup.toml` re-pineado a `be383ea4` (el commit del arreglo). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x |
||
|
|
7b38e2ef16 |
kde: los portales — segundo hueco cerrado, y OBS ya tiene con quién hablar
Medido recorriendo los `.service` de D-Bus de TODO el store: los únicos backends de portal sellados eran los de COSMIC y GNOME. Ninguno de KDE, y la cola `incoming-kde` no tenía ni el frontend. Consecuencia concreta: la imagen KDE trae `obs-studio` con su plugin linux-pipewire, que captura pantalla POR EL PORTAL — o sea que en Plasma no tenía con quién hablar mientras en los otros dos escritorios ese frente estaba cerrado. Sin portal tampoco hay diálogo de fichero ni compartir pantalla para nada que los use. Van los DOS paquetes porque la cadena es de tres: cliente → org.freedesktop.portal.Desktop [frontend] → org.freedesktop.impl.portal.* [backend] El frontend se copia de la cola de COSMIC y **comparte artefacto**: hash idéntico (ecbe13f68eca6) desde las dos colas ⇒ un solo directorio en el store, cero builds. El backend es receta NUEVA, 6.7.2 — la MISMA versión que plasma-workspace, kwin y kscreenlocker de esta cola, porque habla interfaces privadas de kwin para el ScreenCast y desalinear ahí es pedir que dos mitades del mismo release se entiendan por casualidad. **Cero frontera nueva**: sus 22 dependencias (Qt6, KF6, KWayland, protocolos de Wayland, xkbcommon) ya estaban todas, verificadas una por una antes de escribir la receta. ⚠ SIN EL PORTAL DE IMPRESIÓN, y la causa es más honda que el error. El build moría en `src/print.cpp:27` con `'QtPrintSupport/private/qcups_p.h' file not found`, y la cadena está medida entera: no hay NINGUNA receta de cups en el catálogo ⇒ qtbase 6.11.1 se construyó sin CUPS ⇒ su `QtPrintSupport/private/` trae qpaintengine_alpha_p.h y qprintengine_pdf_p.h pero NO qcups_p.h. No falta una cabecera: falta el subsistema. Un portal de impresión sin con qué imprimir no es funcionalidad que se pierde, es código muerto que no enlaza. Upstream no expone `option()` para portales —se compilan todos—, así que el parche va en TRES sitios y no en uno: la fuente, la instanciación (desktopportal.cpp/.h) y **la lista de interfaces que el backend ANUNCIA**. El tercero es el que se olvidaría y el que importa: anunciar `impl.portal.Print` sin implementarlo hace que el frontend enrute a un portal que no contesta, que es peor que no tenerlo. Cada `sed` lleva su `grep` de verificación al lado, para que el día que upstream mueva esas líneas el parche falle RUIDOSO en vez de volverse inerte. VERIFICADO sobre el artefacto sellado: publica `org.freedesktop.impl.portal.desktop.kde.service`, y su `kde.portal` anuncia 17 interfaces con **0 apariciones de Print** e incluyendo ScreenCast, Screenshot y RemoteDesktop, que es exactamente lo que OBS necesita. Y sobre el rootfs hidratado: están el frontend (`org.freedesktop.portal.Desktop.service`), el backend y el `kde.portal`. ⚠ Y la licencia casi la firmo mal: leí la cabecera de UN fichero y generalicé. Contadas las etiquetas SPDX del árbol entero: 88 `LGPL-2.0-or-later`, 23 de la fórmula de KDE e.V. y 1 `GPL-2.0-or-later`. El binario las enlaza a todas ⇒ la expresión es la CONJUNCIÓN. El perfil pasa a 98 raíces, 361/361 listo, deuda 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV |
||
|
|
c599daa346 |
SDD 28: el servidor de producción — y perfil.servidor declarando la deuda que no se ve
El frente arranca con el inventario MEDIDO de gioser, no con un plan: PID 1 es `arje-zero` (nuestro init ya gobierna un servidor real, aunque sobre userland Artix), Hetzner Cloud arranca por **BIOS** —que es justo el layout que `takana-install.sh` ya escribe— y el kernel propio trae VIRTIO/EXT4 `=y` con `netup` haciendo DHCPv4, que es exactamente el caso de una caja hcloud. Dos hallazgos que cambian el trabajo: - **La secuencia de arranque de gioser no está escrita en ningún lado.** `rc-status` dice `stopped` para caddy, gitea, sshd, cronie y act-runner y los cinco están VIVOS colgando de PID 1. Un `arje-absorb --from openrc` produciría una Semilla perfecta que no levanta el servidor. - **La mitad del Caddyfile describe un servidor que ya no existe**: 4 de 19 dominios sin DNS y sin ficheros en `/var/www`, uno en 502. Una mudanza fiel copia también la basura ⇒ la regla queda escrita: no se muda lo que no responde. `[perfil.servidor]` hereda `cli` (SDD 27 §7.1) y declara 5 raíces que existen (openssh, caddy, curl, wget, tmux) y **5 que NO** (takana, chrony, nftables, cronie, logrotate). Quedan `wanted`, y `wanted` no es `debt` ⇒ `drenaje.json` seguirá diciendo `deuda=0`. Se declaran igual: sin ellas el perfil saldría N/N —completo y verde— describiendo un servidor sin hora, sin cortafuegos y sin latido. Es la lección de `foot` en escritorio-sway, pagada por adelantado. Cuenta esperada: 5, verificada resolviendo cada nombre contra el campo `name` de las 869 recetas. La peor de esas cinco: **takana no tiene receta**. El corpus construye 869 y no la suya, así que el host no puede instalar takana desde el repo de takana — el bloqueante de que el servidor se sirva a sí mismo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x |
||
|
|
c20372597a |
targets: kde/gnome/cosmic heredan cli — las imágenes ya no salen sin shell ni red
Paso 1 del SDD 27. Resultó ser UNA LÍNEA POR PERFIL: el mecanismo `hereda` ya existía en `scripts/targets.py` —transitivo, con detección de ciclos y orden preservado— y `escritorio-sway` ya lo usaba desde el 2026-08-07. Los otros tres nunca lo declararon, y eso no era una decisión: era composición no declarada. LO QUE ARREGLA, medido antes: de los 27 paquetes del perfil `base`, la clausura de escritorio-kde alcanzaba DOS. Contra el rootfs real: sin `bash`, sin `sudo`/`doas`, sin `git`, sin `useradd`, sin `gpg` y **sin `dhcpcd`** —o sea sin con qué pedir una IP—; y lo que parecía estar (`ip`, `mount`, `fsck`) eran applets de busybox (`/sbin/ip → ../bin/busybox`). DESPUÉS, verificado hidratando de verdad y no leyendo el grafo: en el rootfs aparecen `bash`, `sudo`, `doas`, `git`, `dhcpcd`, `useradd`, `gpg`, `rg`, `fd`, `bat`, y `/sbin/ip` pasa a ser el BINARIO de iproute2 en vez del applet. raíces kde 49→96 · gnome 39→86 · cosmic 43→88 nodos kde 301→356 · gnome 204→256 · cosmic 162→216 deuda 0 en los tres — no hubo que construir NADA, ya estaba todo sellado colisiones nuevas al hidratar: CERO (los 28 .hammer-tmp son los de gmp/mpfr de antes) ⚠ `escritorio-mirada` NO hereda a propósito: es el rootfs *slim* del USB y sumarle 47 raíces contradice su razón de ser. Si algún día se quiere, es la misma línea. `kde-rootfs` en el volumen se rehidrató con la herencia (11 G) para que el nombre canónico no quede viejo — que es el error que este mismo día costó encontrar en GNOME y COSMIC. El rootfs FUNDIDO y la imagen siguen siendo los de antes: rehacerlos es un paso aparte. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV |
||
|
|
542d9875fb |
kde: upower — la imagen declaraba powerdevil y no tenía batería
Primero de los dos huecos que dejaban a KDE POR DEBAJO de una instalación estándar. El perfil declaraba `powerdevil` —el gestor de energía de Plasma— pero no a `upower`, que es con quien powerdevil habla para leer la batería y aplicar perfiles. Medido: upower sólo existía en la cola de GNOME, y las colas no se ven entre sí (HERMANO→corpus, nunca de reojo). En un portátil eso es un escritorio sin indicador de batería, y el arranque no falla ⇒ no se ve hasta usarlo. Cuesta TRES recetas y no siete, y la diferencia es una perilla: `-Dintrospection=disabled`. GNOME la necesita ENCENDIDA porque su shell hace `imports.gi.UPowerGlib` desde JavaScript, o sea que carga el typelib. powerdevil NO: habla D-Bus con `org.freedesktop.UPower`. Apagarla saca de la frontera gobject-introspection, gi-foreign-girs, glib-introspected y py3-setuptools, que sólo viven en la cola de GNOME. ⚠ Y NO SE PROMUEVEN AL CORPUS, aunque era lo primero que intenté. Al mover `libgudev` allá su ArtifactHash CAMBIÓ (7404bc8d → 87ca5e73) y eso no es una mudanza: `glib` existe en las DOS colas y son recetas distintas, así que desde `incoming-gnome/` se construye contra la glib de GNOME y desde el corpus contra la del corpus. Promoverla habría arrastrado a GNOME a la glib del corpus — el cuadro de las «dos glib» que este repo ya pagó caro. Revertido y verificado que vuelve al hash de antes (7404bc8d), o sea GNOME intacto. Se COPIAN a la cola de KDE. `udev-pc` sí comparte artefacto (sus deps son sólo pkgconf ⇒ mismo hash desde las dos colas, un solo directorio en el store). `libgudev` es otro a propósito, por la glib. VERIFICADO sobre el artefacto sellado, no supuesto: trae `usr/libexec/upowerd`, el `org.freedesktop.UPower.service` de system-services y su política en `system.d` — que es exactamente lo que powerdevil necesita para que el demonio arranque por activación D-Bus. El perfil pasa a 49 raíces, 301/301 listo, deuda 0. Queda el segundo hueco: KDE no tiene backend de portal (ni `xdg-desktop-portal` en su cola), o sea que no hay compartir pantalla — y en particular la captura de obs-studio, que la imagen ya trae, no tiene con quién hablar en Plasma. Eso es más caro: pide traer el portal a la cola y ESCRIBIR `xdg-desktop-portal-kde`, que no existe como receta en ninguna cola. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV |
||
|
|
e1d58cc583 |
cosmic: hidratar desde el perfil — su lista a mano se saltaba 17 declarados
Misma divergencia que se encontró ayer en GNOME, medida igual: `hydrate-cosmic.sh` nombra 25 raíces escritas a mano dentro del script, el perfil `escritorio-cosmic` declara 43, y **17 paquetes declarados no llegaban al rootfs**: adwaita-cursors · xkeyboard-config · bash · atuq (el navegador) · mpv · swayimg zathura + zathura-pdf-poppler · dejavu-fonts y dejavu-fonts-nerd · libnotify desktop-file-utils · hicolor-icon-theme · y las cinco *-shared de runtime ⚠ EL PRIMERO NO ES UNA COINCIDENCIA: `adwaita-cursors` es EXACTAMENTE el ejemplo con el que la cabecera de `scripts/hydrate-profile.py` advirtió de esto el 2026-09-04 — «se añadió a las raíces de cosmic y sway, y los scripts de hidratación, que no la conocen, habrían seguido armando un rootfs sin cursores». No era hipotético: seguía pasando hoy, cinco días después. Y `xkeyboard-config` es la cicatriz de sway repitiéndose: sin él el compositor arranca sin mapa de teclado. Y AL REVÉS NO SE PIERDE NADA, que es lo que hace el cambio barato y lo separa del caso GNOME (donde primero hubo que mover 11 raíces de runtime al perfil): de las 25 del script, la única que el perfil no declara es `zlib-shared`, y la clausura del perfil la alcanza igual. O sea que el motivo que justificaba la lista a mano —los componentes que `cosmic-session` lanza por PATH, que ningún grafo de build ve— YA ESTÁ RESUELTO en el perfil, que los declara todos como raíces. La lista quedó redundante sin que nadie lo notara. VERIFICADO HIDRATANDO, no leyendo: `hydrate-profile.py escritorio-cosmic` proyecta 162/162 nodos (24166 ficheros, 4,8 G) y ahí están los 17. (Ojo con la sonda: `bash` instala en `/bin/bash`, no en `/usr/bin` — mi primer chequeo lo dio por ausente y era el chequeo el que miraba mal.) ⚠ Y UN DATO QUE MATIZA EL DIAGNÓSTICO Y LO EMPEORA. El rootfs que hay en disco (`escritorios/cosmic-rootfs`, 3,9 G) SÍ tiene cursores, xkb y mpv — o sea que no se armó con la lista por defecto, alguien le pasó raíces a mano. Y aun así le falta `atuq`. El problema real no es que la lista esté mal: es que **lo que llega a la imagen depende de lo que alguien se acuerde de teclear**. Derivarlo del perfil quita esa variable. El script se deja porque su cabecera documenta por qué COSMIC necesita raíces de runtime explícitas —sigue siendo cierto sobre la naturaleza de cosmic-session—; lo que ya no es cierto es que su lista sea la verdad. Los dos armadores de imagen (qemu y metal) ahora apuntan a `hydrate-profile.py`. ── Y de paso, una nota vieja de targets.toml corregida ──────────────────────────────────────────── Decía que el perfil de KDE «son 13 raíces de ARRANQUE EN METAL, no la imagen completa», con konsole/kate/dolphin y otras 19 apps selladas y sin declarar. Ya no: hoy son 48 raíces, con esas apps, las fuentes y las *-shared declaradas — el frente KDE lo reordenó y la nota quedó atrás. De su cola quedan 8 recetas fuera del perfil y son las que corresponde: libICE, libSM, libXtst y los cinco eslabones huérfanos de xwayland desde que se retiró su receta. Lo que a KDE le queda es OTRO problema, también medido hoy: su imagen no se hidrata del perfil sino de un rootfs YA ARMADO (8,4 G) que nadie re-deriva. De las 18 hojas del perfil están 15 — faltan `obs-studio` y `atuq`, justo las dos añadidas después de esa hidratación. Re-hidratarlo es decisión del frente KDE. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV |
||
|
|
eb8f217245 |
gnome: el perfil y el hidratador decían cosas distintas — 21 paquetes no llegaban a la imagen
Verificando que las extensiones de la tanda anterior llegaran al rootfs apareció algo bastante peor que lo que fui a buscar: **`scripts/gnome/hydrate-gnome.sh` no lee `targets.toml`**. Arma el rootfs desde una lista de 14 raíces escrita a mano dentro del propio script, y de las 28 raíces que el perfil declaraba sólo DOS coincidían. MEDIDO recorriendo la clausura de esa lista (123 recetas) contra la membresía del perfil: **21 paquetes declarados en `escritorio-gnome` no llegaban al rootfs hidratado**. No son accesorios — son las hojas, que es justo lo que un perfil declara porque no es dep de build de nadie: atuq (el navegador) · foot (la terminal) · helix (el editor) · mpv · swayimg (el visor) zathura + zathura-pdf-poppler (el lector de PDF) · dejavu-fonts y dejavu-fonts-nerd (las FUENTES) xdg-desktop-portal-gnome · libnotify · desktop-file-utils bzip2-shared, expat-shared, libffi-shared, ncurses-shared, xz-shared (las .so que se cazaron el 2026-09-03 recorriendo NEEDED, justamente para que el rootfs no resolviera contra el lab) y las cuatro de ayer: las tres extensiones y gnome-tweaks O sea que el trabajo de descubrir que la imagen no tenía terminal, ni editor, ni una sola fuente —con su análisis largo escrito en este mismo fichero— estaba DECLARADO y no llegaba. Y la divergencia iba en los dos sentidos: 11 raíces de RUNTIME vivían sólo dentro del script y el perfil no las conocía (accountsservice, geoclue, libgdm, libgweather, upower, librsvg, ibus, los dos gi-*-typelibs, gsd-schemas y wireplumber). Sin ellas el shell no arranca: las carga por `imports.gi.*`, que es invisible para la clausura de deps. ARREGLO: las 11 se mueven a `targets.toml` con la justificación que el script ya traía bien escrita, y el script queda marcado como SUPERADO por `scripts/hydrate-profile.py`, que hidrata desde el perfil — la única declaración que existe. Su propia cabecera ya advertía de este modo de fallo («DOS fuentes de verdad para lo mismo, y divergen sin que nada lo diga»); esto es esa advertencia cumpliéndose. El perfil pasa de 28 a 39 raíces y de 190 a 204 nodos, deuda 0. VERIFICADO HIDRATANDO DE VERDAD, no leyendo: `hydrate-profile.py escritorio-gnome` proyecta 210/210 nodos (27993 ficheros, 3,5 G) y ahí están las tres extensiones, `gnome-tweaks` con su módulo `gtweak`, y los tres gschemas en el directorio GLOBAL. Después se corrió el bloque nuevo del lanzador contra ESE árbol: enumeró las tres, escribió el override y `glib-compile-schemas` salió 0 con stderr vacío. ⚠ DOS COSAS QUE APRENDÍ MIDIENDO Y CONVIENE QUE ESTÉN DICHAS: 1. **El hub NO puede hidratar GNOME hoy.** `gjs`, `gnome-shell` y `spidermonkey` salen «no sellado» localmente. No es deriva ni un grafo mintiendo: sus hashes vigentes SÍ están en `work/farm-sellados.txt` — o sea sellados EN EL WORKER — y la cosecha baja el manifiesto y no el store, a propósito (bajarlo deshacía la poda). `build-state` los cuenta bien porque mira también el manifiesto; `hydrate-profile.py` mira sólo el disco local. Los dos tienen razón. 2. **EXDEV entre bind-mounts, otra vez.** Hidratar a `work/` o a `work/out` falla con «Invalid cross-device link» AUNQUE `work/out` esté en el mismo /dev/sdb que el store: son bind-mounts distintos y `linkat()` no cruza montajes. Hay que hidratar accediendo al store por su montaje raíz (`--store /mnt/cosecha/store --into /mnt/cosecha/escritorios/…`). El mensaje de error del script lo dice y aun así me costó dos intentos. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV |
||
|
|
4e4a2c8d2a |
gnome: dash-to-panel, appindicator y gnome-tweaks — y la lista de activas sale de las recetas
GNOME deja de ser la cáscara: tres extensiones y la app que el proyecto no instala y todo el mundo
termina instalando. El perfil queda 190/190 con 28 raíces, deuda 0.
dash-to-panel v73 junta dash, ventanas y bandeja en una sola barra (lo que más cambia el uso)
appindicator v64 devuelve los ICONOS DE BANDEJA que GNOME quitó; sin ella las apps que usan
StatusNotifierItem corren y no tienen dónde mostrarse
blur-my-shell v72 (ya estaba; se le saca el override, ver abajo)
gnome-tweaks 46.1 tema, iconos, cursor, tipografías, los botones de minimizar/maximizar que
GNOME esconde, y el interruptor de las extensiones
⚠ LA LISTA DE ACTIVAS SE MUEVE A UN SOLO SITIO, y esto era una bomba de relojería. `enabled-extensions`
es un ARRAY: un override de GSettings lo escribe ENTERO. Con la primera extensión el override vivía
en su receta y funcionaba; con la segunda NO se habrían sumado — ganaría la que compile última
(orden alfabético) y las otras quedarían instaladas y MUERTAS, sin un solo error. Ahora la arma
`scripts/gnome/gnome-start-qemu.sh`, que ya compilaba los esquemas al arrancar, enumerando los uuid
presentes en /usr/share/gnome-shell/extensions. La política es «lo que la imagen instala, se
activa»; el dconf del usuario gana sobre el default.
Una receta NO puede ser ese sitio: hammer exige `[source]` en toda receta («missing field `source`»),
así que no hay forma de escribir una receta de pura política. Se probó el bloque contra un árbol de
juguete: lista las dos extensiones, salta un directorio sin metadata.json, el override compila con
stderr vacío y `gsettings get` devuelve las dos. Y si la lista cambia borra `gschemas.compiled` para
forzar recompilación — sin eso, una imagen ya arrancada ignoraría la extensión nueva en silencio.
⚠ EL HALLAZGO CARO: `msgfmt` de gettext-tiny REVIENTA con las formas plurales del árabe. SIGILL,
exit 132, «index 6 out of bounds for type 'size_t[6]'», y deja un .mo de 0 bytes. Reproducido FUERA
de la receta con el binario sellado. No es el idioma sino el mensaje: blur-my-shell compila su
ar.po sin problema porque no tiene NINGÚN plural (msgstr[5] = 0); el de tweaks tiene exactamente
uno. Acá se saca `ar` del LINGUAS y se pierde la traducción de UNA app, con el diagnóstico escrito
en la receta. ARREGLARLO ES OTRA UNIDAD DE TRABAJO: gettext-tiny la usan 108 recetas.
Cada receta usa el mecanismo de SU upstream, que no es el mismo en las tres:
blur-my-shell `make build` llama a `gnome-extensions pack` —el CLI que nuestro shell apaga— así
que se copia el árbol a mano, con el layout LEÍDO del zip de EGO y comparado
contra él (idéntico, sin sobras ni faltantes)
dash-to-panel su Makefile tiene un `install` que honra DESTDIR y hace lo de un paquete de
distro: gschema y locale a los directorios GLOBALES. ⚠ `VERSION=73` se pasa a
mano porque sin ella el Makefile hace `git describe` y el árbol viene por
`git archive`, sin `.git` ⇒ metadata quedaría con el marcador "version": 9999
appindicator meson. ⚠ `-Dlocal_install=disabled` explícito: en `auto` decide mirando si el uid
es 0, o sea que hoy acierta POR ACCIDENTE — y si el lab dejara de correr como
root instalaría en $HOME/.local y el artefacto sellaría vacío. Y `jq` va en deps
aunque sea JavaScript puro: su meson saca el uuid del metadata.json con jq
⚠ gnome-tweaks NO costó ninguna receta nueva, y eso hubo que medirlo: su meson declara siete deps
mínimas de runtime y las siete ya estaban selladas. La que parecía traer una cadena de Python
entera —`pygobject-3.0`— la provee `py3-gobject`, que YA ESTABA en el corpus con otro nombre y
publica `pygobject-3.0.pc`. El nombre no es el hecho, otra vez.
Sí costó dos deps que el configure reclamó y que son el patrón `.pc Requires` → `[deps].build`:
`appstream` (la pide `libadwaita-1.pc` y el error culpa a libadwaita, que está bien instalada) y
`desktop-file-utils` (meson la busca como PROGRAMA para `update-desktop-database`).
Sus deps de RUNTIME —python3 y py3-gobject— van declaradas: es una app de Python, si no están en la
imagen se instala y no arranca, y ningún build lo delata.
Las cuatro licencias, verificadas en la fuente pineada y NINGUNA leída del COPYING —cuyo apéndice
«How to Apply» trae siempre la frase «or any later version»—: blur-my-shell del README del commit,
dash-to-panel y appindicator de la cabecera de sus fuentes, gnome-tweaks de su etiqueta SPDX.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV
|
||
|
|
8c2cb10414 |
gnome: la primera extensión del catálogo — blur-my-shell v72, instalada Y activa
La imagen de GNOME traía la CÁSCARA: el shell sabe cargar extensiones —`ui/extensionSystem.js` va
en el gresource sin opción que lo apague y su gschema sellado trae `enabled-extensions`— pero no
había ninguna que cargar. Esta es la primera, y fija el patrón para las otras ~1400.
⚠ NO HIZO FALTA TLS, que era la conclusión fácil del análisis de ayer. glib-networking sólo lo pide
BAJARLAS desde extensions.gnome.org con el descargador del shell. Empaquetada, una extensión se
instala como cualquier receta y no toca la red.
LA FUENTE ES EL REPO DEL AUTOR, NO EL ZIP DE EGO, por dos razones y las dos medidas:
1. hammer no sabe abrir un zip: el fetch extrae con `tar -x` y GNU tar contesta «This does not
look like a tar archive». Ninguna receta del corpus usa .zip.
2. Y aunque lo abriera, el zip de EGO es un ARTEFACTO que construye EGO desde este mismo repo.
Pinear el zip es pinear el binario de un tercero; pinear el commit es pinear el código.
El tag v72 es el que EGO sirve para shell 48 —preguntado a su API, no supuesto— y el metadata.json
declara ['46','47','48','49','50']. ⚠ Eso hay que preguntarlo por extensión: nixpkgs va por GNOME
50 y hay extensiones que ya sólo declaran el major nuevo.
SIN `gnome-extensions pack`, que es lo que usa el Makefile de upstream y es el CLI que nuestra
receta del shell apaga (-Dextensions_tool=false; encenderlo costaría gnome-autoar y re-hashear
gnome-shell entero). No hace falta: `pack` sólo arma un zip que después alguien descomprime, y lo
que el shell lee es un DIRECTORIO. El layout no se inventó — se leyó del zip de EGO v72, que es la
salida de ese mismo `pack`, y el artefacto sellado se comparó contra él: **entradas idénticas, ni
sobras ni faltantes**, 45 .mo compilados.
⚠ INSTALADA ≠ ACTIVA, y esto es lo que hace que la receta sirva. El shell sólo carga lo que esté en
`org.gnome.shell enabled-extensions`: sin eso el artefacto sella, el fichero está en la imagen y NO
PASA NADA al arrancar — el mismo cuadro de «sellado ≠ instalado» que este repo ya se comió con
foot, mpv y libnotify, un escalón más abajo. Se resuelve con un override de GSettings, que cambia
el DEFAULT y deja ganar al dconf del usuario que no la quiera.
PROBADO DE VERDAD, no deducido: se compiló el directorio de esquemas del shell con el override
puesto (`glib-compile-schemas`, exit 0 y stderr vacío — que hay que mirar, porque sale 0 aunque
RECHACE un esquema) y después `gsettings get org.gnome.shell enabled-extensions` devolvió
['blur-my-shell@aunetx'].
⚠ TRAMPA ANOTADA PARA LA SEGUNDA: `enabled-extensions` es un ARRAY y el override lo escribe ENTERO.
Dos recetas con su propio override no se suman — gana la que compile última y la otra queda
instalada y muerta, sin error. Cuando llegue la segunda, la lista pasa a un solo sitio.
Y una corrección sobre la licencia, que casi firmo mal: el LICENSE es el texto del GPLv3, y su
apéndice «How to Apply» CONTIENE la frase «either version 3 … or any later version» siempre, así
que grepearla ahí no distingue -only de -or-later (la trampa que la memoria del repo ya tenía
escrita). La evidencia buena es el README del commit pineado: «This program is distributed under
the terms of the GNU General Public License, version 3 or later». El árbol JS no lleva cabecera de
licencia en ningún fichero.
Va de RAÍZ en escritorio-gnome por la lección de `foot` que targets.toml ya aprendió tres veces.
El perfil queda 183/183 listo con 25 raíces, deuda 0.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV
|
||
|
|
248fa8e991 |
desktop-file-utils: cerrar el «abrir con»
update-desktop-database construye la caché MIME→APLICACIÓN (mimeinfo.cache). Comprobado antes de escribir la receta: NINGÚN artefacto del store lo traía, así que esa caché no se construía nunca. Con shared-mime-info (cerrado ayer) el escritorio sabe QUÉ TIPO es un fichero; sin ésta seguía sin saber CON QUÉ abrirlo — el menú «abrir con» vacío y sin aplicación por defecto. Misma familia que aquel hueco: falla en RUNTIME, no en build. gnome-shell, libadwaita y mutter lo piden en nixpkgs y acá construyen tan contentos sin él, así que ninguna métrica de deuda puede verlo. Prueba del consumidor con 14 .desktop REALES de okular/dolphin/kate/konsole/spectacle: mimeinfo.cache de 2.368 bytes · 47 asociaciones application/pdf=okularApplication_pdf.desktop text/plain=okularApplication_txt.desktop;org.kde.kate.desktop;org.kde.kwrite.desktop Los 4 binarios estáticos con 0 NEEDED (--prefer-static desde el principio, no tras el error). REPRODUCE bit a bit · tarball en el mirror. Va como RAÍZ en los CUATRO escritorios y no en , y la razón importa: escritorio-kde y escritorio-gnome comparten CERO raíces con base y bash NO está en su clausura. escritorio-mirada documenta que no hereda «a propósito, es slim»; esos dos no dicen nada, y sway sí hereda cli. Declararla en los cuatro funciona con cualquiera de las dos lecturas. La asimetría en sí queda para decidir aparte: hoy «KDE 302/302» significa «la capa de escritorio está completa», no «la imagen está completa». Triaje: huecos 2 → 1 (queda ). Clausuras 303/182/160/205, todas selladas. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014QPJteswQvP1L7zSBrQQe2 |
||
|
|
1da6851775 |
bash-completion: cerrar el hueco del tabulador
bash es RAÍZ del perfil base —el shell de la distro— y cinco recetas repartidas en cuatro colas (appstream, colord, gnome-shell, modemmanager, swayimg) instalan completados en el directorio que este paquete define. Sin él NO los instalan y siguen adelante: nada falla, nadie se entera, y el usuario se queda sin tabulador en las herramientas del sistema. Hueco de RUNTIME puro, de los que ninguna métrica de deuda puede ver — por eso hizo falta la frontera para encontrarlo. Va al CORPUS y como raíz de , no a una cola: lo alcanzan las cuatro imágenes de escritorio y la CLI, y una receta resuelve sibling-first y después el catálogo padre, nunca una cola hermana. Y entra como RAÍZ porque nadie lo alcanza por deps de build: los consumidores sólo preguntan por su .pc. Verificado como consumidor, las dos mitades: pkgconf --variable=completionsdir bash-completion → /usr/share/bash-completion/completions el marco carga 131 completados y complete devuelve regla · 522 ficheros instalados REPRODUCE bit a bit · tarball en el mirror Triaje: los huecos vuelven a CERO. base pasa de 26 a 27 raíces, clausura 58/58 sellada. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014QPJteswQvP1L7zSBrQQe2 |
||
|
|
4f9ee5430f |
dunst: sway ya atiende las notificaciones, y hay un píxel que lo prueba
Era la única mitad que quedaba abierta del mapa del §6.10: KDE atiende
`org.freedesktop.Notifications` con plasma-workspace, GNOME con gnome-shell y
COSMIC con cosmic-notifications; sway no tenía a NADIE, así que una página que
pedía notificar mandaba el mensaje al bus y se lo comía el silencio.
`dunst` 1.12.2 y no `mako` —que es el de la casa wlroots— por dos razones
medidas: el nombre `mako` YA ESTÁ OCUPADO en el corpus por el motor de plantillas
de mesa, y el cierre del perfil incluye las herramientas de build, así que la
imagen terminaría con dos artefactos homónimos peleando por las mismas rutas; y
mako habla D-Bus por sd-bus, que en musl es una receta nueva (basu), mientras
dunst habla por GDBus, que ya está sellado. Cero dependencias nuevas.
DOS GUARDIANES EN LA PROPIA RECETA, porque las dos cosas que importan no se ven
en que compile:
- `X11=0` es una intención; el hecho se lee en el ELF. dunst compila los dos
backends por defecto y su config.mk avisa: sin wayland «forzará xwayland». En
esta distro eso sella un binario que arranca, toma el bus y NO PINTA NUNCA. La
fase exige `libwayland-client` entre los NEEDED y prohíbe `libX11`.
- El `.service` de D-Bus es la mitad que importa: sin systemd, al daemon lo
levanta EL BUS. Se comprueba que reclame `org.freedesktop.Notifications` y se
le saca la línea `SystemdService=`, que acá no puede significar nada.
Y `DUNSTIFY=0`: entró primero en 1 y el binario **segfaultea hasta en
`--help`** (rc=139 en las tres pruebas). La causa es el cuadro que este repo ya
tiene escrito: nuestro `libnotify.so.4` es compartido y arrastra la cadena glib
`.so`, mientras esta cola enlaza la GLib estática ⇒ dos copias de GObject en un
proceso. No se pierde nada: el emisor de la casa es `notify-send`, que viene
DENTRO del artefacto de libnotify y ya está en las cuatro imágenes. Hay un
guardián que frena el día que alguien vuelva a poner DUNSTIFY=1 sin leer.
LA PRUEBA, en `scripts/wlr/dunst-headless.sh`: sway headless + bus de sesión +
`notify-send`, y NADIE lanza dunst a mano — lo activa el bus por el .service,
que es el mismo camino de una página web en atuq. La evidencia es un diff de
píxeles antes/después acotado al cuadrante donde dunst dibuja, porque un log en
verde es compatible con una pantalla vacía y el color no distingue (swaynag pinta
su barra de error en el mismo rojo).
positivo 14832 píxeles cambiados · servidor: dunst knopwob 1.12.2 1.2
control 0 píxeles · ServiceUnknown · notify-send rc=1
El control negativo borra el .service dentro del overlay temporal y EXIGE que no
se dibuje nada: probado en los dos sentidos.
Captura en docs/evidencia/dunst-sway-notificacion-2026-09-07.png.
Vigía de sonames: escritorio-sway 203 → 204 nodos, 512 sonames, 0 sin proveedor.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
|
||
|
|
07b9b31454 |
targets: libnotify en los cuatro escritorios que llevan atuq
Sin esta línea la receta queda sellada y en NINGUNA imagen — la lección de `foot`
que este mismo fichero tiene escrita cuatro renglones más arriba de `atuq`, y que
la clausura no puede ver porque mide lo declarado.
Va en los cuatro perfiles que ya llevan el navegador (kde, gnome, cosmic, sway) y
NO en base/cli/mirada, que no lo llevan. Verificado parseando el TOML y no
leyéndolo: libnotify=1 en esos cuatro, 0 en los otros tres.
Medición al lado, con el vigía de sonames antes y después:
kde 306 -> 307 nodos 2083 -> 2086 sonames 0 sin proveedor
gnome 190 -> 191 657 -> 660 0
cosmic 161 -> 162 498 -> 501 0
sway 201 -> 202 509 -> 512 0
mirada 41 -> 41 231 -> 231 0 (no lleva atuq)
+1 nodo exacto por perfil, que es lo que se agregó, y el que no lo lleva no se
movió. Si hubiera arrastrado algo sin querer, el delta no sería 1.
Y una comprobación que el mapa de §6.10 no da y que es la que decide si el
`dlopen` va a funcionar: que resuelvan las deps de la PROPIA librería. Un dlopen
falla en silencio igual si libnotify está pero su gdk-pixbuf no. Se lo pregunté
al loader musl, no a mí:
ld-musl --list /usr/lib/libnotify.so.4
libgdk_pixbuf-2.0.so.0 => /usr/lib/... libgobject-2.0.so.0 => /usr/lib/...
libglib-2.0.so.0 => /usr/lib/... libgio-2.0.so.0 => /usr/lib/...
libc.so => /lib/ld-musl-x86_64.so.1
Toda la cadena cae dentro del rootfs salvo libc.so, que es la excepción del lab
ya documentada.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgNtJEFetMYbXax5dVUjZZ
|
||
|
|
3a83d2eda4 |
gnome: xz-shared — sin ella NINGUNA app de la imagen carga, no una
`scripts/vigia-sonames.py` sobre los cinco perfiles marcaba `liblzma.so.5` en GNOME pedido por `libadwaita` y por `python3`. La columna de QUIÉN lo pide es la que decide, y acá decidió fuerte: mirando el artefacto sellado, el `NEEDED` no lo lleva un binario suelto sino **`usr/lib/libadwaita-1.so.0`**, que es la librería contra la que enlaza toda app GTK4/GNOME de la imagen. Sin proveedor del SONAME el loader falla en TODAS, no en una. Misma fuga de siempre —el `xz` canónico del corpus es `.a`, así que el rootfs lo resolvía contra el sysroot Alpine DEL LAB— y por eso invisible al store: el lab no entra en `hash_inputs`. La receta `-shared` existía sólo en `incoming-kde`. Se promueve al corpus con el mismo procedimiento que `bzip2-shared` el 2026-09-03, y comprobando lo mismo antes de creerlo: el hash es idéntico desde las dos rutas (b3:174289d5) ⇒ un solo artefacto y cero rebuilds. Si hubiera diferido, sería otra receta y no valdría la promoción. Va sólo en GNOME: en cosmic y sway ese soname lo pide únicamente `python3`, que es herramienta de build y no viaja en la imagen. GNOME pasa de 3 sonames sin proveedor a 2. De los dos que quedan, `libperl.so` es de `perl` (build), y `libreadline.so.8` lo pide `usr/bin/sqlite3` —el shell de la CLI, NO `libsqlite3.so.0`—, así que las apps están bien y lo roto es el comando. Queda anotado como verruga, no como rotura. |
||
|
|
92d47cfe1d |
mirada: las tres -shared que faltaban — el USB resolvía expat/zlib/libffi contra el lab
`scripts/vigia-sonames.py` sobre los cinco perfiles: `escritorio-mirada` era el único que aún tenía la fuga al sysroot del lab en componentes de RUNTIME. `mesa-swrast` y `wayland` salen con `NEEDED libexpat.so.1`, `libz.so.1` y `libffi.so.8`; las recetas canónicas de expat, zlib y libffi son `--disable-shared`; ningún artefacto del cierre publicaba esos SONAME. O sea que el rootfs los resolvía contra el Alpine DEL LAB y el USB sólo arrancaba donde hubiera Alpine debajo — que es peor que una dep faltante, porque el lab no entra en `hash_inputs` y el store no puede ni notarlo. Es la misma fuga que los otros cuatro perfiles cerraron el 2026-09-03. mirada quedó fuera porque su lista nació como copia literal del PKGS de `mirada-usb.sh` y nadie la revisó desde entonces. La dirección hoy está invertida —el script deriva su PKGS de `targets.py escritorio-mirada`— así que arreglarlo acá arregla el USB, y no hay dos listas que puedan divergir. Actualicé el comentario, que seguía diciendo «lift verbatim». Las tres viven en `corpus`, la misma cola del perfil, y ya están selladas ⇒ CERO rebuilds: sólo entran a la clausura. NO sumo `bzip2-shared` ni `ncurses-shared`, que sí llevan los otros perfiles: acá esos sonames los pide únicamente `python3`, que es herramienta de build y no viaja en la imagen. El vigía imprime siempre QUIÉN pide cada soname justamente para poder separar eso; sin esa columna su informe no se puede triar y uno acaba tapando ruido. Verificado: mirada pasa de 10 sonames sin proveedor a 7, y los 7 que quedan son todos de `python3` y `perl`. |
||
|
|
b606bf988b |
targets.toml: atuq entra en los cuatro escritorios
Hasta ahora atuq estaba sellado y NO estaba en ninguna imagen. Es exactamente la lección que este fichero ya aprendió con `foot` en el perfil de sway: una receta sellada que ningún perfil declara no la lleva nadie, y la métrica de clausura no lo puede ver porque mide lo DECLARADO. Va en los cuatro por el mismo motivo que `mpv` y desde el mismo sitio: vive en el CORPUS, y una receta resuelve sibling-first y después el catálogo padre, así que las cuatro colas lo alcanzan. Arrastra la cadena GTK3 en sus variantes `-shared`, y el comentario lo dice: con las estáticas, libgtk-3 y libgdk-3 se llevaban cada una su copia de pango/cairo y el navegador no llegaba a pintar. NOTA sobre la métrica: el grafo del hub sólo reporta base/cli/escritorio-mirada — los cuatro escritorios no aparecen en `by_profile` porque sus raíces viven en las colas. Es previo a este cambio y no lo introduce; queda anotado porque significa que esta declaración NO se ve todavía en `build-state.json`. |
||
|
|
e684c26e52 |
targets: obs-studio entra al perfil escritorio-kde (274/274)
Una receta sellada que ningún perfil declara NO ESTÁ EN NINGUNA IMAGEN, y la métrica de clausura no lo puede ver porque mide lo declarado — la lección que este fichero ya aprendió con foot en sway y con las 22 apps KDE. OBS estaba sellado y en ninguna imagen. Entra SÓLO en escritorio-kde, y no por preferencia: su frontend es Qt6 y las 13 recetas Qt viven sólo en esa cola. Verificado con `yupana perfiles obs-studio`. El perfil pasa de 263/263 a 274/274 — arrastra 11 nodos a la clausura, todos ya sellados, y el grafo sigue cerrando. De paso: el aviso de los «tres huecos de mpv» estaba repetido en los CUATRO perfiles de escritorio y hoy quedó vencido en los cuatro (ALSA/PipeWire, Lua/OSC y vaapi están cerrados). Corregidos los cuatro; el assert de count==1 fue lo que destapó que no era uno solo. |
||
|
|
8c4e1a6b24 |
cursores: cosmic y sway corrían con el puntero INVISIBLE — receta propia, y el vigía medía mal
`vigia-imagen.py` daba ✗ en cursores en DOS de los cuatro escritorios. No es cosmético: con el cursor por software —obligatorio en virtio-gpu y en todo render por CPU— el compositor dibuja la imagen que le da el TEMA, y sin tema el ratón se mueve invisible. cosmic llegó a 43/43 y sway a 173/173 así, porque un tema de cursor no es dep de build de nadie: sólo entra si se DECLARA. `adwaita-cursors` (corpus, 48.1, data-only): del mismo tarball que `adwaita-icon-theme` pero SÓLO `Adwaita/cursors/` — 39 ficheros y 14 MB, sin un icono. Promover el tema entero habría regalado a sway y a cosmic los iconos de GNOME, que está anotado como decisión pendiente y no como olvido. Las dos cosas que el tarball no trae y la receta fabrica: · los nombres X11 heredados (`left_ptr`, `xterm`, `watch`, `hand2`…) son enlaces que genera el `meson.build` de upstream. El mapa se PARSEA de ahí, no se copia: copiado envejece en silencio. Si el origen de un enlace no existe, la fase falla — upstream pone un `files()` como aserción. · `/usr/share/icons/default/index.theme` con `Inherits=Adwaita`. Sin `XCURSOR_THEME` en el entorno, libXcursor y wlroots buscan el tema llamado literalmente `default`; sin él no hay puntero AUNQUE Adwaita esté instalado. Es el eslabón que hace que ande sin configuración. ⚠ no declarar esta receta junto a `adwaita-icon-theme`: chocan en `Adwaita/cursors/*`. Y el vigía estaba midiendo el invariante de al lado: exigía `index.theme` en el directorio para contar un tema, que es correcto para ICONOS —la búsqueda XDG recorre `Directories=`— y falso para CURSORES, porque libXcursor abre `<tema>/cursors/<nombre>` directo y el índice sólo hace falta para seguir un `Inherits=`. Con la receta instalada seguía diciendo «NINGÚN tema de cursor». De paso queda anotado en `targets.toml` que el comentario de cosmic decía «sin ellos arranca sin puntero» sobre `cosmic-icons`, que no trae cursores: describía una protección que no existía. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGyBj4QyeCZiwLMkP9GJp6 |
||
|
|
d69c437051 |
escritorios: KDE y GNOME corrían SIN tema de iconos — el panel vacío no era el panel
Medido sobre los artefactos del cierre de cada imagen, contando qué `usr/share/icons/*/`
trae un `index.theme` (que es lo que convierte un directorio en un tema):
escritorio-kde sólo Breeze_Light y breeze_cursors — que son CURSORES
escritorio-gnome CERO, ni siquiera hicolor
escritorio-cosmic Cosmic + hicolor <- el único que estaba bien
escritorio-sway CERO
Las dos capturas de QEMU de hoy ya lo mostraban y se leyeron como «arranca»:
- `plasma6-qemu-2026-09-03-kactivitymanagerd.png`: panel con el reloj y nada más. Los `.so`
de los applets estaban TODOS instalados; lo que faltaba era qué pintar. `kickoff`,
`systemtray`, `showdesktop` y `trash` SON un icono, así que sin tema quedan de ancho cero
— el reloj se ve porque dibuja TEXTO. El log lo decía en una línea:
`kf.iconthemes: Icon theme "breeze" not found.`
- `gnome-shell-qemu-2026-09-03-libs-compartidas.png`: barra superior con la fecha y el pill
de espacios, y la DERECHA vacía — red, volumen y batería son iconos.
`breeze` (ya declarado) es el tema de WIDGETS y CURSORES; los ~14.000 iconos viven en
`breeze-icons`, receta aparte, sellada desde siempre y declarada por NADIE. Igual
`adwaita-icon-theme` en GNOME, que además trae los XCursor (su cabecera dice por qué:
sin tema de cursor no hay puntero visible con el cursor por software de virtio-gpu).
Es la figura de las fuentes y de `foot` otra vez: data de RUNTIME, ninguna arista de BUILD
la alcanza, y la métrica de clausura no la echa de menos porque mide lo declarado.
`hicolor-icon-theme` —el fallback obligatorio de freedesktop— se promueve al corpus para que
lo alcancen las cuatro imágenes: hash IDÉNTICO desde la cola y desde el corpus
(b3:98ad44a5), y sus 4 consumidores (adwaita-icon-theme, cosmic-icons, cosmic-app-library,
cosmic-launcher) miden el mismo hash antes y después ⇒ un solo artefacto, CERO rebuilds.
Cierre tras declararlos, todo sellado y sin deuda:
kde 263/263 (+2) · gnome 158/158 (+2) · sway 173/173 (+1) · cosmic 133/133 (igual)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YGXe6mShYYw6V8ea1dQ13u
|
||
|
|
8df2559556 |
escritorio-kde: declarar kactivitymanagerd (plasmashell ABORTA sin él)
Arrancando la imagen KDE en QEMU: kwin toma el DRM, expone wayland-0, plasmashell arranca… y la pantalla se queda EN NEGRO con los dos procesos vivos. El serial repetía 'STATUS: kwin=1 plasmashell=1' durante minutos, que se lee como salud. La causa estaba en el log de Qt, no en el estado de procesos: kde.plasmashell: Aborting shell load: The activity manager daemon (kactivitymanagerd) is not running. No sale de la clausura de las 7 raíces porque NO es dep de build de nada: plasmashell lo pide por D-Bus al arrancar. scripts/kde/plasma-start-qemu.sh ya lo lanzaba (l.188); lo que faltaba era que el paquete entrara al rootfs. Ya estaba sellado — declararlo no cuesta un build. Misma figura que las raíces de runtime de GNOME (imports.gi.*): lo que se invoca por bus es invisible al grafo de deps. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0164nrZWZc78Mr2srrsnoM8U |
||
|
|
6c37df6fa4 |
ncurses-shared: cierra la fuga de pw-top al lab, que llevaba desde agosto
recipes/pipewire.toml:85-88 afirma que quitando la dep de ncurses «meson saltea pw-top
y el resto construye». ES FALSO, y llevaba así desde al menos el 2026-08-29. Quitar la
dep no desactivó nada: el guardián de upstream es `if ncurses_dep.found()` y meson lo
encontró igual, en el sysroot Alpine DEL LAB. pw-top se construye, se sella y sale con
NEEDED libncursesw.so.6, que ningún artefacto del cierre publica porque el ncurses del
catálogo es --without-shared. Una herramienta rota dentro de las CUATRO imágenes,
invisible para el store porque el lab no entra en hash_inputs.
LA LECCIÓN, que es más grande que pw-top: QUITAR UNA DEP DE [deps] NO APAGA LA FUNCIÓN.
Cambia de dónde sale. Para apagarla de verdad hay que decírselo a la perilla del
proyecto; si no la hay, la dep tiene que estar declarada y satisfecha desde el store.
POR QUÉ UNA RECETA NUEVA Y NO RE-SELLAR PIPEWIRE. El arreglo limpio es declarar la dep y
reconstruir; cuesta 10 dependientes directos y 7 sellados que caen a deuda, entre ellos
xdg-desktop-portal-cosmic y cosmic-settings-daemon, que son Rust —el portal murió por
OOM tres veces y el que selló tardó 48 min con pico de 5,4 GiB de swap—. No se paga eso
hoy por una herramienta de diagnóstico, y menos con otro agente compilando en la misma
máquina. Lo que cruza acá es un SONAME, y para eso el repo ya tiene regla escrita:
enlazar contra una variante y CORRER contra otra es legítimo cuando lo que cruza es un
SONAME. Cero rebuilds.
⚠ QUEDA DEUDA ANOTADA: pipewire sigue enlazando contra el lab en BUILD. El día que se
re-selle por cualquier otro motivo hay que declarar ncurses-shared en sus [deps] y
corregir el comentario mentiroso. Esto tapa el síntoma en runtime, no la causa.
DOS COSAS QUE COSTARON, y están escritas en la receta:
- `-stats` es un flag de GNU ld que lld rechaza, y viene DENTRO del token
`-Wl,-soname,...,-stats,-lc` que arma el configure de ncurses. No se puede pasar por
LDFLAGS ni hay perilla MK_SHARED_LIB, y el patrón .zwrap de poppler tampoco sirve tal
cual porque filtra argumentos completos y acá hay que reescribir uno.
- Quitando `-stats`, el siguiente en caer es el `-lc` del mismo token. El driver de zig
valida lo que va dentro de `-Wl,` y no deja pasar ninguno de los dos; libc la liga él
solo, así que la cola `,-stats,-lc` se va entera. Se quita del Makefile GENERADO, con
un grep previo que FALLA si el configure deja de emitirlo (para no seguir en silencio).
EVIDENCIA, con control discriminante:
sin ncurses-shared -> Error relocating /lib/libncursesw.so.6: __vfprintf_chk: symbol
not found (símbolo de _FORTIFY_SOURCE de glibc que musl no
tiene: la fuga al lab, reproducida)
con ncurses-shared -> carga y corre; sólo falla en el runtime de PipeWire por no haber
demonio ni plugins SPA en la raíz de prueba
vigia-sonames en los cuatro perfiles: libncursesw.so.6 Y libpanelw.so.6 desaparecen.
Lo que queda son herramientas de build (go, python3, perl) más tres de RUNTIME que NO
son de este cambio y quedan anotadas: spidermonkey pide libgcc_s/libstdc++ en gnome,
libadwaita pide liblzma, y sqlite-shared pide libreadline.
Grafos: 820/820, 999/999, 883/883, 856/856, 833/833.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB2iEmxeLZgzqNfhWChrLo
|
||
|
|
68274d6c2a |
kde: declarar las 22 apps que ya estaban selladas y en ninguna imagen
CERO builds: las 22 llevaban tiempo selladas en incoming-kde y no las declaraba nadie, así que sólo entran a la clausura. El perfil pasa de 200 a 259 nodos y sigue 998/998. POR QUÉ FALTABAN, que no es lo mismo que «se decidió que no fueran». Las 7 raíces del perfil son el set MÍNIMO del arranque en metal, tomado de docs/HANDOFF-noche-kde-2026-07-14.md:135. Ese mismo documento nombra lo que faltaba como el trabajo siguiente: «Capa 2 KF6 / apps núcleo: importar+construir konsole/dolphin/kate vía granja (extiende el escritorio usable)». Se construyeron y se sellaron; lo que nunca ocurrió fue DECLARARLAS. La métrica no lo podía ver porque mide lo declarado — la misma figura que dejó a sway en 121/121 sin emulador de terminal. Sin esto la imagen era compositor + shell + tema y nada más: sin terminal no se puede salir de un fallo, y sin plasma-nm/plasma-pa/powerdevil no hay red, ni audio, ni gestión de energía. Entran en tres grupos: - plomería de Plasma: plasma-desktop, plasma-nm, plasma-pa, powerdevil, kscreen, systemsettings, kinfocenter, kmenuedit, kwalletmanager - núcleo usable: konsole, dolphin, kate, okular, ark, spectacle, gwenview - accesorios ya construidos: kcalc, kfind, filelight, kcharselect, kruler, kdf Cómo se encontró, para repetirlo: listar los (cola,nombre) cuyo artefacto instala un usr/share/applications/*.desktop y que yupana.membresia() no asigna a ningún perfil. Salieron 23; 22 eran de acá. VERIFICADO con scripts/vigia-sonames.py, que es donde 22 componentes de RUNTIME nuevos podrían filtrar al lab: escritorio-kde queda con 1254 sonames provistos y 5 sin proveedor, y los 5 son ruido conocido —cmake, python3 y perl son herramientas de build que la imagen no instala, más el pw-top de pipewire que ya estaba anotado—. Ninguna app nueva aporta un NEEDED huérfano. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB2iEmxeLZgzqNfhWChrLo |
||
|
|
1f415802f1 |
apps: GNOME tenía imagen sin terminal ni editor — arreglado con CERO recetas; y el montón A queda agotado
DOS COSAS, y la segunda no estaba en el plan.
1. EL MONTÓN A SE AGOTÓ DE COSAS SIN MURO. Re-triadas con scripts/provee.py las tres
candidatas que la tabla daba como baratas o dudosas; las tres son muro, y ninguna por
el motivo que decía la tabla:
- imv → no hay proveedor de OpenGL de escritorio (ya documentado; la sustituyó swayimg)
- mupdf → la nota decía «el X11 es del visor mupdf-x11; mupdf-gl no». Falso: platform/gl/
va sobre GLUT + OpenGL de escritorio, el mismo muro que imv. mupdf NO TIENE
visor Wayland: sus dos UIs son X11 o GL. El motor no tiene muros pero no hace
falta, el PDF ya lo da poppler.
- inkscape→ era GTK3 encubierto, como la tabla sospechaba: INKSCAPE_1_4_4 pide
gtkmm-3.0>=3.24 y gtk+-3.0>=3.24. master SÍ migró a GTK4 (gtk4>=4.14) pero
está sin publicar, y además el catálogo no tiene NI UN binding C++:
provee.py da ✗ en sigc++-2.0, glibmm-2.4, cairomm-1.0, pangomm-1.4 y gtkmm.
⇒ todo lo que queda está detrás de UNA de cuatro decisiones (GTK3 / Qt6-en-el-corpus /
GL de escritorio / X11), no de trabajo de recetas.
2. LO QUE SÍ ESTABA ROTO, Y NO ERA FALTA DE RECETAS. Cruzando la membresía de los cuatro
perfiles contra emuladores de terminal, editores y gestores de ficheros:
escritorio-cosmic term=cosmic-term editor=cosmic-edit archivos=cosmic-files
escritorio-sway term=foot editor=vim archivos=—
escritorio-gnome term=— editor=— archivos=—
escritorio-kde term=— editor=— archivos=—
Es la lección de foot otra vez y a mayor escala: sway llegó a 121/121 SIN emulador de
terminal porque nadie lo declaraba, y la métrica de clausura no lo ve porque mide lo
DECLARADO. Un escritorio sin terminal no es uno incompleto: es uno del que no se puede
salir cuando algo falla.
GNOME arreglado con CERO recetas nuevas: foot y helix viven en el CORPUS, ya estaban
selladas y no las declaraba NINGÚN perfil. foot es Wayland puro sobre xdg-shell (no
pide protocolos de wlroots) así que corre bajo mutter igual que bajo sway; su único
NEEDED es libc.so. helix es TUI y corre dentro de la terminal.
⚠ KDE NO SE TOCA, y es un hallazgo aparte que hay que acordar con ese frente: tiene 22
apps con .desktop selladas en su cola y sin declarar (konsole, dolphin, kate, okular,
spectacle, ark, gwenview, systemsettings, kcalc…) y además le faltan plasma-desktop,
plasma-nm, plasma-pa, powerdevil y kscreen — o sea red, audio, energía y pantalla. Su
perfil son 13 raíces de ARRANQUE EN METAL, no la imagen de escritorio completa (ésa se
hidrató aparte, por hash). No cuesta un build; cuesta acordarlo.
Nota de método: build-state.py invoca `hammer hash` ~1000 veces y el otro agente
recompiló el binario a mitad del barrido ⇒ kjobwidgets salió `unhashable` y KDE reportó
997/998. No era la receta: era la carrera. Re-corrido da 998/998.
Los cinco grafos: 819/819, 998/998, 882/882, 855/855, 832/832, cero nodos `wanted`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB2iEmxeLZgzqNfhWChrLo
|
||
|
|
cab9439644 |
fuentes: dejavu al corpus (arregla un wanted que dejé), KDE con fuente, y la nerd de monospace
TRES COSAS, y la primera es un bug mío de hace un rato.
1. dejavu-fonts PROMOVIDA AL CORPUS — arregla un nodo `wanted` que introduje.
Al añadirla de raíz a escritorio-gnome quedó como `wanted`: la receta existía en
incoming-{kde,wlr,cosmic} y NO en incoming-gnome ni en el corpus, y una receta no
alcanza una cola hermana. O sea que la raíz no resolvía a nada y GNOME seguía sin
fuentes. `wanted` NO es `debt`, así que drenaje.json seguía diciendo deuda=0 y el
hueco no salía por ninguna métrica — se vio mirando el nodo en build-state-gnome.json.
Promoción gratis: las tres copias sellaban el mismo hash (b3:5b3a5df3) y sus deps
están vacías. Las tres se jubilan. Ahora los cuatro perfiles resuelven a corpus y los
cinco grafos quedan con CERO nodos `wanted`.
2. dejavu-fonts en escritorio-kde, como se pidió. Tenía UNA fuente y de rebote, dentro
de qtbase — apoyarse en un detalle de empaquetado de Qt deja sin nada a todo lo que
no es Qt.
3. dejavu-fonts-nerd 3.5.1 en los CUATRO perfiles: DejaVu Sans Mono parcheada con Nerd
Fonts. Lo que se midió antes de escribirla, porque cambia la forma de hacerla:
- SE AÑADE, NO REEMPLAZA, y no es prudencia: la licencia de Bitstream Vera exige que
una fuente modificada se RENOMBRE. Por eso upstream la llama «DejaVuSansM Nerd Font
Mono» — leído del name de la TTF, no supuesto. Una fuente nerdeada NO puede
responder al nombre de la original; sustituirla rompería a todo lo que pide «DejaVu
Sans Mono», empezando por foot, que ya murió una vez así.
- SÓLO LA MONOESPACIADA: upstream no publica DejaVu Sans ni Serif parcheadas, y en una
fuente de interfaz proporcional esos glifos no los pide nadie. «Todas nerdeadas» no
se puede cumplir al pie de la letra.
- SÓLO UNA DE LAS TRES VARIANTES: NerdFont / NerdFontMono / NerdFontPropo son los
mismos glifos con distinto avance. Las doce pesan 32 MB; las cuatro de Mono, 11 MB.
Se quedan las Mono, que son las correctas para una rejilla de terminal. Referencia:
la familia DejaVu ENTERA son 9,8 MB.
⚠ EL NÚMERO DEL .conf ESTÁ MEDIDO, NO ELEGIDO. fontconfig carga conf.d en orden
alfabético y el <prefer> que llega antes gana. Con 60-nerd-monospace.conf —que ordena
después de 60-latin.conf, que ya prefiere Noto/DejaVu/Inconsolata— `fc-match
monospace` seguía dando DejaVu Sans Mono: la conf estaba INSTALADA Y ERA INERTE, el
mismo modo de fallar que el plugin ALSA de PipeWire. A 59- gana. 45-latin.conf también
menciona monospace pero usa <default>, no <prefer>, así que no compite.
⚠ Y strip_components = 0, que tampoco es cosmético: el tarball de nerd-fonts es PLANO
y con el default de 1 el árbol de fuentes queda VACÍO — el build no se detiene ahí,
sigue y falla más tarde en el cp, con un mensaje que no menciona la extracción.
⚠ LA LICENCIA NECESITA UNA PASADA HUMANA antes de `hammer pack`: el LICENSE.txt del
tarball cubre sólo DejaVu/Bitstream Vera y no menciona los ~10 conjuntos de iconos
importados, que tienen licencias propias (Font Awesome CC-BY-4.0, Octicons MIT,
Material OFL, Powerline MIT…). El campo dice lo DOCUMENTADO, no el conjunto real; no
se inventa una cadena SPDX. Anotado en la receta.
EVIDENCIA, contra el conf.d REAL del artefacto de fontconfig:
monospace -> DejaVuSansM Nerd Font Mono (los iconos llegan)
"DejaVu Sans Mono" -> DejaVu Sans Mono (intacta)
sans-serif -> DejaVu Sans (sin tocar)
serif -> DejaVu Serif (sin tocar)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB2iEmxeLZgzqNfhWChrLo
|
||
|
|
da0186d250 |
apps: zathura + backend de PDF, en gnome/cosmic/sway — y tres defectos de imagen que salieron
La cadena: 5 recetas nuevas (girara, xxhash, poppler-glib, zathura,
zathura-pdf-poppler) y 5 promociones GRATIS al corpus (json-glib, lcms2, glib-shared,
pcre2-shared, bzip2-shared — todas con hash idéntico desde la cola y desde el corpus,
medido con hammer hash antes de mover nada).
NO va en escritorio-kde, y está escrito en tres sitios para que no se liste por
descuido: su backend necesita una poppler con ENABLE_GLIB=ON, y esa imagen ya trae la
de Qt6 vía okular. Las dos instalan /usr/lib/libpoppler.so.146 y son artefactos
DISTINTOS ⇒ hidratar las dos pone dos ficheros en la misma ruta. La salida no sería
listarla igual, sería jubilar una de las dos popplers.
LAS TRES COSAS QUE SE ROMPIERON, que valen más que las recetas:
1. -DENABLE_GLIB=ON SE APAGA SOLO Y EL BUILD SALE OK. CMakeLists:260 hace
`if(NOT CAIRO_FOUND) set(ENABLE_GLIB OFF)`: no falla, obedece distinto. El primer
artefacto selló sin nada de glib, exit 0, sin aviso. La causa era una línea que este
repo ya tiene como patrón: `.pc Requires` → `[deps].build`. cairo.pc pide pixman-1 y
pixman no estaba declarado ⇒ `pkg-config --exists cairo` falso ⇒ CAIRO_FOUND falso.
Un .pc que falta a tres saltos apaga una FUNCIÓN, no una librería. La receta ahora
comprueba en install que poppler-glib.pc y libpoppler-glib.so existan.
2. DOS COPIAS ESTÁTICAS DE GObject EN UN PROCESO. Con la glib estática del corpus todo
sella y zathura arranca — y al dlopen del plugin escupe «cannot register existing
type 'gchar'» y no abre nada. No son dos ficheros en una ruta: son dos copias del
sistema de tipos dentro del mismo proceso, una en el ejecutable y otra dentro de
libpoppler-glib.so. Un .a NO duplica (sólo tiene símbolos sin definir, se resuelven
al ligar); el problema aparece sólo cuando dos objetos enlazados por separado meten
cada uno la suya. Arreglo: las TRES recetas de la cadena declaran glib-shared.
3. FUGA AL LAB, cazada por scripts/vigia-sonames.py: zathura selló con
NEEDED libsqlite3.so.0, un SONAME que ningún artefacto del cierre publica — meson
había resuelto dependency('sqlite3') contra el sysroot Alpine DEL LAB. Y declarar la
dep NO alcanzó: el .pc da un -lsqlite3 pelado y el linker prefiere la .so del lab
sobre la .a del store. Lo arregla -Dprefer_static=true. Medido: los NEEDED bajaron de
DIEZ a CUATRO y los cuatro los publica el cierre.
DOS DEFECTOS DE IMAGEN PREEXISTENTES que salieron de paso:
- libbz2.so.1 no lo publicaba nadie y freetype-shared lo pide, en los TRES perfiles.
Se vio de verdad al probar el visor (el plugin no hacía dlopen). bzip2-shared existía
sólo en incoming-kde; promovida y puesta de raíz junto a expat-shared/libffi-shared.
- escritorio-gnome NO TRAÍA NI UN FICHERO DE FUENTE. Contado sobre los artefactos del
cierre: gnome=0, sway=22, kde=1 (una de rebote dentro de qtbase). Tenía fontconfig,
que es el MOTOR que busca fuentes, no una fuente. Un PDF con base-14 se ve VACÍO y sin
error. Añadida dejavu-fonts, la única receta de fuentes del catálogo entero.
⚠ KDE queda igual y NO se toca acá: no lleva zathura y su árbol lo trabaja otro frente.
EVIDENCIA DE QUE ANDA, no de que sella:
- `zathura --version` con el plugin lista «(plugin) pdf-poppler (2026.07.18)» sin un
solo GObject-CRITICAL.
- poppler-render-check, receta-TESTIGO en el espíritu de gtk4-hello: arma un PDF a mano,
lo abre con poppler-glib, lo rasteriza sobre cairo y CUENTA PÍXELES — 9600 negros,
exactamente el rectángulo de 160x60. Un lienzo blanco no pasa. Su primera versión
medía el TEXTO y falló: el sandbox no tiene fuentes, que es cómo se descubrió el
hueco de arriba. El assert quedó sobre el rectángulo, que no depende de tipografía, y
el texto se informa aparte.
Los cinco grafos quedan en N/N con cero deuda.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XB2iEmxeLZgzqNfhWChrLo
|
||
|
|
d8dd779812 |
apps: swayimg 4.7 sellada, visor de imágenes en las cuatro imágenes
El plan proponía `imv` como paso 4. Al medirla apareció un muro que el método de triaje no podía ver: imv dibuja con OpenGL de función fija (glBegin/glOrtho) y esta distro NO tiene proveedor de GL de escritorio — las tres variantes de mesa van -Dglx=disabled -Dglvnd=false y no publican libGL.so ni gl.pc ni opengl.pc, y no hay receta libglvnd. La trampa fina: mesa SÍ instala GL/gl.h, así que compila entero y muere al ligar. Darlo vuelta = autorar libglvnd + rehacer mesa (radio 139). swayimg cubre el mismo caso de uso sin tocar GL (rasteriza a wl_shm) y con CERO recetas nuevas: sus deps requeridas ya estaban en el corpus. De yapa trae backend DRM (imágenes en TTY pelada, sin compositor). Pineada 4.7 y no 5.5 a propósito: desde la 5.0 luajit es obligatoria y no hay receta (la lua5.2 del OSC de mpv es otra ABI). Escribir luajit es el próximo paso barato. -Dversion=4.7 no es cosmético: su default 0.0.0 dispara un `git describe` sobre el árbol ⇒ la versión estampada en el binario dependería del checkout. Misma familia que el -Dbuild-date de mpv. Evidencia de que anda, no sólo de que sella: NEEDED = libc.so y nada más (cero glibc, cero X11, cero GL), `--version` dice 4.7 con jpeg/png/gif/webp/tiff, prueba las dos UIs (Wayland → DRM) y el bucle de decodificación rechaza lo que no es imagen. Añadida a las raíces de los cuatro perfiles (sellado ≠ instalado). Los cinco grafos quedan en N/N con cero deuda. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XB2iEmxeLZgzqNfhWChrLo |
||
|
|
39f5007cff |
wf-recorder 0.6.0: la primera app que cobra la promoción de pipewire
Sellada b3:25caa359 al primer intento. `--help` corre y los NEEDED traen `libpipewire-0.3.so.0`: graba CON audio, que era exactamente lo que anoche no se podía. Es la app que estaba bloqueada por el defecto del triaje —preguntaba «¿existe la receta?» en vez de «¿la alcanza quien la usa?»—: sus dos backends de captura de sonido vivían sólo en colas y una receta del corpus no alcanza una cola hermana. Con pipewire en el corpus, cae sola. `-Ddefault_audio_backend=pipewire` explícito y no `auto`: su meson resuelve `auto` mirando qué encontró en el sandbox (meson.build:95-101), o sea que el artefacto dependería de lo que quedó montado en el lab. Misma disciplina que en mpv y ffmpeg. ⚠ SE LISTA SÓLO EN `escritorio-sway`, y la razón es de PROTOCOLO: captura por `wlr-screencopy`, que implementan los compositores wlroots. KWin y mutter NO lo implementan —usan el portal/ScreenCast—, así que meterlo en esas dos imágenes sería enviar una herramienta que no puede funcionar ahí: deuda fantasma con forma de app. En COSMIC hay que verificar si cosmic-comp expone el protocolo antes de listarlo; no se listó a ciegas. Cubre el caso de uso principal por el que uno instala OBS, con UNA receta en vez de las 20 + Qt6 + X11 que OBS pedía. escritorio-sway 146/146. |
||
|
|
f64859bade |
qorpa export: shims generados, y la clase ajeno para que nadie los cuente mal
Paso 5 del ADR 0015, sus dos mitades. SHIMS. `hammer qorpa export <id>` genera lanzadores finos y `.desktop` en el espacio del host, desde lo DECLARADO en `[export]` — nunca todo: exportar todo haría que el `ls` de la imagen compita con el nuestro, que es la falla de Bedrock (arbitra en tiempo de exec, por heurística). Gana en tres cosas contra un FUSE: cero costo en runtime, `cat` al shim y ves qué hace, y se revoca borrándolos. Se GENERAN, no se copian. El `.desktop` se arma con lista BLANCA de claves, así que `Exec`, `TryExec`, `Path` y `DBusActivatable` quedan fuera por definición y no por enumeración — una lista negra dejaría entrar la próxima clave ejecutable que invente el estándar. El Exec original se cita en un comentario del fichero generado, para que se vea qué decía y qué no se copió. El icono se busca en la vista merged (upper primero, imagen después: si no, se perdería lo que instaló el gestor de paquetes) y se copia al host, porque un icono que el host no resuelve se ve como un cuadrito gris. Y `exported.json` registra cada fichero escrito, para que `--remove` borre EXACTAMENTE eso y no por patrón sobre el ~/.local/bin de alguien. Probado de punta a punta con un .desktop ajeno real de la imagen de Arch: el shim corre `pacman -Q` del huésped desde el host, el X-KDE-Wayland-Interfaces quedó fuera, el icono viajó, y --remove dejó 0 ficheros con la instancia intacta. CLASE `ajeno`. build-state.py inyecta los nodos declarados en el nuevo docs/state/qorpa-ajenos.toml ANTES que los `wanted`, y ese orden es la mitad del punto: un nodo que provee una imagen ajena no es una receta por escribir. Con eso el `xwayland` de escritorio-kde deja de ser deuda y pasa a contarse aparte: escritorio-kde 187/188 listo falta 1 (raíces 14, + 1 ajenas) Dos decisiones que sostienen esa cifra: los ajenos se RESTAN del denominador (si entraran, el número que se lee como "cuánto construimos" crecería solo cada vez que alguien enjaula una app), y la declaración vive en el REPO y no se lee de /var/lib/hammer — build-state.json se commitea y lo regenera el cron en dos máquinas; si la clase saliera de las instancias instaladas, cada una diría algo distinto y se pisarían en cada cosecha. Es el error que ya se cometió con sealed_remoto. Qué provee una imagen ajena es diseño; qué tenés instalado, no. Un ajeno tampoco se hashea, y no por comodidad: no tiene procedencia de fuente, así que un hash afirmaría que lo reproducimos. 2 tests nuevos (que del .desktop ajeno no sobreviva nada ejecutable; que el shim no se rompa con rutas raras). 47/47. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U5cQtQrYNjWJVXVE6aEpQ2 |
||
|
|
f9da495b8c |
vigía de sonames: KDE, GNOME y COSMIC tenían la MISMA fuga al lab que sway ya había documentado
Verificando que mpv anduviera en las cuatro imágenes salió esto: `libEGL.so.1` (mesa) pide
`libexpat.so.1` y `libwayland-client.so.0` pide `libffi.so.8`, las recetas canónicas de expat y
libffi son `--disable-shared`, y ningún artefacto del cierre publica esos SONAME ⇒ los tres rootfs
los resolvían contra el **sysroot Alpine DEL LAB**.
Es peor que una dep faltante: el lab NO entra en `hash_inputs`, así que el store no puede notarlo —
el artefacto se sella, el perfil reporta 100%, y la imagen sólo arranca en una máquina con Alpine
debajo. El perfil de sway ya tenía escrito exactamente este párrafo desde 2026-08-26; lo que faltaba
era el instrumento para ver que las otras tres estaban igual.
Arreglo (gratis, sin rebuild: las dos recetas ya estaban selladas por sway): `expat-shared` y
`libffi-shared` pasan a raíces de los tres perfiles. Van de raíces y no de `[deps]` por la misma
razón que las fuentes y el XKB — son data de RUNTIME y ninguna arista de build las alcanza.
`scripts/vigia-sonames.py` es el guardián que sale del punto ciego: recorre los NEEDED de todo el
cierre de cada imagen contra los SONAME que ese mismo cierre publica. Dos decisiones de diseño:
· keying por PAR `(cola, nombre)` vía yupana, NO por nombre — `build-state.json` colapsa los
nombres que viven en dos colas y su campo `perfiles` puede quedar vacío para una receta que sí
está en la imagen.
· imprime SIEMPRE quién pide cada soname, porque el cierre incluye herramientas de build
(python3, cmake, perl, go) que la hidratación no instala: sin esa columna el informe no se tría.
Después del arreglo, `libexpat.so.1` y `libffi.so.8` desaparecen de los tres. Lo que queda son
hallazgos REALES que no son de este commit y quedan anotados:
· kde: karchive pide libbz2.so.1 y liblzma.so.5
· gnome: spidermonkey pide libstdc++.so.6 y libgcc_s.so.1; libadwaita pide liblzma.so.5;
freetype-shared pide libbz2.so.1; sqlite-shared pide libreadline.so.8
· cosmic: llvm18 pide libgcc_s.so.1
· los tres: `pw-top` de pipewire enlaza `libncursesw.so.6` DEL LAB — y la receta afirma en un
comentario que sin la dep «meson saltea pw-top». Es falso desde al menos el 2026-08-29:
`dependency('ncursesw')` lo encuentra igual en el sysroot del lab. El comentario dice una cosa
y el binario otra.
|
||
|
|
01b86fd0fb |
targets: mpv entra como raíz de los cuatro escritorios — sellado no es instalado
Sin esto mpv quedaba sellado y en NINGUNA imagen. Es exactamente la lección que este fichero ya tiene escrita en el perfil de sway: `foot` estaba sellado en el corpus, ningún perfil lo listaba, y el escritorio daba 121/121 SIN EMULADOR DE TERMINAL. La métrica de clausura mide las raíces declaradas y no puede ver lo que falta en la declaración. Se lista en los cuatro porque mpv vive en el corpus: una receta resuelve sibling-first y después el catálogo padre, así que desde cualquiera de las cuatro colas se alcanza. Los cuatro perfiles siguen en 0 deuda y cierran con la clausura nueva ya sellada: escritorio-kde 171 → 180/180 escritorio-gnome 119 → 129/129 escritorio-cosmic 90 → 103/103 escritorio-sway 129 → 139/139 Corpus 788 → 798 recetas, 798 selladas. grafo: CIERRA | topo-sort: OK en los cinco. |
||
|
|
776692d01a |
ADR 0015 (propuesto): imágenes ajenas — el mundo glibc entra enjaulado y no entra al store
Decide la frontera antes de escribir código. Sale de una medición incómoda: el
corpus tiene cuatro escritorios que cierran y CERO navegador, ofimática,
reproductor o editor de imagen. Al partir el «qué falta» por causa, la
intersección de «sólo X11» con «compilable desde fuente en musl» es casi vacía:
casi todo lo que se pierde por Wayland ya estaba perdido por la libc. El montón
que duele son binarios ajenos que nadie va a recompilar.
Las siete decisiones:
D1 — Una imagen ajena NO es un artefacto y no vive en el store. El store promete
reconstrucción bit a bit desde fuente; un rootfs de Fedora no. Meterlo ahí
sería la misma clase de error que el artefacto vacío: algo que se lee como
garantía y no lo es. Namespace paralelo, por digest, fuera de hash_inputs.
D2 — Se cruza el borde con protocolos y nodos de dispositivo, NUNCA con
librerías. Wayland/PipeWire son protocolos; /dev/dri y /dev/ntsync son ABI
de kernel. Mesa va adentro de la imagen. Corolario: la jaula no sabe qué
libc hay adentro, y por eso resuelve el montón entero de una vez.
D3 — El manifiesto es la verdad; el `upper` del overlay es CACHÉ. Misma relación
que receta↔artefacto. De ahí se caen solas la actualización de base (se
recrea, no se rebasea), el respaldo (KB, no GB) y la poda.
D4 — Cuatro granularidades, no una. El runtime curado inmutable (tipo 2) sigue
siendo el preferido cuando alcanza: se sella. El rootfs con dnf existe
porque es justo lo que el tipo 2 no permite.
D5 — Transparencia por shims GENERADOS, no por un FUSE global. Es el poder de
Bedrock sin sus formas: cero costo en runtime, inspeccionable, revocable, y
se exporta lo declarado (Bedrock arbitra en tiempo de exec, con heurísticas).
Los nodos exportados entran al grafo con clase `ajeno` ⇒ no se pueden contar
como corpus. Bedrock no puede decirte qué tenés.
D6 — Steam ya ES un contenedor: se anida pressure-vessel adentro, que es la
configuración que Valve prueba. El bwrap anidado hay que VERIFICARLO.
D7 — Es el único lugar del sistema donde la política se escribe en vez de
derivarse. harkaq deriva `política = clausura(deps)`; una imagen ajena no
tiene clausura declarada. Excepción nombrada y acotada, por defecto vacía.
Y lo que el ADR admite que NO resuelve, escrito para no descubrirlo en producción:
el socket de Wayland es un borde de privilegio y lo pasamos crudo (screencopy y
virtual-keyboard incluidos — Flatpak pasa un proxy filtrante, nosotros no lo
tenemos); el UID mapping va a fallar primero y las piezas ya están en el corpus
(shadow instala newuidmap/newgidmap y crea /etc/subuid vacío, falta
provisionarlo); es una segunda cadena de suministro sin garantías; y hay que
acotar por escrito el claim de bit-repro o la cultura de números honestos se
erosiona sola.
Se cae gratis: `xwayland` deja de ser deuda del corpus (va DENTRO de la imagen,
que ya lo trae, y se cuelga de kwin por el socket) ⇒ el wanted de KDE se
disolvería sin escribir la receta y sin tocar Wayland-only. GIMP e Inkscape dejan
de reabrir la deuda GTK3. Y del plan de juegos: F2 (glibc+multilib desde fuente,
«una campaña entera») queda CANCELADA y F0 (flatpak+ostree al catálogo)
innecesaria.
Lo nativo no se afloja: el montón A se sigue construyendo, en orden mpv → OBS →
Firefox.
Toca sólo documentación: el ADR nuevo, la nota de generalización en
plan-jaula-juegos.md §Capa 3, y el comentario en targets.toml que evita que
alguien escriba la receta de xwayland sin ver la decisión pendiente. Cero recetas
tocadas, cero re-hasheo: --kde sigue en 978 sealed / 1 wanted / 171-171, CIERRA.
|
||
|
|
0ac6fbfb5b |
targets: el perfil GNOME ya no reclama las 3 recetas aparcadas por diseño
`targets.toml` declaraba gnome-session, gnome-settings-daemon y gdm como raíces del perfil, mientras el encabezado de las tres recetas dice "APARCADA por diseño" desde el 2026-08-07, verificado contra el meson.build de cada tag: las tres mueren en GTK3, que es una de las tres deudas que el frente GNOME aparcó a propósito (GTK3 / X11 / PAM). Dos documentos del repo decían cosas opuestas, y el que se mira primero es el grafo. El resultado era deuda FANTASMA: escritorio-gnome reportaba 124/127 con 3 en `never` para siempre, se leía como trabajo pendiente, y el worker las reintentaba en cada ciclo. En esta misma sesión me hizo afirmar dos veces que GNOME estaba "a 3 recetas de cerrar". No bloquean el escritorio: gnome-shell no depende de gnome-session ni de g-s-d, ni en build ni para arrancar. El camino vivo es mutter → gnome-shell, lanzado por arje; el único que pedía gnome-session era gdm. Las recetas SIGUEN en el repo con su análisis intacto. Si algún día se autora GTK3, se vuelven a añadir a `paquetes` y el objetivo reaparece solo. escritorio-gnome: 119/119, CIERRA. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4 |
||
|
|
f8f679d038 |
corpus: expat-shared y libffi-shared cierran la fuga al Alpine del lab
sway es `link = "dynamic"` A PROPÓSITO —un compositor dlopea los drivers DRI de mesa en runtime— y sale con doce NEEDED. Nueve los cubría la clausura (pixman, drm, evdev, input, udev, wayland-server, wlroots, xkbcommon, libc), porque esas recetas ya son dinámicas. Los otros tres —libz.so.1, libexpat.so.1, libffi.so.8— venían de recetas `--disable-shared`, así que NINGÚN artefacto sellado producía ese `.so` y el rootfs los resolvía contra el sysroot Alpine DEL LAB. POR QUÉ ERA PEOR QUE UNA DEP FALTANTE. Una dep ausente falla ruidosamente. Ésta no: el lab NO entra en `hash_inputs`, así que el store daba el artefacto por bueno mientras el binario sólo arrancaba en una máquina que tuviera Alpine debajo. La fuga era invisible para todo el sistema de medición. `zlib-shared` ya existía en el corpus y sólo faltaba declararla. `expat-shared` y `libffi-shared` son nuevas, calcadas del patrón: autotools con --enable-shared, sin el truco de --whole-archive que zlib-shared necesita porque SU configure aborta bajo zig cc. SONAMEs verificados contra lo que pide el ELF: libexpat.so.1, libffi.so.8, libz.so.1. Exactos. NO se tocó `link` en las canónicas: entra en `hash_inputs` y habría re-hasheado expat, libffi y todo lo que los lista en deps —fontconfig, dbus, mesa, glib, python3, los crates `-sys`— cientos de recetas selladas por libs que ya están bien. El nombre `*-shared` es distinto del canónico, así que conviven. Verificado antes de construir: recalculadas las 794 recetas, **0 hashes cambiados**, sólo las 2 nuevas. Van al CORPUS y no a incoming-wlr/ porque la resolución de deps es hermano→padre: una receta del corpus no ve una cola. Es donde ya viven zlib-shared, fontconfig-shared, freetype-shared… VERIFICADO CON PÍXELES, no con el log. Rootfs rehidratado desde cero (126/126), CERO ficheros de `.dev-fs/alpine`. Cero «Error relocating». La captura tiene el MISMO sha256 que la de las libs de Alpine: 177fea396caa9dca, 1280x720, 383 colores. Tercera vez que la evidencia sale bit a bit igual al cambiar la procedencia — la soberanía no costó ni un píxel. Perfil: escritorio-sway 126/128. Faltan strace (linux-headers del lab) y rsync (404 de upstream). QUEDA UN RESTO: el loader `/lib/ld-musl-x86_64.so.1` todavía se toma del host. `recipes/musl.toml` existe en el corpus pero está EN DEUDA y fuera de todo perfil — mismo agujero de declaración, un nivel más abajo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o |
||
|
|
428ad80b24 |
wlr: fuentes y XKB DECLARADAS en el perfil — la captura sale bit a bit idéntica
`dejavu-fonts` y `xkeyboard-config` entran en `recipes/incoming-wlr/` y en las raíces de `escritorio-sway`. La clausura pasa de 123 a 125 y ambas ya estaban selladas: cero builds. POR QUÉ TENÍAN QUE SER RAÍCES Y NO PODÍAN LLEGAR POR CLAUSURA. Son DATOS, no binarios: ninguna receta depende de una fuente ni de un mapa de teclado para COMPILAR. El grafo puede seguir todas las aristas que quiera y no va a alcanzarlas nunca. Si no se declaran, no están — y el perfil sigue dando 100%, porque mide la clausura de las raíces declaradas. Copias BYTE-IDÉNTICAS a las de incoming-kde, a propósito. Ya había tres copias iguales de xkeyboard-config (kde, gnome, cosmic) y dos de dejavu-fonts (kde, cosmic); ésta es la cuarta. No se consolidan al corpus ahora porque eso toca las colas de otros tres frentes que otros agentes trabajan. Mantenerlas idénticas deja esa consolidación como un `git mv` trivial en vez de un merge: cuatro ficheros iguales se unifican de un tirón, cuatro casi-iguales exigen revisión. VERIFICADO de punta a punta, no por el log: rootfs rehidratado DESDE CERO con sólo lo que la clausura declara (123/123), y `sway-headless.sh` sin una línea de inyección manual de fuentes ni XKB. Cero errores de xkbcommon, cero de fcft. La captura resultante tiene el MISMO sha256 que la de ayer con todo inyectado a mano — 1280x720, 383 colores. La declaración es exactamente equivalente a la inyección, y el pipeline reproduce. Queda como deuda real (no de declaración): libz.so.1 / libexpat.so.1 / libffi.so.8, que sway pide por estar enlazado dinámico y el corpus sólo produce como `.a`. Y `fonts.conf` NO era deuda: lo trae el propio artefacto de fontconfig. Perfil: escritorio-sway 123/125. Los 2 que faltan son strace (choca con los linux-headers del lab) y rsync (404 de upstream), ninguno del escritorio. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o |
||
|
|
64e1ad2942 |
khipu: el grafo de sway llevaba 17 días congelado — el latido nunca corría --wlr
`build-state.py --wlr` existe desde que se abrió el frente, y su propio comentario dice que sin él
el frente es INVISIBLE para el khipu. La línea nunca se agregó a `cosecha-cron.sh`: el latido
regeneraba {base,kde,gnome,cosmic} y saltaba wlr.
Medido: build-state-wlr.json quedó fijo el 2026-08-09 anunciando `escritorio-sway 121/121`. Hoy,
regenerado, da 108/123. En el medio se re-hashearon freetype, make, python3 y libpng-pic, que
arrastraron a deuda a 14 dependientes (fuzzel, yambar, swaybg, swaylock, slurp, sed, strace, tzdata,
rsync, pciutils, procs, sd, skim, tokei). Nadie lo vio porque el número que se mira estaba perfecto.
Un grafo que nadie regenera no envejece en cualquier dirección: envejece hacia el OPTIMISMO. Sólo
puede sobreestimar lo sellado, porque el paso del tiempo únicamente invalida hashes, nunca los crea.
Va también `foot` a las raíces de escritorio-sway. El comentario de targets.toml afirmaba que entraba
por la clausura de `cli`; el grafo lo desmiente — `perfil.cli` no lo lista y ningún perfil lo
arrastraba. El escritorio daba 121/121 SIN EMULADOR DE TERMINAL. Se vio al hidratar la clausura para
armar la imagen, no antes: la métrica mide la clausura de las raíces DECLARADAS, y es estructuralmente
ciega a lo que falta en la declaración.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
|
||
|
|
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> |
||
|
|
8d10bccf34 |
🔇 wireplumber: construido, corriendo… y NO era el bloqueo
b3:b8f3baf0, selló a la primera. Arranca desde cosmic-start ("wireplumber OK (pid
192) — hay gestor de sesión"), se conecta al grafo (dos clientes en wpctl status) y
wpctl pasa a mostrar una sección Video que antes no existía.
Y el Start de ScreenCast SIGUE sin Response a los 60s. Mismo resultado exacto.
LA HIPÓTESIS QUEDA MATADA POR MEDICIÓN. "Falta el gestor de sesión que mueva el nodo
de Paused a Streaming" era mía y era razonable; no era cierta. Queda escrita junto a
su refutación porque una hipótesis descartada CON EVIDENCIA vale más que una lista de
sospechosos: acota el próximo paso a lo que queda, que es el backend mismo.
El dato nuevo, que es por dónde seguir: con wireplumber corriendo, `wpctl status`
lista `Video → Streams` VACÍO durante el handshake. O sea que el nodo
`cosmic-screencast` que SÍ existe en `pw-cli ls Node` no llega a wireplumber como
stream gestionado. Próximo movimiento: RUST_LOG=trace sobre screencast_thread, no
otra dep.
── por qué receta propia y no la de GNOME ──────────────────────────────────────
La de incoming-gnome funciona pero NEEDea libglib/libgobject/libpipewire de la ISLA
DINÁMICA de GNOME: traerla metería una segunda glib y una segunda pipewire en la
imagen. De 19 deps, 14 dan hash idéntico; las que divergen divergen a propósito —
sobre todo `pipewire`, que acá apunta a la de COSMIC (338d1d8c), la que el escritorio
realmente ARRANCA. Un gestor de sesión contra otra pipewire que la que corre no
gestiona nada.
El cambio de fondo es glib → glib-shared. COSMIC usa la glib ESTÁTICA del corpus, que
le alcanza al portal porque es un ejecutable. Acá no: wireplumber produce un .so y 17
módulos, y libglib-2.0.a tiene 44.275 reubicaciones R_X86_64_32/32S ⇒ no es PIC. La
regla del frente GNOME (readelf -r ANTES de gastar el build) ahorró uno.
Y la salida no fue una campaña nueva sino una MEDICIÓN: incoming-kde/glib-shared
copiada a esta cola resuelve al MISMO hash (0584ce1f) y ya estaba sellada ⇒ cero
rebuild. Idem pcre2-shared, lua y libelogind. Las dos glib coexisten sin pisarse (.a
y .so son ficheros distintos) y el portal conserva sus 3 NEEDED.
escritorio-cosmic: 84 → 89 recetas, 0 faltantes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
ee037912a9 |
🎥 ScreenCast: el stream SE CREA — nodo cosmic-screencast vivo en pipewire
La receta de la sonda (b3:af49d32d) y lo que midió. Es la única receta de la campaña
cuyo producto no es una pieza del escritorio sino un instrumento para medirlo: entra
a la imagen porque un handshake de portal sólo se puede ejercer DESDE la sesión, con
bus y compositor vivos, y sale ESTÁTICA (0 NEEDED) porque un instrumento no debe
depender de aquello que mide.
[1/4] CreateSession → Response 0, session_handle ✓
[2/4] SelectSources → Response 0 ✓
[3/4] Start → el backend ABRE "Share your screen", con
miniatura EN VIVO del framebuffer y el output
Virtual-1; se elige, se pulsa Share…
y NO llega Response en 60s ✗
El log del backend da la línea exacta:
screencast_thread: state-changed 'Connecting' -> 'Paused'
Y `pw-cli ls Node` durante la espera da el veredicto INDEPENDIENTE:
node.name = "cosmic-screencast" media.class = "Video/Source"
EL NODO DE VIDEO EXISTE EN EL GRAFO DE PIPEWIRE. La cadena entera —cliente →
frontend → backend → compositor → demonio— funciona hasta crear y negociar el stream.
Lo único que no ocurre es el Response de vuelta tras quedar en `Paused`. Eso es un
lugar muy distinto del de esta mañana ("no hay demonio con quien negociar").
Sospecha para el próximo paso, y es HIPÓTESIS no medición: falta `wireplumber`. Sin
gestor de sesión nadie mueve el nodo de Paused a Streaming y el backend parece
esperarlo. Construirlo la confirma o la mata.
Gotchas que costaron corridas y quedan escritos:
· matar el backend se lleva puesto al frontend (ambos pierden dueño del bus);
· el lanzador busca por NOMBRE VISIBLE: `cosmic-term` no matchea, `Terminal` sí —
escribir el nombre del binario abre otra app;
· `| head -N` bufferiza y deja la terminal en blanco: parece colgada y está esperando;
· pkg-config OMITE los -L de dirs "estándar" y zig cc cross NO los busca ⇒
`-ldbus-1` pelado da "unable to find static system library", que suena a librería
faltante cuando lo que falta es la ruta.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
1e786aaf42 |
🚪 cosmic: el BACKEND del portal sella (b3:7ad0f124) — cadena completa en la imagen
69 min. NEEDED: libgbm, libpipewire, libxkbcommon, libc. Instala el binario en /usr/libexec, el .service de D-Bus con @libexecdir@ sustituido, el cosmic.portal que declara las cinco interfaces (Access/FileChooser/Screenshot/Settings/ScreenCast) y sus iconos. clang18 se construyó en la granja y se cosechó al laptop (b3:62c6bfb5), pero el backend NO se pudo construir allá: el worker no tiene la cola COSMIC horneada —el snapshot golden es del 2026-07-15 y toda esta cola es posterior— así que intentó reconstruir pipewire y murió buscando glib. Es la regla de «rootfs laptop ≠ worker» pero con el STORE: lo que en el laptop es cache-hit, en el worker es un build entero con sus propias deps. Y destapó que la receta de pipewire NO lleva --wrap-mode=nodownload: ante una dep faltante intenta bajarse un subproyecto de internet y, sin red, el error que sale no es «te falta glib» sino «Unhandled python exception / This is a Meson bug». Queda anotado como deuda. --offline SIN --locked, y no es descuido: el sed del parche corre en la fase compile, o sea DESPUÉS del cargo vendor, así que cargo ve el Cargo.toml cambiado y con --locked se niega. Quitar una feature sólo puede ENCOGER el conjunto de crates, así que lo que haga falta ya está vendorizado; la hermeticidad la da --offline + el árbol vendorizado, no el --locked. Dos raíces en el perfil y en la hidratación, porque son DOS procesos que se encuentran por D-Bus en runtime y ningún [deps] los relaciona. Imagen en 83 recetas (eran 74). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
520271ea2f |
📷 cosmic: cosmic-screenshot sellada y ROTA A PROPÓSITO — y la glib compartida YA existe
b3:8b57ec9b, 9 minutos, NEEDED = libc.so y nada más. Un solo NEEDED, y el único `[deps] build = []` de la campaña: 130 líneas de Rust que no dibujan ni hablan wayland. Se incluye en la imagen sabiendo que falla, para que el hueco sea MEDIBLE en vez de supuesto. Error exacto capturado corriéndola desde cosmic-term dentro de la VM: panicked at src/main.rs:76:10: failed to send screenshot request: Portal(ZBus(MethodError(ServiceUnknown, "The name org.freedesktop.portal.Desktop was not provided by any .service files"))) Misma forma que la deuda de org.freedesktop.locale1: un nombre de D-Bus que nadie sirve. LA CADENA DEL PORTAL SON TRES ESLABONES, NO DOS. Medido en el fuente: xdg-desktop-portal-cosmic declara DBUS_NAME = "org.freedesktop.impl.portal.desktop.cosmic" (src/main.rs:27) — es BACKEND, no toma el nombre que los clientes buscan. Falta también el frontend xdg-desktop-portal, y ninguno de los dos está en ninguna cola. ⚠ CORRECCIÓN AL PROPIO RUNBOOK: decía que `gvfs` exigía «una campaña propia». Es falso desde que KDE selló las piezas. Verificado con `hammer hash` desde las dos colas —el método correcto para saber si una receta se comparte—: glib-shared 2.88.1 (b3:0584ce1f) y pcre2-shared (b3:f254abaa) resuelven IDÉNTICO desde incoming-cosmic ⇒ cero rebuild, los artefactos sirven tal cual. zlib-shared ya estaba en la cola. libffi entra estático dentro de la .so (su Requires.private lo ignora pkg-config fuera del modo estático). Lo que queda de `gvfs` NO es técnico sino una decisión: mete una segunda glib en la imagen —lo que GNOME midió y evitó— y re-hashea cosmic-files, que arrastra a cosmic-term y cosmic-edit porque lo usan como crate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
b51f90ffea |
🛍 cosmic: cosmic-store — sella (b3:a06437f7), y BoringSSL cruza a musl sin cmake
La quinta aplicación, y la primera de la suite que choca con que hammer es
OTRA distro: una tienda es la cara de un gestor de paquetes y el de abajo no
es el suyo. De los cuatro backends de `src/backend/`:
flatpak pide libflatpak (GObject) ⇒ la cadena glib COMPARTIDA más
ostree/libsoup/gpgme: la torre de C que esta campaña no tiene
packagekit Rust puro (packagekit-zbus) pero necesita el DEMONIO en el bus
rpm-ostree irrelevante
pkgar el único que arranca: ni demonio ni enlace, lee el AppStream
del sistema — o sea los .metainfo.xml que instalan las recetas
ALCANCE HONESTO: con pkgar NAVEGA, no INSTALA. Ninguna operación está
cableada al .swm de hammer (Etapa F). El camino para arreglarlo quedó
identificado y no cuesta un solo .so de C: servir
`org.freedesktop.PackageKit` sobre .swm, mismo patrón que
arje-logind-compat.
HALLAZGO REUTILIZABLE: `aws-lc-sys` (BoringSSL, C + ensamblador) compiló
bajo zig-cc/musl SIN cmake y SIN perl — ninguno de los dos está en los 182
binarios de work/builder-rootfs/usr/bin. Tomó el camino de bindings
pregenerados (`cargo:rustc-cfg=universal`) y compiló los 21 MB de
libaws_lc_crypto.a con el crate `cc`. El `Compiling cmake v0.1.58` del log
es el crate ayudante, que se compila aunque el binario no se invoque. Esto
destraba cualquier receta futura que entre con rustls por defecto, que hoy
es casi todo lo que use reqwest.
NEEDED: libxkbcommon.so.0 y libc.so, igual que cosmic-edit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
d5f798a6b1 |
✎ cosmic: cosmic-edit — la 4ª aplicación, y el Cargo.lock miente por exceso
El editor de texto sella en b3:2323a325, igual que el dry-run. Es la más barata de las cuatro: depende de `cosmic-files` como CRATE con `default-features = false`, así que su árbol ya estaba compilado (68 min de enlace, no de compilación desde cero). Lo que aprendió esta receta, y que corrige un borde de la regla anterior: **el `Cargo.lock` lista lo POSIBLE, no lo ENCENDIDO**. Aparecen `gio-sys`, `glib-sys` y `gobject-sys` —que en cualquier otro paquete mandarían a declarar glib y de ahí a la cadena compartida— y sin embargo entran sólo por la feature `gvfs`, que se apaga. El lock dice el universo; las features dicen el recorte. Features: `--no-default-features --features dbus-config,wayland`. Fuera `gvfs` (pide las glib compartidas, el corpus las tiene estáticas) y `wgpu` (el que desbordó el filesystem dos veces en cosmic-files). La doble barra `pop-os//` que mordió en applets y settings acá está comentada en el Cargo.toml; verificado con grep ANTES del build. Evidencia: el binario enlaza sólo `libxkbcommon.so.0` y `libc.so` — dos NEEDED, el mínimo de la suite. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
232f23c5a8 |
⚙ cosmic: cosmic-settings — el panel de control, y NO arrastra medio sistema
Sellada b3:716dac53. Cola 33/33, escritorio-cosmic 71/71. Abre desde el lanzador con la barra lateral entera (Red, Bluetooth, Accesibilidad, Escritorio, Pantallas, Sonido, Energía, Entrada, Aplicaciones, Fecha y hora, Sistema y cuentas). Tarda ~20s con render por software; no está colgado. La sorpresa buena: nada de NetworkManager, libpulse, udisks ni accountsservice — habla con los daemons por zbus, que es Rust puro. Se verificó ANTES de escribir la receta listando los crates -sys del Cargo.lock, que es donde vive la verdad sobre qué C hace falta: sólo drm-sys, input-sys, libudev-sys, dav1d-sys, wayland-sys (dlopen) y gettext-sys. Regla barata: grep '^name = ".*-sys"' Cargo.lock antes de adivinar deps por lo que el programa hace. Volvió a morder la doble barra de cosmic-protocols// (404 de GitHub, muere en el fetch con tres reintentos). Segunda víctima ⇒ es patrón de la suite, no rareza de un repo. Instala 32 .desktop, uno por página. Y default_schema va con find, no cp plano: es el árbol <Componente>/v1/<clave> de cosmic-config, donde cada clave es un fichero con su nombre. Deuda que deja su propio log: org.freedesktop.locale1 no lo sirve nadie ⇒ la página de idioma no podrá cambiar nada. Mismo tipo que login1, mismo arreglo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
23c0e82527 |
📁 cosmic: cosmic-files — la 2ª aplicación, y «ya está compilado» era falso por una línea
Sellada b3:48d2b072. Cola 32/32, escritorio-cosmic 70/70. Abre desde la biblioteca y lista el sistema de ficheros real de hammer (bin 72 ítems, dev, ente, etc, lib, lost+found). Entró creyendo que era casi gratis porque cosmic-term ya la compila como crate. No lo era: cosmic-term la declara con default-features = false. Lo que un paquete cuesta depende de con qué features lo pide quien lo usa, así que «ya se compiló» puede ser falso. Dos features apagadas, las dos por medición: · gvfs trae gio/glib y el enlace final pide las glib COMPARTIDAS; el corpus sólo las tiene estáticas. Se pierden montajes remotos, no la navegación local. Por eso tampoco se construye cosmic-files-applet: su Cargo.toml fija gvfs a mano. · wgpu desbordó el filesystem dos veces (No space left on device en /src/target) con wgpu+naga+ash+glow+spirv a codegen-units=1. Sin él el árbol queda en ~2 GB, y no se pierde nada: el resto de la suite pinta con tiny-skia y la imagen no tiene GPU. Y la regla que dejó el intento con glib declarado: el error fue «Package libpcre2-8, required by glib-2.0, not found» — el que falla no es la dep sino lo que su .pc declara en Requires. Declarar una dep trae su artefacto, no su clausura de pkg-config; se lee con grep ^Requires sobre los .pc ANTES de gastar el build. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e39610588d |
🖥 cosmic: cosmic-term — la primera APLICACIÓN, y la biblioteca deja de estar vacía
Sellada a la primera (b3:d0ea1c1e). Cola 31/31, escritorio-cosmic 69/69. Dentro de la ventana: bash-5.3#, uname -srm devuelve Linux 6.16.12 x86_64 —el kernel propio de hammer— y qalc -t '6*7' devuelve 42, o sea la calculadora que empaquetamos una hora antes. Se abre con click en su icono en la biblioteca, que hasta hoy salía vacía con razón: los 20 .desktop de la imagen eran NoDisplay=true. Éste no lo es. Lo que arrastra y no se adivina: su Cargo.toml depende de cosmic-files, o sea que el gestor de ficheros entra como LIBRERÍA (selector y drag-and-drop) y compilar la terminal compila medio gestor de ficheros. En esta suite las aplicaciones se usan unas a otras como crates, y el grafo de Cargo no se parece al mapa de componentes. De paso, empaquetar cosmic-files como aplicación queda casi gratis. wgpu viene en default, al revés que el resto de los clientes: se deja, porque wgpu y naga ya se habían compilado enteros para cosmic-workspaces y apartarse de las features por defecto es apartarse de la única combinación que upstream prueba. Cierra con dos NEEDED. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |