1# Runbook — COSMIC, el cuarto escritorio
2
3Estado al 2026-08-03: **LA SESIÓN COSMIC ARRANCA ENTERA.** `cosmic-session` —el camino de producción,
4no el andamio— levanta la cadena completa sobre el kernel de hammer y arje-zero como PID1:
5
6```
7cosmic-comp → cosmic-settings-daemon → cosmic-notifications → cosmic-panel
8 → cosmic-osd → cosmic-bg
9```
10
11Los que todavía no existen (`cosmic-app-library`, `cosmic-launcher`, `cosmic-workspaces`,
12`cosmic-greeter`) sólo dejan una línea de `error!` y **la sesión sigue viva** — que es exactamente lo
13que la tabla de `.expect` de más abajo predijo, verificado en vez de supuesto.
14
15
16
17Y el paso anterior: `cosmic-comp` arranca sobre el
18kernel de hammer con arje-zero como PID1, toma el DRM, expone `wayland-1` y presenta frames; y
19`cosmic-bg` se conecta, entrega su buffer y el fondo aparece **con el color exacto que se le
20configuró** — `(0.13,0.29,0.53)` → `(33,74,135)`. O sea que la cadena cliente→compositor→KMS funciona
21de punta a punta. Este documento se escribe mientras se hace, no después.
22
23
24
25Que el color sea **el que se pidió** y no uno cualquiera es lo que hace de esto una medición y no una
26impresión: descarta que se esté viendo un buffer sin inicializar o el borrado del compositor.
27
28El paso anterior —el compositor solo, gris `(39,41,42)` y el cursor, 134 colores— quedó en
29[`cosmic-comp-qemu-2026-08-03.png`](../evidencia/cosmic-comp-qemu-2026-08-03.png), porque la
30diferencia entre las dos imágenes es exactamente la pregunta que el modo `bare` separa.
31
32COSMIC es el escritorio de System76, en Rust sobre **smithay** (compositor) e **iced/libcosmic**
33(clientes). Es el cuarto de hammer, tras [mirada](mirada-usb-nvidia.md),
34[KDE Plasma 6](kde-qemu-desktop.md) y [GNOME](gnome-qemu-desktop.md).
35
36## Por qué NO se parece a los dos anteriores
37
38KDE y GNOME costaron lo que costaron por la misma razón: una torre de C con Qt o
39glib/GObject/introspección debajo, donde cada capa se paga entera antes de ver un píxel. COSMIC no
40tiene esa torre. La primera receta —`cosmic-session`— salió **a la primera y sin una sola dep en C**:
41ELF estático, cero `NEEDED`.
42
43Lo que sí hereda es lo que ya está pago:
44
45| lo que COSMIC necesita | de dónde sale | costo |
46|---|---|---|
47| 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 |
48| `libdisplay-info` + `hwdata` | copiadas byte a byte de la cola GNOME | **cero** — mismo hash, artefacto ya sellado |
49| `bash`, `dbus-run-session`, `hicolor-icon-theme`, mesa-llvmpipe | corpus | cero |
50| el andamiaje de arranque en QEMU (`run-qemu-desktop.sh`, base rootfs, console-getty, screendump) | campañas KDE/GNOME | cero |
51
52La deuda propia que sí trae, y que ninguna de las otras campañas tocó, es la del **unwinder de la std
53de rustc** cuando el enlace no es estático — la misma que ya está documentada para las recetas Cargo
54del corpus.
55
56## El pin es de la SUITE, no de cada paquete
57
58COSMIC versiona **todos** sus repos en lockstep con el tag `epoch-N`. Comprobado repo por repo: los
59doce componentes están en `epoch-1.5.0`.
60
61**No mezclar epochs entre piezas.** Comparten protocolos propios (`cosmic-protocols`) y el formato de
62configuración (`cosmic-config`); un desajuste ahí no da error de compilación, da un escritorio que
63arranca y no se entiende consigo mismo — que es mucho más caro de diagnosticar.
64
65## La cola
66
67`recipes/incoming-cosmic/`, aislada (otro agente comparte el repo; nunca `git add -A`).
68
69Las deps se resuelven **hermano → padre**: desde `incoming-cosmic/` se ven las recetas de
70`recipes/`, pero **no** las de `incoming-gnome/` ni `incoming-kde/`. Por eso `libdisplay-info` y
71`hwdata` entran copiadas.
72
73**Copiar es gratis sólo si TODA la clausura transitiva resuelve a lo mismo.** El ArtifactHash no
74depende de la ruta de la receta, así que un fichero idéntico *cuyas deps resuelven igual* da el mismo
75hash ⇒ cache hit sobre el artefacto que ya existe. Se cumplió con `libdisplay-info`, `hwdata` y
76`nasm`, y se verificó con `hammer hash` **antes** de copiar (`b3:260f0519`, `b3:bdc36cf9`,
77`b3:33383070`), no después. Dónde deja de cumplirse —y por qué importa— está en la sección de
78`cosmic-settings-daemon`, más abajo.
79
80## Qué levanta la sesión, y por lo tanto el orden de ataque
81
82`cosmic-session/src/main.rs` es la lista autoritativa —el mismo método que cerró la capa JS de GNOME:
83*leé el fichero donde el programa declara lo que arranca, no lo descubras de a uno*:
84
85```
86cosmic-comp → cosmic-notifications · cosmic-panel · cosmic-app-library
87 → cosmic-launcher · cosmic-workspaces · cosmic-osd · cosmic-bg · cosmic-idle
88```
89
90Más `cosmic-settings-daemon` y `xdg-desktop-portal-cosmic` fuera de esa lista.
91
92| pieza | estado | hash |
93|---|---|---|
94| `cosmic-session` | ✅ sellado | `b3:81f02351` |
95| `cosmic-icons` | ✅ sellado (671 SVGs) | `b3:94d06f77` |
96| `dav1d` 1.5.4 | ✅ sellado (dep arrastrada, ver gotcha 8) | `b3:aab7a874` |
97| **`cosmic-comp`** | ✅ **SELLADO — el gate** (filtra `ScreenReader`, ver atajos) | `b3:e871eee4` |
98| `cosmic-bg` (dinámico, PINTA) | ✅ sellado | `b3:b2cfc410` |
99| `cosmic-idle` (dinámico) | ✅ sellado | `b3:e1806acc` |
100| `pulseaudio` · `pipewire` (cadena del settings-daemon) | ✅ sellados | `b3:e1ff4736` · `b3:338d1d8c` |
101| `cosmic-panel` | ✅ sellado | `b3:ac5e48c5` |
102| `cosmic-osd` | ✅ sellado | `b3:f15cb9c1` |
103| `cosmic-settings-daemon` | ✅ sellado (filtra `ScreenReader`, ver atajos) | `b3:642919a5` |
104| `cosmic-notifications` | ✅ sellado | `b3:3e2a5478` |
105| `cosmic-launcher` | ✅ sellado | `b3:c17b4956` |
106| `cosmic-app-library` | ✅ sellado | `b3:b2a163e7` |
107| `cosmic-workspaces-epoch` | ✅ sellado (necesitó `mesa`, ver gotcha 11) | `b3:a031249e` |
108| **`cosmic-applets`** (19 binarios en uno) | ✅ **sellado** (necesitó `dbus-shared`, ver gotcha 10) | `b3:3e53a325` |
109| `pop-launcher` 1.2.7 (otro repo, ver abajo) | ✅ sellado a la primera | `b3:64a5cbaa` |
110| `gmp` 6.3.0 · `mpfr` 4.2.2 (cadena de la calculadora) | ✅ sellados a la primera | `b3:d99a0a5d` · `b3:13982d20` |
111| `libqalculate` 5.12.0 (`qalc`) | ✅ sellado — la calculadora del lanzador ANDA (ver abajo) | `b3:edb8d14d` |
112| **`cosmic-term`** 1.5.0 — la 1ª APLICACIÓN | ✅ **sellada a la primera** (arrastra `cosmic-files` y `wgpu`) | `b3:d0ea1c1e` |
113| `cosmic-files` 1.5.0 — la 2ª aplicación | ✅ sellada, **sin `gvfs` ni `wgpu`** (ver abajo) | `b3:48d2b072` |
114| `cosmic-settings` 1.5.0 — la 3ª aplicación | ✅ sellada (necesitó el parche de la doble barra) | `b3:716dac53` |
115
116**La cola está COMPLETA: 33 recetas, `escritorio-cosmic` 71/71.**
117
118**El mínimo viable de una sesión son cuatro**: `cosmic-comp` + `cosmic-settings-daemon` +
119`cosmic-notifications` + `cosmic-panel` (los tres `.expect` de la tabla de arriba).
120
121**El orden no es el de la lista de arranque.** `cosmic-bg` va antes que el panel a propósito: habla
122Wayland con `smithay-client-toolkit` y **no toca libcosmic ni iced**, mientras que panel, launcher,
123osd y workspaces son todos clientes de iced. Con el fondo sellado, «pinta el fondo pero no el panel»
124separa la capa de UI del transporte; sin él, las dos preguntas llegan juntas.
125
126### `cosmic-settings-daemon`: por qué queda al final y NO se resuelve copiando
127
128Es el único con cadena propia. Su workspace incluye `audio-server` como dep de path **no opcional**,
129que arrastra `cosmic-pipewire` → `pipewire-sys` → `libpipewire-0.3` por pkg-config. Y `pipewire` hoy
130vive sólo en las colas GNOME y KDE.
131
132**Acá el truco de copiar deja de ser gratis, y eso corrige lo que dice más arriba.** La regla completa
133es: *copiar una receta entre colas es gratis sólo si **toda su clausura transitiva** resuelve a lo
134mismo.* Se cumplió con `libdisplay-info`, `hwdata` y `nasm` —sus deps están todas en el corpus—, y
135**no** se cumple con pipewire: de sus catorce deps, cinco viven sólo en colas ajenas
136(`dbus-shared`, `alsa-lib`, `libsndfile`, `pulseaudio`, `zlib-shared`) y —lo decisivo— **`glib`
137resolvería distinto**. Desde `incoming-gnome`, `glib` es la sombra de esa cola (`b3:f6ccdf98`); desde
138`incoming-cosmic` sería la del corpus (`b3:93d2cad0`). Son recetas distintas, no la misma en dos
139lugares.
140
141O sea que copiar produciría un **segundo pipewire** construido contra otra glib, arrastrando su cadena
142entera. Y eso no es sólo caro: es exactamente el cuadro que la campaña GNOME ya midió y evitó —dos
143glib distintas con 370 rutas solapadas y dos registros de GType en un proceso—.
144
145Antes de tocar nada se midió con `yupana radio` (la puerta única, nunca `grep`): `pipewire` tiene
146radio 1 en GNOME y 2 en KDE, pero `glib` del corpus tiene **21 dependientes transitivos y afecta la
147imagen escritorio-gnome**.
148
149**Y después se midió el costo real, que resultó mucho menor que la estimación.** Copiar pipewire a
150`incoming-cosmic` necesita **cuatro** recetas hermanas —`alsa-lib`, `dbus-shared`, `libsndfile`,
151`pulseaudio`—, no las seis contadas a ojo, y **tres de ellas dan hash idéntico y ya están selladas**.
152Sólo `pulseaudio` y `pipewire` hay que construir.
153
154Eso refina la advertencia de arriba en el punto que importa: **el peligro de las dos glib es
155mezclarlas en UNA IMAGEN**, y la imagen COSMIC tiene una sola. Acá el costo es de builds, no de
156corrección. Lo que sigue vigente es no promover a ciegas entre colas.
157
158Gotcha del copiado, que cuesta un minuto de confusión: `[source] patches` se resuelve **relativo a la
159receta**, así que copiar el `.toml` sin su `.patch` da «no pude leer patch» y el hash ni siquiera se
160calcula.
161
162## 🚪 EL GATE PASADO: cosmic-comp compila (2026-08-03)
163
164`b3:b8abc52f`, 51 min 39 s de cargo con `CARGO_BUILD_JOBS=1` y `lto = "fat"`. **A la primera**, salvo
165el `--bin` del gotcha 1. Sin un solo parche a la fuente.
166
167Lo que dice el enlace es la parte que vale, porque es la medición y no la impresión:
168
169```
170NEEDED: libdisplay-info.so.2 · libgbm.so.1 · libseat.so.1 · libudev.so.1
171 libinput.so.10 · libpixman-1.so.0 · libxkbcommon.so.0 · libc.so
172```
173
174Ocho, y las siete primeras son **exactamente** los backends de smithay que la receta declara. La
175octava es musl. **No hay `libgcc_s`** — o sea que el compositor no arrastra la deuda del unwinder que
176sí tienen varias recetas Cargo del corpus, porque acá el enlace es dinámico contra musl y no hay
177`+crt-static` que forzar. Y **no aparece nada que no se haya declarado**: la clausura de build y la de
178runtime coinciden, que es la propiedad que uno quiere y casi nunca se cumple gratis.
179
180Que un compositor Wayland completo —DRM, GBM, EGL, libinput, seat, Vulkan, XWayland, un renderer
181multi-GPU y una capa de UI en iced— cierre con ocho librerías compartidas y ningún parche es el
182resultado más limpio de las cuatro campañas de escritorio.
183
184## 🖥 EL PRIMER ARRANQUE: qué falló y qué no (2026-08-03)
185
186Tres imágenes y tres causas distintas, todas medidas. Vale dejarlas porque **ninguna estaba en el
187camino que uno anticipa**, y las tres se manifestaron a varias capas de distancia de su causa.
188
189**1. `cosmic-session` no es opcional en el sentido que yo asumí — y `cosmic-settings-daemon` tampoco.**
190El compositor arrancó perfecto y le devolvió a la sesión su `WAYLAND_DISPLAY=wayland-1`; el gestor
191entonces intentó lanzar `cosmic-settings-daemon`, no lo encontró, y **panickeó**:
192
193```
194INFO cosmic_session: got environmental variables from cosmic-comp: [("WAYLAND_DISPLAY", "wayland-1")]
195ERROR panic: 'failed to start settings daemon: NotFound' src/main.rs:255
196```
197
198Es un `.expect()`, sin feature que lo apague. **Y NO es el único — son TRES**, y esto corrige lo que
199esta misma sección afirmaba hace unas horas:
200
201| línea | componente | qué pasa si falta |
202|---|---|---|
203| `main.rs:255` | `cosmic-settings-daemon` | panic |
204| `main.rs:306` | `cosmic-notifications` | panic |
205| `main.rs:325` | `cosmic-panel` | panic |
206
207El resto (app-library, launcher, workspaces, osd, bg, idle) va por `start_component()`, que ante un
208fallo sólo hace `error!("failed to start …")` y sigue (`main.rs:558`). O sea que **el mínimo viable de
209una sesión COSMIC son cuatro binarios**: compositor + esos tres.
210
211**Cómo se equivocó el diagnóstico, que es la parte reusable**: leí `start_component()`, vi que sólo
212logueaba, y generalicé a «todos menos el settings-daemon». Pero notifications y panel **no pasan por
213esa función** — tienen su propio `.expect()` unas líneas antes. Buscar la función que lanza no es lo
214mismo que buscar los `\.expect(` del fichero; **grepear la construcción que mata cuesta lo mismo y no
215generaliza de más**. **Esto corrige lo que decía la sección de
216cosmic-settings-daemon**: no es que sin él se pierdan el brillo y el tema, es que **no hay sesión**.
217Queda por eso el modo `bare` en `cosmic-start` —el camino que upstream ya soporta como
218`install-bare-session`—: se lanza el compositor solo y los clientes a mano contra su WAYLAND_DISPLAY.
219Separa «¿compone?» de «¿arranca la sesión?», que son dos preguntas y estaban viniendo juntas.
220
221**2. La pantalla negra era `libz.so.1`.** El corpus trae `zlib` estática, que le alcanza a todo el
222cierre de COSMIC. Pero `kms_swrast_dri.so` es un `.so` que mesa abre con `dlopen` y NEEDea
223`libz.so.1`:
224
225```
226MESA-LOADER: failed to open kms_swrast: Error loading shared library libz.so.1
227WARN cosmic_comp::backend::kms: Failed to add device /dev/dri/card0: Failed to initialize GBM device
228```
229
230Sin driver de software no hay GBM, y **el compositor arranca igual y presenta una pantalla negra**. La
231causa está a tres capas del síntoma. Se arregla agregando `zlib-shared` como raíz del hidratador.
232
233**3. Los clientes NO pueden ser estáticos.** `cosmic-bg` moría con «The wayland library could not be
234loaded» **teniendo `libwayland-client.so.0` en el rootfs**. `wayland-client` entra con la feature
235`dlopen`: carga libwayland en runtime en vez de enlazarla, y **un binario musl estático no tiene
236dlopen funcional**. No faltaba la librería: faltaba poder abrirla. Todos los clientes pasan a
237`link = "dynamic"`.
238
239**Lo que NO falló, y conviene decirlo:** el compositor no necesitó un solo parche, ni una perilla de
240KMS, ni forzar modeset legacy. Las cuatro perillas de render por software heredadas de KDE y GNOME
241(`GBM_ALWAYS_SOFTWARE`, `kms_swrast`, `LP_NUM_THREADS=1`) alcanzaron. GNOME costó cinco muros para
242llegar a este punto; COSMIC costó tres, y ninguno era del compositor.
243
244### Dos carreras con /run, la misma causa
245
246`dbus-daemon --system` falló con «Failed to bind socket /run/dbus/system_bus_socket: No such file or
247directory» **aunque el directorio se creaba al principio del script**, y dbus-run-session se quejó
248por su lado de que `XDG_RUNTIME_DIR` no existía. Entre el arranque del getty y el uso pasan varios
249segundos en los que arje-zero sigue montando, y un tmpfs sobre `/run` se lleva puesto lo que hubiera.
250**Crear justo antes de usar** es la única forma de no depender de ese orden; comprobar al principio y
251usar al final es exactamente cómo se cuela una carrera — la misma forma del bug de PolicyKit1 que se
252cerró hoy en GNOME.
253
254## Gotchas medidos (no previstos)
255
2561. **`cargo rustc --` exige UN target.** La fase compile del lab pasa flags tras el `--`, y cualquier
257 paquete con binario + lib + ejemplos corta con «extra arguments to `rustc` can only be passed to
258 one target». Se arregla con `flags = ["--bin", "<nombre>"]`. Le pasa a cosmic-comp y a cosmic-bg
259 (workspace con `config/`), y le va a pasar a casi todos: **ponelo desde el principio.**
2602. **`start-cosmic` es bash de verdad, no sh.** Usa `mapfile`, `[[ ]]` y expansión indirecta
261 `${!var}`; con el busybox del rootfs no arranca. `bash` ya está en el corpus, pero **tiene que
262 entrar en la imagen** — y no lo pide ningún `[deps]`, así que no lo va a traer nadie por accidente.
263 Lo mismo con `dbus-run-session` (de `dbus`), que es lo que el script exec-uta al final.
2643. **`rust-toolchain.toml` con `channel = "1.93"` es inerte acá** — es una perilla de rustup, y el
265 sandbox tiene el rustc de Alpine (1.96) pelado. Conviene saberlo antes de «arreglarlo».
2664. **`cosmic.desktop` existe DOS veces** (en cosmic-session y en cosmic-comp) con contenido distinto
267 y en la misma ruta. El de cosmic-comp es el de la «bare session» y apunta a `/usr/bin/cosmic-service`,
268 que vive en la rama de systemd que acá no va. Se instala **sólo** el de cosmic-session; si entraran
269 los dos, quién gana depende del orden de hidratación y el que pierde es el bueno.
2705. **`libsystemd` no es systemd.** La feature `default = ["systemd"]` de cosmic-comp trae el crate
271 `libsystemd`, que es una **reimplementación en Rust** del protocolo, no un binding a la C. Y
272 `logind` habla `org.freedesktop.login1`, que acá lo sirve `arje-logind-compat`. No hay que apagar
273 nada: el nombre de la feature es el del protocolo, no el del programa que lo contesta.
2746. **Las features de smithay que asustan no cuestan deps de build**: `backend_vulkan` (crate `ash`),
275 `backend_x11` (`x11rb` con `dl-libxcb`) y `xwayland` cargan todo por **dlopen**. Son deuda de
276 RUNTIME —libxcb y vulkan-loader viven hoy sólo en la cola KDE—, no un muro de compilación.
2777. **Todo cliente de la suite lleva las MISMAS CUATRO deps: `libxkbcommon`, `pkgconf`,
278 `libudev-zero`, `libinput`** — incluso las piezas sin interfaz. Las tres primeras se descubrieron
279 de a una, un build por dep, hasta que la línea de enlace las mostró juntas:
280
281 ```
282 -Wl,-Bstatic -ludev -linput -lxkbcommon …
283 ```
284
285 **El error dice la que falta primero; el COMANDO dice todas.** Leerlo entero la primera vez habría
286 ahorrado tres builds. Los tres llegan por la cadena de libcosmic —enumeración de monitores y de
287 dispositivos de entrada—, no por nada que el cliente haga a la vista.
288
289 Y el camino hasta ahí fue de razonamientos buenos y falsos, que es lo que vale dejar escrito:
290 - `cosmic-bg` fue sin `[deps]` porque «habla el protocolo en Rust y no toca C». Reventó en el
291 `build.rs` de `smithay-client-toolkit` pidiendo `xkbcommon.pc`: **sctk ENLAZA libxkbcommon**; el
292 que la dlopea es `winit`, que es otra capa. Hablar Wayland en Rust dice cómo viaja el byte, no
293 con qué se interpreta un teclado.
294 - `cosmic-idle` fue sin `[deps]` porque *ese* argumento ya no aplicaba —no usa sctk— y un demonio
295 de inactividad no interpreta teclas. Reventó en el ENLACE con `-lxkbcommon`, arrastrado por
296 **`cosmic-settings-config`**: de ahí sale la tabla de atajos y la usa **todo** componente de la
297 suite. La dep no viene de lo que el paquete hace, sino de la librería de configuración común.
298
299 La lección de método vale más que la dep: **la dep no se deduce de la función del paquete.** Está
300 escrita en el `build.rs` que panickea o en la línea de enlace, y las dos veces el error dijo
301 exactamente quién y por qué. El `link = "static"` sobrevive porque el artefacto de libxkbcommon
302 trae `.a` además de `.so`.
3038. **Una feature apagada en el paquete puede estar prendida por otro.** `cosmic-bg` declara `image`
304 con `default-features = false` y sin AVIF, y aun así compila `dav1d-sys`: **las features de cargo
305 se UNIFICAN en el grafo**. Se resolvió trayendo `dav1d` (b3:aab7a874) en vez de parchear el
306 Cargo.toml de upstream — si el grafo dice que sabe decodificar AVIF, que lo sepa de verdad.
307 Mismo modo de falla que un `.pc Requires` arrastrando una dep que nadie declaró.
308
3099. **`cosmic-wallpapers` no se puede empaquetar del tarball: está en git-lfs.** El default de
310 cosmic-bg apunta a `/usr/share/backgrounds/cosmic/orion_nebula_nasa_heic0601a.jpg`, y el archivo
311 de GitHub de ese repo pesa **20 KB** — son punteros LFS, no imágenes. Una receta escrita del modo
312 normal produciría un paquete de ficheros de texto que además *parecería* correcto: se instalarían
313 con el nombre bueno en la ruta buena. Mientras tanto `cosmic-start` escribe la config de USUARIO
314 con una fuente `Color`, que cosmic-bg soporta de fábrica — y va en el lanzador, no en el artefacto,
315 porque es política de la imagen de prueba y no algo que el paquete prometa.
316
31710. **`build.rs` contento ≠ linker contento: `cosmic-applets` necesita la libdbus COMPARTIDA.**
318 Los applets de red, bluetooth y status-area hablan D-Bus por `libdbus-sys` (FFI a la C, no zbus
319 como el resto de la suite). Declarar `dbus` a secas alcanza para que su `build.rs` pase —le basta
320 el `.pc`, que el paquete del daemon trae— y el fallo aparece **una hora después, en el enlace
321 final del multiplexor**: `unable to find dynamic system library 'dbus-1'`. El corpus sólo
322 empaqueta `libdbus-1.a`; el `.so` vive en `dbus-shared`. Son dos preguntas distintas y las
323 contestan dos herramientas distintas: pkg-config dice si la dep *existe*, el linker si hay algo
324 que *enlazar*. Era la única receta de la cola que pedía la estática.
325
32611. **`cosmic-workspaces` necesita `mesa`, y la dep sale de lo que el programa MUESTRA.** La vista de
327 espacios de trabajo dibuja miniaturas vivas de las ventanas; el compositor se las pasa como
328 DMA-BUF por `zcosmic-toplevel-info` y el cliente importa el fd con GBM ⇒ `-lgbm`. Es el único
329 cliente de la suite que toca el pipeline gráfico además del compositor, así que el juego fijo de
330 cuatro (`libxkbcommon`, `pkgconf`, `libudev-zero`, `libinput`) no alcanza. Corolario del gotcha
331 de siempre: la dep no se deduce de la familia del paquete, se lee de lo que hace.
332
33312. **DOS GPUs en QEMU = el panel dibuja en la pantalla que no mirás.** `-device virtio-gpu-pci` no
334 reemplaza a la VGA por defecto de QEMU: la SUMA. El invitado ve `card0` y `card1`, cosmic-comp
335 crea dos outputs (`Virtual-1` y `Unknown-1`), `cosmic-bg` pinta el fondo en ambos y el panel se
336 ancla en uno solo. El `screendump` captura el otro ⇒ **fondo perfecto, panel invisible, y ni un
337 solo error en el log**. Con `-vga none` el escritorio aparece entero a la primera.
338 **Y esto corrige un diagnóstico anterior de este mismo runbook**: cuando el panel selló y no
339 dibujó, lo atribuí a que le faltaban los applets. Los applets hacían falta —sin ellos el panel
340 está vacío— pero **no eran la causa de la pantalla lisa**; la causa era la segunda GPU, que ya
341 estaba ahí. Un síntoma puede tener dos causas suficientes y arreglar la primera no prueba nada:
342 la prueba es la pantalla, no el razonamiento.
343
34413. **`mkdir: not found` en `cosmic-start`, y la sesión arrancaba igual.** El getty entrega un PATH
345 sin `/usr/bin`, y en la imagen fundida `mkdir` sólo vive ahí (`/usr/bin/mkdir → coreutils`; la
346 base metal no tendió el symlink `/bin/mkdir → busybox` que sí tienen chmod, mount, sed y tee).
347 Los cuatro `mkdir -p` de la preparación fallaban y **no se notaba**, porque los directorios
348 venían horneados en la imagen. Es la forma más traicionera de bug: gratis hoy, fatal el día que
349 alguien cambie cómo se arma la imagen. Fix: exportar `PATH` como primera línea del script.
350
351## Cómo se construye
352
353```sh
354cd ~/hammer
355./target/release/hammer --store store hash recipes/incoming-cosmic/<pieza>.toml # dry-run, ~2ms
356./target/release/hammer build recipes/incoming-cosmic/<pieza>.toml --store store
357```
358
359## 🏔 EL ESCRITORIO ENTERO, DIBUJADO (2026-08-04)
360
361`docs/evidencia/cosmic-panel-applets-qemu-2026-08-04.png` — 1280×800, **895 colores distintos**:
362
363- **panel superior** (banda `(27,27,27)`, 32 px): botones *Workspaces* y *Applications* a la
364 izquierda, reloj al centro, y a la derecha los applets de accesibilidad, audio, red, batería y
365 encendido;
366- **dock inferior** centrado y redondeado, con lanzador, workspaces, biblioteca de aplicaciones y
367 las entradas `.desktop` del rootfs;
368- fondo `(33,74,135)` = exactamente el `Color(Single((0.13,0.29,0.53)))` que escribe `cosmic-start`.
369
37048 procesos `cosmic-*` estables a los 30 s, sobre el kernel de hammer con arje-zero como PID1.
371Se reproduce así (headless y automatizable; **el `-vga none` no es opcional**, ver gotcha 12):
372
373```sh
374COSMIC_MODE=session bash scripts/cosmic/qemu-desktop-image.sh
375qemu-system-x86_64 -machine q35,accel=kvm -cpu host -smp 8 -m 8192 \ # 8 vCPU: ver el deadline del panel
376 -drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd \
377 -drive if=pflash,format=raw,file=/tmp/OVMF_VARS.fd \
378 -drive file=work/hammer-cosmic-qemu.img,format=raw,if=virtio \
379 -vga none -device virtio-gpu-pci,xres=1280,yres=800 \
380 -device qemu-xhci,id=xhci -device usb-kbd,bus=xhci.0 -device usb-tablet,bus=xhci.0 \
381 -display none -serial file:/tmp/cosmic.serial -qmp unix:/tmp/qmp.sock,server,nowait
382# y después, por QMP: {"execute":"screendump","arguments":{"filename":"/tmp/shot.ppm"}}
383```
384
385El veredicto es **contar colores distintos del PPM**, no leer el log: con dos GPUs el log salía
386impecable y la pantalla estaba lisa.
387
388## 🖱 LA ENTRADA: se usa, no sólo se ve (2026-08-04)
389
390**Hay que usar USB HID, no virtio-input.** El kernel de hammer no trae `VIRTIO_INPUT` (mirá el
391`-e …` de `recipes/linux-generic.toml`), así que `-device virtio-keyboard` no produce ni un
392`/dev/input/event*`. Sí trae `USB_XHCI_HCD` + `USB_HID` + `INPUT_EVDEV`, y además es lo que se va a
393usar en metal, así que la prueba es más fiel:
394
395```
396-device qemu-xhci,id=xhci -device usb-kbd,bus=xhci.0 -device usb-tablet,bus=xhci.0
397```
398
399`usb-tablet` y no `usb-mouse`: reporta coordenadas ABSOLUTAS, y QMP sólo sabe mandar eventos `abs`
400(el eje va de 0 a 32767 sobre el ancho/alto de la pantalla, no en píxeles).
401
402**Se maneja entero por QMP**, sin ver la pantalla y sin humano:
403
404```jsonc
405// mover el puntero a (155,16) — el botón «Applications»
406{"execute":"input-send-event","arguments":{"events":[
407 {"type":"abs","data":{"axis":"x","value":3968}},
408 {"type":"abs","data":{"axis":"y","value":655}}]}}
409// click
410{"execute":"input-send-event","arguments":{"events":[{"type":"btn","data":{"down":true,"button":"left"}}]}}
411{"execute":"input-send-event","arguments":{"events":[{"type":"btn","data":{"down":false,"button":"left"}}]}}
412// una tecla
413{"execute":"input-send-event","arguments":{"events":[
414 {"type":"key","data":{"down":true,"key":{"type":"qcode","data":"c"}}},
415 {"type":"key","data":{"down":false,"key":{"type":"qcode","data":"c"}}}]}}
416```
417
418**El veredicto también es de píxeles, y conviene medirlo por REGIÓN.** El click en *Applications*
419llevó el color dominante de `(33,74,135)` (fondo, 91 %) a `(27,27,27)` (84 %): la biblioteca de
420aplicaciones se abrió a pantalla casi completa. Y para el teclado, contar colores del rectángulo del
421buscador (440..840 × 80..112) bastó: 155 → 118 colores al reemplazarse el placeholder por lo tecleado.
422Evidencia: `cosmic-app-library-click-qemu-2026-08-04.png` y `cosmic-teclado-busqueda-qemu-2026-08-04.png`.
423
424**La biblioteca sale vacía y está BIEN.** Los 20 `.desktop` de la imagen son `NoDisplay=true` —son
425applets y componentes de sesión, no aplicaciones—, así que no hay nada que listar. Que la búsqueda no
426devuelva resultados no es un fallo del escritorio: es que la distro todavía no tiene ni una sola
427aplicación gráfica. Se sabrá arreglado cuando entre `cosmic-files` o un terminal.
428
429### 🔑 Los atajos: dos ficheros envenenados por la misma línea
430
431`Super+A` abre la biblioteca y `Super+W` la vista de espacios de trabajo
432(`cosmic-super-w-workspaces-qemu-2026-08-04.png`, con **miniatura viva** del escritorio dentro — o
433sea que el camino DMA-BUF→GBM del gotcha 11 anda de verdad). Para llegar ahí hubo que sanear DOS
434ficheros de datos, no uno:
435
436| fichero | lo instala | qué liga |
437|---|---|---|
438| `…Shortcuts/v1/defaults` | `cosmic-comp` | tecla → acción |
439| `…Shortcuts/v1/system_actions` | `cosmic-settings-daemon` | acción → comando |
440
441Los dos traen una entrada `ScreenReader`, una variante que `cosmic-settings-config` —el crate que los
442parsea, pineado por cargo en la rev `8c54bbbc`— todavía no tiene en su enum. **RON no ignora lo que
443no conoce: falla la deserialización del MAPA COMPLETO.** Con `defaults` roto no hay ligaduras; con
444`system_actions` roto hay ligadura pero ningún comando que ejecutar. Hacen falta los dos `grep -v`.
445
446**Y acá me equivoqué de fichero teniendo el dato exacto delante.** El error decía
447`3:5-3:17: Unexpected variant named 'ScreenReader'` — con línea y columna. Busqué el *token* en vez
448de preguntarme *qué fichero lo tiene en esa posición*, di con `keybindings.ron` (que también lo
449traía, en la línea 94), lo arreglé, reconstruí `cosmic-comp` **cuarenta minutos** y el error salió
450igualito. Era otro fichero, de otro paquete. *Cuando el error trae coordenadas, la primera pregunta
451es de qué fichero son, no dónde más aparece esa palabra.*
452
453**Es el riesgo del pin lockstep asomando desde adentro de upstream**: el dato y el parser del *mismo*
454epoch no coinciden.
455
456**`Super` abre el lanzador** — pero hizo falta un paquete más, y de otro repo: ver abajo.
457
458### 🔎 `pop-launcher`: el lanzador es DOS programas
459
460`Super` no abría nada y el único rastro era una línea que no menciona ninguna tecla:
461`ERROR pop-launcher failed to start: No such file or directory`. **`cosmic-launcher` es sólo la
462interfaz**: dibuja la lista y manda lo tecleado por stdin a un proceso hijo, `pop-launcher`, que es
463quien busca. Sin ese binario la ventana no tiene qué mostrar, así que no se muestra — el mismo modo
464de falla que el panel sin applets: *lo que falta es un HIJO, no una librería, y el log del padre se
465lee sano.*
466
467Vive en **otro repo** (`pop-os/launcher`), no versiona por `epoch-N` y no entra en el pin lockstep.
468**La versión la fija el `Cargo.lock` de cosmic-launcher** —rev `a332a3a7` de la 1.2.7—, no el último
469tag del repo: el lock es lo que garantiza que hablen el mismo protocolo. El tarball se pide por
470commit (`/archive/<sha>.tar.gz`), porque no hay tag que nombre esa rev. Selló a la primera
471(`b3:64a5cbaa`) con **un solo `NEEDED`: `libc.so`**.
472
473Es otro **multiplexor por symlink**, como cosmic-applets: un binario, y cada plugin es un enlace con
474su nombre dentro de su propio directorio junto al `plugin.ron` que lo describe. El servicio enumera
475`plugins/*/`; si está el `.ron` y falta el enlace —o al revés— el plugin no existe para él, **sin
476error**. Ojo con el `sed 's/_/-/'` de upstream: cambia sólo el primer guión bajo, así que el
477directorio es `desktop_entries` y el ejecutable de adentro `desktop-entries`. Son dos nombres
478distintos a propósito.
479
480Verificado de punta a punta (`cosmic-super-lanzador-`, `cosmic-pop-launcher-plugins-`,
481`cosmic-pop-launcher-calc-qemu-2026-08-04.png`): `Super` abre el lanzador, `?` lista los seis plugins
482con su sintaxis —o sea que la enumeración y el IPC JSON-sobre-stdio andan— y `=2+2` **lanza el
483plugin, que contesta «qalc command is not installed»**. Esa respuesta es la mejor prueba que había
484disponible: el proceso hijo corrió, evaluó la consulta y devolvió una fila. Lo que falta ahora es
485`libqalculate` (el binario `qalc`), que es un paquete, no un problema.
486
487### 🧮 `libqalculate`: la calculadora del lanzador, y un `stdin` que era `stdout`
488
489**`=123*456` → `56088` en el lanzador** (`cosmic-calculadora-qemu-2026-08-04.png`). La cadena entera
490desde una tecla está en el catálogo: `Super` → cosmic-launcher → pop-launcher → plugin `calc` →
491`qalc` → **mpfr → gmp**.
492
493Pero el camino hasta ahí tuvo dos muros que vale la pena dejar escritos, porque ninguno se parecía a
494lo que era.
495
496**1) `stdin` apuntaba al descriptor 1.** El plugin no le pasa la cuenta por argv: arranca `qalc` SIN
497expresión, con stdin en tubería, le escribe la expresión y cierra. En ese modo el binario **no leía
498nada** — ni error ni resultado, la fila del lanzador salía en blanco. El síntoma acusaba a readline,
499al toolchain y a la versión, y no era ninguno:
500
501```
502DIAG-main isatty=0 feof=0 ferror=0 fd=1 ← fileno(stdin) == 1
503nm -S qalc | grep -E ' (stdin|stdout|stderr)$'
504 1847148 8 B stderr
505 1847148 8 B stdin ← las TRES en la misma dirección
506 1847148 8 B stdout
507```
508
509La causa: **el binario salía DINÁMICO aunque la receta dice `link = "static"`, porque libtool se come
510el `-static` que inyecta el harness** ([[link-static-mentira-libtool]] otra vez, cuarto caso). Y en
511un musl dinámico las tres variables de stdio se resolvieron por *copy relocation* a **una sola ranura
512de BSS compartida**: la última que escribe el loader gana, así que el `stdin` del programa era el
513`stdout`. De ahí la asimetría que parecía absurda: `qalc -f /dev/stdin` funcionaba —abre el
514descriptor por RUTA, sin tocar el `FILE*` roto— y `qalc` leyendo `stdin` no.
515
516El fix es el par que ya usa `libxml2`: `make LDFLAGS="-all-static -no-pie"`. Con él el binario queda
517estático de verdad y los tres símbolos vuelven a tener direcciones distintas, en `.rodata`:
518
519```
520 11e71c8 8 R stderr
521 11e71d0 8 R stdin
522 11e71d8 8 R stdout
523```
524
525**La regla, y es de las caras: `link = "static"` en la receta no garantiza un binario estático.**
526Cuando algo se comporta como si su `stdin`/`stdout` estuvieran cruzados, `readelf -d` y
527`nm -S | grep ' std'` contestan en dos segundos lo que el log no dice nunca.
528
529Barrido de la cola entera con ese criterio, después del fix: de las 7 recetas que declaran
530`link = "static"` (`cosmic-session`, `gmp`, `mpfr`, `nasm`, `libqalculate` y las dos que en realidad
531son `dynamic`), **ninguna tiene `NEEDED` indebido y ninguna tiene los símbolos de stdio aliasados**.
532Era un caso, no un patrón — pero el patrón que lo causa (libtool comiéndose el `-static`) ya tiene
533cuatro víctimas anteriores en el corpus.
534
535**2) La pregunta de primer arranque se come la cuenta, SIEMPRE.** Antes de leer nada, `qalc` pregunta
536si activar `autocalc`. El plugin le manda la expresión y cierra: la expresión se consume como
537respuesta, no es un sí/no válido, y qalc **no llega a guardar config** ⇒ la próxima vez vuelve a
538preguntar. No es «falla la primera vez»: no anda nunca. Se siembra
539`~/.config/qalculate/qalc.cfg` con `calculate_as_you_type=0` desde `cosmic-start` — política de la
540imagen, como el color de fondo; una distro de verdad lo pondría en `/etc/skel`.
541
542**Gotchas de empaquetado de la cadena:**
543- **`gmp` va con `--disable-assembly`.** Elige rutinas en ensamblador mirando la CPU de quien
544 compila, o sea hornearía la ISA del laptop en el artefacto y el hash dejaría de significar lo mismo
545 en la granja. Es una calculadora de escritorio: la implementación en C portable sobra.
546- **El configure de libqalculate no encuentra readline** porque prueba `-lreadline` junto a
547 `-lncurses`, `-lcurses`, `-ltermcap` y `-ltinfo`, y el corpus **sólo empaqueta `libncursesw.a`**.
548 Que el paquete se llame `ncurses` no significa que provea `-lncurses`. Los alias van en un
549 directorio LOCAL (`.curses-compat/`) y no en `/usr/lib`: esa vista es la evidencia de qué se
550 declaró, y ensuciarla para que pase un test deja mintiendo justo al mecanismo que existe para no
551 mentir. Ojo: hay que repetir el `-L` en el `make`, porque el `LDFLAGS` del `make` PISA al del
552 `configure`.
553
554### 🖥 `cosmic-term`: la primera APLICACIÓN, y la biblioteca deja de estar vacía
555
556`docs/evidencia/cosmic-terminal-qemu-2026-08-04.png`:
557
558```
559bash-5.3# uname -srm; qalc -t '6*7'
560Linux 6.16.12 x86_64
56142
562```
563
564Una ventana de verdad —con decoración, *File/Edit/View*, minimizar/maximizar/cerrar—, `bash` corriendo
565adentro, el **kernel propio de hammer** contestando, y el `qalc` que empaquetamos una hora antes
566devolviendo 42. Se abre haciendo click en su icono en la biblioteca de aplicaciones
567(`cosmic-biblioteca-con-app-qemu-2026-08-04.png`), que hasta hoy salía vacía **con razón**: los 20
568`.desktop` de la imagen eran `NoDisplay=true`. Éste no lo es, y es el primero.
569
570**Selló a la primera, y eso es noticia por lo que arrastra**: su `Cargo.toml` depende de
571**`cosmic-files`** —el gestor de ficheros entra como LIBRERÍA, por el selector y el
572arrastrar-y-soltar—, así que compilar la terminal compila medio gestor de ficheros. Mismo patrón que
573`cosmic-settings-config` en los applets: **en esta suite las «aplicaciones» se usan unas a otras como
574crates**, y el grafo de deps de Cargo no se parece al mapa de componentes.
575
576`wgpu` viene en `default`, al revés que el resto de los clientes que pintan con tiny-skia. Se dejó
577como está: `wgpu` y `naga` ya se habían compilado enteros para `cosmic-workspaces`, así que el costo
578estaba pago, y apartarse de las features por defecto es apartarse de la única combinación que
579upstream prueba. Cierra con **dos `NEEDED`**: `libxkbcommon.so.0` y `libc.so`.
580
581**Terminfo, para la próxima:** la ncurses del corpus es *libs-only* y no instala la base de terminfo,
582pero trae `--with-fallbacks=…,xterm-256color,…` compilados. A la terminal no le hace falta —el que
583necesita terminfo es lo que corre ADENTRO— y el hijo acá es el `sh` de busybox, que no lo usa. El día
584que entren `vim` o `htop` en la imagen, hará falta un paquete de datos de terminfo.
585
586### 📁 `cosmic-files`: «ya está compilado» era falso por una línea del vecino
587
588`docs/evidencia/cosmic-files-qemu-2026-08-04.png`: el gestor abre desde la biblioteca y **lista el
589sistema de ficheros real de hammer** — `bin` (72 ítems), `dev` (129), `ente`, `etc`, `lib`,
590`lost+found`—, con su barra lateral de Recientes/Carpeta personal/Papelera.
591
592Entró creyendo que era casi gratis, porque `cosmic-term` **ya compila cosmic-files como crate**. No
593lo era, y el motivo cabe en una línea del `Cargo.toml` del vecino: `cosmic-term` lo declara con
594`default-features = false`. **Lo que un paquete cuesta depende de con qué features lo pide quien lo
595usa**, así que «ya se compiló» puede ser falso.
596
597**Se apagaron dos features por defecto, y las dos por una razón medida:**
598
599- **`gvfs`** trae los crates `gio`/`glib`, y el enlace final pide las glib **compartidas**
600 (`unable to find dynamic system library 'gio-2.0'`, y detrás gobject, glib, ffi, pcre2, z). El
601 corpus las empaqueta **sólo estáticas**: haría falta una cadena `-shared` entera, que es una
602 campaña aparte y mete una segunda glib en la imagen. Se pierden los **montajes remotos** (SFTP,
603 MTP), no la navegación local. ⚠ Y por eso tampoco se construye `cosmic-files-applet`: su
604 `Cargo.toml` fija `gvfs` a mano, así que apagarla en el paquete raíz no lo alcanza.
605- **`wgpu`** desbordó el filesystem: `No space left on device` creando un temp dir en `/src/target`,
606 **dos veces**, con wgpu+naga+ash+glow+spirv a `codegen-units=1`. Sin él el árbol de build queda en
607 ~2 GB. No se pierde nada: todos los demás clientes de la suite pintan con tiny-skia y la imagen no
608 tiene GPU.
609
610**El intento con `glib` declarado dejó una regla que vale para toda la cola:** el error fue
611`Package 'libpcre2-8', required by 'glib-2.0', not found` — o sea que **el que falla no es la dep
612sino lo que su `.pc` declara en `Requires`**. Declarar una dep trae su artefacto, no su clausura de
613pkg-config. Se lee en dos segundos con `grep ^Requires` sobre los `.pc` del artefacto, ANTES de
614gastar el build: `glib-2.0.pc → libpcre2-8`, `gobject-2.0.pc → libffi`, `gio-2.0.pc → zlib`.
615
616### ⚙ `cosmic-settings`: un panel de control que NO arrastra medio sistema
617
618`docs/evidencia/cosmic-settings-qemu-2026-08-04.png`: abre desde el lanzador (`Super`, teclear
619«settings») con la barra lateral entera —Red, Bluetooth, Accesibilidad, Escritorio, Pantallas,
620Sonido, Energía, Dispositivos de entrada, Aplicaciones, Fecha y hora, Sistema y cuentas— y la página
621de Escritorio poblada. **Tarda ~20 s en aparecer** con render por software; no está colgado.
622
623**La sorpresa buena: no arrastra NetworkManager, ni libpulse, ni udisks, ni accountsservice.** Habla
624con los daemons por D-Bus vía `zbus`, que es Rust puro. Se verificó ANTES de escribir la receta
625listando los crates `-sys` del `Cargo.lock` —que es donde vive la verdad sobre qué C hace falta—, y
626salieron sólo: `drm-sys`, `input-sys`, `libudev-sys`, `dav1d-sys`, `wayland-sys` (dlopen) y
627`gettext-sys` (trae su propio C). O sea el juego fijo de todo cliente + `libdrm` + `dav1d`.
628**Regla barata que ahorra builds: `grep '^name = ".*-sys"' Cargo.lock | sort -u` antes de adivinar
629deps por lo que el programa *hace*.**
630
631**Volvió a morder la doble barra de `cosmic-protocols//`**, igual que en `cosmic-applets`: el
632manifiesto usa esa forma para que cargo trate la fuente como distinta y el `[patch]` no se apunte a
633sí mismo, GitHub le contesta 404, y el build muere en el *fetch* con tres reintentos de red y
634`failed to load source for dependency 'cosmic-client-toolkit'`. Mismo fix: mover la barra al medio
635(`pop-os//cosmic-protocols`) en `Cargo.toml` **y** `Cargo.lock`. **Segunda víctima ⇒ es un patrón de
636la suite, no una rareza de un repo**: conviene mirarlo apenas un `cargo vendor` da 404.
637
638Instala **32 `.desktop`**, uno por página, así que cada sección del panel aparece como entrada propia
639en la biblioteca. Y `resources/default_schema/` va con `find`, no con `cp` plano: es el árbol
640`<Componente>/v1/<clave>` de cosmic-config, donde **cada clave es un fichero cuyo nombre es la
641clave** — aplanarlo deja el escritorio sin esa configuración, callado.
642
643**Deuda que deja anotada su propio log:** `org.freedesktop.locale1 was not provided by any .service
644files` — nadie sirve `localed` en la imagen, así que la página de idioma no va a poder cambiar nada.
645Es del mismo tipo que `login1` (que sí resuelve arje-logind-compat), y se arregla del mismo modo.
646
647### ⏱ El panel tiene un DEADLINE de arranque: con 4 vCPU sale mutilado
648
649Medido: con `-smp 4` el panel arranca **sin ala izquierda, sin reloj y sin dock** —sólo la bandeja
650derecha— y el único rastro es `Failed to setup panel dbus server deadline has elapsed`. Con
651`-smp 8 -m 8192`: **4 arranques de 4 con el escritorio completo y cero deadlines vencidos**.
652La causa es la estampida del arranque: el panel registra su servidor D-Bus mientras catorce clientes
653de iced escanean fuentes al mismo tiempo.
654
655**CORRECCIÓN medida el 2026-08-04 validando `cosmic-edit`: la carga del anfitrión TAMBIÉN cuenta, y
656`-smp 8` no la compensa.** Acá decía «no es carga del anfitrión (falla con el anfitrión ocioso)» —
657cierto para el caso de 4 vCPU, pero se leía como que con 8 el anfitrión daba igual, y no. Dos
658arranques de la MISMA imagen con la MISMA línea de QEMU (`-smp 8 -m 8192`):
659
660| anfitrión | colores | veredicto |
661|---|---|---|
662| `load average` ≈ 6 (otro agente compilando) | **130** | mutilado, y `deadline has elapsed` en el log |
663| ocioso (`load` ≈ 1,8) | **1671** | completo |
664
665Tiene sentido y conviene decirlo entero: **este anfitrión tiene 8 núcleos** (`nproc`), así que
666`-smp 8` le entrega la máquina entera a la VM y cualquier otra cosa que compile compite por los
667mismos hilos. La receta práctica: antes de creerle a un arranque mutilado, mirá `/proc/loadavg` y
668repetí con el anfitrión quieto — es un minuto y evita perseguir una regresión que no existe, que es
669exactamente el error que ya está documentado dos párrafos más abajo.
670
671**Y una lección de medición que costó horas: el conteo de procesos NO sirve como veredicto.**
672Perseguí una regresión inexistente comparando «48 procesos» contra «31», revirtiendo uno por uno
673tres cambios míos que no tenían nada que ver — cuando un escritorio completo y sano muestra **32**.
674El número de procesos no distingue «arrancó» de «dibujó». El framebuffer sí, y sin ambigüedad:
675**894 colores = completo, 130 = mutilado.** (Con las cuatro aplicaciones en el dock el escritorio
676completo da ~1670: el umbral es «cientos vs. miles», no un número exacto.)
677
678## ✎ LA CUARTA APLICACIÓN: cosmic-edit (2026-08-04)
679
680`docs/evidencia/cosmic-edit-qemu-2026-08-04.png` y `…-escribiendo-…png` — 1280×800, **2105 colores**:
681ventana con menús *File/Edit/View*, pestaña *New document*, numeración de líneas, y el icono del
682editor en el dock con el punto de «en ejecución». La segunda captura tiene `hammer edita` escrito
683por teclado sobre la línea 1 y el punto de modificado en la pestaña: **se escribe, no sólo se ve**.
684
685Se llegó por el lanzador, sin tocar el ratón para elegir: `Super` → teclear `text` → el primer
686resultado es *«Text editor for the COSMIC desktop»* (`Ctrl+1`) → `Enter`. Que el editor sea lo que
687responde a «text» es efecto del `.desktop` que genera `xdgen`, con sus `Keywords` y su
688`MimeType=text/plain` — el mismo fichero que lo hace manejador por defecto de los `.txt` en el
689gestor.
690
691**Lo que la receta enseñó, y que corrige un borde de la regla del `Cargo.lock`:** leer los crates
692`-sys` del lock sigue siendo lo primero que hay que hacer, pero **el lock lista lo POSIBLE, no lo
693ENCENDIDO**. En cosmic-edit aparecen `gio-sys`, `glib-sys` y `gobject-sys` —que en cualquier otro
694paquete mandarían a declarar glib, y de ahí a la campaña de la cadena compartida— y entran sólo por
695la feature `gvfs`, que se apaga. El lock dice el universo; las features dicen el recorte. La receta
696no declara ninguna glib y su binario enlaza **sólo `libxkbcommon.so.0` y `libc.so`**: dos NEEDED, el
697mínimo de la suite.
698
699## Lo que falta, en orden
700
7011. **Más aplicaciones**: `cosmic-store`, `cosmic-screenshot`.
7022. **La cadena glib COMPARTIDA** (`glib` + `pcre2` + `libffi` + `zlib` con `.so`), que destrabaría
703 `gvfs` en cosmic-files y su applet del panel. Es una campaña propia y hay que decidir antes si la
704 imagen puede tener una sola glib.
7052. `xdg-desktop-portal-cosmic` — sin él no hay captura de pantalla, ni file-chooser, ni compartir.
7063. `cosmic-settings` (el panel de control) y `cosmic-files`: la suite tiene aplicaciones y el
707 catálogo todavía no.
7084. `cosmic-greeter` (hoy `cosmic-session` lo lanza y falla con ENOENT, sin consecuencias) y
709 `cosmic-osd` con teclas de brillo/volumen reales.
7105. Metal: la imagen de QEMU y la de metal comparten todo menos el driver; el viaje físico es el
711 mismo que el de `metal-dual-desktop-en-curso`.
cosmic-desktop.md
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 (filtra ScreenReader, ver atajos) | b3:e871eee4 |
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 (filtra ScreenReader, ver atajos) | b3:642919a5 |
cosmic-notifications | ✅ sellado | b3:3e2a5478 |
cosmic-launcher | ✅ sellado | b3:c17b4956 |
cosmic-app-library | ✅ sellado | b3:b2a163e7 |
cosmic-workspaces-epoch | ✅ sellado (necesitó mesa, ver gotcha 11) | b3:a031249e |
cosmic-applets (19 binarios en uno) | ✅ sellado (necesitó dbus-shared, ver gotcha 10) | b3:3e53a325 |
pop-launcher 1.2.7 (otro repo, ver abajo) | ✅ sellado a la primera | b3:64a5cbaa |
gmp 6.3.0 · mpfr 4.2.2 (cadena de la calculadora) | ✅ sellados a la primera | b3:d99a0a5d · b3:13982d20 |
libqalculate 5.12.0 (qalc) | ✅ sellado — la calculadora del lanzador ANDA (ver abajo) | b3:edb8d14d |
cosmic-term 1.5.0 — la 1ª APLICACIÓN | ✅ sellada a la primera (arrastra cosmic-files y wgpu) | b3:d0ea1c1e |
cosmic-files 1.5.0 — la 2ª aplicación | ✅ sellada, sin gvfs ni wgpu (ver abajo) | b3:48d2b072 |
cosmic-settings 1.5.0 — la 3ª aplicación | ✅ sellada (necesitó el parche de la doble barra) | b3:716dac53 |
La cola está COMPLETA: 33 recetas, escritorio-cosmic 71/71.
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).
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 to rustc can only be passed to
one target». Se arregla con flags = ["--bin", "<nombre>"]. Le pasa a cosmic-comp y a cosmic-bg
(workspace con config/), y le va a pasar a casi todos: ponelo desde el principio.
-
start-cosmic es bash de verdad, no sh. Usa mapfile, [[ ]] y expansión indirecta
${!var}; con el busybox del rootfs no arranca. bash ya 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 con dbus-run-session (de dbus), que es lo que el script exec-uta al final.
-
rust-toolchain.toml con channel = "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.desktop existe 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.
-
libsystemd no es systemd. La feature default = ["systemd"] de cosmic-comp trae el crate
libsystemd, que es una reimplementación en Rust del protocolo, no un binding a la C. Y
logind habla org.freedesktop.login1, que acá lo sirve arje-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 (crate ash),
backend_x11 (x11rb con dl-libxcb) y xwayland cargan 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-bg fue sin [deps] porque «habla el protocolo en Rust y no toca C». Reventó en el
build.rs de smithay-client-toolkit pidiendo xkbcommon.pc: sctk ENLAZA libxkbcommon; el
que la dlopea es winit, que es otra capa. Hablar Wayland en Rust dice cómo viaja el byte, no
con qué se interpreta un teclado.
cosmic-idle fue 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 por
cosmic-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.rs que panickea o en la línea de enlace, y las dos veces el error dijo
exactamente quién y por qué. El link = "static" sobrevive porque el artefacto de libxkbcommon
trae .a además de .so.
-
Una feature apagada en el paquete puede estar prendida por otro. cosmic-bg declara image
con default-features = false y sin AVIF, y aun así compila dav1d-sys: las features de cargo
se UNIFICAN en el grafo. Se resolvió trayendo dav1d (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 Requires arrastrando una dep que nadie declaró.
-
cosmic-wallpapers no 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 tanto cosmic-start escribe la config de USUARIO
con una fuente Color, 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.
-
build.rs contento ≠ linker contento: cosmic-applets necesita la libdbus COMPARTIDA.
Los applets de red, bluetooth y status-area hablan D-Bus por libdbus-sys (FFI a la C, no zbus
como el resto de la suite). Declarar dbus a secas alcanza para que su build.rs pase —le basta
el .pc, que el paquete del daemon trae— y el fallo aparece una hora después, en el enlace
final del multiplexor: unable to find dynamic system library 'dbus-1'. El corpus sólo
empaqueta libdbus-1.a; el .so vive en dbus-shared. Son dos preguntas distintas y las
contestan dos herramientas distintas: pkg-config dice si la dep existe, el linker si hay algo
que enlazar. Era la única receta de la cola que pedía la estática.
-
cosmic-workspaces necesita mesa, y la dep sale de lo que el programa MUESTRA. La vista de
espacios de trabajo dibuja miniaturas vivas de las ventanas; el compositor se las pasa como
DMA-BUF por zcosmic-toplevel-info y el cliente importa el fd con GBM ⇒ -lgbm. Es el único
cliente de la suite que toca el pipeline gráfico además del compositor, así que el juego fijo de
cuatro (libxkbcommon, pkgconf, libudev-zero, libinput) no alcanza. Corolario del gotcha
de siempre: la dep no se deduce de la familia del paquete, se lee de lo que hace.
-
DOS GPUs en QEMU = el panel dibuja en la pantalla que no mirás. -device virtio-gpu-pci no
reemplaza a la VGA por defecto de QEMU: la SUMA. El invitado ve card0 y card1, cosmic-comp
crea dos outputs (Virtual-1 y Unknown-1), cosmic-bg pinta el fondo en ambos y el panel se
ancla en uno solo. El screendump captura el otro ⇒ fondo perfecto, panel invisible, y ni un
solo error en el log. Con -vga none el escritorio aparece entero a la primera.
Y esto corrige un diagnóstico anterior de este mismo runbook: cuando el panel selló y no
dibujó, lo atribuí a que le faltaban los applets. Los applets hacían falta —sin ellos el panel
está vacío— pero no eran la causa de la pantalla lisa; la causa era la segunda GPU, que ya
estaba ahí. Un síntoma puede tener dos causas suficientes y arreglar la primera no prueba nada:
la prueba es la pantalla, no el razonamiento.
-
mkdir: not found en cosmic-start, y la sesión arrancaba igual. El getty entrega un PATH
sin /usr/bin, y en la imagen fundida mkdir sólo vive ahí (/usr/bin/mkdir → coreutils; la
base metal no tendió el symlink /bin/mkdir → busybox que sí tienen chmod, mount, sed y tee).
Los cuatro mkdir -p de la preparación fallaban y no se notaba, porque los directorios
venían horneados en la imagen. Es la forma más traicionera de bug: gratis hoy, fatal el día que
alguien cambie cómo se arma la imagen. Fix: exportar PATH como primera línea del script.
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
🏔 EL ESCRITORIO ENTERO, DIBUJADO (2026-08-04)
docs/evidencia/cosmic-panel-applets-qemu-2026-08-04.png — 1280×800, 895 colores distintos:
- panel superior (banda
(27,27,27), 32 px): botones Workspaces y Applications a la
izquierda, reloj al centro, y a la derecha los applets de accesibilidad, audio, red, batería y
encendido;
- dock inferior centrado y redondeado, con lanzador, workspaces, biblioteca de aplicaciones y
las entradas
.desktop del rootfs;
- fondo
(33,74,135) = exactamente el Color(Single((0.13,0.29,0.53))) que escribe cosmic-start.
48 procesos cosmic-* estables a los 30 s, sobre el kernel de hammer con arje-zero como PID1.
Se reproduce así (headless y automatizable; el -vga none no es opcional, ver gotcha 12):
COSMIC_MODE=session bash scripts/cosmic/qemu-desktop-image.sh
qemu-system-x86_64 -machine q35,accel=kvm -cpu host -smp 8 -m 8192 \ # 8 vCPU: ver el deadline del panel
-drive if=pflash,format=raw,readonly=on,file=/usr/share/edk2/x64/OVMF_CODE.4m.fd \
-drive if=pflash,format=raw,file=/tmp/OVMF_VARS.fd \
-drive file=work/hammer-cosmic-qemu.img,format=raw,if=virtio \
-vga none -device virtio-gpu-pci,xres=1280,yres=800 \
-device qemu-xhci,id=xhci -device usb-kbd,bus=xhci.0 -device usb-tablet,bus=xhci.0 \
-display none -serial file:/tmp/cosmic.serial -qmp unix:/tmp/qmp.sock,server,nowait
# y después, por QMP: {"execute":"screendump","arguments":{"filename":"/tmp/shot.ppm"}}
El veredicto es contar colores distintos del PPM, no leer el log: con dos GPUs el log salía
impecable y la pantalla estaba lisa.
🖱 LA ENTRADA: se usa, no sólo se ve (2026-08-04)
Hay que usar USB HID, no virtio-input. El kernel de hammer no trae VIRTIO_INPUT (mirá el
-e … de recipes/linux-generic.toml), así que -device virtio-keyboard no produce ni un
/dev/input/event*. Sí trae USB_XHCI_HCD + USB_HID + INPUT_EVDEV, y además es lo que se va a
usar en metal, así que la prueba es más fiel:
-device qemu-xhci,id=xhci -device usb-kbd,bus=xhci.0 -device usb-tablet,bus=xhci.0
usb-tablet y no usb-mouse: reporta coordenadas ABSOLUTAS, y QMP sólo sabe mandar eventos abs
(el eje va de 0 a 32767 sobre el ancho/alto de la pantalla, no en píxeles).
Se maneja entero por QMP, sin ver la pantalla y sin humano:
// mover el puntero a (155,16) — el botón «Applications»
{"execute":"input-send-event","arguments":{"events":[
{"type":"abs","data":{"axis":"x","value":3968}},
{"type":"abs","data":{"axis":"y","value":655}}]}}
// click
{"execute":"input-send-event","arguments":{"events":[{"type":"btn","data":{"down":true,"button":"left"}}]}}
{"execute":"input-send-event","arguments":{"events":[{"type":"btn","data":{"down":false,"button":"left"}}]}}
// una tecla
{"execute":"input-send-event","arguments":{"events":[
{"type":"key","data":{"down":true,"key":{"type":"qcode","data":"c"}}},
{"type":"key","data":{"down":false,"key":{"type":"qcode","data":"c"}}}]}}
El veredicto también es de píxeles, y conviene medirlo por REGIÓN. El click en Applications
llevó el color dominante de (33,74,135) (fondo, 91 %) a (27,27,27) (84 %): la biblioteca de
aplicaciones se abrió a pantalla casi completa. Y para el teclado, contar colores del rectángulo del
buscador (440..840 × 80..112) bastó: 155 → 118 colores al reemplazarse el placeholder por lo tecleado.
Evidencia: cosmic-app-library-click-qemu-2026-08-04.png y cosmic-teclado-busqueda-qemu-2026-08-04.png.
La biblioteca sale vacía y está BIEN. Los 20 .desktop de la imagen son NoDisplay=true —son
applets y componentes de sesión, no aplicaciones—, así que no hay nada que listar. Que la búsqueda no
devuelva resultados no es un fallo del escritorio: es que la distro todavía no tiene ni una sola
aplicación gráfica. Se sabrá arreglado cuando entre cosmic-files o un terminal.
🔑 Los atajos: dos ficheros envenenados por la misma línea
Super+A abre la biblioteca y Super+W la vista de espacios de trabajo
(cosmic-super-w-workspaces-qemu-2026-08-04.png, con miniatura viva del escritorio dentro — o
sea que el camino DMA-BUF→GBM del gotcha 11 anda de verdad). Para llegar ahí hubo que sanear DOS
ficheros de datos, no uno:
| fichero | lo instala | qué liga |
…Shortcuts/v1/defaults | cosmic-comp | tecla → acción |
…Shortcuts/v1/system_actions | cosmic-settings-daemon | acción → comando |
Los dos traen una entrada ScreenReader, una variante que cosmic-settings-config —el crate que los
parsea, pineado por cargo en la rev 8c54bbbc— todavía no tiene en su enum. RON no ignora lo que
no conoce: falla la deserialización del MAPA COMPLETO. Con defaults roto no hay ligaduras; con
system_actions roto hay ligadura pero ningún comando que ejecutar. Hacen falta los dos grep -v.
Y acá me equivoqué de fichero teniendo el dato exacto delante. El error decía
3:5-3:17: Unexpected variant named 'ScreenReader' — con línea y columna. Busqué el token en vez
de preguntarme qué fichero lo tiene en esa posición, di con keybindings.ron (que también lo
traía, en la línea 94), lo arreglé, reconstruí cosmic-comp cuarenta minutos y el error salió
igualito. Era otro fichero, de otro paquete. Cuando el error trae coordenadas, la primera pregunta
es de qué fichero son, no dónde más aparece esa palabra.
Es el riesgo del pin lockstep asomando desde adentro de upstream: el dato y el parser del mismo
epoch no coinciden.
Super abre el lanzador — pero hizo falta un paquete más, y de otro repo: ver abajo.
🔎 pop-launcher: el lanzador es DOS programas
Super no abría nada y el único rastro era una línea que no menciona ninguna tecla:
ERROR pop-launcher failed to start: No such file or directory. cosmic-launcher es sólo la
interfaz: dibuja la lista y manda lo tecleado por stdin a un proceso hijo, pop-launcher, que es
quien busca. Sin ese binario la ventana no tiene qué mostrar, así que no se muestra — el mismo modo
de falla que el panel sin applets: lo que falta es un HIJO, no una librería, y el log del padre se
lee sano.
Vive en otro repo (pop-os/launcher), no versiona por epoch-N y no entra en el pin lockstep.
La versión la fija el Cargo.lock de cosmic-launcher —rev a332a3a7 de la 1.2.7—, no el último
tag del repo: el lock es lo que garantiza que hablen el mismo protocolo. El tarball se pide por
commit (/archive/<sha>.tar.gz), porque no hay tag que nombre esa rev. Selló a la primera
(b3:64a5cbaa) con un solo NEEDED: libc.so.
Es otro multiplexor por symlink, como cosmic-applets: un binario, y cada plugin es un enlace con
su nombre dentro de su propio directorio junto al plugin.ron que lo describe. El servicio enumera
plugins/*/; si está el .ron y falta el enlace —o al revés— el plugin no existe para él, sin
error. Ojo con el sed 's/_/-/' de upstream: cambia sólo el primer guión bajo, así que el
directorio es desktop_entries y el ejecutable de adentro desktop-entries. Son dos nombres
distintos a propósito.
Verificado de punta a punta (cosmic-super-lanzador-, cosmic-pop-launcher-plugins-,
cosmic-pop-launcher-calc-qemu-2026-08-04.png): 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 que había
disponible: el proceso hijo corrió, evaluó la consulta y devolvió una fila. Lo que falta ahora es
libqalculate (el binario qalc), que es un paquete, no un problema.
🧮 libqalculate: la calculadora del lanzador, y un stdin que era stdout
=123*456 → 56088 en el lanzador (cosmic-calculadora-qemu-2026-08-04.png). La cadena entera
desde una tecla está en el catálogo: Super → cosmic-launcher → pop-launcher → plugin calc →
qalc → mpfr → gmp.
Pero el camino hasta ahí tuvo dos muros que vale la pena dejar escritos, porque ninguno se parecía a
lo que era.
1) stdin apuntaba al descriptor 1. El plugin no le pasa la cuenta por argv: arranca qalc SIN
expresión, con stdin en tubería, le escribe la expresión y cierra. En ese modo el binario no leía
nada — ni error ni resultado, la fila del lanzador salía en blanco. El síntoma acusaba a readline,
al toolchain y a la versión, y no era ninguno:
DIAG-main isatty=0 feof=0 ferror=0 fd=1 ← fileno(stdin) == 1
nm -S qalc | grep -E ' (stdin|stdout|stderr)$'
1847148 8 B stderr
1847148 8 B stdin ← las TRES en la misma dirección
1847148 8 B stdout
La causa: el binario salía DINÁMICO aunque la receta dice link = "static", porque libtool se come
el -static que inyecta el harness ([[link-static-mentira-libtool]] otra vez, cuarto caso). Y en
un musl dinámico las tres variables de stdio se resolvieron por copy relocation a una sola ranura
de BSS compartida: la última que escribe el loader gana, así que el stdin del programa era el
stdout. De ahí la asimetría que parecía absurda: qalc -f /dev/stdin funcionaba —abre el
descriptor por RUTA, sin tocar el FILE* roto— y qalc leyendo stdin no.
El fix es el par que ya usa libxml2: make LDFLAGS="-all-static -no-pie". Con él el binario queda
estático de verdad y los tres símbolos vuelven a tener direcciones distintas, en .rodata:
11e71c8 8 R stderr
11e71d0 8 R stdin
11e71d8 8 R stdout
La regla, y es de las caras: link = "static" en la receta no garantiza un binario estático.
Cuando algo se comporta como si su stdin/stdout estuvieran cruzados, readelf -d y
nm -S | grep ' std' contestan en dos segundos lo que el log no dice nunca.
Barrido de la cola entera con ese criterio, después del fix: de las 7 recetas que declaran
link = "static" (cosmic-session, gmp, mpfr, nasm, libqalculate y las dos que en realidad
son dynamic), ninguna tiene NEEDED indebido y ninguna tiene los símbolos de stdio aliasados.
Era un caso, no un patrón — pero el patrón que lo causa (libtool comiéndose el -static) ya tiene
cuatro víctimas anteriores en el corpus.
2) La pregunta de primer arranque se come la cuenta, SIEMPRE. Antes de leer nada, qalc pregunta
si activar autocalc. El plugin le manda la expresión y cierra: la expresión se consume como
respuesta, no es un sí/no válido, y qalc no llega a guardar config ⇒ la próxima vez vuelve a
preguntar. No es «falla la primera vez»: no anda nunca. Se siembra
~/.config/qalculate/qalc.cfg con calculate_as_you_type=0 desde cosmic-start — política de la
imagen, como el color de fondo; una distro de verdad lo pondría en /etc/skel.
Gotchas de empaquetado de la cadena:
gmp va con --disable-assembly. Elige rutinas en ensamblador mirando la CPU de quien
compila, o sea hornearía la ISA del laptop en el artefacto y el hash dejaría de significar lo mismo
en la granja. Es una calculadora de escritorio: la implementación en C portable sobra.
- El configure de libqalculate no encuentra readline porque prueba
-lreadline junto a
-lncurses, -lcurses, -ltermcap y -ltinfo, y el corpus sólo empaqueta libncursesw.a.
Que el paquete se llame ncurses no significa que provea -lncurses. Los alias van en un
directorio LOCAL (.curses-compat/) y no en /usr/lib: esa vista es la evidencia de qué se
declaró, y ensuciarla para que pase un test deja mintiendo justo al mecanismo que existe para no
mentir. Ojo: hay que repetir el -L en el make, porque el LDFLAGS del make PISA al del
configure.
🖥 cosmic-term: la primera APLICACIÓN, y la biblioteca deja de estar vacía
docs/evidencia/cosmic-terminal-qemu-2026-08-04.png:
bash-5.3# uname -srm; qalc -t '6*7'
Linux 6.16.12 x86_64
42
Una ventana de verdad —con decoración, File/Edit/View, minimizar/maximizar/cerrar—, bash corriendo
adentro, el kernel propio de hammer contestando, y el qalc que empaquetamos una hora antes
devolviendo 42. Se abre haciendo click en su icono en la biblioteca de aplicaciones
(cosmic-biblioteca-con-app-qemu-2026-08-04.png), que hasta hoy salía vacía con razón: los 20
.desktop de la imagen eran NoDisplay=true. Éste no lo es, y es el primero.
Selló a la primera, y eso es noticia por lo que arrastra: su Cargo.toml depende de
cosmic-files —el gestor de ficheros entra como LIBRERÍA, por el selector y el
arrastrar-y-soltar—, así que compilar la terminal compila medio gestor de ficheros. Mismo patrón que
cosmic-settings-config en los applets: en esta suite las «aplicaciones» se usan unas a otras como
crates, y el grafo de deps de Cargo no se parece al mapa de componentes.
wgpu viene en default, al revés que el resto de los clientes que pintan con tiny-skia. Se dejó
como está: wgpu y naga ya se habían compilado enteros para cosmic-workspaces, así que el costo
estaba pago, y apartarse de las features por defecto es apartarse de la única combinación que
upstream prueba. Cierra con dos NEEDED: libxkbcommon.so.0 y libc.so.
Terminfo, para la próxima: la ncurses del corpus es libs-only y no instala la base de terminfo,
pero trae --with-fallbacks=…,xterm-256color,… compilados. A la terminal no le hace falta —el que
necesita terminfo es lo que corre ADENTRO— y el hijo acá es el sh de busybox, que no lo usa. El día
que entren vim o htop en la imagen, hará falta un paquete de datos de terminfo.
📁 cosmic-files: «ya está compilado» era falso por una línea del vecino
docs/evidencia/cosmic-files-qemu-2026-08-04.png: el gestor abre desde la biblioteca y lista el
sistema de ficheros real de hammer — bin (72 ítems), dev (129), ente, etc, lib,
lost+found—, con su barra lateral de Recientes/Carpeta personal/Papelera.
Entró creyendo que era casi gratis, porque cosmic-term ya compila cosmic-files como crate. No
lo era, y el motivo cabe en una línea del Cargo.toml del vecino: cosmic-term lo 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.
Se apagaron dos features por defecto, y las dos por una razón medida:
gvfs trae los crates gio/glib, y el enlace final pide las glib compartidas
(unable to find dynamic system library 'gio-2.0', y detrás gobject, glib, ffi, pcre2, z). El
corpus las empaqueta sólo estáticas: haría falta una cadena -shared entera, que es una
campaña aparte y mete una segunda glib en la imagen. Se pierden los montajes remotos (SFTP,
MTP), no la navegación local. ⚠ Y por eso tampoco se construye cosmic-files-applet: su
Cargo.toml fija gvfs a mano, así que apagarla en el paquete raíz no lo alcanza.
wgpu desbordó el filesystem: No space left on device creando un temp dir en /src/target,
dos veces, con wgpu+naga+ash+glow+spirv a codegen-units=1. Sin él el árbol de build queda en
~2 GB. No se pierde nada: todos los demás clientes de la suite pintan con tiny-skia y la imagen no
tiene GPU.
El intento con glib declarado dejó una regla que vale para toda la cola: el error fue
Package 'libpcre2-8', required by 'glib-2.0', not found — o sea que 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 en dos segundos con grep ^Requires sobre los .pc del artefacto, ANTES de
gastar el build: glib-2.0.pc → libpcre2-8, gobject-2.0.pc → libffi, gio-2.0.pc → zlib.
⚙ cosmic-settings: un panel de control que NO arrastra medio sistema
docs/evidencia/cosmic-settings-qemu-2026-08-04.png: abre desde el lanzador (Super, teclear
«settings») con la barra lateral entera —Red, Bluetooth, Accesibilidad, Escritorio, Pantallas,
Sonido, Energía, Dispositivos de entrada, Aplicaciones, Fecha y hora, Sistema y cuentas— y la página
de Escritorio poblada. Tarda ~20 s en aparecer con render por software; no está colgado.
La sorpresa buena: no arrastra NetworkManager, ni libpulse, ni udisks, ni accountsservice. Habla
con los daemons por D-Bus vía 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—, y
salieron sólo: drm-sys, input-sys, libudev-sys, dav1d-sys, wayland-sys (dlopen) y
gettext-sys (trae su propio C). O sea el juego fijo de todo cliente + libdrm + dav1d.
Regla barata que ahorra builds: grep '^name = ".*-sys"' Cargo.lock | sort -u antes de adivinar
deps por lo que el programa hace.
Volvió a morder la doble barra de cosmic-protocols//, igual que en cosmic-applets: el
manifiesto usa esa forma para que cargo trate la fuente como distinta y el [patch] no se apunte a
sí mismo, GitHub le contesta 404, y el build muere en el fetch con tres reintentos de red y
failed to load source for dependency 'cosmic-client-toolkit'. Mismo fix: mover la barra al medio
(pop-os//cosmic-protocols) en Cargo.toml y Cargo.lock. Segunda víctima ⇒ es un patrón de
la suite, no una rareza de un repo: conviene mirarlo apenas un cargo vendor da 404.
Instala 32 .desktop, uno por página, así que cada sección del panel aparece como entrada propia
en la biblioteca. Y resources/default_schema/ va con find, no con cp plano: es el árbol
<Componente>/v1/<clave> de cosmic-config, donde cada clave es un fichero cuyo nombre es la
clave — aplanarlo deja el escritorio sin esa configuración, callado.
Deuda que deja anotada su propio log: org.freedesktop.locale1 was not provided by any .service files — nadie sirve localed en la imagen, así que la página de idioma no va a poder cambiar nada.
Es del mismo tipo que login1 (que sí resuelve arje-logind-compat), y se arregla del mismo modo.
⏱ El panel tiene un DEADLINE de arranque: con 4 vCPU sale mutilado
Medido: con -smp 4 el panel arranca sin ala izquierda, sin reloj y sin dock —sólo la bandeja
derecha— y el único rastro es Failed to setup panel dbus server deadline has elapsed. Con
-smp 8 -m 8192: 4 arranques de 4 con el escritorio completo y cero deadlines vencidos.
La causa es la estampida del arranque: el panel registra su servidor D-Bus mientras catorce clientes
de iced escanean fuentes al mismo tiempo.
CORRECCIÓN medida el 2026-08-04 validando cosmic-edit: la carga del anfitrión TAMBIÉN cuenta, y
-smp 8 no la compensa. Acá decía «no es carga del anfitrión (falla con el anfitrión ocioso)» —
cierto para el caso de 4 vCPU, pero se leía como que con 8 el anfitrión daba igual, y no. Dos
arranques de la MISMA imagen con la MISMA línea de QEMU (-smp 8 -m 8192):
| anfitrión | colores | veredicto |
load average ≈ 6 (otro agente compilando) | 130 | mutilado, y deadline has elapsed en el log |
ocioso (load ≈ 1,8) | 1671 | completo |
Tiene sentido y conviene decirlo entero: este anfitrión tiene 8 núcleos (nproc), así que
-smp 8 le entrega la máquina entera a la VM y cualquier otra cosa que compile compite por los
mismos hilos. La receta práctica: antes de creerle a un arranque mutilado, mirá /proc/loadavg y
repetí con el anfitrión quieto — es un minuto y evita perseguir una regresión que no existe, que es
exactamente el error que ya está documentado dos párrafos más abajo.
Y una lección de medición que costó horas: el conteo de procesos NO sirve como veredicto.
Perseguí una regresión inexistente comparando «48 procesos» contra «31», revirtiendo uno por uno
tres cambios míos que no tenían nada que ver — cuando un escritorio completo y sano muestra 32.
El número de procesos no distingue «arrancó» de «dibujó». El framebuffer sí, y sin ambigüedad:
894 colores = completo, 130 = mutilado. (Con las cuatro aplicaciones en el dock el escritorio
completo da ~1670: el umbral es «cientos vs. miles», no un número exacto.)
✎ LA CUARTA APLICACIÓN: cosmic-edit (2026-08-04)
docs/evidencia/cosmic-edit-qemu-2026-08-04.png y …-escribiendo-…png — 1280×800, 2105 colores:
ventana con menús File/Edit/View, pestaña New document, numeración de líneas, y el icono del
editor en el dock con el punto de «en ejecución». La segunda captura tiene hammer edita escrito
por teclado sobre la línea 1 y el punto de modificado en la pestaña: se escribe, no sólo se ve.
Se llegó por el lanzador, sin tocar el ratón para elegir: Super → teclear text → el primer
resultado es «Text editor for the COSMIC desktop» (Ctrl+1) → Enter. Que el editor sea lo que
responde a «text» es efecto del .desktop que genera xdgen, con sus Keywords y su
MimeType=text/plain — el mismo fichero que lo hace manejador por defecto de los .txt en el
gestor.
Lo que la receta enseñó, y que corrige un borde de la regla del Cargo.lock: leer los crates
-sys del lock sigue siendo lo primero que hay que hacer, pero el lock lista lo POSIBLE, no lo
ENCENDIDO. En cosmic-edit aparecen gio-sys, glib-sys y gobject-sys —que en cualquier otro
paquete mandarían a declarar glib, y de ahí a la campaña de la cadena compartida— y entran sólo por
la feature gvfs, que se apaga. El lock dice el universo; las features dicen el recorte. La receta
no declara ninguna glib y su binario enlaza sólo libxkbcommon.so.0 y libc.so: dos NEEDED, el
mínimo de la suite.
Lo que falta, en orden
- Más aplicaciones:
cosmic-store, cosmic-screenshot.
- La cadena glib COMPARTIDA (
glib + pcre2 + libffi + zlib con .so), que destrabaría
gvfs en cosmic-files y su applet del panel. Es una campaña propia y hay que decidir antes si la
imagen puede tener una sola glib.
xdg-desktop-portal-cosmic — sin él no hay captura de pantalla, ni file-chooser, ni compartir.
cosmic-settings (el panel de control) y cosmic-files: la suite tiene aplicaciones y el
catálogo todavía no.
cosmic-greeter (hoy cosmic-session lo lanza y falla con ENOENT, sin consecuencias) y
cosmic-osd con teclas de brillo/volumen reales.
- Metal: la imagen de QEMU y la de metal comparten todo menos el driver; el viaje físico es el
mismo que el de
metal-dual-desktop-en-curso.