Mehrere Agents, ein Repository
Jeder Agent nimmt sich einen Task, den die anderen als vergeben sehen. Der Claim ist eine Git-Ref: Der zweite Agent, der danach greift, wird mit dem Namen des Halters abgewiesen, und wer nebenan arbeitet, erfährt, welche Dateien er teilt.
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
Was ohne ihn schiefgeht
Lass zwei Coding-Agents auf einem Repository laufen, und die Fehler sind leise. Nichts wirft einen Fehler; die Arbeit kollidiert einfach.
Derselbe Task, zweimal
Beide Agents lesen dasselbe Backlog und greifen sich denselben ersten Eintrag. Keiner weiß, dass der andere schon angefangen hat.
Dieselben Dateien, neu geschrieben
Zwei Tasks wirken unabhängig und fassen dasselbe Modul an. Der zweite Branch macht den ersten rückgängig, und beim Merge merkst du es.
Ein Claim, der still verfällt
Eine Session stirbt mittendrin. Der Task steht weiter auf „in Arbeit“, niemand sitzt dran, und niemand weiß, was schon versucht wurde.
Ein Tree und eine Identität pro Agent
Gib jedem Agent einen eigenen Working Tree, einen git worktree oder einen Clone, auf einem frisch vom Default-Branch abgezweigten Branch, und eine eigene Identität in ANK_AGENT. ank status zeigt den Abstand zum Default-Branch, denn eine veraltete Basis macht einen grünen Tree anderswo rot.
Alle Worktrees eines Repositorys teilen sich refs/ank/, also entscheidet Gits Compare-and-Swap das Rennen zwischen ihnen: ein Gewinner. Getrennte Clones werden über einen Remote namens origin arbitriert, den ank init so einrichtet, dass er refs/ank/* mitnimmt.
Ohne ANK_AGENT fällt die Identität auf <user>@<hostname> zurück, zwei Terminals auf einem Rechner sind für die Refs also ein Agent. Ein zweiter Claim unter demselben Namen wird abgewiesen, und die Meldung sagt, wie die Session einen eigenen bekommt.
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
Ein Claim ist eine Git-Ref
ank claim macht drei Dinge auf einmal, und keins davon ist ein Feld, das man von Hand ändern kann.
Der Task wechselt auf in_progress
Und
claimsetzt HEAD, die folgenden Befehle brauchen also keine ID. Ein Claim pro Identität, zur selben Zeit.Unter refs/ank/claims/ erscheint eine Ref
Sie entscheidet, wenn zwei Agents nach demselben Task greifen. Jeder Worktree sieht sie, und der Remote trägt sie zu jedem Clone.
Das Kriterium wird per Hash eingefroren
Der Hash landet im Claim-Record, außerhalb der Reichweite des Editors. Das Kriterium aufzuweichen, damit die Arbeit passt, entsperrt nichts:
donevergleicht und lehnt ab.
Ein Lease, verlängert durch Arbeit
Ein Claim hält 30 Minuten und wird durch ank context verlängert, durch ank show auf dem Task, den du hältst, und durch ank log, wenn es schreibt. Kein Heartbeat, an den man denken muss: Aufschreiben, was du gelernt hast, hält die Sperre. Ein abgelaufener Claim ist kein lebender mehr, und jeder kann den Task nehmen.
Lass einen Claim nie still verfallen
ank release --reason gibt den Task zurück und hält den Grund in seinem Log fest, wo der nächste Halter ihn mit ank log <id> liest, bevor er wiederholt, was du schon probiert hast. ank done löscht die Ref nicht: Es macht daraus einen Abschluss-Record mit Commit und Branch, damit kein anderer Tree den Task nimmt, solange der Merge aussteht.
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
Ein Claim nennt die Scopes, die er schneidet
Jeder Task hat einen Scope, eine Menge von Globs. Claimst du einen Task, dessen Scope einen lebenden Claim schneidet, nimmt ank ihn trotzdem, mit einer Warnung, die den Halter, seinen Task und die geteilten Pfade nennt. Das ist eine Information, kein Fehler, der etwas verweigert: Ob zwei Tasks eine Datei teilen dürfen, ist eine Abwägung, und die bleibt bei dem, der claimt.
Die Regel, die die Skills den Agents beibringen, folgt daraus: Nimm den Task, der nicht kollidieren kann, oder keinen. Wenn nichts Offenes zugleich frei und kollisionsfrei ist, ist eine untätige Session billiger als zwei Agents, die denselben Bereich umschreiben.
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
Sehen, wer was hält
Vor einem Claim liest ein Agent drei Dinge: was ein anderer Agent hält, was sein eigener Claim schneiden würde und welche Reihenfolge blocked_by vorgibt.
ank status
Branch, Identität, der Claim, den du hältst, und wann er abläuft, dazu jeder Claim, der anderswo gehalten wird.
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
Der blocked_by-DAG, eingerückt unter dem, was blockiert. Ein Task ohne Blocker ist eine Wurzel und bereit.
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
Ein Dashboard pollt ank status und ank find. Nicht context oder show, die einen Claim verlängern, und nicht check, das veraltete Refs entfernt und damit schreibt.
Parallelität wird abgeleitet, nicht deklariert
blocked_by ist die einzige Ordnung
Tasks, deren Blocker fertig sind, sind gemeinsam bereit, und
ank contextlistet sie auf, die mit der größten Wirkung zuerst. Ohne blocked_by ist die Reihenfolge Fiktion.Ein Branch pro Task
ank donebeweist den Task in dem Tree, in dem er lief. Mergen ist Sache von Git; eine Änderung aus mehreren Tasks bekommt einen Integrations-Task, blockiert von jedem Teil, mit eigenen Verifiern.Git behält, was Git gut kann
Kein ank-Verb legt einen Worktree an, benennt einen Branch oder merged einen. Tasks, Claims und Proofs sind die Ebene von ank; Branches und History bleiben bei Git.
ank installieren
Zwei Befehle. Linux, macOS und Windows, mit git 2.34 oder neuer.
npm install -g @haksolot/ank
npx skills add haksolot/ank