🛍 cosmic: la tienda LISTA — el catálogo AppStream hubo que fabricarlo, y el <provides> la vaciaba
Validado con pantalla: «Popular apps» poblada (App Library, Files,
Launcher, Settings), «Recently updated», y la búsqueda `text` devolviendo
COSMIC Text Editor. 2564 colores sobre un escritorio completo.
DOS TRAMPAS ENCADENADAS, y la primera invalida lo que dije al escribir la
receta:
1. El backend `pkgar` NO lee /usr/share/metainfo/*.metainfo.xml —los
ficheros que instala cada receta—. Lee el CATÁLOGO, o sea
{/usr/share,/var/lib,/var/cache}/{swcatalog,app-info}/{xml,yaml}/, que en
una distro normal genera `appstreamcli compose`. Sin ese fichero la
tienda abre perfecta y no lista NADA. Lo genera ahora
qemu-desktop-image.sh agregando los metainfo en un <components>: doce
líneas de python, cero AppStream y cero glib.
2. Con el catálogo puesto listaba 2 de 7. Los metainfo que genera `xdgen`
escriben <provides> con los envoltorios <mimetypes>/<binaries>, y el
crate `appstream` espera los hijos PLANOS del esquema de catálogo: muere
con «The tag provide doesn't have a value» y DESCARTA EL COMPONENTE
ENTERO, callado. Los dos que se veían eran justo los que no tienen
<provides>. Se elimina el bloque al agregar.
Cómo se diagnosticó, que es lo reutilizable: desde el lanzador la salida del
proceso no llega al log de la sesión. Hay que abrir cosmic-term DENTRO de la
VM y correr `rm -rf ~/.cache/cosmic-store; RUST_LOG=info cosmic-store`. El
rm no es opcional: la tienda cachea el parseo en bitcode y sin borrarlo
repite el resultado viejo.
Y el deadline del panel es una CARRERA, no un umbral: un arranque de esta
tanda salió mutilado (130 colores) con el anfitrión ocioso y -smp 8. Si sale
mutilado con carga baja, rearrancar antes de culpar al propio cambio.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Semantic delta
No definition changed: this change is text, documentation or data.
4 files · +86 −1
diff --git a/docs/evidencia/cosmic-store-busqueda-qemu-2026-08-05.png b/docs/evidencia/cosmic-store-busqueda-qemu-2026-08-05.png
new file mode 100644
index 00000000..32ef6805
Binary files /dev/null and b/docs/evidencia/cosmic-store-busqueda-qemu-2026-08-05.png differ
diff --git a/docs/evidencia/cosmic-store-qemu-2026-08-05.png b/docs/evidencia/cosmic-store-qemu-2026-08-05.png
new file mode 100644
index 00000000..e5b5c93a
Binary files /dev/null and b/docs/evidencia/cosmic-store-qemu-2026-08-05.png differ
diff --git a/docs/runbooks/cosmic-desktop.md b/docs/runbooks/cosmic-desktop.md
index e16ad223..1538c4c5 100644
--- a/docs/runbooks/cosmic-desktop.md
+++ b/docs/runbooks/cosmic-desktop.md
@@ -696,9 +696,52 @@ la feature `gvfs`, que se apaga. El lock dice el universo; las features dicen el
no declara ninguna glib y su binario enlaza **sólo `libxkbcommon.so.0` y `libc.so`**: dos NEEDED, el
mínimo de la suite.
+## 🛍 LA QUINTA: cosmic-store, y el catálogo que hubo que fabricar (2026-08-05)
+
+`docs/evidencia/cosmic-store-qemu-2026-08-05.png` — la tienda abierta con *Popular apps* poblada
+(App Library, Files, Launcher, Settings) y *Recently updated*; y `…-busqueda-…png` con la búsqueda
+`text` devolviendo *COSMIC Text Editor*. Sellada en `b3:a06437f7`, NEEDED = `libxkbcommon` + `libc`.
+
+**Es la primera aplicación de la suite que choca con que hammer es OTRA distro.** Una tienda es la
+cara de un gestor de paquetes. De sus cuatro backends (`src/backend/`, todos tras `#[cfg(feature)]`):
+`flatpak` pide `libflatpak` (GObject ⇒ glib compartida + ostree/libsoup/gpgme: la torre de C que esta
+campaña evita); `packagekit` es Rust puro pero necesita el DEMONIO en el bus; `rpm-ostree` es
+irrelevante; **`pkgar` es el único que arranca** — ni demonio ni enlace.
+
+**ALCANCE: navega, no instala.** Nada está cableado al `.swm` (Etapa F). El camino barato para
+arreglarlo quedó identificado: servir `org.freedesktop.PackageKit` sobre `.swm` y encender la feature
+`packagekit`. Es un shim de D-Bus, mismo patrón que `arje-logind-compat`, y **no cuesta un solo `.so`
+de C**.
+
+### El catálogo AppStream: dos trampas encadenadas
+
+**1. `pkgar` NO lee `/usr/share/metainfo/`.** Lee el CATÁLOGO —
+`{/usr/share,/var/lib,/var/cache}/{swcatalog,app-info}/{xml,yaml}/` — que en una distro normal genera
+`appstreamcli compose` agregando los metainfo en un `<components version=… origin=…>`. Sin ese
+fichero la tienda **abre perfecta y no lista NADA**. Lo genera ahora `qemu-desktop-image.sh` con doce
+líneas de python; no hace falta AppStream ni glib.
+
+**2. Hay que SACAR el `<provides>`.** Los metainfo que genera `xdgen` lo escriben con los envoltorios
+`<mimetypes>`/`<binaries>`, y el crate `appstream` espera los hijos planos del esquema de catálogo.
+Con el envoltorio muere con `The tag provide doesn't have a value` y **descarta el componente entero,
+callado**: listaba 2 de 7, justo los dos sin `<provides>` (App Library y Launcher).
+
+**Cómo se diagnosticó, que es lo reutilizable:** desde el lanzador la salida del proceso NO llega al
+log de la sesión. Hay que abrir `cosmic-term` DENTRO de la VM y correr
+`rm -rf ~/.cache/cosmic-store; RUST_LOG=info cosmic-store 2>/tmp/s.log` — el `rm` no es opcional, la
+tienda cachea el parseo en `~/.cache/cosmic-store/*.bitcode` y si no se borra vuelve a mostrar el
+resultado viejo. Que la imagen tenga terminal deja de ser una comodidad y pasa a ser instrumental.
+
+### Y el deadline del panel es una CARRERA, no un umbral
+
+Con el anfitrión ocioso y `-smp 8`, un arranque de esta tanda salió igual mutilado (130 colores) con
+33 procesos vivos y el `deadline has elapsed` en el log. O sea: 8 vCPU + anfitrión quieto **sube
+mucho la probabilidad pero no garantiza**. Si sale mutilado y la carga estaba baja, **rearrancá antes
+de buscar la causa en tu cambio**.
+
## Lo que falta, en orden
-1. **Más aplicaciones**: `cosmic-store`, `cosmic-screenshot`.
+1. **Más aplicaciones**: `cosmic-screenshot`.
2. **La cadena glib COMPARTIDA** (`glib` + `pcre2` + `libffi` + `zlib` con `.so`), que destrabaría
`gvfs` en cosmic-files y su applet del panel. Es una campaña propia y hay que decidir antes si la
imagen puede tener una sola glib.
diff --git a/scripts/cosmic/qemu-desktop-image.sh b/scripts/cosmic/qemu-desktop-image.sh
index 80dde71d..8cfc159b 100755
--- a/scripts/cosmic/qemu-desktop-image.sh
+++ b/scripts/cosmic/qemu-desktop-image.sh
@@ -150,6 +150,48 @@ ln -sfn /run "$MERGED/var/run"
mkdir -p "$MERGED/root/.config" "$MERGED/root/.cache" "$MERGED/run/user/0" "$MERGED/tmp"
chmod 1777 "$MERGED/tmp"
+# ── CATÁLOGO APPSTREAM, o por qué cosmic-store salía VACÍO ─────────────────────────────────────
+# El backend `pkgar` de la tienda NO lee `/usr/share/metainfo/*.metainfo.xml` —los ficheros que cada
+# receta instala, uno por aplicación—. Lee el **CATÁLOGO**: los ficheros de
+# `{/usr/share,/var/lib,/var/cache}/{swcatalog,app-info}/{xml,yaml}/`, que en una distro normal
+# genera `appstreamcli compose` agregando todos los metainfo en un solo documento
+# `<components version=… origin=…>`. Sin ese fichero la tienda abre perfecta y no lista NADA
+# —«No results for …»— porque su catálogo está vacío, no porque le falte nada del sistema.
+#
+# hammer no tiene appstreamcli (sería AppStream + glib, o sea la torre de C que esta campaña evita),
+# pero el formato es XML plano y la agregación es exactamente eso: envolver los `<component>` de cada
+# metainfo en una raíz. Doce líneas de python valen la librería entera.
+echo "==> catálogo AppStream para cosmic-store (agrega los metainfo del rootfs)"
+#
+# ⚠ Y hay que SACAR el bloque `<provides>`. Los metainfo que genera `xdgen` —los de cosmic-edit,
+# files, settings, store y term— lo escriben con los envoltorios `<mimetypes>`/`<binaries>`, y el
+# parser de la tienda (el crate `appstream`) espera los hijos PLANOS del esquema de catálogo
+# (`<mediatype>`, `<binary>`). Con el envoltorio muere con
+# failed to parse … in "/usr/share/swcatalog/xml/hammer.xml": The tag provide doesn't have a value
+# y —esto es lo caro— **descarta el componente entero, callado para el usuario**: la tienda abría
+# perfecta y listaba sólo 2 de 7, justamente los dos que NO tienen `<provides>` (App Library y
+# Launcher). Se descubrió corriendo `RUST_LOG=info cosmic-store` desde cosmic-term DENTRO de la VM;
+# desde el lanzador la salida del proceso no llega al log de la sesión.
+python3 - "$MERGED" <<'PY'
+import glob, os, re, sys
+merged = sys.argv[1]
+comps = []
+for p in sorted(glob.glob(os.path.join(merged, "usr/share/metainfo/*.xml"))):
+ x = open(p, encoding="utf-8").read()
+ # Quedarse con el elemento raíz <component …>…</component>, sin cabecera XML ni comentarios.
+ m = re.search(r"<component[\s>].*</component>", x, re.S)
+ if m:
+ comps.append(re.sub(r"\s*<provides>.*?</provides>", "", m.group(0), flags=re.S))
+out = os.path.join(merged, "usr/share/swcatalog/xml")
+os.makedirs(out, exist_ok=True)
+with open(os.path.join(out, "hammer.xml"), "w", encoding="utf-8") as f:
+ f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
+ f.write('<components version="0.14" origin="hammer" architecture="x86_64">\n')
+ f.write("\n".join(comps))
+ f.write("\n</components>\n")
+print(" %d componentes → /usr/share/swcatalog/xml/hammer.xml" % len(comps))
+PY
+
echo "==> rootfs fundido: $(du -sh "$MERGED" | cut -f1)"
need_mb=$(du -sm "$MERGED" | cut -f1)
ROOT_SIZE="${ROOT_SIZE:-$(( need_mb + 1536 ))}"