coop — run a coding agent all night long in a box it can't escape.
Usage: coop <command> [<args>...]

AGENTS
  coop <target>                     agent target in a box
  coop <preset>                     run a preset interactively (its lead leads)
  coop acp <target|preset>          serve as an editor agent (ACP; e.g. Zed)
  coop <target> --peer <target>...  a read-only second opinion, named peers

CREDENTIALS, MODELS & PRESETS
  coop login <agent>                sign in an agent (a subscription)
  coop credentials [<agent>]        stored credentials + which are signed in
  coop models [<agent>]             the model menu per agent
  coop presets [<preset>]           orchestration recipes (lead + roles)

THE BOX
  coop run -- <cmd...>              run a raw command in the box
  coop shell                        an interactive shell in the box

FORKS — review and land work like a PR
  coop fork <name>                  open/re-enter; run a target or preset
  coop fork ls                      list this repo's forks
  coop fork review <name>           show a fork's review dossier + diff
  coop fork merge <name>            rebase the fork onto your branch and land it
  coop fork merge --all             rebase and land every fork
  coop fork logs [<name>]           tail a fork's loop log (no name: every fork)
  coop fork rm <name>               discard a fork
  coop fork stop <name>             stop a detached loop
  coop fork open <name>             open the fork in your editor
  coop fork path <name>             print the fork's filesystem path

UNATTENDED
  coop loop [<target|preset>]       work the queue(s) until done, then sign off

TASKS — a folder-per-task queue in .agent/tasks/
  coop tasks ls                     show the queue, grouped by state
  coop tasks watch [--json]         canonical tasks + every sandbox, live
  coop tasks add "<title>"          add a task (then claim/block/unblock/done)
  coop tasks decisions              what's blocked on a decision (-i to answer)
  coop tasks flags                  tasks that changed what runs on your machine
  coop context                      compile the docs relevant to touched paths
  coop backlog                      park unscheduled ideas; promote when ready

SESSIONS — LOCAL REMOTE-SESSION CONTROLLER
  coop sessions serve               run the session controller on Unix
  coop sessions doctor              check the session controller Unix socket
  coop sessions policies            print trusted policy digests for workers
  coop sessions compact             back up and compact turn retry receipts
  coop worker connect               outbound worker: join a fleet controller

SERVICES — the box's .agent/compose.yml sidecars
  coop up                           start the .agent/compose.yml services
  coop down                         stop the .agent/compose.yml services

SAFETY — prove the box holds, catch committed secrets
  coop doctor                       attack the box, prove isolation holds
  coop check-secrets                scan the working tree for committed secrets

SETUP & MAINTENANCE
  coop init [--stack asdf]          scaffold queue, hooks, skills, agent dirs
  coop build                        build the box image (stable, pinned)
  coop update                       self-update coop, then rebuild the box
  coop completion <shell>           shell tab-completion (bash, zsh)
  coop sign                         re-sign unpushed commits with your host key
  coop prompt                       a one-line status for a shell prompt / tmux
  coop help                         this help
  coop version                      print the version

Run 'coop help <command>' or 'coop <command> --help' for a command's details —
for an agent (claude/codex/gemini/grok), --help is the agent's own.

Config  coop.conf (COOP_CONF), or COOP_* env vars
Auth    the config dir (COOP_CONFIG_DIR)
Docs    https://coop.dryga.com

==============================================================================

coop fork — a throwaway clone handed to an agent; review and land it like a PR.

  Usage: coop fork <name> [<target|preset>] | ls | review | merge | logs | rm | stop | open | path

  coop fork <name> <target>         open or re-enter a fork with an agent target
  coop fork <name> <preset>         open or re-enter a fork with an orchestration preset
  coop fork ls [--json]             list fork workers, sandboxes, task progress, and problems
  coop fork logs [<name>]           tail a fork's loop log (no name: all forks)
  coop fork review <name>           dossier + diff (--stat, --tool, --open, --gate)
  coop fork <name> acp <target>     front the fork as an ACP agent (for editors)
  coop fork merge <name>            rebase onto your branch and land one fork
  coop fork merge --all             rebase and land every fork
  coop fork rm <name>               discard a fork (confirms; --force may stop it and return/discard task authority)
  coop fork open <name>             open the fork in your editor
  coop fork path <name>             print the fork's filesystem path
  coop fork stop <name>             stop a detached loop

FLAGS (every short flag has a long form):
  -c, --continue            resume the prior session (the default on re-entry)
      --new                 start a fresh agent session on re-entry
      --fresh               recreate the fork (confirms; --force may stop it and discard Git/task work)
      --loop                work the fork's task queue until done instead of opening an interactive session
  -d, --detach              with --loop, run it in the background
  -t, --tasks               with --loop, select one canonical task queue (default: every project queue)
      --peer <target>       with --loop, a peer iterations may consult read-only (repeatable)
  -f, --force (merge)       bypass the risky-file policy; the rebase gate still must pass
  -f, --force (rm/fresh)    stop the worker; discard Git work, assignments, candidates, and pending proposals
  -y, --yes                 merge/rm/--fresh: skip the delete confirm (required without a TTY)
  -f, --follow              logs: keep streaming new output

REVIEW  --open opens $COOP_EDITOR (else your global git core.editor); --tool uses your global git diff.tool.
        --gate rebases in an isolated scratch clone and runs the parent's gate; source mutations fail review.
NAMES   new fork actions are verb-first (coop fork <verb> <name>); a fork can't be named a reserved verb.
TASKS   fork loops share the project's canonical queue; the host assigns one task at a time.
        Stop keeps its assignment; merge alone completes that canonical task. Copied queues are retired.

Run 'coop help' for all commands.

coop run — run a raw command in the box.

  Usage: coop run -- <cmd...>

  Everything after -- runs verbatim in the sandbox — same mounts, secret-shadowing,
  and network as an agent. "coop run echo hi" works too; use -- when the command has
  flags coop would otherwise read (e.g. coop run -- npm test --watch).

coop shell — open an interactive shell in the box.

  Usage: coop shell

  A shell in the sandbox at your repo — same mounts, secret-shadowing, and
  network as an agent run. Exit to return.

coop login <agent> — sign in to an agent (token persists in the config dir).

  Usage: coop login <agent>[@<account>]

  Runs the agent's sign-in (paste a code, no browser). Re-run any time to
  refresh or switch accounts — e.g. after a usage limit.

  @account signs in a second (or third) account under a name, so one agent can
  hold several subscriptions: coop login claude@work. An unattended loop rotates
  across all of them when one is rate limited (a bare model in a preset's lead agent:
  ladder fans out over every account). Without @account the sign-in targets the default.

coop credentials — list stored credentials; a path grammar edits one.

  Usage: coop credentials [<agent> [<credential>]]
         coop credentials <agent> <credential> default
         coop credentials <agent> <credential> rm

  A CREDENTIAL is one stored account/login — a rate-limit slot. Orchestration
  recipes are PRESETS; see coop help presets.
  Each token narrows: no args lists every agent, an agent lists its credentials
  (signed in? default? how long since its token last rotated?), a credential shows
  its detail, and a trailing attribute reads or writes one property of it. A credential is one subscription; add more
  with 'coop login <agent>@<name>', then an unattended loop rotates across them on
  a rate limit (a bare model in a preset's lead agent: ladder). The model is a separate
  axis — set it inline (claude:opus) or in a preset, never on a credential.

  default                mark this credential as what a plain 'coop <agent>' runs,
                         and the account a loop's rotation starts on. A mark you
                         set — the listing shows it first, tagged (default).
  rm                     delete the credential (its login token and session
                         history). Set a different default first if you're
                         removing the marked one.

  Run on a specific account without changing the default — put it in the target on
  any agent launch: 'coop claude@work', 'coop claude@work --peer codex', and
  'coop acp claude@work' (so an editor entry can pin an account).

coop presets — YAML orchestration recipes under .agent/presets/<name>/.

  Usage: coop presets [<preset>]        list them, or show one recipe in full
         coop presets init [<preset>]   scaffold the frontier template (default name: frontier)

  A PRESET is a runtime recipe: which agent leads, and which roles it can route
  work to — each role an agent: target or fallback list + routing hints. The lead's
  agent: is a target, or a fallback ladder (even CROSS-PROVIDER, [claude:fable, codex:gpt-5.6-sol]):
  a bare provider:model runs on EVERY signed-in account (rotating on a rate limit),
  provider:model@account pins one. On a loop it rotates the ladder top-to-bottom, running each
  rung's own agent; a single run uses the first entry (also the default agent). Accounts are your
  local logins (see coop credentials) — presets name models, not secrets.
  Cross-provider rungs rotate on the loop AND on ACP sessions (ACP re-creates the session on
  the new provider and carries the thread best-effort as text). A non-rotating terminal run
  pins the first rung.

  Run one by NAMING it in the who-runs slot:
    coop <name>
    coop loop <name>
    coop acp <name>
    coop fork <fork> <name> --loop
  A target (claude:opus@work) in that same slot runs the agent directly instead.

  .agent/presets/frontier/preset.yaml:

    lead:
      # a target, or a ladder (cross-provider ok) — frontier models at xhigh effort
      agent: [claude:claude-fable-5/xhigh, codex:gpt-5.6-sol/xhigh]
      prompt: roles/lead.md                 # Optional Markdown, appended to the generated contract.
    roles:                                  # consult/delegate agent: may be a fallback list; default accounts
      thinker:                              # native Claude subagent — deep thinking in-session
        mode: native
        agent: claude:claude-opus-4-8/xhigh # model + effort ride agent: (generates coop-thinker)
        when: [architecture, debugging, code-review]
        prompt: roles/thinker.md            # its system prompt (or set subagent: <name> to reuse one)
      critic:                               # read-only peer via coop-consult
        mode: consult
        agent: [codex:gpt-5.6-sol/xhigh, grok:grok-4.5/high]
        when: [plan-review, security]
      fast:                                 # write-capable delegate via coop-delegate
        mode: delegate
        agent: [gemini:gemini-3.5-flash, codex:gpt-5.4-mini]
        when: [boilerplate, bulk-edits, test-scaffolding]
        commit: never                       # the delegate edits; the LEAD reviews, gates, commits
        concurrent: never                   # delegate runs are serialized

  coop generates the lead's routing contract from this — each role, when to use it, and
  its ROLE-ADDRESSED invocation (@coop-<role>, coop-consult <role>, coop-delegate <role>)
  — and mounts the wrappers. Markdown prompt files (roles/lead.md, roles/<name>.md)
  append to the generated text, never replace it. Required routing files, wrappers, and role
  prompts assemble as one contract: any failure stops before the provider starts instead of
  silently dropping a role. A native role generates a coop-<role>
  Claude subagent in the box from itself (its model + when + prompt) — never written to
  your repo; set subagent: <name> to reference an existing .claude/agents/ subagent
  instead. Consult/delegate ladders advance once per target only after a failed command
  proves a rate limit; ordinary failures stay visible. A delegate advances only from a
  clean worktree when the limited rung left every file and Git history unchanged. A consult
  remembers its successful rung and transcript for --continue. It publishes a native session id
  only after a usable reply. A failed resume returns once, clears that uncertain id, and preserves
  the transcript so the next --continue starts the same rung fresh. Provider stderr is diagnostic,
  not reply text. Reply/diagnostic streams are capped at 1 MiB each; input, constructed prompts,
  and saved transcripts are capped at 512 KiB each. Providers without mounted
  credentials are skipped; every available rung's credential home is mounted in the lead box.
  The role's prompt (if any) is its persona.
  Native roles run inside the lead's session, so
  under a codex/gemini/grok lead they degrade to exactly such a consult (same model + persona),
  coop-consult <role> instead of @coop-<role>.
  A delegate may edit the worktree but must not commit. coop-delegate verifies HEAD, refs,
  reflogs, and the worktree before fallback; bounds prompt, output, and runtime; and fails
  closed while preserving mutation evidence. The lead owns the diff review, gate, and commit.
  Write-capable
  delegation is one level deep: a nested coop-delegate fails before lock/provider launch;
  configured read-only coop-consult remains available. Model ids: coop models.
  Scaffold one: coop presets init.

  WHERE presets live — two locations, repo wins: a preset resolves first from the
  repo's .agent/presets/<name>/, then from a per-user global dir ~/.config/coop/presets/
  (COOP_PRESETS_DIR overrides it), so a recipe like frontier applies across every repo
  without symlinking. A repo preset shadows a same-named global one (repo wins wholesale,
  no merging); coop presets tags a global-sourced one (global). init scaffolds into the
  repo — author a global preset by hand (or copy one there).

coop models [<agent>] — the model menu per agent.

  Usage: coop models [<claude|codex|gemini|grok>] [--refresh]

  A block per agent: its models and when that list was last refreshed. A fresh per-agent
  cache shows the agent's real list; a never- (or stale-) refreshed list is the curated
  static examples — model ids churn, so ANY id the agent's CLI accepts works either way
  (coop never validates a model id). A model is an axis of its own — set it inline in the
  target (claude:opus) or in a preset's lead agent: ladder, never on a credential.

  Plain 'coop models' is instant and never needs the container runtime — it only reads
  the cache. '--refresh' runs grok/codex's native catalog CLI on the host ('grok models',
  'codex debug models') and asks claude/gemini's ACP adapter in a short-lived credential-
  scoped box. Normal 'coop acp' sessions also refresh claude/gemini opportunistically.
  Refresh is best-effort: an unavailable CLI/runtime, timeout, or parse error falls back
  to the last cache or the static list, noted on that block — it never errors or hangs.

  Pick per run inline in the target on any launch: 'coop claude:fable',
  'coop claude:opus --peer codex', 'coop loop claude:haiku',
  'coop fork risky claude:opus --loop', 'coop acp claude:sonnet'.

  Precedence: the target's :model > the active rotation entry's model (a loop stepping
  through a preset's lead agent: ladder, or loop.yaml work.agent) > COOP_<AGENT>_MODEL
  (agent-wide) > a model baked into COOP_<AGENT>_CMD > the agent CLI's own default.
  An account rides the SAME target (claude:opus@work). coop never validates a model
  id — a bad one fails in the agent's own error.

  Reasoning effort is a sibling axis, set with /effort in the same target
  (claude:opus/xhigh, codex/high): low, medium, high, xhigh, or max — coop passes the
  level through and the agent's CLI validates it (claude, codex, and grok have it; gemini
  has none, so a /effort on gemini errors). One axis carries both — a target and
  COOP_<AGENT>_MODEL take model[/effort] (e.g. opus/high), so there is no separate effort
  var. Precedence mirrors the model: the target's /effort > a rotation rung's effort > those.

coop acp <target|preset> — serve as an ACP agent over stdio (for editors).

  Usage: coop acp <target|preset> [--peer <target>...]

  Speaks the Agent Client Protocol on stdin/stdout. Point your editor's ACP
  command at e.g. ["acp","claude"] — one entry per target or preset.
  The initial target is required; credential order is never launch intent. The
  PROVIDER dropdown can still switch a plain session live. coop always proxies
  the session, so the editor stays connected across a box restart (a rebuild/OOM)
  — it reconnects and replays the handshake, no lost session.

  coop owns the editor's toolbar: it runs the agent in yolo mode (the box is the
  sandbox, so no permission prompts), defaults the model dropdown to coop's model,
  drops the permission-mode and subagent pickers, and gives a normal session three
  dropdowns — PRESET (the recipe), PROVIDER (who runs), and ACCOUNT (the lead's login).
  An active preset is different: only PRESET renders because its ladder owns the provider,
  model, effort, account, and roles. Persisted Provider/Account sets while those controls
  are hidden are acknowledged and ignored. Selecting None returns the normal Provider and
  Account controls with the effective provider and Account set to Auto; select None first
  whenever you want to choose a Provider or Account manually.
  In a plain session, an account or same-provider switch is transparent — the conversation is
  preserved (a shared, credential-independent session store). A PROVIDER switch (picking another
  signed-in agent, or a cross-provider preset rung rotating in on a rate limit)
  re-creates the session on the new agent and carries the conversation BEST-EFFORT:
  coop prepends the thread so far — message text plus one-line tool narration, no tool
  payloads, labeled approximate — budgeted by COOP_ACP_CARRY_TOKENS (default 200000;
  trim it below the smallest window you switch into). A switch made MID-TURN re-sends
  the in-flight prompt once the new box is up, so the turn completes on the new target
  instead of erroring. On a rate limit it auto-rotates across the ladder's rungs —
  accounts, models, providers.

  The target pins the session's agent, model, and account — an editor can run two
  entries on different ones, e.g. ["acp","claude:opus@work"].

  A bare preset name in the who-runs slot runs the session under that orchestration
  preset (its lead is the agent; see 'coop help presets').

  --peer <target>... lets the session ask NAMED peers for a read-only second opinion
  (repeatable; only those peers' credentials are mounted) — the orchestrator pattern,
  from your editor. Preset consult roles use the same read-only wrapper and keep their
  role names, target ladders, and personas.

  To make plain provider switching near-instant, coop keeps a box warm per OTHER signed-in
  provider (spawned in the background at session start), so a switch pays only the ACP
  replay, not a container + adapter cold-boot. Set COOP_ACP_WARM=0 to disable
  prewarming (one fewer idle box per signed-in provider) on a low-RAM machine.

  Picking up a rebuilt coop WITHOUT restarting your editor: send the running server
  SIGHUP — 'pkill -HUP -f "coop acp"'. It re-execs the freshly-installed binary in
  place (same process, same stdio), tears down its box, and re-establishes your open
  threads against a fresh box on the new binary — the editor never sees a disconnect.
  (SIGTERM/SIGINT still STOP it; only SIGHUP reloads. A box restart already picks up
  box-side changes, so SIGHUP is for supervisor-side changes to coop itself.)

  Debugging a misbehaving session: set COOP_ACP_TRACE=1 in the editor's server env, or
  create ~/.config/coop/acp-debug, and coop appends the editor<->box ACP wire to
  ~/.config/coop/acp-trace-<pid>.log (the sentinel works on an already-running server).
  The log is size-capped and auto-rotated so it can't grow unbounded; it holds prompts
  and file contents, so treat it as sensitive.

coop tasks — drive the task queue (a folder per task under .agent/tasks/).

  Usage: coop tasks [--tasks <path>]... <command>

  ls [--all] [--todo|--in-progress|--blocked|--done]
                   list tasks by state, with counts (recent done capped; --all shows all). Pass one
                   or more state flags to show only those. Task ids link to the folder — click to open.
  watch [--json]   live board: canonical tasks + every active sandbox (auto-exits when done);
                   --json prints the same project snapshot once for automation; a queue
                   that cannot be read exits 1 instead of counting as drained
  add [--project <name>] "<title>"
                   scaffold a task folder in todo (or fill it inline: --context/--acceptance/--approach/--subtask)
  claim <id> [--as <label>] [--pid <n>] [--force]
                   claim a task before you start it (todo -> in_progress); an agent's claim binds to
                   its process (--as names it, --pid picks it, --force takes over a live claim)
  release <id>     hand back a claim without finishing it (stays in_progress; the loop can adopt it)
  lease <id> [--as <label>] [--pid <n>] [-- <command...>]
                   hold the task's work lock — the one a loop iteration holds — for a command's
                   lifetime, or until the bound process (your agent, else the task's claimant,
                   or --pid) exits or the task moves; ls/watch show 'busy <label>' and a loop
                   in this checkout skips the task meanwhile ('done' and 'block' run by the
                   same agent stop its own holder first)
  block <id>       park it on a decision (-> blocked) and write a decision.md stub
  unblock <id>     move it back to todo; add "<answer>" to record in decision.md
  done <id>        move it to done (the archive)
  path <id>        print a task's resolved folder path
  rm <id>          delete a task folder; --all-done clears the done archive
  decisions [-i]   list open decisions; -i walks them one by one to answer (records + unblocks)
  flags [<id>] [--ack]
                   tasks whose commits changed files that run on YOUR machine — git hooks and
                   attributes, editor/agent settings and hooks, compose files, the Makefile — with
                   the reason each one matters; the board marks such a task until you read the
                   diff and acknowledge it with --ack (the sandbox contains the box, not your tools)
  lint             check the tree (blocked<->decision.md, no status field, ...; exits 1)
  queues           print each configured queue's path, one per line (for scripts and the sweep guard)

  A claim made without a terminal (an agent's tool call) is bound to the claiming process — coop's
  parent, or the nearest non-shell ancestor — so 'claimed by codex (pid 812)' says who holds the
  task, 'owner process gone' says that process died, a second claim by another live process is
  refused, and 'coop loop --preflight' releases claims whose process is gone. A claim made at a
  terminal is a person's: bound to nothing, and never released by the loop. 'coop tasks lease'
  adds the live half for an agent working outside the loop: the same kernel lock a loop iteration
  holds, dying with the holder, so 'busy codex' can never outlive the process it names.

  A task's state is its directory — 00_todo/ 10_in_progress/ 50_blocked/ 99_done/, the
  numeric prefix just sorts 'ls' in lifecycle order — so each transition is a folder move.
  Removing tasks is a MANUAL step: the loop and skills only ever move a finished task to
  done, never delete it, so 'coop tasks rm --all-done' is how you prune the archive.
  Defaults to .agent/tasks/ — or, in a monorepo, every subproject's .agent/tasks listed under
  'subprojects:' in .agent/project.yaml, so you never hand-maintain COOP_TASKS. Override with
  --tasks or COOP_TASKS. Paths are repo-relative. With several queues (a monorepo), ls, lint, and decisions
  (including -i) roll up across all of them, and the id commands (claim/block/unblock/done/
  rm) find their task in whichever queue holds it — erroring only when an id matches in
  more than one queue. In a monorepo, add requires
  --project root|<subproject> so creation picks an explicit queue; a nested member takes its
  full path (terraform/environments/va1) or just its last segment (va1) when no other member
  ends in the same one. With raw --tasks overrides, add still needs a single --tasks because it
  creates into one queue. Copied queue slicing is retired: parallel fork loops schedule distinct
  tasks directly from the same canonical queue.

coop context — compile the committed docs relevant to a scope (instructions + rules + KB).

  Usage: coop context [--changed] [--task <id> [--tasks <path>...]] [--json | --rendered] [<path>...]

  Selects which committed docs an agent needs for the paths in play — canonical
  AGENTS.md/CLAUDE.md (always, whole) plus the .agent/project.yaml 'context.routes'
  whose globs match — so a session carries less than the whole repo's instructions.

  Scope is DETERMINISTIC (never inferred from a prompt), from any of:
    paths...         explicit repo-relative paths
    --changed        paths git reports changed (staged, unstaged, untracked)
    --task <id>      the paths a queued task declares (a 'paths:' frontmatter list)
    (current subproject, when run inside one)

  Task IDs use the same exact-then-unique-fragment matching as coop tasks. Ambiguous
  matches are errors; --tasks <path> selects a queue for --task (repeatable). Explicit
  queues override COOP_TASKS, which otherwise overrides project-derived queues.

  Output: a report of each file + the route that selected it; --json for the same
  as data; --rendered to print the compiled content itself (canonical first, whole).
  A route include that is missing or escapes the repo is an error; canonical files
  are discovered, never truncated. Config comes from the committed project.yaml (so a
  fork inherits the parent's routes); scope comes from the fork's own tree.

  Define routes in .agent/project.yaml:
    context:
      routes:
        - paths: ["portal/**", "**/*.ex"]
          include: [.agent/kb/portal.md]

coop backlog — park the genuinely LARGE as task folders (.agent/tasks/xx_backlog/).

  Usage: coop backlog [--tasks <path>]... [ls | add "<title>" | rm <id> | promote <id>]

  (bare)           list the backlog drawer
  add "<title>"    capture an idea (--context/--acceptance/--approach/--subtask fill it inline)
  promote <id>     move it into 00_todo/ when it's ready to work (then coop tasks claim)
  rm <id>          drop an idea (--yes skips the confirm)

  The backlog is the SAME task-folder format as the queue, in an xx_backlog/ drawer that lives
  OUTSIDE the lifecycle — the loop, the Stop hook, and 'coop tasks' all ignore it, so an idea
  sits here with no nagging until you promote it (a folder move, not a rewrite). This drawer is
  for work one iteration couldn't finish, or that needs a spec or a decision before anyone can
  start; everything else goes straight to the queue ('coop tasks add'), and a close call belongs
  in the queue too. Defaults to .agent/tasks/ — or, in a monorepo, every subproject's queue (see
  coop tasks): ls rolls up across them and rm/promote find the item in whichever queue holds it,
  while add needs a single --tasks.

coop loop [<target|preset>] — work the task queue until done, then sign off.

  Usage: coop loop [<target|preset>] [--tasks <path>]... [--peer <target>...] [--max-tasks <n>] [--preflight] [--no-mcp] [--debug-on-fail]

  A fresh agent per iteration works the todo tasks; when the queue empties, a DEMANDING
  signoff pass (a senior reviewer's bar) re-checks each shipped task — goal met (every
  acceptance criterion + subtask), standards followed (AGENTS.md + .agent/kb/rules, no scope
  creep), the FAILURE path tested, the change polished (docs/CHANGELOG updated), plus
  bookkeeping — then runs the repo's gate ONCE across the whole repo, reopening anything
  short of "merge with no changes". If the signoff reopened work, the loop drains and
  signs off AGAIN, repeating until a signoff reopens nothing (verified done) or the round
  cap is hit — then the task it keeps reopening is blocked for a human (exit 3), not
  reported as done. A later review or verify pass that leaves work actionable exits 1 so
  automation cannot mistake it for verified done. The cap SCALES with the batch: half the tasks worked this run, clamped
  to [3, loop.yaml signoff.rounds] (default 5) — a small batch still gets a few tries, a big
  overnight batch can't ping-pong one stuck task forever. On a rate limit it rotates to the
  next target in its agent: ladder, or waits out the reset when all are limited.

  Every review closes with one AUDIT EVIDENCE line per subject and a structured PASS/FAIL receipt
  naming the exact sorted task IDs it proposes reopening. By default the whole repository,
  including task queues, is read-only. Coop validates the complete proposal, acquires host task
  authority, and applies exact-subject reopens. A successful process with malformed structured
  output gets one immediate full-review retry over the same subjects with a fixed format
  correction; both attempts are recorded. A malformed second verdict, lifecycle churn,
  interruption, failed process, or out-of-scope proposal mutates no task. writes: repo permits
  source fixes, but task lifecycle is still host-applied.

  Completion requires exactly one Coop-Task binding in the current iteration range and exactly
  one binding for that task reachable from HEAD. Reopened work must amend or rewrite the original
  task commit; a second bound commit is rejected and the task is restored to in-progress.

  One committed .agent/loop.yaml configures every step (preflight/work/between/signoff/verify),
  each with its own agent: model ladder and prompt — between is the per-task reviewer, signoff the
  final review, verify an optional post-signoff pass that e2e-tests the affected features. Prompts
  never REPLACE a coop built-in: signoff.prompt APPENDS to its senior review; between.prompt,
  verify.prompt, and preflight.prompt SET their pass. Ordinary between review is opt-in and has no
  built-in prompt, but a completed task that edits a gate-defining file always gets an immediate
  protected audit; it uses between.agent/prompt when configured, otherwise the signoff target and
  a focused built-in prompt. Preflight's built-in tidy is coop itself, run host-side, so its prompt
  is the optional agent cleanup on top. The review
  passes are handed the run's CHANGE CONTEXT — every task completed this loop, by its Coop-Task
  trailer, with the files it touched — so "e2e the affected features" resolves against a concrete
  list; place it inline with {loop.changes} / {loop.tasks} / {loop.affected}. signoff.rounds is the
  round cap, preflight.enabled the pre-loop cleanup, work.command a raw per-iteration override, and
  mcp: false runs every stage's box without the shared MCP config — the servers' tool schemas ride
  at the front of every model request, so a drain that never uses those tools shouldn't pay for
  them each iteration (leave it on if a verify: pass depends on MCP tooling). A missing file or
  field = the built-in default. (coop init scaffolds a commented loop.yaml.) The launch announces
  the exact loop.yaml snapshot the run derives from — a short sha256 digest, or an explicit
  absent/built-in-defaults state — and pins it for the whole run; ladders, prompts, caps, and
  writes are one coherent read. A mid-run edit never hot-reloads: before each later box launch
  coop compares the on-disk bytes with that snapshot and warns once per new digest that the run
  keeps its startup config — restart the loop to apply the change.

  Each step's agent: is a ladder of TARGET (provider[:model][/effort][@account]) or PRESET-NAME
  rungs: signoff.agent runs the final review on its own, typically STRONGER model (the cheap
  work loop does the work, a capable model signs it off), between.agent the per-task audit, and
  work.agent the work rotation when the launch names no target and no preset.

  A preset in the who-runs slot runs the loop under that orchestration preset: its lead is
  the agent, its lead agent: ladder is the rotation, and each iteration gets the preset's role
  routing + wrappers ('coop help presets'). With no preset, the loop rotates the agent's
  default model across all signed-in accounts.

  The target is a one-off ladder for this run (no preset needed): a bare provider
  (claude) fans the agent's default model across all signed-in accounts, claude:opus
  pins the model, claude@work,personal is an explicit account ladder — the loop rotates
  the rungs on a rate limit. A rung without a model uses COOP_<AGENT>_MODEL, then a model
  baked into COOP_<AGENT>_CMD, then the agent CLI's own default — so overnight runs can
  grind on a cheaper model.

  --peer <target>... lets each iteration ask NAMED peers for a read-only second opinion
  (repeatable; coop-consult on PATH, only those peers' credentials mounted) — the
  orchestrator pattern running unattended. Off by default: it widens each box's
  credential scope to exactly the named peers. Also on fork loops:
  coop fork <name> <target> --loop --peer codex --peer gemini.

  On macOS, coop holds a caffeinate assertion for the run so the machine doesn't
  idle-sleep mid-drain and stall an overnight loop (COOP_CAFFEINATE=0 to disable).
  Set COOP_SPINNER=0 to freeze live spinners and suppress the fast repaint ticker while
  debugging or recording the terminal.

  Every attempt is supervised for SILENCE, not slowness: 10m to its first model action,
  30m between recognized actions, 2h on any one foreground tool. Only the provider's own
  structured stream feeds those clocks — never CPU or process names — and an open tool
  suspends the idle one, so long reasoning and a slow gate finish untouched (a provider
  whose stream reports no tool calls gets a single conservative 2h post-progress budget
  instead). Silence past a deadline kills that attempt alone: any completion it wrote is
  restored, the task stays actionable, and a fresh attempt starts — on the next rung when
  the ladder has one — while three in a row on one stage stops the run instead of
  churning. The warning names the deadline that fired and the silence it observed. There
  is no off switch: it is what stops one wedged provider CLI from holding an overnight
  drain, its task lease, and its credential until you notice.

  Ctrl-C is a soft interrupt: the current iteration finishes its completion binding,
  host signing, and mandatory between/protected audit, then exits 130 before final
  signoff or another claim. Press Ctrl-C again to stop now (tearing the running box
  down). (A detached fork has no terminal — stop it with 'coop fork stop'.)

  Defaults to .agent/tasks/ — or, in a monorepo, every queue named by the top-level
  .agent/project.yaml ('subprojects:' + the root's own), so one loop drains all the
  components' work with no setup. Repeat --tasks (or set COOP_TASKS) to override the
  set; the loop keeps going while any queue has unfinished work. The whole repo is
  mounted either way.

  A fork loop does not copy or mount those queues. The host assigns one canonical task at a
  time and exposes only that task through an execution projection. Stop/crash keeps the exact
  assignment resumable; projected done becomes a reviewed fork candidate, and the canonical
  task reaches done only when that exact candidate lands. Multiple forks may therefore share
  the same queue without duplicate work. --tasks on a fork selects one canonical queue; it does
  not create a fork-local queue.

  --max-tasks <n>   work at most N selected tasks, counting each only after it reaches done
                    or blocked following retries and its immediate audit; then pause
                    successfully before another task or final signoff; N must be positive,
                    and an empty actionable queue starts no box
  --preflight       run the pre-loop tidy: coop itself returns blocked/ tasks whose decision
                    now has an answer to todo — host-side, no box; an agent runs only for a
                    loop.yaml preflight.prompt cleanup (default it on with preflight.enabled;
                    --no-preflight overrides). Makes no code changes or commits.
  --no-mcp          run this loop's boxes without the shared MCP config (the committed form
                    is loop.yaml mcp: false)
  --debug-on-fail   on a failure at a terminal, open a box shell, then retry
                    on exit (a no-op in unattended runs)

  Exit codes: 0 = queue verified done or an intentional --max-tasks pause; 1 = failure;
  2 = usage; 3 = stopped with a task blocked on a human decision (including one the
  review kept reopening past the round cap) — resolve with 'coop tasks decisions', then
  re-run; 130 = interrupted before queue verification. So cron/CI can branch without
  parsing output.

  loop.yaml work.command overrides the per-iteration command.

coop up — start the repo's sibling services so the box can reach them by name.

  Usage: coop up

  Brings up the services in .agent/compose.yml on coop's network. The final
  status names the exact resolved Compose services, in Compose order; an agent
  in the box reaches each one by that hostname. Stop them with: coop down

coop down [-v] — stop the repo's sibling services.

  Usage: coop down [-v | --volumes]

  -v, --volumes   also remove the services' volumes (their data)

coop init [--stack asdf] — scaffold coop's working set into the repo.

  Usage: coop init [--stack asdf] [--services postgres,redis] [--agents claude,codex|all]

  Per-agent dirs (.claude/.codex/.gemini) are scaffolded only for the agents you're
  signed in to — or the --agents list ("all" for every one). A repo you keep on
  only .agent/ still works: a box synthesizes missing skills from .agent/skills on
  demand, and Claude also gets fallback settings + hooks from .agent/claude. If a
  real .claude/skills already exists and .agent/skills does not, init keeps it as
  the shared source instead of creating a competing skills tree.

  Writes AGENTS.md, the .agent/ queue, the Claude + git commit hooks, and the
  workflow skills. Subagents are left to you: a preset generates its own
  coop-<role> in the box, so init commits none to the repo (see the README's
  "orchestrator pattern"). The commit hooks' format gate matches the repo's stack —
  detected from go.mod / *.tf / mix.exs / Cargo.toml or .tool-versions (gofmt,
  terraform fmt, mix format, cargo fmt). With nothing detected the gate is left
  neutral (it imposes no checks); at a terminal it asks which gate to add. A
  .tool-versions (or --stack asdf) also scaffolds an asdf .agent/Dockerfile.
  Sibling services (db/redis) are opt-in: at a terminal it asks which to add as a
  .agent/compose.yml — none by default, or pass --services. If the repo already has
  its own Docker and no .agent/Dockerfile yet, it suggests how to build the box on it.
  Also seeds an empty ~/.config/coop/agents/mcp.json (the shared MCP source of truth,
  inert until you add a server) so there's an obvious place to declare MCP servers.
  Writes the TOP-LEVEL .agent/project.yaml (committed): in a monorepo — detected by any
  dir at ANY depth that is itself a coop project (it has a .agent/), so a nested layout
  like terraform/environments/va1 is found too — it lists them under 'subprojects:' so
  coop aggregates their task queues automatically (no COOP_TASKS). A member you add later
  is registered on the next init, in place, leaving the file's comments intact. The walk
  skips hidden dirs, dependency/build output, and anything inside a member.
  coop init scaffolds each member with ONLY its own task queue + backlog
  (it shares the root's AGENTS.md/.claude); the member's queue is for its own work, the
  root's for changes spanning members. A single repo gets a project.yaml template with
  commented serve/subprojects examples. The .gitignore ignores .agent/ state at any
  depth (**/.agent/*) and commits kb/ (including kb/rules/), skills/, presets/, claude/,
  and loop.yaml at any depth too (a large member MAY add its own), keeping only project.yaml
  top-level. Never clobbers existing files.

coop doctor — prove the box's isolation: attack it, inside and from the host.

  Usage: coop doctor

  Runs the escape/leak checks — secret shadowing, network limits, host reach,
  the fork handoff — and prints a pass/fail report. Probes the image this
  repo's boxes run: its per-project image when built, else the shared base
  image, else a stock alpine stand-in (which skips the USER/toolchain checks
  and says so). Honors COOP_RUNTIME.

coop check-secrets — scan the working tree for committed secrets, by content.

  Usage: coop check-secrets [--include-ignored]

  Scans for token shapes and high-entropy values, reporting file:line. Exits
  non-zero on a hit, for use as a pre-flight or CI check. Hide a flagged file
  with .coopignore.

  By default it scans the commit-candidate files (tracked + untracked; gitignored
  excluded) — including a file coop shadows from the box by name (an id_ed25519,
  a *.pem): the box never sees it, but a push would commit it. A 'coop
  run'/'shell'/'loop' mounts the WHOLE tree, though, so a gitignored-but-not-
  shadowed file is still visible to the agent — pass --include-ignored to scan
  the full visible tree too (deps/build dirs and shadowed files git would not
  commit are still skipped). A .coopignore entry silences a file in both modes.

coop build — build the box image (stable, pinned).

  Usage: coop build

  Builds the shared base, or a per-project image if the repo has a
  .agent/Dockerfile — pinning versions for reproducibility. Re-run after
  changing .agent/Dockerfile or .tool-versions. For the latest, use coop update.

  New runs use the fresh image automatically. Editor sessions (coop acp) are
  restarted onto it transparently — they reconnect, so you don't lose the session.
  Other running boxes (a loop or an interactive agent session) keep the old image
  until they next start.

coop update — self-update coop, then rebuild the box image fresh.

  Usage: coop update [--self-only | --box-only | --check]

  First replaces the coop binary when GitHub has a newer release — its versioned
  archive is verified against the release checksum locally, then swapped in
  atomically so replacing the running binary is safe. Then rebuilds the image like
  coop build but --pull --no-cache and unpinned, so the node base and agent CLIs
  jump to latest. Use coop build for a reproducible image. Supervised editor
  sessions are restarted onto the new image transparently, same as build.

    --self-only   update just the coop binary, skip the image rebuild
    --box-only    rebuild just the image, skip the self-update (the old behavior)
    --check       dry-run: report the binary vs the latest release and the box
                  image's build age/staleness, changing nothing (no runtime needed)

  A dev/source build, an already-current or newer binary, or a coop installed
  somewhere unwritable (a package-manager prefix) skips the self-update with a note.

  Once a day, any TTY command also checks for a newer release in the background
  and mentions it after the command's output; COOP_NO_UPDATE_CHECK=1 turns that
  notice off.

coop completion <bash|zsh> — print a shell completion script.

  Usage: coop completion <bash|zsh>

  bash: coop completion bash > ~/.local/share/bash-completion/completions/coop
  zsh:  coop completion zsh > "${fpath[1]}/_coop"
        then add 'source "${fpath[1]}/_coop"' AFTER compinit in ~/.zshrc. Existing
        file-only installs must add this source line; it enables command-local nocorrect
        for coop arguments without changing Zsh correction for other commands.

  Completes commands and verbs, and — via a hidden 'coop __complete' — live values
  (fork names, task ids, credentials), all from local reads.

coop prompt — one compact status line for a shell prompt, tmux, or menubar.

  Usage: coop prompt

  Prints this repo's state on ONE line — task counts and fork/loop activity, with a
  compact separator between non-zero segments. Nothing prints when the queue is empty
  and no forks exist, so an embedding prompt stays clean.

  Read-only and cheap: it reads the task dirs + fork pidfiles (plus one git-root
  lookup) — never a per-fork git call and never docker — so it's safe to run on
  every prompt redraw. Wire it into your shell prompt (a starship custom command)
  or tmux, e.g. set -g status-right '#(cd #{pane_current_path}; coop prompt)'.

coop sessions — serve and inspect local remote sessions.

  Usage: coop sessions serve [--state <path>] [--policies <path>] [--socket <path>]
         coop sessions doctor [--socket <path>] [--json]
         coop sessions policies [--policies <path>] [--json]
         coop sessions compact [--state <path>] --backup <path>

  'serve' owns the state root and exposes the v1 JSON API over an owner-only Unix
  socket. It never listens on TCP. 'doctor' checks only that Unix socket and exits
  nonzero when the service is unavailable or unready. 'policies' validates the
  trusted policy file and prints the immutable policy and model-independent authority
  digests a fleet worker must advertise; JSON output contains policy_file,
  policy_digests, and policy_authority_digests. 'compact' stops
  if the state root is active, writes and verifies a new SQLite backup, replaces
  legacy full-turn retry receipts with prompt-free receipts, checks integrity,
  then vacuums reclaimed pages. It never overwrites the backup path.

  A policy's target: takes one target or a LIST of up to 4 — a fallback ladder, and
  it may cross providers: [codex:gpt-5.6-sol/xhigh@oncall, claude@oncall]. A rate
  limit moves the session to the next free rung and re-delivers the same turn; when
  every rung is cooling the turn fails 'rate_limited' for the client to retry.

  Defaults:
    state    ~/.local/state/coop/sessions
    policies ~/.config/coop/session-policies.yaml
    socket   <state>/control.sock

coop worker — connect one private Coop daemon to Responder.

  Usage: coop worker connect --config <absolute-path>

  The connector opens one outbound mutual-TLS HTTPS poll stream and maps only
  versioned Responder commands to the owner-private Coop Unix API. It journals
  each command before execution and resends terminal results until Responder
  durably acknowledges them. It never opens an inbound TCP port or accepts a
  generic shell command. Populate policy_digests and policy_authority_digests
  from the exact output of 'coop sessions policies --json'.

coop version — print coop's version and exit.

  Usage: coop version

  Prints coop's build version (git tag or commit). Takes no arguments; -v and
  --version are aliases.

SOURCE-TREE CONFORMANCE

  From a source checkout, 'make check' is the blocking no-credential gate. Focused
  deterministic targets are 'make provider-scripted-e2e', 'make acp-scripted-e2e',
  and 'make live-process-control'. Real isolation checks are 'make doctor' and
  'make review-writes-e2e'.

  'make provider-live-e2e', 'make provider-resume-live-e2e',
  'make provider-loop-live-e2e', 'make provider-consult-live-e2e', and
  'make acp-e2e' are opt-in because they use installed CLIs, configured credentials,
  and real quota. Strict '-all' forms, request counts, summaries, and triage are in
  README.md under Layout & development.
