Parallel Planner with Adversarial Review
MIT↓ 2 downloadsPlans parallelizable GitHub issues, implements each on its own branch in parallel, then runs an adversarial Codex review with a per-branch fix loop before merging.
Topology
Disclosures
Everything below runs on your machine or inside the sandbox when you use this workflow. Mismatches between these declarations and the actual code block publishing.
Host hooks
Commands executed on YOUR host machine by Sandcastle lifecycle hooks.
cp .env.example .env
Sandbox hooks
Commands executed inside the sandbox container.
npm install
Network access
Runs `gh` inside the sandbox to list and read issues labelled `Sandcastle` on the current repository.
Shell expansion
Prompt files contain !`command` blocks — the agent CLI executes these commands at prompt-load time. They are highlighted amber in the prompt files below.
Files
Diff vs the stock Sandcastle 0.12.0 template Dockerfile — green lines were added by the author, red lines were removed from stock.
+# Sandbox image for the Parallel Planner with Adversarial Review workflow.+# Extends the stock Sandcastle template with the GitHub CLI (for the gh issue+# loop) and the Codex CLI (for the reviewer agent).FROM node:22-bookworm-# System dependencies.+# System dependencies used by the agents and the gh issue loop.RUN apt-get update && apt-get install -y --no-install-recommends \git \curl \jq \ca-certificates \&& rm -rf /var/lib/apt/lists/*-# Claude Code CLI (the agent runtime).-RUN npm install -g @anthropic-ai/claude-code+# GitHub CLI — used by the !`gh ...` shell-expansion blocks in the prompts.+RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \+ | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \+ && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \+ > /etc/apt/sources.list.d/github-cli.list \+ && apt-get update && apt-get install -y --no-install-recommends gh \+ && rm -rf /var/lib/apt/lists/*+# Agent CLIs: Claude Code (planner + implementers) and Codex (reviewer).+RUN npm install -g @anthropic-ai/claude-code @openai/codex+# Non-root agent user. `sandcastle docker build-image` aligns AGENT_UID/GID to# the host user via --build-arg to avoid permission errors on bind mounts.ARG AGENT_UID=1000ARG AGENT_GID=1000RUN groupadd --gid ${AGENT_GID} agent \&& useradd --uid ${AGENT_UID} --gid ${AGENT_GID} --create-home --shell /bin/bash agentUSER agentWORKDIR /workspace
Show full Dockerfile (highlighted)
# Sandbox image for the Parallel Planner with Adversarial Review workflow.
# Extends the stock Sandcastle template with the GitHub CLI (for the gh issue
# loop) and the Codex CLI (for the reviewer agent).
FROM node:22-bookworm
# System dependencies used by the agents and the gh issue loop.
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
jq \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# GitHub CLI — used by the !`gh ...` shell-expansion blocks in the prompts.
RUN curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
> /etc/apt/sources.list.d/github-cli.list \
&& apt-get update && apt-get install -y --no-install-recommends gh \
&& rm -rf /var/lib/apt/lists/*
# Agent CLIs: Claude Code (planner + implementers) and Codex (reviewer).
RUN npm install -g @anthropic-ai/claude-code @openai/codex
# Non-root agent user. `sandcastle docker build-image` aligns AGENT_UID/GID to
# the host user via --build-arg to avoid permission errors on bind mounts.
ARG AGENT_UID=1000
ARG AGENT_GID=1000
RUN groupadd --gid ${AGENT_GID} agent \
&& useradd --uid ${AGENT_UID} --gid ${AGENT_GID} --create-home --shell /bin/bash agent
USER agent
WORKDIR /workspace
# Auth for the Claude Code planner and implementers.
# Run `claude setup-token` on your host to generate a token.
CLAUDE_CODE_OAUTH_TOKEN=
# Auth for the Codex reviewer.
OPENAI_API_KEY=
# Optional: only needed for the gh issue loop on private repositories.
GITHUB_TOKEN=
Implement issue #{{ISSUE_NUMBER}}
You are working on branch {{SOURCE_BRANCH}}. Your work will be reviewed
against {{TARGET_BRANCH}}.
Here is the issue you must implement:
!gh issue view {{ISSUE_NUMBER}} --json title,body -q "\(.title)\n\n\(.body)"
Implement the change with small, focused commits. Add or update tests to cover the new behaviour, and make sure the existing test suite still passes.
When the implementation is complete and tests pass, output the exact line
<promise>COMPLETE</promise> and stop.
import { run, claudeCode, codex } from "@ai-hero/sandcastle";
import { docker } from "@ai-hero/sandcastle/sandboxes/docker";
const IMAGE = "sandcastle:parallel-review";
// 1. Plan — group the labelled issues into independent, parallelizable items.
// The host hook seeds .env; the sandbox hook installs dependencies.
const plan = await run({
name: "plan",
agent: claudeCode("claude-opus-4-8", { effort: "high" }),
sandbox: docker({ imageName: IMAGE }),
promptFile: ".sandcastle/plan-prompt.md",
hooks: {
host: {
onWorktreeReady: [{ command: "cp .env.example .env" }],
},
sandbox: {
onSandboxReady: [{ command: "npm install", timeoutMs: 300_000 }],
},
},
});
console.log(`Planner finished in ${plan.iterations.length} iteration(s).`);
// In a real run you would parse the plan output to discover the work items.
// For a deterministic package we implement a fixed set of issue numbers.
const issueNumbers = ["101", "102", "103"];
// 2. Implement — fan out one Sonnet implementer per work item, each on its own
// branch, all in parallel. Distinct branches make the fan-out safe.
await Promise.all(
issueNumbers.map((issue) =>
run({
name: `implement-${issue}`,
agent: claudeCode("claude-sonnet-4-6"),
sandbox: docker({ imageName: IMAGE }),
promptFile: ".sandcastle/implement-prompt.md",
promptArgs: { ISSUE_NUMBER: issue },
branchStrategy: { type: "branch", branch: `agent/issue-${issue}` },
maxIterations: 5,
hooks: {
sandbox: {
onSandboxReady: [{ command: "npm install", timeoutMs: 300_000 }],
},
},
}),
),
);
// 3. Review — an adversarial Codex reviewer runs on each branch with a fix
// loop, stopping when it approves the branch.
for (const issue of issueNumbers) {
const review = await run({
name: `review-${issue}`,
agent: codex("gpt-5.4", { effort: "high" }),
sandbox: docker({ imageName: IMAGE }),
promptFile: ".sandcastle/review-prompt.md",
promptArgs: { ISSUE_NUMBER: issue },
branchStrategy: { type: "branch", branch: `agent/issue-${issue}` },
maxIterations: 3,
completionSignal: "<promise>APPROVED</promise>",
});
console.log(
`Review of #${issue} finished with signal: ${review.completionSignal ?? "none"}`,
);
}
Plan parallelizable work
You are the planner. Below are the currently open issues labelled Sandcastle
on this repository:
!gh issue list --state open --label Sandcastle --json number,title,body --limit 100
Here is recent history for context:
!git log --oneline -10
Group the issues into independent work items that can be implemented on separate branches without conflicting with each other. For each work item, note the issue number it corresponds to and a one-line summary of the change.
Output a numbered plan, one work item per line. When the plan is complete,
output the exact line <promise>COMPLETE</promise> and stop.
Adversarially review issue #{{ISSUE_NUMBER}}
You are an adversarial reviewer. Review the changes on {{SOURCE_BRANCH}}
compared against {{TARGET_BRANCH}}.
Here is the diff under review:
!git diff {{TARGET_BRANCH}}...{{SOURCE_BRANCH}}
Look hard for missing edge cases, broken or missing tests, regressions, and security issues. If you find problems, fix them directly on this branch with focused commits, then re-check your work.
When the branch is correct and you have no further changes to make, output the
exact line <promise>APPROVED</promise> and stop.
README
Parallel Planner with Adversarial Review
A three-stage review pipeline: an Opus planner groups your open issues into independent work items, a fan-out of Sonnet implementers builds each item on its own branch in parallel, and a Codex reviewer adversarially reviews each branch with a fix loop before you merge.
Topology
plan ──▶ impl (xN) ──▶ review ──┐
▲ │
└──── fix loop ◀───┘
└──▶ merge
The impl node is a fan-out: one implementer runs per planned work item, each
on agent/issue-<n>. The reviewer runs on the same branch and loops back to fix
problems it finds until it emits <promise>APPROVED</promise>.
Disclosures
- Host hook:
cp .env.example .env(on worktree ready) seeds the sandbox env file. - Sandbox hook:
npm install(on sandbox ready) installs dependencies. - Shell expansion: the prompts use
!`gh ...`and!`git ...`blocks to pull live issue text and diffs into context. This is whyusesShellExpansionistrue. - Network:
ghreaches GitHub to list and read issues labelledSandcastle.
Prompt arguments
implement-prompt.md and review-prompt.md take an {{ISSUE_NUMBER}} argument
supplied per branch, and use the built-in {{SOURCE_BRANCH}} /
{{TARGET_BRANCH}} placeholders that Sandcastle injects automatically.
Requirements
Set CLAUDE_CODE_OAUTH_TOKEN and OPENAI_API_KEY (and GITHUB_TOKEN for
private repos) in .sandcastle/.env, build the image, then run the entrypoint.