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>
Semantic delta
No definition changed: this change is text, documentation or data.
2 files · +55 −6
diff --git a/docs/runbooks/harkaq-granja-volumen.md b/docs/runbooks/harkaq-granja-volumen.md
new file mode 100644
index 00000000..d7106887
--- /dev/null
+++ b/docs/runbooks/harkaq-granja-volumen.md
@@ -0,0 +1,42 @@
+# Granja harkaq con volumen persistente + auto-shutdown
+
+La arquitectura que la 1ª campaña pidió (quedó idle ~4h cobrando). El worker no depende de que yo
+esté viva y no se queda idle. **Validado end-to-end 2026-07-16** (worker se auto-eliminó, volumen
+sobrevivió con las cosechas, helper efímero las recogió).
+
+## Ciclo
+
+```sh
+scripts/farm/harkaq-vol.sh up 2 # 2 workers midiendo → volumen; se auto-eliminan al agotar cola
+# ... (muelen la noche, se destruyen solos) ...
+scripts/farm/harkaq-vol.sh collect # (cuando esté viva) helper efímero baja las cosechas al hub
+scripts/farm/harkaq-vol.sh close # clausura el volumen (deja de cobrar)
+scripts/farm/harkaq-vol.sh status
+```
+
+## Cómo funciona (y por qué así)
+
+- **Volumen `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.
+- **Auto-shutdown**: tras `IDLE_CICLOS` ciclos con 0 mediciones nuevas, el worker **se auto-elimina
+ vía API REST** (`curl` + metadata service; NO hcloud CLI, que la golden no trae). En Hetzner un
+ server apagado **sigue cobrando** ⇒ apagar de verdad es `delete`.
+- **collect**: monta el volumen en un helper efímero barato (o un worker vivo), rsync al hub, lo
+ destruye. Clasificar los verdicts es un paso aparte (el worker MIDE, el hub CLASIFICA con el
+ store completo).
+
+## Rastrillos que ya se pagaron (todos verificados en vivo)
+
+- **`--exclude /work` es crítico**: `work/` son 69GB; sin excluirlo el rsync del `up` cuelga horas.
+- **Tipo de server**: en hel1 los AMD `cpx*` dan "unsupported/stock"; `ccx13`/`ccx23` sí van.
+- **El token va al worker** (para auto-delete): riesgo acotado — efímero, sin servicios entrantes.
+- **`${1:?...}` sin llaves** en el mensaje: una `}` interna cierra la expansión.
+- El bucle **borra el artefacto `-hkm` tras medir** (envenenaba la caché en la 1ª campaña) y gatea
+ por ABI≥7 + binario-con-jaula (no medir ciego).
+
+## Parar de emergencia
+```sh
+hcloud server list | grep hkw # ver workers
+hcloud server delete hkw-N # matar uno
+scripts/farm/harkaq-vol.sh close # y el volumen
+```
diff --git a/scripts/farm/harkaq-vol.sh b/scripts/farm/harkaq-vol.sh
index b61264b2..a7a28f09 100755
--- a/scripts/farm/harkaq-vol.sh
+++ b/scripts/farm/harkaq-vol.sh
@@ -26,7 +26,7 @@ cd "$HUB"
VOL="${VOL:-harkaq-cosecha}"
IMAGE="${IMAGE:-408909310}" # golden con kernel 6.17 (Landlock ABI 7)
TYPE="${TYPE:-ccx23}"
-HELPER_TYPE="${HELPER_TYPE:-cx22}" # para collect: el más barato que monte el volumen
+HELPER_TYPE="${HELPER_TYPE:-ccx13}" # para collect: el más barato que monte el volumen
LOCATION="${LOCATION:-hel1}"
SSHKEY="${SSHKEY:-desarrollo@jlsoltech.com}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
@@ -75,8 +75,11 @@ EOF
ip=$(hcloud server ip "$name")
wait_ssh "$ip" || { echo " $name no responde"; continue; }
# Provisión: código + harkaq + montaje del volumen + servicio con auto-shutdown.
- rsync -az -e "$SSH" --exclude /store --exclude '/store-*' --exclude /target --exclude /dist \
- --exclude /.dev-fs --exclude /.git --exclude /.scratch --exclude '*.png' --exclude '/content*' \
+ # --exclude /work es CRÍTICO: work/ son 69GB (sources, tandas). Sin él el rsync manda
+ # decenas de GB y el up cuelga horas — pasó. farm-up.sh ya lo excluye; se perdió al copiar.
+ rsync -az -e "$SSH" --exclude /work --exclude /store --exclude '/store-*' --exclude /target \
+ --exclude /dist --exclude /tandas --exclude /.dev-fs --exclude /.git --exclude /.scratch \
+ --exclude '*.png' --exclude '/content*' \
./ "root@$ip:$REMOTE/" >/dev/null
$SSH "root@$ip" "MNT=/mnt/cosecha; mkdir -p \$MNT; mount $DEV \$MNT 2>/dev/null || (mkfs.ext4 -q -F $DEV && mount $DEV \$MNT); mkdir -p \$MNT/verdicts \$MNT/logs"
scripts/farm/harkaq-farm-setup.sh "$ip" >/dev/null 2>&1 || echo " (setup con avisos, sigo)"
@@ -120,9 +123,13 @@ collect)
mkdir -p work/harkaq-campana
rsync -az -e "$SSH" "root@$ip:/mnt/cosecha/verdicts/" work/harkaq-campana/ 2>/dev/null || true
[ -n "$helper" ] && { echo "== destruyo el helper"; hcloud server delete "$helper" >/dev/null; }
- echo "== corriendo el harvest sobre lo recogido"
- scripts/farm/harvest-harkaq.sh "local" 2>/dev/null || \
- echo " (harvest necesita un worker; recogido a work/harkaq-campana/, clasificá con harvest-harkaq)"
+ # NO se corre el harvest acá: harvest-harkaq.sh rsync-ea de un WORKER, y en este punto los
+ # verdicts ya están locales (los trajo el rsync del volumen). Clasificarlos es un paso aparte
+ # —lo hace el hub con el store COMPLETO (el worker mide, el hub clasifica)— y su edición de
+ # recetas + commit no debe colgar de un `collect`. Se reporta y se deja al operador/harvest.
+ n=$(ls work/harkaq-campana/*.verdicts 2>/dev/null | wc -l)
+ echo "== recogidos $n verdicts a work/harkaq-campana/. Clasificar/declarar:"
+ echo " scripts/farm/harkaq-suggest.py <v> ~/.cache/harkaq/base.policy \$PWD/store \$PWD/recipes"
;;
close)