#!/bin/sh
# harvest-harkaq.sh — COSECHA-ONLY de la campaña harkaq (modo desatendido, para cron en el hub).
# Determinista, sin IA. Hermano de harvest-go.sh y con la misma disciplina: no elige, no diagnostica,
# no compila en el hub; sólo convierte en commit lo que el worker YA midió.
#
# Uso (cron):  cd /home/sergio/hammer && scripts/farm/harvest-harkaq.sh root@<ip> >>work/harvest-harkaq.log 2>&1
#
# POR QUÉ EXISTE ASÍ: la lista de "recetas a las que les falta declarar make" NO se puede sacar de
# un regex. Se intentó y falló dos veces en la misma tarde: `\bmake\b` colaba `cargo-make` (el guión
# es límite de palabra) y la versión estrecha perdía LAS CINCO recetas donde harkaq había medido la
# deuda de verdad (`compile = "make …"`: el carácter previo es una comilla). El número bailó 69→45→99
# según el retoque. La fuente de verdad es la MEDICIÓN, no la conjetura — que es exactamente para lo
# que existe harkaq. Este script sólo aplica lo que el kernel denunció.
#
# Ciclo (idempotente, seguro de re-correr):
#   1. git pull --rebase (otro agente pushea al mismo main)
#   2. baja los veredictos del worker
#   3. por cada veredicto: harkaq-suggest contra el store COMPLETO del hub (el del worker es parcial
#      y marcaría como irreducible lo que sí es declarable — el worker MIDE, el hub CLASIFICA)
#   4. si la deuda es DECLARABLE ⇒ añade la dep a la receta. Si es IRREDUCIBLE ⇒ NO la toca: va a
#      tandas/needs-review-harkaq/ con el motivo. Declarar algo que el store no provee rompería el
#      build en vez de arreglarlo.
#   5. commit + push con `git add` EXPLÍCITO (nunca -A: respeta al otro agente)
set -u

HUB="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$HUB"
WORKER="${1:?uso: harvest-harkaq.sh root@<ip>}"

# LOCK: dos cosechas a la vez editarían recetas y harían `git commit` en paralelo = árbol
# corrupto. El harvest es lento (re-indexa el store por veredicto), así que un ciclo puede pisar
# al siguiente. flock con -n: si ya hay una corriendo, esta sale limpia en vez de solaparse.
LOCK="$HUB/work/.harvest-harkaq.lock"
exec 9>"$LOCK"
if ! flock -n 9; then
    echo "$(date -u +%FT%TZ) otra cosecha en curso, salto este ciclo"
    exit 0
fi
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes"
REVIEW="tandas/needs-review-harkaq"
mkdir -p "$REVIEW" work

echo "═══ $(date -u +%FT%TZ) cosecha harkaq desde $WORKER"
git pull --rebase -q 2>/dev/null || echo "   (pull falló; sigo con lo local)"

VERD=work/harkaq-verdicts
rm -rf "$VERD"; mkdir -p "$VERD"
rsync -az -e "$SSH" "$WORKER:$REMOTE/work/harkaq-campana/" "$VERD/" 2>/dev/null || {
    echo "   sin veredictos que cosechar (¿el worker sigue midiendo?)"; exit 0; }

tocadas=""
for v in "$VERD"/*.verdicts; do
    [ -f "$v" ] || continue
    n=$(basename "$v" .verdicts)
    [ -f "recipes/$n.toml" ] || continue

    deps=""; irr=0
    while IFS= read -r j; do
        [ -n "$j" ] || continue
        echo "$j" > /tmp/.hkv.json
        s=$(scripts/harkaq/harkaq-suggest.py /tmp/.hkv.json "$HOME/.cache/harkaq/base.policy" \
              "$HUB/store" "$HUB/recipes" 2>/dev/null) || true
        echo "$s" | grep -q IRREDUCIBLE && irr=1
        # "→ declarar dep: X" (el store lo provee y sabe cuál artefacto es).
        d=$(echo "$s" | sed -n 's/.*→ declarar dep: \([a-zA-Z0-9._-]*\).*/\1/p')
        deps="$deps $d"
    done < "$v"

    deps=$(echo "$deps" | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ')
    [ -n "$deps" ] || continue

    if [ "$irr" = 1 ]; then
        # NO se toca: declarar algo que el store no provee rompe el build en vez de arreglarlo.
        cp "$v" "$REVIEW/$n.verdicts"
        echo "   ?? $n — deuda irreducible ⇒ a needs-review (deps declarables vistas:$deps)"
        continue
    fi

    if python3 scripts/farm/harkaq-add-deps.py "recipes/$n.toml" $deps; then
        echo "   ++ $n — declaradas:$deps"
        tocadas="$tocadas recipes/$n.toml"
    fi
done

# Recoger también las recetas editadas por un ciclo ANTERIOR que no llegó a commitear (el proceso
# murió, SIGPIPE, la máquina se apagó). Sin esto quedan huérfanas para siempre: al re-correr,
# add-deps dice "ya estaban declaradas", `tocadas` sale vacío y nadie las commitea nunca. Pasó en
# el primer ciclo real: 13 recetas editadas y sin committear. Se filtra por `recipes/*.toml` y por
# que el diff SÓLO toque la línea `build = [...]`, para no arrastrar trabajo de otro agente.
for f in $(git diff --name-only -- 'recipes/*.toml' 2>/dev/null); do
    case " $tocadas " in *" $f "*) continue ;; esac
    # ¿el diff toca SÓLO `[deps]`/`build =`? Se ignoran las líneas en blanco añadidas/quitadas
    # (`^[+-]\s*$`): add-deps mete un `\n` tras el bloque y ese `+` vacío hacía que el filtro
    # rechazara recetas legítimas (flex, gawk…) como "de otro agente". Y el mensaje usa comillas
    # SIMPLES: con dobles, el `[deps].build` entre backticks se ejecutaba como comando.
    if git diff -U0 -- "$f" | grep -E '^[-+]' | grep -vE '^(---|\+\+\+)' \
         | grep -vE '^[-+]\s*$' | grep -qvE '^[-+]\s*(build\s*=|\[deps\])'; then
        echo '   ·· '"$f"' — el diff toca algo que no es [deps].build: NO lo toco (¿otro agente?)'
    else
        tocadas="$tocadas $f"
        echo "   ↻ $f — pendiente de un ciclo anterior, se commitea ahora"
    fi
done

if [ -n "$tocadas" ]; then
    # `git add` EXPLÍCITO por rutas: el repo lo comparte otro agente (cola gráfica/Go).
    # shellcheck disable=SC2086
    git add $tocadas
    git commit -q -m "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:$(echo "$tocadas" | tr ' ' '\n' | grep -v '^$' | sed 's|recipes/||;s|\.toml||' | tr '\n' ' ')

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 $REVIEW.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>" && git push -q 2>/dev/null || {
        git pull --rebase -q && git push -q; }
    echo "   ✓ commit+push de $(echo "$tocadas" | wc -w) receta(s)"
else
    echo "   nada que declarar en este ciclo"
fi
