Commit Graph
100 Commits
Author SHA1 Message Date
sergioandClaude Opus 5 c13d25f02b wlroots 0.18.2 SELLADA — el desbloqueo del frente de WMs Wayland ligeros
Primera receta del frente nuevo. wlroots es la biblioteca de la que cuelgan casi todos los
compositores ligeros (sway, labwc, river, hyprland): sin ella no hay ninguno, y con ella los
compositores salen baratos porque no traen torre de C debajo. Hasta hoy el catálogo tenía de
toda esa familia sólo `foot` y `mako`.

b3:9b56bac0d00977524072cf393ecc9f0b847fcc1a0bfba9d9c4d57fd9a4a74b44 · 13 MB · construida en
el worker (cadena GUI: no se rebuildea en el laptop por el zig-skew).

COLA AISLADA `incoming-wlr/`, porque otro agente comparte el repo. Como la resolución de deps
es hermano→padre, una receta de acá ve el corpus pero NO ve `incoming-gnome/`, donde viven
`libdisplay-info` y `hwdata` ⇒ se copian como hermanas. No duplica trabajo: los ficheros son
idénticos, el ArtifactHash es el mismo y el build hace cache-hit. Verificado antes de
construir: ambas dan el mismo b3: que su original.

LAS OPCIONES, VERIFICADAS CONTRA EL meson_options.txt DEL TARBALL, no de memoria — porque una
opción `-D` inventada NO se ignora: meson aborta en la primera desconocida. Es exactamente lo
que tenía muerta a gnome-session con su `-Dsystemd=false`. El `.pc` resultante confirma que
salió lo pedido: have_drm_backend=true, have_libinput_backend=true, have_gles2_renderer=true,
have_gbm_allocator=true, have_session=true, y have_x11_backend / have_xwayland en FALSE —
la distro es Wayland pura y X11 es deuda aparcada por diseño; no entra por la puerta de atrás.

DOS COSAS QUE COSTARON UNA ITERACIÓN CADA UNA:
· `-Dwerror=false` no es pereza, es DIFERENCIA DE TOOLCHAIN. wlroots compila con -Werror y
  upstream lo valida con gcc; nuestro `zig cc` es clang y avisa de lo que gcc calla. Murió en
  `types/wlr_shm.c:503` por `-Wunused-but-set-variable` sobre has_argb8888/has_xrgb8888 —
  código correcto que sólo clang señala. Tratar los avisos de OTRO compilador como errores del
  proyecto es un fallo de método, no un hallazgo de calidad.
· `libffi`, `expat` y `zlib` en deps aunque wlroots no los use: los exigen los `.pc` de wayland
  y mesa en su `Requires`, y pkgconf resuelve el grafo de .pc completo antes de dar un cflag.
  Misma lección que wlr-randr esta mañana.

El artefacto trae `libwlroots-0.18.{a,so}`, su .pc y 111 cabeceras bajo
`usr/include/wlroots-0.18/wlr/`. Listo para que sway enlace contra él.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 20:24:34 -04:00
sergio bca7164b4e estado: cosecha granja 2026-08-07T22:36:14Z — avance del árbol KDE 2026-08-07 18:36:14 -04:00
sergioandClaude Opus 5 59c4973bd9 granja: el filtro de «ya sellado» miraba el nombre equivocado — lo arreglo antes de que cueste
El bloque que añadí hace un rato usaba `basename $f .toml` como nombre del artefacto. **Está
mal**: el artefacto en el store se llama por el campo `name` de la RECETA, no por el fichero,
y en 17 recetas del catálogo esos dos no coinciden. El arquetipo es
`recipes/incoming-kde/qtbase.toml`, cuyo `name` es `qt6-qtbase` ⇒ el artefacto es
`<hash>-qt6-qtbase` y mi `grep` lo buscaba como `<hash>-qtbase`.

Efecto: las 17 salían «no selladas» y el worker las habría reconstruido — exactamente el
desperdicio que este filtro existe para evitar. Se vio porque los 10 «pendientes» de
incoming-kde eran todos módulos Qt, que es demasiada coincidencia para ser casualidad; el
hub tenía el artefacto con el MISMO hash, sólo que bajo otro nombre.

Con el filtro correcto, la foto de la granja es exacta por primera vez:
  · incoming-kde    205 recetas · 205 en el hub ⇒ **0 pendientes**
  · incoming-gnome   90 recetas ·  87 en el hub ⇒ 3 pendientes: gdm, gnome-session y
                                    gnome-settings-daemon, o sea el trío bloqueado por GTK3
  · incoming-go / incoming-clib: colas vacías

⇒ **La granja no tiene trabajo construible.** Y ya no lo tenía antes de esto: el worker no
posee ni UN artefacto que el hub no tenga (comparadas las dos listas: 0 exclusivos suyos),
así que sus últimas 22 horas produjeron cero valor neto. Lo que queda por hacer no es
construir sino AUTORAR recetas (wlroots y los WM ligeros, cups, bluez, las gráficas de
terceros), y eso es trabajo de hub.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:32:31 -04:00
sergioandClaude Opus 5 129aff9dd9 granja: el worker rehacía 87 de 90 recetas por ciclo — el sync del store es de UN SOLO SENTIDO
Fui a mirar los fallos de la granja y el número no cuadraba: de 20 «fallos» de incoming-gnome,
17 correspondían a recetas CON ARTEFACTO VIGENTE en el hub. Primero pensé que eran marcadores
`.fail` rancios, y me equivoqué: `build-farm.sh` hace `rm -rf "$FARM"` al empezar cada ciclo,
así que el status es siempre del ciclo actual. Después pensé que el worker corría recetas
viejas, y también me equivoqué: comparados los md5, hub y worker tienen copias IDÉNTICAS.

LA CAUSA REAL está en `farm-sync.sh`: sube el código EXCLUYENDO /store y baja el store del
worker. El store viaja worker→hub y nunca hub→worker. O sea que **el worker no se entera de
nada de lo que se sella en el hub**, y cada ciclo reintenta —y vuelve a fallar— trabajo ya
hecho. Medido: de las 90 recetas de incoming-gnome, **87 ya están selladas en el hub**. El
worker estaba quemando el 97% de esa cola en repetir lo hecho.

EL ARREGLO no es mandarle los artefactos (gigabytes por un enlace de 8 Mbps) sino la LISTA:
`work/farm-sellados.txt` son los nombres `<hash>-<paquete>` del store del hub, unos kilobytes.
`farm-sync.sh` la regenera en cada sync (para que no envejezca sola) y `build-farm.sh` salta
la receta cuyo hash vigente ya esté ahí. Ojo al detalle de rsync: la subida excluye /work, así
que el manifiesto necesita un `--include` ANTES del `--exclude` — rsync aplica la primera regla
que casa, y sin ese orden el fichero no viajaba y el arreglo no habría hecho nada en silencio.

Y LAS TRES DEUDAS REALES DE LA GRANJA SON UNA. gnome-session, gnome-settings-daemon y gdm
mueren todas en GTK3, que el frente GNOME aparcó a propósito. Verificado contra el meson.build
de cada tag: gnome-session 48.0 lo pide incondicional; gnome-settings-daemon 48.1 pide gtk+-3.0
Y gtk+-x11-3.0 (dos de las tres deudas aparcadas, no una); gdm 48.0 lo tiene condicionado a
`if have_xdmcp` pero da igual, porque muere construyendo gnome-session.

De paso, gnome-session pasaba `-Dsystemd=false -Dsystemd_journal=false`, dos opciones que NO
EXISTEN en 48.0 (sus opciones reales son seis: deprecation_flags, session_selector,
systemduserunitdir, docbook, man, x11). Meson aborta en la primera opción desconocida, así que
la receta ni llegaba a configurar y el fallo real quedaba tapado. Corregido, más -Dx11=false.
El diagnóstico de fondo YA ESTABA en la receta desde el 2026-07-27 y era más completo que el
mío (dice GTK3 **y** libsystemd); quité la nota duplicada que había añadido.

Y en el respaldo: la comprobación inicial de SSH era de un solo intento y tiró la corrida al
relanzarlo llegando a casa, con el wifi aún sin levantar. Es el peor momento para rendirse —
quien relanza un respaldo interrumpido acaba de cambiar de red. Ahora reintenta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:27:29 -04:00
sergioandClaude Opus 5 4f265a316f docs: SDD 22 — respuesta verificada al handoff de kernel-config de tawasuyu
El handoff avisa que nada suyo está verificado contra hammer («si un dato de hammer lo
contradice, manda hammer»). Esto es esa verificación: contesta sus seis preguntas del §9 con
evidencia del código, corrige lo que estaba mal y añade dos objeciones que cambian el orden.
NO es aprobación ni plan de implementación.

EL HECHO QUE REORDENA TODO, y que el handoff no podía conocer: las FASES entran en
hash_inputs, y el config del kernel vive en la fase `configure` ⇒ **el config ES la identidad
del artefacto**. Corta en los dos sentidos. A favor: §2.2 (atestar un config por huella de
hardware) sale casi gratis, porque cada config ya es un artefacto direccionado por contenido,
reproducible y firmable — no hay que inventar el mecanismo. En contra: la explosión de builds
no es hipotética, es aritmética visible en el disco, que ya crece 24 G por ciclo. Y una
«perilla» de la UI no es un parámetro de runtime: es una edición de receta.

LAS SEIS RESPUESTAS:
 P1 fragmentos — YA es como §6 prescribe: defconfig + `scripts/config -e/-d` + olddefconfig,
    o sea que la app nunca escribe un .config. Media implementación está en producción. Falta
    el diff-back, sin el cual un símbolo pedido que no sobrevive se pierde EN SILENCIO.
 P2 repro con config variable — sí, por construcción: no hay un kernel cuya reproducibilidad
    dependa de congelar el config; hay N kernels, cada uno congelado. Se pueden PREFIRMAR
    artefactos, no sólo configs.
 P3 tiempo — 35/45/60 min según receta ⇒ la bisección de §5 son ~4,5 h de la máquina que
    acaba de no arrancar. Mata la bisección ingenua.
 P4 harkaq — el patrón sí, el código no: su clausura enumera fichero a fichero sobre
    artefactos del store, semántica de rutas. Kconfig es otro grafo.
 P5 eje huella en el índice — sí y compatible hacia atrás: PackageEntry usa campos serde
    opcionales. Mismo truco que hizo pagable el campo `license` hoy.
 P6 PLAN-KIKIN §4.bis — DADO POR PENDIENTE Y ESTÁ HECHO EN DOS DE TRES: linux-metal y
    linux-generic ya declaran los cuatro símbolos; sólo `linux` deja IO_URING y BPF_SYSCALL
    al azar del defconfig. Y ojo al método: `grep IO_URING` da positivo por un COMENTARIO;
    hay que mirar los -e/-d dentro del bloque configure. Mismo error que infló el conteo de
    licencias hoy y que hace mentir la regla del `.a` no-PIC.

OBJECIÓN a §2.1: «clausura» sobre Kconfig no está definida todavía. Hay tres tipos de arista
(`depends on`, `select`, `imply`) con semánticas incompatibles, y `select` fuerza símbolos
IGNORANDO los `depends on` del destino. «Alcanzable desde CONFIG_WIRELESS» da conjuntos
distintos según la arista y ninguno es la respuesta. No lo invalida: lo convierte en la parte
con contenido técnico real. Y hay con qué validarlo GRATIS: el config actual de linux.toml
YA es un juego de bundles N1 hecho a mano («-d WLAN -d WIRELESS -d CFG80211 -d MAC80211
-d RFKILL» es literalmente «no necesito wifi») sobre un kernel que arranca.

LO QUE AGREGO:
 · Bisección SIN recompilar: kernel «superset» con lo bisecable como módulo ⇒ los bundles de
   hardware se prueban con lista de bloqueo en la línea de arranque. 6 reinicios en vez de 6
   rebuilds. No cubre lo que debe ser =y (buena parte de N2), y el superset es el kernel gordo
   que N1 quiere evitar: es diagnóstico, no el de todos los días.
 · El gate de no-regresión debe ser POR OBJETIVO: linux.toml apaga USB/HID/INPUT a propósito
   (es el kernel de QEMU con consola serie) y un gate global rechazaría una receta sana.
 · «Booteó» no basta para atestar: un kernel arranca y te deja sin red, que es el desastre que
   el propio gate quiere evitar. La atestación debe llevar QUÉ se comprobó — el mismo dato que
   calcula el gate: una implementación, dos usos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:54:18 -04:00
sergioandClaude Opus 5 2c2ee951d6 licencias: resolver -only vs -or-later CON LA CITA — y tres «pruebas» que no probaban nada
Los identificadores obsoletos (`GPL-2.0`, `LGPL-2.1`, `AGPL-3.0`) no dicen si el proyecto
concede «sólo esta versión» o «ésta o cualquier posterior», y eso decide con qué se puede
combinar el paquete y bajo qué términos puede redistribuirlo quien lo reciba.

NO SE PUEDE MIRAR EL COPYING, que es la trampa evidente: el texto de la GPL es IDÉNTICO en
los dos casos —es la licencia, no la concesión— y encima su apéndice «cómo aplicar la
licencia» contiene literalmente «or (at your option) any later version», así que buscarla
ahí da SIEMPRE positivo y parece evidencia siendo plantilla. Misma trampa que la regla del
`.a` no-PIC, donde grep contaba reubicaciones de `.debug_*`. La concesión vive en las
cabeceras de los fuentes y en el README; ahí se busca, excluyendo los ficheros de licencia.

SE GUARDA LA CITA, no sólo el veredicto: fichero + frase exacta que decidió. Una licencia es
una afirmación legal y quien la revise tiene que poder ver POR QUÉ dice lo que dice sin
repetir el trabajo.

Y eso pagó de inmediato: de las 12 primeras resoluciones, TRES eran evidencia inválida y
sólo se vieron porque estaba la cita. Cada una de una clase distinta:
 · caligula   — la frase salía de `checks/headless/expected.iso`, un FIXTURE DE TEST;
 · libnl      — de `include/linux-private/linux/seg6.h`, una cabecera del KERNEL vendorizada.
                La licencia del kernel no es la de libnl;
 · lm-sensors — declarado GPL-2.0 y la cita decía «version 2.1 of the License», que es la
                LGPL. La frase era real pero no sostenía lo que se le atribuía.

Las tres clases quedan filtradas en el script: ficheros de licencia, rutas de test/fixture, y
código vendorizado; más una comprobación nueva de que **la cita hable de la misma versión que
la licencia declarada**.

Sembradas las 9 auditadas (13 ficheros; algunas viven en varias colas). Ambiguas: 55 → 41.
Las 40 sin evidencia quedan listadas y siguen contadas — la mayoría son Go y la familia
cosmic, donde la frase no aparece fuera del COPYING.

Hashes verificados: 0 movidos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:15:20 -04:00
sergioandClaude Opus 5 6263d24788 store-gc + farm-sync: cortar el BUCLE DE CHURN de 24 G que hacía inútil la poda
Podé el store por la mañana: 362 artefactos superados, 24 G. Lo volví a podar por la tarde:
362 artefactos, 24 G. Comparados los dos manifiestos, **solapamiento del 100%: son los
MISMOS 362**. No era casualidad ni recuento nuevo — es un bucle.

LA CAUSA no estaba en el gc sino en `farm/farm-sync.sh`, que bajaba el store del worker
ENTERO sin filtro. El worker nunca se poda, así que conservaba los superados y nos los
devolvía en cada cosecha. Podar → cosechar → vuelven → podar. El disco pagaba 24 G por
vuelta y el gc informaba éxito cada vez, lo que es peor que fallar: parecía que se avanzaba.

Es la misma familia de fallo que el gc que reportaba borrados falsos, sólo que un nivel más
arriba: entonces el borrado no ocurría, ahora ocurre y se deshace solo. En los dos casos el
mensaje de éxito era cierto y engañoso a la vez.

EL ARREGLO es darle memoria al sistema. `store-gc.sh` acumula lo podado en
`work/store-gc-superados.txt` y `farm-sync.sh` lo usa como `--exclude-from` al bajar el
store del worker. Es seguro porque el store es CAS: los nombres `<hash>-<paquete>` son
inmutables y un artefacto superado no vuelve a ser vigente salvo que una receta retroceda —
y si retrocediera, se reconstruye, que es barato.

Disco: 159 G → 182 G tras la poda de hoy, y esta vez debería quedarse.

De paso, en el respaldo: rsync 24 («ficheros del origen desaparecieron») NO es un fallo, es
justo lo que pasa al podar mientras se sube. El bucle de reintentos lo tomaba por error real
y habría parado el respaldo entero por algo inofensivo — y encima cuando el disco aprieta,
que es cuando menos conviene quedarse sin copia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:11:31 -04:00
sergioandClaude Opus 5 89c02db639 respaldo: rsync 24 (ficheros desaparecidos) no es un fallo — es podar mientras se sube
El bucle de reintentos trataba el 24 como «error que no es de red» y habría PARADO el
respaldo entero. Pero 24 significa que ficheros del origen desaparecieron durante la
transferencia, que en este proyecto es una combinación NORMAL: el respaldo dura ~19 h y
`store-gc.sh` es la válvula del disco, así que podar mientras se sube va a pasar. Y pasaría
justo cuando el disco aprieta, o sea cuando menos conviene quedarse sin copia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:06:26 -04:00
sergioandClaude Opus 5 97eb1d4af2 docs: SDD 20 — limpiar el empalme de la sección de licencias
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:54:22 -04:00
sergioandClaude Opus 5 78ad2e8201 docs: SDD 20 — corregido dónde va el texto de licencia (imágenes, no pack) y estado real 1063/1141
El catálogo decía que el texto de la licencia se inyecta en `hammer pack`. El razonamiento
del coste era correcto pero el sitio estaba mal: pack emite un .swm (receta sobre fuente
pública, sin binarios) e install reproduce construyendo, así que el canal de paquetes no
distribuye binarios. El punto de entrega es la IMAGEN, y ahí está hecho, con veto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:53:54 -04:00
sergioandClaude Opus 5 be4e91086c licencias: el texto dentro de la imagen, con veto — y el SDD 19/20 se equivocaba de sitio
═══ LA CORRECCIÓN DE FONDO: no es `pack`, son las IMÁGENES ═══
El SDD 19 y el 20 decían «inyectar el texto de la licencia en `hammer pack`, que es aguas
abajo del ArtifactHash y sale gratis». La conclusión sobre el coste era correcta pero el
SITIO estaba mal, y por una razón que sólo se ve leyendo el código: `pack` produce un
`.swm`, que es una RECETA DE TRANSFORMACIÓN sobre fuente pública y por diseño explícito
«NUNCA transporta binarios cocidos»; e `install` REPRODUCE construyendo, con cache-hit del
corpus, en vez de bajar binarios. O sea que **el canal de paquetes de hammer no distribuye
binarios** y la obligación de acompañar-el-binario ahí casi no aplica.

Donde sí aplica, con toda su fuerza, es en la IMAGEN INSTALABLE: su rootfs se puebla con
`hammer install` sobre un prefix y de ahí sale un USB/ISO lleno de binarios que se le da a
alguien. Ése es el punto de entrega. (La obligación del espejo de FUENTES es otra y sigue
entera: las recetas apuntan a URLs de terceros que se caen.)

═══ LO QUE ENTRA ═══
· `scripts/licencias-textos.sh` + `licenses/`: los 44 textos CANÓNICOS de SPDX
  (spdx/license-list-data), uno por identificador realmente en uso — la lista se saca de las
  recetas, no de un fichero escrito a mano que se desincroniza. 764K.
· `scripts/licencias-rootfs.sh`: escribe /usr/share/licenses/<pkg>/ con el texto de CADA
  licencia de la expresión (`Unlicense OR MIT` ⇒ los DOS textos: en un OR el destinatario
  elige, y entregar uno solo le quita una opción que el autor le concedió), más un
  MANIFEST.tsv. Medido: 1,0 MB para el perfil `cli` de 44 paquetes.

═══ Y SOBRE TODO: ES UN VETO ═══
Sale ≠0 si algún paquete del rootfs no declara licencia. Un informe que no puede bloquear no
cambia lo que ocurre; esto corta el paso. Y se ganó el sueldo al primer intento: reveló que
los perfiles `base` y `cli` —los dos más simples, los que primero se publicarían— llevaban
10 y 12 paquetes con binarios y licencia desconocida. Curados los 10 que se podían afirmar
(doas, iputils, less, mandoc, procps-ng, rsync, strace, sudo, tree, usbutils); ambos perfiles
bajan a 2.

Esos 2 NO se rellenan a propósito, y quedan vetando:
 · lsof   — licencia propia del proyecto, sin identificador SPDX. Toca `LicenseRef-lsof` con
            su texto, que ya viaja en el tarball (la receta instala su COPYING).
 · tzdata — dominio público por declaración de sus autores, y SPDX no tiene identificador
            para eso (es una AUSENCIA de licencia, no una licencia). Toca
            `LicenseRef-PublicDomain` documentado, no forzar uno de la lista.

De paso: normalizado `GPL-3.0+` (sufijo antiguo) a `GPL-3.0-or-later`, misma equivalencia
documentada que la barra de Cargo. Y el caso especial que puse para `Linux-syscall-note`
(«las excepciones viven en exceptions/») era una suposición razonable y FALSA: SPDX las
publica en el mismo `text/`; ese directorio no existe.

1063 de 1141 (93%). Hashes verificados: 0 movidos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:53:20 -04:00
sergioandClaude Opus 5 487e155f74 licencias: leer el LICENSE de verdad cuando GitHub dice «no sé» — 1053 de 1141 (92%)
Último recurso mecánico, `scripts/licencias-texto.sh`: para las 60 recetas donde la API de
GitHub devuelve NOASSERTION (el repo TIENE un LICENSE pero licensee no lo reconoce: texto
retocado, encabezado propio, dos licencias en un fichero), baja el texto y lo clasifica acá.
34 identificadas — 16 MIT, 10 Apache-2.0, 7 BSD-2-Clause, 1 BSD-3-Clause.

SÓLO PERMISIVAS, Y NO ES PEREZA. MIT, Apache-2.0, BSD, ISC, MPL-2.0 y Unlicense se
reconocen por frases inconfundibles y NO tienen variantes -only/-or-later ⇒ reconocer el
texto da el SPDX completo.

La familia GPL se deja al humano A PROPÓSITO, y la razón es sutil: el COPYING de la GPL es
IDÉNTICO tanto si el proyecto es «sólo v3» como «v3 o posterior» — lo que las distingue vive
en las cabeceras de los fuentes. Y encima el propio COPYING incluye, en su apéndice «cómo
aplicar la licencia», la frase «or (at your option) any later version», así que buscarla da
SIEMPRE positivo y parecería evidencia siendo texto de plantilla. Es exactamente la trampa
de la regla del `.a` no-PIC, donde grep contaba reubicaciones de .debug_* y mentía.

Comprobado a mano un caso que daba mala espina: el LICENSE de `conftest` empieza
«Conftest — Write tests against your config files / Copyright (C) 2019 …», que es el formato
típico de una cabecera GPL. Leído entero, dice «Licensed under the Apache License, Version
2.0». La clasificación era correcta; la sospecha, barata.

Hashes verificados sobre las 36 tocadas: idénticos.

Quedan 88, ya sin vía mecánica: los repos donde ni la API ni el texto deciden, la familia
GPL sin desambiguar, y tarballs de sitios propios (gmplib, xiph, sr.ht, codeberg…).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:06:59 -04:00
sergioandClaude Opus 5 0225aa4e08 licencias: el código propio (tawasuyu) queda en MIT — 1017 de 1141 (89%)
Decisión del usuario: «tawasuyu está en MIT». Es la misma licencia que hammer (LICENSE en
la raíz + Cargo.toml), así que todo lo nuestro queda coherente bajo un solo término. Las 20
recetas con fuente en git.tawasuyu.net: arje-* (el init y sus compat), mirada-* (el
compositor, el greeter y mirada-ctl), netup, agora-cli, cosmos-cli, dominium-cli,
tinkuy-sim, llimphi-counter y libelogind.

⚠ ANOTADO EN LA TABLA porque es una trampa que casi pisé: `libelogind` NO es el elogind de
upstream (que sería LGPL-2.1+). Por el nombre lo parece; su cabecera dice que es una
reimplementación PROPIA de la C-ABI sd-login dentro de arje-compat. Declararla LGPL «porque
se llama así» habría sido exactamente el error que este fichero prohíbe — el tercero del
mismo tipo hoy, después de `knighttime` (parecía Go y es KDE) y de la familia `kube*`
(parecen KDE y son Go). El nombre nunca es evidencia; la fuente sí.

Hashes verificados sobre las 20: idénticos.

Quedan 124 sin declarar, ya todas de terceros.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 14:01:15 -04:00
sergioandClaude Opus 5 c8d1357f06 licencias: la declaración del autor manda — y arreglo 5 recetas que ROMPÍ y commiteé
Dos cosas: una mejora de calidad y un fallo mío que hay que contar entero.

═══ EL FALLO: escribí un error JSON dentro de 5 recetas y las commiteé rotas ═══
En `a6a9d59`, cinco recetas quedaron con
    license = "{"message":"Not Found","documentation_url":"...","status":"404"}"
Causa: cuando el repo da 404 (renombrado, borrado, privado), `gh api` escribe el CUERPO DEL
ERROR en stdout y el filtro `--jq` falla, así que la variable queda valiendo el JSON. Mi
`case` sólo descartaba ""/null/NOASSERTION/other ⇒ el JSON pasó como si fuera una licencia.
Las comillas ROMPEN el TOML, y esas 5 recetas dejaron de parsear y de tener ArtifactHash.

No lo vio nadie leyendo: lo cazó la comparación de hashes, al detectar que `cargo-sort`
había cambiado. Afectadas: cargo-sort, cargo-audit, cargo-binstall, waybackurls, usbutils.

La lección no es «se me pasó un caso», es que **validé los «no sé» conocidos en vez de la
FORMA del dato**. Ahora se acepta sólo lo que tiene forma de expresión SPDX, y la barrera
está en los DOS sitios que escriben (detectar y sembrar), más un GUARDIÁN en el informe que
sale ≠0 si alguna licencia tiene forma imposible. Un fallo aguas arriba disfrazado de dato
hay que verlo sin buscarlo.

Y de paso normalizadas 11 licencias con la barra antigua de Cargo (`MIT/Apache-2.0`), que
no es SPDX válido. Traducir la barra a OR no es inferir: Cargo documentó esa equivalencia.

═══ LA MEJORA: `scripts/licencias-cargo.sh` ═══
Lee la licencia que el AUTOR declara en su Cargo.toml, pidiéndola AL TAG QUE LA RECETA
PINEA (`?ref=v<version>`, con HEAD como último recurso y anotando cuál se usó). 176
obtenidas, 167 al tag exacto. Es la fuente más autoritativa de las que usamos y corrige los
dos defectos de la detección por API de una vez:
 · las licencias DOBLES dejan de colapsar: `fd` pasa de `Apache-2.0` a `MIT OR Apache-2.0`,
   `ripgrep` a `Unlicense OR MIT`, `bat` a `MIT OR Apache-2.0`;
 · los SPDX AMBIGUOS caen de 71 a 55, porque el autor sí escribe -only/-or-later.
66 corregidas, 7 nuevas. Y aparecieron cosas que la detección había perdido: `eza` es
EUPL-1.2, una copyleft europea que se habría empaquetado creyendo otra cosa.

Jerarquía de evidencia, escrita en la cabecera del script: nombre (prohibido) < familia/URL
de fuente < API de licencias de GitHub < Cargo.toml del autor.

997 de 1141 (87%). Verificación COMPLETA sobre las 77 recetas tocadas —no una muestra—:
0 hashes cambiados, y 5 que ahora parsean y en HEAD no.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:51:09 -04:00
sergioandClaude Opus 5 5e42d201b1 respaldo: reintentar los cortes de red — el primero murió a los 2,3 G
La primera corrida real subió el cerebro (estado + repo, lo irreemplazable) y 2,3 G de
store, y entonces murió: `Connection reset by peer` / `Broken pipe`, rsync 255. Con ~19 h
de subida sobre un enlace de oficina, que la conexión se corte NO es un accidente: es lo
normal. Y un respaldo que hay que relanzar a mano cada vez que se cae no se completa nunca,
porque nadie está mirando.

Como rsync ya es incremental y `--partial-dir` conserva los trozos a medio subir,
reintentar es barato y seguro: cada intento retoma donde quedó, no reempieza. El bucle
insiste sólo ante fallos de RED (10/12/23/30/35/255) con espera creciente hasta 60 s; un
error de verdad —permisos, destino lleno, ruta inexistente— sale a la primera en vez de
repetirse 40 veces contra la misma pared.

DE PASO, UN ERROR MÍO QUE VALE REGISTRAR: comprobé el respaldo con `pgrep -f
respaldo-storagebox` y dijo que corría, cuando llevaba rato muerto — el pgrep se estaba
matcheando A SÍ MISMO, porque la cadena buscada está en la propia línea de comando del
shell que la ejecuta. Ya me había pasado hoy con el worker. La comprobación buena es mirar
lo que el proceso PRODUCE (bytes en el destino, última línea del log), no si hay un proceso
vivo. Es la misma lección que el guardián de store-gc: un `rm` que devuelve 0 no prueba que
el fichero se fue, y un proceso vivo no prueba que esté avanzando.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:30:58 -04:00
sergioandClaude Opus 5 fe474dab63 licencias: 994 de 1141 (87%) — cerradas las familias Qt, freedesktop, kernel.org y PyPI
Tercera tanda curada, agrupada por la URL DE FUENTE de cada receta (que es la evidencia que
el nombre no da): los once módulos Qt sin prefijo, freedesktop (dbus, libinput, libevdev,
libdisplay-info, poppler, pulseaudio, upower, NetworkManager, ModemManager, polkit),
kernel.org (los tres kernels, linux-headers, git, iproute2, libuuid) y PyPI.

Los kernels llevan `GPL-2.0-only WITH Linux-syscall-note` explícita. No es adorno: sin esa
excepción, todo binario de espacio de usuario que hace un syscall sería obra derivada del
kernel. Es exactamente el tipo de dato que un campo de licencia existe para no perder.

Quedan 147 sin declarar: 69 de GitHub donde la propia API dice NOASSERTION (hay que abrir
el fuente), 20 de tawasuyu —que son nuestras o del otro agente, así que la licencia la
decide el usuario, no yo— y el resto repartido en GNOME, gitlab.freedesktop y sueltos.
Más 71 con SPDX ambiguo pendientes de desambiguar (`licencias.sh --revisar`).

Hashes verificados otra vez sobre 20 recetas tocadas: 20 idénticos, 0 cambiados.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:29:31 -04:00
sergioandClaude Opus 5 a6a9d59e4f licencias: 936 de 1141 (82%) — detección por evidencia, con sus dos imprecisiones declaradas
Esta mañana eran 0. Tras la tabla curada (228) y este paso, 936. Ninguna receta se
re-hasheó: verificado en 30 de las 708 tocadas, 30 hashes idénticos, 0 cambiados.

DE DÓNDE SALE EL DATO, Y POR QUÉ NO ES ADIVINAR. `scripts/licencias-detectar.sh` consulta
la API /repos/{o}/{r}/license de GitHub para las 673 recetas cuya fuente vive allí. Eso
devuelve el resultado de DETECTAR el fichero LICENSE que el repo tiene de verdad
(licensee), no una etiqueta escrita a mano en una web: es la misma evidencia que veríamos
abriendo el tarball, obtenida sin bajar 673 tarballs por un enlace de 8 Mbps. 610 con SPDX
definido; las 63 que GitHub marca NOASSERTION/other se DESCARTAN — un «no sé» de la fuente
se propaga como hueco, no se redondea a una licencia plausible.

Las familias no-GitHub van curadas por la URL DE FUENTE, que es la evidencia que el nombre
no da. Y ahí cometí el error simétrico al que este mismo fichero advertía: había puesto
`knighttime` entre las «herramientas Go» POR SU NOMBRE, y su tarball sale de
download.kde.org — es un Framework de KDE, LGPL. Descartar por nombre falla igual que
aceptar por nombre. Corregido en la cabecera.

LAS DOS IMPRECISIONES, contables con `licencias.sh --revisar` en vez de escondidas:

 1. SPDX OBSOLETOS Y AMBIGUOS (71 recetas). GitHub devuelve `GPL-3.0`, `LGPL-2.1`,
    `AGPL-3.0`… identificadores que SPDX declaró obsoletos PRECISAMENTE porque no
    distinguen `-only` de `-or-later`, y esa diferencia decide con qué se puede combinar el
    paquete. NO se normalizan a ciegas: mapear GPL-3.0 → GPL-3.0-or-later sería inventar el
    dato que falta. Quedan marcadas para resolver mirando el fuente.

 2. LICENCIAS DOBLES COLAPSADAS. La API devuelve UNA sola licencia y muchos proyectos Rust
    son «MIT OR Apache-2.0» (p.ej. `fd` quedó como Apache-2.0). No es falso —cumplir una de
    las opciones concedidas basta— pero es incompleto. Lo resuelve el cierre estructural:
    leer el campo `license` del Cargo.toml en la fase de fetch.

Y un fallo que habría escrito basura en silencio: `licencias-detectadas.tsv` tiene TRES
columnas (añade el owner/repo consultado, para poder auditar) y el sembrador leía dos, así
que la licencia se habría llevado pegado el slug — `license = "MIT<TAB>owner/repo"`, sin
que nada lo validara. Arreglado antes de sembrar.

Quedan 205 sin licencia y 71 por desambiguar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 13:26:22 -04:00
sergio 6060b9bbcd estado: cosecha granja 2026-08-07T16:49:23Z — avance del árbol KDE 2026-08-07 12:49:23 -04:00
sergioandClaude Opus 5 7b2489a03c deuda del corpus: las 12 no eran un problema, eran cuatro — medido en el worker
Fui a destrabar «las 12 recetas en deuda» con el diagnóstico heredado (fallan por el rootfs
del worker: python OSError en meson, find_package en cmake). Lo probé construyéndolas de
verdad y **el diagnóstico es falso**: cmake y meson corren perfectamente ahí. Son cuatro
situaciones distintas y ninguna es el rootfs.

(a) SEIS ESTÁN SUPERADAS, NO EN DEUDA. `gtk4` del corpus es link=static y enlaza
    libfontconfig.a, que tiene 1122 reubicaciones no-PIC ⇒ 13.032 errores
    `R_X86_64_64 cannot be used against local symbol`. No es una receta rota: es imposible.
    Y mientras tanto recipes/incoming-gnome/gtk4.toml (link=dynamic, deps -shared) YA ESTÁ
    SELLADA, con libadwaita y fontconfig-shared. O sea que la cadena estática del corpus
    —gtk4, libadwaita, gtksourceview y los tres hello/edit que cuelgan— es un DUPLICADO
    superado de la cadena dinámica de GNOME.
    Cerrarla no es construirla: es decidir si se promueven las sombras -shared al corpus,
    porque la resolución de deps es hermano→padre. Es una decisión de arquitectura, y hay
    aviso registrado de que promover a ciegas hace que variantes homónimas pisen recetas
    canónicas. NO la tomo yo.

(b) wlr-randr: cerrada en el commit anterior.

(c) dwarves: frente propio con muro identificado, no deuda. Nuestro elfutils entrega SÓLO
    libelf a propósito (libdw arrastra argp/obstack/fts, lo difícil en musl). No se arregla
    ampliando elfutils: de él cuelga el kernel que ya reproduce bit a bit. El camino es una
    receta aparte `elfutils-libdw`, con el patrón de las sombras -shared. Sin urgencia:
    ningún perfil pide dwarves y el kernel desactiva DEBUG_INFO_BTF justamente por su
    ausencia. Queda escrito en la cabecera de la receta, que es donde se va a leer.

(d) mirada-compositor, mirada-greeter, llimphi-counter: source por SSH a
    git.tawasuyu.net y el worker es SIN SECRETOS por diseño ⇒ HUB-ONLY estructural, no
    fallo. llimphi-counter ni siquiera llega a construir: su commit son ceros con el
    comentario «fijar al commit real». Receta sin terminar, y es de tawasuyu.

EL HALLAZGO DE FONDO: el bucle del worker sólo recorre las colas incoming-*; el corpus
(recipes/) NO está en QUEUES. Las 12 nunca se habían intentado allí. Buena parte de «la
deuda» era de ENCOLADO, no técnica — y por eso el diagnóstico heredado nunca se verificó.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 12:27:03 -04:00
sergioandClaude Opus 5 f24b52df18 wlr-randr: SELLADA — eran dos deps ausentes, no el rootfs del worker
Primera de las 12 recetas del corpus en deuda, cerrada. Y de paso desmiente el diagnóstico
heredado («las 12 fallan por el rootfs del worker: python OSError en meson, find_package en
cmake»). Medido hoy contra el worker, las causas son CUATRO distintas y ninguna es el
rootfs:

 · gtk4 construye ahí SIN TOCAR NADA. Nunca había fallado: el bucle del worker sólo recorre
   las colas `incoming-*` y NO incluye el corpus, así que las 12 jamás se intentaron. La
   deuda no era técnica, era de encolado.
 · wlr-randr (ésta): dos deps que faltaban en la receta.
 · dwarves: FindDWARF no halla las libs ELF/DWARF pese a declarar elfutils. Pendiente.
 · mirada-compositor, mirada-greeter, llimphi-counter: `repo = gitea@git.tawasuyu.net:...`
   por SSH. El worker es SIN SECRETOS por diseño ⇒ son HUB-ONLY estructuralmente, no un
   fallo. Y llimphi-counter ni siquiera es un fallo de build: su commit es literalmente
   ceros, con un comentario «fijar al commit real» — es una receta sin terminar, y es de
   tawasuyu.

LAS DOS DEPS DE ESTA RECETA, que son dos lecciones distintas:

 1. `python3` — porque **meson es un script de Python, no un binario**. Faltando el
    intérprete la fase muere con exit 127, que se lee como «meson no está» cuando meson SÍ
    está y se hidrata bien. gtk4/libadwaita/gtksourceview construyen en el worker
    precisamente porque sí lo declaran. Auditadas las 1141 recetas: wlr-randr era **la
    única** con meson y sin python3. Regla: meson en deps ⇒ python3 al lado.

 2. `libffi` — que wlr-randr no usa. Lo exige `wayland-client.pc` en su línea `Requires`, y
    pkgconf resuelve el grafo de .pc completo antes de emitir un cflag. Patrón ya
    documentado `.pc Requires` → `[deps].build`; el síntoma («Package 'libffi', required by
    'wayland-client', not found») culpa a wayland, que es inocente.

En ambos casos el arreglo es hidratar la dep, NO instalar nada en el rootfs del worker:
engordarlo haría que el build dependa de qué hay en una máquina concreta, que es justo lo
que rompe la reproducibilidad.

Re-hashear sale gratis: la receta estaba en deuda, nunca se había sellado.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 12:23:28 -04:00
sergioandClaude Opus 5 e79b26970a docs: SDD 20 (catálogo hasta publicable y hasta completa) + SDD 21 (manual del piloto)
Pedido del usuario: «el catálogo de todo lo que falta hasta decir la distro está
publicable, y lo de más hasta la distro está completa. Y un tipo manual, todos los pasos
que tengo que ir haciendo en las tres etapas que marcan esos dos codos.»

SDD 20 = el inventario, medido contra el repo (grafo, no recuento a mano). SDD 21 = el
manual de a pie, un comando por vez, marcando qué puede hacer el agente y qué es del
usuario.

LO QUE APARECIÓ AL MEDIR, y que no se sabía:

· La deuda de construcción NO son 12+12+13+12 recetas: es UNA lista de 12 vista desde
  cuatro grafos (la cadena GUI de la cascada de mesa). Un solo arreglo —declarar python3 y
  cmake en `[deps]`, no engordar el rootfs del worker— las destraba todas. `base` y `cli`
  ya están al 100%, KDE cierra 162/162.

· De WMs Wayland ligeros no hay NINGUNO: sólo `foot` y `mako`. Falta wlroots entero, los
  compositores y todos los accesorios (waybar, fuzzel, swaybg, grim, slurp, wl-clipboard).

· De aplicaciones gráficas de terceros hay CERO. Ni visor de imágenes ni lector de PDF.

· Firefox no es «una receta más»: su cadena de `*-sys` con C++ es justo la frontera que el
  techo MSRV del sandbox (1.96) no cruza. Antes de Firefox hay que subir el techo; empezar
  por la receta es empezar por el final.

· El texto de la licencia va inyectado en `hammer pack`, NO en la fase install: las fases
  entran al ArtifactHash, así que hacerlo en la receta re-hashearía las 1141. Pack es aguas
  abajo y sale gratis. Misma lógica que hizo pagable el campo `license`.

Dos correcciones al SDD 19, que escribí yo ayer y tenía mal:
· «5 de 771 recetas declaran licencia» — falso por partida doble. El número real era 0 de
  1141; los «5» eran falsos positivos de `grep license` (nombres de paquete, una línea de
  install, un comentario) y 771 son los nodos del grafo del corpus, no las recetas.
· «El repo no declara licencia» — falso: hay LICENSE (MIT) en la raíz y en Cargo.toml. No
  lo miré antes de escribirlo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:28:37 -04:00
sergioandClaude Opus 5 346cd59706 licencias: campo license en la receta — de 0 a 228 de 1141, sin re-hashear nada
La deuda legal bloqueante del SDD 19 §2.1. Medido hoy: **0 de 1141 recetas** declaraban
licencia, no «5 de 771» como decía el informe anterior. Los dos números estaban mal: los
«5» eran falsos positivos de `grep license` (el paquete `addlicense`, el paquete
`cargo-bundle-licenses`, una línea `install .../share/licenses/` y un comentario), y las
recetas son 1141. Contar con `grep -l <palabra>` sobre TOML cuenta comentarios y nombres,
no campos; `scripts/licencias.sh` cuenta el campo de verdad (clave en la raíz, antes del
primer `[table]`).

LO QUE HACE LA DEUDA PAGABLE: `Recipe::hash_inputs` es una LISTA BLANCA — sólo entran
source, compiler, target, link, patches, flags, phases y deps. `license` no entra, igual
que `evidence` y `slots`. Por eso se puede poblar en las recetas YA SELLADAS sin mover un
solo ArtifactHash. Verificado, no supuesto: en 40 recetas modificadas se comparó el hash
con y sin la línea — 40 idénticos, 0 cambiados. Si el campo entrara al hash, declarar la
licencia costaría reconstruir el corpus entero y no se haría nunca. Clavado con el test
`licencia_round_trip_y_no_afecta_el_hash`.

TRAMPA DE TOML: una clave suelta después de un `[table]` pertenece a esa tabla. Puesta al
final del fichero, `license` acaba dentro de `[deps]` y se pierde EN SILENCIO, porque serde
ignora los campos que no conoce — no hay error, simplemente no está. Va arriba, junto a
`name` y `version`; el sembrador la inserta tras `version`.

NO SE ADIVINA. Declarar mal una licencia es peor que dejarla vacía: convierte un hueco
visible en una afirmación falsa. Sólo se puebla desde una tabla curada entrada por entrada
(`docs/licencias-conocidas.tsv`); lo que no tiene evidencia queda vacío y se CUENTA.
Concretamente se descartó el atajo «k* = KDE ⇒ LGPL»: en este catálogo `kail`, `kind`,
`ko`, `kopia`, `krew`, `kustomize`, `kyverno`, `katana`, `kibi`, `kmon` y toda la familia
`kube*` son herramientas Go sin relación con KDE. El nombre no es evidencia.

Quedan 913, casi todas CLIs Go/Rust importados en masa — y ésas sí son automatizables con
evidencia real: Cargo.toml trae el campo `license` y los módulos Go traen su LICENSE en el
árbol. El cierre estructural es capturarlo en la fase de fetch, que ya descarga y extrae
cada tarball, y inyectar el texto en `hammer pack` (aguas abajo del ArtifactHash) en vez de
en la fase install (que sí re-hashearía).

De paso, respaldo-storagebox.sh reordenado por valor irreemplazable y con zstd: medido en
la oficina, el uplink da 8 Mbps iguales por cable y por wifi ⇒ 128 G no caben en una
sentada, así que sube primero el cerebro (estado + repo) y `--partial-dir` hace que cortar
a mitad de un artefacto no tire lo ya subido.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 10:24:11 -04:00
sergioandClaude Opus 5 b12ea3ac9d docs: SDD 19 — qué falta de verdad para lanzar la distro al público
Lo que falta para TERMINAR de construir no es lo mismo que lo que falta para
PUBLICAR. Lo primero es trabajo conocido (16 recetas, WMs Wayland ligeros,
apps de terceros — con el navegador como frente propio). Lo segundo tiene dos
bloqueantes que hoy no existen y no se improvisan el día del anuncio:

1. METADATOS DE LICENCIA: sólo 5 de 771 recetas los declaran. La distro no sabe
   bajo qué términos redistribuye el 99% de lo que empaqueta. Hace falta campo
   obligatorio validado por hammer + el texto dentro del artefacto.
2. ESPEJO DE FUENTES: la GPL no pide que el código exista en internet, pide que
   quien recibe el binario pueda obtener la fuente correspondiente DE VOS. Acá
   estamos bien parados —cada receta pinea tarball+sha256— pero hay que hacerlo.

Más: gestión de claves (firma sin eso es teatro), evidencia de reproducibilidad
publicada como diferenciador comprobable, imagen validada en METAL, y ciclo de
actualización ensayado antes de publicar.

Recomendación de arranque: el campo de licencia obligatorio, ya. A 771 recetas
es un rato; a 2000 es un proyecto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 08:27:14 -04:00
sergio 809b154586 estado: cosecha granja 2026-08-07T12:15:14Z — avance del árbol KDE 2026-08-07 08:15:14 -04:00
sergioandClaude Opus 5 60d8ca9bbf respaldo: Storage Box BX11 en Hetzner — no dependemos más de un laptop
El usuario pidió respaldar en «el volumen, aunque sea montándolo aquí». No se
puede: un HC Volume es un dispositivo de bloque por RED, sólo se adjunta a
servidores de Hetzner del mismo DC, y sólo a uno a la vez. harkaq-cosecha es el
disco del worker efímero y vvv está al 78%.

El producto correcto es un Storage Box: se monta desde el laptop (SSHFS/CIFS) y
habla rsync/borg/restic por SSH. BX11 = 1 TiB, €3,20/mes, sin alta. Creado en
hel1 con protección de borrado y la clave github5.

rsync plano y no borg: el store es CAS, los ficheros son inmutables y se nombran
por hash, así que incremental es exactamente lo correcto y no hay repo ni claves
que mantener. borg comprimiría 4x (79% del store es .debug_) pero 126 G en 1 TiB
no aprieta.

El store va SIN --delete a propósito: un respaldo que replica los borrados no
protege del borrado por error, y store-gc.sh acaba de demostrar que puede
equivocarse en silencio.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 08:10:30 -04:00
sergioandClaude Opus 5 514ae44135 store-gc: guardián — no reportar «borrado» sin comprobarlo
El script informó «364 artefactos borrados · libres: 24G → 48G» y no había
borrado ninguno: los 364 de su propio manifiesto seguían enteros en el store.
`xargs rm -rf` salió con 0 y el echo se lo creyó. Se reprodujo: corriéndolo en
SEGUNDO PLANO el borrado no se materializa; en primer plano sí.

Da igual la causa: un rm que devuelve 0 no es prueba de que el fichero se fue.
La válvula de escape del disco estaba rota EN SILENCIO y encima reportaba
éxito, que es peor que fallar — el disco llegó al 98% mientras yo creía haber
liberado 24G.

Ahora recuenta sobre el filesystem y sale distinto de cero si sobrevivió algo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 07:44:54 -04:00
sergioandClaude Opus 5 85c25619ad recipes: wlr-randr 0.5.0 — el testigo ajeno del protocolo de salidas
La paridad cosmic no se demuestra con un cliente nuestro (eso probaria que nos
entendemos con nosotros mismos), se demuestra con la herramienta que usa el
resto del mundo. wlr-randr habla wlr-output-management-unstable-v1, que mirada
sirve. Entra a base-system-1 con ese rol acotado.

C sobre meson, 28 KB, trae el XML del protocolo adentro: solo wayland-client.
Tarball de release y no el -/archive/ autogenerado de GitLab, que no es estable
byte a byte y rompe el sha256 pinneado con el tiempo.

Queda anotado en la receta lo que hoy NO puede hacer, para no acusar al paquete:
en mirada apply/test de zwlr_output_configuration_v1 contestan failed, asi que
wlr-randr lista bien y no cambia nada. Cuando mirada implemente el apply, esta
misma receta pasa a probar tambien el apply.

Por lo mismo no entran kanshi ni way-displays: no son herramientas sino una
funcion —perfiles de salida por hotplug— y su lugar es adentro de mirada, que ya
tiene el estado. Instalarlos hoy seria ademas inutil.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 07:44:00 -04:00
sergio dd0926df24 estado: cosecha granja 2026-08-07T11:32:45Z — avance del árbol KDE 2026-08-07 07:32:45 -04:00
sergioandClaude Opus 5 aeac4a2ebb gnome: xdg-desktop-portal-gnome SELLA — isla de libadwaita + frontend a 1.19.4
Cae el último muro real de la cola GNOME. Cuatro piezas, ninguna en la receta
que fallaba:

1. SOMBRA de libadwaita en incoming-gnome/. El «relocation ... libfontconfig.a»
   no lo ponía xdp-gnome sino el cierre de la libadwaita del CORPUS, receta de
   la era estática. La del corpus no se toca (3 dependientes ahí).
2. libyaml-shared: la única no-PIC del cierre de libadwaita sin variante.
3. xdg-desktop-portal 1.19.4 en esta cola (COSMIC sigue en 1.18.4). xdp-gnome 48
   exige >=1.19.1, y el corte de la dep dura de gstreamer es 1.19.1 EXACTO
   —1.19.0 no la pide—, así que no hay versión que cumpla ambas. Se saca con dos
   sed: gstreamer alimenta un solo binario auxiliar (validate-sound) que el
   daemon invoca por exec, no linkea. Precio: notificaciones con sonido propio
   quedan rechazadas. Barato vs traer gstreamer+gst-plugins-base al corpus.
4. gettext-tiny + los cierres .pc de libadwaita-1 y gnome-desktop-4.

Y se corrige una regla que estaba MAL escrita en la receta: medir PIC con
`readelf -r x.a | grep R_X86_64_32` cuenta las reubicaciones de .rela.debug_*,
inocuas y presentes en todo objeto. Así medido libepoxy da 39.630 «no-PIC» y
sin embargo está embebida dentro de la libgtk-4.so de la isla, con 3.446
símbolos epoxy_ definidos. Excluyendo debug el control sale limpio (libepoxy=0,
fontconfig=1122), y el barrido dio 7 no-PIC en vez de 13: ahorró cinco builds,
dos de ellos openssl y curl.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 07:18:49 -04:00
sergio 6f215a12dd estado: cosecha granja 2026-08-07T04:14:38Z — avance del árbol KDE 2026-08-07 00:14:38 -04:00
sergioandClaude Opus 5 8aee137e36 gnome: xdp-gnome a la isla dinámica + diagnóstico del muro que queda
Deps canónicas -> variantes -shared, y link=static -> dynamic: la receta era
de la era estática y el link moría con «relocation R_X86_64_64 ... recompile
with -fPIC» sobre libfontconfig.a.

No sella todavía, y el muro queda MEDIDO en la receta para que nadie itere en
falso: el cierre tiene un único camino a la fontconfig estática,
xdp-gnome -> libadwaita -> fontconfig, y esa libadwaita es la del CORPUS, una
receta entera de la era estática (gtk4/glib/cairo/pango canónicas). No le falta
una variante: no pertenece a la isla dinámica.

El paso siguiente es una sombra de libadwaita en incoming-gnome, como se hizo
con glib. `yupana radio libadwaita` = 4 dependientes, 0 sellados cayendo.

El frontend generico xdg-desktop-portal SI sello (b3:db5e5cf8).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 23:59:21 -04:00
sergio cd62c82a0c estado: cosecha granja 2026-08-07T03:30:03Z — avance del árbol KDE 2026-08-06 23:30:03 -04:00
sergio db58515226 estado: cosecha granja 2026-08-07T02:57:00Z — avance del árbol KDE 2026-08-06 22:57:00 -04:00
sergio 494e8fe4bb estado: cosecha granja 2026-08-07T02:24:51Z — avance del árbol KDE 2026-08-06 22:24:51 -04:00
sergio 02cc25542d estado: cosecha granja 2026-08-07T01:52:21Z — avance del árbol KDE 2026-08-06 21:52:21 -04:00
sergio 42d465e490 estado: cosecha granja 2026-08-07T01:19:32Z — avance del árbol KDE 2026-08-06 21:19:32 -04:00
sergio a86eb63244 estado: cosecha granja 2026-08-07T00:46:50Z — avance del árbol KDE 2026-08-06 20:46:50 -04:00
sergio 3605bf63b2 estado: cosecha granja 2026-08-07T00:12:22Z — avance del árbol KDE 2026-08-06 20:12:22 -04:00
sergioandClaude Opus 5 14bed923ad granja: el worker no molía GNOME — faltaban dos colas en QUEUES
El default era fósil de cuando `incoming-gnome-onda1` era el frente entero:
listaba esa cola (6 recetas) pero NO `incoming-gnome` (86, donde viven gtk4,
mutter y gnome-shell) ni `incoming-gnome-onda2` (la isla dinámica, 22). El
worker reportaba moler GNOME y molía 6 de 114.

Se agregan las dos y se mueven las tres ANTES de incoming-kde: con 205 recetas
KDE por delante, un ciclo se consumía sin darles turno aunque estuvieran
listadas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 19:51:41 -04:00
sergioandClaude Opus 5 29d6b32312 gnome: cerrar la frontera de xdg-desktop-portal en la cola GNOME
xdg-desktop-portal-gnome documentaba en prosa que le faltaba el frontend
genérico («la dep dura que falta»). La campaña COSMIC ya lo autoró y selló,
así que se copia a incoming-gnome/ junto con su dep fuse3 y se declara.

NO es cache-hit, y conviene dejarlo escrito: desde incoming-gnome el cierre
resuelve contra la glib de la isla dinámica en vez de la de COSMIC, así que
el ArtifactHash se mueve (f4adb8c9 -> db5e5cf8) y hay que construirlo. Es lo
correcto —backend y frontend tienen que compartir glib— y es el mismo patrón
ya medido con pipewire.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 19:50:12 -04:00
sergio 63c28ae6e6 estado: cosecha granja 2026-08-06T20:11:33Z — avance del árbol KDE 2026-08-06 16:11:33 -04:00
sergio 55ce763e36 estado: cosecha granja 2026-08-06T19:10:27Z — avance del árbol KDE 2026-08-06 15:10:27 -04:00
sergio ba84fecb26 estado: cosecha granja 2026-08-06T18:10:01Z — avance del árbol KDE 2026-08-06 14:10:01 -04:00
sergio c4a8c09022 estado: cosecha granja 2026-08-06T17:09:27Z — avance del árbol KDE 2026-08-06 13:09:27 -04:00
sergioandClaude Opus 5 adbda20e30 🏁 ScreenCast CERRADO en metal — la cadena entera devuelve un stream real
Sube el pin de portal-probe a a5c959b (persist_mode + --wait + flush línea a línea).

MEDIDO en la OptiPlex 3060 (UHD 630, iris por hardware), handshake completo de 4 pasos:

    [3/4] Start → streams: node id 75, position (0,0), size (1920,1080), source_type 1
                  restore_token "d2b5b24f-2f2c-44e2-9a41-014f965b389b"
    [4/4] OpenPipeWireRemote → fd = 5 → socket:[84493]
    == portal-probe screencast: OK ==

Y PipeWire tiene el nodo `cosmic-screencast` como Video/Source con puertos capture_0/1.

El muro no era la GPU. La hipótesis del EGL por software queda falsificada: en metal, con
iris real, el síntoma era idéntico hasta que se mandó `persist_mode`. Era un strlen(NULL)
en xdg-desktop-portal 1.18.4 (ver el commit anterior), que mataba al portal justo antes de
emitir el Response.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 13:07:06 -04:00
sergioandClaude Opus 5 a5c959b618 🎯 portal-probe: omitir persist_mode MATA al portal — por eso Start no contestaba nunca
Causa raíz de que ScreenCast se quedara colgado en Start, medida en metal (OptiPlex 3060).
No era el EGL por software de QEMU: en metal, con iris por hardware, pasaba exactamente
lo mismo. Esa hipótesis queda falsificada.

Lo que pasa de verdad, con cuatro segfaults reproducibles (uno por intento, todos `at 0`
y todos en el MISMO offset de libc, 0x34f64 = `strlen`):

  1. el cliente no manda `persist_mode` ⇒ el portal asume PERSIST_MODE_NONE
  2. el backend de COSMIC devuelve `restore_data` de todos modos
  3. xdg-desktop-portal 1.18.4 entra por la rama NONE de
     xdp_session_persistence_generate_and_save_restore_token, que hace
     `g_clear_pointer(in_out_restore_token, g_free)` — token = NULL
  4. a la vuelta, el llamador hace `g_variant_new_string(*in_out_restore_token)` SIN
     comprobar nada ⇒ glib llama strlen(NULL) ⇒ SIGSEGV

El portal muere JUSTO ANTES de emitir el Response. Desde el cliente eso se ve como «Start
aceptado y nunca contesta», que es indistinguible de un backend que se cuelga — y nos
mandó a buscar el problema en la GPU durante meses.

Con persist_mode >= 1 el token se genera (uuid) y no hay nulo. Es un fallo de aguas
arriba; esto lo esquiva desde el cliente sin tocar el portal. Por defecto 1 (transitorio),
que es lo que quiere cualquiera que sólo va a capturar una vez.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 13:05:34 -04:00
sergioandClaude Opus 5 8182e459b0 🔭 portal-probe: el log quedaba vacío justo mientras medía
Redirigida a fichero, stdout se bufferiza por bloques: mientras la sonda espera un
Response el log muestra sólo la línea de la llamada. Eso se lee como «se colgó al
llamar» cuando en realidad estaba esperando como debe. Nos pasó depurando ScreenCast en
metal — dos minutos mirando un fichero que no avanzaba, con la sonda perfectamente viva.

setvbuf(_IOLBF) incondicional, no sólo cuando la salida es una terminal: el caso que
importa es justamente el redirigido.

Una sonda que no se puede leer MIENTRAS mide es media sonda.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 12:51:46 -04:00
sergio c5e6edc73e estado: cosecha granja 2026-08-06T16:39:08Z — avance del árbol KDE 2026-08-06 12:39:09 -04:00
sergioandClaude Opus 5 5ababec1a5 🔭 portal-probe: el plazo de los pasos que esperan a una PERSONA ahora es ajustable
`Start` de ScreenCast y el `Screenshot` interactivo ABREN UN SELECTOR: no terminan hasta
que alguien elige una pantalla. Tenían 60s clavados, y en metal eso alcanza para que la
sonda se rinda antes de que nadie llegue a hacer clic. El informe entonces dice «el
portal aceptó la llamada y no contestó», que se lee como un fallo del portal cuando la
cadena está simplemente esperando.

Es el mismo error de método que ya nos costó tres diagnósticos falsos en esta campaña
(initramfs 5s, cosmic-diag 50s, wifi-up 6s): medir contra el reloj en vez de contra el
hecho. Acá el hecho depende de un humano, así que el reloj tiene que ser AJUSTABLE, no
adivinado.

Los plazos de CreateSession/SelectSources siguen en 15s a propósito: esos pasos no le
piden nada a nadie, y si tardan es que algo anda mal de verdad.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 12:24:38 -04:00
sergio c18988f1d5 estado: cosecha granja 2026-08-06T16:07:52Z — avance del árbol KDE 2026-08-06 12:07:52 -04:00
sergioandClaude Opus 5 99efe4998b 🖥 mesa: -Dshader-cache=disabled — el escritorio COSMIC ARRANCA en metal
Cierra el segfault mudo de iris. El intento anterior (`-Wl,--build-id=sha1`) NO prendió:
meson lo registra pero `zig cc` 0.13 lo ignora en silencio — el configure lo dice
(«supports link arguments -Wl,--build-id=sha1: NO») y la .so sale sin ninguna nota.
zig 0.16 sí la emite (verificado a mano), pero `zig_version` está pineada a 0.13.0 como
escotilla contra la regresión de zig 0.14: subirlo cambia un muro por otro.

Tampoco había escape por entorno, y esta vez medido sobre el fuente en vez de deducido:
el retorno temprano es `if (INTEL_DEBUG(DEBUG_DISK_CACHE_DISABLE_MASK))` y en 24.0.9 esa
máscara está definida como 0 ⇒ el `if` no dispara nunca. Por eso el desensamblado no
tenía ni un salto condicional: el compilador lo dobló.

`-Dshader-cache=disabled` lo garantiza el preprocesador — el cuerpo entero de
`iris_disk_cache_init` vive en un `#ifdef ENABLE_SHADER_CACHE`. Verificado en el
artefacto: la función es ahora un único `ret`.

MEDIDO EN METAL (OptiPlex 3060 / UHD 630), bibliotecas desplegadas por SSH sobre la
máquina viva, sin re-quemar el USB: sesión COSMIC completa arriba — cosmic-comp, panel
con applets, bg, launcher, term, toplevel, workspaces — y CERO segfaults nuevos.
Confirmado en pantalla por el usuario.

Costo aceptado: sin caché en disco los shaders se recompilan en cada arranque.
Re-sella mesa: b3:6d443d9f… → b3:f43c41d1…

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 12:01:52 -04:00
sergio 6e07646c96 estado: cosecha granja 2026-08-06T15:37:22Z — avance del árbol KDE 2026-08-06 11:37:22 -04:00
sergioandClaude Opus 5 213ebfbb03 📶 metal: el enlace WiFi se caía solo y cortaba la depuración remota a mitad
Depurando la OptiPlex por SSH sobre un hotspot, la máquina desaparecía de la red cada
pocos minutos. No es «la WiFi anda mal»: son dos causas que se suman y ninguna deja
rastro en dmesg.

  - `udhcpc -q` (el que usa wifi-up) pide la dirección UNA vez y termina. Nadie renueva
    el lease.
  - el AP olvida a los clientes ociosos y deja de contestar ARP. Desde fuera se ve «No
    route to host» mientras la máquina se cree perfectamente conectada.

El síntoma engaña justo en la dirección peor: la máquina no reporta nada, así que parece
que se colgó lo que estabas depurando.

`wifi-keep` hace ping al gateway cada 10s, que resuelve las dos a la vez — detecta la
caída para reasociar Y mantiene viva la entrada del cliente en la tabla del AP.

Sin esto cada sesión remota se interrumpe sola y hay que volver físicamente al teclado,
que es exactamente lo que la red venía a evitar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 11:33:30 -04:00
sergioandClaude Opus 5 e2017dcfdc 🎯 mesa: sin build-id, iris desreferenciaba NULL y mataba al compositor en silencio
Causa raíz del escritorio COSMIC que no arrancaba en metal, medida en el OptiPlex 3060
(UHD 630). cosmic-comp moría sin panic y con RUST_BACKTRACE=full; el dmesg tenía la
respuesta que ningún log de usuario podía dar:

    cosmic-comp[1266]: segfault at 10 ip ... error 4 in iris_dri.so[9626c0,...]

El offset resuelve a `_mesa_sha1_format` (bytes del desensamblado idénticos al `Code:`
del kernel), y su llamador `iris_disk_cache_init` es tres llamadas seguidas sin un solo
salto condicional entre medio:

    build_id_find_nhdr_for_addr → NULL      (ninguna .so de mesa traía nota)
    build_id_data               → NULL+0x10 (offsetof del campo)
    _mesa_sha1_format           → lee 0x10  ⇒ SIGSEGV

El `assert(note && ...)` que cazaba esto lo borra -Db_ndebug=true.

Tres decisiones razonables por separado que juntas dan un cuelgue mudo: meson no pide
build-id, lld no lo pone solo (37 de 40 .so del corpus SÍ lo traen — era exclusivo de
mesa) y el assert no existe en release. No lo salva MESA_SHADER_CACHE_DISABLE: la
desreferencia va antes de consultar si la caché está habilitada. Y no se ve en QEMU,
donde el userland es swrast/llvmpipe y esta ruta es de iris.

Fix: -Dc_link_args/-Dcpp_link_args con -Wl,--build-id=sha1.
Re-sella mesa: b3:6d443d9f… → b3:c3c18cde…

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 11:27:35 -04:00
sergioandClaude Opus 5 47d8848e37 🔌 wifi-up: sin ctrl_interface, wpa_cli no tenía puerta a la que llamar
El /tmp/wpa.conf que generaba sólo traía el bloque `network`. Sin
`ctrl_interface=/run/wpa_supplicant`, wpa_supplicant no abre socket de control y
`wpa_cli` no conecta — síntoma que parece un fallo de red y es una puerta que no
existe. Peor: deja ciego el único dato que distingue «clave mal» de «DHCP mudo»,
que es wpa_state. Y la espera de asociación que acabo de añadir dependía de wpa_cli,
o sea que habría fallado siempre.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 18:27:48 -04:00
sergioandClaude Opus 5 89076a5106 📶 wifi-up: esperaba 6s fijos y culpaba a la clave de un problema de tiempo
En el OptiPlex la interfaz wlan SÍ aparece (ath10k cargó y el re-probe PCI funcionó),
pero `udhcpc` salía a preguntar 6s después de lanzar wpa_supplicant — antes de que
hubiera asociación. Sin lease, y el mensaje decía «¿clave correcta?», que manda a
buscar exactamente donde NO está el problema.

Ahora espera al HECHO: wpa_state=COMPLETED (techo 40s), informa el estado real
mientras espera, y distingue los casos en el mensaje de fallo — 4WAY_HANDSHAKE es
clave, SCANNING es que no ve la red. Y DHCP reintenta 3 veces, porque el AP a veces
ignora el primer DISCOVER justo tras asociar.

Tercera vez hoy que un `sleep` fijo produce un diagnóstico falso (initramfs 5s,
cosmic-diag 50s, wifi-up 6s). El patrón: esperar al reloj en vez de al hecho no falla
donde se prueba, falla en la máquina lenta — y miente sobre la causa.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 18:22:23 -04:00
sergio c8b0093aa8 estado: cosecha granja 2026-08-05T22:00:09Z — avance del árbol KDE 2026-08-05 18:00:09 -04:00
sergio 50242906de estado: cosecha granja 2026-08-05T21:18:06Z — avance del árbol KDE 2026-08-05 17:18:07 -04:00
sergioandClaude Opus 5 095b876bba 📶 metal: WiFi por ath10k (QCA9377) + cosmic-diag esperaba con reloj en vez de con hecho
WIFI. El OptiPlex 3060 SÍ tiene WiFi: `pci 0000:02:00.0: [168c:0042]` = Qualcomm
Atheros QCA9377. No aparecía interfaz porque el kernel sólo llevaba IWLWIFI. Añadido
ATH10K/ATH10K_PCI a linux-metal-dual + el firmware QCA9377 a la imagen + `wifi-up`.

El detalle que hace falta: con MODULES desactivado el driver va BUILTIN y prueba el
dispositivo a los ~5s pidiendo firmware, pero el rootfs se monta a los ~13s ⇒
`Direct firmware load failed -2` y la tarjeta queda muda, sin módulo que cargar
después. `wifi-up` fuerza un re-probe PCI (remove+rescan) con /lib/firmware ya
montado. Es la MISMA carrera que la del initramfs con el USB, un piso más abajo.

COSMIC-DIAG. La 1ª versión esperaba `sleep 50` y en esa máquina la sesión tarda ~105s
en llegar al compositor ⇒ el dmesg «después» se capturó ANTES de que cosmic-comp
arrancara y salió byte a byte idéntico al de antes. El viaje se gastó sin dato, y yo
leí ese dmesg vacío como «no hubo segfault», que era una conclusión sin respaldo.
Ahora espera al HECHO: que cosmic-comp aparezca y luego desaparezca (techo de 6 min).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 17:16:54 -04:00
sergioandClaude Opus 5 41e8b579d5 🩹 cosmic-diag: el patrón del grep daba un falso positivo con microcode:
`grep -i "Code: "` matchea `microcode: Current revision: 0x...`, o sea que reportaba
como hallazgo una línea de arranque normal. Anclado a lo que el kernel imprime de
verdad ante una señal: segfault, general protection fault, traps:, Killed process.

Dato real de la corrida en el OptiPlex: NO hubo segfault ⇒ cosmic-comp no muere por
señal, sale por su cuenta sin escribir nada. Cambia la búsqueda.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 17:07:49 -04:00
sergioandClaude Opus 5 e125b7a7c3 🔬 cosmic/metal: cosmic-diag — una corrida, toda la evidencia, incluido el dmesg
En metal cada iteración cuesta un viaje físico, así que la captura no puede depender
de que alguien recuerde los pasos. `cosmic-diag` corre la sesión y deja en
/var/log/cosmic: dmesg ANTES, run.txt de la sesión, y dmesg DESPUÉS.

El dmesg de después es lo que faltaba. Medido en el OptiPlex 3060: cosmic-comp toma
seat0 (seatd lo confirma: "Opened client 1"), vive 4,9s y se DESCONECTA, pero su log
se corta a los 0,4s sin panic y con RUST_BACKTRACE=1 activo. Eso no es un error
manejado, es una señal — y si es SIGSEGV el kernel imprime
`cosmic-comp[PID]: segfault at ... in <BIBLIOTECA>`, que ES el diagnóstico.

Descartado antes de pedir otro viaje: la clausura está completa (iris_dri.so, libEGL
y cosmic-comp resuelven todos sus NEEDED en el rootfs), y los errores de tema no son
la causa — cosmic-panel sobrevive al mismo GetKey(list_button) y muere después, de
NoCompositor. Los tres clientes que "no encuentran compositor" son consecuencia.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 16:42:02 -04:00
sergioandClaude Opus 5 b854230863 🩻 cosmic: escribir a /dev/ttyS0 COLGABA el arranque en metal (open() espera DCD)
Diagnosticado por el usuario en el OptiPlex 3060 quitando los pipes de say().

Abrir un puerto serie sin portadora BLOQUEA en open() esperando DCD, salvo que el
termios tenga CLOCAL. El kernel pone CLOCAL cuando registra el puerto COMO CONSOLA,
y eso pasa en QEMU porque el cmdline lleva `console=ttyS0,115200`. En ese metal la
firmware NO aplica el CONFIG_CMDLINE horneado —el kernel arranca con `Command line:`
VACÍO— así que ttyS0 es un puerto común, sin cable: el PRIMER say() se colgaba ahí.

Explica los tres síntomas que no cerraban:
- /var/log/cosmic se creaba (el mkdir va antes) pero el .log nunca aparecía;
- `cosmic-start > /salida.txt` daba VACÍO ⇒ parecía que el script no se ejecutaba,
  cuando estaba bloqueado en su primera línea de salida;
- la corrida terminaba siempre en «log del compositor (primer tramo)»: no era el
  último paso, era el dump() bloqueándose en el mismo sitio.

El serial no se pierde: cuando ES la consola, /dev/console YA ES ttyS0. Cuando no lo
es, no había nadie del otro lado. En metal lo que sirve es el fichero de log.

Con say() destrabado, la detección de GL quedó CONFIRMADA en metal por primera vez:
  == cosmic :: GL por HARDWARE — kernel drm=i915 + iris_dri.so (sin overrides)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:59:40 -04:00
sergioandClaude Opus 5 77f8b3e3ec 🪵 cosmic: el log de MI validación viajaba horneado en el USB, y la salida salía doble
Dos defectos que juntos hicieron ilegible el 3er viaje.

1. LOG CONTAMINADO. Los logs de sesión van a disco para sobrevivir al apagón en
   metal — bien— pero validar la imagen en QEMU ESCRIBE DENTRO DEL FICHERO DE
   IMAGEN, y después se quema. El usuario grepeó /var/log/cosmic en su máquina y
   leyó `drm=virtio-pci`: mi corrida en QEMU, no la suya. Un log viejo que parece
   nuevo es peor que no tener log.
   Fix: la imagen crea /var/log/cosmic VACÍO como último paso. Y la regla —validar
   sobre una COPIA, o regenerar antes de quemar— queda escrita donde se comete.

2. SALIDA DOBLE. `say` tee'aba a /dev/console siempre. Lanzado a mano desde el
   getty, stdout YA es la pantalla, y /dev/console es tty0 = la MISMA pantalla
   cuando el cmdline llega vacío (la firmware del OptiPlex no aplica el
   CONFIG_CMDLINE horneado). Cada línea salía dos veces, entreverada con lo que
   arje-zero escribe a consola. Eso es lo que se leía como «basura de init»: no era
   ruido ajeno, era el propio script. Ahora /dev/console sólo si `[ -t 1 ]` es falso.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:33:50 -04:00
sergioandClaude Opus 5 1284349ee5 📖 cosmic/metal: el 2º viaje — la imagen no podía ganar, y por qué
Documenta el diagnóstico del OptiPlex 3060: la GPU estaba perfecta (i915 inicializó,
fbcon en el framebuffer) y lo que fallaba era el hardcodeo de software GL, más la
preparación de sesión que la imagen de metal nunca tuvo.

Deja las dos reglas: un artefacto compartido entre QEMU y metal no puede llevar el
entorno hardcodeado (se detecta), y «llega al prompt» no valida NADA del escritorio
— hay que correr cosmic-start en la imagen que se va a quemar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:04:33 -04:00
sergioandClaude Opus 5 86645c379c 🖥 cosmic/metal: la imagen forzaba render por SOFTWARE — el viaje no podía ganar
Diagnóstico del 1er viaje al OptiPlex 3060 (Coffee Lake, UHD 630). El dmesg del
propio USB muestra que la GPU estaba PERFECTA:

  [drm] Found coffeelake (device ID 3e92) ... Initialized i915 1.6.0 on minor 0
  fbcon: i915drmfb (fb0) is primary device

Lo que fallaba era el script de arranque. `cosmic-start-qemu.sh` exportaba
LIBGL_ALWAYS_SOFTWARE=1 y MESA_LOADER_DRIVER_OVERRIDE=kms_swrast INCONDICIONAL —
correcto sobre virtio-gpu, letal sobre Intel real: la mesa del corpus va con
-Dgallium-drivers=iris -Dllvm=disabled y NO EXISTE ningún kms_swrast_dri.so ⇒ EGL
falla. Y en el mejor caso habría sido peor: si arrancaba, componía por software y
ScreenCast fallaba igual que en QEMU ⇒ el viaje NO PODÍA contestar su pregunta.

El origen es un comentario que yo escribí en metal-desktop-image.sh afirmando que
el script no tenía supuestos del emulador. Los tenía, y el nombre del fichero lo
decía. Ahora:

- `cosmic-start-qemu.sh` → `cosmic-start.sh` (se llama por lo que hace).
- El modo de GL se DETECTA con dos patas: driver DRM del kernel Y .so de mesa
  presente. Si no hay ninguna, lo DICE en vez de morir dentro de EGL.
- La imagen VERIFICA que el script instalado no fuerce kms_swrast (falla el build
  si vuelve a pasar).

Y correr cosmic-start sobre la imagen de metal POR PRIMERA VEZ (antes sólo se
validaba que llegara al prompt) destapó que le faltaba entera la preparación de
sesión que sólo tenía qemu-desktop-image.sh: sin grupo `video` no arranca seatd,
sin usuario `messagebus` no arranca el bus de SISTEMA — y sin bus de sistema NO HAY
PORTAL, o sea que portal-probe screencast no habría tenido con quién hablar aunque
el GL fuese perfecto. Copiada: arje-logind-compat + política, video, messagebus,
setuid del launch-helper, /var/run→/run, COSMIC_MODE, libc.musl-x86_64.so.1.

Además, para que el próximo fallo en metal no cueste otro viaje a ciegas:
- Los logs van a /var/log/cosmic (ext4) y no a /tmp (RAM, se evapora al apagar).
  `dump()` también, que iba SÓLO al serial — donde se perdió la explicación de los
  tres fallos de arriba.
- authorized_keys horneada: sshd escuchaba en :22 pero era INALCANZABLE
  (PasswordAuthentication no, sin clave) ⇒ ahora se depura por red.
- netup-wait: el r8169 levanta el enlace a los 20,2s y el ente sshd pedía DHCP a
  los 13,7s, sin reintento. En QEMU no se ve: virtio-net tiene carrier desde el
  instante cero.
- firmware i915 de otras generaciones (la base sólo traía tgl_*; el Coffee Lake
  pedía kbl_dmc_ver1_04.bin).

Validado arrancando como usb-storage: seatd OK, bus de sistema OK, login1 OK,
pipewire+wireplumber OK, compositor OK. La línea de GL dice la verdad en QEMU.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 15:03:47 -04:00
sergio 08440220b6 estado: cosecha granja 2026-08-05T19:00:17Z — avance del árbol KDE 2026-08-05 15:00:17 -04:00
sergioandClaude Opus 5 b5802bc651 📖 cosmic/metal: el 1er viaje físico, la carrera USB y la lección general
Documenta el diagnóstico completo: la AUSENCIA del `EXT4-fs mounted` como prueba de
que el root nunca se montó, la carrera contra la enumeración USB (invisible en QEMU
porque virtio está desde el instante cero), y la ceguera del /dev/console = serial.

La lección que generaliza: CADA capa del arranque tiene que escribir a /dev/tty0. Se
arregló la capa post-switch_root en el viaje de KDE y el initramfs quedó ciego; tapar
una sola capa garantiza que la siguiente vuelva a caer.

Incluye el comando para reproducir metal desde el laptop (qemu-xhci + usb-storage +
-serial null) y la evidencia en pantalla.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:55:18 -04:00
sergioandClaude Opus 5 705a70c33a 🩺 EFI: el initramfs perdía la carrera contra la enumeración USB — y en silencio
Síntoma en metal ajeno: la pantalla se congela justo tras `sdc: sdc1 sdc2 sdc3 sdc4`
/ `Attached SCSI removable disk`. No estaba colgado.

Prueba de que el root nunca se montó: con `ignore_loglevel` un montaje ext4 imprime
`EXT4-fs (...): mounted filesystem` en pantalla sí o sí, y no aparecía.

Dos causas encadenadas:

1. CARRERA. El rdinit arranca en cuanto se desempaqueta el initramfs, pero el bus USB
   enumera asíncrono y tarda segundos (reset de hub, settling de 1s por dispositivo,
   scan SCSI). Los 5 reintentos de 1s no alcanzaban. En QEMU el disco es virtio y está
   desde el instante cero ⇒ la carrera NUNCA se veía en validación. Es el `rootwait`
   que no podemos usar porque no hay root= en el cmdline. Ahora espera 60s.

2. CEGUERA. El cmdline horneado es `console=tty0 console=ttyS0,115200` y /dev/console
   = la ÚLTIMA `console=` ⇒ el serial. El `exec sh` de rescate nacía invisible. Es el
   MISMO bug que costó el 1er viaje físico de KDE, una capa más temprano: allá se
   arregló para después del switch_root (getty en tty1) y el initramfs quedó ciego.
   Ahora el log del pivote va a /dev/tty0, el marcador INIT-OK también, y el rescate
   abre shell en tty1 listando los bloques visibles.

+ `timeout 15` a `hammer boot menu`: corre ANTES del exec de arje-zero ⇒ colgarse ahí
  deja un arranque sin PID1 ni pantalla, indistinguible de un kernel muerto. El
  `|| true` protegía del fallo, no del bloqueo.

Validado arrancando la imagen COMO USB (qemu-xhci + usb-storage, -serial null, con
pantalla): marcadores del pivote visibles, motd y prompt `/ #`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 13:54:23 -04:00
sergioandClaude Opus 5 166355d1fb 📖 cosmic/metal: el runbook del viaje físico, escrito ANTES de bootear
Qué correr con el USB en la mano, en orden, y —lo que importa— CUÁL ES EL VEREDICTO:
el `[4/4] OpenPipeWireRemote` con su fd, no que el diálogo aparezca (eso ya pasaba en
QEMU). Y el contrafáctico que hace falsable el diagnóstico de hoy: si vuelve a
colgarse en `Start` pero YA NO aparece `Erroneous EGL call` en el log del compositor,
entonces la causa era otra y lo de hoy estaba incompleto.

`ls /dev/dri` con renderD128 es la señal barata de que hay HW: con swrast no existe.

Gotchas del quemado, todos medidos y todos con su porqué:
 · NUNCA a un NVMe y NUNCA POR NOMBRE — hoy mismo, tras el reboot, nvme0n1 y nvme1n1
   se intercambiaron respecto de lo anotado en julio. Identificar por TRAN=usb.
 · el progreso NO se mide con kill -0 (sudo dd corre como root ⇒ EPERM desde el
   usuario y parece que terminó): se mide por /sys/block/sda/stat campo 7.
 · no correr os-prober sobre el device que estás dd-eando.
 · `pkill -f <patrón>` mata su propio shell si el patrón está en su cmdline. Volvió a
   pasar hoy (exit 144), con el gotcha ya escrito. Usar pgrep -x.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 12:05:49 -04:00
sergioandClaude Opus 5 11e6a3c78c 🔩 cosmic/metal: imagen EFI con iris HW — el viaje que cierra ScreenCast
Hermano de kde/metal-desktop-image-dual.sh, pero con la decisión INVERTIDA y ése es
el motivo de existir. La de KDE va por mesa-llvmpipe a propósito (tenía que aguantar
también una NVIDIA Pascal, cuya mesa HW sólo existe como binario Alpine ⇒ rompería
soberanía).

Acá el objetivo es cerrar ScreenCast, y el diagnóstico de hoy midió que lo que falla
es el COMPOSITOR en una llamada EGL —8 ms antes del `Paused`— porque en QEMU mesa es
kms_swrast: software, SIN exportación dmabuf. El `mesa` del corpus se construye con
-Dgallium-drivers=iris y YA está en el rootfs de COSMIC; en QEMU nunca se usaba, en
un TigerLake es el driver correcto y trae dmabuf de verdad. O sea que este viaje no
es "lo mismo pero en metal": es la única forma de saber si ScreenCast está bien.

⚠ Por eso NO se inyecta llvmpipe: mezclarlos no es aditivo — llvmpipe trae su propio
libgbm/libEGL/libgallium y sobrescribirlos DESACTIVA iris. Si la máquina no tuviera
Intel, la imagen correcta sería la de KDE, no ésta con un parche.

Y lo que NO hace falta inyectar, medido: la de KDE mete libLLVM.so.18 + libgcc_s +
libstdc++ porque el JIT de llvmpipe los NEEDea. Acá NO — iris_dri.so pide sólo
libglapi/libdrm/libz/libzstd/libc (mesa va con -Dllvm=disabled) y cosmic-comp ocho
NEEDED sin C++. CERO binarios ajenos de Alpine. Verificado con clausura ELF completa
del rootfs: 472 objetos escaneados, 42 raíces de runtime, 0 con NEEDED sin proveedor
(los 3 huecos son llvm-*/perl, herramientas de build que el escritorio no arranca).

Se instala EL MISMO cosmic-start validado en QEMU, no una variante: nada de lo que
hace es específico del emulador, y que corra el mismo fichero es lo que hace que
"validado en QEMU" signifique algo para el metal.

VALIDADO EN OVMF CON PANTALLA, que es la regla de oro que costó un USB en el 1er
viaje de KDE (-serial null para simular metal sin puerto serie): arje-zero PID 1, las
4 particiones montadas, el motd VISIBLE y el prompt `/ #` en la pantalla — o sea que
el getty de tty1 hace su trabajo. El shell responde: iris_dri.so presente,
/dev/dri/card0, y cosmic-start/portal-probe/wireplumber en PATH.

Falta quemar el USB, que es destructivo y necesita que el usuario confirme el device.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 11:41:16 -04:00
sergioandClaude Opus 5 b38f54e25c 🧊 ScreenCast: la causa es EGL POR SOFTWARE en el compositor, no el portal
Con RUST_LOG=trace el log del backend tiene CINCO LÍNEAS en total y la última es la
de siempre ('Connecting' -> 'Paused'). Después no escribe nada más: ni error, ni
panic, ni progreso. O sea que el trace no aportó — screencast_thread no tiene más
instrumentación y buscar ahí estaba agotado.

La pieza que faltaba mirar era EL COMPOSITOR, que es quien entrega los frames:

  15:19:02.301568Z WARN smithay::backend::egl::error: Erroneous EGL call didn't set EGLError
  15:19:02.310127Z INFO …screencast_thread: state-changed 'Connecting' -> 'Paused'
                   ↑ OCHO MILISEGUNDOS

Y la correlación no es casual: el contador de "Erroneous EGL" sube DE A DOS por cada
intento de screencast (uno al abrir el diálogo con su miniatura en vivo, otro al
pulsar Share) y no se mueve en ningún otro momento.

`ls /usr/lib/dri/` → iris_dri.so, kms_swrast_dri.so, swrast_dri.so. En QEMU con
virtio-gpu-pci sin virgl carga kms_swrast: mesa por software, SIN exportación dmabuf
real. Que es justo lo que un stream continuo necesita y una captura de una sola toma
no — por eso cosmic-screenshot SÍ funciona (va por shm) y ScreenCast no.

EL FRENTE NO ESTÁ BLOQUEADO POR UNA RECETA SINO POR EL ENTORNO. Esto reencuadra el
día entero: pipewire no era, wireplumber no era, el backend no era. Las tres piezas
están construidas, corriendo y haciendo su trabajo —el nodo `cosmic-screencast` existe
en el grafo— y el que no puede cumplir es el compositor sobre una GPU de software. Es
el MISMO muro que documenta el frente KDE (GBM/EGL de software), llegando por otro
camino.

Y no se puede verificar acá: `qemu-system-x86_64 -display help` da sólo none/gtk/sdl
y no existe virtio-gpu-gl-pci — este binario no se compiló con virgl, aunque
libvirglrenderer.so.1 esté en el host. Las salidas son metal (donde iris_dri.so YA
está en la imagen) o un QEMU con virgl.

Lo honesto: ScreenCast está verificado HASTA DONDE EL ENTORNO PERMITE — la cadena
D-Bus completa funciona, el stream se crea y se negocia, y el último tramo (los
píxeles) depende de una GPU que esta VM no tiene.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 11:26:15 -04:00
sergio dcaed2b066 estado: cosecha granja 2026-08-05T15:00:09Z — avance del árbol KDE 2026-08-05 11:00:09 -04:00
sergioandClaude Opus 5 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>
2026-08-05 10:54:54 -04:00
sergio 97bcf0673e estado: cosecha granja 2026-08-05T14:42:37Z — avance del árbol KDE 2026-08-05 10:42:37 -04:00
sergio 4984d6942d estado: cosecha granja 2026-08-05T14:30:05Z — avance del árbol KDE 2026-08-05 10:30:05 -04:00
sergioandClaude Opus 5 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>
2026-08-05 10:28:14 -04:00
sergioandClaude Opus 5 ea210ba3cd 🔌 portal-probe: activar el portal en vez de exigirlo presente
Bug que la propia sonda destapó al usarla: los portales son servicios ACTIVABLES por
D-Bus, no demonios siempre presentes — el bus los arranca cuando alguien los pide,
leyendo su `.service`. `NameHasOwner` NO dispara esa activación, sólo pregunta.

Costó una corrida entera de diagnóstico: la sonda dijo "nadie posee
org.freedesktop.portal.Desktop — no hay frontend de portal en el bus" sobre un
sistema donde el portal andaba perfecto, sólo que dormido.

Ahora pide `StartServiceByName` y deja que el bus decida. Si de verdad falta el
`.service`, falla con ServiceUnknown y ahí el diagnóstico sí es real; y distingue
"arrancado ahora" de "ya estaba corriendo", que no es lo mismo cuando lo que se
investiga es una carrera de arranque.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 10:25:55 -04:00
sergio b77cc68a91 estado: cosecha granja 2026-08-05T14:11:28Z — avance del árbol KDE 2026-08-05 10:11:29 -04:00
sergioandClaude Opus 5 6ec556decc 🔌 portal-probe: la sonda del portal XDG, en C sobre libdbus
Los portales NO son RPC: son un protocolo asíncrono en dos tiempos. Cada método
devuelve al instante un object path de `Request`, y el resultado llega DESPUÉS como
señal `Response` sobre ese path. `dbus-send` ya salió para entonces — por eso la URI
del FileChooser nunca se pudo capturar desde el shell.

Y hay una razón más dura: el portal ata la sesión al SENDER. CreateSession con un
dbus-send y SelectSources con otro llegan de nombres únicos distintos y el segundo se
rechaza. Hace falta UNA conexión viva durante todo el intercambio.

El path del Request se PREDICE (/…/request/<sender>/<token>, sender = nombre único
sin ':' y con '.'→'_'). Eso existe a propósito: permite suscribirse ANTES de llamar.
Esperar al path devuelto para recién ahí hacer AddMatch es una carrera real — el
portal puede haber emitido ya la señal.

En C y no con ashpd: el árbol fuente de esta receta es el repo de la propia
herramienta de build, así que ashpd significaría sumar ~150 crates al Cargo.lock de
hammer por una sonda de diagnóstico. libdbus ya está en la imagen y en el corpus
(y con `libdbus-1.a`, así que la sonda sale ESTÁTICA, sin un solo NEEDED). Sobre
todo: acá el protocolo se VE. Una sonda cuyo valor es documentar un handshake no
debería esconderlo tras una biblioteca que lo abstrae.

Cuatro modos: `version` (qué interfaces sirve y en qué versión), `screencast` (el
handshake de 4 pasos hasta el fd de pipewire), `filechooser` (captura las uris) y
`screenshot`. El impresor de valores es recursivo y NO filtra: si el portal devuelve
un campo que este programa no conoce, sale igual por pantalla — que es exactamente lo
que uno quiere de una sonda.

Validado en el host contra el portal real: compila sin warnings y lee las versiones
(Screenshot v2, FileChooser v4, Settings v2).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 10:06:02 -04:00
sergioandClaude Opus 5 28dc473aca 🧹 store-gc: el recolector que hammer gc no tiene — 702 artefactos, 34G
El disco llegó a 100% y el gordo no estaba en `work/`: el store guarda un artefacto
por cada SELLADO, no uno por receta. Cuando una dep cambia, el ArtifactHash de todo
lo que cuelga cambia y el sellado nuevo SE SUMA al viejo. Medido: 1996 artefactos
para 1130 recetas — 12 copias de libqalculate, 9 de qt6-qtdeclarative, 8 de gtk4.
51G de 74G eran versiones anteriores.

El conjunto VIVO es `hammer hash` sobre todas las recetas de todas las colas: la
respuesta a "¿cuál es el hash vigente HOY?", que el store solo no puede dar. Lo que
no está ahí es rancio — pero rancio son DOS cosas muy distintas, y confundirlas es
la diferencia entre podar y perder:

  SUPERADO — su nombre TIENE un artefacto vigente presente. Versión anterior de algo
             ya sellado al día. Se reconstruye desde la receta, que está en git.
  HUÉRFANO — ningún artefacto con ese nombre es el vigente. El hash de hoy NO está
             sellado y éste es el ÚNICO ejemplar. Borrarlo sí pierde.

Por eso el default borra sólo los superados; `--huerfanos` hay que quererlo aparte.
Aplicado: 702 superados = 34G, de 5,4G libres a 43G. La verificación de que el
criterio era correcto no fue el `df`: fue que `hydrate-cosmic.sh` siguió dando 83/83
recetas y 0 faltantes.

Los 255 huérfanos (17G) quedan INTACTOS y son un hallazgo aparte: casi todos KDE
(kio×8, kparts×8, kcmutils×8), o sea que las recetas KDE se movieron después del
último sellado y el escritorio hidratado vive de artefactos que ya no son vigentes.

Dos avisos que el script lleva escritos porque cuestan al descubrirlos solos: el
espacio NO siempre se libera (los rootfs de work/ son hardlinks al store — borrar el
dir no rompe nada pero tampoco libera hasta que el rootfs se vaya), y `--store` por
defecto apunta a /store, no a ./store.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 09:46:41 -04:00
sergioandClaude Opus 5 e43ce95786 🎥 cosmic: pipewire ARRANCA, y el portal ya figura como cliente suyo
El bloqueo decisivo de ScreenCast no era una receta que faltara: `/usr/bin/pipewire`
estaba en la imagen desde que entró como dep de `cosmic-settings-daemon`, pero NADIE
lo lanzaba. En una distro con systemd de usuario eso lo hace `pipewire.socket`; acá
no hay tal cosa, así que es tarea de `cosmic-start` — el mismo patrón que el bus de
sistema y que login1.

Va justo después de asegurar XDG_RUNTIME_DIR y NO junto al bus de sesión, a
propósito: pipewire no necesita D-Bus para arrancar, sólo el runtime dir donde abre
su socket. Ahí sirve para los dos modos (bare y session) sin duplicar el bloque. Y se
espera EL SOCKET, no el pid: un demonio que muere al instante deja el pid existiendo
un rato, y esperar por él da un OK falso.

Medido dentro de la VM, cada paso una pregunta distinta:

  pipewire OK (socket pipewire-0, pid 189)      ¿abre socket?
  pw-cli info 0  → Core/4, 1.2.7, core.daemon   ¿SIRVE a un cliente?
  pw-cli ls Factory → "client-node"             ¿tiene lo que ScreenCast usa?
  pw-cli ls Client  → "xdg-desktop-portal"      ¡el portal YA está conectado!

La última línea es el veredicto: el frontend del portal aparece como cliente del
demonio, una conexión que antes no podía existir y que es justo la que `Start`
necesita para negociar el nodo. `client-node` es la factory con que el backend lo crea.

Sin regresión, con la cadena entera: `cosmic-screenshot` levanta la UI INTERACTIVA
del portal (región/ventana/pantalla + Capture + Save to Pictures) y el click deja un
PNG de 53.590 bytes. Esa UI no estaba documentada — el registro anterior sólo probaba
la ruta no-interactiva. Escritorio completo: 2414 colores (el panel mutilado da 130).

Lo que sigue faltando son DOS cosas distintas, y ninguna es un one-liner:
  · el handshake de punta a punta pide un cliente `ashpd` PERSISTENTE — el portal
    asocia la sesión al *sender*, así que CreateSession con un dbus-send y
    SelectSources con otro se rechazan por venir de nombres únicos distintos;
  · wireplumber, que separa "hay stream de video" de "hay audio". Ni construido está.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 09:46:21 -04:00
sergioandClaude Opus 5 2e26a45060 🎥 cosmic: ScreenCast — dos bloqueos, y el que importa es que NADIE ARRANCA PIPEWIRE
BLOQUEO 1: dbus-send no puede manejarlo, al revés que FileChooser. El
selector se abre con UNA llamada; ScreenCast necesita un handshake de tres
pasos con estado (CreateSession → SelectSources → Start) y la sesión queda
atada a la CONEXIÓN que la creó — cada dbus-send es una conexión distinta
que muere al terminar, así que el paso 2 nunca encuentra la sesión del paso
1. Es límite de la herramienta, no del portal.

Lo que sí se midió: CreateSession FUNCIONA. El frontend devuelve
/org/freedesktop/portal/desktop/request/1_106/sc1 con nuestro handle_token.

BLOQUEO 2, y éste no lo arregla ningún cliente: NO HAY DEMONIO DE PIPEWIRE.

  ls /usr/bin/ | grep -i pipewire  →  pipewire, pipewire-aes67,
                                      pipewire-avb, pipewire-pulse
  ps ax | grep -c pipewire         →  1   (el propio grep)

Los binarios están —el backend del portal enlaza libpipewire-0.3.so— pero el
demonio no corre: cosmic-start no lo lanza y no hay systemd de usuario. O
sea que aunque se escribiera el cliente ashpd, Start no tendría con quién
negociar el stream. Arreglo: lanzarlo desde cosmic-start. Para AUDIO además
falta wireplumber, que ni está construido.

Y se corrigió un comentario DESACTUALIZADO de cosmic-start que decía que
cosmic-settings-daemon «todavía no se puede construir acá (arrastra
pipewire)»: es falso hace tiempo, ese daemon está sellado y corre. Un
comentario viejo manda a buscar un bloqueo que ya no existe, y eso cuesta
igual que un bug.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 07:51:36 -04:00
sergioandClaude Opus 5 5f606111d1 📂 cosmic: FileChooser ejercido — pero NO desde cosmic-store, y el motivo importa
NO SE PUEDE DESDE LA TIENDA. cosmic-store se compila con la feature
xdg-portal, pero su ÚNICO uso del selector (src/update.rs:505,
Message::RepositoryAddDialog) arranca con
`if backend_name == BackendName::FlatpakUser`: el diálogo sólo se abre para
añadir un repositorio flatpak, y esta imagen no construye el backend
flatpak. La feature está compilada y el punto de llamada es INALCANZABLE.
Tener la feature encendida no es tener el flujo disponible.

El camino que sí ejerce la interfaz es llamarla directo por D-Bus, que
además prueba los tres eslabones sin depender de qué app la use:

  dbus-send --session --print-reply --dest=org.freedesktop.portal.Desktop \
    /org/freedesktop/portal/desktop \
    org.freedesktop.portal.FileChooser.OpenFile \
    string:"" string:Prueba dict:string:variant:handle_token,string:hammer1 &

Medido: el frontend devuelve el objeto Request con NUESTRO token
(/org/freedesktop/portal/desktop/request/1_113/hammer3) y el backend dibuja
el diálogo REAL — título «Prueba», el FS de verdad (run/tmp/dev/proc/sys),
panel de detalles (inode/directory, 963 items), Cancel/Open. La navegación
anda: doble clic en dev cambia la miga a «Filesystem › dev» y lista sus 129
entradas; seleccionar y pulsar Open lo cierra.

⚠ LO QUE NO QUEDÓ CAPTURADO: la URI devuelta. El Response de la spec de
portales es una señal UNICAST al llamador, y dbus-send termina apenas recibe
el method return, así que ya no está para recibirla; dbus-monitor con un
match rule normal tampoco la ve. Pide un cliente que implemente el ciclo
Request/Response, que es lo que hace ashpd dentro de las aplicaciones.
El diálogo está probado; la entrega del fichero a la aplicación, no.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 07:32:46 -04:00
sergioandClaude Opus 5 99e670ee4f 🚪 cosmic: las cinco interfaces del portal, sondeadas — y Access no es un hueco
Del lado CLIENTE (org.freedesktop.portal.Desktop): Screenshot 2,
FileChooser 3, ScreenCast 5, Settings 2.

`org.freedesktop.portal.Access` NO EXISTE, y está bien: Access vive sólo
como org.freedesktop.impl.portal.Access, del lado del BACKEND — es como el
frontend le pide al escritorio que muestre el diálogo de permiso. Mi primer
sondeo la pidió al frontend y el «No such interface» era un error DEL
SONDEO, no del portal.

Del lado BACKEND (impl.portal.desktop.cosmic): Screenshot 2, ScreenCast 4,
Settings 2; Access y FileChooser devuelven UnknownProperty 'version'. Ese
error PRUEBA que la interfaz está: si faltara, D-Bus contestaría «No such
interface». UnknownProperty = la encontró y esa implementación no publica
esa propiedad.

Y el ListNames muestra impl.portal.desktop.cosmic ACTIVADO junto a
impl.portal.PermissionStore: el frontend levantó al backend por D-Bus
activation, que es el eslabón que habilita el .service con @libexecdir@
sustituido.

ALCANCE HONESTO: esto prueba DISPONIBILIDAD y ENRUTADO, no los flujos
completos. La única ejercida de punta a punta —con fichero en disco— sigue
siendo Screenshot.

Próximo: ejercer FileChooser desde una app que use el portal (cosmic-store
se compiló con la feature xdg-portal; cosmic-edit NO la usa, va con el
selector propio de cosmic-files como crate) y ScreenCast, que es la que
ejercita pipewire de verdad.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 06:50:30 -04:00
sergioandClaude Opus 5 ada054cf07 🚪 cosmic: LA CADENA DEL PORTAL CERRADA — cosmic-screenshot captura de verdad
cosmic-screenshot corrido desde cosmic-term dentro de la VM devuelve una
ruta en vez del panic de ayer, y el fichero existe:

  /root/Pictures/Screenshots/Screenshot_2026-08-05_10-18-14.png
  -rw-r--r-- 1 root root 42256 Aug  5 10:18
  0000000 211   P   N   G  \r  \n 032  \n     ← firma PNG

El veredicto es el FICHERO, no el código de salida: que no panickee sólo
prueba que encontró el servicio; que haya 42 KB con \x89PNG prueba que el
compositor entregó píxeles por la cadena entera
(cliente → portal.Desktop → impl.portal.desktop.cosmic → cosmic-comp).

Cinco recetas: fuse3, json-glib (sin introspección), xdg-desktop-portal
1.18.4, clang18 y xdg-desktop-portal-cosmic. Dos raíces del perfil, porque
son dos PROCESOS que se encuentran por D-Bus y ningún [deps] los relaciona.

El runbook queda con las decisiones y sus precios (pin 1.18.4 por gstreamer,
el choque de gvdb y los dos caminos descartados por medición, de dónde sale
libclang) y con cuatro gotchas de andamiaje que costaron tiempo real:

  · --offline SIN --locked cuando la receta parchea el manifiesto
  · el worker NO puede construir la cola COSMIC (golden de 2026-07-15,
    cola posterior ⇒ nada es cache-hit allá): «rootfs laptop ≠ worker»
    pero con el STORE
  · farm-down cosecha /opt/hammer/store y farm-up ancla el store en
    /mnt/cosecha/store — directorios DISTINTOS, no symlink
  · pipewire sin --wrap-mode=nodownload: ante una dep faltante intenta
    bajarse un subproyecto y el error miente («This is a Meson bug»)

Próximo: las otras cuatro interfaces (FileChooser, ScreenCast, Settings,
Access). El andamiaje QMP ya está.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 06:20:39 -04:00
sergio d55a83e428 estado: cosecha granja 2026-08-05T10:14:35Z — avance del árbol KDE 2026-08-05 06:14:35 -04:00
sergioandClaude Opus 5 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>
2026-08-05 06:04:13 -04:00
sergio 3f3ae98a63 estado: cosecha granja 2026-08-05T09:14:09Z — avance del árbol KDE 2026-08-05 05:14:09 -04:00
sergio d08d61f698 estado: cosecha granja 2026-08-05T08:43:52Z — avance del árbol KDE 2026-08-05 04:43:52 -04:00
sergio 987d14b7af estado: cosecha granja 2026-08-05T08:12:53Z — avance del árbol KDE 2026-08-05 04:12:54 -04:00
sergioandClaude Opus 5 e914dd63e9 🚪 cosmic: el BACKEND del portal + clang18 — recetas listas para la granja
clang18                    b3:62c6bfb5   (sólo libclang.so)
  xdg-desktop-portal-cosmic  b3:97dfddd8

EL PARCHE DE UNA LÍNEA resultó ser el que se esperaba: xdp-cosmic declara
cosmic-files con features = ["gvfs", "wayland"] FIJADAS a mano —mismo patrón
que deja fuera a cosmic-files-applet— así que apagarla desde afuera no
alcanza. El sed la saca y queda la misma combinación que ya compilaron
cosmic-term y cosmic-edit. Se pierden los montajes remotos DENTRO del
selector del portal, no el selector.

PERO APARECIÓ UN ESLABÓN QUE NO ESTABA EN EL PLAN: libclang.

  xdp-cosmic declara pipewire y libspa-sys como deps DURAS (screencast)
    → libspa-sys/build.rs corre bindgen::builder() INCONDICIONAL
      → bindgen carga libclang.so en tiempo de build

Verificado que no hay atajo: ese build.rs no tiene rama alternativa, a
diferencia de aws-lc-sys —que sí cae a bindings pregenerados
(cargo:rustc-cfg=universal) y por eso cruzó a musl sin cmake ni perl—. Y
libclang no está ni en el corpus ni en los 182 binarios del rootfs del
constructor: llvm18 se selló con -DLLVM_ENABLE_PROJECTS="".

Lo barato es que NO se recompila LLVM: el artefacto llvm18 ya publica
usr/include/llvm, usr/include/llvm-c y usr/lib/cmake/llvm, así que clang18
es un build de clang CONTRA un LLVM sellado. Se instala sólo libclang.so y
clang-c/: el binario `clang` no es el objetivo — el compilador de esta
distro sigue siendo zig-cc, y sumar otro sería lo contrario de la deuda que
la campaña «matar gcc» viene pagando.

Patrón gcc-de-gueto (igual que llvm18 y cmake): g++ con -static-libstdc++
-static-libgcc para que la .so quede con NEEDED sólo libc.musl y no contagie
el runtime C++ de Alpine a cada build que la declare.

Vale decir de dónde sale este costo: no de COSMIC, sino de que pipewire-rs
genera sus bindings en cada build.

Ambas van a la granja (decisión del usuario): mismo peso que llvm18, que ya
se selló en el worker gordo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-05 03:57:07 -04:00
sergio 71aeb36622 estado: cosecha granja 2026-08-05T04:05:55Z — avance del árbol KDE 2026-08-05 00:05:55 -04:00
sergioandClaude Opus 5 d664267eff 🚪 cosmic: EL FRONTEND DEL PORTAL sella — org.freedesktop.portal.Desktop existe
Tres recetas nuevas y el eslabón que faltaba de verdad:

  fuse3 3.18.2          b3:3a84af86
  json-glib 1.10.8      b3:dc8c8e78   (variante SIN introspección)
  xdg-desktop-portal    b3:f4adb8c9   1.18.4

El artefacto trae /usr/share/dbus-1/services/org.freedesktop.portal.Desktop.service
—el nombre exacto que cosmic-screenshot no encontraba— más
xdg-document-portal y xdg-permission-store. NEEDED del frontend: libz,
libpipewire y libc.

CUATRO COSAS MEDIDAS QUE VALEN MÁS QUE LAS RECETAS:

1. PIN A 1.18.4 POR GSTREAMER. Desde 1.20 meson pide
   dependency('gstreamer-pbutils-1.0') sin required: —para validar sonidos
   de notificación— y eso arrastra gstreamer + gst-plugins-base. La 1.18.4
   no lo pide: sus deps duras son glib/gio/gio-unix, json-glib, fuse3,
   gdk-pixbuf y pipewire, y de ésas la única que faltaba era fuse3. Es un
   pin deliberado: el día que se quiera 1.22 el precio es gstreamer.

2. fuse3: NO poner -Ddisable-libc-symbol-version=true aunque suene a lo
   correcto para musl. Lo probé razonando eso y falla el enlace: la opción
   sólo hace que compat.c no emita los alias versionados, pero
   lib/meson.build:54 pasa --version-script INCONDICIONALMENTE, o sea que
   el script queda pidiendo símbolos que ya nadie define. Es una combinación
   incoherente de upstream, no una limitación de musl. La auto-detección de
   upstream tampoco conoce musl: sólo apaga versionado para __UCLIBC__ y
   __APPLE__. Se deja el defecto, que es con lo que Alpine construye.

3. json-glib NO se puede reusar de incoming-gnome: aquella está en la ISLA
   DINÁMICA (introspection=enabled, default_library=both) porque su .gir es
   entrada del .gir de evolution-data-server. Traerla metería toda la
   maquinaria de introspección de GNOME en la clausura de COSMIC para no
   usar un solo .typelib.

4. EL CHOQUE DE gvdb, y por qué se resolvió renombrando símbolos. El portal
   vendoriza gvdb y glib TAMBIÉN lo lleva adentro (GSettings): con glib
   estática el .a expone los símbolos y el enlazador ve dos definiciones.
   Los otros dos caminos se descartaron POR MEDICIÓN:
     · --allow-multiple-definition / -z muldefs: zig-cc NO lo acepta
       («unsupported linker arg»), y meson lo reporta como «Compiler
       hammer-zig-cc cannot compile programs», que manda a buscar el
       problema donde no está.
     · glib-shared: no es cambiar una dep sino abrir una cadena (json-glib y
       gdk-pixbuf también enlazan glib) y meter la segunda glib en la imagen.
   El renombrado con -D es total —afecta definición y usos, todos pasan por
   gvdb-reader.h— así que el binario queda con lector+constructor de la
   misma copia. Importa: glib empaqueta sólo el LECTOR, el CONSTRUCTOR
   existe únicamente en el portal.

Bonus de meson: las opciones de tipo lista se parten por COMAS, así que
-Dc_args/-Dc_link_args no sirven para banderas con comas; van por CFLAGS y
LDFLAGS. Y quitar un subdir() no es gratis: subdir comparte ámbito de
variables, y sacar subdir('tests') rompía el summary() que lee
enable_pytest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 23:56:04 -04:00
sergioandClaude Opus 5 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>
2026-08-04 23:40:00 -04:00
sergio bf07e20deb estado: cosecha granja 2026-08-05T03:34:29Z — avance del árbol KDE 2026-08-04 23:34:29 -04:00
sergioandClaude Opus 5 04146ec77b 🛍 cosmic: la tienda LISTA — el catálogo AppStream hubo que fabricarlo, y el <provides> la vaciaba
Validado con pantalla: «Popular apps» poblada (App Library, Files,
Launcher, Settings), «Recently updated», y la búsqueda `text` devolviendo
COSMIC Text Editor. 2564 colores sobre un escritorio completo.

DOS TRAMPAS ENCADENADAS, y la primera invalida lo que dije al escribir la
receta:

1. El backend `pkgar` NO lee /usr/share/metainfo/*.metainfo.xml —los
   ficheros que instala cada receta—. Lee el CATÁLOGO, o sea
   {/usr/share,/var/lib,/var/cache}/{swcatalog,app-info}/{xml,yaml}/, que en
   una distro normal genera `appstreamcli compose`. Sin ese fichero la
   tienda abre perfecta y no lista NADA. Lo genera ahora
   qemu-desktop-image.sh agregando los metainfo en un <components>: doce
   líneas de python, cero AppStream y cero glib.

2. Con el catálogo puesto listaba 2 de 7. Los metainfo que genera `xdgen`
   escriben <provides> con los envoltorios <mimetypes>/<binaries>, y el
   crate `appstream` espera los hijos PLANOS del esquema de catálogo: muere
   con «The tag provide doesn't have a value» y DESCARTA EL COMPONENTE
   ENTERO, callado. Los dos que se veían eran justo los que no tienen
   <provides>. Se elimina el bloque al agregar.

Cómo se diagnosticó, que es lo reutilizable: desde el lanzador la salida del
proceso no llega al log de la sesión. Hay que abrir cosmic-term DENTRO de la
VM y correr `rm -rf ~/.cache/cosmic-store; RUST_LOG=info cosmic-store`. El
rm no es opcional: la tienda cachea el parseo en bitcode y sin borrarlo
repite el resultado viejo.

Y el deadline del panel es una CARRERA, no un umbral: un arranque de esta
tanda salió mutilado (130 colores) con el anfitrión ocioso y -smp 8. Si sale
mutilado con carga baja, rearrancar antes de culpar al propio cambio.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 23:14:01 -04:00
sergio f808693060 estado: cosecha granja 2026-08-05T03:04:12Z — avance del árbol KDE 2026-08-04 23:04:12 -04:00
sergioandClaude Opus 5 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>
2026-08-04 22:42:53 -04:00
sergio eb792b73b9 estado: cosecha granja 2026-08-05T01:00:58Z — avance del árbol KDE 2026-08-04 21:00:58 -04:00
sergioandClaude Opus 5 539e2acd5b ✎ cosmic: cosmic-edit VALIDADO en QEMU — y el deadline del panel sí mira la carga del anfitrión
El editor abre desde el lanzador (Super → «text» → primer resultado) y se
ESCRIBE: la evidencia tiene `hammer edita` tecleado sobre la línea 1 y el
punto de modificado en la pestaña. 2105 colores, icono en el dock con el
punto de «en ejecución». Hidratación 72 recetas (era 71).

CORRECCIÓN al runbook, medida acá: decía «no es carga del anfitrión (falla
con el anfitrión ocioso)», cierto para 4 vCPU pero se leía como que con
`-smp 8` el anfitrión daba igual. Dos arranques de la MISMA imagen con la
MISMA línea de QEMU:

  load ≈ 6 (otro agente compilando) → 130 colores, mutilado, `deadline has
                                      elapsed` en el log
  anfitrión ocioso (load ≈ 1,8)     → 1671 colores, completo

Este anfitrión tiene 8 núcleos, así que `-smp 8` le da la máquina entera a
la VM y cualquier compilación compite por los mismos hilos. Receta: mirar
/proc/loadavg antes de creerle a un arranque mutilado.

También: con las cuatro aplicaciones en el dock el escritorio completo da
~1670 colores, no 894. El umbral útil es «cientos vs miles».

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-04 20:40:35 -04:00