Las 3 raíces de la onda 1 (gsettings-desktop-schemas, gobject-introspection,
gnome-desktop) copiadas a una cola propia + añadida al QUEUES del worker-loop.
Aislar evita que el worker dispare rebuilds de spidermonkey/mutter (onda 2/3).
El cierre real de RECETAS de las 3 es 39 nodos, 0 en incoming-kde: el "gap del
resolver" que temía era de seed-edges (.pc cairo→libX11), NO de deps declaradas.
El espinazo corpus (38 nodos: gtk4/cairo/pango/gdk-pixbuf en recipes/) está 100%
sellado ⇒ el worker cachea todo e sólo construye las 3. Hashes byte-idénticos a
los sellos de incoming-gnome (base_dir no entra al hash).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`build-farm.sh` usa `xargs -P$JOBS`; con >1, dos recetas que comparten dep disparan la carrera del
árbol de fuentes del ADR 0012 y el árbol queda roto para siempre. Se vio a escala: al invalidar
libdrm, 118 de las 205 recetas KDE pasaron de cache-hit a rebuild real y ~93 murieron con
"/src/.zwrap/cc is not a full path to an existing compiler tool" — el wrapper de zig que la receta
deja en el ÁRBOL DE FUENTE, barrido por el fetch concurrente de otra receta sobre el mismo qtbase.
El reintento serial de build-farm tampoco las recupera: el árbol ya quedó inconsistente.
La caché estaba ocultando la carrera, no evitándola. Serializar es la única mitigación correcta
hasta que el ADR 0012 elija salida.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El `echo "ciclo: N recetas en $Q"` sale ANTES de pedir el lock, así que con la campaña corriendo el
journal mostraba el anuncio del ciclo y después nada: parece un loop trabajando y es un loop
bloqueado. Es la misma clase de problema que el "no encuentro el ejecutable zig" apuntando al
directorio equivocado — un log que miente cuesta horas de diagnóstico.
`flock -n` primero, y sólo si falla se anuncia la espera y se bloquea. Sin coste cuando el lock
está libre, que es el caso normal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Los dos construyen sobre el MISMO work/, y `fetch` nombra el árbol de fuentes de forma determinista
(`work/sources/<receta>-<sha16>`, sin nada que dependa de QUIÉN construye) ⇒ dos procesos que
necesiten la misma DEP apuntan al mismo directorio: uno hace `remove_dir_all` mientras el otro
corre `tar -x`. El árbol queda a medias y devuelve "Directory not empty" (os error 39), y así se
queda hasta que alguien lo borra a mano.
No es teórico: la campaña de deuda y el loop pidieron `mesa` a la vez (el loop lo arrastraba desde
la cola KDE al invalidarse libdrm) y se llevó puesta media cascada GUI, con un error que no nombra
la causa. Casi borro ese árbol a mano antes de ver que había un bwrap montándolo.
Grano: la campaña toma el lock para toda su corrida; el loop, por ciclo de cola. Más fino rompería
el `xargs -P2` de build-farm.sh.
Dos honestidades en los comentarios, para no prometer de más:
- `flock` NO es FIFO. El loop vuelve a pedirlo enseguida y le gana a la campaña que espera:
medido, la campaña entra al terminar TODAS las colas, no entre dos. La ventana real es el
IDLE_SLEEP. Por eso espera con techo (LOCK_WAIT=7200) y sale limpia en vez de colgarse.
- Esto NO cierra la carrera del todo: el `-P2` interno del loop puede correr dos recetas de la
misma cola que compartan dep, y ésas se siguen pisando. El arreglo de fondo es un lock POR
ÁRBOL dentro de `fetch`, que cubriría los dos casos.
Verificado con flock real: exclusión mutua (la campaña entra justo cuando el loop suelta), salida
limpia por timeout con rc=0, y la no-equidad de flock medida en vez de supuesta.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
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>
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>
El watchdog de disco purgaba work/sources/* protegiendo solo árboles bind-montados
por bwrap (prot.1) o cuyo nombre coincide con la receta en vuelo (prot.2). Una dep
transitiva (cairo bajo pango/gtk4) se extrae host-side ANTES del bwrap y con nombre
que no coincide con la receta ⇒ el rm -rf competía con el tar y dejaba el árbol a
medias: 'Cannot mkdir' durante la extracción, luego 'Directory not empty' para
siempre (falla rápido antes de bwrap, nunca se recupera). Prot.3: no purgar árboles
modificados hace <2 min (extrayéndose ahora); los fríos se purgan y re-extraen limpio.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- farm-worker-loop.sh: construye QUEUES='recipes/incoming recipes/incoming-go' (en serie por vuelta;
la cola Go aislada se muele 24/7 sin pisar el incoming/ del otro agente).
- scripts/farm/harvest-go.sh: cosecha determinista sin IA (cron del laptop). Baja el store sellado,
y por cada receta SELLADA (cache-hit bajo timeout, NO compila en el hub) hace SMOKE-TEST del binario
(existe + corre version/--help sin panic/segfault) antes de promover+firmar. Lo que falla el smoke va
a tandas/needs-review-weekend/ para el lunes. add explícito (nunca -A), push con reintento.
vault (go-15) falló en 'go mod vendor' con '~/.cache/go-build/...: no such file': durante el compile
de telegraf el disco cruzó DISK_HIGH, el watchdog corrió 'go clean -cache', y el vendor de vault (que
usa esa cache) arrancó en la ventana siguiente sin ficheros. El guard anterior solo miraba 'go mod
vendor' activo en ese instante; ahora difiere la purga si hay cualquier 'hammer build' en vuelo.
go-13 (dnscontrol/lazysql, árboles de deps Go enormes) destapó otra race del watchdog: durante el
fetch/'go mod vendor'/resolve_phases (host-side, antes del bwrap) el árbol no está bind-montado ⇒ el
watchdog lo borraba a mitad del vendor → go.mod desaparecía → 'receta sin compile, heurística no
encontró build system'. Ahora además protege work/sources/<name>-* si <name> tiene un proceso
'hammer build' activo (cubre toda la vida del build, no solo la fase sandbox).
go-12 (terraform/k8s) destapó la race: con árboles de deps de varios GB en paralelo, el disco
cruza DISK_HIGH y el watchdog corría 'go clean -modcache' a mitad de los 'go mod vendor' en vuelo
(.partial: no such file) ⇒ 5 recetas no convergían. Ahora difiere la purga del modcache si hay un
vendor de host activo (la purga de work/sources ya libera lo grueso y es segura). syncthing: main
real en ./cmd/syncthing (la raíz tiene build-constraints que excluyen todo).
El 'go mod vendor' acumula GOMODCACHE (~/go/pkg/mod) sin tope: una tanda Go
grande lo llevo a 31G y lleno el disco de 80G -> I/O-wait disparo el load a 23
(cuello real, no CPU/RAM). El watchdog ahora purga 'go clean -modcache/-cache'
cuando el disco supera DISK_HIGH% (def 82). El vendor/ local de cada receta ya
tiene lo necesario; si pisa un vendor en curso, esa receta reintenta.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Paraleliza el build a un/varios VPS sin exponer gitea ni la clave de firma.
Modelo: el laptop es el HUB (firma + gitea); el worker sólo construye la cola
incoming/ y sella al store local (PROMOTE=0). Reproducibilidad bit-a-bit +
content-addressing ⇒ el store del worker es byte-idéntico, se rsync-ea de vuelta
y el hash valida solo (no hay que confiar en el VPS).
- vps-setup.sh: provisiona Debian/Ubuntu (deps, userns p/bwrap, swap 16G,
rustup, build hammer, bootstrap-devfs rootfs Alpine edge, systemd unit).
- farm-worker-loop.sh: loop autónomo 24/7, PROMOTE=0, watchdog de disco
integrado; muele lo que haya sin esperar al hub.
- hammer-farm.service: systemd, JOBS=2 (CCX13 = 2 vCPU dedicado), Nice/idle-io.
- farm-sync.sh (hub): rsync código+cola arriba, store sellado abajo,
promote+firma+commit+push. Acceso único laptop->VPS por SSH.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>