compounding-loop 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -6
- package/dist/cli/checkLanes.js +60 -0
- package/dist/cli/checkLanes.js.map +1 -0
- package/dist/cli/doctor.js +54 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.js +87 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +2 -1
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/laneStatus.js +52 -0
- package/dist/cli/laneStatus.js.map +1 -0
- package/dist/cli/lanesInit.js +56 -0
- package/dist/cli/lanesInit.js.map +1 -0
- package/dist/cli/lock.js +42 -0
- package/dist/cli/lock.js.map +1 -0
- package/dist/cli/loops.js +23 -3
- package/dist/cli/loops.js.map +1 -1
- package/dist/cli/new.js +5 -5
- package/dist/cli/new.js.map +1 -1
- package/dist/cli/run.js +104 -16
- package/dist/cli/run.js.map +1 -1
- package/dist/core/doctor.js +49 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/laneStatus.js +82 -0
- package/dist/core/laneStatus.js.map +1 -0
- package/dist/core/lanes.js +139 -0
- package/dist/core/lanes.js.map +1 -0
- package/dist/core/lanesInit.js +143 -0
- package/dist/core/lanesInit.js.map +1 -0
- package/dist/core/runPrompt.js +1 -1
- package/dist/core/table.js +20 -1
- package/dist/core/table.js.map +1 -1
- package/dist/server/data.js +20 -6
- package/dist/server/data.js.map +1 -1
- package/dist/server/events.js +1 -0
- package/dist/server/events.js.map +1 -1
- package/dist/server/health.js +65 -5
- package/dist/server/health.js.map +1 -1
- package/dist/server/loops.js +6 -3
- package/dist/server/loops.js.map +1 -1
- package/dist/server/this-repo.json +371 -0
- package/dist/web/assets/index-B16QCSnY.css +2 -0
- package/dist/web/assets/index-CS82DKbF.js +49 -0
- package/dist/web/index.html +3 -3
- package/method/COMPOUNDING_LOOP.md +34 -0
- package/method/example/K-0002.md +16 -0
- package/method/example/K-0003.md +16 -0
- package/package.json +6 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/runners/github-actions/lanes.yml +103 -0
- package/runners/local/README.md +2 -1
- package/runners/local/launchd.plist +36 -0
- package/runners/routine/README.md +38 -1
- package/runners/routine/control-room-prompt.md +16 -0
- package/runners/routine/lane-prompt.md +12 -0
- package/runners/routine/prompt.md +1 -1
- package/templates/chrome-extension/extension/icons/icon128.png +0 -0
- package/templates/chrome-extension/extension/icons/icon16.png +0 -0
- package/templates/chrome-extension/extension/icons/icon48.png +0 -0
- package/templates/chrome-extension/extension/manifest.json +19 -3
- package/templates/chrome-extension/gitignore +1 -1
- package/templates/lanes/README.md +23 -0
- package/templates/lanes/ci-check-lanes.yml +38 -0
- package/templates/lanes/claude-section.md +17 -0
- package/templates/lanes/control-room.md +3 -0
- package/templates/lanes/outbox.md +5 -0
- package/templates/lanes/task.md +36 -0
- package/templates/static-site/.ai/loop-prompt.md +1 -1
- package/templates/static-site/gitignore +1 -1
- package/dist/web/assets/index-BDdtnG33.js +0 -49
- package/dist/web/assets/index-D3-N8DN4.css +0 -2
package/dist/web/index.html
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<link rel="icon" type="image/svg+xml" href="
|
|
6
|
+
<link rel="icon" type="image/svg+xml" href="./favicon.svg" />
|
|
7
7
|
<title>Compounding Loop</title>
|
|
8
|
-
<script type="module" crossorigin src="
|
|
9
|
-
<link rel="stylesheet" crossorigin href="
|
|
8
|
+
<script type="module" crossorigin src="./assets/index-CS82DKbF.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-B16QCSnY.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
13. Measurement
|
|
25
25
|
14. The human's role
|
|
26
26
|
15. Commands
|
|
27
|
+
16. Lanes: several unattended runs, one repo
|
|
27
28
|
- Part C: Templates
|
|
28
29
|
- Part D: Setup and adoption order
|
|
29
30
|
- Part E: Limits, open questions, where the ideas come from
|
|
@@ -382,6 +383,39 @@ Say it any way you like. The AI maps it to the right protocol.
|
|
|
382
383
|
|
|
383
384
|
> Resume from `.ai/task.md`. Verify Current state against git and the checks before trusting it, tell me about any mismatch, then do the next step.
|
|
384
385
|
|
|
386
|
+
### 16. Lanes: several unattended runs, one repo
|
|
387
|
+
|
|
388
|
+
A lane is one scheduled run that owns one part of the codebase. Several lanes push to the same `main` on staggered schedules. Ownership of paths replaces merging: two lanes never edit the same file, so there is nothing to merge.
|
|
389
|
+
|
|
390
|
+
**When to split into lanes**
|
|
391
|
+
|
|
392
|
+
- Split when the work has parts with clean seams (core library, server, web UI, docs) and each part can be verified on its own, and when one run per hour would otherwise be the bottleneck.
|
|
393
|
+
- Do not split a project that is small, or whose parts change together in most commits. A cross-cutting change in a laned repo means waiting on another lane, which costs a day. One loop with units in order is faster and simpler.
|
|
394
|
+
- Start with two or three lanes. Each lane costs a run budget and a share of your plan's usage limit.
|
|
395
|
+
|
|
396
|
+
**Ownership.** `.ai/lanes.json` lists each lane's `owns` globs and the `shared` files any lane may edit with the smallest possible change (lockfiles, config, shared types). Owning no overlapping paths is checked when the file is read. Reading is free: a lane may read anything. Fixing a red `main` is the one cross-lane edit allowed anywhere. `loop check-lanes` only lets the trailer `Lane: control` touch every path, so commit that fix as `Lane: control` (or list the file under `shared`) and tell the owner in your outbox.
|
|
397
|
+
|
|
398
|
+
**Outboxes.** A lane talks to the others by writing only to its own `.ai/lanes/<lane>/outbox.md`, newest first: `YYYY-MM-DD HH:MM UTC · to <lane|all> · message`. At the start of every run each lane reads every other outbox and the control room's notes, and acts on messages to it or to `all`. Nobody writes into another lane's files, so there is no conflict even on the notes. Say what you shipped that someone waits on, and point to `path:line`.
|
|
399
|
+
|
|
400
|
+
**The control room.** One more scheduled run, every few hours, plays the owner: it runs the whole test suite, does the leak check, reads each lane's task file and outbox, and writes only `.ai/control-room.md` and the smallest fixes. It extends a lane's run budget once if the lane made real progress, answers a `BLOCKED.md` question when the brief answers it safely, copies messages that have waited too long to the lane that ignores them, tells a lane to stop waiting for a finished one, and writes the root `DONE.md` when every lane is done and the product's done-when passes. It uses the trailer `Lane: control`.
|
|
401
|
+
|
|
402
|
+
**The trailer check.** Every commit ends with `Lane: <lane>`. `loop check-lanes` reads the commits in a range and fails any commit that touches a path its lane does not own and that is not shared or the lane's own `.ai/lanes/<lane>/` folder. Commits without a trailer are reported, not failed, because humans commit too. Run it in CI on every push and pull request.
|
|
403
|
+
|
|
404
|
+
**Rules that make parallel runs safe**
|
|
405
|
+
|
|
406
|
+
1. Start every run with `git checkout main && git pull --ff-only`; pull again right before each push.
|
|
407
|
+
2. A rejected push means `git pull --rebase` and push again. A rebase that conflicts in a file you do not own means `git rebase --abort`, note it under Tried, pull fresh and redo your small change on top.
|
|
408
|
+
3. Never force-push. A lane's history is the other lanes' history too.
|
|
409
|
+
4. Each lane has its own task file, run counter, `DONE.md` and `BLOCKED.md`. Only the control room writes the root `DONE.md`.
|
|
410
|
+
|
|
411
|
+
**Failure modes seen when this repo was built by five lanes**
|
|
412
|
+
|
|
413
|
+
- **Stale sandbox clones.** A cloud run can start from an old clone and report that someone force-pushed or that a file vanished. Check `git log` on a fresh pull before believing a run's account of history.
|
|
414
|
+
- **Rate-limit starvation.** On a subscription plan, the lanes share one usage limit. Runs that end in about a second are usually the limit, not a bug. Fewer lanes, fewer runs per day or a lower-tier model for cheap lanes fixes it. The control room marks a lane with no commit in four hours as stalled.
|
|
415
|
+
- **A lane waiting on a finished lane.** The web lane waited for an API the server lane would never add, because the server lane had already written `DONE.md`. The control room now tells the waiting lane to stop waiting, record the gap as a next improvement and finish. Before a lane writes its own `DONE.md` it reads the other outboxes to see who waits on it.
|
|
416
|
+
- **A red `main` nobody owns.** When tests fail on a fresh pull, the lane that notices fixes it first, with the smallest change, and says so in its outbox.
|
|
417
|
+
- **A lock that stops everything.** A runner that writes the session lock itself must tell the prompt the lock is its own, or every run stands down.
|
|
418
|
+
|
|
385
419
|
---
|
|
386
420
|
|
|
387
421
|
## Part C: Templates
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: K-0002
|
|
3
|
+
type: gotcha
|
|
4
|
+
status: confirmed
|
|
5
|
+
scope: project
|
|
6
|
+
triggers:
|
|
7
|
+
errors: ["browserType.launch: Executable doesn't exist", "Failed to download Chromium"]
|
|
8
|
+
files: [playwright.config.ts]
|
|
9
|
+
commands: [npx playwright install chromium]
|
|
10
|
+
topics: [playwright, sandbox, e2e]
|
|
11
|
+
evidence: seen in a cloud sandbox where the browser download is blocked; a preinstalled Chromium was used instead (example entry)
|
|
12
|
+
created: 2026-01-01
|
|
13
|
+
fired: 0
|
|
14
|
+
helped: 0
|
|
15
|
+
---
|
|
16
|
+
In a locked-down sandbox `npx playwright install chromium` can fail. Do not retry it. Point the config at a preinstalled Chromium or a system Chrome, and if Chrome then fails over missing libraries, run `npx playwright install-deps chromium`. Record the working setup in the project's Commands section once.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: K-0003
|
|
3
|
+
type: decision
|
|
4
|
+
status: confirmed
|
|
5
|
+
scope: project
|
|
6
|
+
triggers:
|
|
7
|
+
errors: ["Nothing to do"]
|
|
8
|
+
files: [".ai/session.lock"]
|
|
9
|
+
commands: [loop run]
|
|
10
|
+
topics: [session lock, unattended, stands down]
|
|
11
|
+
evidence: every scheduled run ended with "Nothing to do" because the runner wrote the lock the prompt then respected (example entry)
|
|
12
|
+
created: 2026-01-01
|
|
13
|
+
fired: 0
|
|
14
|
+
helped: 0
|
|
15
|
+
---
|
|
16
|
+
If the runner writes `.ai/session.lock` itself, the prompt must be told the lock is its own, or every run stands down at step one. Treat only a lock this session did not create as another run's. Keep `**/session.lock` in `.gitignore` so per-lane locks are never committed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compounding-loop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Write the brief, check back tomorrow: unattended, verified Claude Code build loops with a local dashboard.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"type": "git",
|
|
25
25
|
"url": "git+https://github.com/Minntooos/compounding-loop.git"
|
|
26
26
|
},
|
|
27
|
+
"homepage": "https://github.com/Minntooos/compounding-loop#readme",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/Minntooos/compounding-loop/issues"
|
|
30
|
+
},
|
|
27
31
|
"keywords": [
|
|
28
32
|
"claude-code",
|
|
29
33
|
"ai-agents",
|
|
@@ -43,6 +47,7 @@
|
|
|
43
47
|
"preview": "vite preview --port 4173 --strictPort"
|
|
44
48
|
},
|
|
45
49
|
"devDependencies": {
|
|
50
|
+
"@axe-core/playwright": "^4.13.0",
|
|
46
51
|
"@fontsource-variable/inter": "^5.3.0",
|
|
47
52
|
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
48
53
|
"@playwright/test": "^1.64.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compounding-loop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Run unattended, verified Claude Code build loops: set up a repo, hand off between windows, run retros and escape stuck loops.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": ["loop", "unattended", "verification", "handoff", "retro"]
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Compounding Loop: one scheduled job per lane on GitHub Actions.
|
|
2
|
+
#
|
|
3
|
+
# Setup (once):
|
|
4
|
+
# 1. Add ONE repository secret under Settings > Secrets and variables > Actions:
|
|
5
|
+
# ANTHROPIC_API_KEY (API billing), or
|
|
6
|
+
# CLAUDE_CODE_OAUTH_TOKEN (a Claude subscription; create it with `claude setup-token`).
|
|
7
|
+
# Secrets are referenced by name only. Never write a key into this file or the repository.
|
|
8
|
+
# 2. Copy this file to .github/workflows/lanes.yml.
|
|
9
|
+
# 3. The `verify` job runs `loop check-lanes`, which needs compounding-loop 0.2.0 or later.
|
|
10
|
+
# 4. Edit the `lane` and `cron` pairs in the matrix to match .ai/lanes.json, and keep the same
|
|
11
|
+
# cron strings in `on.schedule` (see runners/routine/README.md for staggering N lanes).
|
|
12
|
+
#
|
|
13
|
+
# How it runs: every cron line starts the whole matrix, and each job does real work only when
|
|
14
|
+
# the schedule that fired is its own (or when started by hand), so one lane runs per trigger.
|
|
15
|
+
name: compounding-loop-lanes
|
|
16
|
+
|
|
17
|
+
on:
|
|
18
|
+
schedule:
|
|
19
|
+
- cron: "7 * * * *"
|
|
20
|
+
- cron: "19 * * * *"
|
|
21
|
+
- cron: "31 * * * *"
|
|
22
|
+
workflow_dispatch: {}
|
|
23
|
+
|
|
24
|
+
# Workflow-level default: read only. The lane job raises it, because the lane pushes to main.
|
|
25
|
+
permissions:
|
|
26
|
+
contents: read
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
lane:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
timeout-minutes: 60
|
|
32
|
+
permissions:
|
|
33
|
+
contents: write # the lane commits and pushes to main
|
|
34
|
+
strategy:
|
|
35
|
+
fail-fast: false
|
|
36
|
+
max-parallel: 1 # lanes never run at the same time, so two pushes cannot race
|
|
37
|
+
matrix:
|
|
38
|
+
include:
|
|
39
|
+
- { lane: api, cron: "7 * * * *" }
|
|
40
|
+
- { lane: web, cron: "19 * * * *" }
|
|
41
|
+
- { lane: docs, cron: "31 * * * *" }
|
|
42
|
+
# A lane never overlaps its own previous run; queue instead of cancelling one mid-push.
|
|
43
|
+
concurrency:
|
|
44
|
+
group: loop-lane-${{ matrix.lane }}
|
|
45
|
+
cancel-in-progress: false
|
|
46
|
+
steps:
|
|
47
|
+
- name: Is this lane's turn?
|
|
48
|
+
id: gate
|
|
49
|
+
env:
|
|
50
|
+
EVENT: ${{ github.event_name }}
|
|
51
|
+
FIRED: ${{ github.event.schedule }}
|
|
52
|
+
MINE: ${{ matrix.cron }}
|
|
53
|
+
run: |
|
|
54
|
+
if [ "$EVENT" = "workflow_dispatch" ] || [ "$FIRED" = "$MINE" ]; then echo "go=true" >> "$GITHUB_OUTPUT"; else echo "go=false" >> "$GITHUB_OUTPUT"; fi
|
|
55
|
+
- uses: actions/checkout@v4
|
|
56
|
+
if: steps.gate.outputs.go == 'true'
|
|
57
|
+
with:
|
|
58
|
+
ref: main
|
|
59
|
+
fetch-depth: 0
|
|
60
|
+
# The lane pushes, so the checkout token must stay in git's config (persist-credentials: true,
|
|
61
|
+
# the default). A job that only reads, like `verify` below, sets it to false.
|
|
62
|
+
persist-credentials: true
|
|
63
|
+
- uses: actions/setup-node@v4
|
|
64
|
+
if: steps.gate.outputs.go == 'true'
|
|
65
|
+
with:
|
|
66
|
+
node-version: 22
|
|
67
|
+
- uses: anthropics/claude-code-action@v1
|
|
68
|
+
if: steps.gate.outputs.go == 'true'
|
|
69
|
+
with:
|
|
70
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
71
|
+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
72
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
73
|
+
# Paste runners/routine/lane-prompt.md here with {{name}} and {{lane}} filled
|
|
74
|
+
# ("${{ matrix.lane }}" works for the lane). Short form shown:
|
|
75
|
+
prompt: |
|
|
76
|
+
You are one scheduled run of the ${{ matrix.lane }} lane of an unattended build loop.
|
|
77
|
+
Read CLAUDE.md and follow it exactly. Your task file is .ai/lanes/${{ matrix.lane }}/task.md.
|
|
78
|
+
Every commit ends with the trailer line "Lane: ${{ matrix.lane }}". Push to main, never to a claude/ branch.
|
|
79
|
+
Stop if DONE.md or BLOCKED.md exists at the root or in .ai/lanes/${{ matrix.lane }}/.
|
|
80
|
+
Never publish, spend money, or add secrets or tracking.
|
|
81
|
+
claude_args: >-
|
|
82
|
+
--max-turns 200
|
|
83
|
+
--allowedTools "Bash,Edit,Write,Read,Glob,Grep,Task"
|
|
84
|
+
|
|
85
|
+
verify:
|
|
86
|
+
# Read-only: reports any commit that crossed a lane boundary.
|
|
87
|
+
needs: lane
|
|
88
|
+
if: ${{ always() }}
|
|
89
|
+
runs-on: ubuntu-latest
|
|
90
|
+
timeout-minutes: 10
|
|
91
|
+
steps:
|
|
92
|
+
- uses: actions/checkout@v4
|
|
93
|
+
with:
|
|
94
|
+
ref: main
|
|
95
|
+
fetch-depth: 0
|
|
96
|
+
persist-credentials: false # nothing here pushes, so the token is not left in the checkout
|
|
97
|
+
- uses: actions/setup-node@v4
|
|
98
|
+
with:
|
|
99
|
+
node-version: 22
|
|
100
|
+
- name: Check lane ownership
|
|
101
|
+
run: |
|
|
102
|
+
BASE=$(git rev-parse --verify -q "origin/main~20" || git rev-list --max-parents=0 HEAD | tail -1) # a young repo has fewer than 20 commits
|
|
103
|
+
npx --yes compounding-loop check-lanes --range "$BASE..HEAD"
|
package/runners/local/README.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Runs the loop from your own machine on a schedule. Needs Node 20+, git and the `claude` CLI signed in to your account.
|
|
4
4
|
|
|
5
|
-
- **
|
|
5
|
+
- **Linux:** paste the line from `crontab.txt` into `crontab -e` and set your repository path.
|
|
6
|
+
- **macOS:** `crontab.txt` works too, but launchd is the native scheduler and runs missed rounds after sleep. Edit the paths in `launchd.plist` (`loop` from `which loop`, your repository, the log file), copy it to `~/Library/LaunchAgents/` and load it with the `launchctl bootstrap` line at the top of the file. The plist runs `loop run` hourly at minute 17.
|
|
6
7
|
- **Windows:** edit the path in `task-scheduler.xml`, then import it with `schtasks /Create /XML task-scheduler.xml /TN CompoundingLoop`. The `claude.exe` native build must be on your PATH. Add `--skip-permissions` to the task's arguments only in a repository you trust, so the unattended run can edit files.
|
|
7
8
|
|
|
8
9
|
Both use the minute `17`; change it so several loops do not start together. Each run is capped by the session lock (90 minutes) and the run budget (`Run: N / 30`).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<!-- Compounding Loop on macOS: run one round at minute 17 of every hour.
|
|
4
|
+
Install: cp launchd.plist ~/Library/LaunchAgents/com.example.compounding-loop.plist
|
|
5
|
+
(edit the paths below first), then
|
|
6
|
+
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.compounding-loop.plist
|
|
7
|
+
Remove: launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.example.compounding-loop.plist
|
|
8
|
+
For unattended edits add <string>--skip-permissions</string> after <string>run</string> below, only in a repository you trust.
|
|
9
|
+
launchd has a minimal PATH, so PATH below must contain the folders of `node`, `loop` and `claude`. -->
|
|
10
|
+
<plist version="1.0">
|
|
11
|
+
<dict>
|
|
12
|
+
<key>Label</key>
|
|
13
|
+
<string>com.example.compounding-loop</string>
|
|
14
|
+
<key>ProgramArguments</key>
|
|
15
|
+
<array>
|
|
16
|
+
<string>/usr/local/bin/loop</string>
|
|
17
|
+
<string>run</string>
|
|
18
|
+
</array>
|
|
19
|
+
<key>WorkingDirectory</key>
|
|
20
|
+
<string>/path/to/repo</string>
|
|
21
|
+
<key>EnvironmentVariables</key>
|
|
22
|
+
<dict>
|
|
23
|
+
<key>PATH</key>
|
|
24
|
+
<string>/usr/local/bin:/opt/homebrew/bin:/path/to/home/.local/bin:/usr/bin:/bin</string>
|
|
25
|
+
</dict>
|
|
26
|
+
<key>StartCalendarInterval</key>
|
|
27
|
+
<dict>
|
|
28
|
+
<key>Minute</key>
|
|
29
|
+
<integer>17</integer>
|
|
30
|
+
</dict>
|
|
31
|
+
<key>StandardOutPath</key>
|
|
32
|
+
<string>/path/to/repo/.ai/loop-cron.log</string>
|
|
33
|
+
<key>StandardErrorPath</key>
|
|
34
|
+
<string>/path/to/repo/.ai/loop-cron.log</string>
|
|
35
|
+
</dict>
|
|
36
|
+
</plist>
|
|
@@ -12,8 +12,45 @@ A routine is a scheduled Claude Code session in the cloud. It runs `prompt.md` a
|
|
|
12
12
|
|
|
13
13
|
If several loops (or several lanes of one project) run against one repository, give each a different minute so they do not collide. Use the pattern `M * * * *` (hourly), and move `M` by at least 10 minutes per loop, for example `7 * * * *`, `17 * * * *`, `27 * * * *`. Avoid minute `0`: everyone's schedule lands there.
|
|
14
14
|
|
|
15
|
-
Each run stops after about 40 minutes of work, so an hourly schedule leaves a gap before the next one. The 90-minute session lock (`.ai/session.lock`) stops two runs from working at once. The run budget (`Run: N / 30` in `.ai/task.md`) caps the total number of runs.
|
|
15
|
+
Each run stops after about 40 minutes of work, so an hourly schedule leaves a gap before the next one. The 90-minute session lock (`.ai/session.lock`, or `.ai/lanes/<lane>/session.lock` per lane) stops two runs from working at once. The run budget (`Run: N / 30` in `.ai/task.md`) caps the total number of runs.
|
|
16
16
|
|
|
17
17
|
## Safety
|
|
18
18
|
|
|
19
19
|
The prompt never publishes, spends money or stores a secret. The routine uses your Claude account; no API key goes into the repository.
|
|
20
|
+
|
|
21
|
+
## Lanes
|
|
22
|
+
|
|
23
|
+
With several lanes, create one routine per lane plus one control room routine.
|
|
24
|
+
|
|
25
|
+
| File | Placeholders | Routine |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| `lane-prompt.md` | `{{name}}` project, `{{lane}}` lane name | one per lane, hourly |
|
|
28
|
+
| `control-room-prompt.md` | `{{name}}` project, `{{lanes}}` lane names, comma separated | one, every 3 hours |
|
|
29
|
+
|
|
30
|
+
`loop run --lane <name>` fills them for local runs. In the cloud, paste the filled text into the routine. In the cloud nothing writes the lock; it only matters when a local `loop run --lane` is active too. The lane prompt stands down on a fresh `.ai/lanes/<lane>/session.lock`, stops on root or lane `DONE.md`/`BLOCKED.md`, and ends every commit with `Lane: <lane>`.
|
|
31
|
+
|
|
32
|
+
### Staggered cron for N lanes
|
|
33
|
+
|
|
34
|
+
Give lane `i` (counting from 0) the minute `7 + i * step` with `step = 12` for up to five lanes (more lanes: use a smaller step, but keep minutes distinct), and put the control room on its own minute every 3 hours. Runs last up to 40 minutes, so neighbouring lanes overlap; that is expected. Path ownership plus `git pull --rebase` before each push make it safe, and the stagger only spreads the pulls out. Avoid minute 0.
|
|
35
|
+
|
|
36
|
+
3 lanes (`api`, `web`, `docs`):
|
|
37
|
+
|
|
38
|
+
| Routine | Cron |
|
|
39
|
+
|---|---|
|
|
40
|
+
| api | `7 * * * *` |
|
|
41
|
+
| web | `19 * * * *` |
|
|
42
|
+
| docs | `31 * * * *` |
|
|
43
|
+
| control room | `43 */3 * * *` |
|
|
44
|
+
|
|
45
|
+
5 lanes (`core`, `kit`, `server`, `web`, `docs`, as in this repository):
|
|
46
|
+
|
|
47
|
+
| Routine | Cron |
|
|
48
|
+
|---|---|
|
|
49
|
+
| core | `7 * * * *` |
|
|
50
|
+
| kit | `19 * * * *` |
|
|
51
|
+
| server | `31 * * * *` |
|
|
52
|
+
| web | `43 * * * *` |
|
|
53
|
+
| docs | `55 * * * *` |
|
|
54
|
+
| control room | `3 */3 * * *` |
|
|
55
|
+
|
|
56
|
+
Each lane run stops after about 40 minutes, so an hourly lane leaves a gap. Runs that end in about a second usually hit the plan's usage limit, not a bug; fewer or slower lanes cost less.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
You are the scheduled "control room" run for this repository ({{name}}). The lanes ({{lanes}}) push to `main` on their own schedules; you do the supervision the owner would otherwise do by hand. Nobody is watching; the owner reads your final summary later. Be brief and cheap. Read CLAUDE.md (lane table, hard rules, Unattended mode) and IDEA.md (especially "Done when").
|
|
2
|
+
|
|
3
|
+
0. Run `date -u`. `git checkout main && git pull --ff-only`. Push only to `main`; if a push is rejected, `git pull --rebase` and push again. Never force-push. Verify claims on a fresh pull: a stale clone is not evidence, and a lane blaming a force-push gets believed only after `git log` shows one.
|
|
4
|
+
1. If root `DONE.md` exists, give the summary (step 6) and stop.
|
|
5
|
+
2. Run the install and test commands from CLAUDE.md. If the tests are red: find the commit that broke it (`git log`, `git bisect` only if quick). If the fix is small and obvious, make it, run the tests, commit with the trailer `Lane: control` and push. Otherwise add a note to `.ai/control-room.md` addressed to the owning lane with the failing command and the likely commit. Also check lane ownership: run `git fetch --unshallow` if the clone is shallow, then `npx compounding-loop check-lanes --range <commit from 4 hours ago>..HEAD` (or `loop check-lanes` if installed), and report each violation to the lane that caused it. If the command is unavailable or errors, say so in the summary and carry on.
|
|
6
|
+
3. Leak check: the published package must contain nothing from `.ai/`, the brief, CLAUDE.md or tests (`npm pack --dry-run`, or your project's equivalent). Grep the repository for secrets (API keys, tokens, private keys) and for the owner's email address and local paths. Fix any leak immediately with the smallest change, push, and say so in the summary.
|
|
7
|
+
4. For each lane read `.ai/lanes/<lane>/task.md` (Run counter, Handoff), its outbox, and whether its `DONE.md` or `BLOCKED.md` exists. Then:
|
|
8
|
+
- BLOCKED with "run budget reached": if the lane made real progress (commits that pushed tests or features) and its task file has no line "Budget extended by control room", raise the limit by 20, add that line with the date under Decisions, `git rm` the BLOCKED.md, commit and push. If already extended once, leave it for the owner.
|
|
9
|
+
- BLOCKED with a question: if it can be answered safely from the brief and CLAUDE.md (never anything that spends money, creates accounts, publishes, posts, changes repo visibility or settings, or adds secrets), write the answer under Decisions in that lane's task file as "Answer from control room (date): ...", `git rm` the BLOCKED.md, commit and push. Otherwise leave it and put the question in your summary.
|
|
10
|
+
- A lane waiting on a lane that is already DONE: tell the waiting lane in `.ai/control-room.md` to stop waiting, list the gap as a next improvement in its DONE.md, and finish.
|
|
11
|
+
- A message in any outbox that has waited more than 3 hours for a lane that has since run without answering: copy it into `.ai/control-room.md` addressed to that lane.
|
|
12
|
+
- Lanes with no commit in the last 4 hours and no DONE/BLOCKED: note "stalled" in the summary. Runs that end in about a second are usually a plan or rate limit, not a bug.
|
|
13
|
+
5. When every `.ai/lanes/*/DONE.md` exists, check every line of the brief's "Done when" for the whole product by running its commands. If all pass, write root `DONE.md`: what was built, test results, anything unsure, and the owner's launch checklist. Commit and push. If a line fails, add a note to `.ai/control-room.md` for the owning lane, `git rm` that lane's DONE.md so it resumes, commit and push.
|
|
14
|
+
6. Commit only `.ai/` notes and the smallest fixes, each with the trailer `Lane: control`. End with one line per lane: `lane · Run N/LIMIT · building|blocked|done|stalled · last commit time · note`, then one line for tests and the leak check, then any question for the owner.
|
|
15
|
+
|
|
16
|
+
Never spend money, sign up for anything, create accounts, publish anything (no package publish, no posting, no repo visibility or settings changes, no releases), add API keys, secrets, ads, analytics or tracking, force-push, or touch anything outside this repository.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
You are one scheduled run of the **{{lane}}** lane of an unattended build loop for this repository ({{name}}). The other lanes push to the same `main` at other minutes of the hour, so expect new commits between your pulls. Nobody is watching; the owner reads the results later. Read CLAUDE.md and follow it exactly, especially the lane table, the hard rules and "Unattended mode". IDEA.md is the brief. Your task file is `.ai/lanes/{{lane}}/task.md`; wherever the Operating Card or `.ai/method.md` says `.ai/task.md`, use your task file. Every commit message you write ends with the trailer line `Lane: {{lane}}`.
|
|
2
|
+
|
|
3
|
+
0. Run `date -u` and note it as this run's start time. Run `git checkout main && git pull --ff-only`. Commit all work on `main` and push it with `git push origin main`. Never create or push a `claude/` branch. If a push is rejected, run `git pull --rebase` (follow the lockfile rule in CLAUDE.md), re-run the tests if the incoming commits touched code, and push again. If pushing to main is refused by permissions, write that under Tried in your task file, push to a branch named `claude/{{lane}}-blocked`, and stop.
|
|
4
|
+
1. Stop files: if root `DONE.md` or `BLOCKED.md` exists, or `.ai/lanes/{{lane}}/DONE.md` or `.ai/lanes/{{lane}}/BLOCKED.md` exists, reply "Nothing to do" and stop. Session lock: if `.ai/lanes/{{lane}}/session.lock` holds a UTC time less than 90 minutes old and the runner's run note does not say it is your own, another session is working this lane: reply "Nothing to do" and stop. An older lock is stale; ignore it.
|
|
5
|
+
2. Run the install command from CLAUDE.md (`npm install` unless it says otherwise). If it fails, write `.ai/lanes/{{lane}}/BLOCKED.md` with the error and stop. If the tests need a browser, install it the way CLAUDE.md says (a blocked download or a Chrome that will not launch is covered under "Commands" there). If CLAUDE.md names no commands, derive them from `package.json` and record them under Decisions.
|
|
6
|
+
3. Read `.ai/control-room.md` and every other lane's `.ai/lanes/*/outbox.md` for messages to {{lane}} or all. Run the resume protocol (.ai/method.md section 9.2) on your task file and add 1 to its run counter. If the counter has reached its limit, follow "Run budget" in CLAUDE.md.
|
|
7
|
+
4. Run the test command from CLAUDE.md (`npm test` unless it says otherwise) on the fresh pull. If it fails, fixing it is your first unit ("main stays green" in CLAUDE.md), whichever lane owns the failure (commit that fix with the trailer `Lane: control`, which `loop check-lanes` allows to touch any path, and tell the owner in your outbox). Otherwise take the next unit: a message addressed to you that another lane is waiting on, else the next item in your task file's units list. Edit only the paths your lane owns, plus the smallest needed edits to shared files. Then run the tests.
|
|
8
|
+
5. Green: commit, have a reviewer subagent check the diff against IDEA.md and your lane contract ("find the three most likely ways this is wrong for a real user, and check each"), fix any blockers, run the tests again, pull, then push to `main`. Run the reviewer in the foreground if you can. If it runs in the background you are resumed when it reports; then finish this step and continue with step 6. Ending your turn ends the run unless a subagent is still running, so never end it to wait for anything else. Red: write the attempt and the reason under Tried in your task file, then discard the unit's uncommitted changes. The same failure twice means the stuck protocol (§9.3); its last step is `.ai/lanes/{{lane}}/BLOCKED.md`.
|
|
9
|
+
6. A pushed unit does not end the run. Go straight back to step 4 with the next unit and keep going until one of these is true: 40 minutes have passed since the start time (check `date -u` after each push), your context is about 60% full, your lane has no units left, or every remaining unit is blocked. Each unit still gets its own tests, review and push.
|
|
10
|
+
7. Then run the handoff (.ai/method.md section 9.1) in your task file so a fresh run with zero memory can continue at full quality, and add a line to your outbox for any lane affected by what you shipped (new types, routes, files they are waiting for). If every done-when in your lane contract passes and the reviewer finds no blockers, follow "Lane finished" in CLAUDE.md instead: read the other lanes' outboxes first, run the retro, write `.ai/lanes/{{lane}}/DONE.md`. Commit, push to `main`, and stop.
|
|
11
|
+
|
|
12
|
+
Never spend money, sign up for anything, create accounts, publish anything (no package publish, no posting, no repo visibility or settings changes, no releases, no version bump), add API keys, secrets, ads, analytics or tracking, copy other projects' content without a compatible licence, force-push, rewrite history, edit another lane's paths beyond the "main stays green" rule, or touch anything outside this repository.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
You are one scheduled run of the build loop for this repository ({{name}}). Nobody is watching; the owner reads the results later. Read CLAUDE.md and follow it exactly, especially "Unattended mode". IDEA.md is the brief. Your task file is `.ai/task.md`.
|
|
2
2
|
|
|
3
3
|
0. Run `date -u` and note it as this run's start time. Run `git checkout main && git pull --ff-only`. Every commit goes to `main` and is pushed with `git push origin main`. Never create or push a `claude/` branch: the next run starts from `main` and would never see it. If a push is rejected, run `git pull --rebase`, re-run the checks if incoming commits touched code, and push again.
|
|
4
|
-
1. Session lock: if `.ai/session.lock` holds a UTC time less than 90 minutes old, another session owns the repo.
|
|
4
|
+
1. Session lock: if `.ai/session.lock` holds a UTC time less than 90 minutes old, another session owns the repo, unless the lock is your own (the runner's run note says so when the loop runner created it). Otherwise reply "Nothing to do" and stop. If the file is older than that, it is stale: ignore it.
|
|
5
5
|
2. If `DONE.md` or `BLOCKED.md` exists, reply "Nothing to do" and stop.
|
|
6
6
|
3. Run the install command from CLAUDE.md (`npm install` unless it says otherwise). If it fails, write `BLOCKED.md` with the error and stop.
|
|
7
7
|
4. If `.ai/task.md` exists, run the resume protocol (.ai/method.md §9.2) and add 1 to its `Run: N / 30` counter. When the counter reaches 30, write `BLOCKED.md` saying "run budget reached", push, and stop. If there is no task file, write one from IDEA.md first.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -3,7 +3,23 @@
|
|
|
3
3
|
"name": "{{name}}",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"description": "A Chrome extension built and verified by an unattended Compounding Loop.",
|
|
6
|
-
"action": {
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
"action": {
|
|
7
|
+
"default_popup": "popup.html",
|
|
8
|
+
"default_title": "{{name}}",
|
|
9
|
+
"default_icon": {
|
|
10
|
+
"16": "icons/icon16.png",
|
|
11
|
+
"48": "icons/icon48.png"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"background": {
|
|
15
|
+
"service_worker": "background.js"
|
|
16
|
+
},
|
|
17
|
+
"permissions": [
|
|
18
|
+
"storage"
|
|
19
|
+
],
|
|
20
|
+
"icons": {
|
|
21
|
+
"16": "icons/icon16.png",
|
|
22
|
+
"48": "icons/icon48.png",
|
|
23
|
+
"128": "icons/icon128.png"
|
|
24
|
+
}
|
|
9
25
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Lane templates
|
|
2
|
+
|
|
3
|
+
Files `loop init --lanes` copies into a repository that runs several lanes. A lane is one scheduled run that owns one part of the codebase (see `method/COMPOUNDING_LOOP.md`, chapter "Lanes").
|
|
4
|
+
|
|
5
|
+
| File | Written to | Once per |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| `task.md` | `.ai/lanes/<lane>/task.md` | lane |
|
|
8
|
+
| `outbox.md` | `.ai/lanes/<lane>/outbox.md` | lane |
|
|
9
|
+
| `control-room.md` | `.ai/control-room.md` | repo |
|
|
10
|
+
| `claude-section.md` | appended to `CLAUDE.md` and `AGENTS.md`; `loop init --lanes` wraps it in its own markers, so a second run replaces the block | repo |
|
|
11
|
+
| `ci-check-lanes.yml` | `.github/workflows/check-lanes.yml` | repo |
|
|
12
|
+
|
|
13
|
+
## Placeholders
|
|
14
|
+
|
|
15
|
+
Written `{{lane}}`. `loop init --lanes` fills only the ones listed below (`control-room.md` and `ci-check-lanes.yml` have none). A template never ships with one unfilled: `tests/unit/kit/lanes-templates.test.ts` fills every documented placeholder and fails on any `{{x}}` left over. GitHub Actions expressions (`${{ ... }}`) are not placeholders and are left alone.
|
|
16
|
+
|
|
17
|
+
| Placeholder | Meaning | Example |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `{{lane}}` | Lane name (lowercase letters, digits, `-`) | `api` |
|
|
20
|
+
| `{{owns}}` | The lane's owned globs, comma separated | `src/api/**, tests/api/**` |
|
|
21
|
+
| `{{lanes}}` | All lane names, comma separated | `api, web, docs` |
|
|
22
|
+
|
|
23
|
+
`.ai/lanes.json` stays the machine-readable source of truth; `claude-section.md` points to it instead of copying the lane table, so the two cannot drift.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Fails a push or pull request whose commits touch another lane's paths.
|
|
2
|
+
# Needs compounding-loop 0.2.0 or later (the release that adds `check-lanes`). Copy to .github/workflows/check-lanes.yml. Commits without a `Lane:` trailer are reported, not failed.
|
|
3
|
+
name: check-lanes
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
check-lanes:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0 # a shallow clone skips its oldest commit
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
- uses: actions/setup-node@v4
|
|
22
|
+
with:
|
|
23
|
+
node-version: 22
|
|
24
|
+
- name: Check lane ownership
|
|
25
|
+
env:
|
|
26
|
+
EVENT: ${{ github.event_name }}
|
|
27
|
+
BASE_REF: ${{ github.base_ref }}
|
|
28
|
+
BEFORE: ${{ github.event.before }}
|
|
29
|
+
SHA: ${{ github.sha }}
|
|
30
|
+
run: |
|
|
31
|
+
if [ "$EVENT" = "pull_request" ]; then
|
|
32
|
+
RANGE="origin/$BASE_REF..HEAD"
|
|
33
|
+
elif git cat-file -e "$BEFORE^{commit}" 2>/dev/null; then
|
|
34
|
+
RANGE="$BEFORE..$SHA" # not the case on a first push or after a force-push
|
|
35
|
+
else
|
|
36
|
+
RANGE="$SHA~1..$SHA"
|
|
37
|
+
fi
|
|
38
|
+
npx --yes compounding-loop check-lanes --range "$RANGE"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
## Lanes
|
|
2
|
+
|
|
3
|
+
This repository is built by parallel **lanes**. Lanes in this repo: {{lanes}}. Each lane is a scheduled run that owns one part of the codebase. `.ai/lanes.json` says which paths each lane owns and which files are shared; read it first. Each lane's task file is `.ai/lanes/<lane>/task.md`.
|
|
4
|
+
|
|
5
|
+
- **Edit only the paths your lane owns.** Read anything. **Shared files** (`shared` in `.ai/lanes.json`) may be edited by any lane, with the smallest possible edit.
|
|
6
|
+
- **Commit trailer:** every commit message ends with a line `Lane: <your lane>` (the control room uses `Lane: control`). `loop check-lanes` fails a commit that touches another lane's paths.
|
|
7
|
+
- **Talking to other lanes:** write only to your own `.ai/lanes/<you>/outbox.md`, newest first, `YYYY-MM-DD HH:MM UTC · to <lane|all> · message`. At the start of each run read every other lane's outbox and `.ai/control-room.md` for messages to you or `all`, and act on them or say in your task file why not.
|
|
8
|
+
- **Main stays green.** If the tests fail on a fresh pull before you change anything, fixing that is your first unit, whichever lane owns the failure. Make the smallest fix, commit it with the trailer `Lane: control` (the only trailer `loop check-lanes` lets touch any path), and tell the owning lane in your outbox.
|
|
9
|
+
- **Git:** work on `main`. Start every run with `git checkout main && git pull --ff-only`, pull again right before each push, and on a rejected push `git pull --rebase`. If a rebase conflicts in a file you do not own, `git rebase --abort`, record it under Tried, pull fresh, redo your small change on top. Never force-push.
|
|
10
|
+
- **Run budget:** your lane's task file holds `Run: N / LIMIT`. Add 1 at the start of every run; at the limit write `.ai/lanes/<lane>/BLOCKED.md` ("run budget reached") and stop.
|
|
11
|
+
- **Stop files:** if root `DONE.md` or `BLOCKED.md`, or your lane's `DONE.md` or `BLOCKED.md`, exists at the start of a run, do nothing and stop. A lane writes its own `DONE.md` when every done-when in its contract passes and the reviewer finds no blockers. Root `DONE.md` is written only by the control room.
|
|
12
|
+
- **Stuck:** the same failure twice means the stuck protocol; its last step is the lane's `BLOCKED.md` with what you tried, the specific question and your best guess.
|
|
13
|
+
- **Review before every push to `main`:** the tests must pass, then a reviewer subagent that did not write the code checks the diff against the brief ("find the three most likely ways this is wrong for a real user, and check each"). Fix blockers first.
|
|
14
|
+
- **Questions:** no human is watching. Decide from the brief and common sense, write the decision and the reason under **Decisions** in your task file, and pick the option that is easiest to reverse.
|
|
15
|
+
- **Lane finished:** read the other lanes' outboxes first; if anyone waits on you, do that before writing `DONE.md`.
|
|
16
|
+
- **Shared files and shared types** are add-only for other lanes: add fields, never rename or remove, and say so in your outbox.
|
|
17
|
+
- **Never:** spend money, sign up for anything, publish anything, add secrets, telemetry or tracking, or touch anything outside this repository.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Outbox: {{lane}}
|
|
2
|
+
|
|
3
|
+
Written only by the {{lane}} lane. Newest first. Format: `YYYY-MM-DD HH:MM UTC · to <lane|all> · message`.
|
|
4
|
+
Every lane reads every outbox at the start of a run. Say what you shipped that another lane waits on (new types, routes, files), and what you need from them. Point to code with `path:line`; do not paste it.
|
|
5
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Lane: {{lane}}
|
|
2
|
+
|
|
3
|
+
Run: 0 / 20
|
|
4
|
+
Status: not started
|
|
5
|
+
|
|
6
|
+
## Contract
|
|
7
|
+
**Goal:** <one sentence: what this lane delivers>
|
|
8
|
+
**Owns (globs from `.ai/lanes.json`):** {{owns}}
|
|
9
|
+
**Done when:**
|
|
10
|
+
- `npm test` passes (or the project's test command from CLAUDE.md).
|
|
11
|
+
- <a checkable line per deliverable, each provable by a command>
|
|
12
|
+
**Constraints:** edit only the paths you own, plus the smallest edit to shared files (CLAUDE.md "Lanes"). Read anything.
|
|
13
|
+
**Out of scope:** <what belongs to another lane>
|
|
14
|
+
|
|
15
|
+
## Units, in order
|
|
16
|
+
1. **Audit** (`.ai/audit/{{lane}}.md`): review your area as a demanding specialist; findings ranked blocker / major / minor, each with evidence and a one-line fix.
|
|
17
|
+
2. <first real unit>
|
|
18
|
+
|
|
19
|
+
## Decisions
|
|
20
|
+
<decision → reason. Written instead of asking a human.>
|
|
21
|
+
|
|
22
|
+
## Confirmed
|
|
23
|
+
<fact → how we know>
|
|
24
|
+
|
|
25
|
+
## Guesses
|
|
26
|
+
<assumption → what would prove it. Never treat a GUESS from an earlier window as fact.>
|
|
27
|
+
|
|
28
|
+
## Tried
|
|
29
|
+
<attempt → exact error → why it failed>
|
|
30
|
+
|
|
31
|
+
## Don't
|
|
32
|
+
- Edit paths another lane owns (a fix to a red `main` is the one exception; say so in your outbox).
|
|
33
|
+
- Change the Operating Card, the version, or publish anything.
|
|
34
|
+
|
|
35
|
+
## Handoff
|
|
36
|
+
<Rewritten at the end of every run: commit, whether the tree is clean, the single next step, what to read first.>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
You are one run of an unattended build loop for this repository. Nobody is watching. Follow CLAUDE.md exactly, especially "Unattended mode".
|
|
2
2
|
|
|
3
3
|
0. Run `git checkout main && git pull --ff-only`. Every commit in this run goes to `main` and is pushed with `git push origin main`. Never push to a `claude/` branch: the next run starts from `main` and would never see it.
|
|
4
|
-
1. If `.ai/session.lock` holds a UTC time less than 90 minutes old, another session owns the repo: reply "Nothing to do" and stop. If `DONE.md` or `BLOCKED.md` exists, reply "Nothing to do" and stop.
|
|
4
|
+
1. If `.ai/session.lock` holds a UTC time less than 90 minutes old, another session owns the repo (unless the lock is your own: the run note at the end of this prompt says so when the loop runner created it): reply "Nothing to do" and stop. If `DONE.md` or `BLOCKED.md` exists, reply "Nothing to do" and stop.
|
|
5
5
|
2. Run `npm install`, then `npx playwright install chromium`. If the download is blocked, see "Commands" in CLAUDE.md.
|
|
6
6
|
3. If `.ai/task.md` exists, run the resume protocol (.ai/method.md §9.2) and add 1 to its `Run: N / 30` counter. If the counter has reached its limit, follow "Run budget" in CLAUDE.md. Otherwise start the task from IDEA.md.
|
|
7
7
|
4. Do one unit of work, then run `npm test`.
|