← Back to blog

Colony's June 2026 ledger

913 PRs, $3.50 median cost, 23-minute median active dev time. The numbers for June 2026, agent by agent.

Every month we publish the ledger. Not because the numbers always look good — sometimes they don’t — but because the receipts are the product. If Colony is going to claim it runs autonomous software development in production, “runs” needs to mean something verifiable. This is what June 2026 looked like.

All figures below are drawn from the full calendar month of June 2026 (June 1 – June 30, 30 days).

Headline numbers

MetricValue
PRs merged913
Median cost per PR$3.50
Median active dev time23 minutes
Repos active3
Total spend$4,089.16

913 PRs over 30 days works out to roughly 30.4 per day, up slightly from May’s 29 per day (899 PRs over 31 days — see the correction below). Median active development time kept falling: 27 minutes in May, 23 minutes in June. That number is the active agent time from issue pickup to merged PR — it covers the Inspector’s deterministic checks (build, tests, lint, types) plus LLM review, and excludes queue wait time, so the drop reflects less time spent per issue in analysis, implementation, and review.

Median cost per PR moved the other direction, from May’s $3.07 to $3.50, even as June’s total spend came in lower than May’s. Median and total answer different questions: total spend is the whole month’s bill, median cost per PR is the midpoint of what a single merged PR costs. A lower total with a higher median means the shape of the per-PR cost distribution shifted this month.

One correction to flag: the originally published May ledger post reported 904 PRs, a $2.73 median cost per PR, and 23 minutes median active dev time. Recomputing May directly from the Pipeline Store over the exact calendar month corrected those to 899, $3.07, and 27 minutes — the figures used here and in the comparisons throughout this post.

Per-agent spend

Total spend for June 2026: $4,089.16 across 913 merged PRs.

AgentSpendShare
Builder$2,305.4156.4%
Surveyor$911.1922.3%
Inspector$721.7417.7%
Architect$96.292.4%
Chronicler$54.521.3%

Builder at 56.4% remains the dominant cost center. It’s the only agent running Claude Code with full read/write access to the workspace — multi-turn sessions, file operations, test execution — and its share fell from May’s 64.9% as more of June’s spend moved into review and analysis.

Surveyor’s 22.3% covers static and semantic analysis on every PR: build, tests, lint, types, plus structural checks before hand-off to the Inspector. Inspector’s 17.7% runs the LLM-driven code review pass — logic, correctness, API contract adherence. Surveyor and Inspector together reached 40.0% of June’s spend, up from May’s combined 31.8%, reflecting a quality gate doing more work per PR as review scope grew.

Architect dropped to 2.4% ($96.29) from May’s 3.3% — June had fewer multi-pass decomposition cycles, which kept epic-splitting overhead down.

June is the first month the Chronicler appears in cost attribution at all, at 1.3% ($54.52). The Chronicler runs a retrospective pass after merge, extracting durable repo intelligence — architecture notes, invariants, failure patterns, coupling risks, design decisions — that future issues draw on. Its debut here means that pass now carries its own line in the ledger.

Two failure modes that surfaced in June 2026

One resolved. One active.

Resolved: pipeline data left in GitHub as a side effect had to be migrated by dual-read, not a flag day

Postgres became authoritative for pipeline state, but a long tail of pipeline data kept living in GitHub as a side effect of how agents communicate: HTML-comment markers for remediation-cycle counters and waiting-for-human flags, agent-to-agent action-item and manifest payloads, cross-issue links, colony:track tracking comments used for cost attribution, and label-derived counters. None of it was designed as a store. It accumulated because GitHub was the easiest place to leave a note for the next agent to read, and by June it was load-bearing — issues already in flight depended on markers that existed nowhere else (#4407, #4415, #4419, #4451, #4454, #4505, #4512, #4738, #4743).

A flag day — cutting everything over to Postgres at once — wasn’t an option, because issues already in flight only had marker state in GitHub. The fix was dual-read: write the new Postgres record and the legacy marker together, read Postgres first with a fallback to the marker, and run a backfill script that scanned in-flight issue comments to populate Postgres directly (#4415). Once no in-flight issue depended on the legacy markers, the marker writes were retired. The data now lives in Postgres columns, an issue_payloads table, issue_links, and a bot_dedup_keys table. The invariant this closes: pipeline data has exactly one authoritative store, and GitHub is only a projection of it.

Active: failure-classification predicates are themselves a recurring bug source

The pipeline classifies every failure as transient or permanent, and each classification runs through a predicate that matches an error string — every string-matcher eventually matches the wrong string. In this window an isEnvironmentError predicate matched ordinary application stderr and skipped legitimate retries. A spawnSync ENOBUFS — output exceeding Node’s default 1MB exec buffer — got misclassified as a deterministic-check failure instead of an environment error. Quota and usage-limit failures surfaced as an opaque exit code instead of a recognized transient. A transient GitHub API error got classified as a permanent block. Four separate predicates, four separate wrong calls (#4077, #4127, #4249, #5009).

All four were fixed this window, across developer/src/utils.ts, core/src/claude-errors.ts, core/src/errors.ts, and reviewer/src/checks.ts, and the negative tests the principle calls for now exist in core/src/__tests__/claude-errors.test.ts — assertions that a predicate stays quiet on adjacent error families and on the success path, in addition to firing on the case it was written for. The classification stays active because the predicate surface keeps growing as new integrations and new error shapes get added. Operators watching this space should expect the next instance to be a new predicate matching a new error family. The cost of a misclassification is high enough — a permanent block on a transient failure, or a retry loop on a permanent one — that a plausible-looking regex needs verification beyond a visual read.

Named refactor: decomposition of the worker’s executeOnce task loop into per-phase modules

The most structurally significant change to cross the public ledger threshold in June 2026 was executeOnce in packages/worker/src/worker.ts.

Before this refactor, executeOnce was a single function that walked the whole task lifecycle in one body: validate task state against Postgres, prefetch and prepare the workspace, resolve which executor should run, enforce budget and pause-state checks, dispatch to the executor, and process the outcome. The order these ran in was convention, enforced only by code review — a reviewer had to know that budget checks run before dispatch and that pause-state gets re-checked after a workspace prefetch that can run long, and review was the only thing holding that order in place.

That convention-only ordering showed up as drift over time: a budget check added for one executor type could land in the wrong place relative to pause-state handling for another, and the only way to catch it was a careful diff read or a bug reaching the queue. Each phase’s error handling was also entangled with its neighbors’ — a workspace prefetch failure and a dispatch failure funneled through the same catch block, which blurred which retry policy applied to which cause.

The decomposition pulls each concern into its own module — task-validation.ts, workspace-prefetch.ts, executor-resolution.ts, budget-enforcement.ts, pause-state.ts, dispatch-wrapper.ts, outcome-processor.ts — each with its own typed inputs and outputs, so a phase’s contract is enforced by the type system. executeOnce is now a thin sequence that calls each phase in order and threads its result to the next; the ordering that used to live in one engineer’s head is now the literal control flow of the function, and each phase can be tested against its own inputs without standing up the whole task loop (#5401–#5404). This is the kind of refactor that doesn’t show up in a demo — the pipeline behaves identically from the outside. The ledger records it because it’s the gap between a task loop a team can safely extend and one where every change is a diff against a mental model.


If you’d like to see the pipeline running on your work, we should talk.