kraken

agent
Security Audit
Warn
Health Warn
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Pass
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

One head, many tentacles — a task queue built on GitHub Issues where named agent workers (Claude Code, GitHub Copilot CLI, ...) claim tasks, execute them, and deliver draft PRs. Zero infrastructure to operate.

README.md

Kraken

Release
License: MIT

You set the targets; the tentacles devour them. Unleash the Kraken.

One head, many tentacles — a task queue built on GitHub Issues where
named agent workers (Claude Code, GitHub Copilot CLI, Pi, or any tool that
follows the protocol) claim tasks, execute them, and record the
evidence.
Write the list once; the tentacles do the rest.

GitHub does the tracking. Your coding agent does the coding. Kraken is just the
protocol between them
— it ships nothing you have to operate.

In 30 seconds

What it is. A task queue for AI coding agents, where the queue is GitHub
Issues in a private repo you own. You write tasks; named agent workers claim
them one at a time, do the work in your prepared environment, and open draft
PRs. You review and merge.

What you run.

/plugin marketplace add rafael-adcp/kraken
/plugin install kraken@kraken
/kraken:init OWNER/tasks --project my_app                 # once
/kraken:unleash OWNER/tasks --worker-name env-1 --project my_app

Launch that last line in N terminals and you have N workers. There is no
server, no database, no daemon — the coordination state is issue labels plus a
git ref, and the dashboard is the GitHub UI you already have.

Why it might be for you. You have more agent-sized tasks than attention:
you're the bus between a task list and a row of terminals, and things get
dropped. Kraken takes you out of that loop without moving your work off the
machine where your services, data and credentials already live.

Why it might not be. If your code is on GitHub and the task needs nothing
your platform doesn't already give it, Copilot's coding agent or
claude-code-action in CI is less to run — see Why not just use
X?
for the honest cut.

Everything below is detail. The walkthrough is the
next stop if you want to run it; PROTOCOL.md is the spec if
you want to build a worker, and HISTORY.md is how that spec got
the shape it has.

Why?

Kraken treats AI coding prompts the way a CI server treats builds: you push
them onto a queue (GitHub Issues), a pool of named workers (tentacles) picks them up, and the issue timeline tells you what happened.

AI coding agents made each change cheap — but you are still the bus between the task list and the terminals. You can only watch so many spinners, juggle so many windows, and stay awake so many hours before something gets dropped. Kraken removes you from the loop and replaces infrastructure with things that already exist:

Concern Kraken's answer
Queue & state GitHub Issues in a private coordination repo you own
Claiming (no race) Atomic compare-and-swap on a git ref — one creator wins, HTTP 422 to the rest
Dependencies Native blocked-by relationships — closing a task unblocks
Parallelism Capacity = how many workers you launch; 1 task per worker
Dead workers The claim is a lease (30 min, renewed as you work); stop renewing and the next worker to read the queue takes the task over
Bad tasks The issue form requires Goal + Acceptance; status reports what a worker still cannot start
Dashboard The GitHub UI — filters, notifications, mobile app
Audit trail The issue timeline: who, when, why, validated how

Work repos can live anywhere (GitHub, GitLab, private servers) — only the
coordination repo needs to be on GitHub, and it holds issues, never code.

Wondering how this differs from Copilot's coding agent, Claude's cloud agents,
or claude-code-action in CI? The honest side-by-side — including when to
prefer them — is Why not just use X?.

Install

Claude Code and Copilot CLI install from the same marketplace — Copilot CLI
reads the Claude Code plugin format, so the same commands work. Pi has no plugin
marketplace: it loads the same skills straight from a kraken checkout.

Claude Code (plugin)

Zero to a draining queue in four commands:

/plugin marketplace add rafael-adcp/kraken
/plugin install kraken@kraken
/kraken:init OWNER/tasks --project my_app          # stand up the queue (once)
                                                   # ...file task issues, then:
/kraken:unleash OWNER/tasks --worker-name env-1 --project my_app

/kraken:unleash drains the queue once, in the session you are talking to. To
keep a worker in ambush, run the shared loop from the work repo's checkout
instead — it starts a fresh claude -p drain whenever a task is startable:

cd /path/to/my_app
/path/to/kraken/scripts/kraken-loop.sh OWNER/tasks --worker-name env-1 --project my_app --harness claude

GitHub Copilot CLI

Same plugin, same commands — Copilot CLI reads the Claude Code plugin format and
loads its three skills:

/plugin marketplace add rafael-adcp/kraken
/plugin install kraken@kraken

Then launch a worker from the work repo's checkout. Copilot takes its unattended
permissions as launch flags rather than from a settings file, so two scripts in
a kraken checkout carry them for you — the same flags, from one source:

cd /path/to/my_app
/path/to/kraken/scripts/kraken-copilot.sh OWNER/tasks --worker-name env-1 --project my_app                     # interactive
/path/to/kraken/scripts/kraken-loop.sh    OWNER/tasks --worker-name env-1 --project my_app --harness copilot   # headless

kraken-copilot.sh opens a Copilot session running /kraken:unleash that drains
the queue once and stays open for you to talk to; kraken-loop.sh is the ambush —
one fresh copilot -p drain whenever a task is startable, with no session to watch.

Pi

Install kraken as a Pi package, which carries the three skills and the
extension together and pins to a release tag:

pi install git:github.com/rafael-adcp/[email protected]

Pi clones the repo and loads skills/ plus extensions/pi/, so the commit
trailers keep their real version stamp and kraken.py contract boundary
answers — the two things a copied skills/ tree breaks (see below). pi list
shows what resolved; pi config turns individual resources off.

Or point Pi at a checkout's skills/ yourself — in
~/.pi/agent/settings.json, or per run with --skill /path/to/kraken/skills:

{
  "skills": ["/path/to/kraken/skills"],
  "extensions": ["/path/to/kraken/extensions/pi/kraken.ts"],
  "defaultTools": ["+grep", "+find", "+ls"]
}
  • Point at the checkout, never at a copy. kraken.py finds the plugin
    manifest and PROTOCOL.md two levels above its skill folder. A copied
    skills/ tree stamps every delivered commit kraken@unknown, and
    kraken.py contract boundary — the rules a worker asks for — prints nothing.
    Check out a release tag to pin a version, the way the plugin is pinned.
  • Mind the +. Pi's default tools are read, bash, edit and write;
    +name adds one, while a plain name replaces the defaults — so
    ["grep", "find", "ls"] would leave the worker unable to edit or deliver.
  • The extensions entry is optional, and only this install needs it. It
    loads kraken's Pi extension, which does for Pi what hooks.json does for the
    other two: hands the session's claim back when the session ends (only that
    session's — kraken.py claim records Pi's PI_SESSION_ID), and refuses
    gh pr merge, gh repo delete and gh issue close in the bash tool. Pi
    compiles it on load; nothing to build. A package install discovers it, and
    the loop passes it with -e, so this entry is for sessions you start by hand
    off a bare skills pointer. Without it a worker still conforms — an
    abandoned claim frees itself within one lease TTL.
  • Pick a model provider other than a Claude plan's OAuth. Pi on Anthropic
    OAuth is billed as extra usage, not against your plan limits — Anthropic
    answers 400 invalid_request_error: "Third-party apps now draw from your extra usage, not your plan limits.". Use an API key or another provider (OpenAI
    works).

The skills are /skill:init, /skill:unleash and /skill:status in Pi — the
prefix belongs to the harness, the arguments are the same. A worker runs headless
through the loop, from the work repo's checkout:

cd /path/to/my_app
/path/to/kraken/scripts/kraken-loop.sh OWNER/tasks --worker-name env-1 --project my_app --harness pi

Deny rules, in two halves. What Copilot denies with --deny-tool, a Pi
worker under the loop refuses this way: the GitHub MCP server's
merge_pull_request and issue_write tools are excluded whole
(--exclude-tools 'mcp__*__merge_pull_request,mcp__*__issue_write'), and the
extension refuses gh pr merge, gh repo delete and gh issue close inside the
single bash tool. That second half matches the command's words
(extensions/pi/deny.py) — it catches
cd x && GH_TOKEN=… gh pr merge 1 --admin but not a variable, eval, a script
or gh api; it is defense in depth, not a sandbox. Protect the work repo's
default branch, and run Pi workers in an isolated environment (see step
3
).

Each command in context — environments, permissions, parallelism — is
the full walkthrough below.

Requirements: git, python3 (every transition runs through the bundled
kraken.py), and a gh CLI from June 2026 or later — the dependency
flags (--add-blocked-by / --blocked-by) shipped then. Older gh still works for
everything else; set dependencies via the Relationships sidebar instead.

Three skills ship in the box:

Skill Role
/kraken:init The bootstrap — stands up a coordination repo: private repo, templates, canonical labels
/kraken:unleash The worker — claims one task at a time, executes, validates, delivers a draft PR, until the queue is idle; scripts/kraken-loop.sh keeps it in ambush
/kraken:status The console — review + decision queues, what's in flight with PR links, and ready-to-paste unleash launch lines

Concepts

Five nouns do all the work in Kraken. Keep them straight and the rest follows:

Concept What it is What it is not Lives on
Coordination repo The kraken's head — a private repo whose Issues are the queue; also holds the labels (the state machine), the claim refs, and the dependency graph A place for code — it holds none GitHub (required)
Work repo Where the code lives; workers push branches + open draft PRs here The queue — it holds no tasks Anywhere (GitHub, GitLab, private)
project:<name> label A task's canonical identity — --project filters on it, and it says which prepared environment the task belongs to Optional — a task without it is invisible to every worker Coordination repo
Worker (tentacle) A named agent session (Claude Code, Copilot CLI, Pi, ...) draining the queue one task at a time, inside one prepared environment A pool — capacity is just how many you launch Your machine / container / clone
Task An open Issue labeled kraken-task (goal, acceptance, notes) that moves in-progress → awaiting-merge / needs-decision Closed until the work truly lands — the merge closes it Coordination repo

How it works (10,000 ft)

                     YOU
                      │ file kraken-task issues
                      ▼
    ┌────────────────────────────────────┐
    │  COORDINATION REPO (GitHub Issues) │
    │  labels · claim refs · dependencies│
    └──────────────────┬─────────────────┘
                       │ claim, renew, release
                       ▼
    ┌────────────────────────────────────┐
    │  TENTACLES (agent workers)         │
    │  ONE task at a time · per env      │
    └──────────────────┬─────────────────┘
                       │ push branch + draft PR
                       ▼
    ┌────────────────────────────────────┐
    │  WORK REPO (GitHub, GitLab, ...)   │
    │  draft PR with Kraken-Task trailer │
    └──────────────────┬─────────────────┘
                       │
                       ▼
                     YOU
                 (review · merge)

The label state machine

Four labels are the whole state machine — every transition is a label change,
which is why the GitHub UI is the dashboard and the issue timeline is the log:

flowchart LR
    S((" ")) -->|you file the issue| Q["kraken-task (queued)"]
    Q -->|claim| P[in-progress]
    P -->|deliver — draft PR or analysis| M[awaiting-merge]
    P -->|blocking question| D[needs-decision]
    P -->|lease expires — the next reader steals it| Q
    D -->|you reply — the next reader requeues it| Q
    M -->|you reply — the next reader requeues it| Q
    M -->|PR merges — the task closes| E(((closed)))

Every requeue arrow lands the task back in the queue with its full thread — the
next claim inherits the whole discussion as context.

Only two transitions are ever yours: answering a decision and merging (or
bouncing) a PR. The tentacles drive everything else.

The coordination contract — task shape, state machine, the machine marker,
the claim algorithm — is normatively specified in
PROTOCOL.md (kraken-protocol/9); it is agent-agnostic, so any
tool that follows it can be a tentacle on the same queue. How a worker executes it
lives in skills/unleash/SKILL.md — a loop around
kraken.py next-action, which answers "what do I do now" with a JSON envelope so
the ordering rules are the program's job rather than something the agent has to
remember. That makes the skill harness-neutral: the GitHub Copilot CLI worker
reuses it through AGENTS.md, and Pi loads it directly with
--skill — both with no deltas at all.

The full walkthrough

  1. Create the coordination repo (once). Running the plugin? One command stands it
    all up — verifies or creates the private repo, installs the task template, deletes
    anything an earlier release installed to be executed server-side, and creates
    the canonical labels (idempotent, safe to re-run):

    /kraken:init OWNER/tasks --project YOUR_PROJECT_NAME
    
    Not running the plugin? The same setup by hand

    One file. Under kraken-protocol/9 the coordination repo runs nothing —
    no workflows, and no vendored copy of the transition program for them to
    exec. Stale claims, an answered task's requeue and queue-entry validation are
    all derived by the reader, on the queue read a worker or status performs
    anyway (PROTOCOL.md §2.1, §6). You do not need GitHub Actions enabled on this
    repo at all.

    gh repo create OWNER/tasks --private --clone && cd tasks
    mkdir -p .github/ISSUE_TEMPLATE
    curl -sL https://raw.githubusercontent.com/rafael-adcp/kraken/main/skills/unleash/task-template.yml -o .github/ISSUE_TEMPLATE/task.yml
    git add -A && git commit -m "chore: kraken task template" && git push
    
    gh -R OWNER/tasks label create kraken-task
    gh -R OWNER/tasks label create in-progress
    gh -R OWNER/tasks label create needs-decision
    gh -R OWNER/tasks label create awaiting-merge
    gh -R OWNER/tasks label create priority:high                    # optional — jumps the startable queue
    gh -R OWNER/tasks label create "project:YOUR_PROJECT_NAME"      # one per project you'll queue
    
  2. Queue the work: one issue per task (goal, acceptance, notes). Every issue
    gets a project:<name> label (workers are scoped to one project — an
    unlabeled task is invisible to all of them) and dependencies via
    gh issue edit <n> --add-label "project:YOUR_PROJECT_NAME" --add-blocked-by <m>.
    Add the optional priority:high label to jump a task to the front of the
    startable queue — workers claim high-priority tasks before older normal ones,
    with createdAt FIFO still ordering each tier.

  3. Prepare the worker environments — one per worker: a machine, container,
    or just a separate clone where that worker will live, with the project's
    toolchain installed, gh authenticated, and git configured. Workers run
    unattended, so the environment's agent settings must pre-allow the
    delivery commands — a permission prompt with nobody around stalls the task.
    (Copilot's equivalent is launching with --allow-all-tools --no-ask-user,
    plus deny rules that keep the authorization boundaries at the tool layer —
    --deny-tool='shell(gh pr merge:*)', 'shell(gh repo delete:*)',
    'shell(gh issue close:*)', and the GitHub MCP server's
    merge_pull_request / issue_write; scripts/kraken-copilot.sh and
    scripts/kraken-loop.sh --harness copilot pass all of them. Claude Code under
    scripts/kraken-loop.sh --harness claude gets the same floor from
    scripts/lib-claude-drain.sh: --permission-mode dontAsk, the worker's tools allowed, and deny rules for the same three gh
    commands and the GitHub MCP server's two tools — never
    --dangerously-skip-permissions.) Pi inverts the problem: it never
    asks before a tool call, so there is nothing to pre-allow, and it can deny
    only whole tools, never a gh command inside bash. Its own security
    notes
    recommend running it inside a
    container, VM or sandbox; for a kraken worker, take that advice.

    Example allowlist for the working directory's .claude/settings.json
    {
      "permissions": {
        "allow": [
          "Bash(git add:*)",
          "Bash(git commit:*)",
          "Bash(git checkout:*)",
          "Bash(git push:*)",
          "Bash(gh -R OWNER/tasks:*)",
          "Bash(gh pr create:*)"
        ]
      }
    }
    

    Substitute your coordination repo in the gh -R line (queue operations are
    always explicit about their repo; it holds issues only, so nothing lands
    there). Extend the list with what the project's acceptance checks need
    (test runner, package manager).

    Never pre-allow what lands work: gh pr merge stays deliberately off the
    list so merging keeps its ask-gate, and since an allow-list cannot tell a
    work branch from a default branch, protect the work repo's default branch
    (required review) for the hard guarantee. Merges always stay with you.
    Workers that would share test state (database, fixtures, ports) cannot
    share an environment: fully isolated environments, or one worker.

  4. Unleash the kraken — one worker per environment you prepared. Capacity is
    decided at launch: every worker takes ONE task at a time, so a project gets
    exactly as much parallelism as the number of workers you point at it.

    # one tentacle into the "your_project_1" environment -> one worker
    /kraken:unleash OWNER/tasks --worker-name your_project_1-env-1 --project your_project_1
    
    # five tentacles, five isolated clones -> five workers draining your_project_2
    /kraken:unleash OWNER/tasks --worker-name data-env-1 --project your_project_2
    /kraken:unleash OWNER/tasks --worker-name data-env-2 --project your_project_2
    

    Each of those drains once. To keep a worker in ambush, launch it from its
    work repo with scripts/kraken-loop.sh, the same three arguments, and a
    required --harness copilot, claude or pi — see Keep it
    draining
    and the agent cheat sheet.

    Workers deliver on work branches + draft PRs — never the default branch,
    never a merge. Branches follow each work repo's own naming convention (CI
    pipelines key on those patterns); traceability comes from commit trailers
    (Kraken-Task: OWNER/work-tasks#12 (worker: ..., [email protected])).

  5. Come back to evidence: /kraken:status OWNER/tasks prints both human-facing
    queues at once — the awaiting-merge review queue (each task with a result comment
    and a draft PR link), the needs-decision decision queue (questions with options +
    recommendation), plus what's still in flight and any orphan whose PR already merged
    but whose issue never closed. Scripting instead? The raw filters are
    gh -R OWNER/tasks issue list --state open --label awaiting-merge and the same with
    --label needs-decision. Merging a PR closes its task (Closes reference) and
    unblocks the dependents. Nothing merges without you.

Keep it draining

An empty queue doesn't have to stop a worker. Continuous ambush has one
supervisor on every harness — scripts/kraken-loop.sh,
run from the work repo's checkout (or pointed at it with --work-dir). Each poll
runs the free, read-only kraken.py list-startable; only when a task is actually
startable does it start the agent — one fresh --once drain pass, the shared
/kraken:unleash skill carrying tasks until the queue is idle again. An idle
queue costs zero LLM tokens. Enqueue from anywhere (gh issue create, web UI,
mobile app) and the worker picks it up within a poll (60s; --poll changes it).

cd /path/to/my_app
/path/to/kraken/scripts/kraken-loop.sh OWNER/tasks --worker-name env-1 --project my_app --harness copilot  # Copilot CLI
/path/to/kraken/scripts/kraken-loop.sh OWNER/tasks --worker-name env-1 --project my_app --harness claude   # Claude Code
/path/to/kraken/scripts/kraken-loop.sh OWNER/tasks --worker-name env-1 --project my_app --harness pi       # Pi

The shell owns the supervision — polling, starting the agent, Ctrl-C, and
handing back a claim a dead drain left behind (see the self-healing
FAQ
); the skill owns the work. The contract stays in the kraken checkout
the script sits in: Claude Code loads it as the plugin (--plugin-dir), Copilot
gets it by absolute path, Pi with --skill. The loop never outlives its
terminal, and a queue it cannot read (expired token, dead network) is reported
on stderr, never as an idle queue.

One process per pass, so no conversation carries over. Each drain starts a
fresh agent with a fresh context — the isolation a subagent would give, and on
purpose for unattended work. What a worker needs from earlier passes lives on
the task thread, which next-action hands back. If you want to talk to a
worker, run /kraken:unleash in a session instead: it drains once, reports, and
ends its turn — it does not watch the queue in the background.

Want a bounded run — a scheduled container, a one-off drain? Pass --once to
the loop: one pass, then exit.

On Windows, the loop is a bash script: run it from WSL or Git Bash, with
copilot, python3 and git installed on that side (under WSL, install Copilot
CLI inside the distro — npm install -g @github/copilot — rather than calling the
Windows one, which a Linux shell cannot launch). Everything the loop invokes then
resolves in the same environment — gh included: kraken.py reads the queue
with GH_TOKEN, GITHUB_TOKEN or gh auth token, and a WSL shell does not see a
gh.exe installed on Windows. The loop and kraken-copilot.sh check for a token before they start.

GH_TOKEN and Copilot's login. Copilot CLI also authenticates its model
with GH_TOKEN/GITHUB_TOKEN when one is set (after COPILOT_GITHUB_TOKEN), so a
token exported only for the queue — a classic PAT, or one without Copilot access —
logs Copilot out of your own subscription. Prefer gh auth login on the worker
machine and leave both unset; if you must export one, also set
COPILOT_GITHUB_TOKEN to a token that carries Copilot access.

The operator's cheat sheet

Every gesture you ever need, in one table — the tentacles handle
everything else:

You want to... The gesture From the GitHub UI?
Queue a task Open an issue from the task template + project:<name> label ✅ web + mobile
Chain tasks gh -R OWNER/tasks issue edit <n> --add-blocked-by <m> ✅ web — Relationships sidebar
See the queues /kraken:status OWNER/tasks ✅ filter issues by label
Answer a decision Reply on the issue — the next worker to read the queue picks it up again ✅ web + mobile
Send work back after review Comment the feedback — the next worker to read the queue picks it up again ✅ web + mobile
Land the work Merge the draft PR — its Closes line closes the task and unblocks dependents ✅ web + mobile
Cancel a task Close the issue ✅ web + mobile
Add capacity Launch one more worker into one more prepared environment ❌ a worker is a terminal session

Everything except launching workers works from your phone — file tasks on the
commute, answer decisions from the couch, merge from anywhere.

Agent cheat sheet

The same worker on all three harnesses, side by side. $KRAKEN is your kraken
checkout; run every worker command from the work repo's checkout (or pass
--work-dir).

You want to... Claude Code GitHub Copilot CLI Pi
Install the plugin /plugin marketplace add rafael-adcp/kraken
/plugin install kraken@kraken
Same two commands, inside copilot "skills": ["$KRAKEN/skills"] (and optionally "extensions": ["$KRAKEN/extensions/pi/kraken.ts"]) in ~/.pi/agent/settings.json (details)
Stand up the queue (once) /kraken:init OWNER/tasks --project my_app Same, inside copilot /skill:init OWNER/tasks --project my_app, inside pi
Pre-allow unattended work Allowlist in the work repo's .claude/settings.json (walkthrough step 3) Nothing to write — both scripts pass --allow-all-tools --no-ask-user plus the deny rules Nothing to write — Pi never asks; the loop passes --exclude-tools and the extension's gh deny check: isolate the environment
Launch a worker you can talk to /kraken:unleash OWNER/tasks --worker-name env-1 --project my_app $KRAKEN/scripts/kraken-copilot.sh OWNER/tasks --worker-name env-1 --project my_app /skill:unleash OWNER/tasks --worker-name env-1 --project my_app, inside pi
Run with no session to watch $KRAKEN/scripts/kraken-loop.sh OWNER/tasks --worker-name env-1 --project my_app --harness claude $KRAKEN/scripts/kraken-loop.sh OWNER/tasks --worker-name env-1 --project my_app --harness copilot $KRAKEN/scripts/kraken-loop.sh OWNER/tasks --worker-name env-1 --project my_app --harness pi
Drain once and exit /kraken:unleash (every invocation drains once), or --once on the loop Append --once to either script Append --once to the loop
Idle on an empty queue The loop — polling outside the model, zero tokens Same (loop) Same (loop)
See the queues /kraken:status OWNER/tasks Same, inside copilot /skill:status OWNER/tasks, inside pi
Stop a worker Ctrl-C (loop); close the session (interactive) Same Ctrl-C (loop)
Resume after a usage limit Nothing (loop) — the claim is released, the next poll retries Same Nothing (loop)

Witness the Depths

A real task's timeline, end to end — claim, restated goal + assumptions, the PR
delivered, the result with the acceptance check executed, and the close:

Work happens while you don't. Queue a backlog before bed, on the commute, or before a meeting. Come back to finished branches instead of an empty editor.

100 tasks queued at 17:24 → 100 draft PRs + 0 needs-decision by 18:01 — zero terminals watched. (highest burst so far)
So far: 126 tasks filed, 125 PRs merged.
Median claim → draft PR: 13.9 min in normal operation (n=25, mean 16.9, max ~46 min) — 0.4 min in burst mode (n=100), where workers stage the work before claiming.

A kraken task issue timeline: claim comment, assumptions, draft PR link, result comment, and close

FAQ

Doesn't this already exist — Copilot, Claude cloud agents, CI?

Partly, and for a single GitHub repo with no local services those are simpler —
say so and use them. Kraken's edge is the prepared environment, GitLab/private
work repos, and a fan-out of named, audited workers. The honest side-by-side can
be found below.

Why not just use X?

Alternative Coordination primitive Infra required Agent-agnostic Prefer it when
GitHub Copilot coding agent Issue assignment (native GitHub) Hosted sandbox; nothing beyond GitHub Actions No Code is on GitHub and the task needs nothing the platform doesn't already give it
Claude Code cloud / scheduled agents A schedule waking a managed sandbox Zero — the sandbox is managed for you No You want scheduled runs with no environment to keep and the sandbox suits the work
claude-code-action in CI An event trigger (push / PR / label) Ephemeral, disposable CI runner No Automation is CI-shaped and a fresh runner is the correct environment
Kraken An issue queue drained via kraken-protocol/9 Your prepared, long-lived environment Yes The environment is the point — work must run where your services, data, and credentials already live, with named, audited workers

By 2026 the obvious reflex is "doesn't this already exist?" — assigning an issue
to an agent is native GitHub, Claude Code runs scheduled agents in the cloud, and
claude-code-action runs it in CI. It does exist, and for a single repo living
entirely on GitHub with nothing but the code to touch, those are simpler — reach
for them. Kraken earns its keep the moment the work needs your prepared
environment. Here is the honest cut against each.

GitHub Copilot coding agent. Assigning an issue to an agent is native GitHub,
and if the whole story lives there — one repo, no services, secrets already in
Actions — that native path is less to run than anything Kraken adds, and you
should take it. What it can't reach is a hosted sandbox's blind spot: the
worker doesn't have your local Postgres, your seeded fixtures, the private
package registry, or the toolchain pinned to a version the sandbox never ships.
Kraken's workers run in the environment you prepared, and its work repos can
live on GitLab or a private server while only the issue queue sits on GitHub —
so prefer Copilot when your code is on GitHub and the task needs nothing the
platform doesn't already give it.

Claude Code cloud / scheduled agents. A managed sandbox that wakes on a
schedule is genuinely zero-infra, and if the sandbox already has everything the
task touches, that convenience is the right trade — use it. Kraken's difference
is the shape of the run: instead of one agent in a sandbox you don't control,
you fan out N named workers, each in a distinct prepared environment, each
leaving an audit trail in its issue timeline — who claimed what, when, and how
it was validated. Prefer the cloud agents when you want scheduled runs with
no environment to keep and the sandbox is a fine place for the work to happen;
reach for Kraken when the work must run where your services, data, and
credentials already live, and you want to name and audit each worker.

claude-code-action in CI. Wiring the action into a pipeline is the right
answer when the trigger is genuinely event-driven — a push, a PR, a label — and
a fresh, ephemeral runner is exactly the clean context you want; nothing here
beats that, so wire it up. Kraken is for the other case: a queue you drain
unattended against long-lived services and a toolchain that would cost minutes
to rebuild on every runner. And because a tentacle speaks the agent-agnostic
kraken-protocol/9, the queue isn't wed to one vendor's action —
any tool that follows the protocol can drain it. Prefer claude-code-action
when
your automation is CI-shaped and a disposable runner is the correct
environment; prefer Kraken when the environment is the point and you want no
lock to a single runner or vendor.

A task landed in needs-decision — what do I do?

Just reply on the issue ("option B, go"). Nothing else: any comment added
since the worker put the task down is the requeue — the task recorded the
thread's comment count when it escalated, and every worker compares the two on the
read (PROTOCOL.md §3.1, §6), so the task rejoins the queue immediately and whoever
claims it inherits the full thread as context. The needs-decision label stays
until that worker claims it and swaps it off; nothing reads it in the meantime —
which is also why removing the label by hand is no longer the gesture. Comment.

A review asked for changes — how does the task go back?

Just reply on the issue — exactly the same gesture as answering a decision.
One new comment on the thread is the requeue, in both held states
(PROTOCOL.md §6), so the task rejoins the queue and the next claim continues on
the existing branch with the whole discussion in hand.

The flip side, on purpose: a comment that is not a work request ("nice, thanks")
also requeues — the queue counts comments, it does not read them. If the branch is
ready and you have nothing to ask for, the gesture is merge it, not comment on
it — and a worker that picks the task back up and
finds nothing actionable in your comment escalates instead of guessing, so the
worst case is a question on the thread rather than a silent bounce.

How do I find tasks no worker will ever start?

Run /kraken:status — it ends with a 🧹 Queue hygiene section listing
every open task missing a project:<name> label (so no worker will ever see it)
or an empty Goal/Acceptance (so a worker claims it and stalls). It costs
nothing extra: status already reads every open task's labels and body.

The issue form catches most of this at the source — Goal and Acceptance are
required fields, so a blank one never becomes an issue. What the form cannot do
is apply the project: label, which is exactly what hygiene is there for. A
project-less task is reported even under --project: it belongs to no project,
so a scope would bury the one failure you most need to see. It informs —
nothing is blocked, closed, or relabelled.

A worker died mid-task — is the queue stuck?

No, and you don't have to do anything. A claim is a lease: the worker holds
it by renewing its claim ref every few minutes while it works, and the ref's
commit date is the clock. Stop renewing — crash, kill, closed laptop, dead
container — and the lease expires 30 minutes later. The next worker to read
the queue sees an expired lease, takes the task over (leaving a note saying it
did), and carries on from the existing branch and thread. No cron, nothing
installed in the coordination repo, and the same behaviour in every harness.

The dead worker cannot come back and stomp on it either: every write it might
attempt — deliver, escalate, release, even its next renewal — first checks the
lease is still its own, and refuses if it is not.

You only hear about it if a task defeats three workers in a row: at that
point stealing it again would just burn a fourth, so it goes to
needs-decision for you to triage. (The same read also cleans up after itself:
a lock left on a task that already moved on is deleted.)

Want a different clock? KRAKEN_LEASE_TTL_SECONDS sets the TTL; the renewal
interval is always a third of it (kraken.py contract lease-renew).

A worker hit the Claude usage limit mid-task — what happens?

It self-heals. A usage limit kills the turn, not the session: the model stops
mid-drain and the session sits open waiting for input, so the SessionEnd
auto-release never fires. What does fire is Claude Code's StopFailure hook
(a turn ended by an API error), and Kraken registers it with matcher
rate_limit: the bundled hook releases the held claim on the spot — a
released marker with reason: usage limit and the claim ref deleted — so the task is
back on the queue in seconds instead of sitting on a lease nobody is renewing, free for any
worker on an account that still has quota. (gh still works at limit time;
only the model API is blocked, which is why the release can land.)

Retry is automatic under the loop (scripts/kraken-loop.sh --harness claude):
the rate-limited claude -p exits, the loop releases whatever the hook has not
already, and its next poll is the retry — while the limit lasts each pass fails
at the first request, and the first one after the window resets re-claims the
task and continues on the existing branch with the whole thread in hand. No
operator gesture needed. An interactive session does not retry on its own: after
the reset, run /kraken:unleash again.

The lease is the backstop for the residue — the hook itself failing, or a hard
kill: 30 minutes without a renewal and the task is free for the next worker
regardless, no hook involved. (Removing the in-progress label by hand does
nothing here — it's a badge for you, not the lock. The lock is the claim ref,
and only the lease's clock or an explicit release opens it.)

On GitHub Copilot CLI the retry is the same and the release comes from the
loop alone. Kraken has no verified Copilot event for a usage limit, so there is
no StopFailure equivalent: under scripts/kraken-loop.sh --harness copilot the copilot -p
process exits, the loop releases the claim at once and its next poll is the
retry. In an interactive session (scripts/kraken-copilot.sh) the lease frees
the task within its TTL; after the limit resets, run /kraken:unleash again. A
Pi worker under kraken-loop.sh --harness pi behaves like the Copilot loop.

Who can command my workers?

Anyone who can open issues in the coordination repo: a task is, in effect,
instructions that will execute in your worker's environment with your
credentials. Keep the repo private, keep write access yours, and remember that
task bodies are untrusted input to an agent that can push branches.

Does anything survive closing the terminal?

The queue does — it's GitHub Issues. The worker doesn't: /kraken:unleash lives
inside a Claude Code (or Copilot CLI) session, and scripts/kraken-loop.sh inside
the terminal that runs it. But a graceful exit now
self-heals: a bundled SessionEnd hook fires when you close the terminal or
/exit, and if the worker was still holding a claim it runs kraken.py release
for you — released: <worker> / reason: session ended, then drops in-progress,
so the task is back on the queue in seconds instead of at the end of its lease.
The hook hands the release to a detached process and returns at once: Claude Code
gives SessionEnd hooks 1.5 seconds in total and a release against GitHub takes
a few, so a release run inside the hook was cut off half-done.
It frees only the claims that session made (the claim records its Claude Code
or Copilot CLI session), so closing some other claude or copilot window on the same machine never takes
a live task from a worker. That covers a graceful end only; a usage-limit pause never fires SessionEnd
either, but its own StopFailure hook releases the claim there (see the limit
FAQ above). A hard kill / crash / power loss fires neither hook — and it does not
matter: the lease expires on its own 30 minutes later and the next worker to
read the queue takes the task over (§5). The hooks only make that immediate; no
server-side job, so it needs nothing running anywhere. The ambush loop moves
the poll out of the model, but it still needs a live shell to run in; a
genuinely headless driver (system cron, GitHub Actions) is open work —
#57.

Are the Copilot CLI and Pi workers as self-healing as the Claude Code one?

Yes — and under kraken-protocol/9 that includes recovery latency, which is
the part that used to differ. A claim is a lease that expires 30 minutes
after its last renewal, and expiry is applied by whoever reads the queue. So a
worker that dies in any harness, in any way, frees its task within one TTL with
nothing installed. The bundled hooks are an optimization on top (seconds instead
of minutes), not the mechanism — and Copilot CLI runs the SessionEnd one too:
it loads the plugin's hooks.json, and kraken.py claim records the Copilot
session (COPILOT_AGENT_SESSION_ID) so a Copilot session that ends gracefully
while holding a lease hands it back on the spot, exactly like a Claude Code one.

scripts/kraken-loop.sh carries the same optimization
for every harness it drives: kraken.py claim records the open claim in
~/.kraken/claim-<worker>.json and every terminal transition
(deliver/escalate/release) removes it, so when the agent process exits with
that file still present the drain provably died holding the lease — crash, kill,
or a rate-limit abort — and the loop runs kraken.py release on the spot. Its
exit/Ctrl-C trap does the same when the loop itself is stopped mid-drain. The
release is scoped to the loop's own --worker-name, and release refuses (exit
10) on a lease that is no longer this worker's, so a co-located or successor
worker's live lease is never touched.

A bare copilot -p … invocation outside the loop is therefore no longer a
correctness gap, just a slower one: its task comes back in minutes rather than
seconds — exactly like a hard kill / power loss on any harness. Run it
through the loop anyway: it also skips the model entirely on an idle queue.

Pi loads no hooks.json; its equivalent of the SessionEnd hook is the
optional extension at extensions/pi/kraken.ts
(Pi's session_shutdown event), which releases only the ending session's claim,
recorded by Pi's PI_SESSION_ID. Pi exposes no clean usage-limit signal, so
there is no StopFailure equivalent: the loop's release on exit covers that
case. Side by side:

Claude Code GitHub Copilot CLI Pi
Lease expiry (the floor) ✅ ✅ ✅
Release on a graceful session end SessionEnd hook SessionEnd hook session_shutdown in the extension
Release when a loop drain dies kraken-loop.sh --harness claude kraken-loop.sh --harness copilot kraken-loop.sh --harness pi
Release + retry on a usage limit StopFailure hook + loop releases, next poll retries loop releases, next poll retries loop releases, next poll retries
Merge / delete / close refused at the tool layer loop: --disallowedTools rules; interactive: allowlist ask-gate --deny-tool rules MCP writes: --exclude-tools; gh in bash: the extension's deny check
How do I update the plugin?

/plugin marketplace update kraken. The plugin is pinned to the version in its
manifest — pushes to main reach nobody until a release bumps it, so what you
run is always a deliberate release. On Pi, check out the new release tag in the
kraken checkout your skills entry points at.

What does uninstalling leave behind?

Nothing that runs. /plugin uninstall kraken@kraken removes the skills — the
only thing Kraken ever installs on a machine (on Pi: drop the skills and extensions
entries and the checkout). The queue is just a private repo
of issues you own, with nothing running inside it: keep it,
archive it, or delete it. No service to stop, no daemon to kill, no state
anywhere else.

Contributing

Kraken is a small, protocol-first tool. The spec (PROTOCOL.md)
wins on any disagreement, the skills are prompts, and kraken.py is the
mechanics. Dev setup, PR conventions, the release flow, and where design
discussion happens are all in CONTRIBUTING.md; what changed
between versions lives in the GitHub
Releases
.

Origins

Distilled from orch-ai-orchestrator:
same architecture — queue, worker pool, verdicts, heartbeats, audit trail — but zero
infrastructure to operate. GitHub is the queue, the dashboard, and the log.

Reviews (0)

No results found