Commit Graph
1397 Commits
Author SHA1 Message Date
Sergio 3fa0fc3817 estado: cosecha granja 2026-08-27T04:32:44Z — avance del árbol KDE 2026-08-27 04:32:45 +00:00
Sergio 6d72101795 estado: cosecha granja 2026-08-27T04:02:10Z — avance del árbol KDE 2026-08-27 04:02:10 +00:00
Sergio 8ee258a4de scripts: atribuir-fallos.py — qué receta EMITE cada error, no cuál lo sufre
El driver marca FALLA en la receta que invocó, pero `hammer build` construye
deps recursivamente: si una dep muere, la FALLA sale a nombre del dependiente.
El 2026-08-27 siete recetas figuraban rotas (xdg-desktop-portal, gnome-desktop,
gjs, gdm, gnome-session, gnome-settings-daemon, gnome-shell) y ninguna lo
estaba: los 1978 errores los emitía gdk-pixbuf. Los tiempos de 2-10 s son la
firma — demasiado rápido para ser un build propio.

Método: quitar ANSI, partir el log por los marcadores `fetch … name=X` y
atribuir cada error al último fetch anterior. Ya destapó dos causas reales
(gdk-pixbuf/gif y gtk4/PIC) y desinfló la lista de la frontera _ssl, donde
`No module named` lo emite SÓLO spidermonkey.
2026-08-27 03:43:58 +00:00
Sergio 32fb404f88 estado: cosecha granja 2026-08-27T03:32:17Z — avance del árbol KDE 2026-08-27 03:32:17 +00:00
Sergio 5044c9f1ca gdk-pixbuf: -Dgif=disabled — el único .so de la receta no podía enlazar libpng no-PIC
En 2.44 `gif` es opción propia con default `auto` y ya no cae bajo `others`.
Sin declararla, el loader de GIF se construía como MÓDULO COMPARTIDO
(`libpixbufloader-gif.so`, el único .so de los 14 targets; el resto son
ejecutables), y una .so no puede enlazar una .a sin PIC: 1978 errores
`relocation R_X86_64_32 ... recompile with -fPIC`, todos de /usr/lib/libpng16.a
(que es --disable-shared).

Coherente con la intención declarada de la receta —«solo PNG (leaf mínimo)»:
jpeg y tiff ya se desactivaban; gif se coló por DERIVA DE VERSIÓN. Por eso el
arreglo es la flag y NO `[deps]`: cambiar libpng→libpng-pic es la decisión de
arquitectura que se descartó para el canónico el 2026-08-21, y que las colas
wlr/gnome resuelven con SOMBRAS propias.

Regresión CONGELADA, no fallo nuevo: se servía por cache-hit y sólo aparece al
reconstruir. Tumbaba en cascada xdg-desktop-portal, gnome-desktop, gjs, gdm,
gnome-session, gnome-settings-daemon y gnome-shell — todas fallando en 2-45 s
por su dep, no por sí mismas.

Verificado: sella en 12 s, 0 errores PIC, artefacto de 58 M con
libgdk_pixbuf-2.0.a y ningún .so. Blast radius nulo: el canónico estaba en
deuda con 0 dependientes sellados en los cinco grafos.
2026-08-27 03:31:09 +00:00
Sergio 252dec45ab estado: cosecha granja 2026-08-27T03:01:58Z — avance del árbol KDE 2026-08-27 03:01:58 +00:00
Sergio 483a6ec25a vigía: medir los FS donde hammer gasta, no el del repo
El store de hammer se mudó al volumen harkaq-cosecha (/dev/sdb, bind-mount
sobre ./store, work/sources y work/repos; CARGO_HOME también). Desde eso,
medir `$ROOT` mide /mnt/vvv, que hammer COMPARTE con tawasuyu y que ya no
es donde gasta.

Por qué se movió: tawasuyu repuebla su target a ~62 G/h. El 27/08 se le
liberaron 92,9 GiB con cargo clean, se lanzó la campaña con 64 G libres y
70 min después el disco estaba en 237 MB — la tanda abortó en cosmic-applets
por un disco que no era suyo. El cargo clean sirve para un pico, no para
financiar una campaña larga: el vecino lo recupera entero en una hora.

El vigía ahora toma el mínimo de {STORE, work/sources, CARGO_HOME}. Con
$ROOT veía 46 G y bajando; ahora ve 84 G reales.
2026-08-27 02:40:17 +00:00
Sergio 993fc239e8 estado: cosecha granja 2026-08-27T02:31:52Z — avance del árbol KDE 2026-08-27 02:31:52 +00:00
Sergio c4ef89b474 estado: cosecha granja 2026-08-27T01:31:55Z — avance del árbol KDE 2026-08-27 01:31:55 +00:00
Sergio d8e36085b2 estado: cosecha granja 2026-08-27T01:02:12Z — avance del árbol KDE 2026-08-27 01:02:12 +00:00
Sergio 320e88f675 estado: cosecha granja 2026-08-27T00:31:56Z — avance del árbol KDE 2026-08-27 00:31:56 +00:00
Sergio 30f164b181 estado: cosecha granja 2026-08-27T00:02:01Z — avance del árbol KDE 2026-08-27 00:02:01 +00:00
Sergio dd58715e54 estado: cosecha granja 2026-08-26T23:32:18Z — avance del árbol KDE 2026-08-26 23:32:18 +00:00
Sergio d5c0a91309 estado: cosecha granja 2026-08-26T22:31:29Z — avance del árbol KDE 2026-08-26 22:31:29 +00:00
Sergio 560448aaf1 estado: cosecha granja 2026-08-26T22:02:59Z — avance del árbol KDE 2026-08-26 22:02:59 +00:00
Sergio 54e66a172a estado: cosecha granja 2026-08-26T21:32:06Z — avance del árbol KDE 2026-08-26 21:32:06 +00:00
SergioandClaude Opus 5 42ec664fbc mirror git: el placeholder de llimphi-counter no cuenta como fallo
Su commit es 000…0 a propósito y no se puede espejar nunca. Un ✗ permanente en cada tanda entrena a
no mirar los rojos, y entonces el que sí importa pasa desapercibido.

Mirror git completo: 570/571 commits, 2,3 G. El que falta es ese placeholder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 21:15:12 +00:00
SergioandClaude Opus 5 ce69ea23d4 ADR 0013: el pin de una receta no siempre es un commit
Documenta los tags anotados de diffutils/findutils-xargs/kustomize y por qué rompían las dos puntas
del mirror, y corrige la pregunta abierta: ningún servidor ha negado aún el fetch por sha suelto.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 21:13:59 +00:00
SergioandClaude Opus 5 c2b1cafff1 mirror git: soportar pines que son objetos tag anotados
diffutils, findutils-xargs y kustomize pinean el SHA de un tag, no de un commit. Rompía las dos
puntas por la misma suposición: el poblador apuntaba una rama al tag (imposible) y hammer escribía
ese SHA en el fichero shallow (que sólo admite commits).

El poblador pela con ^{commit} y manda el objeto tag aparte en refs/tags/hammer-objeto; sin él el
cat-file -e del otro lado no encuentra lo que la receta pide. hammer lee la frontera del bundle con
git bundle list-heads, que no necesita los objetos, y trae refs/*:refs/*.

Los 339 bundles del formato viejo siguen sirviendo: comprobado con una receta de cada forma contra
un repo que no resuelve por DNS.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 21:12:43 +00:00
SergioandClaude Opus 5 276efdeced mirror git: el poblador reporta el error real de git, no una conjetura
Imprimía siempre «upstream no da el commit» pasara lo que pasara. En la primera tanda marcó así a
diffutils, findutils-xargs y kustomize, cuyos commits se traen a mano sin problema: era transitorio.
Ahora bundle() devuelve (ruta, motivo) con el stderr del paso que falló.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 21:07:10 +00:00
Sergio 75b8cdb945 estado: cosecha granja 2026-08-26T20:01:42Z — avance del árbol KDE 2026-08-26 20:01:43 +00:00
Sergio 3d38d5e573 estado: cosecha granja 2026-08-26T19:31:29Z — avance del árbol KDE 2026-08-26 19:31:29 +00:00
SergioandClaude Opus 5 8ab5040a8d ADR 0013: mirror de fuentes git — bundles shallow por commit
Los 606 repos por commit son el 52% de las fuentes y el mirror de tarballs no los cubría. Se espejan
como `hammer/fuentes-git/{commit}.bundle` — 571 commits distintos, porque hay commits compartidos
entre colas y se espeja uno solo.

La identidad es el commit y la verificación la hace git: al desempaquetar comprueba cada objeto
contra su SHA, así que un bundle alterado no pasa. No hace falta índice ni sha256 aparte.

SHALLOW, NO CLONES COMPLETOS. El bundle sale de un `fetch --depth 1` del commit exacto: para `act`
son 9,3 MB en vez del repo entero, y con 571 fuentes eso decide si el mirror cabe. Es legítimo
porque hammer NUNCA usa la historia — lo único que hace con un repo es `git archive <commit> |
tar -x`, materializar un árbol.

EL DETALLE QUE COSTÓ ENCONTRAR. Un bundle hecho desde un repo shallow no lleva la frontera de
historia, y al desempaquetarlo git aborta con «Failed to traverse parents … did not send all
necessary objects». El mensaje dice que faltan objetos y es ENGAÑOSO: llegan enteros —`git archive`
ya funciona pese al error—; lo que falta es decirle a git dónde termina la historia. Se escribe el
propio commit en `<destino>/shallow` antes del fetch. No hay nada que transportar: la frontera de un
`--depth 1` es exactamente ese commit.

Y UN BUG PROPIO QUE VALE DOCUMENTAR: se pasaba al `git fetch` la ruta RELATIVA del bundle, y como
`run_git` invoca `git -C <destino>`, git la resolvía dentro de `<destino>`. Como el fallo del mirror
se traga a propósito para caer a upstream, el síntoma salía lejísimos: el build moría con «commit …
no existe en <repo> tras fetch», culpando a upstream de un error de ruta local. Ése es el precio de
que el mirror falle en silencio, y por eso el silencio se paga con comentarios explícitos.

Verificado igual que el de tarballs, con receta EFÍMERA para que no haya cache-hit: commit de `act`
ya espejado + un repo cuyo host no resuelve por DNS. Sin HAMMER_MIRROR_GIT falla en el clone; con
él, sella — y el artefacto trae el README.md real de act.

`cargo test -p hammer-build`: 5/5. Población de los 571 bundles corriendo aparte.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 19:25:28 +00:00
Sergio f5decc26b1 estado: cosecha granja 2026-08-26T19:01:29Z — avance del árbol KDE 2026-08-26 19:01:29 +00:00
SergioandClaude Opus 5 45b95f78b9 ADR 0013: mirror de fuentes — la URL es transporte, el sha256 es la identidad
`rsync` (404 de samba.org) y `musl` (musl.libc.org no responde) no se pueden construir hoy, y no por
culpa nuestra. Es el estado estacionario: una distro que construye TODO desde fuente tiene tantos
puntos de fallo como fuentes, y son servidores de terceros que nadie nos prometió mantener.

LA MEDIDA, peor de lo que parecía. Sobre 1167 fuentes (561 tarball + 606 git, 79 hosts):
github.com sostiene 742 — el 64% del corpus depende de UN host. Doce hosts sostienen el 89%. Y 43
hosts sostienen exactamente UNA receta cada uno: ahí es donde muerde el bit-rot lento.

El vigía, en su primera corrida: 8 URLs muertas de 1167. Tres de ellas —busybox, freetype,
freetype-shared— están SELLADAS Y EN USO: son el shell y las fuentes del escritorio que se capturó
hoy. Se salvan sólo porque el tarball sigue en la caché local de esta máquina.

LA URL NUNCA FUE LA IDENTIDAD, y el código ya lo sabía: `hash_inputs` usa `tarball:{sha256}` /
`git:{commit}` y el `..` descarta la URL; la caché se nombra `{sha256}.tar` con un comentario que
dice literalmente que cambiar de mirror no la invalida. Añadir mirrors NO re-hashea NADA. Faltaba el
mecanismo, no el diseño.

Orden: caché local → mirror propio → upstream. El mirror va ANTES, no como rescate: el sha256 se
verifica igual, así que no hay diferencia de contenido posible, y un mirror que sólo se usa cuando
upstream falla es un mirror que nadie prueba — se descubre roto el día que hace falta.

LO QUE HAY QUE HACER BIEN. Un mirror que sirve calladamente lo que upstream perdió convierte un
fallo ruidoso en silencio. Por eso construir y vigilar van SEPARADOS: `hammer build` nunca avisa
(sería ruido en 561 recetas), y `fuentes-vigia.sh` pide cabeceras, escribe
docs/state/fuentes-vigia.json y lo corre el latido. Sin ese contrapeso las URLs se mueren una a una
y el corpus queda irreconstruible con todo en verde — el mismo modo de fallo que dejó el grafo de
wlr 17 días anunciando un 121/121 falso.

 PROHIBIDO cambiar el sha256 para "arreglar" una URL muerta. Es la tentación natural ante un 404 y
no arregla una descarga: cambia lo que la distro construye. Otro sha256 es otro contenido, y la
receta seguiría diciendo `rsync 3.4.4` mientras construye otra cosa.

VERIFICADO DE PUNTA A PUNTA. El primer intento —construir busybox con el mirror puesto— dijo BUILD
OK y NO PROBÓ NADA: cache-hit del artefacto, cero bytes descargados. La prueba válida usa una receta
efímera con un sha256 que sí está en el mirror y una URL que ni resuelve por DNS. Sin HAMMER_MIRROR:
`curl (6) Could not resolve host`. Con él: sella, con el contenido real.

Mirror poblado: 126 objetos en el Storage Box que ya se paga. `cargo test -p hammer-build`: 5/5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 18:57:40 +00:00
SergioandClaude Opus 5 f8f679d038 corpus: expat-shared y libffi-shared cierran la fuga al Alpine del lab
sway es `link = "dynamic"` A PROPÓSITO —un compositor dlopea los drivers DRI de mesa en runtime— y
sale con doce NEEDED. Nueve los cubría la clausura (pixman, drm, evdev, input, udev, wayland-server,
wlroots, xkbcommon, libc), porque esas recetas ya son dinámicas. Los otros tres —libz.so.1,
libexpat.so.1, libffi.so.8— venían de recetas `--disable-shared`, así que NINGÚN artefacto sellado
producía ese `.so` y el rootfs los resolvía contra el sysroot Alpine DEL LAB.

POR QUÉ ERA PEOR QUE UNA DEP FALTANTE. Una dep ausente falla ruidosamente. Ésta no: el lab NO entra
en `hash_inputs`, así que el store daba el artefacto por bueno mientras el binario sólo arrancaba en
una máquina que tuviera Alpine debajo. La fuga era invisible para todo el sistema de medición.

`zlib-shared` ya existía en el corpus y sólo faltaba declararla. `expat-shared` y `libffi-shared` son
nuevas, calcadas del patrón: autotools con --enable-shared, sin el truco de --whole-archive que
zlib-shared necesita porque SU configure aborta bajo zig cc. SONAMEs verificados contra lo que pide
el ELF: libexpat.so.1, libffi.so.8, libz.so.1. Exactos.

NO se tocó `link` en las canónicas: entra en `hash_inputs` y habría re-hasheado expat, libffi y todo
lo que los lista en deps —fontconfig, dbus, mesa, glib, python3, los crates `-sys`— cientos de
recetas selladas por libs que ya están bien. El nombre `*-shared` es distinto del canónico, así que
conviven. Verificado antes de construir: recalculadas las 794 recetas, **0 hashes cambiados**, sólo
las 2 nuevas.

Van al CORPUS y no a incoming-wlr/ porque la resolución de deps es hermano→padre: una receta del
corpus no ve una cola. Es donde ya viven zlib-shared, fontconfig-shared, freetype-shared…

VERIFICADO CON PÍXELES, no con el log. Rootfs rehidratado desde cero (126/126), CERO ficheros de
`.dev-fs/alpine`. Cero «Error relocating». La captura tiene el MISMO sha256 que la de las libs de
Alpine: 177fea396caa9dca, 1280x720, 383 colores. Tercera vez que la evidencia sale bit a bit igual
al cambiar la procedencia — la soberanía no costó ni un píxel.

Perfil: escritorio-sway 126/128. Faltan strace (linux-headers del lab) y rsync (404 de upstream).

QUEDA UN RESTO: el loader `/lib/ld-musl-x86_64.so.1` todavía se toma del host. `recipes/musl.toml`
existe en el corpus pero está EN DEUDA y fuera de todo perfil — mismo agujero de declaración, un
nivel más abajo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 18:42:11 +00:00
SergioandClaude Opus 5 428ad80b24 wlr: fuentes y XKB DECLARADAS en el perfil — la captura sale bit a bit idéntica
`dejavu-fonts` y `xkeyboard-config` entran en `recipes/incoming-wlr/` y en las raíces de
`escritorio-sway`. La clausura pasa de 123 a 125 y ambas ya estaban selladas: cero builds.

POR QUÉ TENÍAN QUE SER RAÍCES Y NO PODÍAN LLEGAR POR CLAUSURA. Son DATOS, no binarios: ninguna
receta depende de una fuente ni de un mapa de teclado para COMPILAR. El grafo puede seguir todas las
aristas que quiera y no va a alcanzarlas nunca. Si no se declaran, no están — y el perfil sigue
dando 100%, porque mide la clausura de las raíces declaradas.

Copias BYTE-IDÉNTICAS a las de incoming-kde, a propósito. Ya había tres copias iguales de
xkeyboard-config (kde, gnome, cosmic) y dos de dejavu-fonts (kde, cosmic); ésta es la cuarta. No se
consolidan al corpus ahora porque eso toca las colas de otros tres frentes que otros agentes
trabajan. Mantenerlas idénticas deja esa consolidación como un `git mv` trivial en vez de un merge:
cuatro ficheros iguales se unifican de un tirón, cuatro casi-iguales exigen revisión.

VERIFICADO de punta a punta, no por el log: rootfs rehidratado DESDE CERO con sólo lo que la
clausura declara (123/123), y `sway-headless.sh` sin una línea de inyección manual de fuentes ni
XKB. Cero errores de xkbcommon, cero de fcft. La captura resultante tiene el MISMO sha256 que la de
ayer con todo inyectado a mano — 1280x720, 383 colores. La declaración es exactamente equivalente a
la inyección, y el pipeline reproduce.

Queda como deuda real (no de declaración): libz.so.1 / libexpat.so.1 / libffi.so.8, que sway pide
por estar enlazado dinámico y el corpus sólo produce como `.a`. Y `fonts.conf` NO era deuda: lo trae
el propio artefacto de fontconfig.

Perfil: escritorio-sway 123/125. Los 2 que faltan son strace (choca con los linux-headers del lab) y
rsync (404 de upstream), ninguno del escritorio.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 18:03:46 +00:00
Sergio fd2dc39a83 estado: cosecha granja 2026-08-26T18:00:25Z — avance del árbol KDE 2026-08-26 18:00:25 +00:00
SergioandClaude Opus 5 d68b750109 evidencia: sway headless 2026-08-26 — 1280x720, 383 colores, foot con texto vivo
Captura tomada por grim (del corpus) sobre sway 1.10 / wlroots 0.18.2 / foot 1.27.0, todo construido
desde fuente con zig cc contra musl. Backend headless en bwrap: ni QEMU ni kernel de por medio.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 17:09:56 +00:00
SergioandClaude Opus 5 f7ce610eb9 wlr: el escritorio DIBUJA — arranque headless en 2 min y 5 piezas que el cierre no traía
`scripts/wlr/sway-headless.sh` corre el perfil escritorio-sway dentro de un bwrap, sin QEMU, sin
kernel y sin imagen de disco, y captura con grim. Evidencia: PNG 1280x720 con 383 colores —#242424
fondo de foot, #285577/#4c7899 bordes de sway, #d0d0d0 el texto. sway 1.10 / wlroots 0.18.2 /
foot 1.27.0, todo del corpus.

Sirve porque el ciclo de diagnóstico pasa de ~40 min (product-rootfs + imagen EFI + arranque) a ~2
min. No sustituye al arranque en QEMU: no prueba kernel, initramfs, DRM ni PID1. Prueba lo que el
arranque en VM tapa detrás de una pantalla negra.

LO QUE SE MIDIÓ, y es lo que importa: el perfil da 121/121 y aun así NO arranca solo. Faltan cinco
cosas, ninguna en la clausura:
  · libz.so.1 / libexpat.so.1 / libffi.so.8 — sway salió DINÁMICO y el corpus sólo produce `.a`.
    Sin ellas ni arranca. libz existe como artefacto `zlib-shared`; las otras dos hoy sólo están en
    `.dev-fs/alpine`, o sea que el escritorio depende del rootfs del lab.
  · xkeyboard-config y dejavu-fonts — HAY receta de las dos, en incoming-{kde,gnome,cosmic}. En la
    cola de wlr no, así que el perfil no las alcanza. Un escritorio sin una sola fuente.
  · /etc/passwd y /etc/fonts/fonts.conf.

Y bash NO CORRE: enlazado dinámico contra ncurses, del que sólo hay `libncurses.a` ⇒ «Error
relocating /bin/bash: tgetent: symbol not found».

El modo de fallo vale más que la lista. Nada de esto se vio como un error: `foot` abría, sway
registraba «New xdg_shell toplevel» —la ventana EXISTÍA en el árbol— y la captura salía con UN SOLO
COLOR. El shell moría al instante y la ventana se cerraba antes del frame. Log entero en verde,
pantalla vacía. Por eso la evidencia es contar colores del PNG y nunca leer el log: 1 color = sólo
swaybg, ningún cliente dibujó.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 17:09:04 +00:00
Sergio b6c469d28f estado: cosecha granja 2026-08-26T17:00:23Z — avance del árbol KDE 2026-08-26 17:00:23 +00:00
Sergio fba59f0267 estado: cosecha granja 2026-08-26T16:30:22Z — avance del árbol KDE 2026-08-26 16:30:23 +00:00
SergioandClaude Opus 5 64e1ad2942 khipu: el grafo de sway llevaba 17 días congelado — el latido nunca corría --wlr
`build-state.py --wlr` existe desde que se abrió el frente, y su propio comentario dice que sin él
el frente es INVISIBLE para el khipu. La línea nunca se agregó a `cosecha-cron.sh`: el latido
regeneraba {base,kde,gnome,cosmic} y saltaba wlr.

Medido: build-state-wlr.json quedó fijo el 2026-08-09 anunciando `escritorio-sway 121/121`. Hoy,
regenerado, da 108/123. En el medio se re-hashearon freetype, make, python3 y libpng-pic, que
arrastraron a deuda a 14 dependientes (fuzzel, yambar, swaybg, swaylock, slurp, sed, strace, tzdata,
rsync, pciutils, procs, sd, skim, tokei). Nadie lo vio porque el número que se mira estaba perfecto.

Un grafo que nadie regenera no envejece en cualquier dirección: envejece hacia el OPTIMISMO. Sólo
puede sobreestimar lo sellado, porque el paso del tiempo únicamente invalida hashes, nunca los crea.

Va también `foot` a las raíces de escritorio-sway. El comentario de targets.toml afirmaba que entraba
por la clausura de `cli`; el grafo lo desmiente — `perfil.cli` no lo lista y ningún perfil lo
arrastraba. El escritorio daba 121/121 SIN EMULADOR DE TERMINAL. Se vio al hidratar la clausura para
armar la imagen, no antes: la métrica mide la clausura de las raíces DECLARADAS, y es estructuralmente
ciega a lo que falta en la declaración.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016v9ozVm44p6DB7EMXeZK4o
2026-08-26 16:02:04 +00:00
Sergio 1d72626004 estado: cosecha granja 2026-08-26T16:00:18Z — avance del árbol KDE 2026-08-26 16:00:18 +00:00
Sergio 136133f3d2 estado: cosecha granja 2026-08-23T19:00:37Z — avance del árbol KDE 2026-08-23 19:00:37 +00:00
Sergio fc3cd21da0 estado: cosecha granja 2026-08-23T18:31:18Z — avance del árbol KDE 2026-08-23 18:31:18 +00:00
SergioandClaude Opus 5 b653d39550 corpus: ORDEN se puede imponer desde fuera — 17 recetas sin reconstruir 780
El fichero de orden estaba fijo en la linea 69 y lo regeneraba el bloque python
en cada corrida, asi que no habia forma de pasarle una lista concreta. Con el
grafo ya corregido quedan 17 recetas que cierran las SEIS imagenes (base, cli,
sway, mirada, cosmic, gnome) y ninguna esta bloqueada; correr el corpus entero
para llegar a ellas es absurdo.

Ahora `ORDEN_IMPUESTO=1 ORDEN=<fichero>` usa la lista del que llama y hereda
gratis lo que hace util a este script: el flock de la regla 1, el vigia de disco
de dos sistemas de ficheros y el log por receta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
2026-08-23 18:28:20 +00:00
SergioandClaude Opus 5 90d9ee1b06 estado: el grafo medía 71 selladas donde hay 415 — le faltaba el manifiesto
`work/farm-sellados.txt` habia desaparecido. Es una de las DOS fuentes con las
que `build-state.py` resuelve presencia (la otra, `respaldo-sellados.txt`, sigue
del 2026-08-12), y sin ella el grafo no ve nada de lo que la granja sello desde
entonces: 415 -> 71 selladas, 363 -> 707 de deuda.

Es exactamente lo que el propio script advierte en su cabecera —"un grafo viejo
miente con la misma cara que uno fresco, pero uno recien escrito miente con mas
autoridad"—. El latido lo regenero y lo pusheo a las 17:30 con los numeros
malos, que es el caso peor: un worker sembrado contra ese grafo se habria puesto
a reconstruir ~344 recetas que existen.

El manifiesto se reconstruye desde el volumen `harkaq-cosecha`, montado de SOLO
LECTURA en gioser (mismo hel1 que el volumen y el Storage Box, asi que no hace
falta el helper efimero de `harkaq-vol.sh collect`). 2749 artefactos.

Los 2 directorios vacios del volumen (`.mirror-tmp`, `.bootstrap-tmp`) quedan
FUERA por la regla 3 del CLAUDE.md; ninguno de los 2749 con nombre de hash esta
vacio, verificado con `find -type d -empty`, no supuesto.

Foto real: base 43/51, cli 62/74, escritorio-mirada 28/31. El grafo CIERRA.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
2026-08-23 18:26:15 +00:00
Sergio bfc8dc72c4 estado: cosecha granja 2026-08-23T17:30:32Z — avance del árbol KDE 2026-08-23 17:30:32 +00:00
SergioandClaude Opus 5 ad69429fa0 runbook: el volumen es de UN server, y el latido de gioser va por cron
Deja escrito lo que costo 21 G el 2026-08-22: `farm-up.sh N` apunta los N
workers al mismo VOL_NAME y un volumen de Hetzner se adjunta a un unico server,
asi que correr sharded exige un volumen POR worker. Con el comando exacto.

Y la linea de crontab del latido, que vive FUERA del repo y se perderia si
gioser se rehace. Incluye por que las dos rutas van absolutas: cron arranca en
$HOME y la redireccion del log se abre ahi, no donde uno cree.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
2026-08-23 17:16:34 +00:00
SergioandClaude Opus 5 5cfebf0087 granja: el aviso de "sin volumen" ahora CORTA en vez de sembrar igual
La noche del 2026-08-22 la campana corrio sharded en dos workers. Un volumen de
Hetzner se adjunta a UN server, y este script crea N workers en un bucle
apuntando todos al MISMO VOL_NAME: hworker-1 tomo harkaq-cosecha y para
hworker-2 no quedaba nada que adjuntar. Construyo el shard 1/2 entero en su
disco raiz y el dead-man se lo llevo a las 03:27Z. 21 G, 397 artefactos
sellados; 584 de sus 675 no existen en ningun otro sitio (work/perdidos-hworker2.txt).

Lo caro es que la deteccion YA ESTABA y era correcta:

    echo "   ⚠ el store NO quedó en el volumen ⇒ lo que construya se PIERDE..."

Ese aviso se imprimio, con esas palabras, y el script siguio adelante: armo el
dead-man y sembro la cola igual. Un aviso que no detiene el pipeline no es un
guardian cuando no hay nadie leyendo el log. Es la regla 3 del CLAUDE.md con
otra cara: el ausente (el volumen) llego hasta el final diciendo que todo fue
bien.

Tres cambios, todos sobre el mismo fallo:

  - `hcloud volume attach` dejaba de tragarse el error. Estaba escondido dos
    veces: `>/dev/null 2>&1` el mensaje y `|| true` el codigo de salida.
  - Se pregunta ANTES quien tiene el volumen tomado, y el motivo lo nombra.
  - Los dos ⚠ finales pasan a ser `sin_volumen`, que NO siembra: borra el server
    recien nacido (vacio, para que no quede idle facturando como hworker-4) y
    sale 1. Mismo blindaje que el dead-man y farm-down: solo borra con label
    role=hammer-worker, verificado que gioser no matchea.

Escape explicito para el caso deliberado: SIN_VOLUMEN_OK=1.

Verificado en negativo, que es como se comprueba lo que un guardian IMPIDE: la
llamada corta con exit 1 sin alcanzar la linea siguiente, y con un server sin
label no borra nada.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
2026-08-23 17:13:09 +00:00
SergioandClaude Opus 5 86c3cc787e store-gc: la poda ocurrio y su registro NO se escribio
Hoy `--aplicar` borro los 256 superados y acto seguido murio con

    scripts/store-gc.sh: line 143: work/store-gc-superados.txt: No such file or directory

Esa combinacion —borrado hecho, ledger sin escribir— es exactamente la que
reabre el bucle churn que 2026-08-07 costo 362 artefactos y 24 G: `farm-sync`
usa ese fichero como --exclude-from, y sin el la proxima cosecha del worker nos
devuelve lo mismo que acabamos de borrar. El sintoma seria «dos podas con el
mismo numero», que ya sabemos leer como señal.

El manifiesto de la linea 109 SI se escribio, y la diferencia entre los dos es
que aquel hace `mkdir -p work` antes. Ahora el ledger usa ruta ABSOLUTA
($ROOT/work), hace mkdir+touch, y si aun asi no puede escribirse el script
FALLA RUIDOSAMENTE diciendo que los borrados van a volver y como reconstruir el
registro a mano — en vez de informar exito como hasta ahora.

El ledger de esta tanda queda reconstruido desde su manifiesto (256 entradas).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
2026-08-21 20:31:43 +00:00
SergioandClaude Opus 5 2b4999f682 libpng-pic: la variante que faltaba entre la estatica y la .so
gdk-pixbuf no sellaba, y las dos libpng del corpus fallaban por extremos
OPUESTOS — probadas las dos hoy, una detras de otra:

  libpng         .a sin PIC   → «relocation R_X86_64_64 cannot be used against
                                local symbol; recompile with -fPIC», porque ese
                                link acaba siendo dinamico (aparece libc.so)
                                pese al `link = "static"` de la receta.
  libpng-shared  solo la .so  → «unable to find static system library 'png16'»,
                                porque la receta pide --prefer-static.

Es la misma tenaza que el `bz2` de freetype: los -dev traen .so pero no .a. La
salida no es elegir un extremo sino una .a que TAMBIEN sea PIC. `libpng-pic` es
copia literal del canonico con `--with-pic` en el configure; nada mas.

EVIDENCIA de que es PIC, y hay que medirla bien: 0 relocs R_X86_64_32/32S
contra 706 del canonico — EXCLUYENDO las secciones .debug_*. Sin ese filtro
ambas dan >13000 y parecen identicas; es la correccion de la regla del .a
no-PIC que ya nos mordio en GNOME.

El canonico NO se toca (decision del usuario): 12 dependientes en cuatro colas
y es estatico por diseño. Se añade una SOMBRA en incoming-wlr que cambia una
sola linea de deps. libpng-pic si va al corpus, porque la resolucion es
HERMANO→PADRE y una receta del corpus no veria una variante en incoming-*.

⚠ ALCANCE: la sombra la ve su cola. gtk4, libadwaita, gtksourceview y otras 4
son del CORPUS y siguen resolviendo el gdk-pixbuf canonico roto.

⚠ NO ERA UN FALLO NUEVO: gdk-pixbuf tenia artefacto sellado y se servia por
cache-hit. La reconstruccion no lo rompio, lo DESTAPO.

Verificado: sombra SELLADA, 71 MB, con .a, .pc y los loaders .so reales — no un
directorio vacio haciendose pasar por artefacto.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
2026-08-21 13:08:49 +00:00
SergioandClaude Opus 5 fe9e06de9e lab: musl-libintl — el rootfs traia los runtimes pero no libintl.h
Decision del usuario. La reconstruccion en la granja destapo que toda la zona
grafica del corpus muere en cadena con

    /usr/include/glib-2.0/glib/gi18n-lib.h:25:10: fatal error: 'libintl.h' file not found

y con ella gdk-pixbuf, appstream, gjs, gdm, mutter, gnome-shell, wireplumber,
sway, swaybg, swaylock, xdg-desktop-portal... El header no estaba en NINGUNA de
las dos maquinas (labs identicos, mismo sha256), asi que no era divergencia de
worker: era el corpus.

En Alpine `libintl.h` NO lo trae musl-dev: lo trae `musl-libintl`, que es
justo lo que faltaba. Verificado: `checking for libintl.h... yes`, y glib,
python3, meson y el resto de la base ya sellan contra el lab nuevo.

El apk va contra EDGE, que es rodante, asi que lo que importa no es solo que
funcione sino que NO ARRASTRE: el lock del toolchain cambia en UNA linea
(+musl-libintl-1.2.6-r2), ni una version movida. La vez anterior un `apk add`
se llevo de propina ncurses 6.5→6.6 y readline 8.3.1→8.3.3.

Se eligio `musl-libintl` y no `gettext-dev` a proposito: el segundo mete `.pc`
que compiten con los del store en la resolucion de pkg-config, que es
exactamente como freetype autodetecto bzip2 y tumbo fontconfig con 27 recetas
detras. Este no trae ninguno.

`openssl-dev` sigue FUERA, respetando el veto de 6ab29b1: el host-tool del
kernel enlaza el openssl del corpus desde el overlay. python3 sigue sin `_ssl`
⇒ gjs/gdm/spidermonkey siguen cayendo por esa via, que es otro frente.

Coste asumido: el corpus se re-hashea otra vez.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
2026-08-21 13:08:49 +00:00
SergioandClaude Opus 5 dba9ec47c5 licencias: el detector borraba la evidencia que decia registrar
Consulta solo las recetas que HOY no tienen licencia, asi que su cosecha MENGUA
a cada pasada: lo sembrado ayer ya no sale en --faltan. Y reescribia el fichero
entero con la cosecha del dia. Hoy una pasada con CERO detecciones dejo
docs/licencias-detectadas.tsv en 0 filas y se llevo las 610 que documentaban de
donde salia cada licencia ya sembrada.

Salio con exit 0 diciendo «escrito» y «sembrar con». Un vacio que llega hasta el
final afirmando que todo fue bien — la regla 3 de CLAUDE.md, esta vez sobre el
registro de procedencia en vez de sobre un artefacto.

Ahora FUSIONA con lo registrado (la fila nueva gana) y se niega a sobrescribir
si el resultado pierde filas: un registro de evidencia que encoge es un fallo,
no un resultado. Con CON=0 lo dice y no invita a sembrar nada.

Y las cuatro de HashiCorp quedan declaradas: BUSL-1.1.

No es adivinado ni sale de la API —que devuelve NOASSERTION en las cuatro, y por
eso llevaban meses sin licencia—: es el texto del LICENSE del TAG QUE LA RECETA
PINEA, no el de la rama por defecto. consul v1.22.7, vault v1.21.4, nomad
v1.11.3 y packer v1.15.4 abren con «Business Source License 1.1».

⚠ BUSL-1.1 NO es una licencia libre: prohibe el uso en produccion que compita
con el producto de pago, y solo pasa a MPL-2.0 cuatro años despues de cada
version. Cuatro paquetes del catalogo publicable no son redistribuibles como si
fueran libres.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
2026-08-20 17:00:15 +00:00
SergioandClaude Opus 5 4996807db0 corpus: el vigia de disco miraba un solo sistema de ficheros
Media `df` sobre el repo y nada mas. Pero las recetas Rust no gastan solo ahi:
`cargo vendor` copia DESDE `~/.cargo/registry`, que en gioser esta en `/` (74 G)
y no en `/mnt/vvv` (255 G). Hoy eso eran 138 G libres segun el vigia y 19 G de
verdad — luz verde mientras el que se llenaba era el otro, y llenar `/` no
rompe una tanda, rompe la maquina.

Ahora toma el MINIMO de los dos FS (el del repo y el de CARGO_HOME). Si
comparten FS, `df` devuelve lo mismo dos veces y es inocuo.

La purga tambien se queda corta y crece:
- `work/repos/*`, los clones --mirror de gitea de las hub-only. Se re-clonan.
- `$CARGO_HOME/registry` como ULTIMO recurso y CON GUARDA: el act-runner de
  tawasuyu corre como el mismo usuario y comparte ese ~/.cargo. Purgarlo con un
  `cargo` ajeno en vuelo le arranca los ficheros a SU compilador. Se pregunta
  por el codigo de salida de `pgrep`, no por su stdout.

De paso queda dicho en la cabecera que CARGO_HOME se vigila: en gioser el
driver se lanza con el suyo propio dentro del volumen, y asi hammer deja de
gastar `/`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xd5DComN9Nu47TBpYMBHBn
2026-08-20 16:52:57 +00:00
SergioandClaude Opus 5 bc9f199993 corpus: subir el umbral de purga a 40 G — 25 se quedaba corto
La tanda del 2026-08-13 ABORTO con 7 G libres despues de haber purgado a los
25: una sola receta se comio >18 G entre purga y purga. Son los cargo vendor
de las recetas Rust grandes, que sueltan varios GB cada una.

Con 40/15 la purga ocurre ANTES de que una receta gorda agote lo que queda,
en vez de justo despues. El guardian de aborto hizo su trabajo —paro sin
llenar el disco y sin corromper nada— pero llegar a el significa perder la
tanda; el objetivo es no llegar.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 15:31:00 +00:00
SergioandClaude Opus 5 683dd9cbee freetype: --with-bzip2=no explicito — su autodeteccion tumbo el escritorio entero
Cadena probada de punta a punta (2026-08-12):

1. Se añadio `bzip2-dev` al lab para que CPython tuviera sus modulos.
2. freetype AUTODETECTA bzip2 —la receta desactivaba harfbuzz y brotli de
   forma explicita, pero no este— y lo grabo en su freetype2.pc:
   `Requires: zlib, bzip2, libpng`.
3. Toda receta que enlaza freetype con `-all-static` resuelve ese Requires
   via pkg-config y pide `-lbz2`.
4. Los `-dev` de Alpine traen `.so` pero NO `.a` ⇒ «unable to find static
   system library 'bz2'».
5. Cayo fontconfig y con el 27 recetas: KDE, GNOME y COSMIC dependen de el.

Verificado: el freetype nuevo vuelve a `Requires: zlib, libpng` y fontconfig
SELLA (8290b402...).

Se arregla en la receta y NO quitando bzip2-dev del lab: esto mueve el hash
de freetype y sus dependientes; lo otro re-hashearia el corpus ENTERO por
tercera vez en un dia. freetype solo usa bz2 para fuentes PCF comprimidas.

⚠ Metodo, porque me costo tres hipotesis fallidas: hay CINCO artefactos de
freetype en el store y mirar uno al azar dio el `.pc` equivocado. Y al
apartar bzip2.pc el error cambio a «freetype2 not met» — eso era evidencia A
FAVOR (pkg-config no podia resolver el Requires) y lo lei como si fuera en
contra. Comprobar el hash VIGENTE, no el primero que lista `ls`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 20:48:55 +00:00
SergioandClaude Opus 5 6d99ca669b sandbox: el wrapper no inyecta -mcpu=baseline en modo preprocesador (-E)
util-linux —perfil BASE— no construia: `zig: error: unsupported option
'-mcpu=' for target`. En modo `-dM -E -` zig traduce `-mcpu=baseline` a un
`-mcpu=` VACIO y lo rechaza, asi que `errnos.h` no se generaba.

La receta YA trae un apaño para esto: parchea sus generadores all_errnos y
all_syscalls para filtrar el flag. Pero filtra `"$@"`, y el flag NO viene ahi
— lo inyecta el wrapper DESPUES, en el `exec`. El apaño quedo inerte cuando
se introdujo el wrapper, y no hay forma de esquivarlo desde una receta.

Omitirlo con `-E` es inofensivo por construccion: preprocesar no genera
codigo, asi que no hay CPU al que apuntar.

⚠ MISMO PATRON QUE make/--export-dynamic: el wrapper cambia lo que producen
los builds FUTUROS sin mover ningun hash (no esta en hash_inputs), asi que la
rotura quedo congelada por cache-hit hasta que el corpus se reconstruyo. Este
cambio tiene la misma propiedad ⇒ se hace AHORA, con el corpus a medio
reconstruir y casi nada sellado, no dentro de un mes.

Verificado: util-linux sella (370530ff...).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 14:39:53 +00:00
SergioandClaude Opus 5 3848e5c945 corpus: SHARD=i/N para repartir la reconstruccion entre workers
Con un worker el corpus son ~7 dias (medido: ~9 min/receta sobre 1161).
Repartirlo es la unica forma de bajarlo, y no hace falta planificador
central: cada worker construye SU parte y las deps que le falten se las
construye `hammer build` solo. Al cosechar todo converge en el mismo store
porque las direcciones coinciden — eso lo PRUEBA el paso 4 de
farm-lab-sync.sh, no se supone.

El reparto es por INDICE (i-1, i-1+N, ...) y no por bloques contiguos: el
orden ya esta por objetivo, asi que bloques contiguos le darian a un worker
todo el tramo GUI —el mas lento— y a otro solo hojas rapidas. Intercalando,
todos avanzan por el mismo terreno a la vez.

⚠ Hay trabajo DUPLICADO y es deliberado: dos workers con recetas que cuelgan
de qtbase lo construyen los dos. Sale a cuenta frente a serializar, pero con
4 workers no son 4x, son ~3x.

Un log por shard, o se pisan. Verificado: los 4 shards suman 1161 exactas y
rechaza 5/4 y `abc`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 11:45:41 +00:00