Inside Orca: When Agents Write the Code, Discipline Has to Become a Machine Gate
You fan one prompt across five coding agents. The first genuinely hard problem isn't merging their work — it's this: did the third one finish?
Is it thinking, waiting for your answer, or did it die ten minutes ago? All you have is a stream of bytes in a terminal. CLI agents expose no status API, no exit code to await (the process is still alive), no unified event stream. You end up staring at a spinner to see whether it's still spinning.
Every parallel-agent orchestrator stalls at this exact wall. stablyai/orca has the least dignified and most effective answer I've seen in an open-source project.
The numbers first
As of August 12, 2026:
| Metric | Value |
|---|---|
| Stars / Forks | 43,205 / 3,014 |
| Created | 2026-03-17 (about five months) |
| Commits | 8,490 |
| Files | 13,310 (TypeScript/TSX: 12,526) |
| Test files | 5,421 .test. / .spec. files — 43% of all TS files |
| Release cadence | v1.4.180, near-daily, with an rc channel |
| License | MIT |
| Company | Stably AI (YC W22); the other product line is Stably, an AI testing platform |
The business model is bring-your-own-subscription: Orca itself is $0, no per-seat fee, and no proxy layer — it never touches your tokens, it just runs your own Claude / Codex / Cursor subscriptions locally. That structure means no short-term incentive to paywall features, and long-term sustainability riding on the parent company's testing business.
The top four contributors account for 6,653 commits, 78% of the total; with the CI bot, roughly 87%. This is open source, not shared governance.
1. Status detection: three channels, degrading by reliability
src/main/agent-hooks/managed-agent-hook-registry.ts registers hook services for 14 agents — claude, openclaude, codex, gemini, antigravity, amp, cursor, droid, command-code, grok, copilot, hermes, devin, kimi. Each carries four lifecycle methods: install / remove / refreshManagedScripts / getStatus.
Channel 1 (primary): managed hooks. Orca writes scripts into the user's agent config, and those scripts post events back over loopback HTTP with a per-pane token:
ORCA_PANE_KEY / ORCA_AGENT_HOOK_PORT / ORCA_AGENT_HOOK_TOKEN
→ POST http://127.0.0.1:$PORT
Header: X-Orca-Agent-Hook-Token: $TOKEN
The receiving end, agent-hooks/server.ts, is 2,907 lines.
Channel 2 (parasitic): hijacking Claude Code's statusLine. This one is clever, and the source comment states the motive outright:
Claude Code pipes
rate_limitsto the statusLine command on every turn; forwarding it gives Orca live usage without spending the OAuth usage endpoint's tight budget. Emits no stdout so the in-terminal status line stays visually unchanged.
Which means your Claude status line has already been intercepted, and you can't tell.
Channel 3 (fallback): parsing terminal output. Spinner frame recognition, pane-title matching. This is the brittle layer — the most frequent patch type in the August 2026 commit stream is exactly this: "treat Claude Code quarter-circle spinners as working," "detect a live OpenCode pane from its native OC | session title." One upstream character change breaks it again.
The real value is the blood in the hook scripts
In hook-stdin-contract.ts, POSIX and Windows follow opposite rules, and each carries an issue number:
# POSIX: own stdin before any no-op exit.
# Why: a stripped PATH must not stop a hook from consuming stdin, or the agent
# sees exit 127 and a broken pipe mid-write (#8110). `command -p` resolves from
# the shell's built-in default PATH, so it also survives hosts without /bin/cat
# (NixOS) and ignores a worktree-local `cat` that could capture the payload.
POSIX_HOOK_STDIN_READER = '{ command -p cat 2>/dev/null || cat; }'
:: Windows: check the env before touching stdin.
:: Why (#11549): missing Orca context means the hook ran outside an Orca pane,
:: where the caller may abandon stdin rather than close it — a read-to-EOF then
:: blocks forever and strands a visible window per hook event.
WINDOWS_HOOK_STDIN_READER = '"%SystemRoot%\System32\more.com"'
more.com is fully qualified because Windows searches the working directory before PATH — and hook payloads must never reach code sitting in the repo.
Then there's statusline throttling: Claude ticks the status line roughly three times a second while streaming, so the Windows variant computes a seconds-of-day stamp using only cmd builtins to avoid spawning findstr + curl on every tick — and it only writes the stamp when a post is certain, so skipped ticks never push the next allowed post further out.
And a detail you only learn by getting burned: PTYs outlive Orca restarts, which makes the port and token in their env stale. So every hook first calls an ORCA_AGENT_HOOK_ENDPOINT file to refresh them, falling back to PTY env only if that's missing.
My call: this layer is the one thing about Orca you cannot fork. The features are copyable. These few hundred platform corner cases were bought one issue number at a time.
2. Renaming one socket file takes seven steps
src/main/daemon/AGENTS.md is the highest-density document in this repository, by a wide margin. Its subject sounds trivial: how the terminal daemon hands over its canonical socket path.
The root cause: net.Server.close() unlinks the pathname it bound with no ownership check. So a departing daemon deleted whichever socket then sat at the canonical path — including a live replacement's. The replacement stayed alive, still hosting every PTY, with no client able to reach it.
What the user sees: terminals that accept keystrokes and never run them.
Two invariants:
Only a daemon publishing itself onto the canonical endpoint may mutate that directory entry, and only by replacing an entry it has itself just proven dead.
No actor removes a name it did not create.
The protocol is seven steps: bind a private .p<hex> name → attempt an exclusive link → on EEXIST, connect to prove the incumbent dead → re-check the entry hasn't changed hands → probe once more → rename in one syscall → verify you kept it.
But the section worth more than the protocol is the one titled "Traps That Already Cost Us":
- Never collapse "can't tell" into "dead." Only
connectedmeans occupied; onlyrefused/missingprove death. A timeout orEPERMproves nothing and must decline — treating it as death deletes an endpoint still serving every terminal on the host. linkfirst, never an unconditionalrename.renamereplaces whatever it finds, which would let a starting daemon destroy a healthy one.linkfails loudly and forces the liveness question.rename, neverunlink-then-link. The latter leaves the name absent between two calls; measured across a live handover it gapped on essentially every observation, whererenamegapped on none in ~14,500 probes.- Do not identify an entry by
birthtimeMs. Node documents it as sometimes holding the ctime, filesystems without a birth time report the epoch, and its granularity is often coarser than the events it must separate. Three attempts to patch around it produced three more defects; inode recycling is now settled by asking whether anything is serving. - Do not add a sweeper. Deciding whether someone else's leftover is safe to delete is the question this design retired; the last one produced five defects, including deleting a live listener's only pathname.
And the hardest line in the whole repo:
Seven review rounds against the older "launcher reclaims a dead process's name" shape produced twenty-three defects, all the same interleaving: a third party observing liveness at T and acting on the directory entry at T+1.
The doc then states its residual risk honestly: the final probe and the rename are two syscalls, and POSIX has no rename-if-target-is-inode-X. The harm is separately unreachable — a daemon never creates a session on an endpoint it no longer holds; it drains rather than serving on.
This document is worth more than the protocol it describes. It records not "what we did" but "what we tried, where each attempt died, and why not to try it again." In most teams that knowledge lives in three people's heads and evaporates when they leave.
3. It isn't a worktree GUI — it's an orchestration engine
src/main/runtime/orchestration/ was the biggest surprise of this teardown: a SQLite-backed coordinator–worker system.
CREATE TABLE runs / tasks / dispatch_contexts / messages / deliveries
CREATE TABLE worker_dispatches / worker_terminal_resources / worker_terminal_archives
CREATE TABLE federated_dispatches / federation_relay_items / remote_questions
CREATE TABLE mutation_receipts
Four decisions worth pulling out:
1) The coordinator is one of very few files granted an exemption. Its first line:
/* eslint-disable max-lines -- Why: the coordinator keeps message processing, task
dispatch, gate handling, escalation, and convergence checking in one class so the
polling loop can make atomic decisions across all these concerns without
split-brain behavior. */
The root AGENTS.md explicitly forbids adding a max-lines disable. This file breaks the rule and writes the justification at the site of the break. Hard rules, justified exceptions — that's what a mature team looks like.
2) Workers are onboarded via a preamble. The coordinator generates a preamble injected into the worker's prompt, instructing it to report worker_done and heartbeats through the orca CLI. Heartbeat interval: 5 minutes; the coordinator's stale threshold: 10 minutes — with a comment explaining exactly why those two numbers: frequent enough to catch a hung worker within one tick, infrequent enough to avoid inbox spam.
3) Dispatch probes base drift first. If the worktree is behind its tracking remote, dispatch is refused (stale-base-refused) unless allow-stale-base: true is set. Then comes what I consider the most valuable comment in the repository:
Callers must NOT pre-populate this with empty data; the drift section is a loud-but-rare signal, and polluting it for fresh worktrees would train workers to ignore it.
That's alert fatigue treated as a first-class prompt-engineering constraint. Anyone who has built agent systems should feel the weight of it immediately — the "evidence must be real" rules in my Prime Agent harness teardown are the same law seen from another side.
4) Federation. Dispatches sync between the local runtime and remote hosts, with ack checkpoints, a relay queue, and schema version-skew migration. Orchestration itself crosses machines.
4. Mixed-version compatibility: silent frame drops are the real enemy
docs/reference/remote-wire-compatibility.md opens with the premise stated plainly: desktop clients and remote Orca runtimes update independently, so mixed versions are the normal state, not an edge case.
- Rule 1: adding a new optional JSON field to an existing frame is safe (RPC params go through zod
.strip(), stream frames throughJSON.parse— both ignore unknown keys). But that safety holds only while every reader treats it as optional. The moment a newer client requires it, that's the same defect as removing a field, just discovered later. - Rule 2: a new stream opcode is not safe and must be capability-negotiated. Because:
const frame = decodeTerminalStreamFrame(bytes)
if (!frame) {
return // silently dropped — the sender never learns
}
An unknown opcode decodes to null and is dropped without an error; the sender never finds out. To the user, the feature simply hangs — the worst kind of bug to chase.
This rule transplants directly into any system where two independently-released processes talk to each other.
5. The thesis: compile the discipline into gates
The four sections above are about hard problems solved. But if you take one thing away, make it this: once most of the code is written by agents, line-by-line human review stops being the quality mechanism, so every constraint has to sink into a machine-executable gate. Orca is the most thorough implementation of that idea I've seen in open source.
The line-count ratchet: it can only shrink
The comment in check-max-lines-ratchet.mjs explains the mechanism:
oxlint already fails any file that exceeds max-lines WITHOUT a suppression, so the only way a file grows past the budget is by adding a disable comment or a per-file bump in
mobile/.oxlintrc.json. This check freezes the set of files currently allowed to do that and fails CI when a NEW bypass appears — existing over-limit files are grandfathered; new ones must split instead. The baseline may only shrink.
Default budgets: 300 lines for .ts, 400 for .tsx, 600 for .mjs, 800 for test files. The baseline currently grandfathers 351 files.
One charming detail: the ratchet excludes itself and its own test from scanning, because those files legitimately contain the directive text as data (regex, fixtures) — "the ratchet does not police itself."
74 reliability gates: a formal reliability ledger
config/reliability-gates.jsonc is the first thing of its kind I've seen in an open-source repo. Each gate is a structured record:
{
"policy": {
"maturityLevels": ["experimental", "soak", "blocking", "accepted-gap", "deprecated"],
"blockingPromotion": {
"minimumSoakRuns": 100,
"minimumSoakDays": 14,
"maximumUnexplainedFlakes": 0
}
}
}
Every gate must declare: invariant (what exactly must hold), oracle (how you'd falsify it), coveredPlatforms / coveredProviders (and, by omission, what isn't covered), motivatingLinks (the issue or PR that caused it), commands (how to run it), and assertionRefs (which specific assertions in which test files back it).
Three decisions stand out:
- One maturity level is
accepted-gap— known to be uncovered, and that's a deliberate decision rather than an oversight. Turning "we know there's a hole here" into a queryable record is far more honest than pretending full coverage. - Promotion to
blockingrequires 100 soak runs, 14 days, and zero unexplained flakes. Flakiness isn't "just rerun it" — it's a hard blocker on promotion. coverageNotesspells out the evidence not yet collected, e.g. one gate states that deterministic store/controller tests cover local, direct-SSH and paired-runtime identity, while "live headed/headless paired-runtime and post-establish Electron IPC remain uncollected."
E2E assertions must target the DOM, never the store
tests/e2e/AGENTS.md contains a real incident worth quoting in full:
The
'create-worktree'modal key lived on in theactiveModalunion long afterAddWorktreeDialog.tsxwas deleted in #710, sostore.openModal('create-worktree')+store.activeModal === 'create-worktree'round-trips succeeded against a modal that rendered nothing. That tautology is what let #1186 (React error #31 inStartFromField) ship — the store-layer test passed while the composer actively crashed for real users.
The conclusion becomes a hard rule: use the store to reach a state; use the DOM to prove the state is correct. A spec that both writes to the store and reads it back is asserting that Zustand's setter works, not that Orca works.
The same doc adds the inverse discipline: an E2E spec that merely calls store.getState().someAction(...) inside page.evaluate is a unit test paying a ~1.5s Electron launch for no extra coverage — go write a store-slice test instead.
The rest of the gates
- Benchmarks as CI gates: startup time, idle CPU, main-thread jank, terminal typing latency, zustand selector fanout, worktree deletion, WSL git shell, hang-watchdog memory. Each has a
bench:script and a budget check. global-fetch-call-site-audit.test.ts: a test that audits every call site of globalfetch. Steal this one — it converts "don't quietly add network calls" from a spoken norm into a red light in CI.- Triple localization validation: catalog consistency, extraction completeness, coverage — three independent scripts, all wired into
pnpm lint. - Naming bans:
AGENTS.mdforbidshelpers,utils,common,misc,shared-stuff— "they carry zero info and tend to become dumping grounds. If you find yourself reaching forhelpers, the file probably has more than one responsibility."
Taken together, this is the full production landing of the idea I covered in the harness engineering guide: the quality of an agent's output is set by the scaffolding around it, not by the model.
6. Two things they built almost incidentally
AI Vault (166 files): a cross-agent session archaeology layer. Claude, Codex and Antigravity each leave local session files in completely different formats, so Orca wrote a parser for each (session-scanner-codex-parser.ts, session-scanner-antigravity-parser.ts, session-scanner-claude-subagents.ts, …) and unified them into one searchable, deletable, remotely-scannable index with concurrency and batching controls. For the first time, every agent conversation you've ever had has a single entry point.
The skills directory: Orca ships 8 skills for agents (orchestration, orca-cli, computer-use, orca-linear, orca-emulator, …). What's interesting is that skills/orchestration/SKILL.md declares itself a discovery stub:
This file is a discovery stub, not the usage guide. The full, version-matched Orca orchestration reference is served by the
orcabinary itself — kept out of this file on purpose so it can never drift from the binary that will actually run your commands.
That's a direct fix for how skill docs usually die (documentation and implementation evolving separately), and a pragmatic answer to the distribution problem I discussed in the agent skills ecosystem piece.
The same file also contains a rule that made me laugh out loud: on Linux, outside an Orca-managed terminal, never run bare orca — it normally resolves to the GNOME Orca screen reader (/usr/bin/orca) and starts speech on the user's machine.
7. How it compares
I picked these four dimensions because they determine where you hit a wall: execution location decides whether you can push work onto a bigger remote box; status reporting decides whether the orchestrator knows what happened; governance decides whether your PR gets merged; form factor decides what it costs to run.
| Project | Stars (2026-08-12) | Form | Execution location | Status reporting | Governance |
|---|---|---|---|---|---|
| Orca | 43,205 | Electron desktop + mobile + VPS | local / WSL / SSH / ephemeral VM | hooks + statusline + terminal parsing | company-led, 4 people = 78% |
| Vibe Kanban | 27,745 | Rust, task kanban | local | process-level | parent company shut down 2026-04, community-maintained |
| Claude Squad | 8,285 | Go, terminal TUI + tmux | local | tmux panes | light community |
| Crystal | 3,108 | TS desktop | local | process-level | small team |
| Conductor | closed source | native Mac | local | undisclosed | commercial |
My recommendation is unambiguous:
- One Mac, only Claude Code and Codex, want polish → Conductor. Orca's surface area is wasted on you.
- Want a task queue that slots into an existing process, don't want an IDE → Vibe Kanban, accepting that no commercial entity is pushing it anymore.
- Need to push work to remote boxes, supervise from a phone, or run five vendors' agents at once → Orca is currently the only option. Nobody else does execution-location abstraction and status reporting to this depth simultaneously.
8. The costs (the honest part)
src/mainhas 200+ flat top-level files, with absurd names likeworktree-removal-session-partition-fencing.test.tsandpowershell-osc133-bootstrap-windows-clm.test.ts. That's the inevitable output of banningutils/helpersplus a line ratchet — a codebase optimized for agents retrieves by filename, not by directory intuition. High barrier for human newcomers; but their newcomers are mostly agents anyway.- The ratchet does not contain the core state machines. A 2,907-line hook server and a 555-line coordinator show complexity doesn't vanish under rules — it concentrates in the few places granted exemptions. The good news: those places genuinely deserve it.
- 14 agent integrations are a linearly growing tax. Each new agent = one hook service × four lifecycle methods × four script variants (POSIX / Windows / WSL / SSH), while upstream can change hook formats or spinner characters at any time. It's the most fragile surface they maintain.
- The Electron bill: roughly a 250 MB installer and 400–800 MB resident with a few agents running. That's a small share of total cost when five agents are running, but it's noticeable on modest hardware.
- Concentrated governance and a community PR backlog. The last 30 merged PRs all came from the four core contributors; the oldest still-open community PR dates to April 11 and hasn't moved in four months. There's a
track-community-prs.yamlworkflow, so they know — it just isn't solved. - Hooks write into your global agent config. The install locks, owner identity checks and script-refresh machinery show care, but in an enterprise environment this still needs a compliance review.
- Daily shipping means routine regressions. The README says it outright: "we ship daily, so this list is perpetually behind." Teams should pin a version rather than track latest.
To be explicit: every conclusion here comes from reading the source and public data, not from long-term production use. The memory-footprint and regression claims above are cited from public reviews and the project's own statements, not numbers I measured.
9. What this means if you never touch Orca
Three takeaways you can lift directly:
1) Any "we agreed to do X" norm has a lifespan under three months unless it has a red light in CI. Orca's move is to attach a script to every norm: naming conventions get a lint plugin, file size gets a ratchet, reliability gets a ledger, outbound requests get a call-site audit. Rules that live only in CONTRIBUTING.md are ignored by agents — and by people.
2) Silent failure deserves to be hunted as its own category. Unknown opcodes dropped without error, hooks blocking forever on stdin, birthtimeMs returning the epoch on some filesystems, timeouts read as death — the most expensive traps Orca hit share one trait: nothing throws, something just quietly stops working. Asking "if the other side is older than me, how does this message vanish silently?" beats writing ten more unit tests.
3) Write down what you tried and why it failed. The value of that daemon AGENTS.md isn't the protocol — it's the five dead ends, each with its cause of death and its price tag (seven review rounds, twenty-three defects, five defects, 14,500 probes). The next person — new hire or new agent run — will not propose adding a sweeper. It's the cheapest knowledge preservation mechanism I've seen.
Conclusion
What makes Orca worth attention isn't its feature list; that gets copied within a quarter. It's that it demonstrates something: a four-person core team, using a fleet of agents, produced VS Code-scale surface area in five months — and the quality mechanism wasn't "review harder," it was compiling every formalizable norm into a CI red light.
If you're deciding whether to adopt it: remote execution, cross-vendor agents, supervision from a phone → install it, there's no substitute, but pin a version. Running one or two agents on a single machine → its complexity is a liability for you.
If you just want to learn something: read four files — src/main/daemon/AGENTS.md, docs/reference/remote-wire-compatibility.md, tests/e2e/AGENTS.md, and config/reliability-gates.jsonc. Forty minutes, and more nutritious than most engineering blogs.
References
- stablyai/orca — GitHub repository (data snapshot taken 2026-08-12)
- Orca official documentation
- Stably AI (Orca) — Y Combinator company page (YC W22)
src/main/daemon/AGENTS.md— terminal daemon endpoint ownership protocoldocs/reference/remote-wire-compatibility.md— remote wire compatibility contracttests/e2e/AGENTS.md— E2E testing disciplineconfig/reliability-gates.jsonc— the reliability gate ledger- GitHub Issues #8110 / #11549 — the two original hook stdin contract incidents
- GitHub Issue #11304 / PR #11369 — the origin of the editor ownership migration gate
- GitHub PR #710 / Issue #1186 — the shipping incident caused by a store-layer tautology
- Andrew Ooo — Orca Review: The IDE Built for Parallel Coding Agents (resource footprint and parallel semantic-conflict observations cited from this review)
- Augment Code — 9 Open-Source Agent Orchestrators for AI Coding (2026)