gnome: el perfil y el hidratador decían cosas distintas — 21 paquetes no llegaban a la imagen

Verificando que las extensiones de la tanda anterior llegaran al rootfs apareció algo bastante peor
que lo que fui a buscar: **`scripts/gnome/hydrate-gnome.sh` no lee `targets.toml`**. Arma el rootfs
desde una lista de 14 raíces escrita a mano dentro del propio script, y de las 28 raíces que el
perfil declaraba sólo DOS coincidían.

MEDIDO recorriendo la clausura de esa lista (123 recetas) contra la membresía del perfil: **21
paquetes declarados en `escritorio-gnome` no llegaban al rootfs hidratado**. No son accesorios —
son las hojas, que es justo lo que un perfil declara porque no es dep de build de nadie:

  atuq (el navegador) · foot (la terminal) · helix (el editor) · mpv · swayimg (el visor)
  zathura + zathura-pdf-poppler (el lector de PDF) · dejavu-fonts y dejavu-fonts-nerd (las FUENTES)
  xdg-desktop-portal-gnome · libnotify · desktop-file-utils
  bzip2-shared, expat-shared, libffi-shared, ncurses-shared, xz-shared (las .so que se cazaron el
  2026-09-03 recorriendo NEEDED, justamente para que el rootfs no resolviera contra el lab)
  y las cuatro de ayer: las tres extensiones y gnome-tweaks

O sea que el trabajo de descubrir que la imagen no tenía terminal, ni editor, ni una sola fuente
—con su análisis largo escrito en este mismo fichero— estaba DECLARADO y no llegaba.

Y la divergencia iba en los dos sentidos: 11 raíces de RUNTIME vivían sólo dentro del script y el
perfil no las conocía (accountsservice, geoclue, libgdm, libgweather, upower, librsvg, ibus, los dos
gi-*-typelibs, gsd-schemas y wireplumber). Sin ellas el shell no arranca: las carga por
`imports.gi.*`, que es invisible para la clausura de deps.

ARREGLO: las 11 se mueven a `targets.toml` con la justificación que el script ya traía bien escrita,
y el script queda marcado como SUPERADO por `scripts/hydrate-profile.py`, que hidrata desde el
perfil — la única declaración que existe. Su propia cabecera ya advertía de este modo de fallo
(«DOS fuentes de verdad para lo mismo, y divergen sin que nada lo diga»); esto es esa advertencia
cumpliéndose. El perfil pasa de 28 a 39 raíces y de 190 a 204 nodos, deuda 0.

VERIFICADO HIDRATANDO DE VERDAD, no leyendo: `hydrate-profile.py escritorio-gnome` proyecta
210/210 nodos (27993 ficheros, 3,5 G) y ahí están las tres extensiones, `gnome-tweaks` con su
módulo `gtweak`, y los tres gschemas en el directorio GLOBAL. Después se corrió el bloque nuevo del
lanzador contra ESE árbol: enumeró las tres, escribió el override y `glib-compile-schemas` salió 0
con stderr vacío.

⚠ DOS COSAS QUE APRENDÍ MIDIENDO Y CONVIENE QUE ESTÉN DICHAS:

1. **El hub NO puede hidratar GNOME hoy.** `gjs`, `gnome-shell` y `spidermonkey` salen «no sellado»
   localmente. No es deriva ni un grafo mintiendo: sus hashes vigentes SÍ están en
   `work/farm-sellados.txt` — o sea sellados EN EL WORKER — y la cosecha baja el manifiesto y no el
   store, a propósito (bajarlo deshacía la poda). `build-state` los cuenta bien porque mira también
   el manifiesto; `hydrate-profile.py` mira sólo el disco local. Los dos tienen razón.
2. **EXDEV entre bind-mounts, otra vez.** Hidratar a `work/` o a `work/out` falla con «Invalid
   cross-device link» AUNQUE `work/out` esté en el mismo /dev/sdb que el store: son bind-mounts
   distintos y `linkat()` no cruza montajes. Hay que hidratar accediendo al store por su montaje
   raíz (`--store /mnt/cosecha/store --into /mnt/cosecha/escritorios/…`). El mensaje de error del
   script lo dice y aun así me costó dos intentos.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCt3ettR4Z7b6wPCBmbvEV
This commit is contained in:
Sergio
2026-09-09 19:56:19 +00:00
co-authored by Claude Opus 5
parent 9bd95cd307
commit eb8f217245
4 changed files with 82 additions and 19 deletions
+45 -18
View File
@@ -36,9 +36,9 @@
"sealed": 80
},
"escritorio-gnome": {
"raices": 28,
"clausura": 190,
"sealed": 190
"raices": 39,
"clausura": 204,
"sealed": 204
},
"escritorio-mirada": {
"raices": 17,
@@ -46,7 +46,7 @@
"sealed": 41
}
},
"sin_perfil": 660,
"sin_perfil": 647,
"orphan_deps": [],
"topo_ok": true,
"nodes": {
@@ -81,7 +81,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 1
},
"act": {
@@ -5301,7 +5303,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"geocode-glib": {
@@ -5337,7 +5341,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 1
},
"gettext-tiny": {
@@ -5474,7 +5480,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"gi-girepository-typelib": {
@@ -5501,7 +5509,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"giflib": {
@@ -7419,7 +7429,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"gsettings-desktop-schemas": {
@@ -8360,7 +8372,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"icu4c": {
@@ -10317,7 +10331,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"libglvnd": {
@@ -10479,7 +10495,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"libical": {
@@ -10827,7 +10845,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"libsass": {
@@ -11620,7 +11640,8 @@
"blocked_by": [],
"unblocks": 0,
"perfiles": [
"escritorio-cosmic"
"escritorio-cosmic",
"escritorio-gnome"
],
"dependientes_total": 2
},
@@ -17522,7 +17543,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"usbutils": {
@@ -18354,7 +18377,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 0
},
"wishlist": {
@@ -18503,7 +18528,9 @@
"state": "sealed",
"blocked_by": [],
"unblocks": 0,
"perfiles": [],
"perfiles": [
"escritorio-gnome"
],
"dependientes_total": 1
},
"x264": {
+1 -1
View File
@@ -31,7 +31,7 @@
},
"escritorio-gnome": {
"grafo": "docs/state/build-state-gnome.json",
"nodos": 190,
"nodos": 204,
"en_deuda": 0,
"ondas": 0,
"profundidad": 0,
+26
View File
@@ -458,6 +458,32 @@ paquetes = [
# `python3` y `py3-gobject` como deps de RUNTIME: sin ellas en la imagen se instala y no arranca,
# y ningún build puede delatarlo.
"gnome-tweaks",
# ── RAÍCES DE RUNTIME QUE EL GRAFO NO PUEDE VER (traídas acá el 2026-09-09) ───────────────────
# ⚠ Estas once vivían SÓLO dentro de `scripts/gnome/hydrate-gnome.sh`, en una lista escrita a
# mano. O sea DOS fuentes de verdad para «qué lleva la imagen de GNOME», y habían divergido en
# los DOS sentidos: el script no conocía 21 paquetes que este perfil declara —entre ellos el
# navegador, la terminal, el editor, el visor de PDF, las fuentes y las tres extensiones— y este
# perfil no conocía estas once, sin las cuales el shell no arranca. Es exactamente el modo de
# fallo que la cabecera de `scripts/hydrate-profile.py` describe y que `vigia-imagen.py` existe
# para cazar.
# Ninguna es dep de BUILD de nadie: el shell las carga por `imports.gi.*` al arrancar, y eso es
# invisible para la clausura de deps. La justificación de cada una la traía el script:
"accountsservice", # imports.gi.AccountsService
"gi-foreign-typelibs", # DBus-1.0 / cairo-1.0, que Gdk y Atspi incluyen
"gi-girepository-typelib", # lo pide el propio gjs desde su JS embebido
"libgdm", # imports.gi.Gdm — el shell lo importa SIN condición al arrancar
"geoclue", # imports.gi.Geoclue
"libgweather", # imports.gi.GWeather
"upower", # imports.gi.UPowerGlib
"librsvg", # imports.gi.Rsvg
"ibus", # imports.gi.IBus
# Ni typelib ni librería: los GSCHEMAS de gnome-settings-daemon. El demonio está aparcado
# (GTK3/X11) pero el shell lee sus esquemas al armar los quick settings, y si faltan la excepción
# ocurre DENTRO de Main.start() ⇒ el panel no se arma y la pantalla queda NEGRA.
"gsd-schemas",
# `wireplumber` es lo que le da DISPOSITIVOS a PipeWire (que ya entra por la clausura): sin él hay
# socket de audio y nada que suene.
"wireplumber",
]
[perfil.escritorio-cosmic]
+10
View File
@@ -10,6 +10,16 @@
# calcula el ArtifactHash de la receta de HOY sin construir. O sea: cero ambigüedad, y si falta algo
# el reporte dice exactamente qué receta y con qué hash lo buscaba.
#
# ⚠ SUPERADO POR `scripts/hydrate-profile.py` (2026-09-09). Sus raíces eran una lista ESCRITA A
# MANO acá adentro, o sea una segunda fuente de verdad frente a `docs/state/targets.toml`, y habían
# divergido en los DOS sentidos: este script no conocía 21 paquetes que el perfil declara —el
# navegador, la terminal, el editor, el visor de PDF, las fuentes, las tres extensiones y
# gnome-tweaks— y el perfil no conocía las 11 raíces de RUNTIME que sólo estaban acá. Las 11 se
# movieron a `targets.toml` con su justificación, así que hoy
# scripts/hydrate-profile.py escritorio-gnome --into <dir>
# arma el rootfs COMPLETO desde la única declaración que existe. Este script se deja porque su
# cabecera documenta por qué cada raíz de runtime hace falta, pero su lista ya no es la verdad.
#
# Uso: scripts/gnome/hydrate-gnome.sh [receta.toml ...]
# Env: RFS (def work/gnome-rootfs) destino
# STORE (def ./store)