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
13 changes: 7 additions & 6 deletions .claude/agents/implementer.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,18 @@ Never call bare `gh`. EVERY `gh` invocation (PR create/update, comments, `gh api
- `CLAUDE.md` — conventions, style, definition of done.

## Workflow
1. **Explore, don't guess.** Delegate codebase discovery to the `Explore` subagent to map the files you'll touch. Stay read-only until you understand the area.
2. **Respect your boundary.** You were assigned a module/path. NEVER edit files outside it. If the task truly requires touching another module, stop and report back to the orchestrator — do not reach across the boundary.
3. **Implement in small commits.** Match surrounding code style. Write/extend tests alongside the change.
1. **Bootstrap your worktree.** Your worktree is a fresh checkout that lacks toolchain state living outside the tree (`node_modules`, Foundry libs from `forge install`, shared caches). Run `bash .claude/scripts/worktree.sh setup` first if present — it runs the adapter's `worktree.setup` hook so that *every* gate is runnable here, not just in the main checkout. Empty/unconfigured = it skips harmlessly. If setup fails, fix it before proceeding — a half-bootstrapped worktree makes gates lie.
2. **Explore, don't guess.** Delegate codebase discovery to the `Explore` subagent to map the files you'll touch. Stay read-only until you understand the area.
3. **Respect your boundary.** You were assigned a module/path. NEVER edit files outside it. If the task truly requires touching another module, stop and report back to the orchestrator — do not reach across the boundary.
4. **Implement in small commits.** Match surrounding code style. Write/extend tests alongside the change.
**Stage explicit paths only — never `git add -A` / `git add .` / `git commit -a`.** A sandboxed session
masks sensitive config paths (shell rc, `.gitconfig`, `.mcp.json`, `.claude/{hooks,skills,routines}`,
editor dirs) as `/dev/null` character-device nodes; `git status` shows them as untracked, and a blanket
`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.
4. **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.
5. **Open a PR** (or leave the branch ready, per `CLAUDE.md` merge policy).
6. **Report back** in this format:
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.
6. **Open a PR** (or leave the branch ready, per `CLAUDE.md` merge policy). Then run `bash .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:
```
- Sub-task: <title>
- Branch: <name>
Expand Down
2 changes: 2 additions & 0 deletions .claude/agents/test-runner.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ You run the project's gates and report results. You do not fix code — you repo
If any gate or check needs `gh` (e.g. fetching CI status via `gh api`/`gh run`), call it through `.claude/scripts/bot-gh.sh`, never bare `gh`, so it runs as the bot.

## What to run
If you're gating a **fresh isolated worktree** (not the main checkout), first run `bash .claude/scripts/worktree.sh setup` if present — it bootstraps toolchain state that lives outside the tree (`node_modules`, `forge install`, caches) so gates don't fail for lack of setup rather than real defects. Empty/unconfigured = skips.

Read `.claude/gates.json` and run the requested gates (or all configured ones) using `.claude/scripts/gate.sh <name>` when available, else the raw command from the file. Typical order: `install` (if needed) → `build` → `lint` → `typecheck` → `test` (or `test_affected`) → `coverage` → `e2e` → `security`.

Skip any gate whose command is empty in `gates.json` and note it as "not configured".
Expand Down
6 changes: 6 additions & 0 deletions .claude/gates.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
"security": ""
},

"worktree": {
"_note": "Optional per-worktree lifecycle. `setup` runs right after an isolated implementer/reviewer worktree is created — bootstrap toolchain state that lives OUTSIDE the tree so every gate is runnable in-worktree (e.g. 'pnpm install', 'forge install', link shared caches). `teardown` runs before the worktree is removed. Empty string = skip. Run via .claude/scripts/worktree.sh setup|teardown.",
"setup": "",
"teardown": ""
},

"review": {
"lenses": ["correctness", "tests", "security", "performance"],
"consensus": "all",
Expand Down
44 changes: 44 additions & 0 deletions .claude/scripts/worktree.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# worktree.sh <setup|teardown>
# Runs the command stored at .worktree.<phase> in the adapter (gates.json) — the
# per-worktree lifecycle hook. `setup` bootstraps a freshly-created isolated
# worktree (install deps, `forge install`, link shared caches) so that EVERY gate
# is runnable in-worktree, not just in the main checkout; `teardown` runs before
# the worktree is removed (free caches, etc.).
#
# Empty/missing command => skip with exit 0 (so unconfigured repos don't block).
# Non-zero command exit => propagates (so a failed bootstrap surfaces, not hides).
# Mirrors gate.sh: honors the GATES_FILE override and resolves the repo root from
# this script's location — which, inside a worktree, IS the worktree root, so the
# hook installs into the worktree the caller is working in.
set -uo pipefail

phase="${1:?usage: worktree.sh <setup|teardown>}"
case "$phase" in
setup|teardown) ;;
*) echo "worktree.sh: phase must be 'setup' or 'teardown' (got '$phase')"; exit 2 ;;
esac

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
root="$(cd "$script_dir/../.." && pwd)"
# Which adapter to read. Defaults to the project adapter; set GATES_FILE to run a
# different one (e.g. GATES_FILE=.claude/self/gates.json). Relative paths resolve
# from the repo root.
gates_ref="${GATES_FILE:-.claude/gates.json}"
case "$gates_ref" in
/*) gates="$gates_ref" ;;
*) gates="$root/$gates_ref" ;;
esac

if [ ! -f "$gates" ]; then
echo "worktree.sh: no $gates found — skipping '$phase'"; exit 0
fi

cmd="$(node -e "try{const g=require('$gates');process.stdout.write((g.worktree&&g.worktree['$phase'])||'')}catch(e){process.stdout.write('')}" 2>/dev/null)"

if [ -z "$cmd" ]; then
echo "worktree.sh: '$phase' not configured in $(basename "$gates") — skipping"; exit 0
fi

echo "▶ worktree '$phase': $cmd"
cd "$root" && eval "$cmd"
6 changes: 6 additions & 0 deletions .claude/self/gates.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
"security": ""
},

"worktree": {
"_note": "Self-host workers need no external toolchain state (gates are node/bash-only), so both hooks are empty. Present to document the schema and exercise worktree.sh's skip path.",
"setup": "",
"teardown": ""
},

"review": { "lenses": ["correctness", "tests"], "consensus": "all", "skills": [] },

"budget": {
Expand Down
Loading