gnome onda 3: 7 recetas selladas y mutter reducido a UN muro (la C-ABI de sd-login)
Selladas: libgudev b3:a767a231, libusb b3:a8507df1, libgusb b3:4903731d,
colord b3:a0c30aac, libei b3:0537145b, py3-jinja2 b3:e8f28561, py3-markupsafe b3:3ef224ac.
EL HALLAZGO DE LA TANDA — colord destapó una regla que vale para todo el frente:
su meson construye libcolord/libcolorhug como shared_library() pase lo que pase, y con
--prefer-static cada .so se tragaba una copia de la glib ESTÁTICA ⇒ una tabla de GType por
objeto compartido. Síntoma: las herramientas que el propio build compila (cd-create-profile,
cd-it8) enlazaban, arrancaban, y morían con `assertion 'G_IS_FILE (file)' failed` + SIGSEGV.
Yo había anotado a lcms2 como sospechoso; era falso y quedó corregido en la receta. Pasar
colord a la ISLA DINÁMICA (glib .so, un solo registro de tipos) lo selló con CERO segfaults
y los 9 perfiles ICC generándose bien. mutter va por el mismo camino: gnome-shell dlopea
libmutter vía gjs, así que también es isla dinámica.
Lecciones menores, todas medidas:
- -Dremote_desktop=false NO evita libei: mutter 48.8 la pide incondicional (meson.build:130),
y del lado SERVIDOR (libeis). Sólo se llevó pipewire.
- libusb necesita --with-pic para poder vivir dentro de un .so — mismo remedio y misma razón
que recipes/libffi.toml, que lo aprendió con Mesa.
- La cadena de build más larga y menos obvia: mutter → libei → jinja2 → markupsafe.
- gvdb NO es frontera: viene dentro del tarball de mutter como subproyecto.
- La mesa del corpus es EGL/GLES sin GL de escritorio (coherente con Wayland-only) ⇒
mutter va con -Dopengl=false.
- gnome-desktop-4.pc arrastra xkeyboard-config/iso-codes/libseccomp: patrón .pc Requires →
[deps].build.
MURO QUE QUEDA, uno solo y bien delimitado: mutter exige un proveedor de logind POR C-ABI
(libsystemd o libelogind por pkg-config), no por D-Bus. Usa ~10 funciones de sd-login:
sd_pid_get_session/get_cgroup/get_user_unit, sd_session_get_type/is_active/get_class,
sd_uid_get_sessions/get_display. Y no se puede esquivar: -Dudev=false exige -Dlogind=false
(meson.build:257) y sin udev+logind no hay backend nativo KMS, o sea no hay compositor real.
Esto CORRIGE lo anotado en el frente ("no hace falta la C-ABI sd-login, los escritorios
consultan login1 por D-Bus"): cierto para los clientes, falso para mutter.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Delta semántico
Ninguna definición cambió: el cambio es de texto, documentación o datos.
9 archivos · +425 −74
- docs/state/build-state-gnome.json +211 −68
- recipes/incoming-gnome/colord.toml +14 −3
- recipes/incoming-gnome/libei.toml +35 −0
- recipes/incoming-gnome/libgudev.toml +31 −0
- recipes/incoming-gnome/libgusb.toml +31 −0
- recipes/incoming-gnome/libusb.toml +34 −0
- recipes/incoming-gnome/mutter.toml +3 −3
- recipes/incoming-gnome/py3-jinja2.toml +33 −0
- recipes/incoming-gnome/py3-markupsafe.toml +33 −0
diff --git a/docs/state/build-state-gnome.json b/docs/state/build-state-gnome.json
index ee54968d..4d819be5 100644
--- a/docs/state/build-state-gnome.json
+++ b/docs/state/build-state-gnome.json
@@ -1,50 +1,50 @@
{
"schema": "hammer-build-state/1",
"totals": {
- "recipes": 802,
- "nodes": 802,
- "sealed": 791,
- "never": 9,
+ "recipes": 807,
+ "nodes": 807,
+ "sealed": 797,
+ "never": 8,
"debt": 2
},
"by_class": {
- "c": 168,
+ "c": 173,
"go": 362,
"gui": 40,
"kernel": 6,
"rust": 226
},
"debt_by_class": {
- "c": 5,
+ "c": 4,
"gui": 5,
"rust": 1
},
"by_queue": {
"corpus": {
- "sealed": 759,
+ "sealed": 758,
"never": 2,
"debt": 2
},
"incoming-gnome": {
- "sealed": 32,
- "never": 7
+ "sealed": 39,
+ "never": 6
}
},
"by_profile": {
"base": {
"raices": 26,
- "clausura": 51,
- "sealed": 51
+ "clausura": 50,
+ "sealed": 50
},
"cli": {
"raices": 46,
- "clausura": 74,
- "sealed": 74
+ "clausura": 73,
+ "sealed": 73
},
"escritorio-gnome": {
"raices": 9,
- "clausura": 88,
- "sealed": 82,
+ "clausura": 96,
+ "sealed": 90,
"never": 6
},
"escritorio-mirada": {
@@ -54,7 +54,7 @@
"debt": 2
}
},
- "sin_perfil": 650,
+ "sin_perfil": 648,
"orphan_deps": [],
"topo_ok": true,
"nodes": {
@@ -2251,28 +2251,37 @@
"colord": {
"iname": "colord",
"version": "1.4.7",
- "link": "static",
+ "link": "dynamic",
"compiler": "zig-cc",
"deps": [
"meson",
"samurai",
"python3",
"pkgconf",
+ "gettext-tiny",
"glib",
"lcms2",
"sqlite",
+ "libgusb",
+ "libgudev",
+ "libudev-zero",
+ "libusb",
+ "json-glib",
+ "hwdata",
"libffi",
"pcre2",
"zlib"
],
"cls": "c",
"queue": "incoming-gnome",
- "hash": "b3:d3b640177ad9de0247b71d33dab6990343ff4e1870a2404b1076c49048d681eb",
- "state": "never",
+ "hash": "b3:a0c30aacb29e80bc13e3cf0246adf0f05c53668e2c7b9c7cc54e1f496db067c7",
+ "state": "sealed",
"blocked_by": [],
- "unblocks": 0,
- "perfiles": [],
- "dependientes_total": 0
+ "unblocks": 1,
+ "perfiles": [
+ "escritorio-gnome"
+ ],
+ "dependientes_total": 1
},
"committed": {
"iname": "committed",
@@ -4216,12 +4225,12 @@
"hash": "b3:56959e5941c8240a888a148af0bc2a697d267627e7c42e4c8e76fda5dd2367dc",
"state": "sealed",
"blocked_by": [],
- "unblocks": 0,
+ "unblocks": 1,
"perfiles": [
"escritorio-gnome",
"escritorio-kde"
],
- "dependientes_total": 78
+ "dependientes_total": 80
},
"gh": {
"iname": "gh",
@@ -4704,11 +4713,11 @@
"hash": "b3:f6ccdf98b91158f9f1e071a278f4b21b4700b3594f2425458ef723a76c7b98e4",
"state": "sealed",
"blocked_by": [],
- "unblocks": 7,
+ "unblocks": 6,
"perfiles": [
"escritorio-gnome"
],
- "dependientes_total": 20
+ "dependientes_total": 23
},
"glib-introspected": {
"iname": "glib-introspected",
@@ -4927,7 +4936,7 @@
],
"cls": "gui",
"queue": "incoming-gnome",
- "hash": "b3:03cdccb023af0a8af1c7eb7a0c11fd5a3363101526fa342d8a0b641fec30ba6f",
+ "hash": "b3:514266ce96a20b8fce66b47caa4e5dc09e7d3c83456b0fce2eab8fd7fa96a69a",
"state": "never",
"blocked_by": [
"mutter"
@@ -6614,7 +6623,7 @@
"perfiles": [
"escritorio-gnome"
],
- "dependientes_total": 2
+ "dependientes_total": 4
},
"hyperfine": {
"iname": "hyperfine",
@@ -6796,11 +6805,11 @@
"hash": "b3:c447da2987354cd6e86edd24855239ed21da30ad8a4e01d2859eee8895cb70b7",
"state": "sealed",
"blocked_by": [],
- "unblocks": 0,
+ "unblocks": 1,
"perfiles": [
"escritorio-gnome"
],
- "dependientes_total": 1
+ "dependientes_total": 2
},
"istioctl": {
"iname": "istioctl",
@@ -6973,7 +6982,7 @@
"perfiles": [
"escritorio-gnome"
],
- "dependientes_total": 1
+ "dependientes_total": 3
},
"jsonnet-bundler": {
"iname": "jsonnet-bundler",
@@ -7865,7 +7874,7 @@
"hash": "b3:07fbf8a1da5b346fe394bebe512cbc0e8acc9b509c3614b462329fe70b1a73b8",
"state": "sealed",
"blocked_by": [],
- "unblocks": 2,
+ "unblocks": 1,
"perfiles": [
"escritorio-gnome"
],
@@ -8120,6 +8129,36 @@
],
"dependientes_total": 134
},
+ "libei": {
+ "iname": "libei",
+ "version": "1.5.0",
+ "link": "dynamic",
+ "compiler": "zig-cc",
+ "deps": [
+ "meson",
+ "samurai",
+ "python3",
+ "pkgconf",
+ "py3-jinja2",
+ "py3-markupsafe",
+ "libevdev",
+ "libxkbcommon",
+ "glib",
+ "libffi",
+ "pcre2",
+ "zlib"
+ ],
+ "cls": "c",
+ "queue": "incoming-gnome",
+ "hash": "b3:0537145b86a96fb93c86df447e7c309f5851786b75f7f3eced525bef522c8c3f",
+ "state": "sealed",
+ "blocked_by": [],
+ "unblocks": 1,
+ "perfiles": [
+ "escritorio-gnome"
+ ],
+ "dependientes_total": 1
+ },
"libepoxy": {
"iname": "libepoxy",
"version": "1.5.10",
@@ -8159,12 +8198,12 @@
"hash": "b3:0cbb4b4d3619b4b8971fd4c66f3416bdb9eecbb44004ee5a3691843f511b8fad",
"state": "sealed",
"blocked_by": [],
- "unblocks": 1,
+ "unblocks": 2,
"perfiles": [
"escritorio-gnome",
"escritorio-mirada"
],
- "dependientes_total": 2
+ "dependientes_total": 4
},
"libevent": {
"iname": "libevent",
@@ -8196,13 +8235,13 @@
"hash": "b3:2f6d62862c9978c79a5fc593625e110c6947475b8a6a600efa2c95b83033a6b1",
"state": "sealed",
"blocked_by": [],
- "unblocks": 9,
+ "unblocks": 8,
"perfiles": [
"escritorio-gnome",
"escritorio-kde",
"escritorio-mirada"
],
- "dependientes_total": 69
+ "dependientes_total": 72
},
"libgcrypt": {
"iname": "libgcrypt",
@@ -8245,6 +8284,62 @@
],
"dependientes_total": 4
},
+ "libgudev": {
+ "iname": "libgudev",
+ "version": "238",
+ "link": "static",
+ "compiler": "zig-cc",
+ "deps": [
+ "meson",
+ "samurai",
+ "python3",
+ "pkgconf",
+ "glib",
+ "libudev-zero",
+ "libffi",
+ "pcre2",
+ "zlib"
+ ],
+ "cls": "c",
+ "queue": "incoming-gnome",
+ "hash": "b3:a767a2310ab7cc4840290f5a8549e64210c142f415e01d791e05b96e54a892e4",
+ "state": "sealed",
+ "blocked_by": [],
+ "unblocks": 1,
+ "perfiles": [
+ "escritorio-gnome"
+ ],
+ "dependientes_total": 2
+ },
+ "libgusb": {
+ "iname": "libgusb",
+ "version": "0.4.9",
+ "link": "static",
+ "compiler": "zig-cc",
+ "deps": [
+ "meson",
+ "samurai",
+ "python3",
+ "pkgconf",
+ "glib",
+ "libusb",
+ "json-glib",
+ "hwdata",
+ "libffi",
+ "pcre2",
+ "zlib"
+ ],
+ "cls": "c",
+ "queue": "incoming-gnome",
+ "hash": "b3:4903731df83a46fe04dd182936fce2d20daa860c1d0bbae550bb063cc732f96f",
+ "state": "sealed",
+ "blocked_by": [],
+ "unblocks": 1,
+ "perfiles": [
+ "escritorio-gnome"
+ ],
+ "dependientes_total": 2
+ },
"libinput": {
"iname": "libinput",
"version": "1.27.1",
@@ -8427,11 +8522,11 @@
"hash": "b3:b3da6b8031d75e5220a1622c94402b9a02156507794655a131e9bbaad1386f9a",
"state": "sealed",
"blocked_by": [],
- "unblocks": 0,
+ "unblocks": 1,
"perfiles": [
"escritorio-gnome"
],
- "dependientes_total": 1
+ "dependientes_total": 2
},
"libsodium": {
"iname": "libsodium",
@@ -8525,7 +8620,7 @@
"hash": "b3:bf6607effbb9bcfa3cb1eb2ffe37d8a06a90702431b1b4c233c1815977455417",
"state": "sealed",
"blocked_by": [],
- "unblocks": 1,
+ "unblocks": 2,
"perfiles": [
"base",
"cli",
@@ -8533,7 +8628,7 @@
"escritorio-kde",
"escritorio-mirada"
],
- "dependientes_total": 18
+ "dependientes_total": 21
},
"libusb": {
"iname": "libusb",
@@ -8541,20 +8636,18 @@
"link": "static",
"compiler": "zig-cc",
"deps": [
- "linux-headers",
"make"
],
"cls": "c",
- "queue": "corpus",
- "hash": "b3:89adafa14aa5f7e12c22f05c14d5715a146a92bb43496451c3f01bae7a6bdf0c",
+ "queue": "incoming-gnome",
+ "hash": "b3:a8507df1a20e98511afce4f28dcf568ec8f6f531f810d4c67dab451ac66a1b3b",
"state": "sealed",
"blocked_by": [],
- "unblocks": 0,
+ "unblocks": 1,
"perfiles": [
- "base",
- "cli"
+ "escritorio-gnome"
],
- "dependientes_total": 1
+ "dependientes_total": 3
},
"libuv": {
"iname": "libuv",
@@ -8613,7 +8706,7 @@
"escritorio-gnome",
"escritorio-mirada"
],
- "dependientes_total": 14
+ "dependientes_total": 15
},
"libxml2": {
"iname": "libxml2",
@@ -8984,7 +9077,7 @@
"escritorio-kde",
"escritorio-mirada"
],
- "dependientes_total": 87
+ "dependientes_total": 88
},
"mako": {
"iname": "mako",
@@ -9334,7 +9427,7 @@
"hash": "b3:b45037ebba3fdbf0361ef0fcc61dee5c9795b08c1ea4a0231aafc7dc45bdaf85",
"state": "sealed",
"blocked_by": [],
- "unblocks": 7,
+ "unblocks": 6,
"perfiles": [
"base",
"cli",
@@ -9342,7 +9435,7 @@
"escritorio-kde",
"escritorio-mirada"
],
- "dependientes_total": 82
+ "dependientes_total": 85
},
"micro": {
"iname": "micro",
@@ -9697,7 +9790,7 @@
"mutter": {
"iname": "mutter",
"version": "48.8",
- "link": "static",
+ "link": "dynamic",
"compiler": "zig-cc",
"deps": [
"meson",
@@ -9736,11 +9829,23 @@
"atk",
"lcms2",
"libdisplay-info",
- "hwdata"
+ "hwdata",
+ "colord",
+ "libgudev",
+ "libudev-zero",
+ "libgusb",
+ "libusb",
+ "sqlite",
+ "gettext-tiny",
+ "libei",
+ "libevdev",
+ "xkeyboard-config",
+ "iso-codes",
+ "libseccomp"
],
"cls": "c",
"queue": "incoming-gnome",
- "hash": "b3:83b078248b1ab914e447b68ff5cff94ab57f102dc35841d1250dfcd5a7aeafb7",
+ "hash": "b3:590449288ebc80117f983579ebb736a174482910a72a6135d7c2b84a31ca4e13",
"state": "never",
"blocked_by": [],
"unblocks": 1,
@@ -10620,14 +10725,14 @@
"hash": "b3:06ad7580fd1d61db1822a5682dc0a9cef359329e4b4cf6bfa6c6dc507973f47d",
"state": "sealed",
"blocked_by": [],
- "unblocks": 7,
+ "unblocks": 6,
"perfiles": [
"base",
"cli",
"escritorio-gnome",
"escritorio-kde"
],
- "dependientes_total": 55
+ "dependientes_total": 58
},
"peco": {
"iname": "peco",
@@ -10802,7 +10907,7 @@
"hash": "b3:f0b5d0b1cb4321b3c61f24a871b9a58da3ebb7eb903af4015e35bd41a6465437",
"state": "sealed",
"blocked_by": [],
- "unblocks": 9,
+ "unblocks": 8,
"perfiles": [
"base",
"cli",
@@ -10810,7 +10915,7 @@
"escritorio-kde",
"escritorio-mirada"
],
- "dependientes_total": 297
+ "dependientes_total": 300
},
"plow": {
"iname": "plow",
@@ -11082,6 +11187,42 @@
"perfiles": [],
"dependientes_total": 0
},
+ "py3-jinja2": {
+ "iname": "py3-jinja2",
+ "version": "3.1.6",
+ "link": "static",
+ "compiler": "zig-cc",
+ "deps": [
+ "py3-markupsafe"
+ ],
+ "cls": "c",
+ "queue": "incoming-gnome",
+ "hash": "b3:e8f28561f52a03b81bcb26f0faee94b9b8ab0898c2f0bb76d0fd251d739ac3c7",
+ "state": "sealed",
+ "blocked_by": [],
+ "unblocks": 0,
+ "perfiles": [
+ "escritorio-gnome"
+ ],
+ "dependientes_total": 1
+ },
+ "py3-markupsafe": {
+ "iname": "py3-markupsafe",
+ "version": "3.0.2",
+ "link": "static",
+ "compiler": "zig-cc",
+ "deps": [],
+ "cls": "c",
+ "queue": "incoming-gnome",
+ "hash": "b3:3ef224aca70fcf973b8bbbd2e6e20051d6f14782cb6023bd3e52833b78d95d05",
+ "state": "sealed",
+ "blocked_by": [],
+ "unblocks": 0,
+ "perfiles": [
+ "escritorio-gnome"
+ ],
+ "dependientes_total": 2
+ },
"py3-setuptools": {
"iname": "py3-setuptools",
"version": "75.8.2",
@@ -11113,7 +11254,7 @@
"hash": "b3:27889026076514935b53d486951792d08ed7fd5210b44e7f92979c03c96be665",
"state": "sealed",
"blocked_by": [],
- "unblocks": 7,
+ "unblocks": 6,
"perfiles": [
"base",
"cli",
@@ -11121,7 +11262,7 @@
"escritorio-kde",
"escritorio-mirada"
],
- "dependientes_total": 240
+ "dependientes_total": 243
},
"qrcp": {
"iname": "qrcp",
@@ -11659,7 +11800,7 @@
"hash": "b3:b8fca71dd92577d3a44ef01a7d5779d86473553199e0c24736438cdc846e5199",
"state": "sealed",
"blocked_by": [],
- "unblocks": 7,
+ "unblocks": 6,
"perfiles": [
"base",
"cli",
@@ -11667,7 +11808,7 @@
"escritorio-kde",
"escritorio-mirada"
],
- "dependientes_total": 214
+ "dependientes_total": 217
},
"sassc": {
"iname": "sassc",
@@ -12242,8 +12383,10 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 1,
- "perfiles": [],
- "dependientes_total": 1
+ "perfiles": [
+ "escritorio-gnome"
+ ],
+ "dependientes_total": 2
},
"ssh-vault": {
"iname": "ssh-vault",
@@ -14168,11 +14311,11 @@
"hash": "b3:bcf9b766d13a579583e1dcdc18128990aa33dbed539a754b90cd78a5dc4b481c",
"state": "sealed",
"blocked_by": [],
- "unblocks": 0,
+ "unblocks": 1,
"perfiles": [
"escritorio-gnome"
],
- "dependientes_total": 1
+ "dependientes_total": 2
},
"xorriso": {
"iname": "xorriso",
@@ -14414,7 +14557,7 @@
"hash": "b3:28522f99f433496dbaacfa309f8e937d6526ac3c8db34b4a94b18e22656dcd33",
"state": "sealed",
"blocked_by": [],
- "unblocks": 10,
+ "unblocks": 9,
"perfiles": [
"base",
"cli",
@@ -14422,7 +14565,7 @@
"escritorio-kde",
"escritorio-mirada"
],
- "dependientes_total": 93
+ "dependientes_total": 96
},
"zlib-ng": {
"iname": "zlib-ng",
diff --git a/recipes/incoming-gnome/colord.toml b/recipes/incoming-gnome/colord.toml
index 1f3c10f5..26335efb 100644
--- a/recipes/incoming-gnome/colord.toml
+++ b/recipes/incoming-gnome/colord.toml
@@ -27,10 +27,21 @@ sha256 = "de02d9910634ae159547585cec414e450f711c27235453b4f9b38a9f2361a653"
[build]
compiler = "zig-cc"
target = "x86_64-linux-musl"
-link = "static"
+#
+# COLORD VA A LA ISLA DINÁMICA, y no por gusto: su meson construye libcolord/libcolordprivate/
+# libcolorhug como shared_library() PASE LO QUE PASE — -Ddefault_library=static no las hace
+# estáticas. Con --prefer-static, cada uno de esos .so se tragaba una copia de la glib ESTÁTICA, o
+# sea una tabla de GType por objeto compartido. El síntoma, medido el 2026-07-27, fue exactamente el
+# que da esa duplicación: las herramientas que el propio build compila (cd-create-profile, cd-it8)
+# enlazan y arrancan, pero mueren con `assertion 'G_IS_FILE (file)' failed` y acto seguido SIGSEGV —
+# el tipo GFile registrado en una copia de glib no es el mismo que el que valida la otra.
+# Enlazar la glib .so (la cola GNOME la provee: default_library=both) deja UN registro de tipos, que
+# es lo que GObject asume. Es la misma razón por la que glib/gtk4/gobject-introspection ya son isla
+# dinámica en este frente: la introspección no fue el único motivo, sólo el primero que lo obligó.
+link = "dynamic"
[build.phases]
-configure = "PKG_CONFIG_PATH=/usr/lib/pkgconfig PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --buildtype=release --wrap-mode=nodownload --prefer-static -Ddefault_library=static -Ddaemon=false -Dsystemd=false -Dudev_rules=false -Dargyllcms_sensor=false -Dbash_completion=false -Dintrospection=false -Dvapi=false -Dsane=false -Dtests=false -Dinstalled_tests=false -Dman=false -Ddocs=false -Dc_args=-Wno-error=date-time"
+configure = "PKG_CONFIG_PATH=/usr/lib/pkgconfig PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --buildtype=release --wrap-mode=nodownload -Ddefault_library=shared -Ddaemon=false -Dsystemd=false -Dudev_rules=false -Dargyllcms_sensor=false -Dbash_completion=false -Dintrospection=false -Dvapi=false -Dsane=false -Dtests=false -Dinstalled_tests=false -Dman=false -Ddocs=false -Dc_args=-Wno-error=date-time"
compile = "PYTHONPATH=/usr/lib/python3.12/site-packages ninja -C output"
install = "PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson install -C output --no-rebuild"
@@ -38,4 +49,4 @@ install = "PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson ins
# sqlite: REQUERIDA aunque daemon=false — meson.build:119 la pide incondicionalmente (la usa la
# librería para leer la base de perfiles ICC). Ya estaba en el corpus y sellada, provee sqlite3.pc.
[deps]
-build = ["meson", "samurai", "python3", "pkgconf", "glib", "lcms2", "sqlite", "libffi", "pcre2", "zlib"]
+build = ["meson", "samurai", "python3", "pkgconf", "gettext-tiny", "glib", "lcms2", "sqlite", "libgusb", "libgudev", "libudev-zero", "libusb", "json-glib", "hwdata", "libffi", "pcre2", "zlib"]
diff --git a/recipes/incoming-gnome/libei.toml b/recipes/incoming-gnome/libei.toml
new file mode 100644
index 00000000..6a5dcba7
--- /dev/null
+++ b/recipes/incoming-gnome/libei.toml
@@ -0,0 +1,35 @@
+# libei 1.5.0 — protocolo de entrada emulada para Wayland (libei = cliente, libeis = servidor).
+# Es el reemplazo de XTEST bajo Wayland: por acá pasan los clicks/teclas sintéticos, del control
+# remoto a la accesibilidad.
+#
+# mutter 48.8 la exige INCONDICIONALMENTE (meson.build:130-131, fuera de cualquier `if`), así que
+# -Dremote_desktop=false NO la evita — al contrario de lo que supuse al apagar esa opción: eso sólo
+# se llevó pipewire. Por eso libei entra igual y libeis tiene que estar encendido: mutter es el lado
+# SERVIDOR (es él quien recibe la entrada emulada).
+#
+# ⚠ FUENTE: el .orig.tar.bz2 inmutable del pool de Debian. libei vive en gitlab.freedesktop.org, y
+# los `-/archive/<tag>` de GitLab no son deterministas (misma lección que cairo-shared e iso-codes).
+# Debian empaqueta 1.5.0 y mutter pide >= 1.3.901, así que alcanza.
+#
+# -Dliboeffis=disabled: liboeffis es el puente al portal XDG por D-Bus y es lo ÚNICO que arrastra un
+# proveedor de sd-bus (libsystemd/libelogind/basu) — ninguno existe acá, y no hace falta: mutter no
+# usa oeffis, lo usan los clientes que piden permiso al portal.
+name = "libei"
+version = "1.5.0"
+
+[source]
+tarball = "http://deb.debian.org/debian/pool/main/libe/libei/libei_1.5.0.orig.tar.bz2"
+sha256 = "da1fba92daccd0667bc46c3ee952d4ae8cfc6bdb4c0bb4d34df26528fb240618"
+
+[build]
+compiler = "zig-cc"
+target = "x86_64-linux-musl"
+link = "dynamic"
+
+[build.phases]
+configure = "PKG_CONFIG_PATH=/usr/lib/pkgconfig PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --buildtype=release --wrap-mode=nodownload -Ddefault_library=shared -Dlibei=enabled -Dlibeis=enabled -Dliboeffis=disabled -Dtests=disabled -Dc_args=-Wno-error=date-time"
+compile = "PYTHONPATH=/usr/lib/python3.12/site-packages ninja -C output"
+install = "PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson install -C output --no-rebuild"
+
+[deps]
+build = ["meson", "samurai", "python3", "pkgconf", "py3-jinja2", "py3-markupsafe", "libevdev", "libxkbcommon", "glib", "libffi", "pcre2", "zlib"]
diff --git a/recipes/incoming-gnome/libgudev.toml b/recipes/incoming-gnome/libgudev.toml
new file mode 100644
index 00000000..e3c03404
--- /dev/null
+++ b/recipes/incoming-gnome/libgudev.toml
@@ -0,0 +1,31 @@
+# libgudev 238 — envoltorio GObject sobre libudev (GUdevClient/GUdevDevice: enumerar dispositivos y
+# escuchar uevents con el bucle de GLib en vez de a mano).
+#
+# La piden DOS de la onda 3, y por eso va antes que colord: mutter por su opción `udev`, que es la
+# que necesita el backend NATIVO (KMS/DRM) —o sea, la que hace que mutter sea un compositor de
+# verdad y no sólo un cliente anidado— y colord, que la exige incondicionalmente (gudev-1.0).
+#
+# libudev lo provee libudev-zero, ya en el corpus y sellado (libudev.pc + libudev.a). Ojo con el
+# gotcha ya conocido de este frente: eudev y libudev-zero PISAN el mismo .pc y el mismo .so — acá se
+# declara libudev-zero explícitamente para que no haya duda de cuál entra en el cierre.
+#
+# Feature-minimal: introspection/vapi/tests disabled (tests piden umockdev), gtk_doc off.
+name = "libgudev"
+version = "238"
+
+[source]
+tarball = "https://download.gnome.org/sources/libgudev/238/libgudev-238.tar.xz"
+sha256 = "61266ab1afc9d73dbc60a8b2af73e99d2fdff47d99544d085760e4fa667b5dd1"
+
+[build]
+compiler = "zig-cc"
+target = "x86_64-linux-musl"
+link = "static"
+
+[build.phases]
+configure = "PKG_CONFIG_PATH=/usr/lib/pkgconfig PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --buildtype=release --wrap-mode=nodownload --prefer-static -Ddefault_library=static -Dintrospection=disabled -Dvapi=disabled -Dtests=disabled -Dgtk_doc=false -Dc_args=-Wno-error=date-time"
+compile = "PYTHONPATH=/usr/lib/python3.12/site-packages ninja -C output"
+install = "PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson install -C output --no-rebuild"
+
+[deps]
+build = ["meson", "samurai", "python3", "pkgconf", "glib", "libudev-zero", "libffi", "pcre2", "zlib"]
diff --git a/recipes/incoming-gnome/libgusb.toml b/recipes/incoming-gnome/libgusb.toml
new file mode 100644
index 00000000..1c55001f
--- /dev/null
+++ b/recipes/incoming-gnome/libgusb.toml
@@ -0,0 +1,31 @@
+# libgusb 0.4.9 — envoltorio GObject sobre libusb (GUsbContext/GUsbDevice, con el bucle de GLib).
+# Último eslabón antes de colord: mutter → colord → libgusb.
+#
+# Sus tres deps ya estaban puestas por otras razones y encajan solas: gio (glib), json-glib —que se
+# autoró para mutter/gnome-shell— y usb.ids, que lo trae hwdata, la receta que vino de arrastre al
+# reusar libdisplay-info de la cola KDE. La opción usb_ids apunta al mismo prefijo /usr donde el
+# sandbox materializa esa dep.
+#
+# ⚠ FUENTE: el tarball del release de GitHub, NO el `tarball_url` de la API (ése es el archive
+# autogenerado del tag, que no es determinista — la lección de cairo-shared y de salsa/iso-codes).
+#
+# Feature-minimal: introspection/vapi/docs/tests off; umockdev disabled (sólo para tests).
+name = "libgusb"
+version = "0.4.9"
+
+[source]
+tarball = "https://github.com/hughsie/libgusb/releases/download/0.4.9/libgusb-0.4.9.tar.xz"
+sha256 = "9df5ef301d6a4b361002aa52cce1165a87a89744055879bdbab31e7e86f1e846"
+
+[build]
+compiler = "zig-cc"
+target = "x86_64-linux-musl"
+link = "static"
+
+[build.phases]
+configure = "PKG_CONFIG_PATH=/usr/lib/pkgconfig PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --buildtype=release --wrap-mode=nodownload --prefer-static -Ddefault_library=static -Dintrospection=false -Dvapi=false -Ddocs=false -Dtests=false -Dumockdev=disabled -Dc_args=-Wno-error=date-time"
+compile = "PYTHONPATH=/usr/lib/python3.12/site-packages ninja -C output"
+install = "PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson install -C output --no-rebuild"
+
+[deps]
+build = ["meson", "samurai", "python3", "pkgconf", "glib", "libusb", "json-glib", "hwdata", "libffi", "pcre2", "zlib"]
diff --git a/recipes/incoming-gnome/libusb.toml b/recipes/incoming-gnome/libusb.toml
new file mode 100644
index 00000000..ada1802e
--- /dev/null
+++ b/recipes/incoming-gnome/libusb.toml
@@ -0,0 +1,34 @@
+# libusb 1.0.30 — acceso a dispositivos USB desde espacio de usuario. Entra al corpus por una cadena
+# larga y poco obvia: mutter → colord → libgusb → libusb. Autotools, estático.
+#
+# --with-pic, y NO es cosmético: colord construye libcolord.so/libcolorhug.so aunque se le pase
+# -Ddefault_library=static (su meson pide shared_library explícito), y meter un .a sin PIC dentro de
+# un .so lo rechaza el linker — «relocation R_X86_64_TPOFF32 ... cannot be used with -shared» y
+# «recompile with -fPIC». Mismo remedio y misma razón que en recipes/libffi.toml, que lo aprendió
+# con Mesa/libwayland-server. PIC es superset: el .a sigue sirviendo para enlazar ejecutables.
+# (libgusb no lo necesita: meson compila los estáticos con b_staticpic=true por defecto.)
+#
+# --disable-udev: libusb puede enumerar y hacer hotplug por dos caminos, netlink+libudev o sysfs a
+# secas. El corpus tiene libudev-zero, pero su hotplug es justamente lo que ese proyecto NO
+# implementa completo; el camino sysfs no depende de nadie y alcanza de sobra para lo que llega hasta
+# acá (colord listando sensores de calibración que este perfil ni siquiera enciende). Menos aristas
+# en el cierre, y sin el riesgo del choque eudev/libudev-zero por el mismo .pc.
+name = "libusb"
+version = "1.0.30"
+
+[source]
+tarball = "https://github.com/libusb/libusb/releases/download/v1.0.30/libusb-1.0.30.tar.bz2"
+sha256 = "fea36f34f9156400209595e300840767ab1a385ede1dc7ee893015aea9c6dbaf"
+
+[build]
+compiler = "zig-cc"
+target = "x86_64-linux-musl"
+link = "static"
+
+[build.phases]
+configure = './configure --build=$CBUILD --host=$CHOST --prefix=/usr --disable-shared --enable-static --with-pic --disable-udev'
+compile = 'make'
+install = 'make DESTDIR=/out install'
+
+[deps]
+build = ["make"]
diff --git a/recipes/incoming-gnome/mutter.toml b/recipes/incoming-gnome/mutter.toml
index 093423b7..11c3dd0f 100644
--- a/recipes/incoming-gnome/mutter.toml
+++ b/recipes/incoming-gnome/mutter.toml
@@ -14,10 +14,10 @@ sha256 = "a483f91f75c16368d388179ecb855fcdb35db9c628f35b315b717153f99681a9"
[build]
compiler = "zig-cc"
target = "x86_64-linux-musl"
-link = "static"
+link = "dynamic"
[build.phases]
-configure = "PKG_CONFIG_PATH=/usr/lib/pkgconfig PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --buildtype=release --wrap-mode=nodownload --prefer-static -Ddefault_library=static -Dxwayland=false -Dx11=false -Dglx=false -Dremote_desktop=false -Dlibwacom=false -Dsound_player=false -Dstartup_notification=false -Dsm=false -Dtests=disabled -Dcogl_tests=false -Dclutter_tests=false -Dmutter_tests=false -Dprofiler=false -Ddocs=false -Dinstalled_tests=false -Dc_args=-Wno-error=date-time"
+configure = "PKG_CONFIG_PATH=/usr/lib/pkgconfig PYTHONPATH=/usr/lib/python3.12/site-packages meson setup output --prefix=/usr --buildtype=release --wrap-mode=nodownload -Ddefault_library=shared -Dxwayland=false -Dx11=false -Dopengl=false -Dglx=false -Dremote_desktop=false -Dlibwacom=false -Dsound_player=false -Dstartup_notification=false -Dsm=false -Dtests=disabled -Dcogl_tests=false -Dclutter_tests=false -Dmutter_tests=false -Dprofiler=false -Ddocs=false -Dinstalled_tests=false -Dc_args=-Wno-error=date-time"
compile = "PYTHONPATH=/usr/lib/python3.12/site-packages ninja -C output"
install = "PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson install -C output --no-rebuild"
@@ -33,4 +33,4 @@ install = "PYTHONPATH=/usr/lib/python3.12/site-packages DESTDIR=/out meson ins
# libgudev, libwacom — device/tablet.
# sysprof-capture — opcional (profiler=false lo evita).
[deps]
-build = ["meson", "samurai", "python3", "pkgconf", "gobject-introspection", "gsettings-desktop-schemas", "gnome-desktop", "glib", "gtk4", "graphene", "wayland", "wayland-protocols", "libinput", "libxkbcommon", "cairo", "pango", "gdk-pixbuf", "mesa", "libdrm", "libepoxy", "pixman", "harfbuzz", "fribidi", "libpng", "freetype", "expat", "libffi", "pcre2", "zlib", "libjpeg-turbo", "libtiff", "fontconfig", "json-glib", "atk", "lcms2", "libdisplay-info", "hwdata"]
+build = ["meson", "samurai", "python3", "pkgconf", "gobject-introspection", "gsettings-desktop-schemas", "gnome-desktop", "glib", "gtk4", "graphene", "wayland", "wayland-protocols", "libinput", "libxkbcommon", "cairo", "pango", "gdk-pixbuf", "mesa", "libdrm", "libepoxy", "pixman", "harfbuzz", "fribidi", "libpng", "freetype", "expat", "libffi", "pcre2", "zlib", "libjpeg-turbo", "libtiff", "fontconfig", "json-glib", "atk", "lcms2", "libdisplay-info", "hwdata", "colord", "libgudev", "libudev-zero", "libgusb", "libusb", "sqlite", "gettext-tiny", "libei", "libevdev", "xkeyboard-config", "iso-codes", "libseccomp"]
diff --git a/recipes/incoming-gnome/py3-jinja2.toml b/recipes/incoming-gnome/py3-jinja2.toml
new file mode 100644
index 00000000..efb1a0f3
--- /dev/null
+++ b/recipes/incoming-gnome/py3-jinja2.toml
@@ -0,0 +1,33 @@
+# py3-jinja2 3.1.6 — motor de plantillas de Python, como MÓDULO importable.
+#
+# Entra al corpus por una cadena que vale la pena dejar escrita: mutter exige libei (entrada emulada
+# bajo Wayland, incondicional), libei GENERA el código de su protocolo con plantillas jinja2, y su
+# meson corta con «python3 is missing modules: jinja2». O sea: no es una dep del escritorio, es una
+# herramienta de build que aparece a tres saltos de mutter.
+#
+# Mismo patrón que py3-setuptools: Python PURO, se COPIA a site-packages (hammer no usa el camino
+# wheel/pip; la fuente ES el árbol Python). markupsafe es su única dep.
+name = "py3-jinja2"
+version = "3.1.6"
+
+[source]
+tarball = "https://files.pythonhosted.org/packages/source/J/Jinja2/jinja2-3.1.6.tar.gz"
+sha256 = "0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"
+
+[build]
+compiler = "zig-cc"
+target = "x86_64-linux-musl"
+link = "static"
+
+[build.phases]
+compile = "true"
+install = '''
+set -e
+SP=/out/usr/lib/python3.12/site-packages
+mkdir -p "$SP"
+cp -r src/jinja2 "$SP/"
+'''
+
+[deps]
+build = ["py3-markupsafe"]
+runtime = ["python3", "py3-markupsafe"]
diff --git a/recipes/incoming-gnome/py3-markupsafe.toml b/recipes/incoming-gnome/py3-markupsafe.toml
new file mode 100644
index 00000000..a37a8993
--- /dev/null
+++ b/recipes/incoming-gnome/py3-markupsafe.toml
@@ -0,0 +1,33 @@
+# py3-markupsafe 3.0.2 — escapado de HTML/XML; es la única dependencia de jinja2, y jinja2 entra al
+# corpus porque libei genera su protocolo con plantillas (ver py3-jinja2).
+#
+# Mismo patrón que py3-setuptools: Python PURO, se COPIA a site-packages. MarkupSafe trae además una
+# extensión C opcional (_speedups) que sólo acelera el escapado; el paquete la deja caer sola a la
+# implementación en Python si no está compilada, así que no se construye — no hay motivo para meter
+# una extensión C en un build-tool que corre unas pocas veces.
+name = "py3-markupsafe"
+version = "3.0.2"
+
+[source]
+tarball = "https://files.pythonhosted.org/packages/source/M/MarkupSafe/markupsafe-3.0.2.tar.gz"
+sha256 = "ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"
+
+[build]
+compiler = "zig-cc"
+target = "x86_64-linux-musl"
+link = "static"
+
+[build.phases]
+compile = "true"
+install = '''
+set -e
+SP=/out/usr/lib/python3.12/site-packages
+mkdir -p "$SP"
+cp -r src/markupsafe "$SP/"
+# La extensión C no se compila: se saca el módulo que la importaría, para que el paquete use su
+# camino puro-Python en vez de fallar al buscar un .so inexistente.
+rm -f "$SP/markupsafe/_speedups.c" "$SP/markupsafe/_speedups.pyi"
+'''
+
+[deps]
+runtime = ["python3"]