Agentic orchestrator for parallel coding agents — plans tasks, spawns agents, and autonomously handles CI fixes, merge conflicts, and code reviews.
Go to file
Ashish Huddar d60c49f5fb
fix(spawn): stop sending branch on spawn, render API errors, wire worker name (#171)
* fix(spawn): stop sending branch on spawn, render API errors, wire worker name

Three spawn-modal bugs, re-landed from the closed redesign branch (#156):

- createTask no longer sends `branch`: the API field names the session's
  NEW worktree branch, so submitting the modal's default ("main") made the
  daemon 409 with BRANCH_CHECKED_OUT_ELSEWHERE on every spawn. The "Based on"
  pane is informational — workers branch off the project's default branch in
  a fresh worktree.
- API errors render their envelope message instead of "[object Object]":
  openapi-fetch resolves non-2xx responses to a plain {code,error,message}
  object, not an Error; new apiErrorMessage unwraps it (message + code).
- The "Worker name" field is actually used: after spawn, a best-effort
  PATCH rename sets the displayName (a failed rename must not look like a
  failed spawn — the worker is already running).

Also revives the renderer test suite, which made these tests (and 7 of 9
suite files) impossible to run on main:

- vitest.config.ts re-exports vite.renderer.config so `vitest run` actually
  loads the jsdom environment + setup file. Forge's per-target
  vite.*.config.ts names are invisible to vitest, so the existing `test`
  block was dead config and every DOM-touching test died on
  "window is not defined".
- vite.renderer.config.ts imports defineConfig from vitest/config so its
  `test` key typechecks.
- routeTree.gen.ts + the session route are regenerated by the pinned
  @tanstack/router-plugin (it runs as part of loading the renderer config;
  the checked-in tree predated the installed plugin version and its route-ID
  drift caused 3 of main's 7 typecheck errors).
- App.test.tsx wraps App in TooltipProvider, mirroring routes/__root.tsx.

Frontend: 9/9 test files, 99/99 tests pass (was 2/9 files). Typecheck is
down from 7 errors to 3 — the survivors (forge.config notarize/maker types,
update-electron-app call signature) predate this branch and are untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* chore: format with prettier [skip ci]

* fix(gitworktree): base new session branches on the local default branch when no remote exists

Re-lands the remoteless fallback from the closed redesign branch (archived
in 641b712). Creating a session worktree resolved the base for a NEW branch
only via the remote-tracking ref (origin/<defaultBranch>), so a registered
repo with no remote failed every spawn with BRANCH_NOT_FETCHED — an error
that misleadingly names the new session branch and suggests `git fetch`,
which is impossible without a remote.

refs/heads/<defaultBranch> now follows origin/<defaultBranch> in the
candidate list: remote-tracking still wins whenever it exists, and a
remoteless repo bases session branches on its local default branch.

Verified live: a plain `git init` repo (no remote) that previously failed
now spawns, and the integration suite covers it
(TestWorkspaceIntegrationCreateInRemotelessRepo).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-11 12:13:30 +05:30
.github/workflows feat(frontend): scaffold for frontend with complimentary backend changes (#168) 2026-06-10 11:40:17 +05:30
backend fix(spawn): stop sending branch on spawn, render API errors, wire worker name (#171) 2026-06-11 12:13:30 +05:30
docs fix(sessions): stop AO hook files from making every worktree permanently dirty (#169) 2026-06-11 11:06:45 +05:30
frontend fix(spawn): stop sending branch on spawn, render API errors, wire worker name (#171) 2026-06-11 12:13:30 +05:30
test/cli chore: add prettier config and CI auto-formatter (#166) 2026-06-10 09:09:17 +05:30
.envrc Add agent adapters and wire per-session agents into the session manager (#65) 2026-06-02 16:51:32 +05:30
.gitignore feat(frontend): scaffold for frontend with complimentary backend changes (#168) 2026-06-10 11:40:17 +05:30
.prettierignore chore: add prettier config and CI auto-formatter (#166) 2026-06-10 09:09:17 +05:30
.prettierrc chore: add prettier config and CI auto-formatter (#166) 2026-06-10 09:09:17 +05:30
AGENTS.md chore: add prettier config and CI auto-formatter (#166) 2026-06-10 09:09:17 +05:30
CLAUDE.md feat(frontend): scaffold for frontend with complimentary backend changes (#168) 2026-06-10 11:40:17 +05:30
DESIGN.md feat(frontend): scaffold for frontend with complimentary backend changes (#168) 2026-06-10 11:40:17 +05:30
README.md chore: add prettier config and CI auto-formatter (#166) 2026-06-10 09:09:17 +05:30
flake.lock Add agent adapters and wire per-session agents into the session manager (#65) 2026-06-02 16:51:32 +05:30
flake.nix Add agent adapters and wire per-session agents into the session manager (#65) 2026-06-02 16:51:32 +05:30
package-lock.json feat: ao session claim-pr + spawn --claim-pr wiring (#101) 2026-06-06 00:01:03 +05:30
package.json chore: add prettier config and CI auto-formatter (#166) 2026-06-10 09:09:17 +05:30

README.md

ReverbCode

A Go-backed agent orchestration daemon for supervising parallel coding-agent
sessions, with an ao CLI today and an Electron supervisor planned. The Go
module and packages remain agent-orchestrator; "ReverbCode" is the public name.

See docs/architecture.md for the backend mental model
and AGENTS.md for the contributor / worker contract.

What's shipped today

  • Loopback-only HTTP daemon (backend/internal/httpd) controlling projects,
    sessions, orchestrators, and hook callbacks over 127.0.0.1.
  • ao Cobra CLI (backend/cmd/ao) — a thin client over the daemon for daemon
    control, project/session/orchestrator management, and worker spawning.
  • Worker/orchestrator spawn into isolated git worktree workspaces, launched
    inside a zellij runtime adapter.
  • Live per-PR observation via the provider-neutral SCM observer
    (backend/internal/observe/scm/): polling loop, ETag guards, semantic
    diffing, CI/check/review-thread tracking, and lifecycle nudges for CI
    failures, review feedback, and merge conflicts.
  • Agent adapters under backend/internal/adapters/agent/: claude-code,
    codex, opencode, grok, cursor, qwen, copilot, kimi, plus
    shared activity-dispatch / hook utilities.
  • SQLite store (backend/internal/storage/sqlite/) with sqlc-generated
    queries, DB-triggered change-data-capture into change_log, and a CDC
    poller/broadcaster (backend/internal/cdc/) feeding in-process subscribers
    and an SSE replay endpoint.
  • Session lifecycle manager + reaper (backend/internal/lifecycle/,
    backend/internal/observe/reaper/): runtime/activity/PR facts reduced into
    the small durable session state, display status derived at read time.

Quick start

Requirements: Go 1.25+, zellij on PATH for the
runtime adapter, and gh (or GITHUB_TOKEN) if you want the SCM observer to
authenticate against GitHub. The SQLite driver is the pure-Go
modernc.org/sqlite — no system SQLite library is required.

cd backend
go build -o /tmp/ao ./cmd/ao

# Start the daemon and wait for /readyz.
/tmp/ao start

# Register a local git repo as a project. The id defaults to the lowercased
# base of --path; pass --id explicitly when the directory name doesn't match.
/tmp/ao project add --path /path/to/your/repo --id your-repo --name your-repo

# Spawn a worker session running the default agent.
/tmp/ao spawn --project your-repo --prompt "Refactor the auth module"

# Inspect what's running.
/tmp/ao status
/tmp/ao session ls

CLI surface

The CLI is intentionally thin: every product command resolves to a daemon HTTP
route. Run ao <command> --help for the authoritative flag shape; the table
below groups what's on main today.

Lane Command Purpose
Daemon ao start Start the daemon in the background and wait for /readyz.
Daemon ao stop Graceful shutdown via loopback POST /shutdown.
Daemon ao status Report PID/port/health/readiness from running.json.
Daemon ao daemon Hidden internal entrypoint used by ao start.
Project ao project add Register a local git repo as a project.
Project ao project ls List registered projects.
Project ao project get <id> Fetch one project.
Project ao project rm <id> Remove a project.
Session ao spawn Spawn a worker session in a registered project.
Session ao session ls List sessions (filter by project, include terminated).
Session ao session get <id> Fetch one session.
Session ao session kill <id> Terminate a session.
Session ao session rename <id> <name> Rename a session.
Session ao session restore <id> Relaunch a terminated session.
Session ao session cleanup Reclaim eligible workspaces for terminated sessions.
Session ao session claim-pr <session> <pr> Attach an existing PR to a session.
Orchestrator ao orchestrator ls List orchestrator sessions.
Messaging ao send Send a message to a running agent session.
Utility ao doctor Local health checks (config, data dir, DB, git, zellij).
Utility ao completion <shell> Generate bash/zsh/fish/powershell completions.
Utility ao version Print build metadata.
Internal ao hooks <agent> <event> Hidden adapter hook callback.

See docs/cli/ for the daemon-control intent and command shape.

Configuration

All configuration is env-driven; the daemon takes no config file. The bind
host is hard-coded to 127.0.0.1 — the daemon has no auth, CORS, or TLS, and
exposing it beyond loopback would be a security regression.

Var Default Purpose
AO_PORT 3001 Bind port; daemon fails fast if taken.
AO_REQUEST_TIMEOUT 60s Per-request timeout (Go duration).
AO_SHUTDOWN_TIMEOUT 10s Graceful-shutdown hard cap.
AO_RUN_FILE <UserConfigDir>/agent-orchestrator/running.json PID + port handshake path.
AO_DATA_DIR <UserConfigDir>/agent-orchestrator/data SQLite DB, WAL files, managed state.
AO_AGENT claude-code Default agent adapter id used by ao spawn.
AO_SESSION_ID (unset) Set inside spawned sessions; read by ao send and ao hooks.
GITHUB_TOKEN (unset) Used by the GitHub SCM and tracker adapters. Falls back to gh auth token.

Health check:

curl localhost:3001/healthz
curl localhost:3001/readyz

Architecture

The daemon is a long-running supervisor. Adapters observe external facts (PR
state, agent activity, runtime liveness); the lifecycle manager reduces those
into a small set of durable session facts (activity_state, is_terminated,
PR rows). Display status is derived from those facts at read time — it is
never stored. SQLite triggers append every user-visible change to change_log,
and the CDC poller broadcasts those events to in-process subscribers and an
SSE stream.

Full mental model and load-bearing rules: docs/architecture.md.
Package-by-package ownership: docs/backend-code-structure.md.

Testing

The local gate is the backend Go build and race-enabled test suite:

cd backend && go build ./... && go test -race ./...

GitHub Actions is the authoritative pre-merge gate; mirror its commands here
when in doubt. See AGENTS.md for the regen workflow when
touching the daemon API surface (npm run sqlc, npm run api).

Status and roadmap

Current main ships the CLI surface above, the SCM observer end-to-end
(issues #75,
#108,
#109), the agent
adapter platform, and the CDC pipeline with SSE replay. The Tracker observer
(#112) and live
pr_* event consumers (#110)
are in flight. The Electron supervisor under frontend/ is still a placeholder
shell — daemon logic stays in the Go backend.

Tracking milestone:
rewrite on GitHub.

Contributing

Repo layout and the worker contract live in AGENTS.md. Keep
changes surgical, follow the package boundaries documented in
docs/backend-code-structure.md, and prefer
adding daemon HTTP routes over leaking storage / runtime into the CLI.