Skip to content

About

Separation of powers for coding agents. An AGENTS.md and Claude Code subagents that split work across an Orchestrator, an Executor, a Reviewer whose blocking verdicts cannot be reinterpreted away, and a Senior Reviewer for escalated disputes. No agent grades its own homework. Built for correctness-critical projects with long-running experiments.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

 

History

2 Commits

Folders and files

Repository files navigation

Four Powers

Separation of powers for coding agents. Plan, execute, review, and arbitrate are held by four different agents, and no agent grades its own homework.

English | 简体中文 | 日本語

An AGENTS.md and three Claude Code subagents that split agent-driven engineering work across four roles: one agent plans, one implements, one reviews, and a senior reviewer is called in only when the review itself is disputed.

It is built for long, evidence-heavy work such as performance optimization, numerical code, porting a reference implementation, or multi-week debugging. On that kind of work a single agent tends to mark its own homework, drift toward whatever hotspot it saw last, and turn "measured once" into "established fact".

Is this for you? This workflow trades speed for confidence. It suits projects where correctness matters most and progress depends on long experiments. It is a poor fit when fast feedback matters most. See When to use it.

.
├── AGENTS.md                      # Orchestrator rules + shared workflow for all roles
├── .claude/agents/
│   ├── executor.md                # does bounded work, returns evidence
│   ├── reviewer.md                # frequent adversarial review of local claims
│   └── senior_reviewer.md         # rare, escalation-only structural review
├── tools/session/
│   ├── log_event.py               # archive one agent_log entry + index row in one command
│   ├── wait_run.py                # wait on a long job without sleep-polling
│   └── test_session_tools.sh      # tests for both tools
├── docs/notes/
│   └── check_project_state.py     # budget check for project_state.md
└── .githooks/
    └── pre-commit                 # runs the budget check on the staged state file

The four roles

Role Who Does Does not
Orchestrator Your main agent session Keeps the global picture and project state, splits the work, integrates results, decides the next action Do large implementation it could delegate
Executor executor subagent Implements, debugs, tests, benchmarks; returns a fixed-format evidence report Redefine goals, change architecture, expand scope
Reviewer reviewer subagent Tries to falsify claims at milestones; returns a verdict Edit files, write plans
Senior Reviewer senior_reviewer subagent Settles escalated disputes; checks whether the problem framing is still right Act as a routine second reviewer

Normal flow:

Orchestrator → Executor → Orchestrator → Reviewer → Orchestrator → (next task)

Escalated flow (only on specific triggers):

… → Orchestrator → Senior Reviewer → Orchestrator → …

Subagents never talk to each other directly. All coordination goes through the Orchestrator.

Usage

Claude Code

  1. Copy .claude/agents/ into your project (or into ~/.claude/agents/ to use it in every project).
  2. Copy AGENTS.md to your project root, together with tools/, docs/notes/check_project_state.py and .githooks/ (see Tools). Enable the hook once per clone with git config core.hooksPath .githooks.
  3. Start a session and give the main agent a goal. It acts as the Orchestrator and calls the subagents itself. You can also name one directly, e.g. "ask the reviewer to check whether this benchmark supports the speedup claim".

The agent frontmatter sets model and effort (Executor: sonnet/high, Reviewer: opus/medium, Senior Reviewer: opus/high). Change them to fit your budget. Reviewers get read-only tools plus Bash, so they can run checks but not edit files.

Codex, OpenCode, and other tools

AGENTS.md works as-is in any tool that reads it. The three agent files are plain Markdown prompts with YAML frontmatter. Move them to your tool's agent directory and adapt the frontmatter fields.

Adapting it to your project

AGENTS.md assumes a few conventions. Keep the ones you want and delete the rest:

  • docs/notes/project_state.md + project_log.md: current state vs. append-only history (see below).
  • workspace/ with UUID-suffixed Markdown files: lets parallel sessions and agents write reports without filename collisions.
  • agent_log/ session archive: a verbatim, indexed record of every delegation, reply, and verdict.
  • references/: put upstream source code, specs, or papers here if your work must match a reference implementation.

The domain words (CPU/GPU, numerical, scientific invariant) come from performance and scientific computing. The workflow itself is not tied to that domain.

Tools

Python 3 standard library only, no dependencies. Tested on Linux; log_event.py uses fcntl, so it needs a POSIX system. Run the tests with bash tools/session/test_session_tools.sh.

tools/session/log_event.py: archive one log entry

Agents write each reply to a file. One command then archives it:

python3 tools/session/log_event.py --session <SESSION_DIR> \
  --agent reviewer --from reply.md \
  --task CACHE-INVALIDATION-ROOT-CAUSE_T1 --event R1 \
  --type review --verdict "REQUEST CHANGES" --summary "Benchmark does not match workloads"
  • Never rewrites evidence. It validates everything first, never overwrites a file, copies byte for byte and checks the copy, and adds the index row only after the copy succeeds.
  • Safe in parallel. It holds a lock on the session directory from choosing the sequence number until the index row is written.
  • Optional extras. --init creates agent_log/ and the index for a new session. --status updates the Last event: line of the session's live_status_<uuid>.md. --check-state runs the project_state check. --commit MSG commits only the named paths, never anything else that is staged. --dry-run shows the plan without changing anything.
  • If a later step fails, earlier steps stay in place, and the output lists what completed and what did not.

tools/session/wait_run.py: wait on long jobs

Left alone, agents tend to wait on long experiments with sleep 30; tail log loops. That burns tool calls and context, and sometimes the agent hands back while the job is still running. wait_run.py blocks until something meaningful happens and then prints a short report: process state, exit code, progress JSON, matched error lines, and the end of the log.

mkdir run1; ( <cmd>; echo $? > run1/exit_code ) > run1/run.log 2>&1 & echo $!
python3 tools/session/wait_run.py --pid <PID> --log run1/run.log --exit-file run1/exit_code \
  --progress run1/progress.json --stall 900
Reason Exit code Meaning
EXITED 0 Job ended. The reason line carries the job's own exit status, so EXITED is not success.
PHASE_CHANGED 10 The progress JSON moved to a new step, phase, or stage
TIMEOUT 11 --max reached and the job is still running. Call it again.
STALLED 20 Neither the progress file nor the log changed for --stall seconds
ERROR_SEEN 21 A new log line matched --error-regex

It is read-only: it never starts, signals, or kills a process. --max is capped at 570 s so each call fits inside Claude Code's 10-minute Bash timeout.

docs/notes/check_project_state.py + .githooks/pre-commit

These enforce the project_state.md budget: at most 250 lines and 30 KB, a short "Next highest-value action" section, and none of the log-style patterns that show the file is turning into a history (dated bullets, strikethrough superseded text, "Addendum" paragraphs). The hook checks the staged version and fails closed. Edit the constants at the top of the script to change the budget.

Design principles

1. Separate the person who acts from the person who judges. An agent that writes the code and also decides it is correct will usually say yes. The Executor has no authority over project decisions. The Reviewer cannot edit files. The Orchestrator decides, but cannot override a blocking review just by reinterpreting it.

2. Reviews are real gates, not suggestions. REQUEST CHANGES blocks acceptance. Only two things can override it: new evidence that resolves the objection, or an explicit Senior Reviewer decision. The usual failure is that a blocking objection gets quietly rewritten as a "caveat", and this rule exists to stop it.

3. Review at decision boundaries, not after every edit. Typical gates are: accepting a root cause, finishing a non-trivial implementation, interpreting a benchmark, an architectural decision, or declaring a phase done. Reviewing after every tool call wastes tokens and teaches the system to ignore reviews.

4. Two tiers of review, with explicit escalation triggers. The Reviewer asks: "Is this step supported well enough to accept?" The Senior Reviewer asks: "Are we solving the right problem, under the right assumptions?" Escalation has a written list of triggers: the same blocker after one correction cycle, unresolved load-bearing disagreement, high-blast-radius changes, conflicting trusted measurements, or performance versus correctness conflicts. Without that list, the senior tier either never gets used or turns into a second routine reviewer.

5. Separate evidence strength from implementation status. "Implemented", "reviewed", "measured once", and "accepted as default" are different states. Every role keeps observation, inference, hypothesis, and conclusion apart. A hypothesis does not become a fact without evidence, and an overturned claim is removed from the project state explicitly.

6. First principles, with guardrails. The Reviewer asks what work the problem requires and what work only the current implementation adds (Potential Waste = Current Work − Necessary Work). It also asks: if this component cost nothing, how much faster would the whole system be? Guardrails prevent this from becoming "redesign everything". A finding must name the assumption it challenges, why it matters, and the smallest experiment that would settle it.

7. Falsify, don't confirm. Reviewers are asked for the smallest discriminating experiment, never generic "more testing". Every Senior Reviewer decision has to state what evidence would reverse it.

8. Protect the North Star from local optima. project_state.md holds only the current state: goal, phase, established facts, open hypotheses, next highest-value action. It has a size budget, and superseded content moves to an append-only project_log.md. Each session starts by asking whether the unfinished task is still the most valuable one, not by carrying on from where the last session stopped.

9. Bounded tasks, fixed return contracts. Each delegation has a clear objective and completion condition. The Executor always returns Result / Changes / Verification / Evidence / Uncertainty / Escalation Signal. The Reviewer and Senior Reviewer end with a verdict from a fixed list. The output is predictable enough for the Orchestrator to integrate and for you to skim.

10. Reuse before building; minimal coherent change. The Executor goes through a reuse ladder (project code → existing deps → stdlib → mature packages → custom code), adds no dependency without approval, and follows the Single Level of Abstraction Principle in any code it writes. The Reviewer checks that principle at implementation gates.

11. The log is passive, and it is also a measurement. Every delegation, reply, and verdict is archived verbatim in agent_log/, with one index row per event (Task ID, rework event R<N> / evidence event E<N>, verdict). There are two reasons:

  • Auditability. When a conclusion is questioned weeks later, the exact prompt the Executor received and the exact objection the Reviewer raised are still there. Nobody has to trust a summary.
  • Measurement. The index records how the system behaved, not just what it produced, so you can compare configurations. See Evaluating model choices.

Logging must never change behavior. No extra reasoning is produced for the log, and no summaries replace the original text. If agents knew the log was a scorecard, they would write for the score, and the log would stop measuring anything.

12. Plain, clear communication. All roles follow a short clarity guide based on ASD-STE100 (Simplified Technical English): answer first, state the scope, keep each result next to its limits, and fix the substance when feedback shows a real problem, not just the wording.

Evaluating model choices

Each role's model is set in its agent frontmatter, so you can try different assignments, such as a cheaper Executor or a stronger Reviewer. The agent_log/ index shows whether an assignment works for your project.

Signal in the index Likely meaning
Many tasks reach R3 or later The Executor ↔ Reviewer loop is not converging
Frequent Senior Reviewer escalations Disputes that the two lower roles cannot settle
Senior Reviewer often returns UPHOLD REVIEWER Mainly an Executor problem: it keeps failing the same objection
Senior Reviewer often returns OVERTURN REVIEWER Mainly a Reviewer problem: it blocks work that is actually fine
Senior Reviewer often returns REFRAME Mainly an Orchestrator problem: tasks are framed or split badly
Many E<N> events with no decision Experiments are not discriminating; check how the Orchestrator and Reviewer design them

In short, if a new model assignment produces many R3s and escalations, those models probably do not suit this work, and the Senior Reviewer's verdicts show which role to change.

Three caveats:

  • Fewer rework rounds is not automatically better. A weaker Reviewer may approve flawed work, which lowers the R count while letting errors through. Also count approved conclusions that were overturned later (they appear in project_log.md).
  • Compare on similar work. A hard phase produces more rework under any model. Compare configurations on the same or similar tasks.
  • Change one role at a time. If you swap the models for two roles at once, you cannot tell which change caused the difference.

When to use it

Every milestone goes through an extra delegation and review round. That adds latency and tokens. It is worth paying for only when a wrong conclusion costs more than the review does.

Good fit: correctness comes first, and progress depends on long-running experiments.

  • A single experiment or benchmark takes minutes to hours, so acting on a wrong conclusion wastes a lot of compute and time.
  • Errors are subtle and easy to miss: numerical drift, measurement artifacts, a speedup that came from doing less work.
  • The project lasts days or weeks, so a small wrong claim gets built on many times before anyone notices.
  • You need an auditable record of why each decision was made.

Poor fit: fast feedback matters more than certainty.

  • UI work, prototypes, and exploratory hacking, where you look at the result yourself in seconds.
  • Small fixes and one-off scripts, where a test run or a quick read already shows whether it works.
  • Tight edit–run loops, where each review round would cost more than simply trying again.

You can also use it for only part of a project. Turn it on for the correctness-critical parts and work directly everywhere else.

License

MIT

About

Separation of powers for coding agents. An AGENTS.md and Claude Code subagents that split work across an Orchestrator, an Executor, a Reviewer whose blocking verdicts cannot be reinterpreted away, and a Senior Reviewer for escalated disputes. No agent grades its own homework. Built for correctness-critical projects with long-running experiments.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages