compounding-loop 0.1.0 → 0.4.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.
Files changed (141) hide show
  1. package/README.md +40 -6
  2. package/dist/cli/checkLanes.js +60 -0
  3. package/dist/cli/checkLanes.js.map +1 -0
  4. package/dist/cli/claim.js +84 -0
  5. package/dist/cli/claim.js.map +1 -0
  6. package/dist/cli/doctor.js +54 -0
  7. package/dist/cli/doctor.js.map +1 -0
  8. package/dist/cli/index.js +253 -4
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/cli/init.js +2 -1
  11. package/dist/cli/init.js.map +1 -1
  12. package/dist/cli/laneRound.js +96 -0
  13. package/dist/cli/laneRound.js.map +1 -0
  14. package/dist/cli/laneStatus.js +54 -0
  15. package/dist/cli/laneStatus.js.map +1 -0
  16. package/dist/cli/lanesInit.js +56 -0
  17. package/dist/cli/lanesInit.js.map +1 -0
  18. package/dist/cli/lock.js +62 -0
  19. package/dist/cli/lock.js.map +1 -0
  20. package/dist/cli/loops.js +30 -3
  21. package/dist/cli/loops.js.map +1 -1
  22. package/dist/cli/new.js +5 -5
  23. package/dist/cli/new.js.map +1 -1
  24. package/dist/cli/next.js +18 -0
  25. package/dist/cli/next.js.map +1 -0
  26. package/dist/cli/pause.js +81 -0
  27. package/dist/cli/pause.js.map +1 -0
  28. package/dist/cli/run.js +119 -19
  29. package/dist/cli/run.js.map +1 -1
  30. package/dist/cli/runs.js +83 -0
  31. package/dist/cli/runs.js.map +1 -0
  32. package/dist/cli/shipCheck.js +59 -0
  33. package/dist/cli/shipCheck.js.map +1 -0
  34. package/dist/core/doctor.js +49 -0
  35. package/dist/core/doctor.js.map +1 -0
  36. package/dist/core/laneRound.js +63 -0
  37. package/dist/core/laneRound.js.map +1 -0
  38. package/dist/core/laneStatus.js +82 -0
  39. package/dist/core/laneStatus.js.map +1 -0
  40. package/dist/core/lanes.js +149 -0
  41. package/dist/core/lanes.js.map +1 -0
  42. package/dist/core/lanesInit.js +143 -0
  43. package/dist/core/lanesInit.js.map +1 -0
  44. package/dist/core/next.js +34 -0
  45. package/dist/core/next.js.map +1 -0
  46. package/dist/core/parse/changelog.js +41 -0
  47. package/dist/core/parse/changelog.js.map +1 -0
  48. package/dist/core/parse/claim.js +24 -0
  49. package/dist/core/parse/claim.js.map +1 -0
  50. package/dist/core/parse/controlRoom.js +38 -0
  51. package/dist/core/parse/controlRoom.js.map +1 -0
  52. package/dist/core/parse/index.js +12 -0
  53. package/dist/core/parse/index.js.map +1 -0
  54. package/dist/core/parse/markers.js +20 -0
  55. package/dist/core/parse/markers.js.map +1 -0
  56. package/dist/core/parse/paused.js +26 -0
  57. package/dist/core/parse/paused.js.map +1 -0
  58. package/dist/core/parse/runs.js +132 -0
  59. package/dist/core/parse/runs.js.map +1 -0
  60. package/dist/core/parse/task.js +96 -0
  61. package/dist/core/parse/task.js.map +1 -0
  62. package/dist/core/repo.js +2 -7
  63. package/dist/core/repo.js.map +1 -1
  64. package/dist/core/runPrompt.js +9 -3
  65. package/dist/core/runPrompt.js.map +1 -1
  66. package/dist/core/shipCheck.js +45 -0
  67. package/dist/core/shipCheck.js.map +1 -0
  68. package/dist/core/table.js +22 -1
  69. package/dist/core/table.js.map +1 -1
  70. package/dist/server/checkLabels.js +27 -0
  71. package/dist/server/checkLabels.js.map +1 -0
  72. package/dist/server/controls.js +72 -0
  73. package/dist/server/controls.js.map +1 -0
  74. package/dist/server/data.js +28 -8
  75. package/dist/server/data.js.map +1 -1
  76. package/dist/server/events.js +1 -0
  77. package/dist/server/events.js.map +1 -1
  78. package/dist/server/health.js +47 -8
  79. package/dist/server/health.js.map +1 -1
  80. package/dist/server/index.js +29 -0
  81. package/dist/server/index.js.map +1 -1
  82. package/dist/server/laneChecks.js +25 -0
  83. package/dist/server/laneChecks.js.map +1 -0
  84. package/dist/server/live/build.js +163 -0
  85. package/dist/server/live/build.js.map +1 -0
  86. package/dist/server/live/github.js +54 -0
  87. package/dist/server/live/github.js.map +1 -0
  88. package/dist/server/live/index.js +9 -0
  89. package/dist/server/live/index.js.map +1 -0
  90. package/dist/server/live/merge.js +16 -0
  91. package/dist/server/live/merge.js.map +1 -0
  92. package/dist/server/live/urls.js +59 -0
  93. package/dist/server/live/urls.js.map +1 -0
  94. package/dist/server/loops.js +146 -39
  95. package/dist/server/loops.js.map +1 -1
  96. package/dist/server/roundState.js +27 -0
  97. package/dist/server/roundState.js.map +1 -0
  98. package/dist/server/runs.js +81 -0
  99. package/dist/server/runs.js.map +1 -0
  100. package/dist/server/static-export.js +22 -1
  101. package/dist/server/static-export.js.map +1 -1
  102. package/dist/server/taskBullets.js +33 -0
  103. package/dist/server/taskBullets.js.map +1 -0
  104. package/dist/server/this-repo.json +1574 -0
  105. package/dist/web/assets/index-BOPytDNU.js +53 -0
  106. package/dist/web/assets/index-BSHEI7fa.css +2 -0
  107. package/dist/web/index.html +3 -3
  108. package/method/COMPOUNDING_LOOP.md +55 -0
  109. package/method/example/K-0002.md +16 -0
  110. package/method/example/K-0003.md +16 -0
  111. package/method/retro.md +7 -0
  112. package/package.json +6 -1
  113. package/plugin/.claude-plugin/plugin.json +1 -1
  114. package/plugin/commands/loop-lanes.md +11 -0
  115. package/plugin/commands/loop-next.md +11 -0
  116. package/plugin/commands/loop-pause.md +10 -0
  117. package/runners/github-actions/lanes.yml +156 -0
  118. package/runners/local/README.md +4 -1
  119. package/runners/local/compounding-loop.service +20 -0
  120. package/runners/local/compounding-loop.timer +11 -0
  121. package/runners/local/launchd.plist +36 -0
  122. package/runners/routine/README.md +38 -1
  123. package/runners/routine/control-room-prompt.md +16 -0
  124. package/runners/routine/lane-prompt.md +12 -0
  125. package/runners/routine/prompt.md +3 -3
  126. package/templates/chrome-extension/extension/icons/icon128.png +0 -0
  127. package/templates/chrome-extension/extension/icons/icon16.png +0 -0
  128. package/templates/chrome-extension/extension/icons/icon48.png +0 -0
  129. package/templates/chrome-extension/extension/manifest.json +19 -3
  130. package/templates/chrome-extension/gitignore +1 -1
  131. package/templates/lanes/README.md +23 -0
  132. package/templates/lanes/ci-check-lanes.yml +38 -0
  133. package/templates/lanes/claude-section.md +20 -0
  134. package/templates/lanes/control-room.md +3 -0
  135. package/templates/lanes/outbox.md +5 -0
  136. package/templates/lanes/task.md +36 -0
  137. package/templates/static-site/.ai/loop-prompt.md +2 -2
  138. package/templates/static-site/CLAUDE.md +2 -0
  139. package/templates/static-site/gitignore +1 -1
  140. package/dist/web/assets/index-BDdtnG33.js +0 -49
  141. package/dist/web/assets/index-D3-N8DN4.css +0 -2
@@ -1,8 +1,8 @@
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
- 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. Reply "Nothing to do" and stop. If the file is older than that, it is stale: ignore it.
5
- 2. If `DONE.md` or `BLOCKED.md` exists, reply "Nothing to do" and stop.
3
+ 0. Run `date -u` and note it as this run's start time. Run `git checkout main && git pull --ff-only`. If that pull fails because local `main` and `origin/main` have diverged or look unrelated, the sandbox started from a stale cached clone; origin `main` is never rewritten, so do not reset anything: run `git fetch origin main && git switch -c lane-run FETCH_HEAD`, work there, and wherever these steps say pull or push use `git pull --rebase origin main` and `git push origin HEAD:main`. 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, 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
+ 2. If `DONE.md` or `BLOCKED.md` exists, reply "Nothing to do" and stop. Pause: if `.ai/PAUSED` exists, the owner paused the loop: reply "Paused" 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.
8
8
  5. 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"). Otherwise take the next unit from the task file. Do one unit at a time.
@@ -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": { "default_popup": "popup.html", "default_title": "{{name}}" },
7
- "background": { "service_worker": "background.js" },
8
- "permissions": ["storage"]
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
  }
@@ -3,4 +3,4 @@ test-results/
3
3
  playwright-report/
4
4
  .env*
5
5
  .ai/runs.jsonl
6
- .ai/session.lock
6
+ **/session.lock
@@ -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,20 @@
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. If `git pull --ff-only` fails because the clone started from a stale cached copy (local `main` and `origin/main` diverged or unrelated), origin `main` was not rewritten: check `git log origin/main`, run `git fetch origin main && git switch -C lane-run FETCH_HEAD`, work on that branch, and from then on pull with `git pull --rebase origin main` and push with `git push origin HEAD:main`.
10
+ - **Run log:** every run that gets past the stop checks ends, pushed or not (also on failing tests or BLOCKED), by appending one line to `.ai/lanes/<your lane>/runs.jsonl` with `loop runs log --lane <your lane> --units <unit names> --passed N --failed N --pushed` (or `--no-pushed`, optionally `--note "why"`), committed in your final commit, so it is pushed with it. The dashboard shows a scheduled slot with no line and no commit as "no run recorded".
11
+ - **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.
12
+ - **Pause:** if `.ai/PAUSED` exists, the owner paused the loop (`loop pause` writes it, `loop resume` removes it): reply "Paused" and stop. The control room stops too.
13
+ - **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.
14
+ - **Claims:** if `.ai/lanes/<your lane>/claim` exists (one line, `name · expires <UTC time>`), names someone other than you (a scheduled run has no name, so any unexpired claim stops it), and its expiry (an ISO UTC timestamp, compared with `date -u`) is in the future, a teammate is working that lane: reply "Nothing to do" and stop. An expired claim counts as none (leave the file; `loop claim` overwrites it).
15
+ - **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.
16
+ - **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.
17
+ - **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.
18
+ - **Lane finished:** read the other lanes' outboxes first; if anyone waits on you, do that before writing `DONE.md`.
19
+ - **Shared files and shared types** are add-only for other lanes: add fields, never rename or remove, and say so in your outbox.
20
+ - **Never:** spend money, sign up for anything, publish anything, add secrets, telemetry or tracking, or touch anything outside this repository.
@@ -0,0 +1,3 @@
1
+ # Control room notes
2
+
3
+ Written only by the control room routine (commits trailer `Lane: control`). Every lane reads this file at the start of a run. Newest first. Format: `YYYY-MM-DD HH:MM UTC · to <lane|all> · message`.
@@ -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. Name finished units as "units 1-3 done" so `loop next` can skip them, and list what you shipped that another lane waits on (also in your outbox).>
@@ -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
- 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.
3
+ 0. Run `git checkout main && git pull --ff-only`. If the pull fails because the clone started from a stale copy (diverged or unrelated history), origin `main` was not rewritten: run `git fetch origin main && git switch -C loop-run FETCH_HEAD`, work there, and push with `git push origin HEAD:main`. Then, on the fresh code: if `.ai/PAUSED` exists, the owner paused the loop: reply "Paused" and stop. 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 (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`.
@@ -104,7 +104,9 @@ No human is watching. The owner reads the results later. These rules replace "as
104
104
  - **Stuck protocol, last step:** there's no human to ask. Write `BLOCKED.md` (what you tried, the specific question, your best guess), commit it, push it, and stop.
105
105
  - **Run budget:** `.ai/task.md` holds `Run: N / LIMIT`. Add 1 at the start of every run. When N reaches LIMIT, write `BLOCKED.md` saying "run budget reached", push it, and stop.
106
106
  - **Finished:** when every done-when in the contract passes and the reviewer finds no blockers, run the retro, write `DONE.md`, delete `.ai/task.md`, push and stop. `DONE.md` holds: what was built (each page and its target keyword), test results, anything you were unsure of, and the owner's launch checklist: check that the Netlify site serves the address in IDEA.md, add that address to Search Console as a URL-prefix property, submit `/sitemap.xml`, and buy the domain named in IDEA.md once there's budget for it. End with the 10 best next pages to build, ranked by search demand and how weak the current results are.
107
+ - **If `.ai/PAUSED` exists at the start of a run:** the owner paused the loop (`loop pause`); reply "Paused" and stop.
107
108
  - **If `DONE.md` or `BLOCKED.md` exists at the start of a run:** do nothing and stop.
109
+ - **Stale clone:** if `git pull --ff-only` fails because the clone started from an old copy (diverged or unrelated history), origin `main` was not rewritten: check `git log origin/main`, run `git fetch origin main && git switch -C loop-run FETCH_HEAD`, work there, and push with `git push origin HEAD:main`.
108
110
 
109
111
  **Never, even if it seems helpful**
110
112
  - Spend money, sign up for anything, or create accounts.
@@ -3,4 +3,4 @@ test-results/
3
3
  playwright-report/
4
4
  .env*
5
5
  .ai/runs.jsonl
6
- .ai/session.lock
6
+ **/session.lock