Varios agentes, un repositorio
Cada agente toma una tarea que los demás ven tomada. El claim es una ref de git: el segundo agente que va a por ella recibe un rechazo con el nombre del titular, y el que trabaja al lado se entera de qué archivos comparte.
ANK_AGENT=claude:auth ank claim 0e7d
claimed TASK-0e7de9c55b80 migrate-auth-to-opaque-sessions -> HEAD
cd ../svc-rate
ANK_AGENT=codex:rate ank claim 0e7d
error[4]: TASK-0e7de9c55b80 held by claude:auth (expires in 30m)
-> ank claim TASK-20e993387d9d (another ready task in this scope)
ANK_AGENT=codex:rate ank claim 20e9
warning: claude:auth holds TASK-0e7de9c55b80, overlapping on src/auth/login.ts
claimed TASK-20e993387d9d rate-limit-the-login-route -> HEAD
Qué se rompe sin él
Pon dos agentes de código sobre un mismo repositorio y los fallos son silenciosos. Nada da error; el trabajo simplemente choca.
La misma tarea, dos veces
Los dos agentes leen el mismo backlog y cogen el mismo primer elemento. Ninguno sabe que el otro ya empezó.
Los mismos archivos, reescritos
Dos tareas parecen independientes y tocan el mismo módulo. La segunda rama deshace la primera, y te enteras en el merge.
Un claim que caduca en silencio
Una sesión muere a mitad de camino. La tarea sigue diciendo «en curso», nadie está en ella y nadie sabe qué se intentó.
Un tree y una identidad por agente
Dale a cada agente su propio working tree, un git worktree o un clon, en una rama recién sacada de la rama por defecto, y su propia identidad en ANK_AGENT. ank status indica cuánto te has desviado de la rama por defecto, porque una base desfasada pone en rojo en otro sitio un tree que estaba en verde.
Todos los worktrees de un repositorio comparten refs/ank/, así que el compare-and-swap de git resuelve la carrera entre ellos: un solo ganador. Los clones separados se arbitran a través de un remote llamado origin, que ank init configura para transportar refs/ank/*.
Sin ANK_AGENT, la identidad recurre a <user>@<hostname>, así que dos terminales en la misma máquina son un solo agente para las refs. Un segundo claim con el mismo nombre se rechaza, y el rechazo explica cómo darle a la sesión el suyo.
git worktree add ../svc-rate -b task/rate
export ANK_AGENT=codex:rate
ank claim 51c2
error[7]: human:marie holds a live claim on TASK-820d259af6a7 (expires in 30m)
-> ank release --reason "<why>" (a second session on this machine sets its own ANK_AGENT)
ANK_AGENT=human:marie-2 ank claim 51c2
claimed TASK-51c2a0f6d418 say-in-the-readme-what-a-session-is-now -> HEAD
Un claim es una ref de git
ank claim hace tres cosas a la vez, y ninguna es un campo que se pueda editar a mano.
La tarea pasa a in_progress
Y
claimfija HEAD, así que los comandos siguientes no necesitan id. Un claim a la vez por identidad.Aparece una ref en refs/ank/claims/
Es lo que arbitra cuando dos agentes van a por la misma tarea. Todos los worktrees la ven, y el remote la lleva a cada clon.
El criterio se congela por hash
El hash va al registro del claim, fuera del alcance del editor. Rebajar el criterio para que el trabajo encaje no desbloquea nada:
donecompara y se niega.
Un lease que se renueva trabajando
Un claim dura 30 minutos y se renueva con ank context, con ank show sobre la tarea que tienes y con ank log cuando escribe. No hay heartbeat que recordar: anotar lo que aprendiste es lo que mantiene el bloqueo. Un claim caducado ya no está vivo, y cualquiera puede tomar la tarea.
Nunca dejes que un claim caduque en silencio
ank release --reason devuelve la tarea y deja el motivo en su log, donde el siguiente titular lo lee con ank log <id> antes de repetir lo que ya probaste. ank done no borra la ref: la convierte en un registro de finalización que nombra el commit y la rama, para que ningún otro tree tome la tarea mientras el merge está pendiente.
git for-each-ref refs/ank/
fc6c72c07eaee7d0e1d31f38316d8d5304382d44 blob refs/ank/claims/TASK-0e7de9c55b80
e671470149b55366650f6da572866a25f33315c5 blob refs/ank/claims/TASK-20e993387d9d
ank release --reason "the criterion names a test that does not exist yet"
released TASK-820d259af6a7 -> open
Un claim nombra los scopes con los que se cruza
Cada tarea lleva un scope, un conjunto de globs. Si tomas una tarea cuyo scope se cruza con un claim vivo, ank la toma igualmente, con un aviso que nombra al titular, su tarea y las rutas compartidas. Es un dato que leer, no un error que rechazar: si dos tareas pueden compartir un archivo es una decisión, y le toca a quien hace el claim.
La regla que las skills enseñan a los agentes sale de ahí: toma la tarea que no puede chocar, o no tomes ninguna. Cuando nada abierto está a la vez desbloqueado y despejado, una sesión parada sale más barata que dos agentes reescribiendo el mismo perímetro.
ANK_AGENT=codex:rate ank claim 20e9
warning: claude:auth holds TASK-0e7de9c55b80, overlapping on src/auth/login.ts
claimed TASK-20e993387d9d rate-limit-the-login-route -> HEAD
Ver quién tiene qué
Antes de hacer un claim, un agente lee tres cosas: qué tiene otro agente, con qué se cruzaría su propio claim y qué orden impone blocked_by.
ank status
La rama, la identidad, el claim que tienes y cuándo caduca, y cada claim que hay en otra parte.
ank status
branch task/rate (default main)
drift 1 entity file(s) differ from main (git merge main)
identity codex:rate (ANK_AGENT)
claim TASK-20e993387d9d Rate-limit the login route
expires 2026-10-02T08:59:19Z
elsewhere 1 claim(s) by other agents
TASK-0e7de9c55b80 Migrate auth to opaque sessions (claude:auth until 2026-10-02T08:59:18Z)
perimeter the scope of TASK-20e993387d9d, 0 constraint(s)
queue 0 proposal(s), 0 finished elsewhere
corpus 0 fault(s), 4 signal(s)
ank graph
El DAG de blocked_by, indentado bajo lo que bloquea. Una tarea sin bloqueos es una raíz, y está lista.
ank graph
.
TASK-0e7d [claimed:claude:auth] Migrate auth to opaque sessions
└── TASK-357b [open] Say in the README what a session is now
TASK-20e9 [claimed:codex:rate] Rate-limit the login route
3 task(s), 2 root(s) — indented under what blocks them
Un dashboard consulta ank status y ank find. No context ni show, que renuevan un claim, ni check, que poda las refs obsoletas y por tanto escribe.
El paralelismo se deduce, no se declara
blocked_by es el único orden
Las tareas cuyos bloqueos están terminados quedan listas a la vez, y
ank contextlas lista, primero las que más desbloquean. Si no hay blocked_by, el orden es una ficción.Una rama por tarea
ank doneprueba la tarea en el tree donde se ejecutó. El merge es cosa de git; un cambio hecho de varias tareas tiene una tarea de integración, bloqueada por cada parte, con sus propios verificadores.Git se queda con lo que git hace bien
Ningún verbo de ank crea un worktree, nombra una rama ni la mergea. Tareas, claims y pruebas son el plano de ank; ramas e historial siguen siendo de git.
Instalar ank
Dos comandos. Linux, macOS y Windows, con git 2.34 o posterior.
npm install -g @haksolot/ank
npx skills add haksolot/ank