diff --git a/.claude/commands/pr-loop.md b/.claude/commands/pr-loop.md index 0b2bf42..1010adf 100644 --- a/.claude/commands/pr-loop.md +++ b/.claude/commands/pr-loop.md @@ -12,20 +12,20 @@ The repo is derived from the git remote (`bash ${CLAUDE_PLUGIN_ROOT:-.claude}/sc 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. +> Run one tick of the autonomous PR loop. 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 (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 **`planned` AND** module:* — `backlog` issues are NOT work, and only the repo OWNER may add the `planned` label; you must never add it — 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 `planned` 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. +> STEP 0 — run the tick. Invoke, as a REAL bash tool call, exactly: `bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/loop-tick.sh`. This one script runs census, then notify-poll.sh, merge-ready.sh, and pr-feedback.sh, IN ORDER, with their full output, and ends with exactly one machine-readable verdict line as the LAST line of output: `action=none`, `action=advance issue=N`, or `action=feedback pr=N`. That verdict line is the SOLE source of truth for what to do next: never hand-count open PRs, planned issues, or feedback PRs yourself, and never skip this invocation because the tick "looks quiet" — it must run, and its output must be read, on every single tick with no exceptions. > -> 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.) After merging, it fast-forwards the LOCAL checkout to `main` when it is clean and already on `main` — so your terminal/IDE shows the latest without a manual pull. It never switches branches or clobbers uncommitted work; anything else is skipped (`local_sync` line reports the reason). -> 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 `` marker comment via bot-gh.sh. Do NOT merge here. -> 4. ADVANCE: ONLY when there are ZERO open PRs — pick the lowest-numbered open issue labelled **`planned`** AND module:* with no feat/issue--* branch; drive it through the orchestrator (scope → worktree implementer → gate.sh gates → reviewer lenses → bot PR). One issue in flight at a time. `backlog` issues are owner-unapproved: never pick them, and if you file an issue yourself, label it `backlog` — NEVER `planned` (that label is the owner's formal approval and is assigned by the owner alone; see docs/USAGE.md → "Autonomous loop & the issue queue"). -> 5. If nothing actionable, reply exactly one line: "No actionable activity." +> CADENCE: the script's `=== 1/4 loop-census.sh ===` section includes a line `cadence=FAST|WATCH|IDLE cron=` — this is the desired cadence; consume it as-is, do NOT re-derive it from counts. If this cron job's current schedule differs from that `cron=`, CronDelete this job and CronCreate a durable replacement with this SAME prompt at the desired schedule. > -> 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. +> Then obey the verdict line (the tick already ran poll/merge/feedback-detection above — do not re-run those scripts): +> - `action=feedback pr=N` → address PR N's feedback: run orchestrator → worktree implementer → reviewer lenses (per .claude/gates.json) on the SAME branch, push to update the PR in place, and post the `` marker comment via bot-gh.sh. Do NOT merge. +> - `action=advance issue=N` → advance issue N through the orchestrator (scope → worktree implementer → gate.sh gates → reviewer lenses → bot PR). One issue in flight at a time. `backlog` issues are owner-unapproved: never pick them, and if you file an issue yourself, label it `backlog` — NEVER `planned` (that label is the owner's formal approval and is assigned by the owner alone; see docs/USAGE.md → "Autonomous loop & the issue queue"). +> - `action=none` → reply exactly one line: "No actionable activity." This is the ONLY path to that phrase — never reply it without loop-tick.sh having actually been invoked (and its output read) earlier in this same turn. +> +> Token discipline: only read docs/USAGE.md and .claude/agents/* when the verdict actually requires orchestrating agents (advance/feedback); an `action=none` tick needs only the script's own output. 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"). +Execute the tick logic 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 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 `planned` 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`. diff --git a/.claude/self/pr-loop-self.md b/.claude/self/pr-loop-self.md index 392c899..49e70fd 100644 --- a/.claude/self/pr-loop-self.md +++ b/.claude/self/pr-loop-self.md @@ -15,20 +15,20 @@ The repo is derived from the git remote (`bash ${CLAUDE_PLUGIN_ROOT:-.claude}/sc 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 `. 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. +> Run one tick of the self-hosted PR loop. 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). 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 `. 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 (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 **`planned` AND** one of `module:docs`, `module:harness`, `module:examples`, `module:ci` (the self modules) — `backlog` issues are NOT work, and only the repo OWNER may add the `planned` label; you must never add it — 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 `planned` 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. +> STEP 0 — run the tick. Invoke, as a REAL bash tool call, exactly: `GATES_FILE=.claude/self/gates.json bash ${CLAUDE_PLUGIN_ROOT:-.claude}/scripts/loop-tick.sh`. This one script runs census, then notify-poll.sh, merge-ready.sh, and pr-feedback.sh, IN ORDER, with their full output, and ends with exactly one machine-readable verdict line as the LAST line of output: `action=none`, `action=advance issue=N`, or `action=feedback pr=N`. That verdict line is the SOLE source of truth for what to do next: never hand-count open PRs, planned issues, or feedback PRs yourself, and never skip this invocation because the tick "looks quiet" — it must run, and its output must be read, on every single tick with no exceptions. (loop-census.sh reads `$GATES_FILE` directly, so it derives module labels from the self set — `module:docs`, `module:harness`, `module:examples`, `module:ci` — not the placeholder root adapter.) > -> 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.) After merging, it fast-forwards the LOCAL checkout to `main` when it is clean and already on `main` — so your terminal/IDE shows the latest without a manual pull. It never switches branches or clobbers uncommitted work; anything else is skipped (`local_sync` line reports the reason). -> 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 `` marker comment via bot-gh.sh. Do NOT merge here. -> 4. ADVANCE: ONLY when there are ZERO open PRs — pick the lowest-numbered open issue labelled **`planned`** AND a self `module:*` label (`module:docs`, `module:harness`, `module:examples`, `module:ci`) with no feat/issue--* 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. `backlog` issues are owner-unapproved: never pick them, and if you file an issue yourself, label it `backlog` — NEVER `planned` (that label is the owner's formal approval and is assigned by the owner alone; see docs/USAGE.md → "Autonomous loop & the issue queue"). -> 5. If nothing actionable, reply exactly one line: "No actionable activity." +> CADENCE: the script's `=== 1/4 loop-census.sh ===` section includes a line `cadence=FAST|WATCH|IDLE cron=` — this is the desired cadence; consume it as-is, do NOT re-derive it from counts. If this cron job's current schedule differs from that `cron=`, CronDelete this job and CronCreate a durable replacement with this SAME prompt at the desired schedule. > -> 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. +> Then obey the verdict line (the tick already ran poll/merge/feedback-detection above — do not re-run those scripts): +> - `action=feedback pr=N` → address PR N's feedback: 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 `` marker comment via bot-gh.sh. Do NOT merge. +> - `action=advance issue=N` → advance issue N 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. `backlog` issues are owner-unapproved: never pick them, and if you file an issue yourself, label it `backlog` — NEVER `planned` (that label is the owner's formal approval and is assigned by the owner alone; see docs/USAGE.md → "Autonomous loop & the issue queue"). +> - `action=none` → reply exactly one line: "No actionable activity." This is the ONLY path to that phrase — never reply it without loop-tick.sh having actually been invoked (and its output read) earlier in this same turn. +> +> Token discipline: only read docs/USAGE.md and .claude/agents/* when the verdict actually requires orchestrating agents (advance/feedback); an `action=none` tick needs only the script's own output. 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"). +Execute the tick logic 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 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 `planned` 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. This file lives under `.claude/self/` (not `.claude/commands/`, see issue #76), so it is not a registered slash command — for a tighter in-session cadence, ask Claude to re-read and re-run this file's instructions on the interval you want instead of `/loop 5m /pr-loop-self`. diff --git a/.claude/settings.json b/.claude/settings.json index f266ae6..e59affe 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -41,6 +41,7 @@ "Bash(bash .claude/scripts/notify-poll.sh:*)", "Bash(bash .claude/scripts/pr-feedback.sh:*)", "Bash(bash .claude/scripts/merge-ready.sh:*)", + "Bash(bash .claude/scripts/loop-tick.sh:*)", "Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)",