tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1569Shell 203Rust 109Markdown 993728 Sutichaykuna

⎇ 7bfad4b4ea63ed6900e4011fab4bc09309643841 docs/plan-apps-usuario-final.md Markdown · 618 siq'ikuna · 40.4 KB Ñawpa willay ch'uya

portal de ficheros en sway: MEDIDO de punta a punta — el diálogo abre, y el núcleo no arranca sin json-glib (SIGSEGV mudo en 1.18.4) f47b56c10 · Sergio ·

1# Plan — apps de usuario final (montón A del ADR 0015), MEDIDO
2
3**Fecha de la medición:** 2026-09-03 · **Catálogo contra el que se midió:** 1079 recetas
4(corpus + las cuatro colas) · **Instrumento:** `makedepends`+`depends` del APKBUILD de Alpine de
5cada candidata, cruzados contra los nombres de receta que existen en el disco.
6
7## Por qué existe este documento
8
9El [ADR 0015](adr/0015-imagenes-ajenas.md) parte las apps que faltan en tres montones y dice del
10**A** —Firefox, Chromium, LibreOffice, mpv, GIMP, Inkscape, Blender, OBS— que no lo bloquea *«nada
11estructural: son Wayland-nativos y compilables. Falta escribirlas»*, y propone el orden
12**mpv → OBS → Firefox**.
13
14`mpv` se escribió el 2026-09-03 y esa parte se confirmó. Al ir por el segundo, la medición
15**desmiente la premisa para el resto del montón**: OBS y Firefox no están a una receta de distancia,
16están detrás de muros que esta distro levantó a propósito. Este documento deja el dato escrito para
17que la decisión se tome mirándolo y no de memoria.
18
19## Cómo leer la tabla
20
21Dos columnas, y **la que importa es la segunda**:
22
23- **recetas que faltan** — cuántos `makedepends` de Alpine no tienen receta acá. Es una **cota
24  superior floja**: Alpine construye con todo activado y nosotros no. El control lo da `mpv`, que
25  YA ESTÁ SELLADA y aun así aparece con 16 faltantes — son exactamente las perillas que su receta
26  apaga a propósito (jack, sndio, libbluray, zimg, rubberband…). O sea: un número alto no prueba
27  que sea caro.
28- **muros de diseño** — dependencias que NO son «una receta más», sino decisiones ya tomadas de
29  esta distro: GTK3 aparcado, X11 al tacho, Qt6 que vive sólo en `incoming-kde`, toolchain wasi
30  inexistente. **Un muro no se paga escribiendo una receta: se paga cambiando una decisión.**
31
32## La tabla
33
34| app | faltan | muros | veredicto |
35|---|---:|---|---|
36| ~~wf-recorder~~ | — | — | ✅ **SELLADA 2026-09-03** (b3:25caa359), con audio. Se destrabó al promover pipewire al corpus. Sólo en `escritorio-sway`: captura por `wlr-screencopy` y KWin/mutter no lo implementan. |
37| ~~imv~~ | 7 | **GL de escritorio** | ❌ **BLOQUEADA 2026-09-03** — el muro no lo veía este método; ver abajo. Reemplazada por `swayimg`, sellada el mismo día. |
38| ~~swayimg~~ | — | — | ✅ **SELLADA 2026-09-03** (b3:9bcd39c1, **5.5**), en las CUATRO imágenes. Visor sin GL: rasteriza a `wl_shm`. Trae backend DRM de yapa. |
39| ~~luajit~~ | — | — | ✅ **SELLADA 2026-09-03** (b3:115d4c1c). Escrita para destrabar swayimg 5.x; mpv también la prefiere. |
40| ~~zathura~~ | — | — | ✅ **SELLADA 2026-09-03** (b3:9c5eac92) + `zathura-pdf-poppler`. **Es GTK4, no GTK3**: upstream vació girara. En gnome/cosmic/sway; **NO en KDE**, por colisión de poppler. |
41| ~~mupdf~~ | 9 | X11 **y GL de escritorio** | ❌ **BLOQUEADA 2026-09-03.** La nota era falsa: `mupdf-gl` **sí** es un muro — su `platform/gl/` va sobre GLUT + OpenGL de escritorio, el mismo muro que imv. mupdf **no tiene visor Wayland**: sus dos UIs son `mupdf-x11` y `mupdf-gl`. El motor (`libmupdf`+`mutool`) no tiene muros, pero no hace falta: el PDF ya lo da poppler. |
42| ~~inkscape~~ | 14 | **GTK3** (en la release) | ❌ **BLOQUEADA 2026-09-03.** Era GTK3 encubierto, como se sospechaba: `INKSCAPE_1_4_4` pide `gtkmm-3.0>=3.24` **y** `gtk+-3.0>=3.24`. `master` **sí** migró a GTK4 (`gtk4>=4.14`, `gtkmm-4.0>=4.13.3`) pero está sin publicar; y de todos modos el catálogo **no tiene NI UN binding C++**: faltan sigc++, glibmm, cairomm, pangomm y gtkmm enteros. |
43| *mpv* | *16* | *X11* | *YA SELLADA — es el control del método, no una candidata.* |
44| **gimp** | 18 | **GTK3**, X11 | GIMP 2.10 es GTK2/3. Fuera de alcance mientras GTK3 esté aparcado. |
45| **obs-studio** | 20 | **Qt6**, X11 | ver abajo. |
46| **firefox** | 26 | **GTK3**, X11, **wasi** | ver abajo. |
47| **chromium** | 38 | **GTK3**, **Qt6**, X11 | peor que Firefox por todos lados. |
48| **libreoffice** | 59 | **GTK3**, **Qt6**, X11 | 59 recetas y tres muros. No es un ticket, es un año. |
49
50## ⚠ El defecto del método, encontrado al usarlo (y lo que destapó)
51
52La columna «recetas que faltan» pregunta **¿existe esta receta en el disco?** y esa es la pregunta
53equivocada. La correcta es **¿la alcanza el consumidor?** — porque una receta resuelve sus deps
54sibling-first (su propia cola) y después el catálogo PADRE, **nunca una cola hermana**.
55
56Se descubrió al ir a escribir `wf-recorder`, que la tabla daba en 0/0: su `makedepends` pide
57`pipewire-dev` y `pulseaudio-dev`, los dos existen… **en las tres colas de escritorio y en ninguna
58en el corpus**. Una app del corpus no los ve. wf-recorder construye igual (`-Dpulse=disabled
59-Dpipewire=disabled`, `have_audio=false`) pero graba **sin sonido**, y no tiene backend ALSA con el
60que caerse.
61
62### Lo que eso destapa, que es más grande que wf-recorder
63
64> **El corpus no tiene ningún cliente de audio salvo ALSA.** `libpipewire-0.3` y `libpulse` viven
65> sólo en `incoming-{kde,gnome,cosmic}`. Toda app multimedia que llegue al corpus va a chocar acá.
66
67Ya chocó dos veces en un día: `mpv` terminó con `--ao=alsa` en vez de su salida nativa de PipeWire
68(y por eso hubo que dar vuelta `-Dpipewire-alsa` en las tres pipewire, que fue el arreglo correcto
69pero fue un rodeo), y ahora `wf-recorder` se queda mudo.
70
71**DECIDIDA Y HECHA el 2026-09-03 — opción 1, con una salvedad medida.** Lo que sigue queda como
72registro de por qué. La decisión era: promover UNA pipewire al corpus. Construye
73—el corpus tiene su propia `glib`, `dbus` y `pcre2`— pero el problema no es construirla: es que una
74imagen que hidrate `corpus/pipewire` **y** la de su cola pondría dos `libpipewire-0.3.so` distintas
75en la misma ruta, que es la enfermedad de las dos glib. Las salidas posibles, para pensarlas
76despierto:
77
781. **Una sola pipewire, en el corpus, y las tres colas jubilan la suya.** Es lo más limpio y es lo
79   que ya se hizo con otras 13 copias que sellaban igual — pero éstas NO sellan igual (difieren en
80   glib/dbus/pcre2), así que hay que decidir cuál gana y volver a sellar wireplumber y los portales
81   de las tres colas.
822. **Aceptar ALSA como la única ABI de audio del corpus**, que es lo que hoy quedó funcionando de
83   hecho: `-Dpipewire-alsa=enabled` hace que un cliente ALSA desemboque en PipeWire. Barato y ya
84   está hecho; el precio es que las apps del corpus nunca usan la API nativa (sin control por-stream,
85   sin metadata de sesión) y que las que sólo hablan pulse/pipewire —wf-recorder— quedan afuera.
863. **Que las apps multimedia vivan en las colas**, una copia por escritorio. Es lo que la estructura
87   empuja hoy y es exactamente lo que no queremos para una app que sirve a las cuatro imágenes.
88
89### Cómo se resolvió
90
91Ganó la **opción 1**, y salió barata porque la variante de COSMIC ya resolvía su cierre entero
92contra el catálogo padre: `recipes/pipewire.toml` sella el MISMO hash que tenía en la cola. Con ella
93subieron `libsndfile` y `dbus-shared` (duplicados exactos). 9 copias de cola jubiladas, **5
94rebuilds**.
95
96Lo que hizo segura la promoción fue una medición, no un argumento: **ningún pipewire enlaza glib**
97—sus NEEDED son `libdbus-1.so.3`, `libpulse.so.0`, `libsndfile.so.1`— así que `libpipewire-0.3.so`
98se comparte entre las cuatro imágenes sin repetir el episodio de las dos glib.
99
100**Y la salvedad, que es la parte que valía la pena medir:** `pulseaudio` NO se promovió.
101`libpulse-mainloop-glib.so.0.0.6` mide **45.712 bytes** en la variante de GNOME (NEEDED
102`libglib-2.0.so.0`) y **2.455.600** en la del corpus, que se traga la glib ESTÁTICA. Esa dentro del
103proceso de gnome-shell —que ya carga la glib sombra dinámica— es el cuadro de colord. Las tres
104pulseaudio se quedan: son variantes deliberadas. Sin esa salvedad los rebuilds habrían sido 8,
105incluido `gnome-shell`.
106
107La regla general que sale de acá, y sirve para la próxima promoción:
108
109> **Enlazar contra una variante y correr contra otra es legítimo cuando lo que cruza es un SONAME**
110> (misma ABI). Lo que no se puede es **hidratar dos artefactos distintos en la misma ruta**. Por eso
111> pipewire se puede compartir y pulseaudio no.
112
113Consecuencias inmediatas: `mpv` pasó a `-Dpipewire=enabled` (con ALSA detrás, para imágenes sin
114demonio) y `wf-recorder` se escribió y selló el mismo día.
115
116## ⚠ EL TERCER DEFECTO DEL MÉTODO: la receta existe, es alcanzable, y aun así no sirve
117
118El defecto anterior era «¿existe la receta?» ≠ «¿la alcanza el consumidor?». Al ir a escribir `imv`
119apareció un **tercer eje**, y es el peor de los tres porque las dos preguntas anteriores dan que sí:
120
121> **La receta existe, el consumidor la alcanza, y la VARIANTE que se construyó no publica la ABI
122> que hace falta.**
123
124`imv` pide OpenGL. `mesa` existe, está en el corpus, y cualquier receta lo alcanza. Pero:
125
126- `imv/src/canvas.c` hace `#include <GL/gl.h>` y dibuja con `glBegin` / `glOrtho` /
127  `GL_TRIANGLE_FAN` / `GL_TEXTURE_RECTANGLE`, o sea **OpenGL de función fija**, perfil de
128  compatibilidad. No existe equivalente en GLES: no es una perilla, es otra API.
129- Su `meson.build` hace `gl_dep = dependency('gl', required: false)` y, si no la encuentra,
130  `dependency('opengl')` **sin `required: false`** ⇒ el configure muere.
131- Y esta distro **no tiene ningún proveedor de OpenGL de escritorio**. Medido en los artefactos
132  sellados, no supuesto: las **tres** variantes de mesa (`mesa`, `mesa-swrast`, `mesa-llvmpipe`)
133  llevan `-Dglx=disabled -Dglvnd=false`, y sus artefactos publican `libEGL.so.1`, `libGLESv2.so.2`,
134  `libgbm` y `egl.pc`/`glesv2.pc`/`gbm.pc` — **ni un `libGL.so*`, ni `gl.pc`, ni `opengl.pc`**.
135  No hay receta `libglvnd` en ninguna cola.
136
137**La trampa fina, y es la parte que hay que recordar:** mesa SÍ instala `/usr/include/GL/gl.h`
138(el header viene con `-Dopengl=true`). O sea que una app así **compila entera** y muere recién al
139LIGAR. Un triaje que mire headers da verde.
140
141### Qué costaría darlo vuelta (para que la decisión se tome con el número)
142
143El camino que el propio `meson.build` de imv nombra —«libglvnd fallback for pure-wayland systems»—
144es autorar `libglvnd` (que provee `libOpenGL.so`/`opengl.pc`) y reconstruir mesa con `-Dglvnd=true`.
145Eso es **radio 139 dependientes directos** sobre `mesa`, y además convierte a libglvnd en pieza
146obligatoria de las cuatro imágenes: sin ella no hay EGL. Es una decisión de distro, no un ticket de
147visor de imágenes.
148
149### La salida, que resultó mejor que el original
150
151`swayimg` cubre el mismo caso de uso **sin tocar GL**: rasteriza a un buffer y lo entrega por
152`wl_shm`. Sus deps requeridas (xkbcommon, fontconfig, freetype2, wayland, wayland-protocols) ya
153estaban todas en el corpus ⇒ **cero recetas nuevas**. Y trae un backend **DRM** que muestra imágenes
154en una TTY pelada, sin compositor, que es exactamente lo que le falta al instalador.
155
156Entró primero como **4.7** —la última sin Lua— porque desde la 5.0 `dependency('luajit')` es
157obligatoria y no había receta (la `lua5.2` del OSC de mpv es otra ABI). Se escribió `luajit` el mismo
158día y subió a **5.5**. La cadena quedó verificada de punta a punta: `swayimg -e` reporta
159`Lua 5.1 · jit=true · LuaJIT 2.1.1787165859`, que es exactamente el `relver` de nuestra receta.
160`luajit` queda además disponible para mpv, que la prefiere sobre 5.2.
161
162**Regla que sale de acá, para el próximo triaje de apps:** la pregunta no es «¿existe la receta?»
163ni sólo «¿la alcanza?», sino **«¿la variante sellada publica el `.pc` y el `.so` que el consumidor
164pide?»**. Se contesta mirando el artefacto, no el catálogo.
165
166Y como acá cada punto ciego se convierte en un guardián, esa pregunta ya tiene instrumento:
167**`scripts/provee.py`**, que indexa los `.pc` y las librerías de TODOS los artefactos sellados y
168contesta quién publica cada nombre y desde qué colas se alcanza. Los tres defectos del método caen
169con un solo comando:
170
171```sh
172scripts/provee.py --desde corpus gl opengl     # ✗ nadie lo publica  → exit 1  (el caso imv)
173scripts/provee.py exiv2 librsvg                # ✓ existen, alcanzables SÓLO desde su cola
174```
175
176Es el hermano de build de `vigia-sonames.py`, que cubre la mitad de runtime. **El triaje de la
177próxima app empieza por acá**, no por contar `makedepends` contra `recipes/*.toml`.
178
179## El montón A quedó AGOTADO de cosas sin muro (2026-09-03)
180
181Con `zathura` cerrada, se re-triaron con `scripts/provee.py` las tres candidatas que la tabla daba
182como baratas o dudosas. **Las tres son muro**, y ninguna lo era por el motivo que decía la tabla:
183
184- **imv** → no hay proveedor de OpenGL de escritorio (sección de arriba). Sustituida por `swayimg`.
185- **mupdf** → sus DOS visores son X11 o GLUT+OpenGL de escritorio. No tiene UI Wayland.
186- **inkscape** → la release es GTK3; el `master` con GTK4 está sin publicar y además pide toda la
187  torre de bindings C++ que este catálogo no tiene (`provee.py` da ✗ en sigc++-2.0, glibmm-2.4,
188  cairomm-1.0, pangomm-1.4, gtkmm-3.0 **y** gtkmm-4.0).
189
190**Todo lo que queda del montón A está detrás de UNA de cuatro decisiones**, no de trabajo de
191recetas: GTK3 (inkscape-release, gimp, firefox), Qt6-en-el-corpus (obs), OpenGL de escritorio
192(imv, mupdf-gl) o X11 (chromium, mupdf-x11). Ver «los dos veredictos» abajo y el orden propuesto.
193
194## ⚠ Lo que SÍ estaba roto y no era falta de recetas: imágenes sin terminal
195
196Al terminar zathura se cruzó la membresía de los cuatro perfiles contra una lista de emuladores de
197terminal, editores y gestores de ficheros. El resultado no tiene nada que ver con el montón A y es
198más grave que cualquier app que falte:
199
200| perfil | terminal | editor | archivos |
201|---|---|---|---|
202| `escritorio-cosmic` | cosmic-term | cosmic-edit | cosmic-files |
203| `escritorio-sway` | foot | vim | — |
204| `escritorio-gnome` | **—** | **—** | — |
205| `escritorio-kde` | **—** | **—** | — |
206
207Es la lección de `foot` otra vez y a mayor escala: sway llegó a 121/121 **sin emulador de terminal**
208porque nadie lo declaraba, y la métrica de clausura no lo puede ver porque mide lo DECLARADO.
209
210**GNOME arreglado el 2026-09-03 con CERO recetas nuevas**: `foot` y `helix` viven en el corpus, ya
211estaban selladas y **no las declaraba ningún perfil**. foot es Wayland puro sobre `xdg-shell` (no
212pide protocolos de wlroots) así que corre bajo mutter; su único NEEDED es `libc.so`.
213
214**KDE es un hallazgo aparte y NO se tocó**: tiene **22 apps con `.desktop` selladas en su cola y sin
215declarar** —konsole, dolphin, kate, okular, spectacle, ark, gwenview, systemsettings, kcalc…— y
216además le faltan `plasma-desktop`, `plasma-nm`, `plasma-pa`, `powerdevil` y `kscreen`, o sea red,
217audio, energía y pantalla. Su perfil son **13 raíces de ARRANQUE EN METAL**, no la imagen de
218escritorio completa (ésa se hidrató aparte, por hash). Reordenarlo es decisión del frente KDE.
219
220## Los dos veredictos que corrigen al ADR
221
222### Firefox NO es montón A
223
224Su APKBUILD pide `gtk+3.0-dev` — **Firefox no tiene backend GTK4 ni lo va a tener a corto plazo**, y
225GTK3 es una de las tres deudas que el frente GNOME aparcó por diseño.
226
227⚠ **CORREGIDO 2026-09-03: el X11 de Firefox NO es un muro.** Se listaba como tal por los `libxt` y
228`libxcomposite` del APKBUILD, pero en el árbol de Gecko todo el código X11 está detrás de
229`CONFIG["MOZ_X11"]` (`widget/gtk/moz.build:131`): es una PERILLA de build, no una dependencia
230estructural, y hay builds Wayland-only. El muro real de Firefox es **GTK3**, más un
231**toolchain wasi** (`wasi-sdk`, `wasi-compiler-rt`) que no existe en el
232corpus, más `nodejs`, `icu`, `nss/nspr`, `clang/llvm/lld` versionados y `cbindgen`.
233
234La memoria del repo decía que a Firefox «le falta subir el techo MSRV». Eso es cierto **y es lo
235menor**: el bloqueo real es GTK3.
236
237**Consecuencia estratégica, que es la decisión que hay que tomar despierto:** todos los navegadores
238del mundo Linux son o GTK3 (Firefox y derivados) o Chromium (que además arrastra Qt6 y GTK3 en
239Alpine). Entonces **«tener navegador» no es un ticket del montón A**: es elegir entre
240
2411. **autorar el stack GTK3** —revierte una decisión de diseño y reabre GIMP/Inkscape de paso—, o
2422. **darle el navegador a [qorpa](adr/0015-imagenes-ajenas.md)**, o sea tratarlo como montón B: el
243   navegador corre en la imagen ajena, hablando Wayland por socket.
244
245La opción 2 es coherente con todo lo demás que ya se decidió, y tiene una ventaja que conviene
246nombrar: el navegador es *justamente* el proceso al que menos ganas dan de darle el sistema entero.
247
248### OBS no puede ser una app del corpus
249
250Pide `qt6-qtbase-dev`, `qt6-qtbase-private-dev` y `qt6-qtsvg-dev`, y **Qt6 vive sólo en
251`incoming-kde/`**. Una receta resuelve sibling-first y después el catálogo padre: desde el corpus no
252se alcanza una cola. O sea que OBS hoy sólo puede existir **dentro de la cola de KDE**, y entonces
253no es una app de las cuatro imágenes sino una app de KDE. Súmese X11 (`libx11`, `libxcb`,
254`libxcomposite`, `libxinerama`) y 20 recetas nuevas (`libdatachannel`, `librist`, `libsrt`,
255`mbedtls`, `rnnoise`, `x264`, `luajit`, `swig`, `vlc`…).
256
257**Si lo que se quiere es grabar la pantalla** —que es el 80% de para qué se instala OBS— eso ya está
258al alcance de la mano: **`wf-recorder` sale con CERO recetas faltantes y cero muros**, porque su
259cierre (wayland, ffmpeg, libdrm) quedó completo cuando entró mpv.
260
261## Orden propuesto, en reemplazo del del ADR
262
2631. ✅ **mpv** — hecho (2026-09-03).
2642. **decisión de audio del corpus** (el ⚠ de arriba). Destraba wf-recorder y le da a mpv
265   su salida nativa. Es una decisión, no una campaña.
2663. **wf-recorder** — inmediato una vez tomada esa decisión; cubre el caso de uso principal de OBS.
2674. ✅ **visor de imágenes** — hecho (2026-09-03), pero con **`swayimg` 4.7 y no `imv`**: imv está
268   bloqueada por el muro de GL de escritorio (sección de arriba). En las cuatro imágenes.
2694b. ✅ **`luajit`** — hecha (2026-09-03). Destrabó swayimg 5.5 y queda disponible para mpv.
2705. ✅ **zathura + backend PDF** — hecho (2026-09-03). **La sospecha de GTK3 era falsa**: upstream
271   VACIÓ `girara` (en 2026.07.18 son siete `.c` de utilidades, cero GTK) y toda la UI se mudó a
272   zathura, que pide GTK4 >= 4.12 — que está en el corpus. Costó 5 recetas nuevas
273   (`girara`, `xxhash`, `poppler-glib`, `zathura`, `zathura-pdf-poppler`) + 4 promociones gratis al
274   corpus (`json-glib`, `lcms2`, `glib-shared`, `pcre2-shared`, `bzip2-shared`).
275   **No va en `escritorio-kde`**: su backend necesita una poppler con `ENABLE_GLIB=ON` y esa imagen
276   ya trae la de Qt6 vía okular; las dos instalan `/usr/lib/libpoppler.so.146`.
2776. ✅ **terminal y editor en GNOME** — hecho (2026-09-03), cero recetas: `foot` + `helix` del
278   corpus, que ya estaban selladas y sin declarar. Ver la sección de arriba.
2797. **DECISIÓN pendiente, y ahora bloquea a TODO lo que queda** — no es trabajo de recetas:
280   **(a)** autorar el stack GTK3 (destraba inkscape-release, GIMP y Firefox de una vez),
281   **(b)** dárselo a [qorpa](adr/0015-imagenes-ajenas.md) como montón B, o
282   **(c)** autorar la torre de bindings C++ (sigc++/glibmm/cairomm/pangomm/gtkmm-4) y seguir el
283   `master` sin publicar de Inkscape.
284   Hasta que se tome, el montón A no tiene ningún ticket barato: está agotado.
2858. **Aparte y sin decisión: las 22 apps de KDE selladas y sin declarar** (sección de arriba). No
286   cuesta un build — cuesta acordarlo con el frente KDE.
287
288OBS, Firefox, Chromium, GIMP y LibreOffice **salen del montón A** hasta que se decida GTK3 o qorpa.
289Que se llame montón A y no se pueda construir es peor que no tenerlo en la lista: es deuda fantasma,
290la misma figura que la terna de GNOME que había que sacar de `targets.toml` para que el grafo dejara
291de mentir.
292
293## Reproducir la medición
294
295⚠ **Ya no se reproduce así.** Lo que sigue describe el método ORIGINAL, que es el que falló las
296tres veces documentadas arriba; se deja escrito para que se entienda de dónde salieron los números
297de la tabla. El triaje de una app nueva se hace hoy cruzando sus `makedepends` contra
298`scripts/provee.py --desde corpus`, que pregunta por el artefacto y no por el catálogo.
299
300`/tmp/.../triaje-apps.py` fue un script de una sola vez; si hace falta repetirlo, lo que hace es:
301bajar el APKBUILD de cada candidata, extraer `makedepends`/`depends`, quitarles el sufijo `-dev`, y
302cruzar contra `recipes/*.toml` + `recipes/incoming-*/*.toml`, separando una lista fija de nombres
303que son muros de diseño (gtk+3.0, qt6-*, lib X11, wasi-*). No se guarda en `scripts/` porque la
304lista de candidatas es del momento, no un invariante que valga la pena vigilar.
305
306---
307
308# ADENDA 2026-09-13 — el censo, el muro de GL revisado, y la respuesta a «¿Electron?»
309
310Sale de tres preguntas del usuario en una sesión: *¿es congruente empaquetar GenOffice?*, *¿teníamos
311planeado compilar Electron?*, *¿qué apps muy usadas valdría la pena replicar de cero en vez de
312incluirlas?*. Las tres se contestaron midiendo, y dos mediciones **corrigen cosas escritas arriba**.
313
314## 1. El censo: de ~50 apps muy usadas, el catálogo tiene 9
315
316Cruzado contra los 1158 nombres de receta del disco (corpus + las cuatro colas):
317
318**Están (9):** `firefox`, `mpv`, `obs-studio`, `zathura`, `okular`, `gwenview`, `dolphin`, `ark`,
319`spectacle` — y **cinco de esas nueve son de KDE**, o sea que sólo llegan a una de las cuatro
320imágenes.
321
322**No están:** gimp, inkscape, krita, darktable, blender, audacity, kdenlive, shotcut, vlc, chromium,
323thunderbird, evolution, libreoffice, calligra, scribus, calibre, transmission, qbittorrent, deluge,
324filezilla, remmina, wireshark, virt-manager, gparted, keepassxc, bitwarden, signal-desktop,
325telegram-desktop, element-desktop, fractal, discord, slack, zoom, spotify, steam, lutris, wine,
326vscode, zed, sublime-text, obsidian, logseq, anki, gnucash, homebank, thunar, nautilus, pcmanfm,
327file-roller, flameshot.
328
329## 2. ⚠ CORRECCIÓN al «tercer defecto del método»: el muro de GL se movió, y luego se disfrazó
330
331La sección de arriba dice, con fecha 2026-09-03, que **ninguna cola publica `libGL.so` ni `gl.pc`** y
332que por eso toda app de GL de escritorio está bloqueada. **Eso dejó de ser cierto al día siguiente** y
333conviene no repetirlo de memoria:
334
335```
336scripts/provee.py --desde corpus gl opengl        # corrido el 2026-09-13
337  gl        ✗ NADIE LO PUBLICA en ninguna cola
338  opengl    ✓ corpus/libglvnd  ⇒  libOpenGL.so.0  opengl.pc
339```
340
341`recipes/libglvnd.toml` existe desde el **2026-09-04**, escrita para OBS, y hace la partición que
342ordena el problema —que arriba estaba mezclado en una sola fila:
343
344- **`libGL.so.1` = GL *más GLX*** en una sola librería ⇒ **implica X11**. Encenderlo metería
345  `libX11`/`libxcb`/`xorgproto` —hoy sólo en `incoming-kde`— **en el CORPUS**, o sea en las cinco
346  imágenes, para servir a una app. Eso sí sería contraproducente: es reabrir X11-al-tacho.
347- **`libOpenGL.so.0` = GL de escritorio pelado**, sin ventana ni plataforma, con el contexto creado
348  por EGL. **No contradice ninguna decisión de la distro.** Es la que se eligió.
349
350⚠ **Pero la campaña quedó a medias, y eso vale más que la buena noticia.** Las tres recetas de mesa
351siguen con `-Dglvnd=false`. Medido en el rootfs de KDE ya hidratado:
352
353| | mesa | libglvnd |
354|---|---|---|
355| `/usr/lib/libEGL.so.1` | → `libEGL.so.1.0.0`, **1.440.624 B** (el driver) | → `libEGL.so.1.1.0`, **323.144 B** (puro despacho) |
356| `libEGL_mesa.so.0` + `/usr/share/glvnd/egl_vendor.d/` | ✗ | ✗ |
357
358Las **dos** están declaradas en `escritorio-kde` ⇒ dos `libEGL.so.1` distintos en la misma ruta. Hoy
359gana el de mesa —por orden de hidratación, no porque nada lo guarde— y el de glvnd queda huérfano al
360lado. La propia receta de `libglvnd` avisaba de esto: *«mesa se reconstruye en la MISMA campaña, no
361después»*.
362
363**Y OBS, que fue la razón de escribir libglvnd, no la usa en runtime.** `libobs-opengl.so` sale con
364`NEEDED libEGL.so.1` y nada más; sus símbolos indefinidos son todos `egl*`, **ni un `gl[A-Z]` sin
365resolver** ⇒ su `glad` resuelve los punteros por `eglGetProcAddress` contra la EGL de mesa. glvnd
366está en la imagen satisfaciendo un requisito **de enlace** (el `OpenGL::GL` de CMake) y es **inerte**.
367
368⇒ **El estado real del muro**: `provee.py` dice `opengl ✓` y eso es verdad **de enlace**. En runtime
369no hay vendor de GL de escritorio registrado. Una app que de verdad llame por `libOpenGL.so.0`
370encontraría una tabla de despacho vacía. El muro pasó de *«no enlaza»* a *«enlaza y no corre»*, que
371es el modo de fallo más caro de esta casa. **Terminarlo es re-sellar las tres mesa con
372`-Dglvnd=true` en un solo movimiento**, y su precio ya está medido: radio grande sobre `mesa` y
373libglvnd pasa a ser pieza obligatoria de las cuatro imágenes.
374
375**Corolario para la tabla de arriba:** `darktable` estaba mal clasificada como caso de GL. No lo es —
376su UI es GTK3+cairo y OpenCL es opcional. Su muro es la **cola de deps**: faltan `lensfun`,
377`libgphoto2`, `openexr`, `imath`, `libraw`, `osm-gps-map`, `portmidi`, y `exiv2`/`librsvg`/
378`libsecret`/`colord` existen **sólo en colas** (inalcanzables desde el corpus). Es un frente de ~8
379recetas, no un muro. **`blender` sí es el caso de GL** (pide 4.3 core).
380
381## 3. Electron: nunca estuvo planeado, y el sustituto ya está construido
382
383`grep -rniE 'electron' docs/ recipes/` da **cero coincidencias**. No hay plan, ni ADR, ni ticket — y
384la tabla de arriba explica por qué sin nombrarlo: `chromium` es la peor fila del montón A (38 recetas
385+ GTK3 + Qt6 + X11), y Electron es Chromium **más** Node.
386
387### No existe un «Electron con base Firefox», y el SDD 26 §1 dice por qué
388
389> Gecko **no tiene API de embebido** en escritorio desde que murió XULRunner; GeckoView existe y es
390> de Android. ⇒ un envoltorio de Gecko es necesariamente **chrome-level**.
391
392Por eso los intentos históricos (Positron, Mozilla 2016) no cuajaron. **Pero eso es exactamente lo
393que es `atuq`, y ya está hecho:**
394
395| pieza de Electron | equivalente en `atuq` | estado |
396|---|---|---|
397| runtime Chromium | Gecko como **artefacto DERIVADO** de `firefox`, sin fork de fuente | ✅ sellado, en las 4 imágenes |
398| UI en HTML/JS | `omni.ja` re-empacado + `userChrome.css` + `distribution/extensions/` | ✅ SDD 26 §4.g |
399| lado Node (acceso al sistema) | **host de native messaging en Rust** | ✅ §7.quater, 2026-09-10 |
400| IPC | `connectNative`, con el contrato MEDIDO | ✅ §7.bis |
401
402⇒ **Lo que falta para que `atuq` sea una plataforma de apps y no sólo un navegador es chico**: un
403modo SSB (una ventana, sin barra, con su icono y su `.desktop`) y una clase de receta «app de atuq».
404Eso es más barato que **un solo** port de Electron, y destraba una familia entera en vez de un
405programa.
406
407### GenOffice, concretamente
408
409Electron + TypeScript + React, sidecar Rust para xlsx, **Apache-2.0** salvo `ee/` (licencia
410Enterprise aparte), en `github.com/genspark-ai/genoffice`. Su README dice que acepta **cualquier
411endpoint OpenAI-compatible, incluidos servidores de modelo locales**.
412
413- **La mitad de IA está resuelta y es una línea de configuración**: `llama-cpp` sirve
414  `/v1/chat/completions` y `/v1/embeddings`, y **ya viaja en los cuatro escritorios** junto con
415  `ia-modelo-chat`. Apuntarlo a `http://127.0.0.1:8080/v1` elimina Genspark, la API key y la salida
416  del documento de la máquina.
417- **La mitad de runtime no**: no hay Electron ni lo va a haber. La vía es **qorpa** —
418  `docs/state/qorpa-imagenes.toml` ya cura `ubuntu-base` y GenOffice publica `.deb`.
419- ⚠ **Y el muro real no es el empaquetado, es el MODELO.** El pineado es Qwen2.5-1.5B-Instruct
420  Q4_K_M (20,1 tok/s en CPU, elegido por peso y licencia). Para reescribir un párrafo alcanza; para
421  lo que GenOffice llama IA agéntica sobre un `.xlsx`, un 1.5B da basura. Conectarlo funciona; que
422  **sirva** pide pinear un modelo mucho mayor, y eso es su propia unidad de trabajo (y RAM).
423
424## 4. Qué vale la pena REHACER en vez de incluir
425
426El criterio, porque sin criterio esto es una lista de deseos:
427
428> **Rehacer sólo cuando el upstream está bloqueado por algo ESTRUCTURAL y el valor vive en un
429> protocolo o formato abierto, no en el código.** Si no: empaquetar, o enjaular.
430
431**Empaquetar — el muro cayó o nunca existió:**
432
433- **`thunderbird`** es el más desaprovechado: **es Gecko**. Misma cadena que `firefox`, que ya
434  construye. Ningún muro nuevo, sólo otro build largo. Es el cliente de correo gráfico más barato
435  que este catálogo puede tener.
436- **`gimp`**: estaba bloqueado por GTK3 y **`gtk3` ya está sellada** (Wayland-only). Pide re-triaje
437  con `provee.py` antes de prometer nada.
438- **`krita`, `kdenlive`, `qbittorrent`, `keepassxc`, `calibre`**: son Qt6, y Qt6 está **completo en
439  `incoming-kde`**. Son recetas de cola, no muros — pero sólo llegan a la imagen KDE.
440- **`fractal`** (Matrix, GTK4+Rust) y **`transmission`**: sin muro conocido.
441
442**Enjaular (qorpa), no rehacer:** vscode, discord, slack, signal, obsidian, zoom, spotify, steam,
443**genoffice**. Y **blender**, porque la jaula trae su propia mesa glibc y **esquiva la campaña de
444glvnd por completo**.
445
446**Rehacer desde tawasuyu — la lista corta que pasa el criterio:**
447
4481. **El shell de apps sobre `atuq`** (§3). No es una app: es el runtime. Máximo apalancamiento.
4492. **Bóveda / gestor de contraseñas.** KeePassXC es Qt y Bitwarden es Electron, pero el valor está
450   en el formato KDBX y en la integración con el navegador — y **esa mitad, que es la cara en todos
451   lados, ya está construida**: `agora` (Ed25519), `qullqa` y el host de native messaging.
4523. **Cliente Matrix nativo — sólo si `fractal` no construye.** Medir antes de escribir.
453
454El precedente que dice que el método funciona es propio: **torrent (SDD 26 §6.9) y medios (§6.6) se
455rehicieron como demonios Rust en vez de empaquetar transmission y vlc, y los dos están cerrados.**
456
457⚠ Y la advertencia que el SDD 26 §2 ya pagó: portar un upstream vivo compra **su cinta de correr**
458(*«el costo de Zen no es el motor: es el rebase cada cuatro semanas»*). Un port de GenOffice es una
459relación, no una receta.
460
461---
462
463# ADENDA 2026-10-13 — la suite ArtCraft entra ENTERA, y lo que eso desmiente
464
465Doce aplicaciones de escritorio en **Rust puro sobre egui + wgpu** (`github.com/storytold`,
466MIT OR Apache-2.0), construidas y **medidas corriendo** en una noche. Las recetas están en
467`recipes/{word,grid,deck,photo,vector,design,light,cad,effect,sound,film,pdf}craft.toml`.
468
469## El resultado, que es el que cambia el plan
470
471| | selladas | abren ventana y **pintan** |
472|---|---:|---|
473| ArtCraft (12) | **12/12** | **12/12** |
474
475Medido con `scripts/test-craft-ventana.sh <app>` —rootfs de `escritorio-sway` + `llvm18` +
476`mesa-llvmpipe`, sway headless, captura con grim— y la evidencia es **contar colores**, no leer el
477log: de 1.250 (`vectorcraft`) a 30.356 (`lightcraft`), con el azul del compositor desapareciendo de
478la foto en las doce. Cada una sella entre 11 y 26 min; 455-730 crates por app.
479
480## Lo que esto desmiente de la tabla de arriba
481
482La fila `libreoffice` —**59 recetas, GTK3 + Qt6 + X11, «no es un ticket, es un año»**— medía bien el
483coste de ESA app, pero se leía como el coste de tener ofimática. **No lo era.** La misma función
484—textos, hoja con gráficos, presentaciones— entró como **tres recetas `cargo_vendor` sin un solo
485muro de toolkit**, y de yapa trajo el hueco que la terna ofimática ni cubre (`vectorcraft` ≈ Draw) y
486cinco categorías del censo de §1 que estaban en «no están»: gimp/krita (`photocraft`), darktable
487(`lightcraft`), inkscape (`vectorcraft`), kdenlive/shotcut (`filmcraft`), scribus (`designcraft`).
488
489⚠ **Lo que NO desmiente:** fidelidad. Son 0.x —el README de `wordcraft` pone a prueba la beta
490«against real-world .docx files, keeping charts and SmartArt»— y portar un upstream vivo compra su
491cinta de correr. LibreOffice sigue siendo el patrón de fidelidad y **no se autora**: si hace falta
492de verdad, sale por qorpa, como se decidió para `genoffice`.
493
494## La línea divisoria de esta generación de apps, medida
495
496> **egui + wgpu entra hoy; GPUI / Vulkan-only no.**
497
498`egui-wgpu` pide `Backends::from_env().unwrap_or(PRIMARY | GL)`, o sea que **GL está en el default**:
499sin Vulkan, wgpu cae al camino EGL/GLES y pinta. Lo dice el log de las doce
500(`backend: Gl … llvmpipe … 4.5 (Core Profile)`) y lo confirma el control **sin** `WGPU_BACKEND`.
501`lightcraft` lo hace explícito: `renderer wgpu (order ["vulkan", "gl", "glow"])`.
502**Zed** es el contraejemplo: GPUI exige **Vulkan 1.3** en Linux ⇒ en esta distro no arranca, y el
503desbloqueo es el mismo que ya pedía el ScreenCast de COSMIC: **lavapipe** (`-Dvulkan-drivers=swrast`).
504
505## Tres huecos de la IMAGEN que estas apps destaparon (ninguno es de ellas)
506
5071. **No hay diálogo de ficheros EN SWAY** —y esta entrada se corrigió dos veces, que es casi todo
508   lo que tiene de interesante—. Lo que `photocraft` dice es
509   `file dialog backends: Findings { libdbus: None, file_chooser_portal: Some(false), zenity: false }`,
510   y de ahí salió primero un «no hay diálogo de ficheros» a secas: **generalización de UNA imagen**.
511   Medido después: los tres escritorios grandes **sí declaran un backend con `FileChooser`**
512   (`xdg-desktop-portal-{kde,gnome,cosmic}`, los tres sellados, con
513   `org.freedesktop.impl.portal.FileChooser` en su `.portal`). El hueco es de **sway y del corpus**.
514   ⚠ Y la sonda de la app **no sirve como medida**: contesta `libdbus: None` aunque `libdbus-1.so`
515   esté presente, o sea que aplasta «no hay portal» y «no supe preguntar» en el mismo valor.
516
517   **La salida está MEDIDA de punta a punta** (`scripts/test-portal-ficheros.sh`, 2026-10-13): sobre
518   el rootfs de sway se montan `xdg-desktop-portal` + `xdg-desktop-portal-cosmic` —el backend de
519   COSMIC es Rust sobre wgpu y **no arrastra ni GTK ni Qt**, a diferencia del de GNOME (gtk4 +
520   libadwaita) y el de KDE (Qt6)— y se llama al método real:
521
522       gdbus call … org.freedesktop.portal.FileChooser.OpenFile "" "Elegi un fichero" "{}"
523       → (objectpath '/org/freedesktop/portal/desktop/request/1_5/t')
524       árbol de sway → "app_id": "com.system76.CosmicFilesDialog"   · captura: 1.098 colores
525
526   O sea: **el diálogo abre, se ve y lista ficheros**. Lo que hace falta para llevarlo a la imagen:
527   (a) que las dos recetas sean alcanzables desde `incoming-wlr` (hoy viven en `incoming-cosmic`, y
528   una cola no ve a su hermana), (b) **`json-glib`**, y (c) cuatro líneas de
529   `/usr/share/xdg-desktop-portal/sway-portals.conf`, porque el `.portal` del backend trae
530   `UseIn=COSMIC` y en una sesión `sway` no lo elegiría solo:
531
532       [preferred]
533       default=cosmic
534       org.freedesktop.impl.portal.FileChooser=cosmic
535
536   ⚠⚠ **El punto (b) es el que casi me hace publicar un veredicto falso.** Sin `json-glib` el núcleo
537   del portal NO ARRANCA, y cada versión lo disfraza distinto: la 1.19.4 de la cola gnome dice
538   `Error relocating … json_parser_get_root: symbol not found` (rc=127) y la 1.18.4 de kde/cosmic se
539   cae con **SIGSEGV pelado** después de listar los backends, sin nombrar nada. Llegué a escribir que
540   el artefacto estaba roto; con la capa de `json-glib` montada, **las dos corren**. Es la figura de
541   siempre —sellado, presente, reproducible y sin arrancar— y la lección de método es que un arnés al
542   que le falta la clausura de RUNTIME de lo que mide produce un veredicto que parece del artefacto.
543   En las tres imágenes de escritorio esto no muerde porque `json-glib` entra por ser dep declarada
544   del propio portal; en sway no entra nada de esto.
5452. **OCHO de las doce se quedan sin portapapeles**: su `arboard` no trae `wayland-data-control`,
546   intenta X11 y expira (`cadcraft`, `deckcraft`, `designcraft`, `effectcraft`, `filmcraft`,
547   `lightcraft`, `pdfcraft`, `soundcraft`). Las cuatro que sí lo traen: `gridcraft`, `photocraft`,
548   `vectorcraft`, `wordcraft`. Medido en los BINARIOS corriendo, uno por uno, no en los manifiestos.
549   Parche de una línea por app, y mejor aguas arriba que acá.
5503. **Sin tarjeta de sonido degradan bien**: `soundcraft` cae a «silent clock» y `filmcraft` dice
551   `no audio output device found`. No es un hueco de la distro, es el LXC; se anota para que la
552   próxima medición no lo lea como fallo.
553
554Los tres son **función instalada y apagada en silencio**, que es la forma de fallo que esta casa
555persigue: la app abre, dibuja, y no puede abrir un fichero.
556
557## Cuatro premisas que la propia campaña desmintió (y cómo se vieron)
558
5591. **El `Cargo.lock` lista lo POSIBLE, no lo que se construye.** `photocraft` y `filmcraft` traen
560   `pipewire-sys`/`gtk-sys` en el lock y **ninguno se compila en Linux**: entran por `xcap`
561   (`cfg(windows)`) y `muda` (`cfg(macos)`). Un triaje por lock habría bloqueado la app más grande
562   de la familia sin motivo.
5632. **Los features de `eframe` del manifiesto de la app tampoco son los del build**: son ADITIVOS y
564   los enciende otro miembro del workspace. `vectorcraft` «no pedía wgpu» y corre sobre wgpu.
5653. **El panic de `zbus-lockstep-macros`** (la cicatriz de `boveda`/`mirada-greeter`) **no se dispara
566   en estas versiones**: `vectorcraft` compiló `atspi-common` 0.13 sin el `LOCKSTEP_XML_PATH` y con
567   el `xml/schemas/` presente.
5684. **`provee.py` contesta sobre el store que le den**, y su default `./store` en la jaula está
569   VACÍO: dijo «alsa: NADIE LO PUBLICA» de un `alsa-lib` sellado en las dos máquinas. Un falso
570   «nadie» se ve igual que un hueco real del catálogo.
571
572Y una trampa vieja que volvió a morder, con el mismo disfraz de siempre: **`pdfcraft` falló porque
573el `cargo vendor` de takana le pisó su propio `vendor/`** (cuatro crates `hayro` enganchados por
574`[patch.crates-io]` POR RUTA). El error hablaba de un `.cargo-checksum.json` que faltaba. Se arregla
575con `cargo_vendor_dir`, igual que `boveda` y `mise`; `vectorcraft` committea un `vendor/` también y
576lo lleva ahora de forma preventiva.
577
578## El CONTROL de todo esto: `photosuite`, de otra gente
579
580`eolix/photosuite` (1.110★, Apache-2.0, `v0.9.20`, 687 crates) hace lo mismo que `photocraft` con la
581misma cadena —**eframe/egui 0.36 + wgpu**— y lo hace **otro equipo**. Selló en una pasada
582(`b3:a4d43550`, hash predicho por el hub y confirmado por el worker) y **pinta**: 2.837 colores,
583`app_id` `io.github.eolix.PhotoSuite`. Importa porque convierte la frase «egui+wgpu entra en esta
584distro» de anécdota en medición: dos implementaciones independientes, el mismo resultado.
585
586Y gana donde las doce pierden: trae `arboard` con `wayland-data-control` ⇒ **sí tiene portapapeles**,
587y `winit` con `wayland-csd-adwaita` ⇒ dibuja sus propias decoraciones donde el compositor no las pone.
588O sea que el hueco del portapapeles **no es del toolkit**: es una línea del manifiesto de cada app.
589
590## Lo que NO entra, y por qué — para no volver a triarlo
591
592- **`lexmount/moli`** (17.791★, navegador headless para agentes, ~100 crates propios de DOM/layout/
593  paint): usa `v8` (`rusty_v8`), y su `build.rs` **se baja binarios en los DOS caminos** — el default
594  `download_static_lib_binaries()` y, con `V8_FROM_SOURCE=1`, su propio gn, ninja, clang y Rust. Sin
595  red en el sandbox y con «reproducir, no confiar» como premisa, eso es una contradicción, no una
596  dificultad. ⇒ **campaña de `v8`, no receta.**
597- **`zed`**: GPUI exige **Vulkan 1.3** en Linux. Desbloqueo: lavapipe.
598- **`niri`**: arrastra gtk4+libadwaita y compite con el compositor propio (smithay).
599- **`rio`**: redundante con `foot`, ya sellado y en los perfiles.
600
601**Y de esa lista, dos ya ENTRARON el mismo día** (`recipes/typst.toml`, `recipes/jj.toml`):
602- **`typst` 0.15.1** (`b3:98425d9a`) — y su prueba vive DENTRO del build: el `install` compone un
603  `.typ` de tres líneas y exige un PDF que empiece por `%PDF-`. Salieron 8.124 bytes con las fuentes
604  **embebidas** (`--font-path` apuntando a un directorio vacío, para que la prueba sea esa y no otra).
605  ⚠ Y falló la primera vez por `openssl-sys`: `typst-kit/system-downloader` → `ureq` → `native-tls` →
606  `openssl`, **sin alternativa de rustls**. Se le da el openssl del corpus, y de las dos variantes la
607  que lleva threads (`openssl-threads`): la otra se construye con `-static`, y eso hace que el
608  `Configure` de openssl apague `threads` y `pic` en silencio.
609- **`jj` 0.45.1** (`b3:15d8e6f9`) — repo creado, cambio detectado y los tres completados de shell
610  generados por el propio binario, todo dentro del build. **Va pineada a 0.45.1 a propósito**: la
611  0.46.0 pide `rust-version = 1.97.1` y el lab trae **1.97.0**. Dos números de parche, muro duro, y
612  subir el lab re-hashearía el corpus entero porque entra en `hash_inputs`.
613
614Sigue pendiente **`lapce`** (38.918★, floem+wgpu, el editor gráfico que zed no puede ser acá;
615`openssl`/`zlib`/`zstd` ya tienen receta).
616**`servo`** es la única que cambiaría algo estructural: usa `mozjs_sys` —SpiderMonkey, que esta casa
617**ya construye desde fuente** dentro de firefox— así que, a diferencia de moli, no choca con la
618premisa. Sigue siendo campaña (1.148 crates), no receta de una tarde.

plan-apps-usuario-final.md

Plan — apps de usuario final (montón A del ADR 0015), MEDIDO

Fecha de la medición: 2026-09-03 · Catálogo contra el que se midió: 1079 recetas (corpus + las cuatro colas) · Instrumento: makedepends+depends del APKBUILD de Alpine de cada candidata, cruzados contra los nombres de receta que existen en el disco.

Por qué existe este documento

El ADR 0015 parte las apps que faltan en tres montones y dice del A —Firefox, Chromium, LibreOffice, mpv, GIMP, Inkscape, Blender, OBS— que no lo bloquea «nada estructural: son Wayland-nativos y compilables. Falta escribirlas», y propone el orden mpv → OBS → Firefox.

mpv se escribió el 2026-09-03 y esa parte se confirmó. Al ir por el segundo, la medición desmiente la premisa para el resto del montón: OBS y Firefox no están a una receta de distancia, están detrás de muros que esta distro levantó a propósito. Este documento deja el dato escrito para que la decisión se tome mirándolo y no de memoria.

Cómo leer la tabla

Dos columnas, y la que importa es la segunda:

La tabla

appfaltanmurosveredicto
wf-recorder——✅ SELLADA 2026-09-03 (b3:25caa359), con audio. Se destrabó al promover pipewire al corpus. Sólo en escritorio-sway: captura por wlr-screencopy y KWin/mutter no lo implementan.
imv7GL de escritorio❌ BLOQUEADA 2026-09-03 — el muro no lo veía este método; ver abajo. Reemplazada por swayimg, sellada el mismo día.
swayimg——✅ SELLADA 2026-09-03 (b3:9bcd39c1, 5.5), en las CUATRO imágenes. Visor sin GL: rasteriza a wl_shm. Trae backend DRM de yapa.
luajit——✅ SELLADA 2026-09-03 (b3:115d4c1c). Escrita para destrabar swayimg 5.x; mpv también la prefiere.
zathura——✅ SELLADA 2026-09-03 (b3:9c5eac92) + zathura-pdf-poppler. Es GTK4, no GTK3: upstream vació girara. En gnome/cosmic/sway; NO en KDE, por colisión de poppler.
mupdf9X11 y GL de escritorio❌ BLOQUEADA 2026-09-03. La nota era falsa: mupdf-gl sí es un muro — su platform/gl/ va sobre GLUT + OpenGL de escritorio, el mismo muro que imv. mupdf no tiene visor Wayland: sus dos UIs son mupdf-x11 y mupdf-gl. El motor (libmupdf+mutool) no tiene muros, pero no hace falta: el PDF ya lo da poppler.
inkscape14GTK3 (en la release)❌ BLOQUEADA 2026-09-03. Era GTK3 encubierto, como se sospechaba: INKSCAPE_1_4_4 pide gtkmm-3.0>=3.24 y gtk+-3.0>=3.24. master sí migró a GTK4 (gtk4>=4.14, gtkmm-4.0>=4.13.3) pero está sin publicar; y de todos modos el catálogo no tiene NI UN binding C++: faltan sigc++, glibmm, cairomm, pangomm y gtkmm enteros.
mpv16X11YA SELLADA — es el control del método, no una candidata.
gimp18GTK3, X11GIMP 2.10 es GTK2/3. Fuera de alcance mientras GTK3 esté aparcado.
obs-studio20Qt6, X11ver abajo.
firefox26GTK3, X11, wasiver abajo.
chromium38GTK3, Qt6, X11peor que Firefox por todos lados.
libreoffice59GTK3, Qt6, X1159 recetas y tres muros. No es un ticket, es un año.

⚠ El defecto del método, encontrado al usarlo (y lo que destapó)

La columna «recetas que faltan» pregunta ¿existe esta receta en el disco? y esa es la pregunta equivocada. La correcta es ¿la alcanza el consumidor? — porque una receta resuelve sus deps sibling-first (su propia cola) y después el catálogo PADRE, nunca una cola hermana.

Se descubrió al ir a escribir wf-recorder, que la tabla daba en 0/0: su makedepends pide pipewire-dev y pulseaudio-dev, los dos existen… en las tres colas de escritorio y en ninguna en el corpus. Una app del corpus no los ve. wf-recorder construye igual (-Dpulse=disabled -Dpipewire=disabled, have_audio=false) pero graba sin sonido, y no tiene backend ALSA con el que caerse.

Lo que eso destapa, que es más grande que wf-recorder

El corpus no tiene ningún cliente de audio salvo ALSA. libpipewire-0.3 y libpulse viven sólo en incoming-{kde,gnome,cosmic}. Toda app multimedia que llegue al corpus va a chocar acá.

Ya chocó dos veces en un día: mpv terminó con --ao=alsa en vez de su salida nativa de PipeWire (y por eso hubo que dar vuelta -Dpipewire-alsa en las tres pipewire, que fue el arreglo correcto pero fue un rodeo), y ahora wf-recorder se queda mudo.

DECIDIDA Y HECHA el 2026-09-03 — opción 1, con una salvedad medida. Lo que sigue queda como registro de por qué. La decisión era: promover UNA pipewire al corpus. Construye —el corpus tiene su propia glib, dbus y pcre2— pero el problema no es construirla: es que una imagen que hidrate corpus/pipewire y la de su cola pondría dos libpipewire-0.3.so distintas en la misma ruta, que es la enfermedad de las dos glib. Las salidas posibles, para pensarlas despierto:

  1. Una sola pipewire, en el corpus, y las tres colas jubilan la suya. Es lo más limpio y es lo que ya se hizo con otras 13 copias que sellaban igual — pero éstas NO sellan igual (difieren en glib/dbus/pcre2), así que hay que decidir cuál gana y volver a sellar wireplumber y los portales de las tres colas.
  2. Aceptar ALSA como la única ABI de audio del corpus, que es lo que hoy quedó funcionando de hecho: -Dpipewire-alsa=enabled hace que un cliente ALSA desemboque en PipeWire. Barato y ya está hecho; el precio es que las apps del corpus nunca usan la API nativa (sin control por-stream, sin metadata de sesión) y que las que sólo hablan pulse/pipewire —wf-recorder— quedan afuera.
  3. Que las apps multimedia vivan en las colas, una copia por escritorio. Es lo que la estructura empuja hoy y es exactamente lo que no queremos para una app que sirve a las cuatro imágenes.

Cómo se resolvió

Ganó la opción 1, y salió barata porque la variante de COSMIC ya resolvía su cierre entero contra el catálogo padre: recipes/pipewire.toml sella el MISMO hash que tenía en la cola. Con ella subieron libsndfile y dbus-shared (duplicados exactos). 9 copias de cola jubiladas, 5 rebuilds.

Lo que hizo segura la promoción fue una medición, no un argumento: ningún pipewire enlaza glib —sus NEEDED son libdbus-1.so.3, libpulse.so.0, libsndfile.so.1— así que libpipewire-0.3.so se comparte entre las cuatro imágenes sin repetir el episodio de las dos glib.

Y la salvedad, que es la parte que valía la pena medir: pulseaudio NO se promovió. libpulse-mainloop-glib.so.0.0.6 mide 45.712 bytes en la variante de GNOME (NEEDED libglib-2.0.so.0) y 2.455.600 en la del corpus, que se traga la glib ESTÁTICA. Esa dentro del proceso de gnome-shell —que ya carga la glib sombra dinámica— es el cuadro de colord. Las tres pulseaudio se quedan: son variantes deliberadas. Sin esa salvedad los rebuilds habrían sido 8, incluido gnome-shell.

La regla general que sale de acá, y sirve para la próxima promoción:

Enlazar contra una variante y correr contra otra es legítimo cuando lo que cruza es un SONAME (misma ABI). Lo que no se puede es hidratar dos artefactos distintos en la misma ruta. Por eso pipewire se puede compartir y pulseaudio no.

Consecuencias inmediatas: mpv pasó a -Dpipewire=enabled (con ALSA detrás, para imágenes sin demonio) y wf-recorder se escribió y selló el mismo día.

⚠ EL TERCER DEFECTO DEL MÉTODO: la receta existe, es alcanzable, y aun así no sirve

El defecto anterior era «¿existe la receta?» ≠ «¿la alcanza el consumidor?». Al ir a escribir imv apareció un tercer eje, y es el peor de los tres porque las dos preguntas anteriores dan que sí:

La receta existe, el consumidor la alcanza, y la VARIANTE que se construyó no publica la ABI que hace falta.

imv pide OpenGL. mesa existe, está en el corpus, y cualquier receta lo alcanza. Pero:

La trampa fina, y es la parte que hay que recordar: mesa SÍ instala /usr/include/GL/gl.h (el header viene con -Dopengl=true). O sea que una app así compila entera y muere recién al LIGAR. Un triaje que mire headers da verde.

Qué costaría darlo vuelta (para que la decisión se tome con el número)

El camino que el propio meson.build de imv nombra —«libglvnd fallback for pure-wayland systems»— es autorar libglvnd (que provee libOpenGL.so/opengl.pc) y reconstruir mesa con -Dglvnd=true. Eso es radio 139 dependientes directos sobre mesa, y además convierte a libglvnd en pieza obligatoria de las cuatro imágenes: sin ella no hay EGL. Es una decisión de distro, no un ticket de visor de imágenes.

La salida, que resultó mejor que el original

swayimg cubre el mismo caso de uso sin tocar GL: rasteriza a un buffer y lo entrega por wl_shm. Sus deps requeridas (xkbcommon, fontconfig, freetype2, wayland, wayland-protocols) ya estaban todas en el corpus ⇒ cero recetas nuevas. Y trae un backend DRM que muestra imágenes en una TTY pelada, sin compositor, que es exactamente lo que le falta al instalador.

Entró primero como 4.7 —la última sin Lua— porque desde la 5.0 dependency('luajit') es obligatoria y no había receta (la lua5.2 del OSC de mpv es otra ABI). Se escribió luajit el mismo día y subió a 5.5. La cadena quedó verificada de punta a punta: swayimg -e reporta Lua 5.1 · jit=true · LuaJIT 2.1.1787165859, que es exactamente el relver de nuestra receta. luajit queda además disponible para mpv, que la prefiere sobre 5.2.

Regla que sale de acá, para el próximo triaje de apps: la pregunta no es «¿existe la receta?» ni sólo «¿la alcanza?», sino «¿la variante sellada publica el .pc y el .so que el consumidor pide?». Se contesta mirando el artefacto, no el catálogo.

Y como acá cada punto ciego se convierte en un guardián, esa pregunta ya tiene instrumento: scripts/provee.py, que indexa los .pc y las librerías de TODOS los artefactos sellados y contesta quién publica cada nombre y desde qué colas se alcanza. Los tres defectos del método caen con un solo comando:

scripts/provee.py --desde corpus gl opengl     # ✗ nadie lo publica  → exit 1  (el caso imv)
scripts/provee.py exiv2 librsvg                # ✓ existen, alcanzables SÓLO desde su cola

Es el hermano de build de vigia-sonames.py, que cubre la mitad de runtime. El triaje de la próxima app empieza por acá, no por contar makedepends contra recipes/*.toml.

El montón A quedó AGOTADO de cosas sin muro (2026-09-03)

Con zathura cerrada, se re-triaron con scripts/provee.py las tres candidatas que la tabla daba como baratas o dudosas. Las tres son muro, y ninguna lo era por el motivo que decía la tabla:

Todo lo que queda del montón A está detrás de UNA de cuatro decisiones, no de trabajo de recetas: GTK3 (inkscape-release, gimp, firefox), Qt6-en-el-corpus (obs), OpenGL de escritorio (imv, mupdf-gl) o X11 (chromium, mupdf-x11). Ver «los dos veredictos» abajo y el orden propuesto.

⚠ Lo que SÍ estaba roto y no era falta de recetas: imágenes sin terminal

Al terminar zathura se cruzó la membresía de los cuatro perfiles contra una lista de emuladores de terminal, editores y gestores de ficheros. El resultado no tiene nada que ver con el montón A y es más grave que cualquier app que falte:

perfilterminaleditorarchivos
escritorio-cosmiccosmic-termcosmic-editcosmic-files
escritorio-swayfootvim—
escritorio-gnome———
escritorio-kde———

Es la lección de foot otra vez y a mayor escala: sway llegó a 121/121 sin emulador de terminal porque nadie lo declaraba, y la métrica de clausura no lo puede ver porque mide lo DECLARADO.

GNOME arreglado el 2026-09-03 con CERO recetas nuevas: foot y helix viven en el corpus, ya estaban selladas y no las declaraba ningún perfil. foot es Wayland puro sobre xdg-shell (no pide protocolos de wlroots) así que corre bajo mutter; su único NEEDED es libc.so.

KDE es un hallazgo aparte y NO se tocó: tiene 22 apps con .desktop selladas en su cola y sin declarar —konsole, dolphin, kate, okular, spectacle, ark, gwenview, systemsettings, kcalc…— y además le faltan plasma-desktop, plasma-nm, plasma-pa, powerdevil y kscreen, o sea red, audio, energía y pantalla. Su perfil son 13 raíces de ARRANQUE EN METAL, no la imagen de escritorio completa (ésa se hidrató aparte, por hash). Reordenarlo es decisión del frente KDE.

Los dos veredictos que corrigen al ADR

Firefox NO es montón A

Su APKBUILD pide gtk+3.0-dev — Firefox no tiene backend GTK4 ni lo va a tener a corto plazo, y GTK3 es una de las tres deudas que el frente GNOME aparcó por diseño.

⚠ CORREGIDO 2026-09-03: el X11 de Firefox NO es un muro. Se listaba como tal por los libxt y libxcomposite del APKBUILD, pero en el árbol de Gecko todo el código X11 está detrás de CONFIG["MOZ_X11"] (widget/gtk/moz.build:131): es una PERILLA de build, no una dependencia estructural, y hay builds Wayland-only. El muro real de Firefox es GTK3, más un toolchain wasi (wasi-sdk, wasi-compiler-rt) que no existe en el corpus, más nodejs, icu, nss/nspr, clang/llvm/lld versionados y cbindgen.

La memoria del repo decía que a Firefox «le falta subir el techo MSRV». Eso es cierto y es lo menor: el bloqueo real es GTK3.

Consecuencia estratégica, que es la decisión que hay que tomar despierto: todos los navegadores del mundo Linux son o GTK3 (Firefox y derivados) o Chromium (que además arrastra Qt6 y GTK3 en Alpine). Entonces «tener navegador» no es un ticket del montón A: es elegir entre

  1. autorar el stack GTK3 —revierte una decisión de diseño y reabre GIMP/Inkscape de paso—, o
  2. darle el navegador a qorpa, o sea tratarlo como montón B: el navegador corre en la imagen ajena, hablando Wayland por socket.

La opción 2 es coherente con todo lo demás que ya se decidió, y tiene una ventaja que conviene nombrar: el navegador es justamente el proceso al que menos ganas dan de darle el sistema entero.

OBS no puede ser una app del corpus

Pide qt6-qtbase-dev, qt6-qtbase-private-dev y qt6-qtsvg-dev, y Qt6 vive sólo en incoming-kde/. Una receta resuelve sibling-first y después el catálogo padre: desde el corpus no se alcanza una cola. O sea que OBS hoy sólo puede existir dentro de la cola de KDE, y entonces no es una app de las cuatro imágenes sino una app de KDE. Súmese X11 (libx11, libxcb, libxcomposite, libxinerama) y 20 recetas nuevas (libdatachannel, librist, libsrt, mbedtls, rnnoise, x264, luajit, swig, vlc…).

Si lo que se quiere es grabar la pantalla —que es el 80% de para qué se instala OBS— eso ya está al alcance de la mano: wf-recorder sale con CERO recetas faltantes y cero muros, porque su cierre (wayland, ffmpeg, libdrm) quedó completo cuando entró mpv.

Orden propuesto, en reemplazo del del ADR

  1. ✅ mpv — hecho (2026-09-03).
  2. decisión de audio del corpus (el ⚠ de arriba). Destraba wf-recorder y le da a mpv su salida nativa. Es una decisión, no una campaña.
  3. wf-recorder — inmediato una vez tomada esa decisión; cubre el caso de uso principal de OBS.
  4. ✅ visor de imágenes — hecho (2026-09-03), pero con swayimg 4.7 y no imv: imv está bloqueada por el muro de GL de escritorio (sección de arriba). En las cuatro imágenes. 4b. ✅ luajit — hecha (2026-09-03). Destrabó swayimg 5.5 y queda disponible para mpv.
  5. ✅ zathura + backend PDF — hecho (2026-09-03). La sospecha de GTK3 era falsa: upstream VACIÓ girara (en 2026.07.18 son siete .c de utilidades, cero GTK) y toda la UI se mudó a zathura, que pide GTK4 >= 4.12 — que está en el corpus. Costó 5 recetas nuevas (girara, xxhash, poppler-glib, zathura, zathura-pdf-poppler) + 4 promociones gratis al corpus (json-glib, lcms2, glib-shared, pcre2-shared, bzip2-shared). No va en escritorio-kde: su backend necesita una poppler con ENABLE_GLIB=ON y esa imagen ya trae la de Qt6 vía okular; las dos instalan /usr/lib/libpoppler.so.146.
  6. ✅ terminal y editor en GNOME — hecho (2026-09-03), cero recetas: foot + helix del corpus, que ya estaban selladas y sin declarar. Ver la sección de arriba.
  7. DECISIÓN pendiente, y ahora bloquea a TODO lo que queda — no es trabajo de recetas: (a) autorar el stack GTK3 (destraba inkscape-release, GIMP y Firefox de una vez), (b) dárselo a qorpa como montón B, o (c) autorar la torre de bindings C++ (sigc++/glibmm/cairomm/pangomm/gtkmm-4) y seguir el master sin publicar de Inkscape. Hasta que se tome, el montón A no tiene ningún ticket barato: está agotado.
  8. Aparte y sin decisión: las 22 apps de KDE selladas y sin declarar (sección de arriba). No cuesta un build — cuesta acordarlo con el frente KDE.

OBS, Firefox, Chromium, GIMP y LibreOffice salen del montón A hasta que se decida GTK3 o qorpa. Que se llame montón A y no se pueda construir es peor que no tenerlo en la lista: es deuda fantasma, la misma figura que la terna de GNOME que había que sacar de targets.toml para que el grafo dejara de mentir.

Reproducir la medición

⚠ Ya no se reproduce así. Lo que sigue describe el método ORIGINAL, que es el que falló las tres veces documentadas arriba; se deja escrito para que se entienda de dónde salieron los números de la tabla. El triaje de una app nueva se hace hoy cruzando sus makedepends contra scripts/provee.py --desde corpus, que pregunta por el artefacto y no por el catálogo.

/tmp/.../triaje-apps.py fue un script de una sola vez; si hace falta repetirlo, lo que hace es: bajar el APKBUILD de cada candidata, extraer makedepends/depends, quitarles el sufijo -dev, y cruzar contra recipes/*.toml + recipes/incoming-*/*.toml, separando una lista fija de nombres que son muros de diseño (gtk+3.0, qt6-, lib X11, wasi-). No se guarda en scripts/ porque la lista de candidatas es del momento, no un invariante que valga la pena vigilar.


ADENDA 2026-09-13 — el censo, el muro de GL revisado, y la respuesta a «¿Electron?»

Sale de tres preguntas del usuario en una sesión: ¿es congruente empaquetar GenOffice?, ¿teníamos planeado compilar Electron?, ¿qué apps muy usadas valdría la pena replicar de cero en vez de incluirlas?. Las tres se contestaron midiendo, y dos mediciones corrigen cosas escritas arriba.

1. El censo: de ~50 apps muy usadas, el catálogo tiene 9

Cruzado contra los 1158 nombres de receta del disco (corpus + las cuatro colas):

Están (9): firefox, mpv, obs-studio, zathura, okular, gwenview, dolphin, ark, spectacle — y cinco de esas nueve son de KDE, o sea que sólo llegan a una de las cuatro imágenes.

No están: gimp, inkscape, krita, darktable, blender, audacity, kdenlive, shotcut, vlc, chromium, thunderbird, evolution, libreoffice, calligra, scribus, calibre, transmission, qbittorrent, deluge, filezilla, remmina, wireshark, virt-manager, gparted, keepassxc, bitwarden, signal-desktop, telegram-desktop, element-desktop, fractal, discord, slack, zoom, spotify, steam, lutris, wine, vscode, zed, sublime-text, obsidian, logseq, anki, gnucash, homebank, thunar, nautilus, pcmanfm, file-roller, flameshot.

2. ⚠ CORRECCIÓN al «tercer defecto del método»: el muro de GL se movió, y luego se disfrazó

La sección de arriba dice, con fecha 2026-09-03, que ninguna cola publica libGL.so ni gl.pc y que por eso toda app de GL de escritorio está bloqueada. Eso dejó de ser cierto al día siguiente y conviene no repetirlo de memoria:

scripts/provee.py --desde corpus gl opengl        # corrido el 2026-09-13
  gl        ✗ NADIE LO PUBLICA en ninguna cola
  opengl    ✓ corpus/libglvnd  ⇒  libOpenGL.so.0  opengl.pc

recipes/libglvnd.toml existe desde el 2026-09-04, escrita para OBS, y hace la partición que ordena el problema —que arriba estaba mezclado en una sola fila:

⚠ Pero la campaña quedó a medias, y eso vale más que la buena noticia. Las tres recetas de mesa siguen con -Dglvnd=false. Medido en el rootfs de KDE ya hidratado:

mesalibglvnd
/usr/lib/libEGL.so.1→ libEGL.so.1.0.0, 1.440.624 B (el driver)→ libEGL.so.1.1.0, 323.144 B (puro despacho)
libEGL_mesa.so.0 + /usr/share/glvnd/egl_vendor.d/✗✗

Las dos están declaradas en escritorio-kde ⇒ dos libEGL.so.1 distintos en la misma ruta. Hoy gana el de mesa —por orden de hidratación, no porque nada lo guarde— y el de glvnd queda huérfano al lado. La propia receta de libglvnd avisaba de esto: «mesa se reconstruye en la MISMA campaña, no después».

Y OBS, que fue la razón de escribir libglvnd, no la usa en runtime. libobs-opengl.so sale con NEEDED libEGL.so.1 y nada más; sus símbolos indefinidos son todos egl*, ni un gl[A-Z] sin resolver ⇒ su glad resuelve los punteros por eglGetProcAddress contra la EGL de mesa. glvnd está en la imagen satisfaciendo un requisito de enlace (el OpenGL::GL de CMake) y es inerte.

⇒ El estado real del muro: provee.py dice opengl ✓ y eso es verdad de enlace. En runtime no hay vendor de GL de escritorio registrado. Una app que de verdad llame por libOpenGL.so.0 encontraría una tabla de despacho vacía. El muro pasó de «no enlaza» a «enlaza y no corre», que es el modo de fallo más caro de esta casa. Terminarlo es re-sellar las tres mesa con -Dglvnd=true en un solo movimiento, y su precio ya está medido: radio grande sobre mesa y libglvnd pasa a ser pieza obligatoria de las cuatro imágenes.

Corolario para la tabla de arriba: darktable estaba mal clasificada como caso de GL. No lo es — su UI es GTK3+cairo y OpenCL es opcional. Su muro es la cola de deps: faltan lensfun, libgphoto2, openexr, imath, libraw, osm-gps-map, portmidi, y exiv2/librsvg/ libsecret/colord existen sólo en colas (inalcanzables desde el corpus). Es un frente de ~8 recetas, no un muro. blender sí es el caso de GL (pide 4.3 core).

3. Electron: nunca estuvo planeado, y el sustituto ya está construido

grep -rniE 'electron' docs/ recipes/ da cero coincidencias. No hay plan, ni ADR, ni ticket — y la tabla de arriba explica por qué sin nombrarlo: chromium es la peor fila del montón A (38 recetas

No existe un «Electron con base Firefox», y el SDD 26 §1 dice por qué

Gecko no tiene API de embebido en escritorio desde que murió XULRunner; GeckoView existe y es de Android. ⇒ un envoltorio de Gecko es necesariamente chrome-level.

Por eso los intentos históricos (Positron, Mozilla 2016) no cuajaron. Pero eso es exactamente lo que es atuq, y ya está hecho:

pieza de Electronequivalente en atuqestado
runtime ChromiumGecko como artefacto DERIVADO de firefox, sin fork de fuente✅ sellado, en las 4 imágenes
UI en HTML/JSomni.ja re-empacado + userChrome.css + distribution/extensions/✅ SDD 26 §4.g
lado Node (acceso al sistema)host de native messaging en Rust✅ §7.quater, 2026-09-10
IPCconnectNative, con el contrato MEDIDO✅ §7.bis

⇒ Lo que falta para que atuq sea una plataforma de apps y no sólo un navegador es chico: un modo SSB (una ventana, sin barra, con su icono y su .desktop) y una clase de receta «app de atuq». Eso es más barato que un solo port de Electron, y destraba una familia entera en vez de un programa.

GenOffice, concretamente

Electron + TypeScript + React, sidecar Rust para xlsx, Apache-2.0 salvo ee/ (licencia Enterprise aparte), en github.com/genspark-ai/genoffice. Su README dice que acepta cualquier endpoint OpenAI-compatible, incluidos servidores de modelo locales.

4. Qué vale la pena REHACER en vez de incluir

El criterio, porque sin criterio esto es una lista de deseos:

Rehacer sólo cuando el upstream está bloqueado por algo ESTRUCTURAL y el valor vive en un protocolo o formato abierto, no en el código. Si no: empaquetar, o enjaular.

Empaquetar — el muro cayó o nunca existió:

Enjaular (qorpa), no rehacer: vscode, discord, slack, signal, obsidian, zoom, spotify, steam, genoffice. Y blender, porque la jaula trae su propia mesa glibc y esquiva la campaña de glvnd por completo.

Rehacer desde tawasuyu — la lista corta que pasa el criterio:

  1. El shell de apps sobre atuq (§3). No es una app: es el runtime. Máximo apalancamiento.
  2. Bóveda / gestor de contraseñas. KeePassXC es Qt y Bitwarden es Electron, pero el valor está en el formato KDBX y en la integración con el navegador — y esa mitad, que es la cara en todos lados, ya está construida: agora (Ed25519), qullqa y el host de native messaging.
  3. Cliente Matrix nativo — sólo si fractal no construye. Medir antes de escribir.

El precedente que dice que el método funciona es propio: torrent (SDD 26 §6.9) y medios (§6.6) se rehicieron como demonios Rust en vez de empaquetar transmission y vlc, y los dos están cerrados.

⚠ Y la advertencia que el SDD 26 §2 ya pagó: portar un upstream vivo compra su cinta de correr («el costo de Zen no es el motor: es el rebase cada cuatro semanas»). Un port de GenOffice es una relación, no una receta.


ADENDA 2026-10-13 — la suite ArtCraft entra ENTERA, y lo que eso desmiente

Doce aplicaciones de escritorio en Rust puro sobre egui + wgpu (github.com/storytold, MIT OR Apache-2.0), construidas y medidas corriendo en una noche. Las recetas están en recipes/{word,grid,deck,photo,vector,design,light,cad,effect,sound,film,pdf}craft.toml.

El resultado, que es el que cambia el plan

selladasabren ventana y pintan
ArtCraft (12)12/1212/12

Medido con scripts/test-craft-ventana.sh <app> —rootfs de escritorio-sway + llvm18 + mesa-llvmpipe, sway headless, captura con grim— y la evidencia es contar colores, no leer el log: de 1.250 (vectorcraft) a 30.356 (lightcraft), con el azul del compositor desapareciendo de la foto en las doce. Cada una sella entre 11 y 26 min; 455-730 crates por app.

Lo que esto desmiente de la tabla de arriba

La fila libreoffice —59 recetas, GTK3 + Qt6 + X11, «no es un ticket, es un año»— medía bien el coste de ESA app, pero se leía como el coste de tener ofimática. No lo era. La misma función —textos, hoja con gráficos, presentaciones— entró como tres recetas cargo_vendor sin un solo muro de toolkit, y de yapa trajo el hueco que la terna ofimática ni cubre (vectorcraft ≈ Draw) y cinco categorías del censo de §1 que estaban en «no están»: gimp/krita (photocraft), darktable (lightcraft), inkscape (vectorcraft), kdenlive/shotcut (filmcraft), scribus (designcraft).

⚠ Lo que NO desmiente: fidelidad. Son 0.x —el README de wordcraft pone a prueba la beta «against real-world .docx files, keeping charts and SmartArt»— y portar un upstream vivo compra su cinta de correr. LibreOffice sigue siendo el patrón de fidelidad y no se autora: si hace falta de verdad, sale por qorpa, como se decidió para genoffice.

La línea divisoria de esta generación de apps, medida

egui + wgpu entra hoy; GPUI / Vulkan-only no.

egui-wgpu pide Backends::from_env().unwrap_or(PRIMARY | GL), o sea que GL está en el default: sin Vulkan, wgpu cae al camino EGL/GLES y pinta. Lo dice el log de las doce (backend: Gl … llvmpipe … 4.5 (Core Profile)) y lo confirma el control sin WGPU_BACKEND. lightcraft lo hace explícito: renderer wgpu (order ["vulkan", "gl", "glow"]). Zed es el contraejemplo: GPUI exige Vulkan 1.3 en Linux ⇒ en esta distro no arranca, y el desbloqueo es el mismo que ya pedía el ScreenCast de COSMIC: lavapipe (-Dvulkan-drivers=swrast).

Tres huecos de la IMAGEN que estas apps destaparon (ninguno es de ellas)

  1. No hay diálogo de ficheros EN SWAY —y esta entrada se corrigió dos veces, que es casi todo lo que tiene de interesante—. Lo que photocraft dice es file dialog backends: Findings { libdbus: None, file_chooser_portal: Some(false), zenity: false }, y de ahí salió primero un «no hay diálogo de ficheros» a secas: generalización de UNA imagen. Medido después: los tres escritorios grandes sí declaran un backend con FileChooser (xdg-desktop-portal-{kde,gnome,cosmic}, los tres sellados, con org.freedesktop.impl.portal.FileChooser en su .portal). El hueco es de sway y del corpus. ⚠ Y la sonda de la app no sirve como medida: contesta libdbus: None aunque libdbus-1.so esté presente, o sea que aplasta «no hay portal» y «no supe preguntar» en el mismo valor.

    La salida está MEDIDA de punta a punta (scripts/test-portal-ficheros.sh, 2026-10-13): sobre el rootfs de sway se montan xdg-desktop-portal + xdg-desktop-portal-cosmic —el backend de COSMIC es Rust sobre wgpu y no arrastra ni GTK ni Qt, a diferencia del de GNOME (gtk4 + libadwaita) y el de KDE (Qt6)— y se llama al método real:

    gdbus call … org.freedesktop.portal.FileChooser.OpenFile "" "Elegi un fichero" "{}"
    → (objectpath '/org/freedesktop/portal/desktop/request/1_5/t')
    árbol de sway → "app_id": "com.system76.CosmicFilesDialog"   · captura: 1.098 colores
    

    O sea: el diálogo abre, se ve y lista ficheros. Lo que hace falta para llevarlo a la imagen: (a) que las dos recetas sean alcanzables desde incoming-wlr (hoy viven en incoming-cosmic, y una cola no ve a su hermana), (b) json-glib, y (c) cuatro líneas de /usr/share/xdg-desktop-portal/sway-portals.conf, porque el .portal del backend trae UseIn=COSMIC y en una sesión sway no lo elegiría solo:

    [preferred]
    default=cosmic
    org.freedesktop.impl.portal.FileChooser=cosmic
    

    ⚠⚠ El punto (b) es el que casi me hace publicar un veredicto falso. Sin json-glib el núcleo del portal NO ARRANCA, y cada versión lo disfraza distinto: la 1.19.4 de la cola gnome dice Error relocating … json_parser_get_root: symbol not found (rc=127) y la 1.18.4 de kde/cosmic se cae con SIGSEGV pelado después de listar los backends, sin nombrar nada. Llegué a escribir que el artefacto estaba roto; con la capa de json-glib montada, las dos corren. Es la figura de siempre —sellado, presente, reproducible y sin arrancar— y la lección de método es que un arnés al que le falta la clausura de RUNTIME de lo que mide produce un veredicto que parece del artefacto. En las tres imágenes de escritorio esto no muerde porque json-glib entra por ser dep declarada del propio portal; en sway no entra nada de esto.

  2. OCHO de las doce se quedan sin portapapeles: su arboard no trae wayland-data-control, intenta X11 y expira (cadcraft, deckcraft, designcraft, effectcraft, filmcraft, lightcraft, pdfcraft, soundcraft). Las cuatro que sí lo traen: gridcraft, photocraft, vectorcraft, wordcraft. Medido en los BINARIOS corriendo, uno por uno, no en los manifiestos. Parche de una línea por app, y mejor aguas arriba que acá.

  3. Sin tarjeta de sonido degradan bien: soundcraft cae a «silent clock» y filmcraft dice no audio output device found. No es un hueco de la distro, es el LXC; se anota para que la próxima medición no lo lea como fallo.

Los tres son función instalada y apagada en silencio, que es la forma de fallo que esta casa persigue: la app abre, dibuja, y no puede abrir un fichero.

Cuatro premisas que la propia campaña desmintió (y cómo se vieron)

  1. El Cargo.lock lista lo POSIBLE, no lo que se construye. photocraft y filmcraft traen pipewire-sys/gtk-sys en el lock y ninguno se compila en Linux: entran por xcap (cfg(windows)) y muda (cfg(macos)). Un triaje por lock habría bloqueado la app más grande de la familia sin motivo.
  2. Los features de eframe del manifiesto de la app tampoco son los del build: son ADITIVOS y los enciende otro miembro del workspace. vectorcraft «no pedía wgpu» y corre sobre wgpu.
  3. El panic de zbus-lockstep-macros (la cicatriz de boveda/mirada-greeter) no se dispara en estas versiones: vectorcraft compiló atspi-common 0.13 sin el LOCKSTEP_XML_PATH y con el xml/schemas/ presente.
  4. provee.py contesta sobre el store que le den, y su default ./store en la jaula está VACÍO: dijo «alsa: NADIE LO PUBLICA» de un alsa-lib sellado en las dos máquinas. Un falso «nadie» se ve igual que un hueco real del catálogo.

Y una trampa vieja que volvió a morder, con el mismo disfraz de siempre: pdfcraft falló porque el cargo vendor de takana le pisó su propio vendor/ (cuatro crates hayro enganchados por [patch.crates-io] POR RUTA). El error hablaba de un .cargo-checksum.json que faltaba. Se arregla con cargo_vendor_dir, igual que boveda y mise; vectorcraft committea un vendor/ también y lo lleva ahora de forma preventiva.

El CONTROL de todo esto: photosuite, de otra gente

eolix/photosuite (1.110★, Apache-2.0, v0.9.20, 687 crates) hace lo mismo que photocraft con la misma cadena —eframe/egui 0.36 + wgpu— y lo hace otro equipo. Selló en una pasada (b3:a4d43550, hash predicho por el hub y confirmado por el worker) y pinta: 2.837 colores, app_id io.github.eolix.PhotoSuite. Importa porque convierte la frase «egui+wgpu entra en esta distro» de anécdota en medición: dos implementaciones independientes, el mismo resultado.

Y gana donde las doce pierden: trae arboard con wayland-data-control ⇒ sí tiene portapapeles, y winit con wayland-csd-adwaita ⇒ dibuja sus propias decoraciones donde el compositor no las pone. O sea que el hueco del portapapeles no es del toolkit: es una línea del manifiesto de cada app.

Lo que NO entra, y por qué — para no volver a triarlo

Y de esa lista, dos ya ENTRARON el mismo día (recipes/typst.toml, recipes/jj.toml):

Sigue pendiente lapce (38.918★, floem+wgpu, el editor gráfico que zed no puede ser acá; openssl/zlib/zstd ya tienen receta). servo es la única que cambiaría algo estructural: usa mozjs_sys —SpiderMonkey, que esta casa ya construye desde fuente dentro de firefox— así que, a diferencia de moli, no choca con la premisa. Sigue siendo campaña (1.148 crates), no receta de una tarde.