Skip to content

CodeCrew, precisely

codecrew.works is the introduction site; the repository's README is the technical entry point, with the install line and the routing-table example. This page says what CodeCrew is, what exists today, and where everything else lives — it is the map.

Read in this order

Two things first, because the pages below can give the wrong impression. The verbs are documented so they can be run by hand; in practice the coding agent runs them and a human answers the gates. And onboarding is the three commands under Start now — install the extension, cd into the repo, gh codecrew init — after which an agent dispatched into the repo reads .codecrew/AGENTS.md (the root AGENTS.md points at it) and its role contract and does the rest; the quickstart is the long form of what happens next, not a prerequisite.

  1. codecrew.works — the introduction site: what the problem is, how the protocol works, and what it has delivered.
  2. This page — the three parts, what is shipped, the refusal codes.
  3. Your first milestone — the quickstart: one human, one agent, a milestone from opening to audited close.
  4. Identities — running solo, staffing seats with humans, teams and App identities, dispatching a role session.
  5. Platform interop — the ladder's last rung: how an orchestration platform hosts the whole crew, written from four cycles of doing it.
  6. CLI.md — the CLI reference: every verb's synopsis, options, what it reads and writes, refusal codes and exit status. Read it before the SPEC if you are running the verbs.
  7. Working offline — which verbs run with no network, how the rest refuse, and the recipe for work begun offline.
  8. SPEC.md — the protocol itself: topology, state model, configuration, verbs, roles, gates.
  9. Founding decisions and the per-milestone records in milestones/ — the "why", as it was recorded.
  10. GSD vs. "just let the model orchestrate" — the essay that started the project: one person's experience with GSD across several projects, kept as the motivation, not a verdict on GSD.

Three parts

CodeCrew is a lightweight framework for agent-driven software delivery: the auditability and reproducible discipline of heavyweight frameworks, without the ceremony. Project state lives in the tools teams already use — GitHub issues, PRs, and CI — and the only documents the framework produces are per-milestone records of the decisions that shaped the system and why.

  1. A protocol (SPEC.md) — conventions for representing milestones, tasks, plans, decisions, deviations, and gates in GitHub, and how agents and humans transact over them.
  2. Role contracts (.codecrew/roles/) — harness-neutral prompt files for the implementer, reviewer, qa, and doc-synthesizer roles, and for the coordinator that dispatches them, loadable by any agent (Claude Code, Codex, Gemini CLI, or an orchestrator's company). A project extends a contract without forking it in .codecrew/roles/<role>.local.md (SPEC §7).
  3. A CLI — codecrew, a single static Go binary wrapping gh, providing the workflow verbs with gates enforced as code.

What exists

Shipped: v2.1.1 of the gh extension, implementing protocol 2.1 (version prints both: v2.1.1 (protocol 2.1); the pointer's protocol major is checked, another major refuses, and a pointer naming an earlier minor such as "2.0" is current; gh itself must be 2.50.0 or later, or the CLI refuses GH_TOO_OLD before any verb runs). Verbs: init, migrate, status, milestone new/evidence/close/strike, task new/start/finish, checkpoint, role, roles diff/show/sync, identity new/token/webhook, and version — all implemented, with machine-readable refusals (refused[CODE]: detail, catalogued below) when a gate blocks. What each verb takes, what it reads and writes, which codes it can refuse with and what --dry-run does where it has one is CLI.md, a section per verb. milestone new, task finish, milestone close, milestone strike, migrate and roles sync take --dry-run, which shows what the verb would do and writes nothing. Protocol 2.1 adds three things to the 2.0 lifecycle. A requirement can be struck by a recorded decision of the coordination layer (milestone strike), and milestone close counts a struck requirement as terminal, as it does a QA satisfied. A change with no decision in it — a tool's output, such as a contract sync or a dependency bump — is housekeeping and takes a light path: a reviewed chore: pull request with no task, milestone or verdict; the milestone record takes the same path. And roles sync brings a hub's contracts, and any repo's .codecrew/AGENTS.md, up to the release's text where they are absent or an earlier release's, refusing a fork rather than overwriting it. What changed and when: CHANGELOG.md. Not yet here: any backend other than GitHub, and GitHub Enterprise Server — github.com only.

Who holds a seat. Every role is always staffed, by exactly one of four kinds of principal, named by the routing table's type prefix: the operator themselves (identity: ~), a named human (user:<login>), a GitHub team (team:<org>/<slug> — any member holds the role), or a GitHub App identity (app:<slug>). Solo is a routing configuration, not a degraded tier: the qa holder's verdicts are the ones that count at close, and the reviewer holder's approving review is the one task finish requires. A solo operator therefore needs nothing but gh auth login; gh codecrew init scaffolds a project with every role routed to ~. When a project outgrows solo, identity new <role> mints a dedicated App identity through the manifest flow and routes it for you — minted with --with-approval-permission, a reviewer App's approvals count toward GitHub's own required-review rules, which makes fully agent-gated merges possible (identities.md).

The routing table. Who holds which seat is one roles: table in the hub's .codecrew/config.yml: a row per seat, naming the typed identity that holds it and the harness and model it is dispatched under, with ~ where the operator holds it. Two worked examples are one click away — this repository's own table, as it stands today, in the README, and an annotated generic one on the home page. SPEC §5 is the field-by-field reference.

How a repo joins. Every repo in a CodeCrew project carries a .codecrew/config.yml pointing at the hub — a spoke's is a two-line pointer (init --hub owner/repo); this repo is its own hub (hub: self; SPEC §3 on choosing yours). The hub's config also routes the five roles — the four crew seats and the coordinator, which unrouted is you — and init writes that table for you. Every repo also carries the entry point — the instructions in .codecrew/AGENTS.md, a root AGENTS.md pointing at them and a CLAUDE.md importing that — so an agent dispatched into a spoke lands the same way it does in the hub. Agent identities are GitHub Apps; a seat's first act is export GH_TOKEN=$(gh codecrew identity token <slug>), which mints a short-lived installation token from the platform's env bindings or the local key and stub and discovers the installation from the App itself — see identities.md (SPEC §5 for the tiers).

What it has done — the receipts are on the home page; the per-milestone records are in milestones/. The largest environment it has run in — an orchestration platform dispatching all five seats — has its own page: Platform interop.

Install and use

gh extension install radiusred/gh-codecrew   # precompiled, all platforms
gh codecrew version        # confirm what you installed (gh never auto-updates extensions)
gh codecrew init           # scaffold a new project (see first-milestone.md)
gh codecrew migrate        # a repo still on the 1.x layout: move it to 2.0 (--dry-run first)
gh codecrew status         # open milestones, inferred task states, raised gates, notes
gh codecrew role reviewer  # who holds a role: app:<slug>, user:<login>, team:<org>/<slug>, or ~ (you)
gh codecrew help           # the full verb list

# or build from source (single static binary; requires gh on PATH):
go build -o gh-codecrew ./cmd/codecrew

Projects with external contributors

A project that takes issues from outside the crew accumulates community reports alongside protocol traffic, in one issue list. That is safe by construction, because the framework reads labels and an unlabelled issue is invisible to every verb: status lists the hub's cc:milestone issues and walks their sub-issues, milestone close counts the same set, task start refuses NOT_A_TASK on an issue that is not labelled cc:task, and task finish then refuses NOT_OWNER, because nothing recorded a start on it. A report nobody labelled is a report, however many of them arrive. Three conventions keep it that way.

No issue template may auto-apply a cc: label. GitHub restricts labelling to triage permission and above, so a first-time reporter cannot apply one by hand — but a template's labels: front matter, and an issue form's labels: key, label the issue on the reporter's behalf whatever their permission. That is the one path by which a read-only user's issue arrives already wearing a protocol label, and a template naming cc:task would hand the verbs an issue with no plan, no requirement IDs and no milestone above it. So: no template under .github/ISSUE_TEMPLATE/ names a cc: label, in front matter or in a form key. It is a convention a project's template review holds to and not a gate — the CLI never reads .github/ISSUE_TEMPLATE/, and nothing refuses over one.

A community report is adopted, never converted. When the project takes one up, it becomes work the way any backlog capture does: a task, opened with gh codecrew task new --milestone <n> --adopts <ref>, which lists the report under the task's ## Adopts section and comments on the report naming the task. The report itself is left exactly as filed — not relabelled cc:task, not rewritten into the task template, not edited to carry a plan. task finish closes it after the merge with a comment naming the task, the pull request and the commit the merge left, so no pull request body has to remember a closing keyword for it (#193). Converting in place loses on both counts: the report is the reporter's account of a problem, and the trail is worth more showing what was asked for and what was built as two linked objects than as one issue the maintainer overwrote — and a report relabelled cc:task is an issue the verbs will now pick up, where the good case is task start refusing it for having no plan and the bad one is a plan pasted over the reporter's words. SPEC §4 has the adoption grammar, and the coordinator contract the rule as a coordinator meets it.

Reverting a cc: label applied below maintain is deferred. A project that grew enough triage-level helpers to worry about the protocol labels being misapplied could add a workflow on the labeled event that reads the actor's permission, removes any cc: label applied by an actor below maintain who is not one of the routing table's role Apps, and comments to say why. Nothing here ships it: no protocol behaviour depends on it, GitHub's triage floor already keeps reporters out, and it would cost a workflow run on every label event in the repository — build it when a project has the helpers that make the misuse real, not before.

Refusal codes

A blocked gate exits 1 — every failure does, and there is no exit-code taxonomy — with one line on stderr:

codecrew: refused[CODE]: detail

The code is for the agent: a fixed vocabulary whose meanings are stable within a protocol major, so an orchestrator branches on it. The detail is for the human, and is free to be reworded in any release; nothing should parse it. A note: line is advisory and not a failure, and most are not on that stream: they go to stdout beside the report they qualify, and CLI.md's Output channels names the few on stderr.

The catalogue is SPEC §10 — every code, the verbs that raise it and what it means, in one table. It is the single list: this page carried a second one until protocol 2.0, and keeping two in step across a milestone's worth of new codes proved to be exactly the drift the table exists to prevent. The source of record behind both is refuse("CODE" in internal/cli/, and a test fails the build when the table and the source disagree. The other direction — which codes a given verb can exit with — is that verb's section in CLI.md, and a test fails the build when those two disagree too.

The fix for a refusal is in the detail line the CLI prints, which names the condition met and the way out — read it before reaching for a code table. Your first milestone walks through the ones a new project meets first: NO_PLAN, NO_CHECKS, VERDICT_MISSING, DOC_MISSING.

NO_CHECKS is about facts, not effort: a check that reports skipping satisfies the gate, so a docs-only pull request can be cheap — see the self-gating workflow. [skip ci] produces no check at all, and absence is refused.

Licensed under Apache 2.0.