複数のエージェント、ひとつのリポジトリ
各エージェントが取ったタスクは、他のエージェントから取得済みとして見えます。claim は git の ref なので、同じタスクに手を伸ばした 2 番目のエージェントは保持者の名前つきで拒否され、隣で作業するエージェントはどのファイルを共有しているかを知らされます。
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
それがないと何が起きるか
ひとつのリポジトリで 2 つのコーディングエージェントを走らせると、失敗は静かに起きます。エラーは出ません。ただ作業がぶつかるだけです。
同じタスクを二度
両方のエージェントが同じ backlog を読み、同じ先頭の項目を取ります。どちらも相手が始めたことを知りません。
同じファイルを書き換える
独立に見える 2 つのタスクが同じモジュールに触れます。後のブランチが前の変更を打ち消し、merge の時点で初めて気づきます。
黙って切れる claim
セッションが途中で落ちます。タスクは「作業中」のまま、誰も手をつけておらず、何が試されたのかも誰も知りません。
エージェントごとに、ツリーとアイデンティティをひとつ
各エージェントに専用の作業ツリー(git worktree か clone)を与え、デフォルトブランチから切ったばかりのブランチに置き、ANK_AGENT で専用のアイデンティティを与えます。ank status はデフォルトブランチとのずれを示します。古いベースは、ここで緑のツリーを別の場所で赤にするからです。
同じリポジトリの worktree はすべて refs/ank/ を共有するので、その間の競合は git の compare-and-swap が決着をつけます。勝者はひとり。別々の clone は origin という名前の remote を通じて調停され、ank init がそれを refs/ank/* を運ぶように設定します。
ANK_AGENT がなければ、アイデンティティは <user>@<hostname> にフォールバックします。つまり同じマシンの 2 つのターミナルは、ref から見れば同じエージェントです。同じ名前での 2 つ目の claim は拒否され、その拒否メッセージがセッションに自分の名前を与える方法を示します。
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
claim は git の ref
ank claim は 3 つのことを同時に行います。どれも手で書き換えられるフィールドではありません。
タスクが in_progress になる
さらに
claimは HEAD を設定するので、続くコマンドに id は要りません。claim はアイデンティティごとに一度にひとつ。refs/ank/claims/ に ref ができる
2 つのエージェントが同じタスクを狙ったとき、これが調停します。どの worktree からも見え、remote がそれをすべての clone に届けます。
完了条件がハッシュで凍結される
ハッシュは claim の記録に入り、エディタの手は届きません。作業に合わせて条件を緩めても何も解決しません。
doneが比較して拒否します。
作業することで更新されるリース
claim の有効期限は 30 分で、ank context、自分が持つタスクへの ank show、書き込むときの ank log で更新されます。覚えておくべき heartbeat はありません。学んだことを書き留めることが、そのままロックを保つことになります。期限切れの claim はもう有効ではなく、誰でもそのタスクを取れます。
claim を黙って切らさない
ank release --reason はタスクを返し、その理由をタスクの log に残します。次の保持者は、あなたが試したことを繰り返す前に ank log <id> でそれを読みます。ank done は ref を消しません。commit とブランチを記した完了記録に変えるので、merge を待つ間に他のツリーがタスクを取ることはありません。
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
claim は重なる scope を名指しする
どのタスクも scope、つまり glob の集合を持ちます。有効な claim と scope が重なるタスクを claim すると、ank はそれでも取らせたうえで、保持者、そのタスク、共有するパスを示す警告を出します。拒否のためのエラーではなく、読むための事実です。2 つのタスクがファイルを共有してよいかは判断であり、その判断は claim する側に委ねられます。
skills がエージェントに教えるルールはここから来ています。衝突しえないタスクを取るか、何も取らないか。開いているタスクに、ブロックされておらず衝突もしないものがないなら、アイドルのセッションのほうが、2 つのエージェントが同じ範囲を書き換えるより安くつきます。
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
誰が何を持っているかを見る
claim の前に、エージェントは 3 つを読みます。他のエージェントが何を持っているか、自分の claim が何と重なるか、そして blocked_by がどんな順序を課しているか。
ank status
ブランチ、アイデンティティ、保持中の claim とその期限、そして他で保持されているすべての claim。
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
blocked_by の DAG を、ブロックしているタスクの下にインデントして表示します。ブロッカーのないタスクはルートで、すぐに着手できます。
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
ダッシュボードがポーリングするのは ank status と ank find です。claim を更新してしまう context や show ではなく、古い ref を刈り込む、つまり書き込む check でもありません。
並列性は宣言せず、導き出す
順序は blocked_by だけ
ブロッカーが完了したタスクは一斉に着手可能になり、
ank contextがそれを、多くをアンブロックするものから順に並べます。blocked_by がなければ、その順序は作り話です。タスクごとにブランチひとつ
ank doneは、タスクが走ったツリーの中でそれを証明します。merge は git の仕事です。複数のタスクからなる変更には、各部分にブロックされ、独自の検証を持つ統合タスクを用意します。git が得意なことは git に
worktree を作る、ブランチに名前をつける、merge する。そうした ank の動詞はありません。タスク、claim、証明は ank の領域で、ブランチと履歴は git のものです。
ank をインストール
コマンドは二つ。Linux、macOS、Windows に対応し、git 2.34 以降が必要です。
npm install -g @haksolot/ank
npx skills add haksolot/ank