tawasuyu

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 1411Shell 141Markdown 84Rust 832898 Sutichaykuna

kde/Plasma: hidratación del ESCRITORIO COMPLETO store-directa (rebuild-free), cierre runtime verificado

- hydrate-from-store.sh: proyecta artefactos SELLADOS del store al FHS vía hammer hydrate,
  sin reproduce (install-reproduce rebuildеa qtbase por consumidor — hashing frágil bajo
  régimen dinámico). Selección por-pkg = artefacto más reciente con mtime<hoy (cosecha final
  coherente de la granja). 137/137 proyectados, 0 faltantes, 1257 .so, 5.0G en kde-store-rootfs.
- Hashes elegidos = los sellados de la campaña (kwin 3a674cb6, plasma-workspace 284c862f,
  libplasma 963403fe, kscreenlocker 55120487, breeze df1d1e68, kio 44376420).
- Cierre runtime VERIFICADO completo: todos los NEEDED de plasmashell(30)/kwin_wayland(24)/
  startplasma-wayland(11)/krunner(19) + libs core resuelven en el rootfs — cero colgantes.
- hydrate-fhs.sh: soporte multi-target (TARGETS) + canon prefiere nombre real qt6-* (el alias
  fuerza cache-miss+rebuild) + más build-only excluidos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

d6944b2cd9e4a87fce046ead1369d02325e94c9c · ruwaq sergio · (3 killa ñawpaq) · tayta 08510c042 · kay commitpi sach'ata qhaway

Semántico sayk'uy

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

3 qillqakuna · +108 −10

diff --git a/docs/adr/0011-escritorio-kde-plasma.md b/docs/adr/0011-escritorio-kde-plasma.md
index 992365fc..9e1a6ff7 100644
--- a/docs/adr/0011-escritorio-kde-plasma.md
+++ b/docs/adr/0011-escritorio-kde-plasma.md
@@ -243,8 +243,35 @@ probada, en tres piezas nuevas:
 - **Fuentes**: Qt ya no trae fuentes (`QFontDatabase: Cannot find font directory`). Cosmético para
   H-QML (no bloquea la instanciación), pero desplegar dejavu/fontconfig antes de apps reales.
 
+### Hidratación del ESCRITORIO COMPLETO — store-directa, rebuild-free (2026-07-14)
+`install`-reproduce NO escala al escritorio completo: el hashing determinista es frágil bajo el régimen
+dinámico — cada consumidor qt (qtwayland/kwin) recomputa el cierre de build y puede obtener un hash de
+qtbase distinto del sellado → **rebuild** (se observó qtbase rebuildeándose al instalar qtwayland). La
+vía correcta es **`hammer hydrate <hash> --into` = proyectar el artefacto YA SELLADO del store al FHS,
+sin reproduce**. `scripts/kde/hydrate-from-store.sh`:
+- computa el cierre (nombres reales qt6-*) y, por paquete, elige el artefacto `store/<hash>-<name>` MÁS
+  RECIENTE con mtime **anterior al inicio de hoy** (CUTOFF) = la cosecha FINAL de la granja (set
+  coherente, todo contra el mismo qtbase del cascade; evita los rebuilds ad-hoc de la sesión).
+- **Resultado: 137/137 proyectados, 0 faltantes, 1257 `.so`, 241 binarios, 5.0G** en `work/kde-store-
+  rootfs`. Los hashes elegidos coinciden EXACTO con los sellados de la campaña (kwin `3a674cb6`,
+  plasma-workspace `284c862f`, libplasma `963403fe`, kscreenlocker `55120487`, breeze `df1d1e68`,
+  kio `44376420`). Binarios presentes: `plasmashell kwin_wayland startplasma-wayland krunner
+  plasma_session kscreenlocker_greet` (ksmserver no existe en Plasma 6 Wayland → plasma_session).
+- **CIERRE RUNTIME VERIFICADO COMPLETO**: todos los `NEEDED` de plasmashell (30), kwin_wayland (24),
+  startplasma-wayland (11), krunner (19) y libs core (libPlasma/libPlasmaQuick/libKF6XmlGui/libQt6Quick)
+  resuelven dentro del rootfs+sysroot musl. **Cero dependencias colgantes** — el objetivo "capturar el
+  cierre runtime" queda cumplido para el escritorio entero.
+
+`hydrate-fhs.sh` (install-reproduce) queda para validar UN closure chico con verificación de firma
+end-to-end (como H-QML); `hydrate-from-store.sh` es la vía para el rootfs de PRODUCTO completo.
+
 ### Próximo paso concreto
-Con H-QML cerrado, **Capa 2 (KDE Frameworks 6)**: `extra-cmake-modules` ya está; atacar tier 1
+Con H-QML cerrado y el escritorio hidratado con cierre runtime completo, lo que falta para "escritorio
+VIVO" es: (1) **arrancar `startplasma-wayland` sobre `kwin_wayland` en metal/VM con GPU** (el worker es
+headless glibc — necesita KMS/DRM real; coordinar con [[arranque-grafo-mirada]] / QEMU-GPU); (2)
+desplegar fuentes (dejavu/fontconfig, hoy warning); (3) empaquetar `work/kde-store-rootfs` como imagen
+de disco (reusar `scripts/disk-image.sh`/`install-image-efi.sh`). En paralelo, **Capa 2 (KDE Frameworks
+6)**: `extra-cmake-modules` ya está; atacar tier 1
 (`kcoreaddons`, `kconfig`, `ki18n`, `kirigami`…) hacia el hito **H-KF6** (una app KDE de ejemplo
 corre). En paralelo saldar la deuda Capa 0 (sqlite/openssl/glib shared-PIC) para promover un qtbase
 "real" a canónico/firmado. Mantener la hidratación FHS como el patrón de validación por capa.
diff --git a/scripts/kde/hydrate-fhs.sh b/scripts/kde/hydrate-fhs.sh
index 40caf449..6823cf16 100644
--- a/scripts/kde/hydrate-fhs.sh
+++ b/scripts/kde/hydrate-fhs.sh
@@ -13,21 +13,28 @@
 set -uo pipefail
 ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
 REPO="${REPO:-work/repo-kde}"; TRUST="${TRUST:-dist/keys}"; STORE="${STORE:-./store}"
-RFS="${RFS:-work/kde-rootfs}"; TARGET="${TARGET:-qt6-qtdeclarative}"
+RFS="${RFS:-work/kde-rootfs}"
+# TARGETS = uno o varios paquetes tope (unión de cierres). TARGET (singular) sigue soportado.
+TARGETS="${TARGETS:-${TARGET:-qt6-qtdeclarative}}"
 HAMMER="${HAMMER:-./target/release/hammer}"
 DB="$RFS/var/lib/hammer/installed.json"
 
-# cierre transitivo del TARGET, leído del índice (deps + aliases ya resuelven). Excluimos herramientas
-# puramente de build (no aportan runtime) para acelerar; si algo falta lo delata ldd.
-CLOSURE=$(python3 - "$REPO/index.json" "$TARGET" <<'PY'
+# cierre transitivo de TODOS los targets, leído del índice (deps + aliases ya resuelven). Excluimos
+# herramientas puramente de build (no aportan runtime) para acelerar; si algo falta lo delata ldd.
+CLOSURE=$(python3 - "$REPO/index.json" $TARGETS <<'PY'
 import json,sys
-idx=json.load(open(sys.argv[1])); tgt=sys.argv[2]
+idx=json.load(open(sys.argv[1])); targets=sys.argv[2:]
 by={p['name']:p for p in idx['packages']}
 BUILD_ONLY={'cmake','samurai','meson','ninja','bison','flex','gperf','util-macros','xorgproto',
  'xcb-proto','wayland-protocols','pkgconf','mako','markupsafe','packaging','python3','extra-cmake-modules',
- 'qttools','qt6-qttools'}
-# alias qt-corto -> qt6-*
+ 'qttools','qt6-qttools','intltool','perl','perl-xml-parser','gettext-tiny','m4','autoconf','automake',
+ 'libtool','util-macros'}
+# PREFERIR el nombre real qt6-* sobre el alias qt-corto: install nombra el artefacto por la clave del
+# índice, y sólo `<hash>-qt6-qtdeclarative` está sellado (no `<hash>-qtdeclarative`) ⇒ instalar el alias
+# fuerza cache-miss+rebuild. El alias existe sólo para resolver deps DENTRO del .swm, no para instalar.
 def canon(n):
+    if n.startswith('qt') and not n.startswith('qt6-') and ('qt6-'+n) in by:
+        return 'qt6-'+n
     return n if n in by else ('qt6-'+n if 'qt6-'+n in by else n)
 seen=set(); order=[]
 def visit(n):
@@ -36,14 +43,14 @@ def visit(n):
     seen.add(n)
     for d in by[n].get('deps',[]): visit(d)
     order.append(n)
-visit(tgt)
+for t in targets: visit(t)
 for n in order:
     if n not in BUILD_ONLY: print(n)
 PY
 )
 
 mkdir -p "$(dirname "$DB")"
-echo "==> hidratando cierre runtime de $TARGET → $RFS ($(echo "$CLOSURE"|wc -w) pkgs)"
+echo "==> hidratando cierre runtime de [$TARGETS] → $RFS ($(echo "$CLOSURE"|wc -w) pkgs)"
 ok=0; fail=0
 for pkg in $CLOSURE; do
   before=$(find "$RFS" -type f 2>/dev/null | wc -l)
diff --git a/scripts/kde/hydrate-from-store.sh b/scripts/kde/hydrate-from-store.sh
new file mode 100644
index 00000000..8e4a8fba
--- /dev/null
+++ b/scripts/kde/hydrate-from-store.sh
@@ -0,0 +1,64 @@
+#!/usr/bin/env bash
+# hydrate-from-store.sh — hidrata un FHS runtime KDE PROYECTANDO artefactos SELLADOS del store, SIN
+# reproduce ni rebuild (`hammer hydrate <hash> --into`). Es la vía correcta para el escritorio completo:
+# `install` reproduce-desde-fuente y el hashing determinista es frágil bajo el régimen dinámico (cada
+# consumidor puede recomputar un hash distinto de qtbase → rebuild). Aquí se toma la salida ya sellada
+# por la granja.
+#
+# Selección de hash por paquete: el artefacto store/<hash>-<name> MÁS RECIENTE con mtime ANTERIOR al
+# CUTOFF (default: hoy) — i.e. la cosecha FINAL de la granja (set coherente, todo construido contra el
+# mismo qtbase del cascade final), evitando los rebuilds ad-hoc de la sesión de hoy.
+#
+# Uso:  TARGETS="plasma-workspace kwin ..." ./hydrate-from-store.sh
+# Env:  RFS (def work/kde-store-rootfs), REPO (def work/repo-kde), CUTOFF (epoch; def inicio de hoy),
+#       LINK (static|dynamic, def static), STORE (def ./store)
+set -uo pipefail
+ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
+REPO="${REPO:-work/repo-kde}"; STORE="${STORE:-./store}"; LINK="${LINK:-static}"
+RFS="${RFS:-work/kde-store-rootfs}"; HAMMER="${HAMMER:-./target/release/hammer}"
+TARGETS="${TARGETS:-${TARGET:-qt6-qtdeclarative}}"
+# CUTOFF: por defecto medianoche de hoy (excluye rebuilds de la sesión). Pasá 9999999999 para no filtrar.
+CUTOFF="${CUTOFF:-$(date -d 'today 00:00' +%s 2>/dev/null || echo 9999999999)}"
+
+# cierre transitivo (nombres REALES qt6-*, no aliases: los artefactos del store se sellan por nombre real)
+CLOSURE=$(python3 - "$REPO/index.json" $TARGETS <<'PY'
+import json,sys
+idx=json.load(open(sys.argv[1])); targets=sys.argv[2:]
+by={p['name']:p for p in idx['packages']}
+BUILD_ONLY={'cmake','samurai','meson','ninja','bison','flex','gperf','util-macros','xorgproto',
+ 'xcb-proto','wayland-protocols','pkgconf','mako','markupsafe','packaging','python3','extra-cmake-modules',
+ 'qttools','qt6-qttools','intltool','perl','perl-xml-parser','gettext-tiny','m4','autoconf','automake','libtool'}
+def canon(n):
+    if n.startswith('qt') and not n.startswith('qt6-') and ('qt6-'+n) in by: return 'qt6-'+n
+    return n if n in by else ('qt6-'+n if 'qt6-'+n in by else n)
+seen=set(); order=[]
+def visit(n):
+    n=canon(n)
+    if n in seen or n not in by: return
+    seen.add(n)
+    for d in by[n].get('deps',[]): visit(d)
+    order.append(n)
+for t in targets: visit(t)
+for n in order:
+    if n not in BUILD_ONLY: print(n)
+PY
+)
+
+rm -rf "$RFS"; mkdir -p "$RFS"
+echo "==> hidratando desde el store SELLADO (cutoff<$(date -d @$CUTOFF 2>/dev/null||echo $CUTOFF)) → $RFS ($(echo "$CLOSURE"|wc -w) pkgs)"
+ok=0; miss=0
+for pkg in $CLOSURE; do
+  # artefacto <hash>-<pkg> más reciente con mtime < CUTOFF (nombre EXACTO, no substring)
+  hash=$(find "$STORE" -maxdepth 1 -type d -name "*-$pkg" -printf '%T@ %f\n' 2>/dev/null \
+         | awk -v c="$CUTOFF" '$1<c' | sort -rn | head -1 | awk '{print $2}' | sed "s/-$pkg\$//")
+  [ -z "$hash" ] && hash=$(find "$STORE" -maxdepth 1 -type d -name "*-$pkg" -printf '%T@ %f\n' 2>/dev/null \
+         | sort -rn | head -1 | awk '{print $2}' | sed "s/-$pkg\$//")   # fallback: el más nuevo aunque sea de hoy
+  if [ -z "$hash" ]; then printf "  ✗ %-24s (sin artefacto en el store)\n" "$pkg"; miss=$((miss+1)); continue; fi
+  if "$HAMMER" --store "$STORE" hydrate "$hash" --into "$RFS" --link "$LINK" >"$RFS/.hs-$pkg.log" 2>&1; then
+    printf "  ✓ %-24s %s\n" "$pkg" "${hash:0:12}"; ok=$((ok+1))
+  else
+    printf "  ✗ %-24s %s (ver .hs-%s.log)\n" "$pkg" "${hash:0:12}" "$pkg"; miss=$((miss+1))
+  fi
+done
+echo "==> $ok proyectados, $miss faltantes; .so totales: $(find "$RFS" -name '*.so*'|wc -l)"
+rm -f "$RFS"/.hs-*.log 2>/dev/null || true