Segundo hueco de RUNTIME del triaje de la frontera. Nadie lo pide para construir
—por eso el escritorio sellaba completo sin él— pero sin este módulo los controles
QML de Plasma (botones, sliders, combos, los menús de los applets y de los KCM) se
dibujan con el estilo genérico de Qt. No lo reemplaza qqc2-desktop-style: aquél es
el estilo de escritorio integrado con KStyle; éste es la implementación QML nativa
de Breeze, la que Plasma 6 usa por defecto.
Dos cosas que no eran obvias:
1. Se llama casi igual que el vecino y viene de otro tarball. qqc2-DESKTOP-style
sale de Frameworks 6.27.0; qqc2-BREEZE-style sale del release de Plasma 6.7.2,
el mismo de breeze/kwin/plasma-workspace. Copiar la URL del vecino da 404.
El sha256 va contrastado contra el .sha256 que publica KDE al lado del tarball,
no sólo contra lo que bajó acá: la primera descarga volvió con 0 bytes y su
sha256 era el de la cadena vacía — un vacío que se lee como éxito.
2. La lista de deps no es la del vecino copiada: sale de la clausura de
find_dependency de los KF6*Config.cmake que ya están en el store. Ese recorrido
enseñó algo reutilizable: casi todo lo que esos configs piden (X11, XCB,
Wayland, OpenSSL, BZip2, LibLZMA) vive dentro de un `if (NOT TRUE)` — la rama
de build ESTÁTICO — y por lo tanto es código muerto en nuestro corpus, que
compila KF6 dinámico. Por eso acá no hay libX11 ni xorgproto, coherente con
Wayland-only. El find_package(X11) del CMakeLists raíz no es REQUIRED, así que
falla en silencio sin romper el feature_summary(FATAL_ON_MISSING_REQUIRED).
Confirmado a posteriori: los NEEDED del plugin son exactamente los 5 KF6 que la
clausura predijo (KirigamiPlatform, IconThemes, ColorScheme, GuiAddons,
ConfigCore) más Qt6 Quick/Gui/DBus/Core.
La fase install verifica por CONTENIDO, no por presencia: qmldir de org.kde.breeze
y de org.kde.breeze.impl no vacíos, Button.qml presente y el plugin de plataforma
de kirigami instalado. Un módulo QML sin su qmldir ocupa disco y arranca con el
estilo genérico sin decir nada. Salieron 83 controles y 3 .so.
Selló al primer intento y REPRODUCE bit a bit (verificar-repro.sh 1/1).
corpus 788/788 · escritorio-kde 166/166 · grafo CIERRA · gate --check OK
Quedan 2 wanted en KDE: xwayland y polkit-qt-1.
Nota sobre el diff de los cinco grafos: cambian los `dependientes_total` de las
deps de esta receta en TODAS las vistas, no sólo en --kde. Es correcto y está
documentado en build-state.py: ese campo se calcula sobre el grafo entero (todas
las colas del disco), que es la corrección del bug de libdrm.
Primero de los cuatro huecos de RUNTIME del triaje de la frontera. Ninguna receta
lo pedía para construir (por eso escritorio-kde cerraba 163/163 sin él), pero sin
la base el escritorio no sabe qué es un fichero: ni asociaciones, ni iconos, ni
abrir-con. Qt6 y GLib lo leen los dos de /usr/share/mime.
Vive en recipes/ y no en incoming-kde: es freedesktop puro, y en la raíz lo ven
los cinco grafos (GNOME y COSMIC lo quieren igual — gdk-pixbuf hoy lo ESQUIVA con
-Dgio_sniffing=false).
Tres cosas que no eran obvias:
1. Fuente por GIT, no tarball. Freedesktop no publica downloads de release para
este proyecto: la API sólo ofrece los -/archive/ autogenerados de GitLab, que
no son estables byte a byte (mismo criterio ya escrito en wlr-randr). El pin
es el tag 2.5.1 PELADO — acá el tag es objeto tag, la trampa de las 4 del
mirror (ADR 0013).
2. i18n.merge_file de data/meson.build es INCONDICIONAL: no lo apaga
-Dbuild-translations=false, porque es el paso que PRODUCE freedesktop.org.xml,
el payload entero. Eso exige un msgfmt que entienda --xml. El de gettext-tiny
sirve y no degrada nada: desde 2.x el template ya es XML válido (las
traducciones se marcan con reglas ITS externas, cero ocurrencias de "<_"), así
que sin catálogos la salida de un msgfmt real ES el template. Verificado a mano:
copia byte a byte de los 384036 del template.
3. meson install deja SÓLO el XML fuente. Los consumidores no leen ese XML: leen
los índices que genera update-mime-database (mime.cache, globs2, magic,
aliases, subclasses). Sin ese paso el artefacto pasa toda verificación de
presencia y el escritorio sigue sin saber qué es un .png — la regla 3 del repo
en su forma exacta. La fase install lo corre contra /out y verifica por
CONTENIDO: mime.cache y globs2 no vacíos, image/png en types. Salieron 1038
tipos y 1448 globs.
Nace con strip_debug = true y REPRODUCE bit a bit (verificar-repro.sh: 1/1, cero
divergencias), caché binaria incluida. Estático, 0 NEEDED.
corpus 788/788 sealed · escritorio-kde 165/165 · grafo CIERRA · gate --check OK
Quedan 3 wanted en KDE: xwayland, polkit-qt-1, qqc2-breeze-style.
Deja de ser una plantilla con `commit = 000…0`. Tres cosas, y las dos últimas
son el valor del commit:
1. PIN. `v0.1.0` era el tag que la versión declaraba y NO SIRVE: su `Cargo.lock`
está desincronizado con sus manifiestos en el propio repo, y `cargo vendor
--locked` se niega ("cannot update the lock file … because --locked"). No es
de hammer: el vendoreo del fetch arranca bien y muere DENTRO de cargo.
Comprobado con `cargo metadata --locked` en los tres refs — v0.1.0
incoherente, v0.2.0 y main coherentes. Se pinea v0.2.0, PELADO con ^{commit}
porque es objeto tag (93253cc2, no 0beb83b7).
=> Un tag no sirve como pin sólo por existir: hay que verificar que su
lockfile cierre.
2. El requisito de `vendor/` en la fuente que pedía el comentario quedó VIEJO:
hammer vendorea en el FETCH a partir del Cargo.lock, host-side, y por eso el
`--offline --locked` del sandbox se cumple. El repo no trae vendor/ en ningún
tag y aun así construye.
3. ⚠ FASE `install` PROPIA, obligatoria con `--example`. El default de Cargo hace
`find target/release -maxdepth 1 -type f -perm -100`, y cargo deja los
ejemplos en `target/release/examples/`. El find no encontraba nada, salía 0 y
**hammer selló un artefacto SIN BINARIO**: 22 min de compilación y un `sealed`
con sólo `.hammer/recipe.toml` dentro, 20K. `hash --check` decía SELLADO y el
grafo lo habría contado como al día.
No lo atrapa `Store::has` ni el guardián de vacíos de build-state, porque el
directorio NO está vacío — tiene el manifiesto. Es la regla 3 del CLAUDE.md en
su peor forma: un ausente falla a gritos, esto llegó al final diciendo que
todo fue bien. Por eso la fase lleva un `test -x` que hace ruidosa la
ausencia.
Verificado por contenido, no por el `sealed`: 17 M, usr/bin/llimphi-counter, ELF
pie, NEEDED = libc.so (dinámico, como la receta promete para un binario que
dlopea Vulkan/Wayland). El artefacto falso se podó y quedó en el ledger.
`version` pasa de 0.1.0 a 0.2.0 y NO mueve el hash: no está en hash_inputs (como
`license`). La identidad la da el commit.
Corpus: 787/787 SELLADAS, 0 deuda, 0 never. Gate --check OK, grafo CIERRA.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
gdm, gnome-session y gnome-settings-daemon salen de la cola y quedan aparcadas
en `recipes/incoming-gnome/.deferred/`, que es el mecanismo que el repo ya usa
(precedente: incoming-kde/.deferred/libXft.toml). El glob del worker es
TOP-LEVEL y el del grafo también, así que dejan de molerse cada ciclo y dejan de
contarse como deuda que nadie va a pagar. `targets.toml` ya no las lista desde
el 2026-08-07 («APARCADAS POR DISEÑO»); esto alinea el árbol con esa decisión.
Y el mapa que llevaban estaba mal en las dos direcciones:
1. «La terna GTK3» es un nombre engañoso: **GTK3 no es el muro**. GTK3 tiene
backend Wayland y se construye con -Dx11_backend=false. Autorarlo no habría
destrabado ninguna de las tres. Lo que bloquea de verdad:
- g-s-d 48.1: gtk+-x11-3.0 / x11 / xfixes INCONDICIONALES => imposible en
Wayland-only, no «pendiente».
- gnome-session 48.0: dependency('libsystemd', required: true) en meson:124
es una comprobación de pkg-config EN BUILD. arje-logind-compat no la
satisface y es a propósito — su receta explica que GNOME consulta login1
en RUNTIME por D-Bus y que no hace falta la C-ABI sd-login. Único camino:
parchear ese required a false, que es una decisión, no un arreglo.
- gdm: cuelga de gnome-session, y con mirada-greeter probablemente sobra.
2. La lista «FRONTERA» estaba VIEJA: ya existen libX11, libXfixes, xorgproto,
libXau/Xcursor/Xdmcp/Xext/Xi/Xrender/Xtst, libxcb (casi todas por KDE) y
polkit, upower, geocode-glib, libgweather. Cuatro de las ocho líneas de g-s-d
habían dejado de ser ciertas. Faltan de verdad: gtk3, libnotify y una
variante libcanberra-gtk3.
Verificado contra el TARBALL (sha256 = el del pin), no contra el comentario.
incoming-gnome queda en 79 recetas, sellado=79 deuda=0 nunca=0.
Gate --check OK, grafo CIERRA, escritorio-gnome sigue 119/119.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
`fontconfig-shared`, `freetype-shared`, `libjpeg-turbo-shared`, `libpng-shared`,
`libtiff-shared`, `libxml2-shared`, `libyaml-shared` y `zlib-shared` existían a
la vez en `incoming-gnome/` y en `recipes/`, y las ocho daban el MISMO
ArtifactHash que su gemela del corpus. Como la resolución de deps cae al padre
cuando no hay hermano, quitarlas deja a las consumidoras resolviendo contra
`recipes/` y con el mismo hash.
Medido, no supuesto: se hashearon las 1144 recetas antes y las 1136 después, y
de las 1136 supervivientes **0 cambiaron de hash**. No hay rebuild.
Lo que NO se toca, y conviene que quede dicho porque se parece:
- Las variantes de la ISLA DINÁMICA (glib, gtk4, gdk-pixbuf, pango, harfbuzz,
graphene, libadwaita, json-glib, libusb, libxcvt, pipewire, pulseaudio,
wireplumber, xdg-desktop-portal): mismo nombre, hash DISTINTO. Son sombras a
propósito — la introspección de GNOME exige .so reales.
- Las copias entre COLAS (alsa-lib, hwdata, xkeyboard-config, libdisplay-info,
lcms2, icu4c, lua, nasm, fuse3, libsndfile, libelogind, hicolor-icon-theme,
dbus-shared): también dan el mismo hash, pero NO sobran. Cada cola necesita su
propio hermano para cerrar su clausura; borrar la de una rompe esa cola. No es
el caso de onda-2, donde la cola entera duplicaba a otra.
Y el criterio, otra vez: `pipewire` y `pulseaudio` son TEXTUALMENTE idénticas a
las de COSMIC y sellan distinto, porque sus deps resuelven distinto según la
cola. El fichero no dice la verdad; el hash sí.
Gate --check OK, grafo CIERRA, escritorio-gnome sigue 119/119. La cola queda en
82 recetas (79 selladas + la terna GTK3) y sus 5 parches, todos en uso.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
Yo mismo las reporte como "la misma clase de colision que onda1" y me pase: no
lo son. onda1 tenia una copia RANCIA del mismo build; esto son dos builds
distintos, cada uno correcto para su frente:
KDE link=dynamic --enable-shared --enable-cxx => libgmp.so + libgmpxx.so
COSMIC link=static --disable-shared => libgmp.a, sin C++
Las dos citan libqalculate y llegan a conclusiones opuestas sobre gmpxx.h, y las
dos tienen razon para su cola. Unificarlas romperia una de las dos.
Lo que compartir nombre SI cuesta es que hace ambigua la clasificacion de
store-gc: un artefacto rancio de una puede caer como SUPERADO porque la otra
tiene sellado vigente. La condicion es precisa y esta medida — solo hay riesgo si
el hash vigente de una NO esta sellado, y hoy las dos lo estan => exposicion
CERO. Antes de podar con nombres compartidos hay que comprobar ESO, no el nombre.
La salida, si algun dia molesta, es renombrar la dinamica a gmp-shared (la
convencion que el corpus ya usa: zlib-shared, cairo-shared...). No se hizo
porque cuesta: da 7 sellados de KDE que caen a deuda. es
el mismo par por la misma razon.
Los comentarios NO entran en hash_inputs (medido: mismo hash antes y despues),
asi que esto no re-hashea nada.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
Al verificar la resolucion estricta de parches salio la unica referencia rota
del arbol: `.deferred/kalker.toml` pide `system-libs.patch` y el fichero estaba
en `recipes/`. Los patches se resuelven `base_dir.join(nombre)` sin fallback, asi
que la receta nunca lo habria encontrado.
No es una rotura nueva: `.deferred/system-libs.patch` no existio jamas (el
importador dejo la receta en .deferred y el parche en recipes/). Y es
inequivocamente de kalker — parchea `kalk/Cargo.toml` — ademas de que ninguna
receta lo citaba desde recipes/, o sea que ahi era un huerfano.
Ningun hash se mueve por esto. Referencias de parche: 95 resuelven, 0 rotas.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
`recipes/incoming/.deferred/` decia "24 recetas aparcadas con diagnostico, p.ej.
git con muro en libgit.a". Las dos mitades de esa frase eran falsas.
Cinco de las 24 ya estaban RESUELTAS en otro fichero y sellan hoy: dprint, git,
gmp, mise e yj (recipes/git.toml, recipes/mise.toml, incoming-kde/gmp.toml...).
La copia aparcada era la version anterior y seguia leyendose como deuda abierta
— y el ejemplo que daba el comentario era justamente uno de esos cinco. mise se
construyo esta misma tarde en el worker. Se van, con `gcc15.patch` que solo
citaba la gmp aparcada (ninguna de las dos gmp que sellan declara patches).
Y las 19 que quedan no estan "aparcadas con diagnostico": son volcado CRUDO del
importador, 15 de import-nix y 4 de import-alpine, todas con la cabecera "PUNTO
DE PARTIDA, no final". Nadie las intento todavia; no hay ningun muro que
respetar. Es una cola de trabajo sin empezar, no una lista de imposibles, y
leerla como lo segundo desanima de tocarla.
Gate --check OK, grafo CIERRA, corpus 786/787; las 5 que sellan intactas.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
Era la cola de staging de las tandas base-system/foot y sus recetas ya se
habian PROMOVIDO a canonicas commit a commit ("promover 7 tier-2", "promover
cadena crypto", "promover tllist/utf8proc/scdoc/fcft/foot"). Lo que quedaba
eran 0 .toml y 26 .patch: 5 copias byte a byte de la version canonica de
recipes/ y 21 de recetas que ya no viven ahi.
No eran "probablemente inutiles": son inalcanzables POR CONSTRUCCION.
`fetch::apply_patches` resuelve `recipe.base_dir.join(nombre)` y `base_dir` es
el directorio de la receta, sin fallback a `recipes/` — un parche en una cola
sin recetas no lo puede pedir nadie.
⚠ El corolario queda escrito en farm-worker-loop.sh: al promover una cola, los
`.patch` NO viajan con la receta, y la cola queda en un estado que PARECE vivo
(tiene ficheros) sin moler nada.
Sale de QUEUES en farm-worker-loop.sh y de la lista de promote de farm-down.sh.
`incoming-go` se queda en las dos aunque el directorio no exista: es la cola
aislada del frente Go y el guard `ls $Q/*.toml` salta las vacias; quitarla seria
dejarla muerta el dia que ese frente la recree.
Verificado con la regla ESTRICTA de resolucion de parches sobre todo el corpus:
94 referencias resuelven, 0 rotas. (El chequeo que use al jubilar onda-2 era mas
laxo que hammer — aceptaba recipes/<parche> como fallback; su conclusion se
sostiene, pero el metodo queda corregido.)
Gate --check OK, grafo CIERRA, corpus 786/787.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LykB7vw38b4Ck1Zij15RQ4
Lo destapó escribir la contraparte del ADR 0014 para churay: churay direcciona sus blobs
con blake3(bytes) pelado y hammer iba a sellar el digest con of_inputs(&[bytes]), los dos
bajo el prefijo `b3:`. El mismo archivo con dos hex distintos, y un CAS compartido que no
falla ruidosamente: cada lado busca un nombre distinto para los mismos bytes.
Con una sola entrada el length-prefijado no desambigua ninguna concatenación — sólo hace
que el nombre deje de ser verificable por un tercero con b3sum en la mano.
`ArtifactHash::of_bytes` YA EXISTÍA y ya era blake3 pelado: es la convención de of_file y
la del expected_hash de un .swm, así que of_inputs era además la pieza fuera de sitio
dentro del propio repo. of_inputs se queda para lo que fue escrito: hashear una LISTA.
Coste: una línea, porque ningún índice publicado lleva todavía el campo. Es el argumento
del ADR aplicado a sí mismo — decidir antes de que haya usuarios. La corrección queda en
el ADR, no reescrita en silencio.
Guardián: digest_es_blake3_pelado_y_no_length_prefijado, con vector fijo de b3sum y un
assert_ne contra of_inputs que nombra la consecuencia.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CK6HpSoHcN9M4GBpqRSusR