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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
Sellada b3:716dac53. Cola 33/33, escritorio-cosmic 71/71. Abre desde el lanzador con la
barra lateral entera (Red, Bluetooth, Accesibilidad, Escritorio, Pantallas, Sonido,
Energía, Entrada, Aplicaciones, Fecha y hora, Sistema y cuentas). Tarda ~20s con render
por software; no está colgado.
La sorpresa buena: nada de NetworkManager, libpulse, udisks ni accountsservice — habla con
los daemons por zbus, que es Rust puro. Se verificó ANTES de escribir la receta listando
los crates -sys del Cargo.lock, que es donde vive la verdad sobre qué C hace falta: sólo
drm-sys, input-sys, libudev-sys, dav1d-sys, wayland-sys (dlopen) y gettext-sys. Regla
barata: grep '^name = ".*-sys"' Cargo.lock antes de adivinar deps por lo que el programa
hace.
Volvió a morder la doble barra de cosmic-protocols// (404 de GitHub, muere en el fetch con
tres reintentos). Segunda víctima ⇒ es patrón de la suite, no rareza de un repo.
Instala 32 .desktop, uno por página. Y default_schema va con find, no cp plano: es el árbol
<Componente>/v1/<clave> de cosmic-config, donde cada clave es un fichero con su nombre.
Deuda que deja su propio log: org.freedesktop.locale1 no lo sirve nadie ⇒ la página de
idioma no podrá cambiar nada. Mismo tipo que login1, mismo arreglo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sellada b3:48d2b072. Cola 32/32, escritorio-cosmic 70/70. Abre desde la biblioteca y lista
el sistema de ficheros real de hammer (bin 72 ítems, dev, ente, etc, lib, lost+found).
Entró creyendo que era casi gratis porque cosmic-term ya la compila como crate. No lo era:
cosmic-term la declara con default-features = false. Lo que un paquete cuesta depende de
con qué features lo pide quien lo usa, así que «ya se compiló» puede ser falso.
Dos features apagadas, las dos por medición:
· gvfs trae gio/glib y el enlace final pide las glib COMPARTIDAS; el corpus sólo las tiene
estáticas. Se pierden montajes remotos, no la navegación local. Por eso tampoco se
construye cosmic-files-applet: su Cargo.toml fija gvfs a mano.
· wgpu desbordó el filesystem dos veces (No space left on device en /src/target) con
wgpu+naga+ash+glow+spirv a codegen-units=1. Sin él el árbol queda en ~2 GB, y no se
pierde nada: el resto de la suite pinta con tiny-skia y la imagen no tiene GPU.
Y la regla que dejó el intento con glib declarado: el error fue «Package libpcre2-8,
required by glib-2.0, not found» — el que falla no es la dep sino lo que su .pc declara en
Requires. Declarar una dep trae su artefacto, no su clausura de pkg-config; se lee con
grep ^Requires sobre los .pc ANTES de gastar el build.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sellada a la primera (b3:d0ea1c1e). Cola 31/31, escritorio-cosmic 69/69.
Dentro de la ventana: bash-5.3#, uname -srm devuelve Linux 6.16.12 x86_64 —el kernel
propio de hammer— y qalc -t '6*7' devuelve 42, o sea la calculadora que empaquetamos una
hora antes. Se abre con click en su icono en la biblioteca, que hasta hoy salía vacía con
razón: los 20 .desktop de la imagen eran NoDisplay=true. Éste no lo es.
Lo que arrastra y no se adivina: su Cargo.toml depende de cosmic-files, o sea que el
gestor de ficheros entra como LIBRERÍA (selector y drag-and-drop) y compilar la terminal
compila medio gestor de ficheros. En esta suite las aplicaciones se usan unas a otras como
crates, y el grafo de Cargo no se parece al mapa de componentes. De paso, empaquetar
cosmic-files como aplicación queda casi gratis.
wgpu viene en default, al revés que el resto de los clientes: se deja, porque wgpu y naga
ya se habían compilado enteros para cosmic-workspaces y apartarse de las features por
defecto es apartarse de la única combinación que upstream prueba. Cierra con dos NEEDED.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
De las recetas de incoming-cosmic que declaran link="static", ninguna tiene NEEDED
indebido ni los símbolos de stdio compartiendo dirección. Era un caso, no un patrón.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
=123*456 → 56088 en el lanzador. libqalculate re-sellado b3:edb8d14d.
La causa del «no lee nada de la tubería» no era readline, ni el toolchain, ni la versión:
el binario salía DINÁMICO pese a link="static", porque libtool se come el -static que
inyecta el harness. Y en un musl dinámico stdin, stdout y stderr se resolvieron por copy
relocation a UNA SOLA ranura de BSS compartida —las tres en la misma dirección—, así que
fileno(stdin) daba 1: el stdin del programa era el stdout. Eso explica la asimetría que
parecía absurda, que qalc -f /dev/stdin funcionara (abre el fd por RUTA, sin tocar el
FILE* roto) y qalc leyendo stdin no.
Fix: make LDFLAGS="-all-static -no-pie", el mismo par que ya usa libxml2. Con él los tres
símbolos vuelven a tener direcciones distintas en .rodata. Regla para el runbook:
link="static" en la receta NO garantiza un binario estático — readelf -d y
nm -S | grep ' std' lo contestan en dos segundos.
Segundo muro: qalc pregunta si activar autocalc antes de leer nada, el plugin le manda la
expresión y cierra, la expresión se consume como respuesta y qalc nunca llega a guardar
config ⇒ vuelve a preguntar siempre. Se siembra qalc.cfg desde cosmic-start, política de
imagen como el color de fondo.
Y hay que repetir el -L de los alias de curses en el make: el LDFLAGS del make pisa al
del configure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Las tres selladas a la primera (gmp b3:d99a0a5d, mpfr b3:13982d20, libqalculate
b3:6bb435a7). La cadena entera desde una tecla está en el catálogo: Super →
cosmic-launcher → pop-launcher → plugin calc → qalc → mpfr → gmp. Cola 30/30,
escritorio-cosmic 68/68.
qalc FUNCIONA: -t '2+2' da 4, -f fichero anda, e interactivo sobre terminal calcula.
Pero el plugin lo invoca SIN expresión, con stdin en tubería, le escribe la cuenta y
cierra — y en ese modo nuestro binario no lee nada. Acotado a que, con el EOF ya
pendiente en un stdin que no es terminal, readline() devuelve NULL antes de entregar la
línea que sí está en el búfer.
Descartado midiendo, para no repetirlo: no es el toolchain (una sonda estática musl del
sandbox lee la tubería en C y en C++); no es readline vs no-readline (sin ella es peor:
no lee nunca); no es la versión de readline (una 8.3 sombra no movió el síntoma, y se
descartó en vez de dejar una receta duplicada inútil); y no es el descriptor 0, porque
Could not open "/dev/stdin". —el mismo pipe reabierto por ruta— sí funciona. El que no sirve es
el FILE* stdin, y ahí queda la pista.
Dos gotchas del empaquetado: gmp va con --disable-assembly (elige rutinas mirando la CPU
de quien compila, o sea hornearía la ISA del laptop en el hash), y el configure de
libqalculate no encuentra readline porque prueba -lncurses/-lcurses/-ltinfo y el corpus
sólo empaqueta libncursesw.a — los alias van en un directorio local, no ensuciando
/usr/lib, que es la evidencia de qué se declaró.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cosmic-launcher sólo dibuja: manda lo tecleado por stdin a un proceso hijo, pop-launcher,
que es quien busca. Sin ese binario la ventana no tiene qué mostrar y no se muestra —
mismo modo de falla que el panel sin applets: falta un HIJO, no una librería, y el log
del padre se lee sano.
Vive en otro repo (pop-os/launcher), fuera del pin epoch-N. La versión la fija el
Cargo.lock de cosmic-launcher (rev a332a3a7 de la 1.2.7), no el último tag: el lock es
lo que garantiza que hablen el mismo protocolo. Tarball por commit, porque no hay tag que
nombre esa rev. Selló a la primera, b3:64a5cbaa, con un solo NEEDED: libc.so.
Verificado de punta a punta: Super abre el lanzador, «?» lista los seis plugins con su
sintaxis (o sea que la enumeración y el IPC JSON-sobre-stdio andan) y «=2+2» LANZA el
plugin, que contesta «qalc command is not installed». Esa respuesta es la mejor prueba
disponible: el hijo corrió, evaluó y devolvió fila. Falta libqalculate, que es un
paquete, no un problema.
Cola 27/27, escritorio-cosmic 63/63.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Faltaba sanear un SEGUNDO fichero: system_actions de cosmic-settings-daemon, que liga
acción→comando, con la misma entrada ScreenReader que el enum del parser no conoce. Con
defaults roto no hay ligaduras; con system_actions roto hay ligadura y ningún comando.
Ahora Super+A abre la biblioteca y Super+W la vista de espacios de trabajo — con
miniatura VIVA del escritorio dentro, o sea que el camino DMA-BUF→GBM de cosmic-workspaces
anda de verdad. Super a secas no abre nada porque pop-launcher (el motor del lanzador)
no está en el catálogo; los atajos no tienen la culpa.
Me equivoqué de fichero teniendo el dato: el error traía línea y columna y busqué el
token en vez del fichero que lo tiene en esa posición. Costó 40 minutos de rebuild.
Y el panel tiene un DEADLINE para registrar su servidor D-Bus: con -smp 4 arranca sin
ala izquierda, sin reloj y sin dock (894 colores vs 130); con -smp 8 -m 8192 son 4 de 4
completos. Perseguí eso como una regresión mía, revirtiendo tres cambios inocentes, porque
usé el conteo de procesos como veredicto: un escritorio sano muestra 32, no 48. El
framebuffer es el veredicto; los procesos no distinguen «arrancó» de «dibujó».
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El click sobre «Applications» abre la biblioteca (el color dominante pasa de fondo 91%
a 27,27,27 84%) y tecleando aparece «cosmic» en el buscador. Se maneja entero por QMP
con input-send-event, sin humano y sin ver la pantalla.
USB HID, no virtio-input: el kernel de hammer no trae VIRTIO_INPUT, así que
-device virtio-keyboard no crea ni un /dev/input/event*. Con qemu-xhci + usb-kbd +
usb-tablet anda, y además es lo que se va a usar en metal.
Y el hallazgo de fondo: Super no abría el lanzador TENIENDO la ligadura en el fichero.
keybindings.ron de epoch-1.5.0 liga Super+Alt+S a System(ScreenReader), acción que el
crate que lo parsea (cosmic-settings-config, rev 8c54bbbc) no tiene en su enum. RON no
ignora lo desconocido: falla el mapa entero y se caen las 29 ligaduras. El único rastro
era un WARN de cosmic-idle a 200 líneas del arranque hablando de un enum — el síntoma no
nombra ni la tecla ni el fichero. El pin lockstep fallando desde adentro de upstream.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
26/26 recetas selladas, escritorio-cosmic 62/62. La captura (895 colores) muestra el
panel superior con Workspaces/Applications, reloj y los applets de a11y, audio, red,
batería y encendido; el dock inferior con lanzador, workspaces y biblioteca; y el fondo
en el color exacto que configura cosmic-start. 48 procesos cosmic estables a los 30s.
Y CORRIGE UN DIAGNÓSTICO MÍO. Cuando el panel selló y no dibujó, lo atribuí a que le
faltaban los applets. Hacían falta —sin ellos el panel está vacío— pero no eran la causa
de la pantalla lisa: -device virtio-gpu-pci no reemplaza la VGA por defecto de QEMU, la
SUMA. Dos cards, dos outputs, cosmic-bg pinta en los dos y el panel se ancla en uno; el
screendump capturaba el otro. Log impecable, pantalla lisa. Con -vga none sale entero.
Un síntoma puede tener dos causas suficientes: arreglar la primera no prueba nada.
También: PATH como primera línea de cosmic-start. El getty da un PATH sin /usr/bin y en
la imagen mkdir sólo vive ahí, así que los cuatro mkdir -p fallaban en silencio — gratis
hoy porque los directorios venían horneados, fatal el día que cambie la imagen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Los cuatro que faltan (applets, launcher, app-library, workspaces) quedaron sin
sellar porque se cortó la sesión de trabajo, NO porque fallaran: los tres clientes
venían compilando limpio y cosmic-applets ya había pasado el vendoreo y la dep de
libdbus. Se retoman con un 'hammer build' directo, sin nada que diagnosticar.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cosmic-session levanta la cadena completa sobre el kernel de hammer con arje-zero
como PID1: cosmic-comp → settings-daemon → notifications → panel → osd → bg. El
fondo pinta (164 colores, el azul configurado) y el cursor está.
Los cuatro que faltan (app-library, launcher, workspaces, greeter) dejan una línea
de error! y la sesión SIGUE VIVA — exactamente lo que la tabla de .expect del
runbook predecía. Verificado, no supuesto.
cosmic-notifications sellado (b3:3e2a5478) completa el mínimo viable de cuatro.
Nota de método: esta corrida fue headless porque el Xwayland del laptop perdió la
autorización a mitad de sesión ('Authorization required, but no authorization
protocol specified'). El screendump por el monitor de QEMU sirve igual y es la
forma automatizable de la regla de validar con pantalla.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El arranque en modo session murió con 'failed to start notifications daemon'
(main.rs:306). Grepeando '\.expect(' aparecen los tres:
main.rs:255 cosmic-settings-daemon
main.rs:306 cosmic-notifications
main.rs:325 cosmic-panel
O sea que el mínimo viable de una sesión COSMIC son CUATRO binarios: compositor
más esos tres.
Cómo me equivoqué, que es la parte reusable: leí start_component(), vi que sólo
logueaba, y generalicé a 'todos menos el settings-daemon'. Pero notifications y
panel NO PASAN por esa función — tienen su propio .expect() unas líneas antes.
Buscar la función que lanza no es lo mismo que buscar los .expect del fichero, y
grepear la construcción que mata cuesta lo mismo y no generaliza de más.
De paso, el bloque que configura el fondo de color sale del branch de modo bare:
estaba adentro, así que en modo session cosmic-bg se quedaba con el wallpaper por
defecto (que vive en git-lfs y no existe) y la pantalla salía negra por una razón
distinta de la que se estaba investigando.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
start_component() sólo hace error!() y sigue (main.rs:558). Medido leyendo el
código, no probando: la sesión arranca con la suite incompleta y lo que falte
aparece como líneas de error. Cambia el orden en que conviene probar.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cosmic-bg dinámico (b3:b2cfc410) se conecta a cosmic-comp, entrega su buffer y el
compositor lo presenta: fondo completo en el color EXACTO que se le configuró,
(0.13,0.29,0.53) → (33,74,135), 164 colores distintos.
Que el color sea el pedido y no uno cualquiera es lo que hace de esto una
medición: descarta un buffer sin inicializar o el borrado del compositor. La
cadena cliente→compositor→KMS funciona de punta a punta.
El NEEDED del binario confirma de paso que dav1d no era un capricho del grafo:
libdav1d.so.7 y libc.so, nada más.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El default de cosmic-bg apunta a una imagen del repo cosmic-wallpapers, cuyo
tarball de GitHub pesa 20 KB: son punteros LFS, no imágenes. Una receta escrita
del modo normal produciría un paquete de ficheros de texto que además PARECERÍA
correcto — nombre bueno, ruta buena.
Mientras tanto cosmic-start escribe la config de usuario con una fuente Color,
que cosmic-bg soporta de fábrica. Va en el lanzador y no en el artefacto porque
es política de esta imagen de prueba, no algo que el paquete prometa.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pantalla completa en el gris de COSMIC (39,41,42) con el cursor dibujado, 134
colores distintos, validado CON PANTALLA (DISP=gtk + screendump).
Entra el andamiaje completo: hydrate-cosmic.sh (44 recetas, 0 faltantes),
qemu-desktop-image.sh y cosmic-start-qemu.sh.
Tres causas medidas en tres arranques, ninguna donde uno la busca:
1. cosmic-settings-daemon NO es opcional: cosmic-session lo lanza con .expect()
(main.rs:255) y panickea si falta. Corrige lo que este runbook afirmaba. De
ahi el modo bare del lanzador, que separa como compone de como arranca la
sesion.
2. La pantalla negra era libz.so.1: kms_swrast_dri.so lo NEEDea y el corpus solo
traia zlib estatica. Sin driver de software no hay GBM y el compositor arranca
igual, negro. Causa a tres capas del sintoma.
3. Los clientes no pueden ser estaticos: wayland-client entra con la feature
dlopen y un musl estatico no tiene dlopen funcional — moria 'The wayland
library could not be loaded' CON el .so presente.
Y dos carreras con /run (dbus y XDG_RUNTIME_DIR) de la misma forma que el bug de
PolicyKit1 de hoy en GNOME: comprobar al principio y usar al final. Se crea justo
antes de usar.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Decía «copiar es gratis de verdad, no casi». La regla honesta es: gratis SÓLO si
toda la clausura transitiva resuelve a lo mismo. Se cumple con libdisplay-info,
hwdata y nasm; no con pipewire.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El compositor compiló a la primera en 51m39s (JOBS=1 + lto fat), sin un solo
parche a la fuente; lo único que hubo que corregir fue el --bin del gotcha 1.
Lo que vale es el enlace, porque es medición y no impresión: ocho NEEDED, y las
siete primeras son EXACTAMENTE los backends de smithay que la receta declara
(display-info, gbm, seat, udev, input, pixman, xkbcommon). La octava es musl.
No hay libgcc_s —el compositor no arrastra la deuda del unwinder— y no aparece
nada sin declarar: la clausura de build y la de runtime coinciden.
Un compositor Wayland completo (DRM/GBM/EGL/libinput/seat/Vulkan/XWayland,
renderer multi-GPU y UI en iced) cerrando con ocho librerías compartidas y cero
parches es el resultado más limpio de las cuatro campañas de escritorio.
Queda anotado además por qué cosmic-settings-daemon NO se resuelve copiando
pipewire: copiar entre colas es gratis sólo si TODA la clausura transitiva
resuelve igual, y ahí glib resolvería distinto (sombra de GNOME b3:f6ccdf98 vs
corpus b3:93d2cad0) ⇒ un segundo pipewire contra otra glib, que es el cuadro de
dos registros de GType que la campaña GNOME ya midió y evitó. Medido con yupana
radio antes de tocar nada.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cosmic-idle se escribió sin [deps] con un argumento que parecía sólido: no usa
smithay-client-toolkit (el que hizo caer a cosmic-bg) y un demonio de inactividad
no interpreta teclas. Reventó igual, pero en el ENLACE y no en un build.rs:
-lxkbcommon lo arrastra cosmic-settings-config, de donde sale la tabla de atajos
y que usa TODO componente de la suite. La dep no viene de lo que el paquete hace
sino de la librería de configuración común.
Dos veces el mismo error de método con dos razonamientos distintos, y las dos
veces el mensaje decía exactamente quién y por qué. Queda en el runbook como
regla, no como anécdota.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
cosmic-bg se escribió sin [deps] con ese argumento y reventó en el build.rs de
smithay-client-toolkit pidiendo xkbcommon.pc. El que dlopea xkbcommon es winit,
que es otra capa. Hablar el protocolo en Rust dice cómo viaja el byte, no con
qué se interpreta un teclado. El link=static sigue siendo verdad: el artefacto de
libxkbcommon trae .a además de .so.
Corolario anotado en el runbook: todo cliente de la suite va a necesitar al menos
libxkbcommon + pkgconf.
Entran además cosmic-idle (cliente Wayland puro, SIN deps a propósito: no usa
sctk y no necesita interpretar teclas) y cosmic-osd, que es el primer cliente de
iced/libcosmic y por eso vale como sonda: panel, launcher, app-library,
workspaces y notifications comparten su mismo sustrato exacto.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deja asentado por qué COSMIC no se parece a KDE ni a GNOME (no hay torre de C
debajo: la primera receta salió estática y con cero NEEDED), la tabla de lo que
se hereda gratis de mirada/GNOME, el pin en lockstep epoch-1.5.0, y los seis
gotchas medidos — entre ellos que start-cosmic es bash de verdad (mapfile, [[ ]],
${!var}) y que bash no lo pide ningún [deps], así que a la imagen no lo trae
nadie por accidente.
Y el porqué del orden de ataque, que NO es el orden de arranque: cosmic-bg antes
del panel para que «pinta el fondo pero no el panel» separe la capa de UI del
transporte.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
El plan seguía sin tilde aunque el shell arranca justamente porque están: se
verificó uno por uno con `hammer hash` contra el store en vez de fiarse del
recuerdo. Se deja el texto original del plan por la advertencia de libelogind,
que sigue vigente.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
accounts-daemon y upowerd morían al arrancar: el shim de polkit se lanza en
background y tarda ~2s en adquirir org.freedesktop.PolicyKit1, ellos llamaban a
polkit_authority_get_sync() en ese hueco, D-Bus intentaba ACTIVAR el servicio y
fallaba con 'Failed to execute program ... Permission denied'.
Lo peor era el informe: los tres esperar_nombre estaban al final, así que para
cuando corrían el shim ya tenía el nombre y el resumen daba 'PolicyKit1 OK'
sobre dos clientes ya muertos. Un chequeo posterior a la carrera no la mide.
esperar_nombre pasa a definirse antes del primer daemon y el bloque de polkit
espera su propio nombre antes de que arranque ningún cliente suyo. Lanzar en
orden no es estar listo en orden.
De paso queda cerrado el ítem 4 del runbook: validación CON PANTALLA (DISP=gtk)
del Overview con el PID1 sellado nuevo — 689 colores distintos, con evidencia.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Audio
├─ Devices: 48. HDA Intel [alsa]
├─ Sinks: * 52. HDA Intel Analog Stereo [vol: 0.40]
├─ Sources: * 53. HDA Intel Analog Stereo [vol: 1.00]
Y gvc con perfiles reales (`output:analog-stereo+input:analog-stereo`). Se fue el auto_null.
LA CAUSA: SPA pide el device `card` (alsa-udev.c:178-183), que no tiene major:minor, y
libudev-zero recorría SÓLO /sys/dev/{block,char} — sólo devices CON nodo. El device que
SPA necesita nunca entraba en la enumeración: no faltaba una propiedad, faltaba la mitad
del espacio de búsqueda.
**Y LA PRIMERA VERSIÓN DEL PARCHE ARREGLÓ EL AUDIO Y ROMPIÓ EL VÍDEO.** Recorría
/sys/class entero, y /sys/class/drm no trae sólo tarjetas: trae los CONECTORES
(card0-Virtual-1) y un fichero `version`, ninguno con nodo. Mutter los tomaba como
candidatos ⇒ `No available CRTC for monitor` + `Page flip failed: drmModeAtomicCommit:
Invalid argument` en bucle. Pantalla negra con el compositor vivo: el mismo síntoma de
antes por una causa nueva. El udev real también los enumera, pero les adjunta las
propiedades por las que mutter los descarta, y ésas libudev-zero no las sintetiza.
La lección, que vale para cualquier reimplementación parcial de una API: **ensanchar la
enumeración expone los huecos de PROPIEDADES a más consumidores. Los dos lados del
contrato tienen que crecer juntos.** El parche final lleva una LISTA ACOTADA de
subsistemas (`sound` hoy); agregar otro es una línea *y* una prueba de que el consumidor
lo necesita y no se rompe.
POR QUÉ NO eudev — y corrijo mi propia sugerencia de antes, que era la peor opción: el
repo ya lo había probado en otro frente y ROMPIÓ EL INPUT. Su libudev.so.1 pisa el de
libudev-zero y, sin udevd, no expone ID_INPUT ⇒ libinput ignora todos los dispositivos en
silencio. Verificación A/B en QEMU de aquel episodio: eudev = 0 «New device»,
libudev-zero = 5. eudev sin udevd es PEOR, y correr udevd contradice la arquitectura
(arje-zero es PID1). El arreglo va donde está el hueco.
Radio pagado: 51 sellados a deuda, 9 en el cierre GNOME (reconstruidos: libinput,
libgudev, colord, mutter, gnome-shell, libgdm, upower, pipewire, wireplumber). El resto
—base, cli, KDE— queda para el latido.
Captura actualizada: escritorio pintando (628 colores) Y audio, cero page-flip fallidos.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cierra el gestor de sesión de PipeWire, que es lo que separa «PipeWire acepta clientes»
de «PipeWire tiene dispositivos». Su política está escrita en Lua, así que arrastró una
receta de Lua que hubo que autorar entera.
LO QUE LA RECETA DE LUA TIENE QUE INVENTAR: el Makefile de upstream sólo produce
liblua.a y los binarios — **no hay regla de .so ni fichero pkg-config**, los agrega cada
distro. Acá la compartida se enlaza a mano desde la .a con --whole-archive (una estática
sólo aporta lo referenciado y hay que llevarse todo) y el .pc se escribe con los TRES
nombres que se usan por ahí, porque wireplumber prueba lua-5.4, lua5.4 y lua54 en orden.
`-fPIC` no es opcional: libwplua es un objeto compartido.
Dos símbolos más en libelogind (tawasuyu 087230054 → re-pineado 749edfe41):
sd_uid_get_seats y sd_uid_get_state, que module-logind.c de wireplumber usa para saber si
el usuario está en un asiento antes de tomar los dispositivos. Van 28 símbolos sd-*.
**EL `Devices:` VACÍO DE wpctl NO ES UN FALLO DE wireplumber: el kernel no tiene ALSA.**
recipes/linux-metal.toml:111 lo apaga explícito (`-d SOUND -d SND`), así que no hay
/dev/snd que enumerar. Y esto NO se dio por supuesto: se agregó una ich9-intel-hda
emulada a QEMU (AUDIO=1, ahora el default de run-qemu-desktop.sh) justo porque un
«Devices: vacío» se lee IGUAL si wireplumber funciona sobre una VM sin hardware que si no
funciona. Con tarjeta y sin ALSA en el kernel, el resultado no cambia — la ambigüedad
queda resuelta. Encender el sonido en la distro es una decisión con costo (re-sellar el
kernel, rehacer la imagen metal) y queda a la vista en vez de escondida en un default.
Bug propio destapado en el camino: el bloque de wireplumber quedó DUPLICADO en
gnome-start y había DOS wireplumber peleándose el grafo. El síntoma no era un error sino
una lista de clientes que se lee normal si no se cuenta: dos «WirePlumber» con pids
distintos en wpctl status.
Y una nota de ADR 0012 que costó un rato: la cascada de libelogind cortó a mitad y dejó
el `output/` de mutter a medio hacer; el reintento moría con «error opening
'...c.o.d': No such file or directory». Un `rm -rf output` en el configure NO alcanzó
—el árbol tenía estado viejo más allá del build dir—; lo que lo arregló fue BORRAR
work/sources/mutter-* para que el fetch lo re-extraiga limpio.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Cierra una pregunta que estaba explícitamente abierta en varias recetas, y que es la
razón por la que `pulseaudio` se había construido con `-Ddaemon=false`: sólo el cliente,
para que gvc hablara el protocolo sin cerrar la decisión de prestado.
**Los dos conviven y por eso la decisión no rompe nada**: pipewire va con
`-Dlibpulse=enabled` ⇒ `pipewire-pulse`, un servidor que habla el protocolo de
PulseAudio. gnome-shell usa libpulse sin enterarse de qué hay del otro lado. No hubo que
tocar una línea del cliente.
== gnome-qemu :: socket pipewire-0 OK
== gnome-qemu :: socket pulse/native OK — gvc va a poder conectar
Gvc-DEBUG: Updating client: index=32 name='pipewire'
Gvc-DEBUG: Updating sink: index=33 name='auto_null' description='Dummy Output'
El `Failed to connect context: Connection refused` desapareció. El sink es auto_null, que
es la respuesta honesta: la VM no tiene tarjeta de sonido.
**⚠ Falta wireplumber** (gestor de sesión, Lua + su cierre). Sin él PipeWire acepta
clientes pero NO enumera ni enruta dispositivos. «El shell conecta» ≠ «hay sonido», y el
log de arriba es justo el caso donde confundirlos sería fácil. Queda escrito en la receta
y en el runbook.
GOTCHA MEDIDO: ya existía incoming-kde/pipewire.toml sellada y NO se puede reusar
hidratándola acá — los dos cierres traen glib y **no son la misma glib**:
incoming-kde/glib-shared y incoming-gnome/glib producen libglib-2.0.so.0.8800.1 con bytes
distintos (verificado con cmp) y comparten 370 rutas. Proyectar las dos deja que una gane
por orden de proyección ⇒ dos registros de GType en un proceso, el cuadro que costó el
episodio de colord. De ahí la copia en la cola GNOME (glib-shared→glib, dbus→dbus-shared,
pcre2-shared→pcre2), que sí re-construye. Lo que FUE gratis: alsa-lib, cuya única dep es
pkgconf y resuelve al catálogo PADRE ⇒ hash idéntico y cache hit (b3:93cae411).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
bus: org.freedesktop.login1 OK
bus: org.freedesktop.PolicyKit1 OK
bus: org.freedesktop.Accounts OK
bus: org.freedesktop.UPower OK
accounts-daemon y upowerd arrancaban, seguían vivos, y NUNCA adquirían su nombre: los
dos se bloquean en `polkit_authority_get_sync()` al iniciar, y `org.freedesktop.PolicyKit1`
no lo servía nadie —nuestra receta polkit es libs-only a propósito, porque el demonio lo
pone arje—. gnome-shell esperaba después 25s por cada uno.
**El dato que lo resolvió no salió del log de los daemons sino de la LISTA DE NOMBRES DEL
BUS**: ahí estaban como conexiones anónimas `:1.0`, `:1.1`, sin nombre bien conocido. Eso
distingue tres cosas que en el log del cliente se ven igual — «no arrancó», «arrancó y no
llegó a pedir el nombre» y «lo pidió y se lo negaron». Era la segunda. `gnome-start` ahora
espera cada nombre con NameHasOwner y, si no aparece, vuelca el log del daemon y la lista.
Receta nueva `arje-polkit-compat` (b3:03e0a86f), tercer shim de este tipo tras
arje-logind-compat y arje-sdlogin-compat. Su propio autor ya había escrito el diagnóstico
en el crate: «apps que usan polkit bloquean en CheckAuthorization si no responde nadie».
⚠ AUTORIZA TODO — es la postura de sistema confiado que arje ya tenía tomada, no algo que
esta receta introduzca; queda explícito en su comentario.
Dos gotchas del empaquetado:
- **La política de polkit ya existe y NO alcanza**: permite `own` sólo al usuario polkitd
y el shim corre como root. Se agrega un `zz-arje-polkit-compat.conf` en vez de reescribir
la de upstream — dbus lee system.d en orden alfabético y las posteriores ganan.
- **Los ficheros del rootfs fundido son hardlinks de SÓLO LECTURA del store** (0444). Un
`cat >` encima falla con Permission denied y rompió un build. Regla: nunca sobrescribir
un fichero que venga de un artefacto; agregar al lado.
Queda escrito en el runbook lo que sigue faltando (colord, y que el shell no tiene servidor
de sonido porque pulseaudio se construyó sólo-cliente: el demonio de audio de la distro
sigue sin decidirse) y las dos lecciones de método que costaron una iteración cada una.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Captura actualizada: ahora se ve el puntero (Adwaita) y el icono de la notificación.
Colores distintos 582 → 629.
El setuid del dbus-daemon-launch-helper explicaba DOS cosas, no una: el «permission of
the setuid helper is not correct» de colord y, por la misma vía, que ninguna activación
por bus de sistema funcionara. Con el arreglo, colord pasó de «permiso incorrecto» a
«timed out» — la activación ya se intenta y lo que queda es del demonio.
Queda UPower: upowerd arranca y sigue vivo, pero no adquiere su nombre en el bus (su
política D-Bus está verificada y permite own a root), así que el shell espera sus 25s.
Es el indicador de batería en una VM sin batería.
Y queda escrita la lección que costó una iteración: **un pid no es un servicio**.
Reportar «lanzado» sin comprobar que el daemon adquirió su nombre es reportar una
intención, no un hecho.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs/evidencia/gnome-shell-qemu-2026-07-29.png: el conmutador de espacios de trabajo,
el reloj en el panel, la miniatura del escritorio, el dash, y la notificación estándar
de GNOME por sesión de root. Todo construido desde fuente sobre el kernel de hammer,
arje-zero como PID1 y musl.
**EL MURO NO ERA DE KMS.** El diagnóstico anterior culpaba al plano primario de
virtio-gpu («no advertised formats», sólo `Queue mode set`, ningún page flip). Era
correlación. Se probó quitando MUTTER_DEBUG_FORCE_KMS_MODE=simple —el log pasó a decir
`using atomic mode setting`, o sea que el cambio SÍ tomó efecto— y la pantalla siguió
con exactamente 2 colores. Refutada. Mutter no tenía un frame que presentar; no es que
no supiera presentarlo.
La causa estaba en el stack trace, en el log, desde el principio: la excepción por el
gschema `org.gnome.settings-daemon.peripherals.touchscreen` ocurre DENTRO de
`Main.start()`, construyendo los quick settings ⇒ el panel nunca se arma. Proceso vivo,
compositor con DRM master, nada que pintar.
Receta `gsd-schemas`: los gschemas de gnome-settings-daemon SIN g-s-d, que sigue
aparcada por GTK3/X11. **Tercera vez que aparece la misma lección** (tras la política
D-Bus de login1 y el `org.gnome.login-screen` de libgdm): un gschema no es un fichero de
datos del demonio, es una interfaz publicada que consume otro programa.
Dos trampas de diagnóstico, las dos ahora blindadas:
- **`glib-compile-schemas` sale con 0 aunque RECHACE un esquema.** Instalé los once XML,
el log dijo `esquemas OK` y el shell seguía diciendo `not found`: faltaba
`org.gnome.settings-daemon.enums.xml`, que meson genera con glib-mkenums y no es uno
de los `.in`. Sin los `<enum>`, glib descarta el esquema entero, avisa por stderr y
sigue. `gnome-start` ahora vuelca ese stderr SIEMPRE, no sólo al fallar.
- El log serial persiste entre arranques y me hizo leer dos veces un `STATUS +30s` de una
VM ya muerta. Truncarlo antes de arrancar; `Failed to get "write" lock` avisa de que
hay otro QEMU con el disco tomado.
Y queda escrito cómo validar CON PANTALLA sin humano delante: screendump por el monitor
de QEMU, con una métrica barata previa a mirar — contar colores distintos. Negro = 2;
pintando = 582, con el azul GNOME (2,60,136) al frente.
Lo que falta son detalles, ninguno impide el escritorio: lanzar upowerd desde gnome-start,
un tema de cursor, el setuid de colord y gnome-control-center.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>