Vários agentes, um repositório

Cada agente pega uma tarefa que os outros veem como pega. O claim é uma ref do git: o segundo agente que tenta pegá-la é recusado com o nome de quem a detém, e quem trabalha ao lado fica sabendo quais arquivos compartilha.

dois worktrees do mesmo repositório
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

O que dá errado sem ele

Coloque dois agentes de código no mesmo repositório e as falhas são silenciosas. Nada dá erro; o trabalho simplesmente colide.

  • A mesma tarefa, duas vezes

    Os dois agentes leem o mesmo backlog e pegam o mesmo primeiro item. Nenhum sabe que o outro já começou.

  • Os mesmos arquivos, reescritos

    Duas tarefas parecem independentes e mexem no mesmo módulo. A segunda branch desfaz a primeira, e você só descobre no merge.

  • Um claim que expira em silêncio

    Uma sessão morre no meio do caminho. A tarefa continua “em andamento”, ninguém está nela e ninguém sabe o que já foi tentado.

Um tree e uma identidade por agente

Dê a cada agente o próprio working tree, um git worktree ou um clone, numa branch recém-criada a partir da branch padrão, e uma identidade própria em ANK_AGENT. O ank status mostra o quanto você se afastou da branch padrão, porque uma base desatualizada deixa vermelho em outro lugar um tree que estava verde.

Todos os worktrees de um repositório compartilham refs/ank/, então o compare-and-swap do git decide a disputa entre eles: um só vencedor. Clones separados são arbitrados por um remote chamado origin, que o ank init configura para levar refs/ank/*.

Sem ANK_AGENT, a identidade cai para <user>@<hostname>, então dois terminais na mesma máquina são um só agente para as refs. Um segundo claim com o mesmo nome é recusado, e a recusa diz como dar à sessão um nome próprio.

git worktree add ../svc-rate -b task/rate
export ANK_AGENT=codex:rate
duas sessões, um nome
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

Um claim é uma ref do git

ank claim faz três coisas de uma vez, e nenhuma delas é um campo que dá para editar à mão.

  1. A tarefa vai para in_progress

    E o claim define o HEAD, então os comandos seguintes não precisam de id. Um claim por vez para cada identidade.

  2. Surge uma ref em refs/ank/claims/

    É ela que arbitra quando dois agentes vão atrás da mesma tarefa. Todo worktree a enxerga, e o remote a leva até cada clone.

  3. O critério é congelado por hash

    O hash vai para o registro do claim, fora do alcance do editor. Afrouxar o critério para o trabalho caber não desbloqueia nada: o done compara e recusa.

Um lease renovado pelo trabalho

Um claim dura 30 minutos e é renovado por ank context, por ank show na tarefa que você detém e por ank log quando ele escreve. Não existe heartbeat para lembrar: anotar o que você aprendeu é o que mantém a trava. Um claim expirado não está mais vivo, e qualquer um pode pegar a tarefa.

Nunca deixe um claim expirar em silêncio

ank release --reason devolve a tarefa e registra o motivo no log dela, onde o próximo responsável lê com ank log <id> antes de repetir o que você já tentou. ank done não apaga a ref: transforma-a num registro de conclusão com o commit e a branch, para que nenhum outro tree pegue a tarefa enquanto o merge não sai.

o que as refs guardam
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

Um claim aponta os scopes com que se cruza

Toda tarefa tem um scope, um conjunto de globs. Faça claim de uma tarefa cujo scope se cruza com um claim vivo e o ank a pega mesmo assim, com um aviso que nomeia o responsável, a tarefa dele e os caminhos em comum. É um fato para ler, não um erro que bloqueia: se duas tarefas podem dividir um arquivo é uma avaliação, e ela fica com quem faz o claim.

A regra que as skills ensinam aos agentes vem daí: pegue a tarefa que não pode colidir, ou não pegue nenhuma. Quando nada aberto está ao mesmo tempo desbloqueado e livre, uma sessão parada sai mais barato que dois agentes reescrevendo o mesmo perímetro.

um scope é src/auth/**, o outro aponta src/auth/login.ts
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 quem detém o quê

Antes do claim, um agente lê três coisas: o que outro agente detém, com o que o próprio claim se cruzaria e qual ordem o blocked_by impõe.

ank status

A branch, a identidade, o claim que você detém e quando expira, e cada claim detido em outro lugar.

~/svc-rate
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

O DAG de blocked_by, indentado sob o que bloqueia. Uma tarefa sem bloqueio é uma raiz, e está pronta.

~/svc
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

Um dashboard faz polling de ank status e ank find. Não de context ou show, que renovam um claim, nem de check, que poda refs vencidas e, portanto, escreve.

Paralelismo se deduz, não se declara

  • blocked_by é a única ordem

    Tarefas cujos bloqueios terminaram ficam prontas juntas, e o ank context as lista, primeiro as que mais desbloqueiam. Sem blocked_by, a ordem é ficção.

  • Uma branch por tarefa

    ank done prova a tarefa no tree onde ela rodou. Merge é assunto do git; uma mudança feita de várias tarefas ganha uma tarefa de integração, bloqueada por cada parte, com verificadores próprios.

  • O git fica com o que o git faz bem

    Nenhum verbo do ank cria um worktree, nomeia uma branch ou faz merge. Tarefas, claims e provas são o plano do ank; branches e histórico continuam com o git.

Instalar o ank

Dois comandos. Linux, macOS e Windows, com git 2.34 ou mais recente.

npm install -g @haksolot/ank
npx skills add haksolot/ank