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>
20 KiB
Runbook — COSMIC, el cuarto escritorio
Estado al 2026-08-03: LA SESIÓN COSMIC ARRANCA ENTERA. cosmic-session —el camino de producción,
no el andamio— levanta la cadena completa sobre el kernel de hammer y arje-zero como PID1:
cosmic-comp → cosmic-settings-daemon → cosmic-notifications → cosmic-panel
→ cosmic-osd → cosmic-bg
Los que todavía no existen (cosmic-app-library, cosmic-launcher, cosmic-workspaces,
cosmic-greeter) sólo dejan una línea de error! y la sesión sigue viva — que es exactamente lo
que la tabla de .expect de más abajo predijo, verificado en vez de supuesto.
Y el paso anterior: cosmic-comp arranca sobre el
kernel de hammer con arje-zero como PID1, toma el DRM, expone wayland-1 y presenta frames; y
cosmic-bg se conecta, entrega su buffer y el fondo aparece con el color exacto que se le
configuró — (0.13,0.29,0.53) → (33,74,135). O sea que la cadena cliente→compositor→KMS funciona
de punta a punta. Este documento se escribe mientras se hace, no después.
Que el color sea el que se pidió y no uno cualquiera es lo que hace de esto una medición y no una impresión: descarta que se esté viendo un buffer sin inicializar o el borrado del compositor.
El paso anterior —el compositor solo, gris (39,41,42) y el cursor, 134 colores— quedó en
cosmic-comp-qemu-2026-08-03.png, porque la
diferencia entre las dos imágenes es exactamente la pregunta que el modo bare separa.
COSMIC es el escritorio de System76, en Rust sobre smithay (compositor) e iced/libcosmic (clientes). Es el cuarto de hammer, tras mirada, KDE Plasma 6 y GNOME.
Por qué NO se parece a los dos anteriores
KDE y GNOME costaron lo que costaron por la misma razón: una torre de C con Qt o
glib/GObject/introspección debajo, donde cada capa se paga entera antes de ver un píxel. COSMIC no
tiene esa torre. La primera receta —cosmic-session— salió a la primera y sin una sola dep en C:
ELF estático, cero NEEDED.
Lo que sí hereda es lo que ya está pago:
| lo que COSMIC necesita | de dónde sale | costo |
|---|---|---|
| las deps en C de smithay (drm, gbm, seat, input, udev, xkb, pixman) | mirada-compositor, que ya es un compositor smithay de este lab |
cero — misma lista |
libdisplay-info + hwdata |
copiadas byte a byte de la cola GNOME | cero — mismo hash, artefacto ya sellado |
bash, dbus-run-session, hicolor-icon-theme, mesa-llvmpipe |
corpus | cero |
el andamiaje de arranque en QEMU (run-qemu-desktop.sh, base rootfs, console-getty, screendump) |
campañas KDE/GNOME | cero |
La deuda propia que sí trae, y que ninguna de las otras campañas tocó, es la del unwinder de la std de rustc cuando el enlace no es estático — la misma que ya está documentada para las recetas Cargo del corpus.
El pin es de la SUITE, no de cada paquete
COSMIC versiona todos sus repos en lockstep con el tag epoch-N. Comprobado repo por repo: los
doce componentes están en epoch-1.5.0.
No mezclar epochs entre piezas. Comparten protocolos propios (cosmic-protocols) y el formato de
configuración (cosmic-config); un desajuste ahí no da error de compilación, da un escritorio que
arranca y no se entiende consigo mismo — que es mucho más caro de diagnosticar.
La cola
recipes/incoming-cosmic/, aislada (otro agente comparte el repo; nunca git add -A).
Las deps se resuelven hermano → padre: desde incoming-cosmic/ se ven las recetas de
recipes/, pero no las de incoming-gnome/ ni incoming-kde/. Por eso libdisplay-info y
hwdata entran copiadas.
Copiar es gratis sólo si TODA la clausura transitiva resuelve a lo mismo. El ArtifactHash no
depende de la ruta de la receta, así que un fichero idéntico cuyas deps resuelven igual da el mismo
hash ⇒ cache hit sobre el artefacto que ya existe. Se cumplió con libdisplay-info, hwdata y
nasm, y se verificó con hammer hash antes de copiar (b3:260f0519, b3:bdc36cf9,
b3:33383070), no después. Dónde deja de cumplirse —y por qué importa— está en la sección de
cosmic-settings-daemon, más abajo.
Qué levanta la sesión, y por lo tanto el orden de ataque
cosmic-session/src/main.rs es la lista autoritativa —el mismo método que cerró la capa JS de GNOME:
leé el fichero donde el programa declara lo que arranca, no lo descubras de a uno:
cosmic-comp → cosmic-notifications · cosmic-panel · cosmic-app-library
→ cosmic-launcher · cosmic-workspaces · cosmic-osd · cosmic-bg · cosmic-idle
Más cosmic-settings-daemon y xdg-desktop-portal-cosmic fuera de esa lista.
| pieza | estado | hash |
|---|---|---|
cosmic-session |
✅ sellado | b3:81f02351 |
cosmic-icons |
✅ sellado (671 SVGs) | b3:94d06f77 |
dav1d 1.5.4 |
✅ sellado (dep arrastrada, ver gotcha 8) | b3:aab7a874 |
cosmic-comp |
✅ SELLADO — el gate | b3:b8abc52f |
cosmic-bg (dinámico, PINTA) |
✅ sellado | b3:b2cfc410 |
cosmic-idle (dinámico) |
✅ sellado | b3:e1806acc |
pulseaudio · pipewire (cadena del settings-daemon) |
✅ sellados | b3:e1ff4736 · b3:338d1d8c |
cosmic-panel |
✅ sellado | b3:ac5e48c5 |
cosmic-osd |
✅ sellado | b3:f15cb9c1 |
cosmic-settings-daemon |
✅ sellado | b3:0263288e |
cosmic-notifications · cosmic-applets |
🔨 en construcción | — |
cosmic-launcher · cosmic-app-library · cosmic-workspaces-epoch |
receta escrita, sin construir | — |
El mínimo viable de una sesión son cuatro: cosmic-comp + cosmic-settings-daemon +
cosmic-notifications + cosmic-panel (los tres .expect de la tabla de arriba). Tres están
sellados; falta notifications.
Y cosmic-applets es aparte pero no opcional para VER algo: sin sus veinte symlinks el panel arranca,
dice «Done spawning applets» y no dibuja.
El orden no es el de la lista de arranque. cosmic-bg va antes que el panel a propósito: habla
Wayland con smithay-client-toolkit y no toca libcosmic ni iced, mientras que panel, launcher,
osd y workspaces son todos clientes de iced. Con el fondo sellado, «pinta el fondo pero no el panel»
separa la capa de UI del transporte; sin él, las dos preguntas llegan juntas.
cosmic-settings-daemon: por qué queda al final y NO se resuelve copiando
Es el único con cadena propia. Su workspace incluye audio-server como dep de path no opcional,
que arrastra cosmic-pipewire → pipewire-sys → libpipewire-0.3 por pkg-config. Y pipewire hoy
vive sólo en las colas GNOME y KDE.
Acá el truco de copiar deja de ser gratis, y eso corrige lo que dice más arriba. La regla completa
es: copiar una receta entre colas es gratis sólo si toda su clausura transitiva resuelve a lo
mismo. Se cumplió con libdisplay-info, hwdata y nasm —sus deps están todas en el corpus—, y
no se cumple con pipewire: de sus catorce deps, cinco viven sólo en colas ajenas
(dbus-shared, alsa-lib, libsndfile, pulseaudio, zlib-shared) y —lo decisivo— glib
resolvería distinto. Desde incoming-gnome, glib es la sombra de esa cola (b3:f6ccdf98); desde
incoming-cosmic sería la del corpus (b3:93d2cad0). Son recetas distintas, no la misma en dos
lugares.
O sea que copiar produciría un segundo pipewire construido contra otra glib, arrastrando su cadena entera. Y eso no es sólo caro: es exactamente el cuadro que la campaña GNOME ya midió y evitó —dos glib distintas con 370 rutas solapadas y dos registros de GType en un proceso—.
Antes de tocar nada se midió con yupana radio (la puerta única, nunca grep): pipewire tiene
radio 1 en GNOME y 2 en KDE, pero glib del corpus tiene 21 dependientes transitivos y afecta la
imagen escritorio-gnome.
Y después se midió el costo real, que resultó mucho menor que la estimación. Copiar pipewire a
incoming-cosmic necesita cuatro recetas hermanas —alsa-lib, dbus-shared, libsndfile,
pulseaudio—, no las seis contadas a ojo, y tres de ellas dan hash idéntico y ya están selladas.
Sólo pulseaudio y pipewire hay que construir.
Eso refina la advertencia de arriba en el punto que importa: el peligro de las dos glib es mezclarlas en UNA IMAGEN, y la imagen COSMIC tiene una sola. Acá el costo es de builds, no de corrección. Lo que sigue vigente es no promover a ciegas entre colas.
Gotcha del copiado, que cuesta un minuto de confusión: [source] patches se resuelve relativo a la
receta, así que copiar el .toml sin su .patch da «no pude leer patch» y el hash ni siquiera se
calcula.
🚪 EL GATE PASADO: cosmic-comp compila (2026-08-03)
b3:b8abc52f, 51 min 39 s de cargo con CARGO_BUILD_JOBS=1 y lto = "fat". A la primera, salvo
el --bin del gotcha 1. Sin un solo parche a la fuente.
Lo que dice el enlace es la parte que vale, porque es la medición y no la impresión:
NEEDED: libdisplay-info.so.2 · libgbm.so.1 · libseat.so.1 · libudev.so.1
libinput.so.10 · libpixman-1.so.0 · libxkbcommon.so.0 · libc.so
Ocho, y las siete primeras son exactamente los backends de smithay que la receta declara. La
octava es musl. No hay libgcc_s — o sea que el compositor no arrastra la deuda del unwinder que
sí tienen varias recetas Cargo del corpus, porque acá el enlace es dinámico contra musl y no hay
+crt-static que forzar. Y no aparece nada que no se haya declarado: la clausura de build y la de
runtime coinciden, que es la propiedad que uno quiere y casi nunca se cumple gratis.
Que un compositor Wayland completo —DRM, GBM, EGL, libinput, seat, Vulkan, XWayland, un renderer multi-GPU y una capa de UI en iced— cierre con ocho librerías compartidas y ningún parche es el resultado más limpio de las cuatro campañas de escritorio.
🖥 EL PRIMER ARRANQUE: qué falló y qué no (2026-08-03)
Tres imágenes y tres causas distintas, todas medidas. Vale dejarlas porque ninguna estaba en el camino que uno anticipa, y las tres se manifestaron a varias capas de distancia de su causa.
1. cosmic-session no es opcional en el sentido que yo asumí — y cosmic-settings-daemon tampoco.
El compositor arrancó perfecto y le devolvió a la sesión su WAYLAND_DISPLAY=wayland-1; el gestor
entonces intentó lanzar cosmic-settings-daemon, no lo encontró, y panickeó:
INFO cosmic_session: got environmental variables from cosmic-comp: [("WAYLAND_DISPLAY", "wayland-1")]
ERROR panic: 'failed to start settings daemon: NotFound' src/main.rs:255
Es un .expect(), sin feature que lo apague. Y NO es el único — son TRES, y esto corrige lo que
esta misma sección afirmaba hace unas horas:
| línea | componente | qué pasa si falta |
|---|---|---|
main.rs:255 |
cosmic-settings-daemon |
panic |
main.rs:306 |
cosmic-notifications |
panic |
main.rs:325 |
cosmic-panel |
panic |
El resto (app-library, launcher, workspaces, osd, bg, idle) va por start_component(), que ante un
fallo sólo hace error!("failed to start …") y sigue (main.rs:558). O sea que el mínimo viable de
una sesión COSMIC son cuatro binarios: compositor + esos tres.
Cómo se equivocó el diagnóstico, 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; grepear la construcción que mata cuesta lo mismo y no
generaliza de más. Esto corrige lo que decía la sección de
cosmic-settings-daemon: no es que sin él se pierdan el brillo y el tema, es que no hay sesión.
Queda por eso el modo bare en cosmic-start —el camino que upstream ya soporta como
install-bare-session—: se lanza el compositor solo y los clientes a mano contra su WAYLAND_DISPLAY.
Separa «¿compone?» de «¿arranca la sesión?», que son dos preguntas y estaban viniendo juntas.
2. La pantalla negra era libz.so.1. El corpus trae zlib estática, que le alcanza a todo el
cierre de COSMIC. Pero kms_swrast_dri.so es un .so que mesa abre con dlopen y NEEDea
libz.so.1:
MESA-LOADER: failed to open kms_swrast: Error loading shared library libz.so.1
WARN cosmic_comp::backend::kms: Failed to add device /dev/dri/card0: Failed to initialize GBM device
Sin driver de software no hay GBM, y el compositor arranca igual y presenta una pantalla negra. La
causa está a tres capas del síntoma. Se arregla agregando zlib-shared como raíz del hidratador.
3. Los clientes NO pueden ser estáticos. cosmic-bg moría con «The wayland library could not be
loaded» teniendo libwayland-client.so.0 en el rootfs. wayland-client entra con la feature
dlopen: carga libwayland en runtime en vez de enlazarla, y un binario musl estático no tiene
dlopen funcional. No faltaba la librería: faltaba poder abrirla. Todos los clientes pasan a
link = "dynamic".
Lo que NO falló, y conviene decirlo: el compositor no necesitó un solo parche, ni una perilla de
KMS, ni forzar modeset legacy. Las cuatro perillas de render por software heredadas de KDE y GNOME
(GBM_ALWAYS_SOFTWARE, kms_swrast, LP_NUM_THREADS=1) alcanzaron. GNOME costó cinco muros para
llegar a este punto; COSMIC costó tres, y ninguno era del compositor.
Dos carreras con /run, la misma causa
dbus-daemon --system falló con «Failed to bind socket /run/dbus/system_bus_socket: No such file or
directory» aunque el directorio se creaba al principio del script, y dbus-run-session se quejó
por su lado de que XDG_RUNTIME_DIR no existía. Entre el arranque del getty y el uso pasan varios
segundos en los que arje-zero sigue montando, y un tmpfs sobre /run se lleva puesto lo que hubiera.
Crear justo antes de usar es la única forma de no depender de ese orden; comprobar al principio y
usar al final es exactamente cómo se cuela una carrera — la misma forma del bug de PolicyKit1 que se
cerró hoy en GNOME.
Gotchas medidos (no previstos)
-
cargo rustc --exige UN target. La fase compile del lab pasa flags tras el--, y cualquier paquete con binario + lib + ejemplos corta con «extra arguments torustccan only be passed to one target». Se arregla conflags = ["--bin", "<nombre>"]. Le pasa a cosmic-comp y a cosmic-bg (workspace conconfig/), y le va a pasar a casi todos: ponelo desde el principio. -
start-cosmices bash de verdad, no sh. Usamapfile,[[ ]]y expansión indirecta${!var}; con el busybox del rootfs no arranca.bashya está en el corpus, pero tiene que entrar en la imagen — y no lo pide ningún[deps], así que no lo va a traer nadie por accidente. Lo mismo condbus-run-session(dedbus), que es lo que el script exec-uta al final. -
rust-toolchain.tomlconchannel = "1.93"es inerte acá — es una perilla de rustup, y el sandbox tiene el rustc de Alpine (1.96) pelado. Conviene saberlo antes de «arreglarlo». -
cosmic.desktopexiste DOS veces (en cosmic-session y en cosmic-comp) con contenido distinto y en la misma ruta. El de cosmic-comp es el de la «bare session» y apunta a/usr/bin/cosmic-service, que vive en la rama de systemd que acá no va. Se instala sólo el de cosmic-session; si entraran los dos, quién gana depende del orden de hidratación y el que pierde es el bueno. -
libsystemdno es systemd. La featuredefault = ["systemd"]de cosmic-comp trae el cratelibsystemd, que es una reimplementación en Rust del protocolo, no un binding a la C. Ylogindhablaorg.freedesktop.login1, que acá lo sirvearje-logind-compat. No hay que apagar nada: el nombre de la feature es el del protocolo, no el del programa que lo contesta. -
Las features de smithay que asustan no cuestan deps de build:
backend_vulkan(crateash),backend_x11(x11rbcondl-libxcb) yxwaylandcargan todo por dlopen. Son deuda de RUNTIME —libxcb y vulkan-loader viven hoy sólo en la cola KDE—, no un muro de compilación. -
Todo cliente de la suite lleva las MISMAS CUATRO deps:
libxkbcommon,pkgconf,libudev-zero,libinput— incluso las piezas sin interfaz. Las tres primeras se descubrieron de a una, un build por dep, hasta que la línea de enlace las mostró juntas:-Wl,-Bstatic -ludev -linput -lxkbcommon …El error dice la que falta primero; el COMANDO dice todas. Leerlo entero la primera vez habría ahorrado tres builds. Los tres llegan por la cadena de libcosmic —enumeración de monitores y de dispositivos de entrada—, no por nada que el cliente haga a la vista.
Y el camino hasta ahí fue de razonamientos buenos y falsos, que es lo que vale dejar escrito:
cosmic-bgfue sin[deps]porque «habla el protocolo en Rust y no toca C». Reventó en elbuild.rsdesmithay-client-toolkitpidiendoxkbcommon.pc: sctk ENLAZA libxkbcommon; el que la dlopea eswinit, que es otra capa. Hablar Wayland en Rust dice cómo viaja el byte, no con qué se interpreta un teclado.cosmic-idlefue sin[deps]porque ese argumento ya no aplicaba —no usa sctk— y un demonio de inactividad no interpreta teclas. Reventó en el ENLACE con-lxkbcommon, arrastrado porcosmic-settings-config: de ahí sale la tabla de atajos y la 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.
La lección de método vale más que la dep: la dep no se deduce de la función del paquete. Está escrita en el
build.rsque panickea o en la línea de enlace, y las dos veces el error dijo exactamente quién y por qué. Ellink = "static"sobrevive porque el artefacto de libxkbcommon trae.aademás de.so. -
Una feature apagada en el paquete puede estar prendida por otro.
cosmic-bgdeclaraimagecondefault-features = falsey sin AVIF, y aun así compiladav1d-sys: las features de cargo se UNIFICAN en el grafo. Se resolvió trayendodav1d(b3:aab7a874) en vez de parchear el Cargo.toml de upstream — si el grafo dice que sabe decodificar AVIF, que lo sepa de verdad. Mismo modo de falla que un.pc Requiresarrastrando una dep que nadie declaró. -
cosmic-wallpapersno se puede empaquetar del tarball: está en git-lfs. El default de cosmic-bg apunta a/usr/share/backgrounds/cosmic/orion_nebula_nasa_heic0601a.jpg, y el archivo de GitHub de ese repo 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: se instalarían con el nombre bueno en la ruta buena. Mientras tantocosmic-startescribe la config de USUARIO con una fuenteColor, que cosmic-bg soporta de fábrica — y va en el lanzador, no en el artefacto, porque es política de la imagen de prueba y no algo que el paquete prometa.
Cómo se construye
cd ~/hammer
./target/release/hammer --store store hash recipes/incoming-cosmic/<pieza>.toml # dry-run, ~2ms
./target/release/hammer build recipes/incoming-cosmic/<pieza>.toml --store store
Lo que falta, en orden
- Cerrar
cosmic-comp. Es el gate: sin compositor no hay escritorio que probar. - El primer cliente de iced (
cosmic-panel), que es la pregunta abierta de verdad — libcosmic conwinit/wgpupuede pedir más de lo que pide cosmic-bg. cosmic-settings-daemon,cosmic-launcher,cosmic-app-library,cosmic-notifications,cosmic-osd,cosmic-workspaces-epoch,cosmic-idle.- Imagen QEMU con
bash+dbus-run-session+hicolor-icon-theme, y validar CON PANTALLA (DISP=gtk+screendump, contar colores distintos) — regla heredada de KDE y GNOME. xdg-desktop-portal-cosmicycosmic-greeter, que son de la etapa siguiente.

