Medido hoy, y contra mi propia metida de pata: el commit ff0b556 se llevó dentro
un rename y un borrado de otro agente HABIENDO usado `git add <ruta explícita>`
y `git commit` sin -a. O sea, cumpliendo la regla al pie de la letra.
La causa es que el índice es estado COMPARTIDO entre los agentes que trabajan el
árbol: `git commit` commitea el índice entero, no lo que uno acaba de añadir.
Comprobado en un repo de juguete en los dos sentidos:
git add mio.txt && git commit -m … -> arrastra el ajeno.txt que el otro tenía staged
git commit -m … -- mio.txt -> sólo mio.txt; lo del otro queda staged e intacto
La regla decía «sólo rutas explícitas» y esa frase apunta al `add`, que no es
donde está el peligro. Queda apuntando al `commit`, que es donde sí.
Lo levantó la sesión hammer-f8 al ver sus ficheros dentro de mi commit.
4.4 KiB
Reglas del repo compartido
Este repo lo trabajan varios agentes a la vez (hoy: frente granja/store y frente kernel). Lo de
aquí abajo no son preferencias de estilo: son las dos formas conocidas de que un agente destruya el
trabajo de otro sin enterarse. El resto del diseño está en docs/.
1. Todo hammer build va envuelto en flock
flock work/.farm-build.lock ./target/release/hammer --store ./store build <receta>
Para una tanda, tomar el lock una sola vez y no por receta:
flock work/.farm-build.lock bash -c 'for r in ...; do ./target/release/hammer --store ./store build "$r"; done'
Por qué. hammer build comparte work/sources/<dep>-<sha> entre todas las recetas. Dos builds
concurrentes que compartan una dependencia se pisan el árbol de fuentes: uno hace fetch y lo borra
mientras el otro lo usa, y el árbol queda roto para siempre — reintentar no lo arregla. Medido a
escala: al invalidar libdrm, ~93 de 205 recetas KDE murieron con /src/.zwrap/cc is not a full path to an existing compiler tool, que es el wrapper de zig que la receta deja EN EL ÁRBOL, barrido por
el fetch concurrente de otra receta. Es el ADR 0012, sin decidir; hasta que se decida
(lock por árbol / árbol privado / caché inmutable + copia), serializar es la única mitigación
correcta. Por eso el worker corre con JOBS=1.
scripts/farm/farm-worker-loop.sh y campana-deuda.sh ya toman ese mismo fichero de lock, así
que usarlo nos serializa con la granja además de entre nosotros. No está dentro de hammer build
a propósito: esos scripts lo toman por fuera y hammer se bloquearía contra ellos.
2. Nunca git add -A — y acotar el COMMIT, no sólo el add
Sólo rutas explícitas. Un add -A arrastra al commit los ficheros a medias de otro agente. Commits
granulares, en español, directo sobre main, y git push tras cada unidad de trabajo (el origin
empuja a gitea y al espejo privado de GitHub; ver scripts/espejo-setup.sh).
⚠ «Rutas explícitas» en el add NO ALCANZA, y esto es medido, no teórico (2026-09-06): el
commit ff0b556 se llevó dentro un rename y un borrado de otro agente habiendo usado
git add recipes/firefox.toml y git commit sin -a. La causa es que el índice es estado
COMPARTIDO: git commit commitea el índice ENTERO, no lo que vos acabás de añadir, así que
cualquier cosa que el otro agente dejó en git add viaja en tu commit. Comprobado en un repo de
juguete, en los dos sentidos:
git add mio.txt && git commit -m … # ⇒ arrastra ajeno.txt (estaba staged por el otro)
git commit -m … -- mio.txt # ⇒ SÓLO mio.txt; lo del otro queda staged e intacto
Entonces: git commit -m "…" -- <rutas>. El -- acota el commit por pathspec y es lo único que
aísla de verdad. Vale también para git commit -F -. Corolario: no dar por bueno el alcance sin
mirarlo — git show --stat sobre el commit recién hecho cuesta un segundo y es la única forma de
enterarse el mismo día en vez de por el otro agente.
3. Antes de dar un artefacto por presente, mirá que tenga contenido
Un directorio vacío en el store no es un artefacto: es un nombre. Store::has ya lo rechaza y
respaldo-storagebox.sh --listar los separa a work/respaldo-vacios.txt, pero la regla general
sigue valiendo para cualquier código nuevo — un ausente falla ruidosamente; un vacío llega hasta
el final diciendo que todo fue bien.
4. La superficie de la CLI va en INGLÉS; los mensajes, en castellano
Subcomandos, flags y nombres de opciones: inglés, sin excepción (build, hash, hydrate,
mirror push, kernel closure, qorpa pull). Lo que el usuario LEE —ayuda, errores, logs— va en
castellano, y los nombres propios del proyecto son quechua (qorpa, harkaq, yupana, arje).
Por qué. Un verbo de CLI es contrato: entra en scripts, cron y runbooks, y renombrarlo después
rompe llamadores que nadie recuerda. Media superficie en cada idioma obliga a adivinar en cada
comando nuevo, y ya pasó: el ADR 0015 nació con traer/crear/correr y hubo que corregirlo.
⚠ Deuda conocida, NO barrida todavía: varios scripts de scripts/ exponen flags en castellano
(--crear, --traer, --listar, --sha). El barrido es su propia unidad de trabajo — tocarlos de
paso rompe cron y la granja. Código NUEVO nace en inglés desde hoy.