hammer-install crece una cara humana sin tocar la mecánica EFI/BIOS validada:
- sin device + TTY ⇒ interactive_setup: detecta discos (/sys/block, marca el
montado como posible medio live), el usuario elige + confirma escribiendo el
nombre del disco, y recoge hostname / credencial de root (contraseña vía
cryptpw o authorized_keys) / zona horaria (best-effort si hay tzdata) /
teclado / tamaños de / y /store.
- apply_post_install_config escribe hostname+hosts, reemplaza el hash de root en
/etc/shadow, instala authorized_keys, symlinkea localtime y deja install.conf;
se llama tras la copia en AMBAS ramas (EFI y BIOS).
- con device ⇒ autoinstalador de siempre (iso/efi-install-test intactos); la
config sale de env (HAMMER_HOSTNAME/ROOT_PW/ROOT_AUTHKEYS/TZ/KEYMAP).
- iso-image.sh: ISO instalador sin AUTO_INSTALL ahora recibe con la TUI en la
consola (INSTALLER_INTERACTIVE, default on) y ofrece reiniciar/caer al live.
Verificado: scripts/install-tui-test.sh (4/4, dry-run con sysfs+rootfs falsos)
+ iso-install-test.sh VERDE end-to-end en QEMU (autoinstalador con la config
nueva bajo busybox real → disco bootea solo y sirve SSH).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Los huecos reales para una distro completa (el sistema ya está: bootstrap/
self-host/kernel/init/EFI/paquetería). Cola aislada en incoming-clib:
git rsync bash ca-certificates sudo doas util-linux sed tzdata e2fsprogs
dosfstools parted iproute2 iputils dhcpcd vim dbus foot. Import 18/18
(17 alpine + 1 nix), deps ya-canónicas podadas. Patches musl viajan.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hammer boot menu ata las tres piezas del contrato: emite el grafo → lanza el
compositor (mirada, --compositor configurable) → activa el nodo que el usuario
dejó en boot-select. Graceful: sin compositor (servidor headless) emite el grafo
y sigue el arranque. Refactor: activate_and_report compartido con boot activate;
--out/--select configurables (testeable sin /run/hammer root-only).
Wiring: iso-image INSTALLER=1 + install-image-efi bundlean el CLI hammer (static
musl) al sistema instalado (el producto trae arje-zero/hammerd pero no el CLI);
el wrapper /sbin/init lo invoca tras hammer-recover, salida al serial (no pinta
tty0 ⇒ respeta cero-parpadeo). Cierra la pieza #3 del handoff mirada (quién
lanza el menú en el boot): lo ownea hammer, desde el hook de init.
Tests: boot_menu.rs (3 caminos del glue: graceful/sin-selección/selección→activate)
+ efi-disk-boot-test asevera que el menú corre. Validado en OVMF: pivote →
INIT-OK → menú (emite boot-graph.json + saltea sin mirada) → arje-zero.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Validado por captura de framebuffer OVMF-GOP: el kernel metal con deferred-
takeover + quiet no toca el framebuffer (Δ=0). Falta sólo el seamless i915 y el
metal real.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
efi-flicker-test valida por CONTRIBUCIÓN del kernel al framebuffer (2 capturas:
handoff-firmware vs post-boot; Δ≈0 ⇒ el kernel no tocó el FB). Con el kernel
metal cero-parpadeo: Δ=0.000 VERDE — el splash de la firmware sobrevive intacto,
sin texto de boot ni cursor (el kernel viejo saltaba de ~3 a ~11). No mide negro
absoluto: el splash de OVMF (TianoCore) daría falso-positivo y no existe en metal.
Fix: busybox switch_root NO mueve /dev ⇒ los wrappers /sbin/init montan devtmpfs
antes de tee'ar HAMMER-EFI-INIT-OK a /dev/ttyS0 (si no, el nodo no existe y el
marcador se perdía). efi-disk-boot-test VERDE con el kernel quiet.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El 1er número de identify -verbose varía por quantum (10.99 en PPM vs 0.129 en
PNG para la misma imagen); el normalizado 0..1 es fiable. Validado: negro→0,
texto→11, umbral 1.5 los separa.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y (fbcon no toca el framebuffer
hasta el primer output ⇒ pantalla negra desde la firmware hasta que mirada abre
el DRM). Cmdline horneada: quiet loglevel=3 vt.global_cursor_default=0, sin el
earlyprintk=efi,keep ignore_loglevel de bringup (forzaban salida y rompían el
deferred-takeover). Orden de consola: ttyS0 ÚLTIMO ⇒ /dev/console=serie ⇒ la
shell de arje-zero sale por serial, no pinta tty0 (pantalla). CONFIG_LOGO ya off.
Fix diagnosticado por captura de framebuffer en OVMF-GOP. Rebuild en curso.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Con el kernel metal cero-parpadeo (quiet), los marcadores serie kernel-side
(EFI-stub/Linux-version/EXT4) desaparecen. Los /init de pivote + /sbin/init
real ahora tee'an marcadores a /dev/ttyS0 (escritura al puerto directo, sortea
el printk ⇒ sobreviven quiet): HAMMER-EFI-DISK-PIVOT-OK + HAMMER-EFI-INIT-OK.
efi-disk-boot-test y efi-install-test pasan a apoyarse en ésos (kernel-side =
informativos). Compatible con el kernel viejo (verbose) también.
efi-flicker-test.sh: valida cero-parpadeo por captura de framebuffer OVMF-GOP
(media de brillo < umbral ⇒ pantalla negra, sin texto de boot). El seamless
i915 queda para metal Intel real.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Evidencia por captura de framebuffer en OVMF-GOP: el kernel metal pinta todo
el log de boot sobre el FB gráfico (simpledrm 160x50). Quitar console=tty0 no
alcanza (VT_CONSOLE=y auto-registra); quiet/loglevel por LoadOptions no frena
(fbcon redibuja el ring buffer + ignore_loglevel horneado fuerza salida).
Fix identificado (requiere rebuild del kernel metal, cambia hash firmado):
CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER=y + cmdline horneada quiet
loglevel=3 vt.global_cursor_default=0 sin earlyprintk/ignore_loglevel
(CONFIG_LOGO ya off). Seamless i915 sólo validable en metal Intel real.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
efi-install-test.sh cierra el lazo completo bajo UEFI (hermano de
iso-install-test.sh que es BIOS): (1) ISO live EFI=1 INSTALLER=1 AUTO_INSTALL
en OVMF con disco en blanco → hammer-install detecta /sys/firmware/efi y toma
la rama EFI (MBR+ESP-0xEF) desatendido → HAMMER-INSTALL-OK; (2) el disco
instalado bootea SOLO en OVMF → firmware → BOOTX64.EFI → pivote → arje-zero
PID1, sin GRUB. VERDE end-to-end.
Prueba el instalador live REAL corriendo dentro del live, no sólo sus
artefactos. ADR 0010: paso 5 cerrado salvo metal.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
hammer-live-install.sh detecta /sys/firmware/efi y ramifica a EFI-stub soberano:
MBR con ESP tipo 0xEF (UEFI arranca una ESP MBR igual que GPT ⇒ sólo busybox
fdisk/mkfs.vfat/mount, sin GPT-tool ni mtools en el live). Arma el mismo
initramfs de pivote (findfs LABEL=hammer-root → switch_root) que el builder
host-side; /sbin/init = wrapper hammer-recover→arje-zero (el pivote ya montó
store/estado por LABEL). La rama BIOS+GRUB queda intacta tras la detección.
iso-image.sh INSTALLER=1 bundlea el kernel metal EFI-stub (bzImage-efi) en el
payload; el genérico no sirve (no hornea initrd=/rdinit= en la cmdline).
MBR+ESP-EF spot-checked en OVMF (firmware → BOOTX64.EFI → pivote → arje-zero).
ADR 0010 actualizado: pasos 2 y 5 ✅.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
install-image-efi.sh arma una imagen GPT+ESP donde el bzImage metal ES el
binario EFI (\EFI\BOOT\BOOTX64.EFI, ruta fallback removible) — sin GRUB ni
systemd-boot. La cmdline horneada del kernel (initrd=/initramfs.cpio.gz
rdinit=/init) se activa al no haber LoadOptions; un initramfs mínimo de pivote
resuelve hammer-root por LABEL (findfs) y hace switch_root a la ext4 real.
Layout: ESP + hammer-root/store/state ext4 (mke2fs -d bajo unshare -r, ESP con
mtools de hammer). Cierra el paso 2 (initrd chico destraba EFI-stub) y el 5.
efi-disk-boot-test.sh valida en OVMF (sin -kernel): VERDE — firmware →
BOOTX64.EFI → pivote → arje-zero PID1. Marcadores serie deterministas (los dos
primeros bytes del handoff firmware→kernel son informativos; el veredicto se
apoya en el montaje/re-mount aguas abajo, prueba concluyente de la cadena).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Hornea firmware nvidia/gp106 (ctxsw/acr/sec2) descomprimido a .bin (nouveau da GL 3D).
- init: modo HARDWARE por defecto (WGPU_BACKEND=gl sobre gallium-nouveau, que da float16
vía unpackHalf2x16; tapa lavapipe). Pascal no tiene NVK ⇒ greeter va por GL, no Vulkan.
- cmdline default = hardware; 'mirada.sw nouveau.modeset=0' = fallback software.
- CHIP/FW_SRC parametrizables para otras GPUs.
Regresión: el fallback software sigue levantando el compositor en la imagen final.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- recipes/mesa-swrast.toml: mesa softpipe hammer-nativo (sin LLVM) para render software.
- scripts/mirada-usb.sh: ensambla rootfs (dev-fs musl + hydrate mirada/seatd/libinput/
mesa + overlay Alpine llvmpipe/lavapipe/nouveau + XKB + PAM + init seatd+compositor) y
empaqueta la USB con linux-generic vía systemd-boot.
- docs/mirada-usb-nvidia.md: uso + estado.
Validado en QEMU virtio-gpu: el COMPOSITOR arranca entero (libseat→card0→GBM+EGL+
GlesRenderer→[8/8]→escritorio, acepta clientes, dibuja la ventana del greeter). El
GREETER (Vello) exige shaders float16 que llvmpipe/lavapipe NO dan ⇒ su login sólo
pinta con GPU real (nouveau+firmware, v2).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El fix cargo_vendor_dir (b580c5b) destrabó mise: selló (ELF estático funcional,
mise 2026.7.0) y se promovió. Sale del diferido; queda en recipes/mise.toml con
[source] cargo_vendor_dir=.hammer-cargo-vendor. README del diferido actualizado.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`cargo vendor vendor` borra lo que no reconoce del dir destino; proyectos que
commitean su PROPIO vendor/ (mise → vendor/aqua-registry, 3.1MB que build.rs lee)
lo perdían → build.rs falla 'No such file'. Nuevo campo opcional [source]
cargo_vendor_dir (default 'vendor', sin cambios para las ~200 recetas selladas)
manda el vendoreo cargo a otro dir; el .cargo/config.toml que emite cargo vendor
ya apunta ahí. mise.toml usa '.hammer-cargo-vendor'. Validado en vivo: aqua-registry
sobrevive + compila 591 crates pasando los muros previos (openssl vía rustls + AR).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mise compila las 828 crates con rustls (openssl resuelto); el muro real es que
`cargo vendor vendor` de hammer borra el vendor/aqua-registry que mise commitea
(3.1MB que build.rs:357 lee). Documentado en el README del diferido. Fix pendiente:
override per-receta del destino de vendoreo cargo.
El default zig-cc exporta AR="zig ar" (con espacio); el crate `cc` lo ejecuta
como binario único y falla. Wrapper `.hammer-zig-ar` per-receta destraba el
sys-crate C. feroxbuster construye+sella; mise pasa openssl vía feature rustls
pero su bin final falla (giant aws-sdk/rattler, a diagnosticar). mdcat (openssl
transitivo) y jless (clipboard X11) diferidos a tandas/deferred-openssl-xcb/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Verificado contra el parser REAL del otro agente (mirada-boot-core::BootGraph,
recién commiteado en tawasuyu): su struct declara `current: String` y
`default: String` OBLIGATORIOS (sin Option, sin default). Mi emisor los omitía
(`skip_serializing_if`) cuando no hay generación viva ⇒ en un sistema recién
instalado (cero upgrades) el JSON era `{"version":1,"nodes":[]}` y mirada
fallaba al parsear con "missing field `current`".
Fix del lado productor (adaptar la salida a la forma publicada del contrato):
BootGraph.current/default pasan a String, presentes siempre, cadena vacía = "sin
generación viva" (degrada limpio: default_index() de mirada cae al primer
bootable). Probado e2e pasando el JSON real de `hammer boot graph` (vacío +
poblado) por mirada-boot-core::BootGraph::from_path → ambos OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El menú de arranque como navegación del grafo content-addressed de estados
(no una lista de kernels). Sube el modelo de generaciones in-place a un grafo
navegable y define el contrato de datos con mirada.
- hammer_upgrade::boot_graph: BootGraph/BootNode (formato exacto del contrato),
build() arma el DAG desde las generaciones (id = of_tree sin b3:, parents =
of_tree del padre, Base para la raíz del linaje), nodo Recovery sintético
colgando de la viva, emit() atómico a /run/hammer/boot-graph.json.
- activate(): resuelve el id content-addressed a una generación y deja el
sistema en ese nodo — no-op si ya viva, rollback paso a paso a un ancestro
(reusa el rollback E4), recovery = un rollback, error honesto ante un "redo"
hacia una generación huérfana (pide re-aplicar el árbol).
- CLI `hammer boot graph [--out|--stdout]` y `hammer boot activate <id>
[--from-select]` (lee el id que mirada deja en /run/hammer/boot-select).
- Tests: DAG + activate a ancestro + recovery + redo-falla; rfc3339 sin deps.
Validado e2e por el binario (3 generaciones → grafo → activar → recovery).
Cierra el lado `proceso` de SDD 15 §H4 (arrancar = activar un nodo del DAG).
mirada ya puede maquetar contra el grafo real (HANDOFF-arranque-grafo.md).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El menú de arranque soberano (sin systemd-boot, sin GRUB) no es un port de GRUB: es la
navegación del grafo content-addressed de estados del sistema, renderizado por mirada
sobre KMS y respaldado por las generaciones/DAG de hammer. Cierra el lado 'proceso' de
SDD 15 §H4 (arrancar = activar un nodo). Fija la división de labores, el contrato de
datos (/run/hammer/boot-graph.json + hammer boot activate), el invariante cero-parpadeo
(simpledrm→i915 seamless) y la vía del loader (squashfs+overlay → initrd chico → EFI-stub
directo viable). Espejo en tawasuyu/HANDOFF-arranque-grafo.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dalfox/templ/errcheck/ineffassign/unconvert (Go) + dprint (Rust) + kyverno (Go),
construidos en un worker efímero hcloud, cosechados y firmados. Binarios musl-static
verificados: dalfox 3.1.2, templ v0.3.1020, dprint 0.55.1, ineffassign, y los
analizadores Go corren. feroxbuster/jless/mdcat/mise quedan staged (frontera *-sys:
openssl-sys/xcb desde fuente).
Fix farm-down: el promote final ahora recorre TODAS las colas del worker
(incoming/incoming-go/incoming-clib), no sólo recipes/incoming — los artefactos de
las otras colas se cosechaban pero no se firmaban.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Los workers efímeros reciclan IPs de Hetzner. accept-new RECHAZA una clave de host
cambiada ⇒ el 'until ssh true' de farm-up colgaba para siempre en una IP reusada.
Como el modelo es hub-and-spoke (worker sin secretos, compute descartable) no hay
superficie MITM: saltamos verificación de host y no ensuciamos known_hosts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Encadena farm-up + farm-down con detección de fin-de-ciclo por el marcador 'ciclo
terminado' del worker-loop (cuenta ocurrencias en el journal, robusto a skew de reloj
y builds largos). 'Subí la cola, corré esto, se apaga solo al terminar.'
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cierra la simetría de la vía observada: además de observar lo que un paquete escribe (H4c),
observa de qué depende A UNA VERSIÓN. Fuente observable sin declaración: el paquete se
construyó contra la versión de sus deps que hay en el repo (deps.runtime del .swm +
expected_hash de cada dep en el índice); si el usuario tiene esa dep instalada a OTRO hash,
la divergió -> rechazo duro. Es el caso wayland DERIVADO (el que H4b captura cuando el autor
declara requires, ahora leído del cierre).
- compat::observed_requires(swm, index) + version_conflicts(db, req) — reusan deps del .swm,
expected_hash del índice e InstalledDb.hash (cero declaración nueva).
- Cableado en install (rechazo duro, no lo salva --force-slots) y en `hammer compat`.
- Verificado e2e real: `hammer compat` marca app INCOMPATIBLE por su dep wayland-protocol
instalada a un hash divergido del repo (read-only, ve el source_patch sin construirlo).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Responde la pregunta que arrancó §H4 ("cuando busco, ¿cuáles puedo adoptar?"): es el `filtrar`
del prototipo wawa-memo, ahora sobre el repo real y READ-ONLY (no construye ni toca nada).
Evalúa cada paquete contra el estado instalado y lo parte en {compatibles, requieren-elección,
incompatibles}, combinando la vía declarada (slots, H4b) con la observada (paths, H4c):
incompatible domina, colisión (de slot o fichero) -> elección, si no compatible.
Verificado e2e real (tests/compat_gate.rs): un repo de dos paquetes se parte correctamente
(uno choca de fichero con lo instalado -> elección; otro limpio -> compatible).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
La misma subida de H1 (prometer -> verificar), ahora sobre topología: la superficie más
común de un paquete es el conjunto de paths que escribe, y esos paths ya están declarados
en el .swm (target_bin + file_drop.path), conocidos ANTES de hidratar.
- compat::output_paths(swm) lee esos paths; compat::path_collisions(db, name, paths) detecta
cuáles ya posee OTRO paquete instalado (reusa InstalledDb.files + owner_of, cero declaración
nueva). Reinstalar el mismo paquete sobre sus propios paths NO colisiona (upgrade).
- Gate en `install` corre el chequeo observado JUNTO al declarado (H4b): pisar el fichero de
otro paquete = caso logo a nivel de fichero (elección) -> aborta salvo --force-slots.
- Verificado e2e REAL (tests/compat_gate.rs, shell-ea al binario hammer): dos paquetes
escriben /share/logo.png; el 2do aborta con "COLISIÓN de fichero" sin escribir nada; con
--force-slots la elección se respeta y el fichero se escribe.
Un paquete SIN declarar slots ya participa del gate por lo que de verdad toca.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sube el modelo de slots de wawa-memo (prototipo host) a hammer:
- Recipe + source_patch del .swm llevan bloque `slots` {claims, requires} (slot->b3:…),
FUERA de hash_inputs (topología ≠ identidad, no mueve el artifact_hash). Viaja intacto
por los dos sentidos del puente (Recipe->.swm->Recipe): test de round-trip.
- InstalledDb registra claims por paquete + system_state() -> slot->hash (el Estado del gate).
- hammer-core::compat::evaluar(estado, slots) -> Veredicto {Compatible, Colision, Incompatible}
(el álgebra probada en wawa-memo, sobre tipos de hammer).
- Gate en `hammer install`: antes de tocar nada evalúa el paquete contra el estado instalado.
Incompatible (requisito sin resolver, caso wayland) -> aborta; Colisión (caso logo) ->
aborta pidiendo elección salvo --force-slots; Compatible -> procede y registra los claims.
Plumbing propagado por los 5 sitios de Mutation::SourcePatch (from_recipe, swm_bridge,
export, bus, orchestrator). Tests: compat (4) + slots-no-en-hash/round-trip (2) +
system_state (1) + puente receta<->swm (1). Workspace compila y verde.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Aterriza H2/H3 sobre el caso de uso real del usuario: compartir configuraciones
por la malla y, al buscar, saber que son compatibles/completas/seguras.
- H4a ✅ (prototipo en wawa-memo): el modelo de slots reclama/requiere; compatible
= colisión sobre la misma superficie (logo=elección, wayland=rechazo); completa y
segura reusan H3c/H2c.
- H4b: subir slots a la receta/.swm real (InstalledDb como slot->Id).
- Sección "la ambición hasta el final": config y programa son el mismo objeto (hash);
el último lado abierto es el proceso (replay del MonotonicLog, plan OS-CRDT).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Migración del worker pet-24/7 a workers efímeros on-demand. farm1 (ccx23 persistente que
idleaba quemando plata) → snapshot golden 'hammer-golden-2026-07-05' (img 405120842: toolchain
+ store sellado horneados) → destruido. Baseline ahora 0 cajas / €0.
- farm-up.sh [N]: crea N workers desde el snapshot (cache-hit instantáneo del catálogo baked),
los alinea con la cola actual del laptop, registra la flota en scripts/farm/.fleet (gitignored).
- farm-down.sh [name...]: cosecha el store (CAS, merge seguro) al laptop y DESTRUYE; promueve+firma.
Orden seguro: sólo destruye si el pull de store salió bien.
- Validado punta a punta: up 1 → boot+cache+toolchain OK → down (cosecha+destruye) OK.
- Cron laptop harvest-go contra farm1 removido (la cosecha ahora la hace farm-down).
El worker sigue sin secretos (hub-and-spoke): compute puro y descartable.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cosecha del ciclo que sacó al VPS del idle. Build-yield 20/22 en incoming-go (fallan
dolt/migrate, quedan staged). El resto de las 'frescas' del refeed resultaron duplicados
ya catalogados (hugo/delve/glow/sops/dnsx/kubeseal/dbmate/jsonnet-bundler/opa/scaleway-cli
promovidos en tandas previas).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
El worker VPS estaba idle (load 0.01): las 3 colas eran 100% cache-hit (tanda go-2026-07-05b
sellada, incoming/ del otro agente, incoming-clib 21/21). Cosecho 13 attrs Go nuevos verificados
por nix eval (los alias de las listas de refeed daban 'ninguna fuente' por no ser attrs reales).
Import 13/13 nix + pin tag→SHA + rsync dirigido a la cola aislada incoming-go/.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Prioridad: el worker VPS nunca idle. Import de 108 candidatos Go nuevos (listas
fuel-go menos el corpus); 2 olas → 51 recetas importadas (yield ~47%), pusheadas
a la cola incoming-go del VPS. El worker las está moliendo (load ~17). Incluye
proyectos Go grandes (dolt/etcd/dagger/crane/gitlab-runner/forgejo) que vendorean
cientos de deps ⇒ builds largos. Crudas de nixpkgs (tag sin pin, algún misimport
Python que falla inocuo); la cosecha promueve sólo las que sellan + pasan smoke.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>