Kanban Task Claiming — Architecture
Problem
When multiple Claude agents run concurrently (nightly workers, on-demand agents, CI-triggered agents), they may pick up the same kanban task from the backlog. Without a locking mechanism, two agents can start implementing the same task simultaneously, wasting compute and creating merge conflicts.
Solution
A claim API on the kanban app that atomically marks a task as "In Progress" on the main branch via the GitHub REST API. Agents call a one-liner CLI script before starting work.
Flow (Step by Step)
Components
API Route — POST /api/tasks/[id]/claim
Location: apps/kanban/app/api/tasks/[id]/claim/route.ts
Runs on Vercel — no git CLI, no filesystem. All operations use the GitHub REST API via fetch.
| Step | GitHub API Call | Purpose |
|---|---|---|
| 1 | GET /repos/{repo}/contents/{path} | Read task file from main |
| 2 | Parse frontmatter | Validate status is claimable |
| 3 | GET /repos/{repo}/git/ref/heads/main | Get base SHA for branching |
| 4 | POST /repos/{repo}/git/refs | Create claim branch |
| 5 | PUT /repos/{repo}/contents/{path} | Commit updated file to branch |
| 6 | POST /repos/{repo}/pulls | Open PR |
| 7 | POST /repos/{repo}/issues/{n}/labels | Add skip-deploy + auto-merge |
Responses:
| Status | Meaning |
|---|---|
| 200 | Claimed successfully — { ok, taskId, branch, prUrl } |
| 400 | Invalid task ID (must be [a-z0-9][a-z0-9-]*) |
| 404 | Task file not found in repo |
| 409 | Already claimed (In Progress) or Done/Archived |
| 502 | GitHub API error |
| 503 | GITHUB_TOKEN not configured |
CLI Script — scripts/llm/claim-task.ts
Location: scripts/llm/claim-task.ts
Thin wrapper that calls the API. This is what agents run.
# Basic claim
pnpm tsx scripts/llm/claim-task.ts fix-video-upload-bug
# With assignee (recorded in task frontmatter)
pnpm tsx scripts/llm/claim-task.ts fix-video-upload-bug --assignee "agent-session-xyz"
# Dry run (shows what would happen)
pnpm tsx scripts/llm/claim-task.ts fix-video-upload-bug --dry-run
Exit codes:
0— Claimed successfully, OR already claimed (409 is not an error — the task is locked either way)1— Actual failure (network, auth, task not found)
Override URL: Set KANBAN_API_URL env var to point to a local dev server instead of production.
CI Integration
The claim flow relies on two existing CI mechanisms:
[skip-deploy]in commit message —detect-changes.tssees this and skips all Vercel/GCP deploymentsauto-mergelabel —auto-merge-eligible.ymlworkflow detects that the PR only changes.kanbn/tasks/*.mdfiles (matches thekanban-taskspattern) and merges the PR automatically
No new CI configuration is needed.
Environment Requirements
| Env Var | Where | Purpose |
|---|---|---|
GITHUB_TOKEN | Vercel (kanban app) | PAT or fine-grained token with contents:write + pull_requests:write |
GITHUB_REPOSITORY | Vercel (auto-detected) | owner/repo format — auto-detected from VERCEL_GIT_REPO_OWNER/VERCEL_GIT_REPO_SLUG, falls back to happy-client/happyclient-monorepo |
KANBAN_API_URL | Agent shell (optional) | Override API URL for local development |
Race Condition Handling
If two agents try to claim the same task simultaneously:
- Both read the file from
main→ both see status is "Ready" - Both create branches and commits → both succeed (different branch names)
- Both create PRs → both succeed
- First PR merges → status on
mainis now "In Progress" - Second PR has a merge conflict on the same file → auto-merge fails, PR stays open harmlessly
The window for this race is small (seconds). In practice, the first agent to call the API wins. The second agent's PR will simply fail to merge and can be cleaned up by nightly hygiene.
File Changes Summary
| File | Change |
|---|---|
apps/kanban/app/api/tasks/[id]/claim/route.ts | New API route (GitHub REST API) |
apps/kanban/lib/env.ts | Added GITHUB_TOKEN, GITHUB_REPOSITORY |
scripts/llm/claim-task.ts | New CLI script for agents |
CLAUDE.md | Updated Kanban Task Lifecycle section |