You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Give a new adopter a single, explicit path to configure this orchestrator for their own project — and document the one convention that trips people up: the autonomous loop only builds issues you have explicitly queued. This issue doubles as the reference until it's distilled into the docs (see "Deliverables").
How the autonomous loop decides what to work on (the mental model)
The cron loop (/pr-loop) runs ticks. Each tick, in order: poll → merge → address-feedback → advance. The two human control points are:
What gets built — the module:* label is an opt-in work queue.
The ADVANCE step only picks up open issues labelled module:<name>, lowest-numbered first, one at a time, and only when there are zero open PRs. An unlabelled issue is never touched. This is deliberate:
Intent gate: most issues are discussions, questions, or half-scoped bugs that a bot shouldn't auto-implement. The label is you saying "this is scoped and ready for an autonomous worker."
Mechanism: every worker runs in an isolated git worktree and MUST NOT edit outside its module's path (from .claude/gates.json → modules). The module:<name> label maps the issue to exactly one module → one path → the worker's hard boundary. No module ⇒ no boundary ⇒ can't be delegated safely.
What ships — you approve the bot's PR.
Workers open PRs as the bot (.claude/scripts/bot-gh.sh). The loop's MERGE step only merges PRs that you (the repo owner) have Approved on GitHub and that are CI-green + mergeable. It never approves on your behalf. Your GitHub Approve is the ship-it gate.
Corollary: docs / infra / "non-module" work is not second-class — it just isn't loop-eligible until (a) its area exists as a module in gates.json, and (b) the issue carries the matching module:* label. Commenting "approved" on an issue does nothing; nothing watches issue text.
New-project configuration checklist
.claude/gates.json — the only per-project file that must be filled:
project.{name,language,packageManager}
modules[] — one entry per independently-ownable area, each with a non-overlapping path. These paths are the worker boundaries AND the set of module:<name> labels the loop understands. Include non-code areas you want automatable (e.g. { "name": "docs", "path": "docs" }).
gates.* — real shell commands for build/lint/typecheck/test/test_affected/coverage/e2e/security (empty string = skip). coverage_threshold.
CLAUDE.md — project context, conventions, definition of done, merge policy.
Bot machine account — create it, add as a write collaborator, put GH_BOT_TOKEN in .env (gitignored). All agent/loop gh calls run as the bot via bot-gh.sh; only git commits/pushes stay as the owner so the owner can approve bot PRs. Setup notes are in .claude/scripts/bot-gh.sh.
Create the module:* labels that match your modules[] names (gh label create module:<name> ... via bot-gh.sh). Without the label, ADVANCE can't queue the issue.
Arm the loop — run /pr-loop (re-run each session; the cron is session-scoped). It self-adjusts cadence: FAST when there's ≥1 open PR or ≥1 open module:* issue, else IDLE.
(optional)Hardening — /harden for the bypass + strict-sandbox profile (see docs/HARDENING.md).
Deliverables (the actual work for this issue)
Add an "Autonomous loop & the issue queue" section to docs/GETTING_STARTED.md (or docs/USAGE.md) covering the mental model above — especially the module:* opt-in and the owner-approval merge gate.
Add the new-project configuration checklist as a copy-pasteable list (link it from the README/onboarding).
Consider a small label-bootstrap helper (or a note in seed-issues.sh) that creates module:* labels from gates.json.modules[] so labels and the module map can't drift.
Cross-link from docs/PROMPTS.md (onboarding prompt) so a brand-new project lands here first.
Why file this
Every one of these steps is currently tribal knowledge spread across script header comments and this conversation. A single onboarding reference removes the "why didn't the loop pick up my issue?" foot-gun for the next adopter.
Note: to make THIS issue loop-eligible it would itself need a module (e.g. module:docs) once docs/.claude are added to gates.json.modules[]; otherwise drive it manually via the orchestrator.
Goal
Give a new adopter a single, explicit path to configure this orchestrator for their own project — and document the one convention that trips people up: the autonomous loop only builds issues you have explicitly queued. This issue doubles as the reference until it's distilled into the docs (see "Deliverables").
How the autonomous loop decides what to work on (the mental model)
The cron loop (
/pr-loop) runs ticks. Each tick, in order: poll → merge → address-feedback → advance. The two human control points are:What gets built — the
module:*label is an opt-in work queue.The ADVANCE step only picks up open issues labelled
module:<name>, lowest-numbered first, one at a time, and only when there are zero open PRs. An unlabelled issue is never touched. This is deliberate:path(from.claude/gates.json→modules). Themodule:<name>label maps the issue to exactly one module → onepath→ the worker's hard boundary. No module ⇒ no boundary ⇒ can't be delegated safely.What ships — you approve the bot's PR.
Workers open PRs as the bot (
.claude/scripts/bot-gh.sh). The loop's MERGE step only merges PRs that you (the repo owner) have Approved on GitHub and that are CI-green + mergeable. It never approves on your behalf. Your GitHub Approve is the ship-it gate.Corollary: docs / infra / "non-module" work is not second-class — it just isn't loop-eligible until (a) its area exists as a module in
gates.json, and (b) the issue carries the matchingmodule:*label. Commenting "approved" on an issue does nothing; nothing watches issue text.New-project configuration checklist
.claude/gates.json— the only per-project file that must be filled:project.{name,language,packageManager}modules[]— one entry per independently-ownable area, each with a non-overlappingpath. These paths are the worker boundaries AND the set ofmodule:<name>labels the loop understands. Include non-code areas you want automatable (e.g.{ "name": "docs", "path": "docs" }).gates.*— real shell commands forbuild/lint/typecheck/test/test_affected/coverage/e2e/security(empty string = skip).coverage_threshold.review.{lenses,consensus},budget.*(model routing,max_parallel_workers),merge.{policy,baseBranch}.CLAUDE.md— project context, conventions, definition of done, merge policy.GH_BOT_TOKENin.env(gitignored). All agent/loopghcalls run as the bot viabot-gh.sh; onlygitcommits/pushes stay as the owner so the owner can approve bot PRs. Setup notes are in.claude/scripts/bot-gh.sh.module:*labels that match yourmodules[]names (gh label create module:<name> ...viabot-gh.sh). Without the label, ADVANCE can't queue the issue..github/workflows/gates.ymlalready ships (see Ship a CI workflow that runs gates.json gates on PRs (server-side gate enforcement) #8); confirm it runs your gate commands and set branch protection / required checks if on a plan that supports it./pr-loop(re-run each session; the cron is session-scoped). It self-adjusts cadence: FAST when there's ≥1 open PR or ≥1 openmodule:*issue, else IDLE./hardenfor the bypass + strict-sandbox profile (seedocs/HARDENING.md).Deliverables (the actual work for this issue)
docs/GETTING_STARTED.md(ordocs/USAGE.md) covering the mental model above — especially themodule:*opt-in and the owner-approval merge gate.seed-issues.sh) that createsmodule:*labels fromgates.json.modules[]so labels and the module map can't drift.docs/PROMPTS.md(onboarding prompt) so a brand-new project lands here first.Why file this
Every one of these steps is currently tribal knowledge spread across script header comments and this conversation. A single onboarding reference removes the "why didn't the loop pick up my issue?" foot-gun for the next adopter.
Note: to make THIS issue loop-eligible it would itself need a module (e.g.
module:docs) oncedocs/.claudeare added togates.json.modules[]; otherwise drive it manually via the orchestrator.