gioser

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 1572Shell 203Rust 109Markdown 993728 Sutichaykuna

🔊 audio FUNCIONANDO — el muro era libudev-zero, y ensancharlo de más rompía el vídeo

  Audio
   ├─ Devices:   48. HDA Intel                [alsa]
   ├─ Sinks:   * 52. HDA Intel Analog Stereo  [vol: 0.40]
   ├─ Sources: * 53. HDA Intel Analog Stereo  [vol: 1.00]

Y gvc con perfiles reales (`output:analog-stereo+input:analog-stereo`). Se fue el auto_null.

LA CAUSA: SPA pide el device `card` (alsa-udev.c:178-183), que no tiene major:minor, y
libudev-zero recorría SÓLO /sys/dev/{block,char} — sólo devices CON nodo. El device que
SPA necesita nunca entraba en la enumeración: no faltaba una propiedad, faltaba la mitad
del espacio de búsqueda.

**Y LA PRIMERA VERSIÓN DEL PARCHE ARREGLÓ EL AUDIO Y ROMPIÓ EL VÍDEO.** Recorría
/sys/class entero, y /sys/class/drm no trae sólo tarjetas: trae los CONECTORES
(card0-Virtual-1) y un fichero `version`, ninguno con nodo. Mutter los tomaba como
candidatos ⇒ `No available CRTC for monitor` + `Page flip failed: drmModeAtomicCommit:
Invalid argument` en bucle. Pantalla negra con el compositor vivo: el mismo síntoma de
antes por una causa nueva. El udev real también los enumera, pero les adjunta las
propiedades por las que mutter los descarta, y ésas libudev-zero no las sintetiza.

La lección, que vale para cualquier reimplementación parcial de una API: **ensanchar la
enumeración expone los huecos de PROPIEDADES a más consumidores. Los dos lados del
contrato tienen que crecer juntos.** El parche final lleva una LISTA ACOTADA de
subsistemas (`sound` hoy); agregar otro es una línea *y* una prueba de que el consumidor
lo necesita y no se rompe.

POR QUÉ NO eudev — y corrijo mi propia sugerencia de antes, que era la peor opción: el
repo ya lo había probado en otro frente y ROMPIÓ EL INPUT. Su libudev.so.1 pisa el de
libudev-zero y, sin udevd, no expone ID_INPUT ⇒ libinput ignora todos los dispositivos en
silencio. Verificación A/B en QEMU de aquel episodio: eudev = 0 «New device»,
libudev-zero = 5. eudev sin udevd es PEOR, y correr udevd contradice la arquitectura
(arje-zero es PID1). El arreglo va donde está el hueco.

Radio pagado: 51 sellados a deuda, 9 en el cierre GNOME (reconstruidos: libinput,
libgudev, colord, mutter, gnome-shell, libgdm, upower, pipewire, wireplumber). El resto
—base, cli, KDE— queda para el latido.

Captura actualizada: escritorio pintando (628 colores) Y audio, cero page-flip fallidos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

7f2fdd5a41c5a5ce2a2bbab3c3d547b6f71419b9 · ruwaq sergio · (2 killa ñawpaq) · tayta 5ca11de82 · kay commitpi sach'ata qhaway

Semántico sayk'uy

Mana ima sutichaypas t'ikrakurqanchu: qillqalla, yachachiylla utaq willaylla t'ikrakurqan.

4 qillqakuna · +152 −0

diff --git a/docs/evidencia/gnome-shell-qemu-2026-07-29.png b/docs/evidencia/gnome-shell-qemu-2026-07-29.png
index 32842460..f95b0ff4 100644
Binary files a/docs/evidencia/gnome-shell-qemu-2026-07-29.png and b/docs/evidencia/gnome-shell-qemu-2026-07-29.png differ
diff --git a/docs/runbooks/gnome-qemu-desktop.md b/docs/runbooks/gnome-qemu-desktop.md
index 7bfc0639..8e7372cb 100644
--- a/docs/runbooks/gnome-qemu-desktop.md
+++ b/docs/runbooks/gnome-qemu-desktop.md
@@ -177,6 +177,53 @@ Gvc-DEBUG: Updating sink: index=33 name='auto_null' description='Dummy Output'
 El `Failed to connect context: Connection refused` desapareció y gvc ve el servidor. El sink es
 `auto_null` («Dummy Output»), que es la respuesta honesta: la VM no tiene tarjeta de sonido.
 
+### 🔊 AUDIO FUNCIONANDO (cerrado): el muro era `libudev-zero`
+
+```
+Audio
+ ├─ Devices:   48. HDA Intel                      [alsa]
+ ├─ Sinks:   * 52. HDA Intel Analog Stereo        [vol: 0.40]
+ ├─ Sources: * 53. HDA Intel Analog Stereo        [vol: 1.00]
+```
+
+Y gvc lo ve con perfiles reales: `output:analog-stereo+input:analog-stereo (Current)`. Ya no hay
+`auto_null`.
+
+Hicieron falta TRES cosas, en este orden, y cada una parecía la última:
+
+1. **ALSA en el kernel** (`linux-metal` b3:613bca15). Estaba `-d SOUND -d SND`.
+2. **`wireplumber` + `lua`** — el gestor de sesión, sin el cual PipeWire acepta clientes y no enumera.
+3. **Parchear `libudev-zero`** para enumerar devices de CLASE. Esto era el muro real: SPA pide el
+   device `card` (`alsa-udev.c:178-183`), que no tiene `major:minor`, y libudev-zero recorría
+   **sólo** `/sys/dev/{block,char}` — o sea sólo devices CON nodo. **El device que SPA necesita nunca
+   entraba en la enumeración**: no faltaba una propiedad, faltaba la mitad del espacio de búsqueda.
+
+**⚠ Y la primera versión del parche, que recorría `/sys/class` ENTERO, arregló el audio y ROMPIÓ EL
+VÍDEO.** `/sys/class/drm` no trae sólo tarjetas: trae los conectores (`card0-Virtual-1`) y un fichero
+`version`, ninguno con nodo. Al entrar en la enumeración, mutter los toma como candidatos:
+
+```
+Failed to use linear monitor configuration: No available CRTC for monitor 'RHT QEMU Monitor'
+Page flip failed: drmModeAtomicCommit: Invalid argument      (en bucle)
+```
+
+Pantalla negra con el compositor vivo — el mismo cuadro de antes, por una causa nueva. El udev real
+también enumera esos conectores, pero les adjunta las propiedades por las que mutter los descarta, y
+ésas libudev-zero no las sintetiza (su tabla es fija y está orientada a input).
+
+**La lección, y vale para cualquier reimplementación parcial de una API: ensanchar la enumeración
+expone los huecos de PROPIEDADES a más consumidores. Los dos lados del contrato tienen que crecer
+juntos.** El parche final va con una **lista acotada de subsistemas** (`sound` hoy); agregar otro es
+una línea *y* una prueba de que el consumidor lo necesita y no se rompe.
+
+**Por qué no eudev**, que era el camino que parecía obvio: el repo ya lo había probado en otro frente y
+**rompió el input** — su `libudev.so.1` pisa el de libudev-zero y, sin udevd corriendo, no expone
+`ID_INPUT`, así que libinput ignora todos los dispositivos en silencio. Verificación A/B en QEMU: eudev
+= 0 «New device», libudev-zero = 5. eudev sin udevd es PEOR, y correr udevd contradice la arquitectura.
+
+Costo del parche: `yupana radio libudev-zero` = **51 sellados a deuda** (9 en el cierre GNOME, que se
+reconstruyeron acá; el resto —base, cli, KDE— quedan para el latido).
+
 **`wireplumber` cerrado** (b3:39a9a06c), y con él `lua` (b3:e6c35f98), que hubo que autorar: el
 Makefile de Lua **no produce `.so` ni `.pc`** —los agrega cada distro—, así que la receta enlaza la
 compartida a mano desde la `.a` con `--whole-archive` y escribe el pkg-config. `-fPIC` es obligatorio:
diff --git a/recipes/libudev-zero-scan-class.patch b/recipes/libudev-zero-scan-class.patch
new file mode 100644
index 00000000..1ea0ea0c
--- /dev/null
+++ b/recipes/libudev-zero-scan-class.patch
@@ -0,0 +1,96 @@
+libudev-zero: enumerar devices de CLASE para una lista ACOTADA de subsistemas.
+
+EL HUECO, medido en las fuentes de los dos lados. `udev_enumerate_scan_devices` recorre
+**exclusivamente** `/sys/dev/block` y `/sys/dev/char` (udev_enumerate.c:281), o sea sólo devices que
+tienen `major:minor` y por tanto nodo en /dev. El udev real recorre `/sys/class` entero. La diferencia
+es invisible para casi todos los consumidores y letal para los que piden un device de CLASE.
+
+CÓMO SE DESTAPÓ: el audio. `spa/plugins/alsa/alsa-udev.c:178-183` de PipeWire pide el device **card**
+—`/sys/devices/.../sound/card0`—, que es de clase: sin major:minor, sin nodo. Con ALSA encendido en el
+kernel, `/dev/snd/controlC0` presente y `/sys/class/sound` listando `card0`, `wpctl status` daba
+`Devices:` vacío **porque card0 nunca entraba en la enumeración**. No era una propiedad que faltara:
+faltaba la mitad del espacio de búsqueda.
+
+⚠ POR QUÉ UNA LISTA Y NO `/sys/class` ENTERO — se probó global y ROMPIÓ EL VÍDEO
+La primera versión de este parche recorría todo `/sys/class`. El audio pasó a funcionar
+(`48. HDA Intel [alsa]`, sink y source reales en vez de `auto_null`) **y la pantalla se puso negra**:
+
+    libmutter-WARNING: Failed to use linear monitor configuration:
+                       No available CRTC for monitor 'RHT QEMU Monitor' not found
+    libmutter-WARNING: Page flip failed: drmModeAtomicCommit: Invalid argument   (en bucle)
+
+`/sys/class/drm` no contiene sólo las tarjetas: contiene los CONECTORES (`card0-Virtual-1`,
+`card1-eDP-1`…) y un fichero `version`, ninguno con nodo. Al aparecer en la enumeración, mutter los
+toma como candidatos y no encuentra CRTC. **El udev real también los enumera** — pero además les
+adjunta las propiedades por las que mutter los descarta, y ésas libudev-zero no las sintetiza (su
+tabla es fija y está orientada a input: `ID_INPUT*`, `EV`, `KEY`, `ABS`, `REL`).
+
+La lección, que vale para cualquier reimplementación parcial de una API: **ensanchar la enumeración
+expone los huecos de PROPIEDADES a más consumidores.** Los dos lados del contrato tienen que crecer
+juntos.
+
+Así que se agrega subsistema por subsistema, con un caso de uso medido detrás de cada uno. Hoy:
+`sound`. Agregar otro es una línea en `class_subsystems[]` **y una prueba de que el consumidor lo
+necesita y no se rompe**.
+
+POR QUÉ ESTO Y NO eudev, y acá el repo ya tenía la respuesta escrita: empaquetar eudev se probó en otro
+frente y **rompió el input** — su `libudev.so.1` pisa el de libudev-zero y, SIN udevd corriendo, eudev
+no expone `ID_INPUT`, así que libinput ignora TODOS los dispositivos EN SILENCIO (`udev_assign_seat`
+devuelve OK con 0 devices). Se resolvió restaurando libudev-zero, con verificación A/B en QEMU: eudev =
+0 «New device», libudev-zero = 5. Ver [[mirada-usb-nvidia]]. eudev sin udevd es PEOR, y correr udevd
+contradice la arquitectura (arje-zero es PID1, sin udevd en el camino).
+
+⚠ COSTO: `yupana radio libudev-zero` = **51 artefactos sellados cayendo a deuda** en las cinco
+imágenes (9 en el cierre GNOME). Una variante NO era opción: dos `libudev.so.1` en el mismo rootfs es
+exactamente el choque del episodio de eudev.
+
+diff -Naur a/udev_enumerate.c b/udev_enumerate.c
+--- a/udev_enumerate.c
++++ b/udev_enumerate.c
+@@ -276,6 +276,37 @@
+     return 1;
+ }
+ 
++/* Enumera devices de CLASE (los que no tienen major:minor y por tanto no aparecen bajo
++ * /sys/dev/{block,char}) para una LISTA ACOTADA de subsistemas.
++ *
++ * Por que una lista y no /sys/class entero: se probo global y ROMPIO EL VIDEO. /sys/class/drm no
++ * contiene solo las tarjetas: contiene los CONECTORES (card0-Virtual-1, card1-eDP-1...) y un fichero
++ * `version`, ninguno con nodo. Al aparecer en la enumeracion, mutter los trata como candidatos y
++ * termina en `No available CRTC for monitor` + `Page flip failed: drmModeAtomicCommit: Invalid
++ * argument` en bucle: pantalla negra con el compositor vivo. El udev real tambien los enumera, pero
++ * ademas les adjunta las propiedades por las que mutter los descarta, y esas propiedades libudev-zero
++ * no las sintetiza. O sea: ensanchar la enumeracion expone los huecos de PROPIEDADES a mas
++ * consumidores.
++ *
++ * Asi que se agrega subsistema por subsistema, con un caso de uso medido detras de cada uno:
++ *   sound  -> PipeWire pide el device `card` de la tarjeta (alsa-udev.c:178-183), que no tiene nodo.
++ *             Sin esto `wpctl status` da `Devices:` vacio con ALSA funcionando y /dev/snd poblado.
++ */
++static const char *const class_subsystems[] = { "sound", NULL };
++
++static int scan_class(struct udev_enumerate *udev_enumerate)
++{
++    char subsystem_path[PATH_MAX];
++    int i;
++
++    for (i = 0; class_subsystems[i]; i++) {
++        snprintf(subsystem_path, sizeof(subsystem_path), "/sys/class/%s", class_subsystems[i]);
++        scan_devices(udev_enumerate, subsystem_path);
++    }
++
++    return 1;
++}
++
+ int udev_enumerate_scan_devices(struct udev_enumerate *udev_enumerate)
+ {
+     const char *path[] = { "/sys/dev/block", "/sys/dev/char", NULL };
+@@ -291,6 +322,8 @@
+         }
+     }
+ 
++    scan_class(udev_enumerate);
++
+     return 0;
+ }
+ 
diff --git a/recipes/libudev-zero.toml b/recipes/libudev-zero.toml
index 2f48be36..43d61ee7 100644
--- a/recipes/libudev-zero.toml
+++ b/recipes/libudev-zero.toml
@@ -1,3 +1,11 @@
+# ⚠ LLEVA UN PARCHE: `libudev-zero-scan-class.patch`. Upstream enumera SÓLO `/sys/dev/block` y
+# `/sys/dev/char`, o sea sólo devices con `major:minor` y nodo en /dev; el udev real recorre
+# `/sys/class` entero. La diferencia es invisible para casi todos los consumidores y letal para los que
+# piden un device de CLASE: PipeWire busca el `card` de una tarjeta de sonido, que no tiene nodo, y sin
+# él `wpctl status` daba `Devices:` vacío con ALSA funcionando y /dev/snd poblado. El parche explica por
+# qué se arregla ACÁ y no empaquetando eudev (que sin udevd es PEOR: rompió el input en otro frente).
+# Ojo al radio: `yupana radio libudev-zero` = 51 sellados a deuda.
+#
 # libudev-zero 1.0.4 — implementación de libudev SIN udevd (lee /sys y /dev directo). Encaja con la
 # filosofía no-systemd de tawasuyu (arje es el init, sin daemon udev). La pide `libinput` (vía
 # input-sys/libudev-sys de smithay) para enumerar dispositivos de entrada/DRM. Provee libudev.so +
@@ -9,6 +17,7 @@ version = "1.0.4"
 [source]
 tarball = "https://github.com/illiliti/libudev-zero/archive/refs/tags/1.0.4.tar.gz"
 sha256  = "10148cfd6047d387bf71eca72cd19c177084224d96434a118d8def6e0a3d6316"
+patches = ["libudev-zero-scan-class.patch"]
 
 [build]
 compiler = "zig-cc"