Commit Graph
191 Commits
Author SHA1 Message Date
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 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 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 50d51bb4a8 granja: cosecha-heartbeat.sh — latido de cosecha bajo mirada (arje-zero sin crond)
El crontab */30 no dispara cuando el laptop bootea en mirada (PID1=arje-zero, sin
crond). Envuelve el one-shot cosecha-cron.sh en un loop setsid — el mecanismo que sí
corre sin root y sobrevive al fin de sesion (no al reboot).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 11:52:16 -04:00
sergioandClaude Opus 4.8 19a3cda8ca granja: el worker recompila hammer al arrancar (evita el fósil de la golden)
Raíz de una noche de KDE atascada: la golden del 29-jun horneaba un hammer SIN el fix del
overlay lowerdir (e69eaaa, 12-jul) ⇒ toda receta de muchas deps (kio=52, kwin, plasma-*)
desbordaba el límite de 4KB de mount options y el sandbox ni arrancaba. 43 recetas 'en cola'
que en realidad reventaban al instante. El source llega fresco por farm-sync pero nadie
recompilaba. Ahora el worker-loop hace 'cargo build --release --bin hammer' al arrancar
(~24s cacheado). Binario del worker vivo ya rebuildeado a mano y verificado: kio cruza el
mount (funde 52 deps en una capa) y configura.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 05:07:13 -04:00
sergioandClaude Opus 4.8 20666643e2 granja: estado-granja.sh — foto on-demand (worker+avance KDE+cron) sin esperar el latido
Chequeo manual por si venís antes del cron o no corrió: salud del worker y qué muele,
barra de avance KDE desde el grafo, y últimas líneas del ciclo de cosecha. Filtra el
self-match del pgrep (el ']+.toml' que se colaba).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 21:55:44 -04:00
sergioandClaude Opus 4.8 2a52d7804f granja: cron infinito de cosecha+siembra (recoger/sembrar cada 30min) — latido sin tokens
El worker efímero muele KDE 24/7; este cron en el laptop recoge su store sellado
(worker->laptop, CAS merge seguro), siembra recetas nuevas (laptop->worker) y regenera
el grafo de estado, commiteando SÓLO docs/state/*.json (el avance que el humano sigue).
NO promueve incoming-kde->canónico (decisión de clasificación humana). Robusto a
worker-ausente (dead-man ya lo mató por idle). KDE: 66/206 sellado.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 20:12:09 -04:00
sergioandClaude Opus 4.8 4357cd7b0f estado: buscar el artefacto por el name INTERNO, no el basename del fichero
El grafo buscaba store/<hash>-<basename> pero el artefacto se sella por el name INTERNO de la receta
(qtbase.toml → name=qt6-qtbase → store/<hash>-qt6-qtbase). Las qt6-* KDE salían 'debt' aunque
estuvieran selladas. Fix: nodo lleva iname=d['name']; state_of busca por iname. Las aristas de deps
siguen por basename (así resuelve resolve_dep_path). qtbase ahora sale sealed correctamente.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 15:32:14 -04:00
sergioandClaude Opus 4.8 ff79dc5e28 estado+kde: el grafo ahora cubre la cola incoming-kde; base KDE desbloqueada en el laptop
MOTIVO: 'estábamos haciendo kde'. La cola recipes/incoming-kde/ (206 recetas) era punto ciego del
grafo (sólo miraba recipes/ top-level). Modo --kde: build-state.py/build-state-view.py cubren la cola
KDE (sombreando canónicas homónimas sibling-first, como el sandbox) → build-state-kde.{json,html}.

CAUSA de la deriva (193/206 en deuda): fui YO — esta sesión re-sellé pkgconf y samurai (arreglos
static), y 194 recetas KDE declaran pkgconf, 142 samurai ⇒ re-hash en cascada de todo el árbol. El
sistema determinista funcionando: cambia un input base, cascan los dependientes. Deuda legítima.

DESBLOQUEO (el 'GUI rompe en el laptop' era sólo cairo/pango, NO la base): construida en el laptop
toda la base KDE guiado por el orden de ataque del grafo — extra-cmake-modules, util-macros,
xorgproto, xcb-proto, wayland, libdrm, dbus, icu4c, util-linux, fontconfig, pixman, glib, freetype,
harfbuzz, fribidi, libXau/libXdmcp, la cadena X11 (xtrans/libxcb/libX11/libXext/libXrender/libXfixes/
libXi), la familia xcb-util, libxkbcommon, wayland-protocols, mesa (24.0.9 iris-only, sin LLVM), y
los -shared. KDE 9→39 selladas. qtbase (↑117, la raíz) quedó DESBLOQUEADO y compilando.

DISCO: el build llenó el disco (100%). Liberados 82G borrando work/sources (57G, se re-extrae solo)
y .farm-harvest (26G, verificado 100% redundante en el store, CAS).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 13:27:55 -04:00
sergioandClaude Opus 4.8 a267cf4f48 estado: clasificación Rust corregida (había ~180 Rust escondidas como C) + musl
Señal fuerte para Rust: la receta copia su binario de target/release/ en las fases. Medido 225
recetas, 0 con dep go ⇒ sin falsos positivos con Go (findutils entra bien: es uutils/findutils, find
en Rust). LÍMITE: un crate BuildSys::Cargo PURO sin fase install propia (zellij) no deja rastro en el
TOML y cae a 'c' — sólo se ve bajando la fuente, que el generador no hace.

Efecto: clases c 322→141, rust 45→226 (la mayoría eran Rust auto sin fase cargo explícita). Y la
LECTURA de la deuda cambia: la deuda C REAL es sólo 8 (casi toda saldada esta sesión), no 74. Lo que
queda es Rust (15, CLI pesados → granja), GUI (29 → granja), Go (5 → worker), kernel (4).

musl sellado (base). Avance: sealed 703→704, debt 53→52.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 12:36:23 -04:00
sergioandClaude Opus 4.8 a30e0d3146 estado: orden de ataque por impacto de desbloqueo (lectura accionable del DAG)
El grafo ahora computa, sobre las dependencias, dos campos por receta:
  blocked_by — deps que están en deuda (lo que impide construirla ya)
  unblocks   — cuántas recetas EN DEUDA la declaran como dep (su impacto de desbloqueo)

Una receta en deuda sin blocked_by es construible YA; ordenadas por unblocks desc, dan el orden que
libera el grafo más rápido. La vista muestra la pastilla ↑N en las filas listas y ordena por ahí.

Top de impacto (todo el stack GUI concentrado, más zstd/perl de C-base):
  glib ↑14  wayland ↑13  pixman ↑12  libdrm ↑11  fontconfig ↑11  zstd ↑10  libxkbcommon ↑9  perl ↑8

Es la guía para cuando se levante la granja: construir esas primero desbloquea el grueso.

De paso, diagnóstico de las 8 'never' (ninguna es cruft ni bug): dwarves BLOQUEADA-documentada
(necesita libdw, elfutils da sólo libelf a propósito — sub-proyecto elfutils-libdw); llimphi-counter
es un EJEMPLO/plantilla intencional; las otras 6 son imports Go/Rust que van al worker. Y confirmado
parseando: 0 recetas con FIXME real en el campo sha256 (el grep decía 43, todas comentarios) — otra
vez grep miente, el grafo (parsea) dice la verdad.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:48:28 -04:00
sergioandClaude Opus 4.8 e19fb6347f estado: vista humana del grafo (build-state-view.py → build-state.html)
Dashboard self-contained (sin recursos externos, tema claro/oscuro) que rinde build-state.json para
VER y SEGUIR: barra de avance del corpus, tiles por estado, reparto de deuda por clase, y los huecos
en ORDEN DE ATAQUE. Ese último es el uso accionable del grafo: cada receta en deuda muestra sus deps
coloreadas por estado; una fila 'lista' tiene todas sus deps al día (construible ya), una 'espera N'
depende de N recetas también en deuda. Foto actual: 117 en deuda, 71 listas ahora.

Publicado como Artifact para verlo en el navegador; el HTML también queda en el repo (abrir con
file://). Regenerar ambos: scripts/build-state.py && scripts/build-state-view.py.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:34:02 -04:00
sergioandClaude Opus 4.8 7f89a635a4 estado: grafo de build persistido y versionado (scripts/build-state.py → docs/state/build-state.json)
El estado real del build vivía disperso —en mi cabeza, en docs que envejecen (matar-gcc decía 47,
eran 16), en el store (que guarda TODOS los sellados históricos, no el vigente)— y cada medición a
mano mentía distinto. Este generador lo deriva de la ÚNICA fuente de verdad (recetas + hammer hash
+ store) a un JSON firme. El git diff de ese fichero ES el avance entre dos corridas: qué se saldó,
qué se rompió, qué cambió de estado.

NODO = receta {name, class, link, compiler, deps[], hash, state}:
  sealed  — el artefacto del hash VIGENTE está en el store (al día)
  debt    — hay sellados históricos pero ninguno vigente (cambió, falta rebuild)
  never   — sin ningún sellado
  unhashable — hammer hash falló (hueco real)
ARISTA = dep de build. El grafo CIERRA (0 deps huérfanas) y topo-ordena sin ciclos.

Foto inicial (764 recetas): sealed 647 | debt 109 | never 8. Deuda por clase: c=74 go=5 gui=29
kernel=4 rust=5. Clases: go 362, c 322, rust 45, gui 30, kernel 5.

Validado contra lo que sé de esta sesión: samurai/which sealed, curl/openssl debt, helix sealed
(rust/gcc), naabu sealed (dynamic/go), mesa debt (gui), linux debt (kernel). Todos correctos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:30:41 -04:00
sergioandClaude Opus 4.8 fa8dff08b1 granja: borrar la cola gráfica obsoleta de incoming/ (no había otro agente)
Las 7 recetas del stack gráfico (libdrm/mesa/meson/samurai/seatd/wayland/wayland-protocols) +
6 patches eran imports crudos de Alpine, redundantes: las 7 ya tienen receta CANÓNICA en recipes/
(mesa pineada a 24.0.9 iris-only A PROPÓSITO, no la 26.1.1 cruda con FIXME-sha256). Su trabajo
aterrizó por la vía canónica (7131cd4) hace 3 semanas; la cola quedó de cruft rompiendo cada ciclo.

fa45978 las sacó de QUEUES creyéndolas de 'otro agente' (5126a8b). Confirmado que NO hay otro
agente ⇒ borradas. recipes/incoming/ vuelve a ser cola de staging general y REGRESA a QUEUES; el
guard ls-vacío la salta si no hay nada. recipes/incoming/.deferred/ (24 recetas aparcadas con
diagnóstico, git con muro en libgit.a) NO se toca: el glob de QUEUES es top-level, no la muele.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:26:42 -04:00
sergioandClaude Opus 4.8 54b2cd69ce granja: saldar-deuda-static.sh — reconstruye la deuda que destapó hammer hash
El nuevo 'sin artefacto' del static-audit (deuda de rebuild ya no enmascarada) son 92 recetas
link=static cuyo hash VIGENTE no está sellado: el trabajo reciente (matar-gcc, static, harkaq)
cambió recetas base sin re-sellar, y cada cambio re-hashea en cascada todo lo que las declara.

Este script las salda. Calcula la deuda EN VIVO con 'hammer hash --check' (nunca una lista que
envejece), y construye cada receta NO-SELLADA. NO necesita orden topológico: 'hammer build' arrastra
sus deps recursivamente (construir curl construye openssl+perl primero); las cache-hit son instant.

Pensado para el WORKER (store completo, toolchain que no rompe el stack GUI): SKIP_GUI=1 por default
salta cairo/pango/gtk… que fallan en el laptop por zig-skew. Reparto medido: 76 C-base + 16 GUI.

Verificado end-to-end SIN quemar el laptop: modo DRY lista la deuda; y construí UNA receta diminuta
(which: NO-SELLADO → build 58s → SELLADO, hash vigente idéntico, estática de verdad en el audit).
El ciclo build+re-check del script es correcto. which quedó saldada de paso (deuda 76→75).

Confirma la decisión de usar la granja: 58s × 76 con openssl/gnupg/perl pesadas = 2-4h de laptop.
Cuando levantes la granja: farm-up, correr esto en el worker (SKIP_GUI=0 para incluir el GUI),
farm-down.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:18:54 -04:00
sergioandClaude Opus 4.8 1e823d377b hammer hash: dry-run que faltaba — cierra el sobre-reporte del static-audit de raíz
El agujero que dejé señalado 3 veces esta sesión: nada podía saber el sellado VIGENTE de una receta
sin construirla, así que static-audit.sh auditaba el más reciente por mtime (ls -dt) y acusaba a
recetas ya sanas (dbus/libnl) por un sellado anterior a sus flags.

FIX = subcomando `hammer hash <receta> [--check]`. Calcula el ArtifactHash puro sobre las recetas
(source_id + compiler/target/link + patches + flags + fases + hashes de deps recursivos) SIN bajar
fuentes ni compilar. artifact_hash() ya era pub; el CLI sólo lo expone. Cero cambios en la lógica
de hashing ⇒ NINGÚN sellado se mueve.

Verificado: `hash` da EXACTAMENTE el mismo hash que `build` (samurai, byte a byte); `--check` sobre
receta editada → NO-SELLADO en 2ms, exit 1, sin construir nada.

static-audit.sh ahora selecciona el artefacto VIGENTE por hash, no el más reciente por mtime. Con
fallback a ls -dt si hammer no está compilado. Efecto en el store completo: las ~65 recetas cuyo
sellado no es el vigente pasan de 'auditadas' (falsa cobertura) a 'sin artefacto' (deuda de rebuild
REAL, ahora visible): estáticas de verdad 615 | MIENTEN 0 | sin artefacto 123. Corre en 15s, sin
build. El '58 sin medir' de antes estaba enmascarando ~65 recetas más.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 11:08:29 -04:00
sergioandClaude Opus 4.8 117f8e6dee granja: el smoke-test del cron escribía en el repo (gocron sembró config/)
El smoke de harvest-go.sh ejecutaba cada binario cosechado con el cwd en la RAÍZ DEL REPO, así que
un binario que escribe estado al arrancar dejaba basura entre las fuentes. Pasó: `gocron version`
—y `version` es justo el PRIMER flag que prueba el smoke, así que se disparaba siempre— sembró
config/{config.yaml,db.sqlite} (5 jobs de ejemplo + una sqlite) el 2026-07-04, y quedó sin trackear
hasta hoy. Medido, un flag a la vez, en cwd limpios:

    [version]   DEJO: ./config ./config/db.sqlite ./config/config.yaml
    [--version] limpio    [-v] limpio    [--help] limpio    [-h] limpio

Un `--help` no debería poder tocar el repo. El smoke ahora corre en un mktemp -d que se borra.
Vale para cualquier herramienta futura, no sólo gocron.

Sin regresión en el veredicto: en tmpdir `gocron version` panica (le falta web/index.html), el
smoke ya trata el panic (continue) y pasa a --version, que funciona ⇒ gocron sigue aprobando.

config/ borrado (no trackeado, sin una sola referencia en scripts/recetas/código).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:39:49 -04:00
sergioandClaude Opus 4.8 fa459784e4 granja: dejar de moler la cola del OTRO agente (los 6 'No such file' de cada farm-down)
recipes/incoming/ es la cola del stack gráfico tawasuyu, de otro agente. Sus 7 recetas son
imports crudos de Alpine con sha256='FIXME-sha256' y deps sin expandir (clang$_llvmver, _dev,
py3-gpep517) ⇒ NO construibles por construcción. El worker las fallaba en CADA vuelta (CPU
pagada) y farm-down repetía los 6 errores al bajar, con pinta de ser nuestros.

Su trabajo YA aterrizó por la vía canónica (7131cd4 'MESA iris-only CONSTRUIDA — stack gráfico
COMPLETO'), así que la cola quedó obsoleta. Pero NO es nuestra para borrarla: 370e7b7 ya la
parqueó una vez como cruft y 5126a8b tuvo que revertirlo. Dejamos sus ficheros en paz y sólo
los sacamos de QUEUES.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:22:01 -04:00
sergioandClaude Opus 4.8 c0a473589b granja: BLINDAJE de gioser — el token de la granja podía borrarlo
Petición explícita del usuario: gioser.net es FIJO y NO TIENE BACKUP. Vive en el
MISMO proyecto hcloud que los workers, y Hetzner no da tokens por-recurso: el
token que el dead-man switch necesita para auto-borrarse puede borrar CUALQUIER
server del proyecto. farm-down era peor: borraba por NOMBRE leído de .fleet sin
verificar NADA — un nombre equivocado en esa lista y adiós.

Dos capas, porque una sola no basta cuando el fallo es irreversible:

  1. LISTA NEGRA por nombre (gioser*) — explícita y legible.
  2. LABEL role=hammer-worker — sólo se borra lo que NACIÓ de farm-up. Ésta es la
     capa fuerte: no depende de mantener una lista al día. Un server que no es
     worker no se borra, punto.

VERIFICADO contra los servers vivos, no en teoría:
   gioser     labels=map[]                       PROTEGIDO
   hworker-4  labels=map[role:hammer-worker]    BORRABLE
Y .fleet sólo contiene hworker-4.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:06:17 -04:00
sergioandClaude Opus 4.8 949a1d657a granja: automuerte TOTAL + el store VIVE en el volumen (sin ventana de pérdida)
Dos fallos de raíz, no uno:

1. HABÍA DOS CAMINOS de crear workers. harkaq-vol.sh ya tenía volumen Y
   automuerte desde la 1ª campaña; farm-up.sh no tenía ninguna de las dos.
   hworker-4 nació por farm-up ⇒ 5 días idle con 37G que nadie salvó. La
   protección existía y el camino que usé la esquivaba. Ahora farm-up monta el
   MISMO volumen (harkaq-cosecha) ⇒ collect/close cosechan y clausuran ambos.

2. NI harkaq-vol salvaba los artefactos: su rsync excluye /store y el volumen
   sólo guardaba verdicts/ y logs/. Los 438 artefactos KDE se habrían perdido
   igual.

FIX: el store del worker ES /mnt/cosecha/store (symlink desde /store).
Sellar YA es persistir: cada artefacto está en el volumen en el instante en que
se crea. "Guardar lo generado" deja de ser un paso que puede fallar antes de
morir y pasa a ser la estructura.

⇒ la automuerte puede ser INCONDICIONAL. Quitada la guarda "no borrar si hay
cosecha pendiente": era el bug de hworker-4 con otra cara — el worker se queda
VIVO justo cuando hay trabajo que salvar. Un switch que se desarma solo cuando
más falta hace no sirve. Antes de morir sólo sync+umount: no es guardar (ya está
guardado), es cerrar la puerta al salir.

El volumen sobrevive al server a propósito (~€0.044/GB/mes). El baseline €0 lo
da harkaq-vol.sh close, que es decisión del usuario, no de un timer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 09:01:41 -04:00
sergioandClaude Opus 4.8 0d35044b53 granja: DEAD-MAN SWITCH — el worker se borra solo tras 1h idle
hworker-4 estuvo 5 DÍAS idle (carga 0.00, sin crontab, sin loops, 37G de KDE
sin cosechar) quemando dinero. El usuario había pedido esto explícitamente
—"los vps se autoapagan cuando se detectan idle por un tiempo"— y de sus 3
puntos (volumen / auto-apagado / cosecha) implementé el 1 y el 3. Éste faltaba.

EL FALLO ERA ESTRUCTURAL, no un olvido: farm-down.sh existe pero es MANUAL. El
modelo "efímero" dependía de que el agente se acordara de llamarlo — y si su
contexto se corta, o la sesión muere, el server queda vivo para siempre. Un
invariante que necesita que alguien recuerde NO es un invariante. Por eso el
switch vive EN EL WORKER: para apagarse no necesita ni al hub ni a mí.

BORRA, no apaga: en Hetzner un server apagado SIGUE COBRANDO (disco + IP). Un
poweroff daría sensación de ahorro y seguiría facturando. Precio: el token vive
en el worker (/etc/hammer-deadman.env 0600). Riesgo real y consciente; se acepta
porque la alternativa MEDIDA fue peor: 5 días de VPS idle.

Cuenta por INACTIVIDAD CONTINUA, no por antigüedad: cualquier señal de trabajo
(hammer build, worker-loop, heartbeat <30min) resetea los ticks. Guarda
/var/lib/hammer-no-borrar aborta el borrado si hay cosecha pendiente.

systemd timer y NO cron, por la lección medida: un cron "validado a mano" nunca
disparó porque el PID 1 de aquella máquina (arje-zero) no tenía crond. Validar
la LÍNEA no es validar que un demonio la ejecute.  y el
farm-up comprueban que el timer quedó ACTIVO — evidencia, no fe.

Cableado en farm-up: todo worker NACE con el switch puesto.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 08:00:24 -04:00
sergioandClaude Opus 4.8 372baed4f6 static-audit: el '28 mienten' estaba INFLADO por mi propio bug (ls|head -1)
El audit tomaba 'ls -d store/*-<n> | head -1' = orden ALFABÉTICO POR HASH, no el
artefacto vigente. El store guarda TODOS los sellados de una receta (expat tenía
5, de junio a hoy), así que auditaba uno de hace tres semanas. Mismo bug de
clase que comparar libpng.a con libpng16.a por find|head -1.

Con 'ls -dt' (más reciente): 28 → 11. Tres de las supuestas mentirosas
(cargo-audit, git-absorb, yazi) nunca lo fueron: su artefacto reciente ya era
estático y el audit leía el viejo.

Lo que NO cambia: el hallazgo central es real y verificado a mano. libtool
ignora el -static del lab, el curl sellado NO arrancaba en el host, y de las 14
que arreglé cada una se verificó contra sus artefactos previos (expat: los 4
viejos con NEEDED=1, el nuevo con 0). Estaban rotas de verdad.

HONESTIDAD escrita en el script: 'más reciente' ≠ 'vigente'. Lo vigente sería el
artefacto cuyo hash corresponde a la receta de HOY, y hammer no lo expone sin
construir (no hay hammer hash / dry-run). Por eso el audit va DESPUÉS del
rebuild, nunca antes.

Quedan 11: file, helix, libcap, libgcrypt, pcre2, pkgconf, samurai, tuc,
util-linux, xz + naabu (nuevo: Go con libc.so.6 de GLIBC, otro caso).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 05:01:40 -04:00
sergioandClaude Fable 5 9395fbfc5c piloto trace END-TO-END: build real de zlib trazado en store desechable — el hash REPRODUCE bit a bit el sellado (b3:2623a403), poda filosa (make 1/8 ficheros, busybox 1/2), taxonomía de ruido medida (+/cache al normalizador); harkaq-trace-build.sh orquesta (tracer por fase vía /proc/pid/root)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 04:08:08 -04:00
sergioandClaude Opus 4.8 c707c1e13f static-audit: link="static" era mentira en 28 recetas selladas (libtool lo ignora)
Destapado migrando procps-ng. libtool lee el -static del lab como "preferí mis
.a", NO como flag al linker: el binario sale dinámico y la receta jura que es
estático. Con gcc pasaba igual — no es regresión de zig, es un agujero que
nadie había medido.

Tres consecuencias medidas, no teóricas:

1. NO CORREN. El curl sellado en el host: "Error relocating /lib/libz.so.1:
   __snprintf_chk: symbol not found". Su NEEDED libc.so es el soname de la musl
   de zig; en el host /lib/libc.so son 255B de linker script. El artefacto sólo
   funciona dentro del sandbox.
2. ARRASTRAN GCC. helix, yazi, git-absorb, cargo-audit y tuc traen libgcc_s.so.1
   de Alpine DENTRO del binario. Deuda de matar-gcc que ningún compiler="gcc"
   declara: invisible para el frente entero hasta ahora.
3. Un NEEDED es una entrada no declarada — lo que harkaq mide en build, pero en
   RUNTIME. Rompe el cono de affected.py (SDD 17 §4): un CVE en zlib no
   alcanzaría a un curl que se declara estático.

Script, no gate duro: fallar hoy rompe 28 selladas de golpe, varias del sistema
base (curl, util-linux, sudo). Primero se arreglan con evidencia, después se
cierra la puerta. Exit 1 mientras haya mentirosos ⇒ sirve de gate en CI cuando
la lista llegue a cero.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 00:12:59 -04:00
sergioandClaude Fable 5 91496007ca harkaq-trace: la traza POSITIVA de la clausura (plan-freebsd T1.1-T1.2, prior art filemon/META MODE)
- harkaq-trace.c: fanotify FAN_MARK_FILESYSTEM — agnóstico de mount-ns, el
  tracer en el HOST ve los open() de dentro del bwrap (ptrace lo deniega el
  seccomp D4; audit sólo emite denegaciones). Crudo y tonto a propósito
  (reparto Q1c); D9: sin fanotify ⇒ exit 3 SinEvidencia, jamás traza vacía.
- harkaq-trace-norm.py: canoniza (drop /proc /sys /dev /tmp /out /src, dedupe,
  orden estable — la lección nº1 de META MODE) + atribución por dep con
  --resumen = la señal de PODA (T1.4): dep declarada con 0 toques = grasa.
- VALIDADO: modo --fs como root (worker, 5s, sin tocar colas): capturó
  exactamente zlib.h + 3×libz.a de un cat/head; laptop sin privilegio ⇒
  SinEvidencia honesto. Piloto completo (traza de un hammer build real +
  resumen de poda) pendiente de un rebuild natural en el worker.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 23:51:17 -04:00
sergioandClaude Opus 4.8 83d40af35c harkaq: el clasificador nunca propone runtime base como declarable (fix de raíz de busybox)
El bug que declaró `busybox` en 29 recetas y rompió binutils: harkaq-suggest veía
`/bin/busybox` denegado, encontraba `recipes/busybox.toml` en el store y decía
"declarar dep: busybox" — sin mirar que la política YA lo concede (`ro /bin/busybox`:
el sandbox corre `sh -c` y /bin/sh→busybox ⇒ es CONTRATO, no dep).

Fix: harkaq-suggest ahora lee los paths CONCEDIDOS de la política (`ro`/`rw`/`list`),
no sólo los `# expect`. Si un path está concedido ⇒ categoría RUNTIME BASE: no se
declara, aunque el store lo provea.

Las 4 clasificaciones verificadas tras el cambio:
  /bin/busybox              → RUNTIME BASE (ya concedido; NO declarar)   ← el fix
  /usr/bin/make             → DECLARABLE (declarar dep: make)
  /usr/lib/libstdc++.so     → IRREDUCIBLE (nadie lo provee)
  /usr/bin/gcc en broot     → DEUDA DE COMPILADOR (compiler=gcc)
  /usr/bin/gcc en zlib      → sin deuda (sonda esperada; zlib compila con zig)

Con esto puesto, busybox JAMÁS se habría declarado. Cierra el TODO que dejó el
revert: la campaña automática ya no puede cimentar contrato como dependencia.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:27:57 -04:00
sergioandClaude Opus 4.8 d99ed58850 harkaq: revertir busybox (era runtime base, rompía builds) + coordinar el §3 con Fable 5
DOS ERRORES MÍOS, uno de dirección y otro técnico, los dos en la cosecha automática:

1. DIRECCIÓN: declaró `busybox` como dep en 29 recetas — cuando la Etapa C lo está
   ELIMINANDO (USERLAND_COMPONENTS=[uutils,findutils,…], ya cerrada; joyas-reusables
   §5 lo confirma: "uutils… Ubuntu 25.10 los envía como default"). Estaba cimentando
   la deuda que el roadmap borra.
2. TÉCNICO: busybox YA está en el runtime base de harkaq (`ro /bin/busybox`, `ro
   /bin/sh` — el sandbox corre `sh -c` y /bin/sh→/bin/busybox). Es CONTRATO, no dep.
   Declararlo apila el busybox de hammer sobre el de Alpine y ROMPE el build:
   binutils daba "cannot run C compiled programs" en las DOS máquinas. Verificado:
   sin busybox declarado, binutils construye (b3:f4507dcd…). Y la cadena se explica:
   binutils roto ⇒ zlib (que lo declara) tampoco construía.

Auditoría de la cosecha (diff real, no la línea completa del +): añadió sólo 5 deps
distintas — make ×73, busybox ×29, perl ×8, pkgconf ×5, binutils ×1. Sólo busybox
estaba mal; las otras 4 son deps reales medidas. busybox revertido de 30 recetas
(queda sólo en busybox.toml, pre-existente).

Es el mismo error que ya me habían señalado con otro disfraz: MEDIR BIEN Y ACCIONAR
MAL. harkaq midió correcto (el build toca /bin/busybox: es el shell); la acción
correcta no era declararlo sino reconocerlo como contrato.

+ COORDINACIÓN del §3 con Fable 5 (mismo diseño, tareas repartidas):
  - Su lección casper queda CONFIRMADA y REFORZADA: la clausura de build no sólo le
    FALTAN las clases del mundo (offline) — también le SOBRA casi todo (headers, gcc).
    Medido: htop (estático, 0 NEEDED) no toca NADA al correr ⇒ política = su binario.
  - Su "la clase viaja como campo de la ConcesionCapacidad, sin formato nuevo" se
    cumple LITERALMENTE: lo firmado es format::Permisos = u32 bitmask en 36 bytes
    canónicos (Ring 0) ⇒ las clases SON los bits. La cripto no se toca.
  - Diseño unificado: frontera (clases, u32, declaradas) + detalle (paths, Landlock,
    medidos). D3 rige en ambos.
  - Reparto: clases→Fable 5; medición/harness→Opus. CONTACTO: runtime-policy.sh ahora
    emite la CLASE detectada (/etc/resolv.conf→dns, /etc/ssl/certs→tls-certs, …), no
    sólo el path: la medición alimenta la tabla, la tabla decide el bit.
  - Consumidor esperando: plan-jaula-juegos F1 (Steam que no puede leer ~/.ssh).

+ juez.sh (§10): nombre único por corrida (con uno fijo pega en caché ⇒ falso
  "sin evidencia"). Fue el juez quien destapó todo esto en su primera corrida real.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 23:14:19 -04:00
sergioandClaude Opus 4.8 4374d2b591 cierres §4: CVE por grafo — frontera mínima de rebuild (dirty_cone + topo_order)
`scripts/affected.py <pkg> [--agenda] [--sellados]`. Un CVE no obliga a
reconstruir el catálogo: obliga a reconstruir el CONO de lo que depende del
paquete, y nada más. El grafo declarado ya lo sabe.

  CVE en zlib  → 65 de 761 recetas (8%) — el resto NO se toca
  CVE en expat → 20 de 761 (2%), las 20 selladas = el trabajo real
                 agenda: fontconfig → fcft → cairo → pango → dbus → wayland…

Es el `dirty_cone` de `dataflow` (crate no_std de tawasuyu) + `topo_order` como
agenda (una dep antes que su consumidor), calculados sobre las deps declaradas.
`--sellados` separa lo que hay que reconstruir de verdad (tiene artefacto) de lo
que no existe aún.

CAE SOLO DEL INVARIANTE: las deps están declaradas por hash ⇒ el cono es
computable y EXACTO. Una distro sin clausura declarada tiene que adivinar o
reconstruir todo por las dudas. Ésa es la tesis del SDD 17.

Honestidad grabada en el propio output: la frontera es exacta respecto de lo
DECLARADO. Lo que una receta usa sin declarar queda FUERA del cono — y ese es el
agujero que harkaq cierra (SDD 16). La campaña de esta semana declaró ~84 deps
que el kernel midió y nadie había escrito: sin eso, este cono habría sido
optimista. **El cono es tan bueno como la clausura**, y por eso los dos frentes
se necesitan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:09:59 -04:00
sergioandClaude Opus 4.8 be52e735ab cierres §3: política de runtime derivada por medición (sin tocar la cripto)
El #3 decía "la clausura medida por harkaq en build = permisos del binario
instalado". MEDIDO: es falso en dos ejes, y por eso NO se implementó así.

1. SUJETO: la clausura de BUILD (zlib.h, make, gcc) no es la de RUNTIME. Un build
   lee headers; el binario resultante no. Dos clausuras, dos sujetos.
2. GRANULARIDAD/CRIPTO: lo que la ConcesionCapacidad firma NO es card-core::
   Permissions (struct) sino format::Permisos = u32 bitmask, dentro de
   `mensaje_capacidad` = hash(32)||permisos_le(4) = 36 bytes canónicos, zero-alloc,
   con espejo no_std en wawa-kernel (Ring 0). Meterle paths rompería TODAS las
   firmas y contradice el "capacidad = frontera física, no tabla" que el propio
   plan cita como referencia.

⇒ Camino elegido: la concesión firmada QUEDA INTACTA (frontera gruesa, Ring 0) y
la clausura granular se deriva por MEDICIÓN como política Landlock en userspace,
donde Vec<String> sí cabe. Coexisten: el kernel verifica la frontera, Landlock
aplica el detalle.

runtime-policy.sh: corre el binario bajo la jaula con política mínima DERIVADA
(harkaq-base-closure, no a mano) y resta el BASELINE del lanzador (el sh deniega
locale/ld.so.cache; sin restarlo, la política del binario se lleva esa basura).

Demo real — htop (musl-estático): baseline 3 paths del sh; htop --version no tocó
NADA propio ⇒ su política de runtime es sólo `ro <su binario>`. Medido, no supuesto.
htop tiene 0 NEEDED: para un estático la política NO sale de las libs, sólo de
correrlo — lo que confirma que la técnica de harkaq es la única vía para el #3.

+ FIX del lector (lo cazó el chequeo anti-pérdida `nº ACCESS == denials`): descartaba
los ACCESS previos al canario porque hasta verlo no sabe su domain= — pero el sh
deniega ANTES del probe y esas son suyas. El kernel contaba 4 y el lector recibía 1.
Ahora se bufferizan con su domain y se rescatan retroactivamente al ver el canario.
Sin ese chequeo habría emitido una política con 1 de 4 accesos, perfectamente creíble.
(Primer intento de fix —drenar el socket al ver el deallocated— era plausible y NO
era la causa: el contador siguió en 4 vs 1.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:02:05 -04:00
sergioandClaude Opus 4.8 e9b6283722 cierres §2: why-differs — explica POR QUÉ dos artefactos no reproducen
El #2 de SDD 17, y encadena con el #1: cuando  da DIVERGENCIA, esto
dice qué difiere y por qué. Hoy debuggear una no-reproducción es artesanal; esto
la vuelve mecánica y legible por máquina (lo que el bucle agéntico necesita).

Algoritmo (cruce con  de tawasuyu): árbol = grafo blob+árbol hasheado ⇒
dos subárboles idénticos colapsan al mismo hash ⇒ descender SÓLO donde difiere.
O(diferencia), no O(tamaño).

Clasifica la causa, que es lo que decide la acción: solo-en-A/B (no-determinismo
de estructura) · tamaño · build-id · timestamp · path-embebido · codegen ·
contenido.

Probado: control (A vs A) → 'árboles IDÉNTICOS' ✓. Caso real (bzip2 gcc vs zig):
de 21 ficheros sólo difieren los 4 binarios → causa .

DOS VECES tuve que arreglar el diagnóstico por confundir SÍNTOMA con causa:
  1. Chequeaba build-id PRIMERO ⇒ reportaba 'build-id' cuando la causa era el
     codegen. El build-id es un HASH DEL CONTENIDO: difiere siempre que difiera
     el código.
  2. El umbral era '<1% del binario' — sonaba bien y clasificaba 1196 bytes como
     build-id. Un build-id son 20 bytes (sha1). Atado al TAMAÑO REAL (<=64).
Un diagnóstico plausible no es un diagnóstico correcto.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 20:33:51 -04:00
sergioandClaude Opus 4.8 8b1eea8911 cierres §1: consenso de reconstrucción — funcionando (2/2 builders, mismo hash)
El top-1 de SDD 17 (mayor ventaja competitiva por esfuerzo). N builders
INDEPENDIENTES reproducen el mismo hash ⇒ el binario es confiable SIN FIRMA.
Convierte la repro de QA en primitiva de distribución: cualquiera puede ser
mirror, nadie puede envenenar el repo.

  scripts/consenso.sh recipes/bzip2.toml 2
    builder 1: hub local (Artix)     → b3:86a33b76…
    builder 2: worker efímero (Ubuntu) → b3:86a33b76…
     CONSENSO (2/2, bit a bit)

Dos máquinas físicas, dos distros, mismo hash byte a byte. Nix/Debian no pueden
ofrecerlo (repro incompleta). Cae SOLO del invariante que hammer ya paga — que es
la tesis del SDD 17: buscar qué cae del invariante, no qué frontera agregar.

Lo originó una observación accidental: al hacer el rollout de cmake noté que el
laptop y el worker daban el MISMO hash sin que nadie lo buscara. Eso ya era
consenso ocurriendo; esto sólo lo formaliza.

Divergencia ⇒ exit 1 y apunta a why-differs (§2): una divergencia YA es
información (un canal impuro encontrado). NO firma ni publica: sólo el veredicto.
El log de transparencia (fork-proof: hash-encadenado + detección de equivocation)
ya está escrito en tawasuyu — no hay que construir un rekor.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 20:26:03 -04:00
sergioandClaude Opus 4.8 c6ff858f23 harkaq: cpp/getent en las sondas del provisioning (coherente con base.policy)
Faltaba persistir en harkaq-farm-setup.sh las 2 sondas que añadí a base.policy
al revisar needs-review (cpp=frontend gcc, getent=NSS musl). Sin esto, un worker
nuevo derivaría una base.policy sin ellas y volvería a marcar irreducibles
falsos (dhcpcd/strace).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 15:22:39 -04:00
sergioandClaude Fable 5 81a5d8d746 mirada-usb: el overlay de mesa pisaba libudev-zero con el libudev de eudev — input muerto en el tester NVIDIA
Causa raíz del viaje 2026-07-16 (pantalla viva, teclado/mouse muertos): el apk
de mesa arrastra eudev-libs y el 'cp *.so*' del overlay pisaba el libudev.so.1
de libudev-zero. Sin udevd, el libudev de eudev no expone ID_INPUT y libinput
ignora TODOS los dispositivos en silencio; la GPU no lo delata porque smithay
la encuentra por syspath. Reproducido y verificado A/B en QEMU (usb-kbd):
eudev = 0 dispositivos; libudev-zero = 5 y Ctrl+Alt+BackSpace corta el
compositor al instante.

- 5c-bis: restaurar libudev-zero tras el overlay + aserción cmp (regresión = build roto)
- MIRADA_DEBUG_DIR → run-NNNN del pendrive (eventos.log y panics ya no mueren en RAM)
- imagen re-empaquetada con el fix + gpt en la cmdline

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 13:02:17 -04:00
sergioandClaude Opus 4.8 2d1d40a6f1 matar-gcc: migra-zig.sh + veredicto medido — las C-puras SON granjeables (~85%)
¿Las 47 compiler=gcc que harkaq nombró son factibles de migrar a zig, o hay que
matracar? MEDIDO, no adivinado.

migra-zig.sh: para cada receta, build con zig-cc + SMOKE-TEST del binario —
porque el motivo del gcc era segfault en RUNTIME, no fallo de build; un artefacto
que sella pero segfaultea es peor que gcc. Idempotente, acumula por lotes (probar
47 no cabe en un timeout).

Muestra de 13 C-puras: 11 MIGRAN (bzip2 expat json-c gzip htop less libpng
libyaml zstd libffi xz), 2 MATRACA (pcre2: linker version script que zig ld no
soporta; file: subdir magic). ≈85% yield.

VEREDICTO: es GRANJEADA para las ~36 C-puras. El compiler=gcc era deuda histórica
— marcadas con zig 0.13/0.16 (que segfaultaban C clásicos), zig mejoró, el gcc
quedó por inercia. ~85% migran solas; el residuo (~15%) es matraca individual por
causas concretas del build-system.

Las 11 Rust con sys-crate C son otra evaluación (gcc para el C embebido de
libgit2-sys, no el Rust) — se deja para después.

Rollout = decisión del usuario (re-hashea recetas del índice firmado 748). Correr
migra-zig.sh sobre las 47 en la granja VPS da la lista final; promover las
migrables re-firma el índice. Reporte: tandas/needs-review-harkaq/migracion-zig.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 12:31:53 -04:00
sergio 0f6c841782 mirada-usb: force_gpt en la cmdline + post-mortem del viaje NVIDIA
El firmware del tester reparo el GPT dd-eado en el POST y dejo el header
primario invalido (entradas apuntadas a LBA 2016, presentes en LBA 2):
Linux sin gpt en cmdline no cae al respaldo, no vio particiones y la
telemetria MIRADALOG murio en RAM. Con gpt el kernel usa el respaldo.
Documentado: greeter por nouveau GL validado en metal; input muerto queda
abierto con el plan de evidencia para el proximo viaje.
2026-07-16 12:16:04 -04:00
sergioandClaude Opus 4.8 71ec92345f harkaq: el clasificador distingue gcc-compilador de gcc-sonda — matar-gcc son 47 recetas
El límite del modelo que mtdev destapó, cerrado. harkaq clasificaba /usr/bin/gcc
como "sonda esperada" SIEMPRE, así que marcaba `Hermetico` a las recetas que
compilan CON el gcc de Alpine (compiler=gcc = CC=gcc, musl+GNU ld). Estuvo ciego
a la deuda de compilador todo el barrido.

Fix en harkaq-suggest: 5º arg = la receta medida. Si usa compiler=gcc, los
frontends de gcc (gcc/cpp/c89/c99/g++/cc/ld/as) dejan de ser "esperada" y pasan a
DEUDA DE COMPILADOR (matar-gcc). Para compiler=zig-cc (default, 714 recetas) gcc
sigue siendo sonda de configure — esperada, como antes. Verificado: mtdev(gcc) →
deuda de compilador; zlib(zig) → sin deuda.

EL HALLAZGO: matar-gcc no es "{kernel, cmake}" como creía la memoria del proyecto.
Son 47 recetas (36 C puro + 11 Rust con sys-crate C) que dependen del gcc de
Alpine. harkaq lo reveló sólo cuando aprendió a leer el compiler= de la receta —
antes gcc-como-sonda-esperada ocultaba una deuda mayor que toda la que sí detectó.
Reporte: tandas/needs-review-harkaq/deuda-compilador-matar-gcc.md.

Cada una usa gcc porque zig-cc la miscompila (documentado en su receta). Acción
por receta: migrar a zig cuando zig mejore, o aceptar el escape como deuda
conocida. Ahora es una deuda NOMBRADA y contada, no invisible.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 09:43:06 -04:00
sergioandClaude Opus 4.8 35f58aa75b harkaq: granja con volumen + auto-shutdown VALIDADA end-to-end
Test real (worker + volumen + helper), no en el papel. Las 3 piezas:
  1. worker midió lz4+bzip2 → volumen
  2. tras 1 ciclo idle SE AUTO-ELIMINÓ (verificado: el server desapareció solo)
  3. volumen SOBREVIVIÓ con los 2 verdicts; helper efímero ccx13 los recogió al
     hub y se auto-destruyó

3 bugs cazados EN EL TEST (no adivinados):
  - falta --exclude /work: work/=69GB colgó el rsync del up horas
  - cpx11 'unsupported' en hel1 (AMD sin stock) → ccx13
  - collect llamaba harvest con 'local' (rsync a un worker inexistente): ahora
    sólo recoge; clasificar es paso aparte (worker mide, hub clasifica)

+ auto-delete por API REST (curl+metadata), NO hcloud CLI (la golden no lo trae).
El ID propio sale del metadata service, no del nombre.

Cierra los 2 huecos de la 1ª campaña: idle cobrando (ahora auto-delete) y
dependencia de que yo esté viva (ahora el volumen persiste). Runbook:
docs/runbooks/harkaq-granja-volumen.md. Volumen clausurado tras el test (€0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 07:47:36 -04:00
sergioandClaude Opus 4.8 f7916b5ee9 harkaq: granja con volumen persistente + workers auto-terminantes
La arquitectura que la 1ª campaña pidió a gritos (el worker quedó idle ~4h
cobrando). Tres piezas en harkaq-vol.sh {up|collect|close|status}:

1. VOLUMEN persistente (harkaq-cosecha): los workers escriben verdicts a
   /mnt/cosecha, que sobrevive a su destrucción ⇒ NO dependo de estar viva para
   no perder el trabajo.
2. AUTO-SHUTDOWN (harkaq-campana-vol.sh): tras IDLE_CICLOS ciclos con 0
   mediciones nuevas, el worker SE AUTO-ELIMINA. En Hetzner un server apagado
   sigue cobrando ⇒ apagar de verdad es delete. Vía API REST + curl + metadata
   service (NO hcloud CLI: la golden Ubuntu no lo trae; curl siempre está). El
   ID propio sale del metadata, no del nombre.
3. collect: monta el volumen en un helper barato efímero (o un worker vivo),
   rsync al hub, corre el harvest, destruye el helper. close: detach+delete del
   volumen (deja de cobrar, ~€0.44/mes mientras exista).

+ dos fixes de la 1ª campaña horneados en el bucle: borra el artefacto -hkm tras
medir (envenenaba la caché) y los dos gates (ABI>=7 + binario-con-jaula).

El token va al worker (para auto-delete): riesgo acotado — worker efímero, sin
servicios entrantes salvo SSH-por-clave, destruido al terminar.

Bug cazado antes de gastar: las llaves {..} dentro del mensaje de ${1:?...}
cerraban la expansión (CMD salía 'status}').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 07:23:02 -04:00
sergioandClaude Opus 4.8 7ec2e86b7b harkaq: declarar deps medidas por el kernel (cosecha automática)
Cosecha desatendida de la campaña harkaq. Cada dep de acá la denunció el audit de
Landlock en un build real: el kernel vio al build usarla sin declararla, y el
store del hub confirma qué artefacto la provee (deuda DECLARABLE, §4.5).

Recetas:coreutils elfutils expat file flex gawk

No se tocó ninguna receta con deuda IRREDUCIBLE: declarar algo que el store no
provee rompería el build en vez de arreglarlo. Esas van a tandas/needs-review-harkaq.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 06:30:36 -04:00
sergioandClaude Opus 4.8 21ac060c4b harkaq: declarar deps medidas por el kernel (cosecha automática)
Cosecha desatendida de la campaña harkaq. Cada dep de acá la denunció el audit de
Landlock en un build real: el kernel vio al build usarla sin declararla, y el
store del hub confirma qué artefacto la provee (deuda DECLARABLE, §4.5).

Recetas:gettext-tiny giflib git gnupg gperf gzip htop iproute2 jq kbd libassuan libcap libevent libffi libgcrypt libgpg-error libksba libnl libpng libsass libsodium libssh2 libudev-zero libusb libwebp libxml2 libyaml linux-generic linux-headers linux-metal linux-pam linux lz4 mandoc mtools musl nano npth openssh openssl parted pciutils pcre2 pigz procps-ng python3 readline rsync samurai ca-certificates curl doas dosfstools e2fsprogs fontconfig freetype

No se tocó ninguna receta con deuda IRREDUCIBLE: declarar algo que el store no
provee rompería el build en vez de arreglarlo. Esas van a tandas/needs-review-harkaq.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:27:57 -04:00