compounding-loop 0.0.0-stage → 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/LICENSE +21 -0
- package/README.md +123 -3
- package/bin/loop.js +2 -0
- package/dist/cli/checkLanes.js +60 -0
- package/dist/cli/checkLanes.js.map +1 -0
- package/dist/cli/dashboard.js +52 -0
- package/dist/cli/dashboard.js.map +1 -0
- package/dist/cli/doctor.js +54 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/gh.js +29 -0
- package/dist/cli/gh.js.map +1 -0
- package/dist/cli/index.js +228 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.js +73 -0
- package/dist/cli/init.js.map +1 -0
- 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 +92 -0
- package/dist/cli/loops.js.map +1 -0
- package/dist/cli/new.js +54 -0
- package/dist/cli/new.js.map +1 -0
- package/dist/cli/run.js +169 -0
- package/dist/cli/run.js.map +1 -0
- package/dist/core/blocked.js +42 -0
- package/dist/core/blocked.js.map +1 -0
- package/dist/core/brief.js +91 -0
- package/dist/core/brief.js.map +1 -0
- package/dist/core/doctor.js +49 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/gh.js +2 -0
- package/dist/core/gh.js.map +1 -0
- package/dist/core/init.js +59 -0
- package/dist/core/init.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/repo.js +81 -0
- package/dist/core/repo.js.map +1 -0
- package/dist/core/rounds.js +50 -0
- package/dist/core/rounds.js.map +1 -0
- package/dist/core/runPrompt.js +31 -0
- package/dist/core/runPrompt.js.map +1 -0
- package/dist/core/status.js +21 -0
- package/dist/core/status.js.map +1 -0
- package/dist/core/table.js +43 -0
- package/dist/core/table.js.map +1 -0
- package/dist/core/template.js +29 -0
- package/dist/core/template.js.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/server/data.js +50 -0
- package/dist/server/data.js.map +1 -0
- package/dist/server/events.js +106 -0
- package/dist/server/events.js.map +1 -0
- package/dist/server/five-sites.json +3071 -0
- package/dist/server/health.js +193 -0
- package/dist/server/health.js.map +1 -0
- package/dist/server/index.js +134 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/loops.js +145 -0
- package/dist/server/loops.js.map +1 -0
- package/dist/server/settings.js +25 -0
- package/dist/server/settings.js.map +1 -0
- package/dist/server/static-export.js +28 -0
- package/dist/server/static-export.js.map +1 -0
- package/dist/server/static.js +80 -0
- package/dist/server/static.js.map +1 -0
- 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/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/dist/web/favicon.svg +1 -0
- package/dist/web/index.html +14 -0
- package/dist/web/logo.svg +1 -0
- package/method/COMPOUNDING_LOOP.md +603 -0
- package/method/example/K-0001.md +16 -0
- package/method/example/K-0002.md +16 -0
- package/method/example/K-0003.md +16 -0
- package/method/knowledge-index.md +5 -0
- package/method/operating-card.md +40 -0
- package/method/retro.md +10 -0
- package/method/reviewer-prompt.md +13 -0
- package/method/stuck-protocol.md +12 -0
- package/method/task-template.md +41 -0
- package/package.json +76 -4
- package/plugin/.claude-plugin/plugin.json +7 -0
- package/plugin/commands/handoff.md +10 -0
- package/plugin/commands/loop-init.md +13 -0
- package/plugin/commands/retro.md +11 -0
- package/plugin/commands/stuck.md +10 -0
- package/runners/github-actions/lanes.yml +103 -0
- package/runners/github-actions/loop.yml +51 -0
- package/runners/local/README.md +9 -0
- package/runners/local/crontab.txt +6 -0
- package/runners/local/launchd.plist +36 -0
- package/runners/local/task-scheduler.xml +32 -0
- package/runners/routine/README.md +56 -0
- package/runners/routine/control-room-prompt.md +16 -0
- package/runners/routine/lane-prompt.md +12 -0
- package/runners/routine/prompt.md +13 -0
- package/templates/chrome-extension/CLAUDE.md +18 -0
- package/templates/chrome-extension/IDEA.md +22 -0
- package/templates/chrome-extension/extension/background.js +5 -0
- 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 +25 -0
- package/templates/chrome-extension/extension/popup.css +3 -0
- package/templates/chrome-extension/extension/popup.html +15 -0
- package/templates/chrome-extension/extension/popup.js +16 -0
- package/templates/chrome-extension/gitignore +6 -0
- package/templates/chrome-extension/package.json +12 -0
- package/templates/chrome-extension/playwright.config.js +22 -0
- package/templates/chrome-extension/scripts/check.mjs +51 -0
- package/templates/chrome-extension/scripts/test.mjs +59 -0
- package/templates/chrome-extension/tests/popup.spec.js +37 -0
- 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/index.md +5 -0
- package/templates/static-site/.ai/log.md +3 -0
- package/templates/static-site/.ai/loop-prompt.md +10 -0
- package/templates/static-site/.ai/task.md +27 -0
- package/templates/static-site/.gitattributes +1 -0
- package/templates/static-site/CLAUDE.md +113 -0
- package/templates/static-site/IDEA.md +36 -0
- package/templates/static-site/gitignore +6 -0
- package/templates/static-site/netlify.toml +14 -0
- package/templates/static-site/package.json +14 -0
- package/templates/static-site/playwright.config.js +27 -0
- package/templates/static-site/scripts/check.mjs +188 -0
- package/templates/static-site/scripts/serve.mjs +44 -0
- package/templates/static-site/scripts/test.mjs +59 -0
- package/templates/static-site/site/index.html +23 -0
- package/templates/static-site/site/robots.txt +4 -0
- package/templates/static-site/site/sitemap.xml +4 -0
- package/templates/static-site/tests/pages.spec.js +21 -0
- package/templates/static-site/vercel.json +19 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: K-0001
|
|
3
|
+
type: gotcha
|
|
4
|
+
status: confirmed
|
|
5
|
+
scope: project
|
|
6
|
+
triggers:
|
|
7
|
+
errors: ["fetch is not a function"]
|
|
8
|
+
files: []
|
|
9
|
+
commands: []
|
|
10
|
+
topics: [node version, fetch]
|
|
11
|
+
evidence: reproduced on Node 16, fixed on Node 20 (example entry)
|
|
12
|
+
created: 2026-01-01
|
|
13
|
+
fired: 0
|
|
14
|
+
helped: 0
|
|
15
|
+
---
|
|
16
|
+
Global `fetch` needs Node 18 or newer. Check `node --version` before debugging the call itself.
|
|
@@ -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.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
## Operating Card (the Compounding Loop)
|
|
2
|
+
|
|
3
|
+
> The full method is in `.ai/method.md`. The § numbers point to Part B there.
|
|
4
|
+
|
|
5
|
+
**Objective:** the most verified-correct results per minute of the owner's attention. Tokens are the cheapest resource. A wrong result that looks right is the most expensive one.
|
|
6
|
+
|
|
7
|
+
**Start of every session**
|
|
8
|
+
1. Read `.ai/index.md`. Open a knowledge entry only when it's relevant to what you're doing.
|
|
9
|
+
2. If `.ai/task.md` exists, run the resume protocol (§9.2) before anything else.
|
|
10
|
+
|
|
11
|
+
**Before work**
|
|
12
|
+
3. Size the task. Tiny: just do it. Medium: contract + checks. Large or unclear: plan, then loop.
|
|
13
|
+
4. For medium and large tasks, write the contract (§5): goal, done-when (checkable by a command), constraints, out of scope.
|
|
14
|
+
|
|
15
|
+
**During work**
|
|
16
|
+
5. One unit of work at a time. Run the checks after each unit. You never declare done; the checks do.
|
|
17
|
+
6. Write a note the moment you learn something, marked CONFIRMED or GUESS. Don't save it for the end.
|
|
18
|
+
7. After any error, search `.ai/index.md` for the error text before trying a fix.
|
|
19
|
+
8. Before editing a file, check the index for entries about that path.
|
|
20
|
+
9. Send reading-heavy work (search, docs, logs, review) to subagents that report back in 10 lines or fewer. Keep code-writing in one thread.
|
|
21
|
+
10. Same failure twice: stop and run the stuck protocol (§9.3). Don't retry a variant.
|
|
22
|
+
11. If reality contradicts the plan or contract, stop and record it. Don't silently work around it.
|
|
23
|
+
12. Point to code and docs (`path:line`). Never copy them into notes.
|
|
24
|
+
|
|
25
|
+
**Context**
|
|
26
|
+
13. At ~50–60% context, or when a unit is done and the next one is big: run the handoff (§9.1) and end the run.
|
|
27
|
+
|
|
28
|
+
**Finishing**
|
|
29
|
+
14. When the checks pass, run the retro (§12), log the task and delete `.ai/task.md`.
|
|
30
|
+
|
|
31
|
+
**Unattended runs** (no human watching; the owner reads the results later)
|
|
32
|
+
- Session lock: if `.ai/session.lock` shows another run started less than 90 minutes ago, do nothing and stop.
|
|
33
|
+
- Run budget: `.ai/task.md` holds `Run: N / 30`. Add 1 at the start of every run; at 30, write `BLOCKED.md` ("run budget reached") and stop.
|
|
34
|
+
- If `DONE.md` or `BLOCKED.md` exists, do nothing and stop. Write `DONE.md` only when every done-when passes; write `BLOCKED.md` (tried, the question, best guess) when stuck.
|
|
35
|
+
|
|
36
|
+
**Never**
|
|
37
|
+
- Mark your own work done without the checks passing.
|
|
38
|
+
- Treat a GUESS from an earlier window as fact.
|
|
39
|
+
- Delete or rewrite CONFIRMED knowledge without new evidence.
|
|
40
|
+
- Change this card.
|
package/method/retro.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Retro
|
|
2
|
+
|
|
3
|
+
Run it when the checks pass, before deleting `.ai/task.md`. About two minutes.
|
|
4
|
+
|
|
5
|
+
1. List what was learned. For each item: does it apply beyond this task? Is it confirmed or a guess? At what moment would it have helped (file, command or error)?
|
|
6
|
+
2. Move general, confirmed items down the promotion ladder as far as they go: `guess → confirmed note → note with a trigger → automated check`. The best memory is one the model never has to read: turn a rule into a lint, test or script and delete the prose.
|
|
7
|
+
3. Write each kept lesson as a knowledge entry (`.ai/knowledge/K-NNNN.md`, see `method/example/K-0001.md`) with triggers, and add one line to `.ai/index.md`.
|
|
8
|
+
4. Flag entries this task showed to be wrong or stale. Never delete a CONFIRMED entry without new evidence.
|
|
9
|
+
5. Add one line to `.ai/log.md`: date, task, checks result, runs used, what was learned.
|
|
10
|
+
6. Delete `.ai/task.md`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Reviewer prompt
|
|
2
|
+
|
|
3
|
+
Give this to a subagent that did not write the code. It sees the diff and the brief, not your reasoning.
|
|
4
|
+
|
|
5
|
+
> You are reviewing a change before it is pushed. Read `IDEA.md` (the brief), the task contract in `.ai/task.md`, and the diff (`git diff origin/main..HEAD`).
|
|
6
|
+
>
|
|
7
|
+
> Find the three most likely ways this is wrong for a real user, and check each by reading code or running a small command. Do not edit files.
|
|
8
|
+
>
|
|
9
|
+
> Look for: behaviour the contract promises but no test proves; a check that passes without exercising the feature; paths, quoting or line endings that break on Windows; secrets, tracking or calls home; private files that would be published; a published URL or API that changed.
|
|
10
|
+
>
|
|
11
|
+
> Report in 10 lines or fewer. Mark each finding BLOCKER (fix before push) or NOTE. Say what you ran and what you only read.
|
|
12
|
+
|
|
13
|
+
The author fixes every BLOCKER, runs the checks again, then pushes. A reviewer's NOTE goes in the task file under Guesses.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Stuck protocol
|
|
2
|
+
|
|
3
|
+
Triggered by the same failure twice, or by two runs with no progress toward done-when. Do not retry a variant.
|
|
4
|
+
|
|
5
|
+
1. Stop. Record the attempt under Tried: what you did, the exact error, why it failed.
|
|
6
|
+
2. Search `.ai/index.md` for the error text.
|
|
7
|
+
3. Write down every assumption you are making. Move the ones you never tested to Guesses.
|
|
8
|
+
4. Test the weakest assumption with the cheapest experiment.
|
|
9
|
+
5. Try a structurally different approach, not a variant of what failed.
|
|
10
|
+
6. Still stuck, unattended: write `BLOCKED.md` (what you tried, the specific question, your best guess), commit, push and stop. With a human present: ask the same question.
|
|
11
|
+
|
|
12
|
+
Hard cap: a task that reaches about 5 runs without the checks passing goes to `BLOCKED.md`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# TASK: <name>
|
|
2
|
+
|
|
3
|
+
Run: 0 / 30
|
|
4
|
+
Status: not started
|
|
5
|
+
|
|
6
|
+
## Contract
|
|
7
|
+
**Goal:** build v1 of IDEA.md (replace with one sentence per task)
|
|
8
|
+
**Done when:** derive from IDEA.md: `<command>` passes; <other checks>
|
|
9
|
+
**Constraints:** <what must not change, what must be reused>
|
|
10
|
+
**Out of scope:** <what not to touch>
|
|
11
|
+
|
|
12
|
+
## Current state
|
|
13
|
+
(rewrite every window)
|
|
14
|
+
- Branch / commit: <branch> @ <hash>, clean
|
|
15
|
+
- Checks: <passing / failing, names>
|
|
16
|
+
- Next step: <exactly one step>
|
|
17
|
+
- Read first: <path:lines>, <K-ids>
|
|
18
|
+
|
|
19
|
+
## Confirmed
|
|
20
|
+
(facts proven by a command or test, with how we know)
|
|
21
|
+
- <fact> (seen in <test / log / commit>)
|
|
22
|
+
|
|
23
|
+
## Guesses
|
|
24
|
+
(unproven; test before building on them, never treat as fact in a later run)
|
|
25
|
+
- <hypothesis> (test: <cheapest experiment>)
|
|
26
|
+
|
|
27
|
+
## Tried
|
|
28
|
+
(append-only: approach → result → why)
|
|
29
|
+
1. <approach> → <result> → <why it failed>
|
|
30
|
+
|
|
31
|
+
## Decisions
|
|
32
|
+
(decision · reason · how to reverse)
|
|
33
|
+
|
|
34
|
+
## Don't
|
|
35
|
+
- <thing not to retry> (Tried #<n>)
|
|
36
|
+
|
|
37
|
+
## Handoff
|
|
38
|
+
(each run ends by writing: where it stopped, the exact next step, anything half-done)
|
|
39
|
+
|
|
40
|
+
## promote?
|
|
41
|
+
(for the retro: general lessons worth keeping)
|
package/package.json
CHANGED
|
@@ -1,6 +1,78 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "compounding-loop",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Write the brief, check back tomorrow: unattended, verified Claude Code build loops with a local dashboard.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"loop": "bin/loop.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"dist/",
|
|
13
|
+
"templates/",
|
|
14
|
+
"method/",
|
|
15
|
+
"runners/",
|
|
16
|
+
"plugin/",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/Minntooos/compounding-loop.git"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://github.com/Minntooos/compounding-loop#readme",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/Minntooos/compounding-loop/issues"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"claude-code",
|
|
33
|
+
"ai-agents",
|
|
34
|
+
"autonomous",
|
|
35
|
+
"dashboard",
|
|
36
|
+
"cli",
|
|
37
|
+
"context-engineering"
|
|
38
|
+
],
|
|
39
|
+
"scripts": {
|
|
40
|
+
"check": "node scripts/check.mjs",
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p web/tsconfig.json --noEmit",
|
|
42
|
+
"unit": "vitest run",
|
|
43
|
+
"build": "tsc -p tsconfig.build.json && node demo/copy-demo.mjs && vite build",
|
|
44
|
+
"e2e": "playwright test",
|
|
45
|
+
"test": "npm run check && npm run typecheck && npm run unit && npm run build && npm run e2e",
|
|
46
|
+
"dev:web": "vite",
|
|
47
|
+
"preview": "vite preview --port 4173 --strictPort"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@axe-core/playwright": "^4.13.0",
|
|
51
|
+
"@fontsource-variable/inter": "^5.3.0",
|
|
52
|
+
"@fontsource-variable/jetbrains-mono": "^5.3.0",
|
|
53
|
+
"@playwright/test": "^1.64.0",
|
|
54
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
55
|
+
"@tanstack/react-query": "^5.104.1",
|
|
56
|
+
"@types/node": "^26.6.4",
|
|
57
|
+
"@types/react": "^19.3.0",
|
|
58
|
+
"@types/react-dom": "^19.3.0",
|
|
59
|
+
"@vitejs/plugin-react": "^6.1.2",
|
|
60
|
+
"cmdk": "^1.1.1",
|
|
61
|
+
"lucide-react": "^1.52.0",
|
|
62
|
+
"react": "^19.3.0",
|
|
63
|
+
"react-dom": "^19.3.0",
|
|
64
|
+
"recharts": "^3.10.1",
|
|
65
|
+
"tailwindcss": "^4.3.3",
|
|
66
|
+
"tsx": "^4.23.15",
|
|
67
|
+
"typescript": "^7.0.2",
|
|
68
|
+
"vite": "^8.3.3",
|
|
69
|
+
"vitest": "^5.0.3",
|
|
70
|
+
"yaml": "^2.9.1"
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@clack/prompts": "^1.8.1",
|
|
74
|
+
"@hono/node-server": "^2.1.4",
|
|
75
|
+
"commander": "^15.0.0",
|
|
76
|
+
"hono": "^4.13.13"
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "compounding-loop",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Run unattended, verified Claude Code build loops: set up a repo, hand off between windows, run retros and escape stuck loops.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": ["loop", "unattended", "verification", "handoff", "retro"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Write the handoff so a fresh session with zero memory can continue at full quality
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Run the handoff protocol (method section 9.1) for the current task.
|
|
6
|
+
|
|
7
|
+
1. Run the checks and record the real result (counts and failing names) in `.ai/task.md` under "Current state".
|
|
8
|
+
2. Rewrite "Current state": branch and commit, whether the tree is clean, the single next step, and what to read first (`path:line`, knowledge ids). Do not copy code into the file.
|
|
9
|
+
3. Make sure every new fact is under Confirmed (with how we know) or Guesses (with the cheapest test). Move anything half-done into the handoff as "half-done: ...".
|
|
10
|
+
4. Commit the task file. Then tell the user the next window is ready and name the first thing it will do.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Set up this repository for the Compounding Loop (operating card, task file, knowledge index)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Set this repository up for unattended, verified build loops.
|
|
6
|
+
|
|
7
|
+
1. If the `loop` CLI is available, run `npx compounding-loop init` and report what it created. Otherwise do the steps below by hand.
|
|
8
|
+
2. Make sure these exist, creating only what is missing and never overwriting the user's files:
|
|
9
|
+
- `CLAUDE.md` containing the Operating Card (the "Operating Card (the Compounding Loop)" section).
|
|
10
|
+
- `.ai/method.md`, `.ai/index.md`, `.ai/knowledge/`, `.ai/log.md`.
|
|
11
|
+
- `.ai/task.md` with `Run: 0 / 30`, a Contract with a "Done when" that a command can check, and empty Confirmed, Guesses, Tried and Decisions sections.
|
|
12
|
+
3. Ask the user for the one-sentence goal and the command that proves it works (for example `npm test`) if `IDEA.md` or the task contract does not already say.
|
|
13
|
+
4. Finish with the exact command the user should run next, and do not start the build yourself.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run the retro after the checks pass - promote lessons, flag stale entries, log the task
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Run the retro (method section 12) now that the checks pass.
|
|
6
|
+
|
|
7
|
+
1. Run the checks once more and confirm they pass. If they do not, stop and say so; the task is not done.
|
|
8
|
+
2. Read `.ai/task.md`. List the lessons worth keeping beyond this task (Confirmed facts that will bite again, gotchas, decisions). For each, propose a knowledge entry with its triggers (files, error text, commands, topics) and evidence.
|
|
9
|
+
3. Flag existing entries in `.ai/index.md` that this task showed to be wrong or stale. Do not delete or rewrite a CONFIRMED entry without new evidence.
|
|
10
|
+
4. Add one line to `.ai/log.md`: date, task, checks result, number of windows, what was learned.
|
|
11
|
+
5. After the user approves the proposed entries, write them, update `.ai/index.md`, and delete `.ai/task.md`.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Run the stuck protocol when the same failure has happened twice
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
You have hit the same failure twice. Stop retrying variants and run the stuck protocol (method section 9.3).
|
|
6
|
+
|
|
7
|
+
1. Write the failure down under Tried in `.ai/task.md`: what you did, the exact error, and why you think it failed. Search `.ai/index.md` for the error text.
|
|
8
|
+
2. List what you have not yet checked: assumptions you treated as fact (move them to Guesses), the cheapest experiment that would split the possible causes, and whether a subagent could read the logs or docs for you.
|
|
9
|
+
3. Run that one experiment. If it explains the failure, fix it and record the cause as Confirmed.
|
|
10
|
+
4. If it does not, and unattended: write `BLOCKED.md` (what you tried, the specific question, your best guess), commit, push and stop. If a human is present: report the question and your best guess, and wait.
|
|
@@ -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"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Compounding Loop: scheduled build run 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
|
+
# Never write a key into this file or the repository.
|
|
8
|
+
# 2. Copy this file to .github/workflows/loop.yml.
|
|
9
|
+
# 3. Pick a staggered minute in `cron` below (avoid minute 0).
|
|
10
|
+
name: compounding-loop
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
schedule:
|
|
14
|
+
- cron: "17 * * * *"
|
|
15
|
+
workflow_dispatch: {}
|
|
16
|
+
|
|
17
|
+
# Two runs on one branch would fight; queue instead of cancelling a run mid-push.
|
|
18
|
+
concurrency:
|
|
19
|
+
group: compounding-loop
|
|
20
|
+
cancel-in-progress: false
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: write
|
|
24
|
+
|
|
25
|
+
jobs:
|
|
26
|
+
run:
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
timeout-minutes: 90
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
with:
|
|
32
|
+
ref: main
|
|
33
|
+
fetch-depth: 0
|
|
34
|
+
- uses: actions/setup-node@v4
|
|
35
|
+
with:
|
|
36
|
+
node-version: 22
|
|
37
|
+
- uses: anthropics/claude-code-action@v1
|
|
38
|
+
with:
|
|
39
|
+
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
40
|
+
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
41
|
+
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
|
42
|
+
prompt: |
|
|
43
|
+
You are one scheduled run of the build loop for this repository. Nobody is watching.
|
|
44
|
+
Read CLAUDE.md and follow "Unattended mode" exactly. IDEA.md is the brief.
|
|
45
|
+
If .ai/session.lock is less than 90 minutes old, or DONE.md or BLOCKED.md exists, reply "Nothing to do" and stop.
|
|
46
|
+
Add 1 to the Run counter in .ai/task.md (Run: N / 30); at 30 write BLOCKED.md ("run budget reached") and stop.
|
|
47
|
+
Do units of work, run the tests after each, and push to main only when they pass.
|
|
48
|
+
Finish with the handoff in .ai/task.md. Never publish, spend money, or add secrets or tracking.
|
|
49
|
+
claude_args: >-
|
|
50
|
+
--max-turns 200
|
|
51
|
+
--allowedTools "Bash,Edit,Write,Read,Glob,Grep,Task"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Local runner
|
|
2
|
+
|
|
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
|
+
|
|
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.
|
|
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.
|
|
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,6 @@
|
|
|
1
|
+
# Compounding Loop on macOS or Linux: run one round at minute 17 of every hour.
|
|
2
|
+
# Install with `crontab -e` and paste the line below, replacing /path/to/repo.
|
|
3
|
+
# `loop run` skips the round when DONE.md / BLOCKED.md exists; the prompt itself checks the session lock.
|
|
4
|
+
# Cron has a minimal PATH: if `loop` or `claude` is not found, use their full paths. For unattended
|
|
5
|
+
# edits add `--skip-permissions` (see `loop run --help`) only in a repository you trust.
|
|
6
|
+
17 * * * * cd /path/to/repo && loop run >> .ai/loop-cron.log 2>&1
|
|
@@ -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>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<?xml version="1.0"?>
|
|
2
|
+
<Task version="1.4" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
|
|
3
|
+
<RegistrationInfo>
|
|
4
|
+
<Description>Compounding Loop: run one round of the build loop every hour.</Description>
|
|
5
|
+
</RegistrationInfo>
|
|
6
|
+
<Triggers>
|
|
7
|
+
<CalendarTrigger>
|
|
8
|
+
<Repetition>
|
|
9
|
+
<Interval>PT1H</Interval>
|
|
10
|
+
<StopAtDurationEnd>false</StopAtDurationEnd>
|
|
11
|
+
</Repetition>
|
|
12
|
+
<StartBoundary>2026-01-01T00:17:00</StartBoundary>
|
|
13
|
+
<Enabled>true</Enabled>
|
|
14
|
+
<ScheduleByDay>
|
|
15
|
+
<DaysInterval>1</DaysInterval>
|
|
16
|
+
</ScheduleByDay>
|
|
17
|
+
</CalendarTrigger>
|
|
18
|
+
</Triggers>
|
|
19
|
+
<Settings>
|
|
20
|
+
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
|
|
21
|
+
<StartWhenAvailable>true</StartWhenAvailable>
|
|
22
|
+
<ExecutionTimeLimit>PT90M</ExecutionTimeLimit>
|
|
23
|
+
<Enabled>true</Enabled>
|
|
24
|
+
</Settings>
|
|
25
|
+
<Actions Context="Author">
|
|
26
|
+
<Exec>
|
|
27
|
+
<Command>loop.cmd</Command>
|
|
28
|
+
<Arguments>run</Arguments>
|
|
29
|
+
<WorkingDirectory>C:\path\to\repo</WorkingDirectory>
|
|
30
|
+
</Exec>
|
|
31
|
+
</Actions>
|
|
32
|
+
</Task>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Claude Code routine runner
|
|
2
|
+
|
|
3
|
+
A routine is a scheduled Claude Code session in the cloud. It runs `prompt.md` against your repository on a cron schedule, with nobody watching.
|
|
4
|
+
|
|
5
|
+
## Set up
|
|
6
|
+
|
|
7
|
+
1. Copy `prompt.md`. Its only placeholder is `{{name}}`, your project name. The install and test commands come from your `CLAUDE.md`.
|
|
8
|
+
2. In Claude Code on the web, create a routine for the repository and paste the prompt. Give it write access to `main` only.
|
|
9
|
+
3. Pick a schedule (see below). `loop` generates a staggered cron line for you.
|
|
10
|
+
|
|
11
|
+
## Staggered cron
|
|
12
|
+
|
|
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
|
+
|
|
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
|
+
|
|
17
|
+
## Safety
|
|
18
|
+
|
|
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.
|