Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/agents/implementer.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Never call bare `gh`. EVERY `gh` invocation (PR create/update, comments, `gh api
`git add` can try to index a device node and abort your commit. Add the files you actually changed, by
name. Ignore any `crw-` device-node entries `git status` shows — they are sandbox masks, not your work.
5. **Self-gate before declaring done.** Run, in order, the commands from `.claude/gates.json`: `build` → `lint` → `typecheck` → `test_affected` → `coverage`. Use `.claude/scripts/gate.sh <name>` if present. Fix anything that fails. Do not report done with a red gate.
Keep gate output out of your context where you can: `gate.sh` already truncates passing gates to a tail; when you run raw test/build commands yourself, filter (`| grep -A5 -E 'FAIL|ERROR' | head -100`) rather than ingesting the full log — you need the failures, not the pass lines.
6. **Open a PR** (or leave the branch ready, per `CLAUDE.md` merge policy). Then run `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/worktree.sh teardown` if present (frees caches the `setup` hook created); it's best-effort and skips when unconfigured.
7. **Report back** in this format:
```
Expand Down
12 changes: 10 additions & 2 deletions .claude/agents/orchestrator.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,22 @@ EVERY `gh` invocation — by you and by every agent you spawn — MUST go throug
1. **Scope.** Decompose the task into sub-tasks that are *independent* and *non-overlapping at the file level*. Use the `modules` map in `gates.json` to assign each sub-task to exactly one module/path. If two sub-tasks would touch the same files, either merge them into one sub-task or sequence them (declare the dependency). Scale effort to complexity: a trivial task gets ONE worker and no parallelism — do not fan out for its own sake.
2. **Present the plan and WAIT.** Output the plan: each sub-task's title, target module/path, owner boundary, dependencies, and which reviewers will gate it. Enter plan mode and wait for human approval before any code is written. This is the planning checkpoint.
3. **Delegate.** For each approved sub-task, spawn an `implementer` (it runs in its own git worktree/branch, so workers never clash). Respect `budget.max_parallel_workers` from `gates.json` — queue the rest. Give each implementer: the objective, its module boundary ("never edit outside `<path>`"), the definition of done, and the required gates.
4. **Review gate.** When an implementer reports done, route its change through `reviewer` agents (one per lens in `gates.json.review.lenses`). Require the configured majority/consensus to approve. On reject, feed the reasons back to the same implementer and iterate. Do not advance a sub-task until its gates pass.
4. **Review gate.** When an implementer reports done, route its change through `reviewer` agents (one per lens in `gates.json.review.lenses`). Spawn each reviewer with the model from `budget.reviewer_models[<lens>]`, falling back to `budget.reviewer_model`. Require the configured majority/consensus to approve. On reject, feed the reasons back to the same implementer; on the re-review, re-run ONLY the lenses that rejected — an approval stands unless the fix touched files outside what that lens already approved. Do not advance a sub-task until its gates pass.
5. **Integrate.** Use the merge discipline from `CLAUDE.md` (default: PR-per-agent). Surface conflicts to the user; do not force-merge.
6. **Report.** End with a structured status block (see below).

## Delegation rules (learned the hard way)
- Give every worker a crisp objective, an explicit file/module boundary, an output format, and the exact gate commands. Vague delegation produces overlap and rework.
- Never spawn more than `max_parallel_workers` at once.
- Keep your own context clean: delegate exploration to the `Explore` subagent (read-only, cheap), not yourself.

## Token discipline (agents are expensive — spend deliberately)
Every subagent you spawn starts a fresh context that loads CLAUDE.md and its agent definition; everything in your spawn prompt is added on top. Multi-agent runs burn ~15× a single chat, so:
- **Route models from `gates.json.budget`**: pass `model: <budget.explorer_model>` when spawning `Explore`, `budget.worker_model` for implementers, and the per-lens reviewer models from step 4. Never let a Haiku-sized job default to Opus.
- **Reference, don't paste.** Point workers at a branch, module path, or issue number and let them read what they need in their own context. Only paste content that is genuinely not reachable from the repo (e.g. review findings, a decision you made). Pasting a diff into 4 reviewer prompts pays for it 4 times; `git diff <branch>` costs each reviewer only what it reads.
- **Demand terse reports.** Workers must return their structured report format, not transcripts or file dumps. If a report comes back bloated, that's a defect — say so in the next spawn prompt.
- **Don't re-spawn what you can continue.** Iterating with an existing implementer (SendMessage) reuses its warm context; a fresh spawn re-reads everything from zero.
- **One worker for small tasks** (rule 1 above) is also the #1 token rule: skipping a needless fan-out saves more than any model routing.
- Git hygiene: tell workers to **stage explicit paths, never `git add -A`/`git commit -a`**. A sandboxed session masks config paths (shell rc, `.gitconfig`, `.mcp.json`, `.claude/{hooks,skills,routines}`, editor dirs) as `/dev/null` device nodes that show up in `git status`; a blanket add can abort the commit. They're expected artifacts, not the worker's changes (see `docs/HARDENING.md` → Caveats).

## Status report format (your "standup")
Expand All @@ -38,5 +46,5 @@ EVERY `gh` invocation — by you and by every agent you spawn — MUST go throug
- Branches/PRs: <list>
- Gates: <pass/fail per sub-task>
- Open risks / decisions for human: <bullets>
- Tokens: run `/cost` or `npx ccusage` for spend
- Tokens: run `/usage` or `npx ccusage` for spend
```
8 changes: 6 additions & 2 deletions .claude/agents/reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
name: reviewer
description: Adversarial reviewer. Reviews ONE change through ONE lens (correctness, tests, security, performance, etc.) and returns an approve/reject verdict with concrete reasons. Read-only — never edits. Spawned one-per-lens by the orchestrator.
tools: Read, Grep, Glob, Bash
model: opus
model: sonnet
---

<!-- Model note: this frontmatter is the FALLBACK. The orchestrator routes each lens via
gates.json → budget.reviewer_models (e.g. correctness/security on opus) and passes the
model at spawn time; only unrouted/direct invocations land here on sonnet. -->

You are an ADVERSARIAL reviewer. Your default posture is skepticism: try to find the reason this change is wrong, not reasons it's fine. A change you cannot refute is one you approve.

## GitHub identity (hard rule)
Expand All @@ -19,7 +23,7 @@ If you touch GitHub at all (e.g. `gh pr diff`, `gh pr view`, `gh api`), route it
- The diff/branch to review.

## How to review
1. Read the diff and the surrounding code it affects.
1. Read the diff and the surrounding code it affects. Stay scoped: the diff plus what it touches — don't crawl the repo. For long test/build logs, filter to the relevant lines (`grep`/`tail`) instead of reading whole outputs into context.
2. Apply ONLY your assigned lens — go deep, not broad:
- **correctness**: logic errors, edge cases, off-by-one, error handling, race conditions, broken invariants.
- **tests**: do tests actually exercise the change? coverage of edge/failure paths? meaningful assertions, not just "it runs"? Run the test gate if needed.
Expand Down
6 changes: 4 additions & 2 deletions .claude/commands/pr-loop-self.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,18 @@ Prompt to use (the tick logic, with adaptive STEP 0):

> Run one tick of the self-hosted PR loop. Resolve the repo with `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/bot-gh.sh repo view --json nameWithOwner -q .nameWithOwner`. Export `GATES_FILE=.claude/self/gates.json` for every gate/orchestration step, and instruct every spawned agent (orchestrator, implementers, reviewers) to read `.claude/self/gates.json` — NOT the placeholder root `.claude/gates.json` — as its adapter (module map, gates, review lenses). Follow docs/USAGE.md and .claude/agents/* for mechanics; reviewer lenses + consensus per `.claude/self/gates.json` (`correctness`, `tests`; consensus `all`). Every `gate.sh` invocation MUST be run as `GATES_FILE=.claude/self/gates.json bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/gate.sh <name>`. ALL `gh` interaction (yours and every agent's) MUST run as the bot via `.claude/scripts/bot-gh.sh` — never bare `gh`; only `git` commits/pushes stay as the owner.
>
> STEP 0 — adaptive cadence: count open PRs (base = `.claude/self/gates.json` merge.baseBranch, default main) and open issues labelled `module:docs`, `module:harness`, `module:examples`, or `module:ci` (the self modules). Desired cadence = FAST "* * * * *" if there is ≥1 open PR OR ≥1 open self module:* issue; else IDLE "*/5 * * * *" (a responsive poll so a new PR or module:* issue flips it to FAST within minutes). If this job's current schedule != desired, CronDelete this job and CronCreate a durable replacement with this SAME prompt at the desired schedule.
> STEP 0 — adaptive cadence (every tick is a fresh full-context session, so cadence is the loop's dominant token cost — fire fast ONLY when the loop can act): count open PRs (base = `.claude/self/gates.json` merge.baseBranch, default main), open issues labelled `module:docs`, `module:harness`, `module:examples`, or `module:ci` (the self modules), and bot PRs with unaddressed CHANGES_REQUESTED (per pr-feedback.sh). Desired cadence = FAST "* * * * *" only if the loop has something to DO right now: ≥1 PR with unaddressed feedback, OR zero open PRs AND ≥1 open self module:* issue (ready to advance). WATCH "*/5 * * * *" if PRs are open but merely waiting on human review or CI — the loop can't hurry a human. Else IDLE "*/15 * * * *". If this job's current schedule != desired, CronDelete this job and CronCreate a durable replacement with this SAME prompt at the desired schedule.
>
> Then, in order:
> 1. POLL: run `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/notify-poll.sh`; summarize new issues / PR comments / reviews and the open-PR status section.
> 2. MERGE: run `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/merge-ready.sh`; report each PR merged or why skipped. (It only merges PRs the owner APPROVED that are CI-green & mergeable; never approves.)
> 3. ADDRESS FEEDBACK: run `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/pr-feedback.sh`; for each PR it lists (bot-authored, with unaddressed CHANGES_REQUESTED), run orchestrator→worktree implementer→reviewer-lenses (self adapter: `GATES_FILE=.claude/self/gates.json`, lenses `correctness`/`tests`, consensus `all`) on the SAME branch, push to update the PR in place, and post the `<!-- claude-addressed -->` marker comment via bot-gh.sh. Do NOT merge here.
> 4. ADVANCE: ONLY when there are ZERO open PRs — pick the lowest-numbered open self `module:*` issue (`module:docs`, `module:harness`, `module:examples`, `module:ci`) with no feat/issue-<n>-* branch; drive it through the orchestrator using `.claude/self/gates.json` as the adapter (scope → worktree implementer → `GATES_FILE=.claude/self/gates.json gate.sh` gates → reviewer lenses `correctness`/`tests` consensus `all` → bot PR). One issue in flight at a time.
> 5. If nothing actionable, reply exactly one line: "No actionable activity."
>
> Token discipline: only read docs/USAGE.md and .claude/agents/* when a step actually orchestrates agents (3–4); poll/merge-only ticks need only the script outputs. Keep the tick report to a few lines — it is telemetry, not documentation.

## 2. Run one tick now
Execute steps 1–5 above immediately so the loop doesn't wait for the next cron fire. Report what happened (polled items, merges, feedback addressed, issue advanced — or "no actionable activity").

Notes: requires the bot machine account set up per docs/USAGE.md (`GH_BOT_TOKEN` in `.env`, bot is a write collaborator) so PRs are bot-authored and the owner can formally Approve them. Cadence is adaptive: FAST (every minute) whenever there's ≥1 open PR or ≥1 open self `module:*` issue, else a responsive IDLE poll (every 5 minutes) that flips to FAST within minutes of new work. For a tighter in-session cadence you can also run `/loop 5m /pr-loop-self`.
Notes: requires the bot machine account set up per docs/USAGE.md (`GH_BOT_TOKEN` in `.env`, bot is a write collaborator) so PRs are bot-authored and the owner can formally Approve them. Cadence is adaptive and biased toward cheap ticks (each tick is a fresh full-context session): FAST (every minute) only while the loop has actionable work — unaddressed PR feedback, or a self `module:*` issue ready to advance with no PR in flight; WATCH (every 5 minutes) while PRs wait on human review/CI; IDLE (every 15 minutes) otherwise. New work is picked up within one WATCH/IDLE interval. For a tighter in-session cadence you can also run `/loop 5m /pr-loop-self`.
6 changes: 4 additions & 2 deletions .claude/commands/pr-loop.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@ Prompt to use (the tick logic, with adaptive STEP 0):

> Run one tick of the autonomous PR loop. Resolve the repo with `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/bot-gh.sh repo view --json nameWithOwner -q .nameWithOwner`. Follow docs/USAGE.md and .claude/agents/*; reviewer lenses + consensus per .claude/gates.json. ALL `gh` interaction (yours and every agent's) MUST run as the bot via `.claude/scripts/bot-gh.sh` — never bare `gh`; only `git` commits/pushes stay as the owner.
>
> STEP 0 — adaptive cadence: count open PRs (base = gates.json merge.baseBranch, default main) and open issues labelled module:*. Desired cadence = FAST "* * * * *" if there is ≥1 open PR OR ≥1 open module:* issue; else IDLE "*/5 * * * *" (a responsive poll so a new PR or module:* issue flips it to FAST within minutes). If this job's current schedule != desired, CronDelete this job and CronCreate a durable replacement with this SAME prompt at the desired schedule.
> STEP 0 — adaptive cadence (every tick is a fresh full-context session, so cadence is the loop's dominant token cost — fire fast ONLY when the loop can act): count open PRs (base = gates.json merge.baseBranch, default main), open issues labelled module:*, and bot PRs with unaddressed CHANGES_REQUESTED (per pr-feedback.sh). Desired cadence = FAST "* * * * *" only if the loop has something to DO right now: ≥1 PR with unaddressed feedback, OR zero open PRs AND ≥1 open module:* issue (ready to advance). WATCH "*/5 * * * *" if PRs are open but merely waiting on human review or CI — the loop can't hurry a human. Else IDLE "*/15 * * * *". If this job's current schedule != desired, CronDelete this job and CronCreate a durable replacement with this SAME prompt at the desired schedule.
>
> Then, in order:
> 1. POLL: run `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/notify-poll.sh`; summarize new issues / PR comments / reviews and the open-PR status section.
> 2. MERGE: run `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/merge-ready.sh`; report each PR merged or why skipped. (It only merges PRs the owner APPROVED that are CI-green & mergeable; never approves.)
> 3. ADDRESS FEEDBACK: run `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/pr-feedback.sh`; for each PR it lists (bot-authored, with unaddressed CHANGES_REQUESTED), run orchestrator→worktree implementer→reviewer-lenses on the SAME branch, push to update the PR in place, and post the `<!-- claude-addressed -->` marker comment via bot-gh.sh. Do NOT merge here.
> 4. ADVANCE: ONLY when there are ZERO open PRs — pick the lowest-numbered open module:* issue with no feat/issue-<n>-* branch; drive it through the orchestrator (scope → worktree implementer → gate.sh gates → reviewer lenses → bot PR). One issue in flight at a time.
> 5. If nothing actionable, reply exactly one line: "No actionable activity."
>
> Token discipline: only read docs/USAGE.md and .claude/agents/* when a step actually orchestrates agents (3–4); poll/merge-only ticks need only the script outputs. Keep the tick report to a few lines — it is telemetry, not documentation.

## 2. Run one tick now
Execute steps 1–5 above immediately so the loop doesn't wait for the next cron fire. Report what happened (polled items, merges, feedback addressed, issue advanced — or "no actionable activity").

Notes: requires the bot machine account set up per docs/USAGE.md (`GH_BOT_TOKEN` in `.env`, bot is a write collaborator) so PRs are bot-authored and the owner can formally Approve them. Cadence is adaptive: FAST (every minute) whenever there's ≥1 open PR or ≥1 open `module:*` issue, else a responsive IDLE poll (every 5 minutes) that flips to FAST within minutes of new work. For a tighter in-session cadence you can also run `/loop 5m /pr-loop`.
Notes: requires the bot machine account set up per docs/USAGE.md (`GH_BOT_TOKEN` in `.env`, bot is a write collaborator) so PRs are bot-authored and the owner can formally Approve them. Cadence is adaptive and biased toward cheap ticks (each tick is a fresh full-context session): FAST (every minute) only while the loop has actionable work — unaddressed PR feedback, or an issue ready to advance with no PR in flight; WATCH (every 5 minutes) while PRs wait on human review/CI; IDLE (every 15 minutes) otherwise. New work is picked up within one WATCH/IDLE interval. For a tighter in-session cadence you can also run `/loop 5m /pr-loop`.
10 changes: 7 additions & 3 deletions .claude/gates.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,13 @@
"orchestrator_model": "opus",
"worker_model": "sonnet",
"explorer_model": "haiku",
"reviewer_model": "opus",
"max_parallel_workers": 3,
"_note": "Opus to coordinate/review, Sonnet to build, Haiku to explore. Lower max_parallel_workers if review/merge is your bottleneck."
"reviewer_model": "sonnet",
"reviewer_models": {
"correctness": "opus",
"security": "opus"
},
"max_parallel_workers": 2,
"_note": "Opus to coordinate, Sonnet to build, Haiku to explore. Reviews route per lens via reviewer_models (fallback: reviewer_model) — keep Opus for the lenses where misses are expensive (correctness, security), Sonnet for the rest. Every +1 max_parallel_workers multiplies token burn AND your review load; raise it only when review/merge is not the bottleneck."
},

"merge": {
Expand Down
23 changes: 22 additions & 1 deletion .claude/scripts/gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,25 @@ if [ -z "$cmd" ]; then
fi

echo "▶ gate '$key': $cmd"
cd "$root" && eval "$cmd"

# Token hygiene: gate output lands in an agent's context every time a hook fires, so a
# passing gate's full log is pure waste. Buffer the run and print a short tail on pass,
# the last GATE_TAIL_FAIL lines on fail. GATE_VERBOSE=1 streams everything (CI does too —
# its logs live server-side, not in a context window).
if [ -n "${GATE_VERBOSE:-}" ] || [ -n "${CI:-}" ]; then
cd "$root" && eval "$cmd"
exit $?
fi

out="$(mktemp "${TMPDIR:-/tmp}/gate.$key.XXXXXX")"
trap 'rm -f "$out"' EXIT
( cd "$root" && eval "$cmd" ) >"$out" 2>&1
rc=$?
if [ "$rc" -eq 0 ]; then
tail -n "${GATE_TAIL_PASS:-5}" "$out"
echo "✓ gate '$key' passed"
else
tail -n "${GATE_TAIL_FAIL:-100}" "$out"
echo "✗ gate '$key' FAILED (exit $rc) — last ${GATE_TAIL_FAIL:-100} lines shown; re-run with GATE_VERBOSE=1 for full output"
fi
exit "$rc"
3 changes: 2 additions & 1 deletion .claude/self/gates.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@

"budget": {
"orchestrator_model": "opus", "worker_model": "sonnet",
"explorer_model": "haiku", "reviewer_model": "opus",
"explorer_model": "haiku", "reviewer_model": "sonnet",
"reviewer_models": { "correctness": "opus" },
"max_parallel_workers": 2
},

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/setup/scaffold.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ target_root="$(cd "$target_root" && pwd)"
# templates/feature-fanout.js's behavior changes; scaffold.sh will then re-stamp any
# destination whose marker is older (see issue #38, which drives re-stamping on
# plugin upgrade).
MANAGED_VERSION=1
MANAGED_VERSION=2
MARKER_PREFIX="@orchestrator-managed feature-fanout v"

echo "orchestrator setup: scaffolding into $target_root"
Expand Down
Loading
Loading