gioser

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1471Shell 173Rust 105Markdown 943545 Definitions

fix(granja): el publicador avisaba 48 veces al día de algo que no pasaba

Tras publicar, el intento de adelantar la rama local **falla en el caso normal** y el aviso lo
trataba como anomalía: «⚠ la rama local no pudo adelantar (árbol con cambios)», cada media hora,
para siempre. Es la misma trampa que `espejo-setup.sh` acababa de dejar de pisar — un guardián que
grita en falso se deja de leer, y el día que grite de verdad tampoco se lee.

Y no pasaba nada: el generador acaba de reescribir `docs/state/` en el árbol y eso es justo lo que
se publicó, así que **el árbol ya tiene el mismo contenido que el commit nuevo**. git igual se
niega («local changes would be overwritten») porque su comprobación mira ÍNDICE contra HEAD, no
árbol contra destino. Medido en `/opt/takana`: los 10 ficheros que bloqueaban el ff eran byte a byte
idénticos a lo publicado.

Entonces el adelanto se hace, con `reset --mixed`, que deja el árbol intacto. Va GUARDADO por dos
condiciones, y la primera importa más que el arreglo: **el índice no tiene nada stageado**. El
índice es estado COMPARTIDO (regla 2) y pisar lo que otro agente dejó en `git add` es el daño que
este repo ya documentó con un commit que se llevó un rename ajeno por delante. Si hay algo
stageado, no se toca nada y se dice en voz baja.

De paso, el ref de SEGUIMIENTO: se trae con refspec explícito y se actualiza DESPUÉS del push. Sin
eso quedaba un ciclo atrás por construcción —se trae antes de empujar— y el `git log
origin/main..HEAD` que este guion le sugiere a un humano mentía justo cuando se iba a usar para
diagnosticar.

Controles 9-12 en repo de juguete: árbol con el contenido publicado ⇒ adelanta y queda limpio; tres
ciclos seguidos ⇒ `HEAD == origin/main`, 0 sucios; con algo stageado por otro ⇒ NO toca el índice y
lo ajeno sigue stageado.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

c98dea090a7f433d914dbea113d05cb1c4237329 · by Sergio · (15 d ago) · parent 9615f1e9a · browse the tree at this commit

Semantic delta

No definition changed: this change is text, documentation or data.

1 files · +36 −9

diff --git a/scripts/farm/publicar-estado.sh b/scripts/farm/publicar-estado.sh
index 9a390be6..30b9fe70 100755
--- a/scripts/farm/publicar-estado.sh
+++ b/scripts/farm/publicar-estado.sh
@@ -81,8 +81,13 @@ intento=0
 while :; do
   intento=$((intento + 1))
 
-  git fetch -q "$REMOTE" main || { echo "!! no pude traer $REMOTE/main"; exit 1; }
-  base="$(git rev-parse FETCH_HEAD)"
+  # El refspec explícito `+main:refs/remotes/<remoto>/main` no es adorno: con `git fetch <remoto>
+  # main` a secas el ref de SEGUIMIENTO puede quedar rancio, y entonces el `git log origin/main..HEAD`
+  # que este guion le sugiere a un humano miente justo cuando se va a usar para diagnosticar. El `+`
+  # fuerza el ref de seguimiento, que no es trabajo local: se puede pisar sin perder nada.
+  git fetch -q "$REMOTE" "+main:refs/remotes/$REMOTE/main" \
+    || { echo "!! no pude traer $REMOTE/main"; exit 1; }
+  base="$(git rev-parse "refs/remotes/$REMOTE/main")"
 
   # Índice temporal sembrado con el árbol de origin/main. `GIT_INDEX_FILE` lo aísla del índice
   # compartido del repo, que es el que arrastra el trabajo a medias de otro agente.
@@ -134,6 +139,10 @@ while :; do
   CANON="$(git config --get remote.$REMOTE.url)"
   if git push -q "$CANON" "$commit:refs/heads/main" 2>/dev/null; then
     echo "==> estado publicado: ${commit:0:9} sobre ${base:0:9} ($puestos fichero(s))"
+    # El ref de seguimiento al día CON LO QUE ACABAMOS DE EMPUJAR. Sin esto queda un ciclo atrás por
+    # construcción —se trae antes de empujar— y el `git log origin/main..HEAD` del final mentiría
+    # siempre. Es un ref de seguimiento, no trabajo local: se escribe sin riesgo.
+    git update-ref "refs/remotes/$REMOTE/main" "$commit" 2>/dev/null || true
     # ── ESPEJOS ───────────────────────────────────────────────────────────────────────────────
     # Un espejo que falla NO invalida lo publicado, así que no cambia el código de salida; pero se
     # grita, porque el modo de falla conocido es que el espejo DERIVE EN SILENCIO durante días
@@ -148,14 +157,32 @@ while :; do
         || echo "   ⚠ EL ESPEJO QUEDÓ ATRÁS: $url — lo publicado está en el canónico, el espejo no"
     done
     [ "$espejos" -gt 0 ] || echo "   (este clon no tiene espejo configurado — ver scripts/espejo-setup.sh)"
-    # Adelantar la rama local sólo si es un fast-forward limpio y el árbol no estorba. Si no se
-    # puede, NO es un fallo: lo publicado ya está, que es lo que importa. Se dice y se sigue.
-    if git merge-base --is-ancestor HEAD "$commit" 2>/dev/null; then
-      git merge --ff-only -q "$commit" 2>/dev/null \
-        && echo "   rama local al día" \
-        || echo "   ⚠ la rama local no pudo adelantar (árbol con cambios) — lo publicado NO depende de eso"
-    else
+    # ── ADELANTAR LA RAMA LOCAL ───────────────────────────────────────────────────────────────
+    # Lo publicado NO depende de esto: si no se puede, no es un fallo. Pero tampoco se avisa a lo
+    # bruto, porque este guion corre 48 veces al día y un ⚠ en cada ciclo deja de leerse — la misma
+    # lección que `espejo-setup.sh` acababa de aprender gritando «gh NO autenticado» con el push
+    # funcionando.
+    #
+    # ⚠ EL CASO NORMAL ES QUE EL `merge --ff-only` FALLE, Y NO INDICA NADA MALO. El generador acaba
+    # de reescribir `docs/state/` en el árbol y eso es justo lo que se publicó, así que el árbol ya
+    # tiene EL MISMO CONTENIDO que el commit nuevo. git igual se niega («local changes would be
+    # overwritten») porque su comprobación mira ÍNDICE contra HEAD, no árbol contra destino.
+    if ! git merge-base --is-ancestor HEAD "$commit" 2>/dev/null; then
       echo "   ⚠ la rama local está DIVERGIDA de $REMOTE/main — publicado igual; mirá: git log $REMOTE/main..HEAD"
+    elif git merge --ff-only -q "$commit" 2>/dev/null; then
+      echo "   rama local al día"
+    # Si el árbol YA coincide con el destino en todo lo que el ff tocaría, el adelanto es un no-op
+    # en disco y se puede hacer moviendo la rama. Se hace con `reset --mixed`, que deja el árbol
+    # intacto y reescribe el índice —y por eso va GUARDADO por «el índice no tiene nada»: el índice
+    # es estado COMPARTIDO (regla 2) y pisar lo que otro agente dejó en `git add` es exactamente el
+    # daño que este repo ya documentó. Si hay algo stageado, no se toca y se dice.
+    elif git diff --cached --quiet HEAD 2>/dev/null \
+         && git diff --quiet "$commit" -- $(git diff --name-only HEAD "$commit") 2>/dev/null; then
+      git reset --mixed -q "$commit" \
+        && echo "   rama local al día (el árbol ya tenía el contenido publicado)" \
+        || echo "   la rama local queda detrás — lo publicado NO depende de eso"
+    else
+      echo "   la rama local queda detrás (hay cambios propios en el árbol) — lo publicado NO depende de eso"
     fi
     exit 0
   fi