Commit Graph
875 Commits
Author SHA1 Message Date
sergioandClaude Opus 4.8 3a64b603d7 catálogo objetivo P1: manifiesto de perfiles — el set de la distro deja de ser un string de shell
El destino de la distro vivía en 2 strings de product-userland-from-repo.sh, 1
de mirada-usb.sh y 55 tandas planas. Ahora en docs/state/targets.toml: un perfil
= una imagen enviable, listando sólo las RAÍCES (lo que se pide por nombre); la
clausura la calcula el grafo, no un humano.

4 perfiles: base (26), cli (46, hereda base), escritorio-mirada (14),
escritorio-kde (7 raíces, cola incoming-kde). scripts/targets.py expande hereda
(transitivo, con detección de ciclo) preservando el orden — mirada lo necesita.

Lift-and-shift verificado: las 3 listas expandidas son byte-idénticas a los
strings que reemplazan. Ninguna imagen cambia de contenido.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:41:01 -04:00
sergioandClaude Opus 4.8 c963b24b5e ADR 0012: registrar como PENDIENTE el dilema del árbol de fuentes (caché vs workspace)
Queda una carrera abierta que el lock de script NO cierra: `build-farm.sh` corre `xargs -P2` y dos
recetas de la misma cola que compartan dep se pisan igual, porque `fetch` nombra el árbol de forma
determinista y sin nada del constructor.

El fondo no es "falta un lock", es que el árbol cumple DOS papeles que se contradicen bajo
concurrencia: caché direccionada por contenido (clave = nombre+sha, re-extraer es caro) y workspace
mutable de build (lib.rs lo parchea, aísla el workspace Cargo y vendorea EN EL SITIO, y después el
sandbox lo bind-montea). Por eso un lock alrededor de la extracción parece correcto y NO lo es: un
segundo proceso puede borrar un árbol que un bwrap ya está compilando.

El ADR deja las tres salidas (lock por árbol durante todo el build / árbol privado por build /
separar caché inmutable + copia privada) con su contra concreta cada una —incluida que el worker
corre ext4, sin reflink, así que la copia de la opción C es real— y los tres números que habría que
medir para elegir en vez de opinar.

Se ancla en los dos sitios donde alguien va a chocar: el ADR indexado en docs/README.md (de paso se
agregan 0009-0011, que faltaban) y un comentario en el propio `fetch_tarball` que dice explícitamente
que no se arregle a medias.

Va como PENDIENTE y no decidido a propósito: hay otro agente sobre este repo y esto es lo que evita
que la carrera se "arregle" de una forma que parece bien y deja el bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:34:50 -04:00
sergio 062ae142aa estado: cosecha granja 2026-07-22T21:23:35Z — avance del árbol KDE 2026-07-22 17:23:35 -04:00
sergioandClaude Opus 4.8 142c417d36 farm: que el loop DIGA que está esperando el lock, en vez de parecer que trabaja
El `echo "ciclo: N recetas en $Q"` sale ANTES de pedir el lock, así que con la campaña corriendo el
journal mostraba el anuncio del ciclo y después nada: parece un loop trabajando y es un loop
bloqueado. Es la misma clase de problema que el "no encuentro el ejecutable zig" apuntando al
directorio equivocado — un log que miente cuesta horas de diagnóstico.

`flock -n` primero, y sólo si falla se anuncia la espera y se bloquea. Sin coste cuando el lock
está libre, que es el caso normal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:21:24 -04:00
sergioandClaude Opus 4.8 75c33fffe5 plan: catálogo objetivo — manifiesto de perfiles + estado wanted en el grafo
El grafo de lo que EXISTE cierra perfecto (768 nodos, 0 huérfanas, topo OK).
El de lo que la distro DEBE tener no existe: vive en 2 strings de shell de
product-userland-from-repo.sh, uno de mirada-usb.sh y 55 tandas planas. De ahí
que "cuánto falta" no tenga respuesta.

Plan en 5 piezas: targets.toml (perfiles = imágenes, listando RAÍCES y dejando
que la clausura salga del grafo), estado `wanted` + membresía de perfil en
build-state.py, siembra de aristas desde metadata upstream sin construir, y el
drenaje topológico donde harkaq reemplaza la arista hipotética por la medida.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:13:40 -04:00
sergioandClaude Opus 4.8 1b436a717f farm: lock compartido entre campana-deuda y el worker-loop
Los dos construyen sobre el MISMO work/, y `fetch` nombra el árbol de fuentes de forma determinista
(`work/sources/<receta>-<sha16>`, sin nada que dependa de QUIÉN construye) ⇒ dos procesos que
necesiten la misma DEP apuntan al mismo directorio: uno hace `remove_dir_all` mientras el otro
corre `tar -x`. El árbol queda a medias y devuelve "Directory not empty" (os error 39), y así se
queda hasta que alguien lo borra a mano.

No es teórico: la campaña de deuda y el loop pidieron `mesa` a la vez (el loop lo arrastraba desde
la cola KDE al invalidarse libdrm) y se llevó puesta media cascada GUI, con un error que no nombra
la causa. Casi borro ese árbol a mano antes de ver que había un bwrap montándolo.

Grano: la campaña toma el lock para toda su corrida; el loop, por ciclo de cola. Más fino rompería
el `xargs -P2` de build-farm.sh.

Dos honestidades en los comentarios, para no prometer de más:
  - `flock` NO es FIFO. El loop vuelve a pedirlo enseguida y le gana a la campaña que espera:
    medido, la campaña entra al terminar TODAS las colas, no entre dos. La ventana real es el
    IDLE_SLEEP. Por eso espera con techo (LOCK_WAIT=7200) y sale limpia en vez de colgarse.
  - Esto NO cierra la carrera del todo: el `-P2` interno del loop puede correr dos recetas de la
    misma cola que compartan dep, y ésas se siguen pisando. El arreglo de fondo es un lock POR
    ÁRBOL dentro de `fetch`, que cubriría los dos casos.

Verificado con flock real: exclusión mutua (la campaña entra justo cuando el loop suelta), salida
limpia por timeout con rc=0, y la no-equidad de flock medida en vez de supuesta.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 17:06:42 -04:00
sergio 0639d8a53d estado: cosecha granja 2026-07-22T20:51:43Z — avance del árbol KDE 2026-07-22 16:51:43 -04:00
sergioandClaude Opus 4.8 bbcdad1625 recetas: -Ddefault_library=both en las 5 libs meson que sólo publicaban .so
Mismo fallo raíz que ya se corrigió en pixman, ahora en la siguiente capa de la cascada GUI: meson
produce SÓLO .so por defecto, el consumidor enlaza estáticamente y el link muere. gtk4 lo dice
explícito: "unable to find static system library 'xkbcommon'" y "Static library 'drm' not found
for dependency 'libdrm'".

NO son "las 30 recetas meson". De las que no tienen `default_library=both`, 16 declaran
`=static` a propósito (gtk4, glib, cairo, pango…) y pasarlas a `both` sería otro cambio, no este
arreglo. Quedan 13 sin ninguna forma del flag —ojo, hay que buscar las DOS grafías,
`-Ddefault_library` y `--default-library`, o fcft se cuela como falso positivo— y de ésas sólo 5
producen librería que alguien enlaza: libxkbcommon (9 consumidores), libdrm (12), tllist (2),
libinput (1), seatd (1). El resto son ejecutables puros (bwrap, foot, iputils, usbutils, los
mesa-*) donde el flag no produce nada y sólo re-hashearía sin ganancia, o datos (wayland-protocols
son XML de protocolos, no compila librería).

`mesa` queda FUERA a propósito: produce librería y tiene 10 consumidores, pero es un build enorme
y no aparece en la cadena de fallo. Si hace falta, se decide aparte.

Radio de re-sellado medido con `hammer hash --check` antes/después: 8 recetas, de 754 selladas a
746. Las 4 tocadas que estaban selladas (libdrm, libxkbcommon, tllist, seatd) más 4 dependientes
que se invalidan (libepoxy y la cadena mesa, que cuelgan de libdrm). libinput ya estaba en deuda.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:49:27 -04:00
sergioandClaude Opus 4.8 a107649250 build: desacoplar el lab (.dev-fs) del padre del store
Atar el lab al PADRE DEL STORE era una suposición oculta y cara. Al anclar el store de un worker a
un volumen persistente, el lab pasó a buscarse en /mnt/cosecha/.dev-fs —que no existe— y toda
receta con `zig_version` murió con "no encuentro el ejecutable zig en …", un mensaje que apunta al
lugar equivocado: el zig estaba, y estaba bien, en /opt/hammer/.dev-fs/tools/. Dos campañas
leyeron eso como deuda de la cascada GUI. El commit anterior lo tapó con un bind-mount, que respeta
la suposición en vez de eliminarla; esto la elimina.

Son dos decisiones independientes: dónde se GUARDA lo sellado (almacenamiento) y dónde vive el
toolchain de desarrollo (entorno).

`work_root` NO se desacopla, y es deliberado: el seal final es un rename, que sólo es atómico
dentro del mismo filesystem, así que work DEBE seguir al store. Ése es un acoplamiento real, no una
suposición — el test lo fija para que nadie lo "arregle" de más.

Resolución del lab, de más explícito a más adivinado: `HAMMER_LAB` → hermano del store si existe
(preserva EXACTAMENTE el comportamiento histórico en la topología normal) → hacia arriba desde el
CWD, como git con .git (ésta es la que desacopla) → None, que cae al default de siempre para que
los errores sigan apuntando a un lugar previsible.

La parte que huele el entorno (env + CWD) queda sólo en `from_env_or_defaults`;
`defaults_for_store` se mantiene PURA para que los tests sigan siendo herméticos.

Los hashes NO se mueven: `artifact_hash` no recibe BuildConfig y `hash_inputs` sólo mezcla
contenido de la receta (source id, compiler, target, link, el string zig_version, patches, flags,
phases, hashes de deps) — ninguna ruta del lab. Verificado empíricamente además de por lectura:
`hammer hash --check` sobre el corpus da 754 selladas / 14 sin sellar de 768, calcado al grafo de
estado (12 deuda + 2 nunca). Si algún hash se hubiera movido, una sellada diría NO-SELLADO.

Verificado también end-to-end: con un store cuyo padre no tiene .dev-fs, hammer sube desde el CWD,
encuentra el lab y construye (antes moría en el acto); y la topología normal sigue dando cache-hit
instantáneo con el mismo hash.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:41:32 -04:00
sergioandClaude Opus 4.8 ebd8e7f929 farm: anclar el store con bind-mount, no symlink — el ln -s rompía toda receta con zig_version
Regresión introducida ayer al mover el store al volumen persistente (`ln -sfn /mnt/cosecha/store
$REMOTE/store`). El síntoma se leía como deuda de la cascada GUI y era otra cosa.

hammer deriva el lab del PADRE DEL STORE: `BuildConfig::defaults_for_store` hace
`project_root = store_root.parent()` y de ahí `.dev-fs/{alpine,tools/zig,cache}`. Con el store
symlinkeado, resuelve a /mnt/cosecha/store ⇒ busca /mnt/cosecha/.dev-fs, que no existe, y muere con
"no encuentro el ejecutable zig en …" — un mensaje que apunta al lugar equivocado: el zig 0.13.0
está, y está bien, sólo que en /opt/hammer/.dev-fs/tools/.

Golpea SÓLO a las recetas que fijan `zig_version` (20 en el corpus), porque son las únicas que
resuelven un zig hermano del por defecto. Por eso la campaña del 05:41 dio 26 selladas / 11
fallidas y las 11 eran exactamente ésas (tllist pango gtk4 libadwaita gtksourceview fcft
*-hello hammer-edit dwarves), y por eso la de las 19:48 —que era justo la lista de deuda— dio 0/14.

El bind-mount mantiene las DOS invariantes a la vez: los datos siguen en el volumen (sellar =
persistir, que es la razón del volumen) y `$REMOTE/store` vuelve a ser una ruta real bajo $REMOTE
⇒ el padre es /opt/hammer y .dev-fs se encuentra. Verificado en el worker vivo: con el bind-mount
y SIN overrides de entorno, libinput pasa la resolución de zig y falla por su dep real de Python.

Se persiste en fstab para que sobreviva al reboot, y el chequeo de verificación pasa de `test -L`
a `mountpoint -q`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:26:52 -04:00
sergioandClaude Opus 4.8 f3206d7520 latido: colgar el latido de la sesión, no del init (+ lock anti-solapamiento)
El latido vivía sólo en el crontab y eso resultó frágil: este laptop arranca a veces con
`init=/usr/local/sbin/arje-zero` y a veces con OpenRC (KDE), y no hay systemd en ninguno de los
dos. `cronie` es un servicio OpenRC ⇒ en el arranque arje no existe y el latido no late. Peor: no
late EN SILENCIO. Se destapó tras un corte sucio del 2026-07-22, en el que además se vio el
runlevel `default` quedar a medias (cronie/metalog/acpid caídos, NetworkManager/dbus arriba) ⇒ ni
siquiera arrancando OpenRC era garantía.

El costo del síntoma es caro y callado: sin latido el hub no siembra, el worker agota la cola y se
queda idle quemando € sin moler.

`latido.sh` cuelga el latido de la SESIÓN: cualquier terminal, en cualquier arranque, asegura que
haya exactamente un latido vivo (`--ensure` desde ~/.zshrc, ~5ms y mudo si ya hay uno). Sin root,
sin unidad de servicio, sin mantener lo mismo por duplicado en dos inits. Contra asumido: no late
sin sesión abierta — es un laptop, no un server, y el primer ciclo dispara al instante de abrir la
terminal (el cron esperaba hasta 30 min al próximo tick).

El lock va DENTRO de cosecha-cron.sh, no en el llamador, para que valga venga de donde venga. Eso
además tapa un bug latente que ya existía: nada impedía que dos ciclos se solaparan haciendo rsync
sobre el mismo store y `git commit`+`push` a la vez. Con el lock, el crontab puede quedarse: bajo
KDE dispara cron, bajo arje dispara la sesión, y nunca se pisan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 16:21:27 -04:00
sergio 660f86c3f3 estado: cosecha granja 2026-07-22T10:31:59Z — avance del árbol KDE 2026-07-22 06:31:59 -04:00
sergioandClaude Opus 4.8 e09a48a69e farm: tandas.sh — encadenar campañas en orden, sin solaparse
En serie y no en paralelo: dos `hammer build` simultáneos compiten por el mismo work/sources y por
el watchdog de disco del worker-loop. El paralelismo vive DENTRO de cada build (-j$(nproc)).

En tandas y no en una lista plana: una tanda es una unidad topológica (la cadena GUI, el stack
wayland, los kernels). Cuando una se cae por su raíz —como pango arrastró a 7 recetas— se ve de un
vistazo en el resumen y se re-lanza sola; una lista de 40 nombres no dice nada al fallar.

Espera a que termine la campaña en vuelo, así se encola mientras otra muele.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 22:51:11 -04:00
sergioandClaude Opus 4.8 a8bb83add5 why-differs-barrido: no llamar "no-reproducción" a lo que no está probado
El artefacto guarda su recipe.toml pero NO los hashes de sus deps, y el ArtifactHash sí los
incluye ⇒ dos sellados con el mismo recipe.toml pueden diferir LEGÍTIMAMENTE porque cambió una
dependencia. La primera versión gritaba "NO-REPRODUCCIÓN REAL" sobre 128 paquetes; eso era un
superconjunto, no una prueba.

Ahora separa por la CAUSA, que sí discrimina: si TODO lo que difiere son metadatos que un cambio
de dep no explica (MTIME de gzip, cabecera ar, ruta de build, secciones ELF informativas), es
no-determinismo probable; si difiere .text/.data o hay ficheros de más, es indistinguible de un
cambio de dep sin reconstruir. 128 → 92 probables + 36 no concluyentes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 22:42:32 -04:00
sergioandClaude Opus 4.8 6ad078d035 pixman: -Ddefault_library=both — el fallo raíz de pango y de toda la cascada GUI
meson produce SÓLO .so por defecto. pango corre un test de enlace ("Cairo is built with FreeType
and FontConfig support") que enlaza cairo-ft ESTÁTICAMENTE; sin `libpixman-1.a` el enlace falla y
meson concluye `ERROR: cairo-ft does not have the required FontConfig support` — un mensaje que
apunta al lugar equivocado: no falta fontconfig, falta pixman.a.

Detectado en el barrido de deuda en la granja (2026-07-21): de 37 recetas, glib/harfbuzz/cairo/
gdk-pixbuf/graphene sellaron, y pango arrastró en cascada a gtk4, libadwaita, gtksourceview,
gtk4-hello, adwaita-hello, sourceview-hello y hammer-edit. `pixman` YA estaba declarado en
[deps].build de pango y cairo — el problema no era la declaración sino el artefacto.

`both` y no `static`: mirada-compositor y compañía siguen enlazando la dinámica.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 22:42:32 -04:00
sergioandClaude Opus 4.8 895401b479 cierre #2 del SDD 17: hammer why-differs — el diffoscope propio
Cuando un artefacto no reproduce, el store sólo sabe decir "el hash no coincide" y el resto es
trabajo artesanal. Esto responde POR QUÉ, en términos de la CAUSA y no del byte:

- gzip con MTIME embebido (bytes 4..8)      → remedio: `gzip -n`
- cabecera `ar` de un `.a` (mtime/uid/gid)  → remedio: modo determinista (`ar D`)
- secciones ELF, con lectura experta: sólo `.comment` ⇒ otra versión de compilador; sólo
  `.debug_*` ⇒ rutas de build; sólo `.symtab`/`.dynsym` ⇒ orden de símbolos (código idéntico);
  sólo build-id ⇒ residuo, no causa raíz. En un `.a` dice QUÉ MIEMBRO difiere.
- ruta del árbol de build embebida, texto (línea que difiere), y bytes como último recurso.

Y sobre todo trae la EVIDENCIA, no sólo la hipótesis: para las secciones de texto extrae las
cadenas que están en un ELF y no en el otro. Caso real que lo motivó (alsa-lib): la
interpretación decía "típicamente rutas de build" y la evidencia mostró
`/src/target/release/build/libsodium-sys-<hash-cargo>/out/…`. Sin la cadena era una corazonada;
reproducir eso a mano cuesta varios readelf, la herramienta lo da en 40ms.

Descenso, no comparación total: sólo baja donde los hashes difieren (el cruce con format/
reconcile del SDD 17). Sin dependencias externas — parsers gzip/ar/ELF propios, como manda el
ADR 0004: un diffoscope de verdad se apoya en medio mundo de binarios ajenos.

`--json` para el bucle agéntico; exit 0 si reproduce, 1 si diverge (encadenable en scripts).
`scripts/why-differs-barrido.sh` lo pasa por todo el store y separa los dos casos que se
confunden a ojo: recipe.toml distinto (divergencia esperada) vs recipe.toml IDÉNTICO y artefacto
distinto (no-reproducción a investigar).

5 tests nuevos; los 142 de hammer-core siguen en verde.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 22:35:03 -04:00
sergioandClaude Opus 4.8 6b38b46293 farm: campana-deuda.sh — el HUB dicta la lista, el worker sólo muele
`saldar-deuda-static.sh` calcula la deuda en vivo con `hammer hash --check` contra el store
LOCAL. En el worker ese store es PARCIAL (el del volumen) ⇒ mide 716 en vez de 37. Es la regla
de siempre con otra cara: el worker MIDE, el hub CLASIFICA. Acá el hub decide (DRY=1) y el
worker ejecuta una lista explícita, con las raíces del stack GUI primero (glib unblocks=14 →
harfbuzz/cairo/pango → gtk4 → libadwaita) para que la cascada caiga cuanto antes.

Incluye el PATH del `go` del store: `go mod vendor` corre host-side y una sesión SSH no
interactiva no carga /etc/profile ni cargo/env. Y HOME por defecto, que systemd-run no hereda
(con `set -u` era fatal).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 22:10:31 -04:00
sergioandClaude Opus 4.8 c21602b707 farm: fix — farm-up tiraba el store HORNEADO de la golden (rm -rf) y el worker medía media distro como deuda
El anclaje del store al volumen hacía `rm -rf $REMOTE/store` antes de symlinkear. Eso borra
justo el catálogo cacheado que es la razón de ser de la imagen golden ("arranca con TODO el
catálogo ⇒ cache-hit instantáneo"). Consecuencia medida hoy: el worker calculó 716 recetas de
deuda donde el hub medía 37, y se puso a reconstruir media distro (fallando, además, porque
sin `go` en el PATH host-side las recetas Go mueren en `go mod vendor`).

Fix: fusionar en vez de borrar. El store es CAS (nombre = hash) ⇒ `mv -n` al volumen es seguro
por construcción: no pisa lo que el volumen ya tiene, y lo horneado queda disponible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 22:10:31 -04:00
sergioandClaude Opus 4.8 24d9fa7fdd kde/deps: libXft — declarar expat y dejar asentado por qué queda diferido
La cadena de deps .pc SÍ quedó resuelta (libpng+expat añadidos); el muro restante es PIC
(freetype.a/libpng.a/expat.a no-PIC dentro de libXft.so), no resolución. Nadie lo consume:
plasma-workspace (su único declarante) selló sin él — KDE es Wayland, Xft es X11 legacy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 21:44:55 -04:00
sergioandClaude Opus 4.8 081fdd8a34 kde/metal: fix — el prompt nacía en el SERIAL; getty en tty1 (por eso el USB "no abría")
Síntoma: el USB de escritorio arranca, se ve el kernel, "arje-zero: despierta como PID 1",
el remount de la ext4 y el stack-depth de netup… y ahí la pantalla se congela para siempre.

Causa: el CMDLINE horneado es "console=tty0 console=ttyS0,115200 …" y el kernel hace
/dev/console = la ÚLTIMA console= ⇒ ttyS0. La seed card de la base metal supervisa un solo
getty, sobre `console` ⇒ en una laptop sin puerto serie el shell nace INVISIBLE. Los printk
sí van a las DOS consolas: por eso se ven los mensajes del kernel y después silencio. El
sistema nunca estuvo colgado — reproducido en OVMF: por el serial hay shell root (PID 98,
sshd arriba); en pantalla, nada. Nunca se vio antes porque toda validación fue -nographic,
donde el serial ES la pantalla (y el comentario de install-image-efi.sh afirmaba lo contrario:
"console=tty0 al final ⇒ el stdout va a la PANTALLA").

Fix en el script de imagen, no en el cmdline: un getty sobre tty1 es independiente del
cmdline (en metal siempre hay VT) y no obliga a recompilar/re-sellar el kernel (~60min).
Se conserva el getty de `console` para debug por serial.

- seed card del rootfs fundido += nodo tty1-getty (clona console-getty, argv → tty1)
- /usr/bin/console-login: muestra el motd y exec sh. Exporta PATH: arje-zero lanza el getty
  con envp VACÍO ⇒ sin él no se resolvía ni `cat` ni `plasma-start`.
- ambos escriben rompiendo el hardlink (os.replace / rm -f): $MERGED es cp -al de la base,
  escribir in-place mutaba work/metal-rootfs y toda imagen que comparta el inodo — ya había
  pasado con /etc/motd.

Validado en OVMF con pantalla (no -nographic): motd + prompt "/ #" visibles, y `ls /dev/dri`
tecleado por QMP responde card0 (teclado + PATH OK).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 21:32:06 -04:00
sergio 6c1a4a8b37 estado: cosecha granja 2026-07-21T14:30:04Z — avance del árbol KDE 2026-07-21 10:30:04 -04:00
sergioandClaude Opus 4.8 0a3a00eb6e kde/metal: fix — la imagen dual usa linux-metal-dual (no linux-generic, que no bootea por EFI-stub)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 16:52:31 -04:00
sergioandClaude Opus 4.8 746d2dedd1 kde/metal: andamiaje escritorio dual-GPU (Intel i915 + NVIDIA nouveau) por software
Objetivo: USB "live" que arranca el escritorio KDE en metal y aguanta ambas máquinas
(TigerLake/iris y Pascal/nouveau), render por software (mesa-llvmpipe soberano) sobre
el KMS del kernel — uniforme en cualquier GPU, reusa los fixes validados en QEMU.

- recipes/linux-metal-dual.toml: = linux-generic (dual-GPU i915+nouveau+radeon) + el
  CMDLINE de pivote de linux-metal (initrd=/initramfs.cpio.gz rdinit=/init) para bootear
  por EFI-stub directo desde el ESP (root en disco, no RAM). Ni linux-generic (sin pivote)
  ni linux-metal (nouveau OFF) servían solos.
- scripts/kde/metal-desktop-image-dual.sh: arma la imagen — base metal + KDE + inyección
  mesa-llvmpipe/libLLVM/musl + firmware nvidia gp106 (nouveau modeset Pascal) + kernel
  linux-metal-dual.
- scripts/kde/plasma-start-metal-sw.sh: launcher software-GL para metal, detección de GPU
  real (i915/nouveau/amd), lanzamiento MANUAL (bringup seguro), con los fixes de la sesión
  QEMU (GBM_ALWAYS_SOFTWARE, USE_MODIFIERS=0, KWIN_COMPOSE=Q, FORCE_SW_CURSOR, cursor/iconos
  breeze, D-Bus sesión+sistema).

Pendiente: build del kernel (~60min, en curso) → rebuild imagen → validar OVMF → quemar USB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 16:32:46 -04:00
sergioandClaude Opus 4.8 a1ceaf6ff9 docs: runbook para relanzar el escritorio KDE en QEMU
Cómo relanzar (run-qemu-desktop.sh), verificar sin ojos humanos (screendump por el
monitor QEMU + STATUS del serial), reconstruir la imagen, y la tabla de los 7 fixes
que lo hacen andar + los gotchas de operación (serial stale, pkill auto-mata, OVMF
VARS stale → TianoCore). Para relanzar/verificar, no rediagnosticar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 14:01:02 -04:00
sergioandClaude Opus 4.8 44d50a76b5 kde/qemu: KWIN_FORCE_SW_CURSOR=1 — cursor visible y estable
El tema de cursor ya cargaba (sin "Failed to load cursor theme") pero el cursor
seguía apareciendo/desapareciendo: kwin usaba el plano de cursor por HARDWARE del
DRM, que en virtio-gpu con render software no se presenta bien. KWIN_FORCE_SW_CURSOR=1
⇒ kwin compone el cursor dentro del framebuffer principal (software) ⇒ visible y
estable. Verificado por screendump: la flecha breeze aparece compuesta en el scanout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:36:28 -04:00
sergioandClaude Opus 4.8 abf4e25c8f kde/qemu: tema de cursor breeze — mata "Failed to load cursor theme"
kwin buscaba el tema "default" (inexistente) ⇒ cursor invisible. El rootfs trae
breeze_cursors (115 cursores). Fix: XCURSOR_THEME=breeze_cursors + XCURSOR_PATH +
kcminputrc [Mouse] cursorTheme. El error desapareció del log (0 ocurrencias).

Estado final verificado por screendump: escritorio Plasma 6 completo (wallpaper +
panel con launcher/pager/bandeja + reloj), estable (139=0; el run previo aguantó
7.5 min a +450s con kwin=1 plasmashell=1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:30:32 -04:00
sergioandClaude Opus 4.8 d724db6556 kde/qemu: -vga none — el escritorio por fin SE VE (no más TianoCore congelado)
El escritorio booteaba y kwin pintaba por dentro (serial: kwin vivo), pero el scanout
REAL (verificado por `screendump` del monitor QEMU → PNG) seguía congelado en el logo
TianoCore. Causa: sin -vga none, QEMU añade una VGA estándar por defecto ADEMÁS del
virtio-gpu-pci. OVMF pinta su GOP en la VGA estándar ⇒ el kernel la envuelve con
simpledrm (card1) y ESE es el scanout que se muestra; kwin pinta en virtio-gpu (card0),
otro device que no se ve. Como son devices distintos, virtio no expulsa simpledrm ⇒
pantalla clavada en el frame EFI. (Con VARS reusados el timing lo tapaba; al resetear
los VARS quedó expuesto.)

Fix: -vga none ⇒ virtio-gpu-pci es el único display ⇒ OVMF pinta ahí ⇒ virtio-gpu-DRM
expulsa simpledrm (mismo device) ⇒ un solo card0, y el modeset de kwin toma la pantalla.
Verificado por screendump: se ve el wallpaper de Plasma 6 (1592x960), no TianoCore.

Herramienta nueva: -monitor unix socket en run-qemu-desktop.sh ⇒ `screendump` captura
el framebuffer REAL del guest (oráculo independiente de la ventana gtk, que puede
congelarse si mirada reinicia su Xwayland).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:22:07 -04:00
sergioandClaude Opus 4.8 82cbdb7e54 kde/qemu: KWIN_COMPOSE=Q (QPainter) — esquiva el segfault del rasterizador llvmpipe
El "pinta y vuelve a negro" NO era el output config: era kwin SEGFAULTEANDO (139) a
los segundos de pintar y respawneando. Core dump (core.llvmpipe-0, 176MB) + gdb del
host: el crash está en un hilo rasterizador de llvmpipe —
  #0 util_fill_rect  #1 util_fill_box  #2 lp_rast_clear_color  #3 rasterize_scene
  #4 thread_function (llvmpipe worker)
llvmpipe peta al limpiar el color buffer (bug de stride/geometría o vectorización del
fill en este entorno virtio+kms_swrast).

Fix: KWIN_COMPOSE=Q ⇒ kwin compone con QPainter (raster de Qt directo al buffer, SIN
armar escenas llvmpipe) ⇒ nunca entra a lp_rast_clear_color. (El segfault viejo con =Q
era el de gbm-init, ya resuelto por GBM_ALWAYS_SOFTWARE; llvmpipe sigue disponible para
gbm/EGL.) Verificado: QPainter compositing initialized, 139=0, kwin vivo a +15s/+30s.

+ STATUS monitor arreglado (busybox no tiene pgrep -c ⇒ pgrep|wc -l) con timestamp.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:09:38 -04:00
sergioandClaude Opus 4.8 2f99446ca1 kde/qemu: un solo output virtio (KWIN_DRM_DEVICES) — mata el negro-tras-pintar
Tras arreglar el FB (modifiers), el escritorio pintaba con plasmashell y volvía a
negro. Causa: kwin veía DOS outputs — virtio-gpu ("QEMU Monitor") Y el efifb/simpledrm
("Unknown-1", framebuffer EFI read-only que no fue expulsado al cargar virtio-gpu).
El conflicto entre ambos rompía el import/destroy de buffers EGL (spam
eglDestroyImageKHR EGL_BAD_PARAMETER) y tiraba la conexión de plasmashell
("error in client communication") ⇒ negro.

Fix: plasma-start detecta el card cuyo driver es virtio_gpu (robusto al numerado
card0/card1 que cambia entre boots) y lo pasa por KWIN_DRM_DEVICES ⇒ kwin ignora el
efifb. Resultado: un solo output, atomic modeset, y TODOS los errores correlacionados
con el negro a 0 (client-comm, eglDestroyImage, FB fail, output disabled). kwin carga
efectos normalmente.

+ monitor de estado cada 15s al serial (kwin/plasmashell vivos) para diagnóstico.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:01:16 -04:00
sergioandClaude Opus 4.8 6c0ef0867d kde/qemu: KWIN_DRM_USE_MODIFIERS=0 — mata la pantalla negra (FB rechazado por virtio)
Con el segfault ya resuelto, el escritorio salía NEGRO: kwin encontraba el conector
(1280x800) pero "Failed to create a framebuffer: Invalid argument" ⇒ "Failed to find
a working output layer" ⇒ sin scanout. Se veía el wallpaper un instante (el modeset
inicial usa un buffer dumb sin modifiers) y luego negro (los buffers del compositor
van por drmModeAddFB2WithModifiers).

virtio-gpu ADVIERTE el CAP de modifiers pero rechaza el modifier explícito (aún
LINEAR=0) con EINVAL. Fix: KWIN_DRM_USE_MODIFIERS=0 ⇒ kwin usa drmModeAddFB2 plano
(modifier implícito) que virtio sí acepta. "Failed to create a framebuffer" pasó de
2526 a 0; input Qt fluyendo; sin crash.

También: quitado KWIN_DRM_NO_AMS (era corazonada del segfault; atomic es el path
sólido en virtio) y arreglado el stream vivo del kwin.log al serial (busybox sed no
soporta -u ⇒ tail -f directo, sin sed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 11:52:59 -04:00
sergioandClaude Opus 4.8 6e5bc48c58 kde/qemu: system D-Bus — mata el spam udisks2 y completa el escritorio
plasmashell/solid spameaba "kf.solid.backends.udisks2: Not connected to D-Bus
server" en cada poll: sólo había bus de sesión, no de sistema. Ahora plasma-start
levanta dbus-daemon --system (socket estándar /run/dbus/system_bus_socket).

- qemu-desktop-image.sh: siembra el usuario messagebus:81 en passwd/group
  (system.conf hace setuid a él; el passwd base no lo traía). Rompe el hardlink
  read-only con `mv` de un temp, como el patch del seed.
- plasma-start-qemu.sh: arranca el system bus antes del de sesión + copia
  machine-id a /var/lib/dbus.

No hay udisksd/upowerd reales en el rootfs ⇒ sin discos/batería, pero el bus
existe y solid deja de errorear. Verificado: "Not connected" pasó de spam
continuo a 0; system bus ON; kwin+plasmashell+KSplash+kioworker vivos; 139=0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 11:43:34 -04:00
sergioandClaude Opus 4.8 b53a822b42 kde/qemu: MURO 2 caído — GBM_ALWAYS_SOFTWARE=1 y el escritorio PINTA
El segfault-139 de kwin en QEMU NO era llvmpipe ni el composite: diagnóstico
por core dump + gdb del host (backtrace #0 0x0 ← dri2_initialize_drm ← eglInitialize
← EglDisplay::create ← DrmBackend::initialize; call *0x30(vtable swkmsMesaCoreExtension)
= queryCompatibleRenderOnlyDeviceFd = NULL en el driver software).

virtio-gpu-pci sin 3D expone un nodo KMS-only (no render-capable). En mesa 24.0.9
gbm sólo pone software=true vía dri_screen_create_sw, al que sólo se llega con
GBM_ALWAYS_SOFTWARE o si dri_screen_create falla. Con MESA_LOADER_DRIVER_OVERRIDE
kms_swrast, dri_screen_create tiene éxito ⇒ software=false ⇒ dri2_initialize_drm
llama el vtable NULL ⇒ SIGSEGV. Fix: GBM_ALWAYS_SOFTWARE=1 en plasma-start.

- plasma-start-qemu.sh: + GBM_ALWAYS_SOFTWARE=1 (el fix); + andamiaje de diagnóstico
  gated por DIAG=1 (logging kwin/mesa/egl verboso, stream vivo del kwin.log al serial,
  core dumps a /core + sync, sleep 3600 para congelar el serial).
- run-qemu-desktop.sh (nuevo): run reproducible (DISP=none/gtk, TIMEOUT, VARS OVMF rw).
- qemu-desktop-image.sh: restaurado bit +x.

Verificado: headless (139=0, sin queryCompatible) y ventana gtk en DISPLAY=:0 —
kwin_wayland + plasmashell + KSplash + kioworker vivos.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 11:30:45 -04:00
sergioandClaude Opus 4.8 1c6b4ed54b kde/mesa: cadena llvmpipe soberana — LLVM18+mesa-llvmpipe from source, GBM cruzado
mesa-llvmpipe con gcc/libstdc++ (matchea el ABI de libLLVM; zig-cc usaba libc++ ⇒
undefined symbols en el JIT). qemu-desktop-image inyecta libLLVM.18+libgcc_s+libstdc++.
HITO: kwin toma DRM master de virtio-gpu, crea el GBM device (llvmpipe, ya no softpipe),
expone compositor y ACEPTA clientes. Muro restante: kwin segfaultea (139) al componer la
1ra superficie con llvmpipe — necesita backtrace (gdb) para diagnosticar.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 16:15:21 -04:00
sergioandClaude Opus 4.8 8556ef9e1c llvm18: -include cstdint (gcc/clang 15 no arrastran <cstdint> ⇒ uint64_t undeclared en SmallVector.h)
LLVM 18.1.8 usa uint64_t sin incluir <cstdint>; libstdc++ 15 ya no lo trae transitivo.
Fix estándar upstream vía flag. Pasó de morir en [14/2378] a compilar limpio.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 15:28:28 -04:00
sergioandClaude Opus 4.8 ebb3fcdb0e mesa: receta llvm18 + mesa-llvmpipe para el escritorio KDE en VM
llvmpipe necesita libLLVM (softpipe no bindea el GBM/EGL que kwin exige). El LLVM 22 del
sandbox no compila con mesa 24.0.9 ⇒ sellamos LLVM 18.1.8 desde fuente (rango soportado),
patrón gcc-de-gueto + static-libstdc++. mesa-llvmpipe = mesa-swrast con -Dllvm=enabled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 15:00:48 -04:00
sergioandClaude Opus 4.8 dafd97be79 kde: pipeline escritorio QEMU (qemu-desktop-image + plasma-start-qemu)
Imagen QEMU booteable con Plasma auto-lanzado: merge base+kde-metal-rootfs, inyección
del loader musl (la imagen metal nunca pudo correr KDE dinámico — base estática + KDE
sin libc), parche swrast, seed patcheado (console-getty ejecuta plasma-start), PATH/dbus/
fuentes/iconos. HITO: kwin toma DRM master de virtio-gpu y expone wayland-0 (lo que el
anidado no podía). Muro pendiente: GBM/EGL de software — mesa es softpipe, falta llvmpipe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 14:21:37 -04:00
sergioandClaude Opus 4.8 7964e45bfa kde: nested-app siembra kdeglobals + activa KDEPlasmaPlatformTheme (iconos+Breeze)
Sin kdeglobals Qt caía al tema de iconos hicolor (vacío) ⇒ dolphin pelado. Ahora
siembra [Icons]Theme=breeze + QT_QPA_PLATFORMTHEME=kde ⇒ aplica iconos + estilo
Breeze + colores. El motor SVG (libqsvgicon/libqsvg/libQt6Svg) ya estaba hidratado.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 22:03:48 -04:00
sergioandClaude Opus 4.8 44d06a899e kde: kwindowsystem KWINDOWSYSTEM_QML=ON — construye el módulo QML org.kde.kwindowsystem
Elimina el injerto del artefacto viejo que destrababa la sesión Plasma anidada.
Desktop.qml de plasma-workspace importa ese módulo; sin él plasmashell falla
"module not installed". qtdeclarative ya estaba en deps. Rebuild va a la granja.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 20:04:59 -04:00
sergioandClaude Opus 4.8 e6a62a04fb kde: anotar deuda KWINDOWSYSTEM_QML=OFF en el recipe (rompe Desktop.qml de plasmashell)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 17:29:11 -04:00
sergioandClaude Opus 4.8 4d418cd07c kde: nested-plasma.sh — sesión Plasma COMPLETA anidada en mirada (kwin+plasmashell)
kwin_wayland como compositor anidado en Xwayland :0 (backend X11; el wayland-nested
segfaultea en gbm/renderD128) + compositado por SOFTWARE (llvmpipe/swrast) + QtQuick
software backend (sin él plasmashell no crea contexto GL/EGL). plasmashell carga la
corona org.kde.plasma.desktop y renderiza. Cliente wayland, no DRM master ⇒ seguro
sobre la sesión viva. Verificado los 3 procesos vivos + Desktop.qml cargado.

Requirió hidratar al rootfs: kactivitymanagerd (daemon aparte de plasma-activities),
mesa-swrast, y GRAFT del módulo qml org.kde.kwindowsystem (ver deuda en recipe).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 17:28:18 -04:00
sergioandClaude Opus 4.8 f78fdeef94 kde: QT_QPA_FONTDIR + SHELL en nested-app — mata los tofu
qtbase se construyó SIN fontconfig (libQt6Gui no enlaza libfontconfig) ⇒ Qt usa su
motor básico que busca fuentes en /usr/lib/fonts (vacío) → glyphs tofu. Atajo sin
rebuild: QT_QPA_FONTDIR=/usr/share/fonts/dejavu hace que el motor básico cargue los
ttf directo. SHELL=/bin/sh da shell a konsole (zsh no está en el rootfs). Fix 'correcto'
(qtbase con FEATURE_fontconfig) = rebuild que re-hashea el escritorio → deuda para la granja.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 17:13:12 -04:00
sergioandClaude Opus 4.8 b2d80e6f69 kde: lanzador anidado (nested-app.sh) — apps KDE hidratadas como ventana en mirada
Corre una app KDE del rootfs hidratado como cliente wayland ANIDADO en el compositor
mirada vivo (WAYLAND_DISPLAY=/run/mirada-wayland), vía bwrap overlay (alpine+kde-rootfs
como /). Cliente, no master: renderiza por renderD128/iris, cero riesgo al display.
Resuelve dbus de sesión (root-en-userns + machine-id + /run/dbus). konsole y dolphin
verificados corriendo en vivo. Fix portabilidad: run-plasma-headless.sh += --setenv PATH.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 17:04:16 -04:00
sergioandClaude Opus 4.8 4ce086edcc openrc: corregir el marco de la receta — init alternativo, NO supervisor sobre arje
El comentario justificaba openrc como 'el supervisor de servicios que le falta a arje':
falso. arje-zero es autónomo (PID1 + gestiona servicios). openrc es un init ALTERNATIVO
que la distro ofrece y COMPITE con arje por PID1, no una capa que se apila. Reencuadrado
como el patrón para el resto de la oferta multi-init (runit/s6/dinit). Corrige también la
nota de deps: libcap NO es opcional en Linux (dependency incondicional en meson.build).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 16:37:49 -04:00
sergio b209205ba8 estado: cosecha granja 2026-07-18T20:37:15Z — avance del árbol KDE 2026-07-18 16:37:15 -04:00
sergioandClaude Opus 4.8 ecc4a0dc6f openrc 0.62.6: + libcap (requerido en Linux sin toggle) → sella; promovido a canónico
openrc es el supervisor de servicios (rc-service/rc-status/rc-update/start-stop-daemon)
que faltaba para una distro instalable — arje-zero es PID1, openrc gestiona /etc/init.d.
El build fallaba por libcap: meson.build lo pide incondicional en Linux (dependency
'libcap' >=2.33, sin -Dlibcap). Añadido → binarios estáticos-musl sellados.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 16:16:59 -04:00
sergio 441c0dfba9 estado: cosecha granja 2026-07-18T19:01:00Z — avance del árbol KDE 2026-07-18 15:01:00 -04:00
sergioandClaude Opus 4.8 9d96d4f59c kde: diferir libXft — huérfano PIC, nadie lo consume
libXft es el único de los 11 que no sella. Falla por PIC (freetype/libpng/expat
estáticas no-PIC dentro de un .so), riesgo que su propio header ya predecía. NADIE
lo consume: plasma-workspace (su único declarante) selló SIN él porque KDE es Wayland.
Forzarlo sellaría un artefacto muerto. A .deferred/ para que el loop no lo reintente;
la cadena de deps .pc quedó resuelta (libpng+expat), sólo resta el muro PIC (Capa 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 14:59:41 -04:00
sergio 01bfee7a6a estado: cosecha granja 2026-07-18T16:59:38Z — avance del árbol KDE 2026-07-18 12:59:39 -04:00
sergio 25ce9c0cce estado: cosecha granja 2026-07-18T16:22:58Z — avance del árbol KDE 2026-07-18 12:22:58 -04:00
sergioandClaude Opus 4.8 7ba0b8abf9 kde: destrabar los 11 que no sellaban — 2 causas raíz
- libcanberra: URL de 0pointer.de muerta → mirror BLFS/OSUOSL (mismo tarball, sha256
  idéntico verificado). Desbloquea 7: knotifyconfig/konsole/plasma-{desktop,pa,workspace}/powerdevil.
- cadena X11 (deps .pc faltantes, recetas ya existían): libICE/libSM += xtrans;
  libXft += libpng (freetype2.pc); libXtst += libXfixes (xi.pc). Desbloquea plasma-workspace.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 12:06:19 -04:00