🛍 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>
This commit is contained in:
2026-08-04 23:14:01 -04:00
co-authored by Claude Opus 5
parent f808693060
commit 04146ec77b
4 changed files with 86 additions and 1 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

+44 -1
View File
@@ -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.
+42
View File
@@ -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 ))}"