Skip to content

Repository files navigation

🎼 Maestro

Claude is the master. Codex is the hands.

An orchestrator/implementer loop for Claude Code — Opus plans, debates, and reviews; Codex writes the code and proves it works.

License Claude Codex Bash Node Platform Tests No API key


The orchestrator thinks through what and how — architecture, plans, reviews, talking to you — but never touches source files. Codex, dispatched through a watchdog, writes the code, runs the verification, and returns a structured result: evidence, or the questions it needs answered.

It runs on your existing ChatGPT Plus / Codex login. No OpenAI API key, no proxy, no second subscription.

git clone https://github.com/Pythoughts-labs/maestro.git
cd maestro && node install.mjs

Restart Claude Code (plain claude) afterward so the rules and hooks load.


Maestro in action

Run node install.mjs, restart Claude Code in your project terminal, then ask Claude directly to use Codex for the task. That's it — no Maestro skill or slash command is needed.

Maestro dispatching a plan to Codex and monitoring the implementer loop Maestro relaying Codex questions and tracking verified implementation progress

Why

Some teams trust Claude's judgment more than its patience, and Codex's typing more than its plans. Maestro is for that split: the model you want making decisions (Opus 5 / Fable 5) holds the plan and the final review; the model you want grinding through edits holds the pen.

The loop also fixes what breaks in practice — Codex hanging mid-task, an implementer that guesses when the plan is ambiguous, a review that rubber-stamps its own plan.

How it works

session start → pick model + debate/write efforts (or keep a complete pin)        ← the setup
your prompt
   → design fork or murky bug? Opus DEBATES Codex (read-only, transcript-carried)  ← the argument
       grill and be grilled, until CONVERGED / ESCALATE / 6-turn cap
   → Opus plans it: objective, files, steps, constraints, verification commands   ← the brain
   → the AUTONOMOUS LOOP runs Codex (write-enabled) until verified:               ← the hands
       dispatch → RESULT → local re-verification → failure evidence re-dispatched
       exits only: VERIFIED_DONE / NEEDS_ANSWERS / BLOCKED / STUCK-at-cap
   → questions relayed back to you, answers re-dispatched                          ← the loop
   → stuck with unclear root cause? the attempts log goes to the debate table
   → Opus reviews the actual diff (SHIP / FIX-FIRST / RETHINK)
   → done

Exit codes

The loop never ends in prose. Every run finishes on a machine-readable state:

Code State What it means What you do
0 VERIFIED_DONE Plan executed and the verify command passed locally Review the diff — a claim is not proof
10 NEEDS_ANSWERS Codex hit real ambiguity and stopped instead of guessing Answer the QUESTIONS: block, re-run
11 BLOCKED Missing access, a destructive step, lease contention, or an unconfirmed cancelled writer Surface it; never improvise around it
12 STUCK Hit the iteration cap without verification Read the attempts log, re-plan — don't just raise the cap

These codes describe implementer-loop.sh. A directly invoked single-shot watchdog uses rc 125 plus MAESTRO_FINAL: WATCHDOG POISONED when cancellation leaves quiescence unconfirmed; the outer loop translates that condition to BLOCKED/11.

Write contention waits without arrival ordering only while the current lease has a confirmed release path. MAESTRO_LOCK_WAIT_SEC caps the wait (default 300 seconds; 0 disables it), and MAESTRO_LOCK_WAIT_POLL_SEC controls polling (default 5 seconds, minimum 1); invalid values disable waiting.

Foreground write supervisors update a separate lease heartbeat every MAESTRO_LOCK_HEARTBEAT_INTERVAL_SEC (default 20 seconds, minimum 1; invalid values use 20). MAESTRO_LOCK_HEARTBEAT_STALE_SEC controls when a missed heartbeat is reported (default 90 seconds; 0 disables staleness reporting; invalid values use 90). A stale heartbeat is only a recovery candidate: --clear-lease still refuses while the recorded owner process is alive or unidentifiable, or any repository-global companion writer is visible.

MAESTRO_MAX_DISPATCH_SEC is a hard ceiling: unset write jobs get 2400 seconds and read-only discussions get 1200; an explicit valid value is used exactly, while invalid input warns and falls back to 1200. Startup consumes this budget, poll sleeps are clipped to the nearest deadline, and one halfway warning continues the same job without claiming progress or creating a checkpoint. Idle time uses elapsed monotonic time rather than configured poll counts. --max-idle and --poll must be positive integers and are rejected before any lease or task starts. The local verifier has its own process-group deadline (MAESTRO_VERIFY_TIMEOUT_SEC, default 900 seconds), and MAESTRO_COMPANION_TIMEOUT_SEC bounds each companion call (default 120 seconds). Four consecutive empty or malformed statuses, or the hard ceiling during status loss, cancel and fail closed. Read-only status loss consumes its configured retry allowance; idle/deadline cancellation does not. A write cancellation—including one reported externally by the companion—poisons and retains the lease, ends the loop as BLOCKED, emits UNREPORTED_PARTIAL at the hard ceiling, and never starts a replacement writer. Once no Codex job is writing, recover with bash hooks/implementer-loop.sh --clear-lease (installed: bash ~/.claude/hooks/implementer-loop.sh --clear-lease). A metadata-less lease younger than five seconds is treated as an owner still initializing, not an orphan to clear.

On every SessionStart source, the hook appends a validated MAESTRO_SESSION_ID export to $CLAUDE_ENV_FILE. The value is attribution only: the token, PID/process-start identity, and companion job liveness remain the ownership checks. A missing or invalid value is recorded as unknown; the session appears in lease metadata, contention/poison messages, and provenance records.

Components

Installed under ~/.claude, plus one shared library they source.

File Role
session-start.mjs Opens each session with model plus separate debate/write effort picks. Resumed sessions get a status line instead of a re-ask.
codex-model-select.sh Serializes concurrent selectors and transactionally pins model plus debate/implementation effort, preserving config modes and top-level TOML scope.
codex-mcp-check.sh Shows exactly which MCP servers your background Codex jobs inherit, env keys masked.
implementer-loop.sh The autonomous heart: dispatch → parse RESULT → re-verify locally → feed failure evidence back in. Bounded by --max-iters and a verifier deadline.
discussion-loop.sh Read-only debate with collision-resistant workspace identity, private transcripts, sidecar turn state, and stale-lock recovery.
implementer-watchdog.sh Single dispatch with --write; idle or absolute cancellation poisons and retains its write lease.
orchestrator-inject.mjs Resets the direct-edit flag per task; states the loop only when the prompt carries a code/design signal.
orchestrator-gate.mjs Blocks the orchestrator's Edit/Write/MultiEdit on source files.
lib-companion.sh Shared library: the write lease, provenance detection, companion resolution.

Plus the behavioral specs: orchestrator-implementer.md (read every session) and coding-discipline.md (useful standalone).

Four design choices that matter

Grilling is structured, not vibes

Multi-turn debate between models fails in two known ways: endless courteous loops, and one side caving to sound cooperative. A mandatory stance line (AGREE / PUSHBACK / ALTERNATIVE / REFRAME) forces Codex to commit each turn; an AGREE still has to name the assumption most likely to be wrong; REFRAME gives it explicit license to reject the question itself. A 6-turn cap forces every debate to land on CONVERGED with stated assumptions, or ESCALATE to you with both cases intact.

The output isn't lost either — the converged design, the losing alternatives, and their rejection reasons become the Decisions section of the plan, so the implementer sees the debate it wasn't part of.

Write access is scoped by contract, reviewed by diff

Codex really edits your tree — that's the point. It may touch only the files the plan names, must report every file it changed, and nothing it does is believed until the orchestrator re-reads the actual git diff against the stated goal. Before its first edit, it must run git status --short, report the pre-existing dirty paths, preserve them, and confirm out-of-scope paths stayed untouched; this is an obligation and report, never a dirty-tree gate. Fixes never get silently patched by the orchestrator; they go back to Codex so the diff stays single-author.

Questions are a first-class channel, not a failure

A background job can't ask interactively, so the contract gives it a structured way to stop instead of guess: RESULT: NEEDS_ANSWERS plus a numbered QUESTIONS: block. An implementer that guesses is worse than one that asks.

The final review is mandatory, and honest about being same-vendor

The orchestrator reviews its own plan's execution. The spec says so out loud and compensates: fresh-eyes diff read, re-run the cheap verification yourself, check for scope creep, open with SHIP / FIX-FIRST / RETHINK. If you want a cross-vendor review, pair Maestro with a separate read-only advisor for the review step only.

Research

Codex's built-in web search is disabled — that was the thing that kept hanging. Its configured MCP servers (tavily, context7, …) stay available to both loops for version-sensitive facts.

Research flows plan-first: the orchestrator pre-researches and embeds facts before dispatching, and Codex verifies only what turns out version-sensitive — capped at 2 lookups per run, no retries on stall. Verified facts come back labeled verified via <mcp>: <fact>, so the reviewer can trust them over either model's training data.

Tests

bash tests/run.sh

Sixteen suites cover leases, liveness, installation ownership, model selection, gate authorization, discussions, provenance, and nested process cleanup. They drive real entry points end to end—acquiring leases, mutating repositories, signalling supervisors, and installing into isolated homes—rather than replacing lifecycle behavior with mocks.

They are slow on purpose: several suites wait on real lease timeouts.

Requirements

  • Claude Code (ships Node), with Opus 5 (claude-opus-5) or Fable 5 (claude-fable-5) as the session model — a /model choice, not a config here.
  • The Codex plugin/plugin install codex@openai-codex inside Claude Code.
  • A ChatGPT Plus / Codex logincodex login. Not an API key.
  • Optional, for --with-workflow: /plugin install ralph-loop@claude-plugins-official.

Install

git clone https://github.com/Pythoughts-labs/maestro.git
cd maestro
node install.mjs

Or hand the repo to Claude Code and say: "run node install.mjs in this repo."

The installer validates options, settings, and every managed destination before changing anything. An ownership manifest records installed bytes; known-owned files update atomically, byte-identical reinstalls preserve file identity, and divergent or same-named user files cause a refusal instead of an overwrite. Ordinary late failures roll back every path published by that run; abrupt termination still leaves byte-atomic files that a rerun can reconcile. Settings/config backups refresh immediately before each merge, only a true top-level TOML web_search key satisfies the hang guard, disabled session prompts stay disabled on reinstall, and hook registrations carry exact Maestro markers rather than filename substring guesses.

Optional: the workflow rule
node install.mjs --with-workflow

Adds workflow.md plus the ralph-protocol skill it defers to: a bounded execution loop on top of the orchestration loop. Plans live in tasks/todo.md, a verifier hierarchy decides what counts as proof, and long jobs run through /ralph-loop capped at 8 iterations with an explicit stop line. The Ralph loop drives the orchestration; the implementer inside it is still Codex.

Uninstall

node uninstall.mjs

Removes only files whose bytes still match the ownership manifest, strips only exact Maestro-marked commands from settings.json, and clears private direct-edit authorization markers. Modified hooks, rules, skills, foreign similarly named commands, and backups remain untouched; uninstall ownership does not depend on the current checkout version.

Limits

Stated plainly, because a tool that overstates its guarantees is worse than one that has fewer.

  • The gate is a guardrail, not a boundary. It is registered for Edit|Write|MultiEdit only. Bash, MCP tools, and Workflow/Agent are not matched, so a redirect or sed -i reaches the tree untouched. Authorization requires a validated session, lives under a private ~/.maestro/direct-edit directory with owner/mode/content checks, ignores the legacy forgeable /tmp marker path, and is revoked by malformed prompt payloads. Scratch/non-code exemptions use canonical existing targets or parents so symlinks cannot change classification; executable files remain gated regardless of extension. The orchestrator not writing source remains a discipline, not a sandbox.
  • Provenance detection reports, it never attributes. Each write-lease acquisition hashes actual materialized bytes with Git filters disabled, using Git itself rather than a platform-specific digest utility, across healthy worktrees, initialized submodules, and non-ignored nested repositories. One prunable worktree degrades independently. Baseline records publish before lease handoff, and log publication atomically replaces rather than follows a symlink. A mismatch names an interval—never a writer—and ignored paths remain out of scope on cost grounds. MAESTRO_DIGEST_TIMEOUT_SEC bounds each snapshot (default 120); timeout degrades that interval to unavailable and disables comparison rather than blocking dispatch. It is not an adversarial control.
  • Cancellation terminality is upstream. The companion does not expose the brokered turn's terminal event to Maestro's shell. A cancelled write may therefore leave unreported edits, so Maestro stops and retains the lease instead of guessing that the turn is quiescent.
  • Same-vendor review. The orchestrator reviews its own plan's execution.
  • Model pin depends on config being honored for debate max/ultra. Model and wrapper-supported efforts are explicit per task. Debate max/ultra rely on the top-level Codex config because the companion cannot express them; implementation therefore rejects max/ultra instead of silently substituting another tier. A fresh unpinned install cannot dispatch until model and effort values are selected.
  • Plugin flag drift. Write dispatches preflight the companion's global --help and refuse only when it describes task without --write; inconclusive help (empty, error, or no synopsis) proceeds rather than blocking work.
  • Windows. The watchdog is a bash script — run Claude Code from Git Bash or WSL.
  • Codex on Plus. The implementer model is whatever your ChatGPT plan's Codex can reach.

License

MIT. See LICENSE. © elkaix / Pythoughts Labs.

About

Claude is the master, Codex is the hands — an orchestrator/implementer loop for Claude Code. Opus plans, debates, and reviews; Codex writes the code and proves it works. Runs on your ChatGPT login, no API key.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages