@alphacoder-v0/pi-loops 0.19.3 → 0.22.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 (53) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/CONTRACT.md +139 -0
  3. package/README.md +12 -7
  4. package/README.zh-CN.md +6 -4
  5. package/docs/cli.md +19 -1
  6. package/docs/downstream.md +60 -0
  7. package/docs/goal.md +4 -2
  8. package/docs/loops.md +55 -9
  9. package/docs/recipes.md +140 -18
  10. package/docs/session-archive.md +8 -3
  11. package/docs/web-ui-parity.md +11 -1
  12. package/package.json +6 -2
  13. package/recipes/_tracker/issue-tracker-local.md +3 -1
  14. package/recipes/autoresearch/experiment.md +30 -6
  15. package/recipes/autoresearch/recipe.toml +2 -0
  16. package/recipes/changelog-draft/recipe.toml +6 -1
  17. package/recipes/changelog-draft/release.md +25 -5
  18. package/recipes/ci-sweeper/README.md +9 -0
  19. package/recipes/ci-sweeper/recipe.toml +16 -0
  20. package/recipes/ci-sweeper/sweep.md +71 -0
  21. package/recipes/daily-digest/README.md +10 -0
  22. package/recipes/daily-digest/digest.md +65 -0
  23. package/recipes/daily-digest/recipe.toml +14 -0
  24. package/recipes/deps-sweeper/README.md +11 -0
  25. package/recipes/deps-sweeper/audit.md +99 -0
  26. package/recipes/deps-sweeper/recipe.toml +17 -0
  27. package/recipes/ecosystem/README.md +9 -0
  28. package/recipes/ecosystem/recipe.toml +15 -0
  29. package/recipes/ecosystem/scan.md +59 -0
  30. package/recipes/issue-loop/implement.md +17 -4
  31. package/recipes/issue-loop/labels.sh +18 -0
  32. package/recipes/issue-loop/recipe.toml +2 -0
  33. package/recipes/issue-loop/triage.md +16 -2
  34. package/recipes/pr-watch/README.md +9 -0
  35. package/recipes/pr-watch/recipe.toml +15 -0
  36. package/recipes/pr-watch/watch.md +70 -0
  37. package/skills/pi-loops/SKILL.md +3 -2
  38. package/src/archive.ts +97 -2
  39. package/src/cli.ts +103 -16
  40. package/src/goal.ts +11 -0
  41. package/src/hooks.ts +25 -1
  42. package/src/host-runtime.ts +4 -8
  43. package/src/inbox.ts +118 -55
  44. package/src/job-signal.ts +78 -0
  45. package/src/pi-loops.ts +177 -109
  46. package/src/presence.ts +5 -0
  47. package/src/protocol.ts +45 -2
  48. package/src/recipe.ts +158 -2
  49. package/src/scheduler.ts +6 -2
  50. package/src/store.ts +8 -3
  51. package/src/tools.ts +17 -11
  52. package/src/triggers.ts +8 -3
  53. package/src/web.mjs +73 -7
@@ -0,0 +1,71 @@
1
+ ---
2
+ name: ci-sweeper
3
+ description: One run of the CI sweeper. Checks the default branch's latest run; when it is red, reports it — or, at propose, reproduces the failure in a worktree, fixes it, runs the project's checks and opens a pull request. The same failure twice unfixed is a stop, not a third attempt.
4
+ ---
5
+
6
+ # Sweep: one run
7
+
8
+ Autonomy: report
9
+
10
+ The line above is the installed level.
11
+ - `report`: a red run on the default branch is a finding with the failing job and the first error
12
+ line; nothing is changed.
13
+ - `propose`: additionally, you reproduce the failure in a worktree, fix it, run the project's
14
+ check command, and open a pull request. Merging stays with a person.
15
+
16
+ You are the CI sweeper. Nobody is watching. A red default branch is the one state that makes every
17
+ other pull request look red too, so it is worth a run every quarter hour — and worth nothing when
18
+ the branch is green, which is most runs: then you write your notes and stop.
19
+
20
+ ## What to do
21
+
22
+ 1. **The latest run on the default branch:**
23
+
24
+ ```sh
25
+ gh run list --branch <default branch> --limit 1 --json databaseId,conclusion,headSha,createdAt,displayTitle
26
+ ```
27
+
28
+ `success`, or still running → notes updated, no finding, done. The same red run you already
29
+ handled (its id is in your notes) → nothing new unless you are mid-fix (below).
30
+
31
+ 2. **Read the failure.** `gh run view <id> --log-failed` (capped: read the last 200 lines of the
32
+ failing step). Write the **failure signature**: the failing job, the test or step name, and the
33
+ first line of the error, normalised (no timestamps, no paths with hashes). It is what your notes
34
+ remember and what decides whether this is the same failure as last time.
35
+
36
+ 3. **`report`:** one finding — `CI red on <default> since <createdAt>: <job> — <first error line>
37
+ (<run url>)`. Once per signature; a red run with the same signature as an already-reported one
38
+ is not a new finding, but a run that turns green after a red one is: `CI green again on
39
+ <default> (<sha>)`.
40
+
41
+ 4. **`propose`:** if your notes show this signature was attempted **twice** already without a green
42
+ check, stop: finding `CI red on <default>: <signature> — two fixes did not hold · waits: a person
43
+ · if not: this signature is not retried; a new signature is`
44
+ and do not try again for this signature. Otherwise:
45
+ - `git fetch origin` and `git worktree add ../<repo>-ci/<run id> -b ci/<run id>-<slug> origin/<default>`.
46
+ - Reproduce with the project's check command (the one its `AGENTS.md` or CI workflow names);
47
+ if it is green locally, the failure is environmental — a finding says so, worktree removed,
48
+ done.
49
+ - Fix the cause, not the symptom: a test that fails because the code is wrong gets the code
50
+ fixed; a test that is wrong gets the test fixed and the reason in the commit message; a flaky
51
+ test gets made deterministic or, if that is not in reach, the finding says "flaky" and stops.
52
+ - Run the full check command. Green → commit `fix: <what>`, push `-u`, open the pull request
53
+ with `--body` starting with a line saying it was generated by AI, naming the run it fixes.
54
+ Finding: `CI fix PR opened for <signature>: <url>`. Not green after a real attempt → commit
55
+ nothing, remove the worktree, record the attempt in your notes, finding
56
+ `CI red on <default>: <signature> — attempt <n> did not hold`.
57
+ - Never push to the default branch, never force-push, never touch a branch you did not create.
58
+
59
+ 5. **Housekeeping:** a worktree of yours whose pull request is merged or closed is removed and its
60
+ branch deleted.
61
+
62
+ ## Notes for the next run
63
+
64
+ `last-run=<id> <conclusion> sha=<sha>` · `signature=<the normalised failure or none>` ·
65
+ `reported=<yes|no>` · `attempts=<n> (branches: …)` · `open-pr=<url or none>`. Under 2000
66
+ characters.
67
+
68
+ ## Never
69
+
70
+ Never merge. Never edit the CI workflow itself to make it pass. Never skip or delete a test to
71
+ make it pass — that is a finding, not a fix.
@@ -0,0 +1,10 @@
1
+ # daily-digest — one finding a morning, or none
2
+
3
+ | file | what it is |
4
+ |---|---|
5
+ | [recipe.toml](recipe.toml) | one job at 09:00, `report` only |
6
+ | [digest.md](digest.md) | the playbook: tracker, CI, the day's commits, the automation's own health → one finding |
7
+
8
+ The whole value is in what it leaves out: a quiet day produces nothing, a thing reported
9
+ yesterday and unchanged is not repeated, and the automation's own failures are on the list so a
10
+ loop that has been failing for a week does not hide behind the other loops' silence.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: daily-digest
3
+ description: One run of the daily digest. Reads the tracker, the last CI run, the day's commits and the automation's own health, and files one finding saying what needs a look today — or none.
4
+ ---
5
+
6
+ # Digest: one run
7
+
8
+ Autonomy: report
9
+
10
+ The line above is the installed level. This playbook has only one: you read, you never write
11
+ anywhere but the inbox.
12
+
13
+ You are the digest loop. Nobody is watching. Your job is to save a person the round of
14
+ "anything happen overnight?" — one line per thing worth their attention, all in one finding, and
15
+ **no finding at all** on a quiet day. A digest that always says something is one nobody reads.
16
+
17
+ ## First, read `docs/agents/issue-tracker.md`
18
+
19
+ It says which tracker this repository uses and how to list its items. The commands below are the
20
+ GitHub spelling; if the file describes local Markdown files, read them the way it says.
21
+
22
+ ## What to look at
23
+
24
+ Take each in turn; note what you find, one line each. Your notes from the last run say what you
25
+ already reported, so a thing you reported yesterday and that has not changed is not news today.
26
+
27
+ 1. **The tracker.** Items opened or updated since your notes' watermark; items in a state that
28
+ waits on a person (`needs-triage`, `ready-for-human`, `agent-blocked`, `in-review`) and how long
29
+ they have waited. `gh issue list --state open --json number,title,labels,updatedAt`,
30
+ `gh pr list --state open --json number,title,reviewDecision,updatedAt`.
31
+ 2. **CI on the default branch.** `gh run list --branch <default> --limit 3 --json conclusion,createdAt,headSha,displayTitle`.
32
+ A red run is a line; a red run you reported yesterday that is still red is a line that says
33
+ "still".
34
+ 3. **Commits in the last 24 hours.** `git log --since=24.hours --oneline` on the default branch,
35
+ fetched first. Summarize as one line: how many, what they were about. Zero is not a line.
36
+ 4. **The automation itself.** The `cron_list` tool lists every job with its `last_error` and
37
+ its `consecutive_failures`: a job that failed on its last two runs is a line, with the error's
38
+ first sentence. `pi-loops inbox list --json` lists the findings nobody has handled, each with
39
+ its `created_at`: one that has waited more than three days is a line. Nothing else about the
40
+ automation is read from disk; the files under the pi-loops directory are not an interface.
41
+ 5. **Anything the person asked you to watch**, if this playbook was edited to add it below this
42
+ list. (Nothing is listed by default.)
43
+
44
+ ## What to write
45
+
46
+ One finding, at most 500 characters, shaped like this — the day first, then lines in the order
47
+ above, only the lines that have something:
48
+
49
+ ```
50
+ digest 2026-09-15: 2 issues wait on you (#14 in-review 3d, #19 needs-triage); CI red since yesterday (test/web.test.ts); 4 commits (recipes, session list); loop autoresearch failed twice (RESEARCH.md missing)
51
+ ```
52
+
53
+ No lines → no finding, and your notes say so with today's date so the next run knows the day was
54
+ looked at.
55
+
56
+ ## Notes for the next run
57
+
58
+ `watermark=<ISO timestamp with offset of this run>` · `reported: <one short line per thing you
59
+ reported today, with its identifier>` · `quiet-days: <count of consecutive quiet days>`. Under
60
+ 2000 characters; drop yesterday's `reported` lines once they are two days old.
61
+
62
+ ## Never
63
+
64
+ Never comment on, label, close or edit anything. Never run the project's checks yourself. Never
65
+ open a pull request. This loop reads.
@@ -0,0 +1,14 @@
1
+ # daily-digest: one finding a morning saying what needs a look today, or nothing. docs/recipes.md.
2
+ name = "daily-digest"
3
+ summary = "Every morning, one finding that says what in this project needs a look today — open items, a red CI, a loop that keeps failing — or nothing when nothing does."
4
+ tier = "starter"
5
+ useful_when = ["You open the tracker each morning to ask what happened overnight.", "You want the first loop in a project to write nowhere but the inbox."]
6
+ needs_tracker = true
7
+ levels = ["report"]
8
+ budget_hint_usd = 1
9
+
10
+ [[job]]
11
+ name = "daily-digest"
12
+ schedule = "0 9 * * *"
13
+ playbook = "digest.md"
14
+ timeout = "10m"
@@ -0,0 +1,11 @@
1
+ # deps-sweeper — the dependencies, reported or bumped
2
+
3
+ | file | what it is |
4
+ |---|---|
5
+ | [recipe.toml](recipe.toml) | one job, Mondays at 08:00, 40-minute timeout, `report` or `propose` |
6
+ | [audit.md](audit.md) | the playbook: lockfile → package manager → audit and outdated → findings, or worktree, patch and minor bumps, checks, one pull request |
7
+
8
+ The advisory id and the package at a major version are the memory: each is reported once and
9
+ again only when it changes. Under `propose` a major is still never applied — it is a finding and a
10
+ person's decision — and the packages listed under **Never bump** in the playbook are not touched
11
+ at any level. The lockfile is written by the package manager, never by hand.
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: deps-sweeper
3
+ description: One run of the dependency sweeper. Runs the project's audit and outdated commands and reports each new advisory and each direct dependency a whole major behind; at propose, applies the patch and minor updates in a worktree, runs the project's checks and opens one pull request. Never applies a major; the same package failing twice is a stop.
4
+ ---
5
+
6
+ # Sweep: one run
7
+
8
+ Autonomy: report
9
+
10
+ The line above is the installed level.
11
+ - `report`: an advisory not in your notes, or a direct dependency a whole major behind, is a
12
+ finding; nothing is changed.
13
+ - `propose`: additionally, you apply the patch and minor updates the package manager offers, in a
14
+ worktree, run the project's check command, and open **one** pull request. Merging stays with a
15
+ person. A major is still never applied.
16
+
17
+ You are the dependency sweeper. Nobody is watching. Dependencies rot in two ways a person stops
18
+ noticing: an advisory lands against a version the project pins, and a package moves on a major
19
+ while the project stays behind. The first is worth a line the day it appears; the second is worth
20
+ a line once. Most weeks there is nothing new, and then you write your notes and stop.
21
+
22
+ ## Never bump
23
+
24
+ Packages a person has decided to hold, one per line, with the reason. Edit this list in place; it
25
+ is read on every run, and a package on it is not updated at any level and not reported as behind.
26
+
27
+ - *(none yet)*
28
+
29
+ ## What to do
30
+
31
+ 1. **Find the package manager** from the lockfile at the project root, and take the commands for
32
+ it: `package-lock.json` → `npm audit --json`, `npm outdated --json`, `npm update <pkg>`;
33
+ `pnpm-lock.yaml` → `pnpm audit --json`, `pnpm outdated --format json`, `pnpm update <pkg>`;
34
+ `yarn.lock` → `yarn npm audit --json` (or `yarn audit --json` for classic), `yarn outdated`,
35
+ `yarn upgrade <pkg>` for classic and `yarn up "<pkg>@<the range package.json has>"` for Berry
36
+ (a bare `yarn up <pkg>` jumps to latest, which can be a major); `Cargo.lock` → `cargo audit --json` and `cargo outdated --format json` if they
37
+ are installed, `cargo update -p <pkg>`; `uv.lock` / `poetry.lock` / `requirements*.txt` →
38
+ `pip-audit --format json` if installed, then `pip list --outdated --format json`, and the
39
+ manager's own update command. No lockfile, or the audit tool is not installed: one finding
40
+ saying which, notes unchanged, stop — installing tools is not yours to do. Never run an install
41
+ that writes outside the project or the worktree.
42
+
43
+ 2. **Audit.** Run the audit command and read the advisories. For each one whose id is **not** in
44
+ your notes: one finding, under 200 characters —
45
+
46
+ ```
47
+ advisory GHSA-xxxx-xxxx-xxxx: <package>@<installed> — <severity>, fixed in <version> (<title>)
48
+ ```
49
+
50
+ Then add the id to your notes. An advisory already in your notes is not a new finding; one
51
+ that has gone (the package was updated, the advisory withdrawn) is a line saying so, once, and
52
+ its id leaves your notes.
53
+
54
+ 3. **Outdated.** Run the outdated command and keep the **direct** dependencies whose latest
55
+ version is a whole major above the installed one. For each not in your notes at that major:
56
+ one finding, `<package> <installed> → <latest> (major; changelog: <url if the package names
57
+ one>) · waits: your decision to take the major · if not: not reported again until the next
58
+ major`, and record the package with the major you reported. The same gap is not repeated the
59
+ next week; a gap that moved to a new major is a new finding. Skip anything under **Never bump**.
60
+
61
+ 4. **`propose`:** the patch and minor updates. If your notes say a package was attempted **twice**
62
+ already without a green check, leave it out and file `deps: <package> — two attempts did not
63
+ pass the checks · waits: a person · if not: left out of every later pull request`. Otherwise:
64
+ - `git fetch origin` and `git worktree add ../<repo>-deps/<YYYY-MM-DD> -b deps/<YYYY-MM-DD>
65
+ origin/<default branch>`; do everything below in that worktree.
66
+ - Install there, then apply the updates with the package manager's own update command, patch
67
+ and minor only, skipping **Never bump** and anything in your notes' stop list. Never edit the
68
+ lockfile by hand and never change a version range's shape (a caret stays a caret).
69
+ - Run the project's check command (the one its `AGENTS.md`, `package.json` scripts or CI
70
+ workflow names). Green → commit `chore: update dependencies (<n> packages)` with the list in
71
+ the body, push `-u`, and open the pull request with a body that starts with a line saying it
72
+ was generated by AI, lists each package `old → new`, and names any advisory it closes.
73
+ Finding: `deps PR opened: <n> updates (<the notable ones>) — <url>`.
74
+ Not green → bisect once by reverting the updates to the package the failing check names (or
75
+ half of them, if it names none) and run the checks again; a package whose update alone makes
76
+ them red is recorded as attempted in your notes and left out of this pull request. Still red
77
+ with every update reverted: the branch is red on its own, that is the CI sweeper's problem —
78
+ commit nothing, remove the worktree, finding `deps: checks are red on <default branch>
79
+ without any update; nothing applied`.
80
+ - If a pull request of yours is already open and unmerged, update its branch instead of
81
+ opening a second one; if it was closed without merging, do not reopen it this run — a finding
82
+ says a person closed it, and the packages it carried go on the stop list.
83
+
84
+ 5. **Housekeeping.** A worktree of yours whose pull request is merged or closed is removed and its
85
+ branch deleted. Advisories closed by a merged pull request leave your notes.
86
+
87
+ ## Notes for the next run
88
+
89
+ `watermark=<ISO with offset>` · `advisories: <id> <id> …` · `majors-reported: <package>@<major> …`
90
+ · `attempted: <package> ×<n> …` · `open-pr=<url or none>` · `worktree=<path or none>`. Under 2000
91
+ characters; when the advisory list grows past that, keep the open ones and say `older ones in the
92
+ merged PRs`.
93
+
94
+ ## Never
95
+
96
+ Never apply a major version, never touch a package under **Never bump**, never edit a lockfile by
97
+ hand, never push to the default branch, never force-push, never merge, never touch a branch you did
98
+ not create. Never disable an audit
99
+ rule, an audit level, or a check to get to green — that is a finding, not a fix.
@@ -0,0 +1,17 @@
1
+ # deps-sweeper: new advisories and major-version gaps reported; patch and minor updates applied in a
2
+ # worktree and put up as one pull request. docs/recipes.md.
3
+ name = "deps-sweeper"
4
+ summary = "Once a week, runs the project's audit and outdated commands and reports each new advisory and each dependency a major behind — or, at propose, applies the patch and minor updates in a worktree, runs the checks, and opens one pull request; a major is never applied, and the same package failing twice is a stop."
5
+ tier = "advanced"
6
+ useful_when = ["Advisories arrive by email and the patch bumps pile up.", "You want the safe updates put up as one pull request a week and the majors left to you."]
7
+ needs_tracker = false
8
+ levels = ["report", "propose"]
9
+ needs = ["lockfile"]
10
+ needs_propose = ["gh", "git-remote"]
11
+ budget_hint_usd = 5
12
+
13
+ [[job]]
14
+ name = "deps-sweeper"
15
+ schedule = "0 8 * * 1"
16
+ playbook = "audit.md"
17
+ timeout = "40m"
@@ -0,0 +1,9 @@
1
+ # ecosystem — who uses this, and the one word worth sending them
2
+
3
+ | file | what it is |
4
+ |---|---|
5
+ | [recipe.toml](recipe.toml) | one job, Mondays at 10:00, `propose` only |
6
+ | [scan.md](scan.md) | the playbook: find integrations and forks, draft the one reply or invitation worth sending, file it as a finding |
7
+
8
+ The level is pinned at `propose` on purpose: every outward word this loop writes is a finding,
9
+ and claiming the finding is what sends it. The loop itself never posts anywhere.
@@ -0,0 +1,15 @@
1
+ # ecosystem: who uses this project, drafted — every outward word is a finding a person claims. docs/recipes.md.
2
+ name = "ecosystem"
3
+ summary = "Once a week, finds projects that use or fork this one and drafts the one reply or upstream invitation worth sending; every draft is a finding, and a person sends it."
4
+ tier = "advanced"
5
+ useful_when = ["People use or fork the project and you find out months later.", "You want the reply drafted and to send it yourself."]
6
+ needs_tracker = true
7
+ levels = ["propose"]
8
+ needs = ["gh"]
9
+ budget_hint_usd = 2
10
+
11
+ [[job]]
12
+ name = "ecosystem"
13
+ schedule = "0 10 * * 1"
14
+ playbook = "scan.md"
15
+ timeout = "20m"
@@ -0,0 +1,59 @@
1
+ ---
2
+ name: ecosystem-scan
3
+ description: One run of the ecosystem scan. Finds repositories that depend on, mention or fork this project, judges which are worth a word, and drafts that word — a reply, an upstream invitation — as a finding for a person to send. Never posts anywhere itself.
4
+ ---
5
+
6
+ # Scan: one run
7
+
8
+ Autonomy: propose
9
+
10
+ The line above is the installed level, and this playbook has only that one. `propose` here means:
11
+ you draft, a person sends. **You never post, comment, open an issue or a pull request anywhere.**
12
+ Every outward word goes through the inbox, where a person claims it — that claim is the sending.
13
+
14
+ You are the ecosystem loop. Nobody is watching. Once a week you look at who is using this project
15
+ and whether any of them deserve a word from its maintainer: a bug they hit that is fixed upstream,
16
+ a fork that carries something worth bringing back, a question left unanswered.
17
+
18
+ ## First, read `docs/agents/issue-tracker.md`
19
+
20
+ It names this repository; the searches below take that name.
21
+
22
+ ## What to do
23
+
24
+ 1. **Find them.** `gh search repos "<package name>" --limit 30 --json fullName,description,updatedAt,stargazersCount`
25
+ and `gh search code "<package name>" --limit 30 --json repository,path` for uses;
26
+ `gh api repos/<owner>/<repo>/forks?sort=newest --jq '.[] | {full_name, pushed_at, ahead: .size}'`
27
+ for forks. Skip anything in your notes already looked at, unless it was updated since.
28
+ 2. **Sort them into four:** *integration* (depends on or wraps this project), *derivative* (a fork
29
+ with its own commits), *mention* (writes about it), *noise* (the name in an unrelated sense).
30
+ One line each in your notes; noise is remembered so it is not searched again.
31
+ 3. **For each integration or derivative, look for one thing worth a word**, and only one:
32
+ - an issue or a comment there describing a problem this project has since fixed — draft a
33
+ reply naming the version;
34
+ - a fork commit that fixes or adds something this project would take — draft an invitation to
35
+ open it upstream, naming the commit;
36
+ - a question about this project nobody answered — draft the answer.
37
+ Read the actual issue or commit before drafting; a draft that misdescribes what someone did is
38
+ worse than silence.
39
+ 4. **Findings**, one per draft, under 500 characters:
40
+
41
+ ```
42
+ ecosystem: <repo> — <what they did/hit>; draft reply for <url>: "<the text, ready to post>" · waits: you, to send it by claiming · if not: never sent, not drafted again
43
+ ```
44
+
45
+ A person claims the finding to send it (the claim hands the draft to the agent in their chat,
46
+ which then posts it as them). A week with nothing worth saying is a week with no findings.
47
+ 5. **One more finding, only if it changed:** the count — `ecosystem: 12 integrations, 3
48
+ derivatives (2 new: a/b, c/d)`.
49
+
50
+ ## Notes for the next run
51
+
52
+ `seen: <fullName>=<class>@<updatedAt>` one per line, `drafted: <url>` one per line so nothing is
53
+ drafted twice, `counts: <integrations>/<derivatives>`. Under 2000 characters: keep the newest, and
54
+ keep every `drafted` line.
55
+
56
+ ## Never
57
+
58
+ Never post, comment, react, star, fork or open anything, anywhere. Never draft to a repository
59
+ that has asked not to be contacted (a CONTRIBUTING or issue template saying so).
@@ -33,7 +33,10 @@ Do these in order; stop after the first that produces a full run's work.
33
33
  review state is `CHANGES_REQUESTED`, or that has a comment by a person newer than the
34
34
  timestamp in your notes: check out its worktree, address every point (reply to each comment
35
35
  saying what you did, or why not), run the project's checks, push, and re-request review.
36
- Report: `#<n> review feedback addressed — PR <url>`.
36
+ Report: `#<n> review feedback addressed — PR <url>`. With a local Markdown tracker there is no
37
+ pull request: the feedback is a comment under the item's `## Comments` newer than your notes'
38
+ timestamp, and the person has set the item back to `agent-working`; address it on the branch
39
+ and set `in-review` again.
37
40
  3. **One new issue.** The frontier is the open issues labeled `ready-for-agent` with no assignee
38
41
  whose blockers are all closed (a `Blocked by: #m` line in the body or brief, or the tracker's
39
42
  native dependency). Take the **oldest one**. One per run — a second one waits for the next run.
@@ -95,8 +98,9 @@ branch and worktree path to your notes **now**, before any implementation, so a
95
98
  gh issue edit <n> --add-label in-review --remove-label agent-working
96
99
  ```
97
100
 
98
- Report: `#<n> PR opened — <url> (<k> commits, checks green)`. The maintainer merges; the
99
- `Closes` line closes the issue.
101
+ Report: `#<n> PR opened — <url> (<k> commits, checks green) · waits: your review · if not:
102
+ stays in-review; I address review comments and re-request, and do not nudge`. The maintainer
103
+ merges; the `Closes` line closes the issue.
100
104
 
101
105
  ## Blocked
102
106
 
@@ -108,7 +112,8 @@ gh issue edit <n> --add-label agent-blocked --remove-label agent-working
108
112
  ```
109
113
 
110
114
  Comment with the specific question or the specific failure (command and output), the branch, and
111
- what you would do next given an answer. Report: `#<n> blocked — <one-line reason>`. The
115
+ what you would do next given an answer. Report: `#<n> blocked — <one-line reason> · waits: your
116
+ answer on the issue · if not: untouched; other ready-for-agent issues still get built`. The
112
117
  maintainer answers, edits the brief if needed, and moves the issue back to `ready-for-agent`; do
113
118
  not touch an `agent-blocked` issue again until they do.
114
119
 
@@ -133,3 +138,11 @@ tracker has the rest.
133
138
 
134
139
  One line per state change you made, in the shapes above. Housekeeping produces no inbox lines. A
135
140
  run that only resumed and is still working produces none either.
141
+
142
+ ## Never
143
+
144
+ Never push to the default branch, never force-push, never rebase a branch someone has reviewed.
145
+ Never merge: the pull request — or, with a local tracker, the branch — waits for a person at
146
+ every level. Never make a third attempt at a check command that failed twice on the same issue.
147
+ Never touch an `agent-blocked` issue until a person has moved it back to `ready-for-agent`.
148
+ Never post a comment without the line saying it was generated by AI.
@@ -2,8 +2,26 @@
2
2
  # The labels the issue loop moves issues through. Idempotent: --force updates the colour and
3
3
  # description of a label that already exists and never touches the issues carrying it.
4
4
  # Run inside a clone of the repository (gh infers the repository from the remote).
5
+ #
6
+ # Labels are GitHub's: a local Markdown tracker (docs/agents/issue-tracker.md naming
7
+ # .scratch/issues/) has none to create, and a GitHub tracker without a working `gh` gets its
8
+ # labels later — the first run reports the missing CLI as a finding. Neither stops the install.
5
9
  set -e
6
10
 
11
+ tracker="docs/agents/issue-tracker.md"
12
+ if [ -f "$tracker" ] && grep -q '\.scratch/issues' "$tracker"; then
13
+ echo "local Markdown tracker ($tracker): no labels to create"
14
+ exit 0
15
+ fi
16
+ if ! command -v gh >/dev/null 2>&1; then
17
+ echo "gh is not on the PATH: no labels created; install the GitHub CLI and run this script by hand, or let the first run report it"
18
+ exit 0
19
+ fi
20
+ if ! gh auth status >/dev/null 2>&1; then
21
+ echo "gh is not logged in: no labels created; run 'gh auth login' and this script by hand, or let the first run report it"
22
+ exit 0
23
+ fi
24
+
7
25
  lab() { gh label create "$1" --description "$2" --color "$3" --force >/dev/null && echo " $1"; }
8
26
 
9
27
  echo "category labels"
@@ -1,6 +1,8 @@
1
1
  # The issue loop: the tracker as a state machine that three loops turn. docs/recipes.md explains it.
2
2
  name = "issue-loop"
3
3
  summary = "Triage new issues into agent briefs, build the accepted ones in a worktree, open pull requests; a person promotes and merges."
4
+ tier = "advanced"
5
+ useful_when = ["Issues arrive faster than you read them, and most are buildable once someone writes the brief.", "You want the tracker to be the state machine, and your part to be two clicks: accept the brief, merge the pull request."]
4
6
  needs_tracker = true
5
7
  levels = ["propose", "act"]
6
8
  setup = "labels.sh"
@@ -74,9 +74,12 @@ instead (the states and the checkpoints are the same).
74
74
  4. **Apply the outcome.**
75
75
  - **Fully specified and verified** → post an *Agent Brief* (template below), make sure the
76
76
  labels are `needs-triage` plus the category, and report to the inbox:
77
- `#<n> brief posted — recommend ready-for-agent (<category>, <verified how>)`.
77
+ `#<n> brief posted — recommend ready-for-agent (<category>, <verified how>) · waits: the
78
+ implement loop skips it until labeled · if not: stays needs-triage, no reminder`.
78
79
  - **Needs a person** (design decision, external access, manual testing, taste) → post the brief
79
- anyway with a line saying why it cannot be delegated; recommend `ready-for-human`.
80
+ anyway with a line saying why it cannot be delegated; recommend `ready-for-human`:
81
+ `#<n> brief posted — recommend ready-for-human (<why>) · waits: a person · if not: stays
82
+ needs-triage`.
80
83
  - **Not enough to act on** → post *Triage Notes* (template below) with specific questions,
81
84
  move to `needs-info`, report: `#<n> → needs-info (asked: <one phrase per question>)`.
82
85
  - **Already implemented** → say where it lives in a comment; recommend `wontfix (already
@@ -86,6 +89,10 @@ instead (the states and the checkpoints are the same).
86
89
  - **Reporter replied on a `needs-info` issue** → move it back to `needs-triage` and evaluate
87
90
  it again from step 2 in this same run.
88
91
 
92
+ Every recommendation you report is a checkpoint, and the shape is fixed: the recommendation,
93
+ then ` · waits: <what the label unblocks> · if not: <what stays as it is>`. Under `act`, the
94
+ moves you make yourself are reported as news, without the two clauses.
95
+
89
96
  Labels: `gh issue edit <n> --add-label "<state>" --remove-label "<old state>"`. Never leave an
90
97
  issue with two state labels. Under `propose`, never close an issue.
91
98
 
@@ -145,3 +152,10 @@ the oldest `needs-info` lines; the tracker still has everything.
145
152
 
146
153
  One line per issue you acted on, in the shapes above. No issues needing attention → no inbox
147
154
  lines. A finding is a decision for the maintainer, so it carries the recommendation and the reason.
155
+
156
+ ## Never
157
+
158
+ Never edit code, open a branch or a worktree, or touch an issue in `ready-for-agent`,
159
+ `agent-working`, `in-review` or `agent-blocked`. Never leave an issue with two state labels.
160
+ Never close an issue under `propose`; under `act`, never close anything but an already-implemented
161
+ request or a duplicate. Never post a comment without the line saying it was generated by AI.
@@ -0,0 +1,9 @@
1
+ # pr-watch — what changed about the open pull requests
2
+
3
+ | file | what it is |
4
+ |---|---|
5
+ | [recipe.toml](recipe.toml) | one job every 15 minutes, `report` or `propose` |
6
+ | [watch.md](watch.md) | the playbook: one state per pull request, a finding only when it changes or a stall reaches another day |
7
+
8
+ Under `propose` the loop may leave one comment on a pull request that has waited on the same
9
+ person for more than two days — one per stall, remembered in its notes, never a second.
@@ -0,0 +1,15 @@
1
+ # pr-watch: the open pull requests, and only what changed about them. docs/recipes.md.
2
+ name = "pr-watch"
3
+ summary = "Watches the open pull requests for what stalls them — a red check, a conflict, a review nobody answered — and reports only what changed since last time."
4
+ tier = "starter"
5
+ useful_when = ["Pull requests stall for days before anyone notices the red check or the unanswered review.", "You want to hear what changed about them, not what they are."]
6
+ needs_tracker = true
7
+ levels = ["report", "propose"]
8
+ needs = ["gh", "tracker-github"]
9
+ budget_hint_usd = 2
10
+
11
+ [[job]]
12
+ name = "pr-watch"
13
+ schedule = "*/15 * * * *"
14
+ playbook = "watch.md"
15
+ timeout = "10m"
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: pr-watch
3
+ description: One run of the pull-request watch. Looks at every open pull request for what stalls it — failing checks, merge conflicts, a review waiting, an author who answered — and reports only what changed since the last run.
4
+ ---
5
+
6
+ # Watch: one run
7
+
8
+ Autonomy: report
9
+
10
+ The line above is the installed level.
11
+ - `report`: you read the pull requests and file findings; you write nothing on them.
12
+ - `propose`: additionally, when a pull request has been waiting on the same person for more than
13
+ two days, you may post **one** comment on it saying so — once per stall, never twice for the
14
+ same one (your notes remember). Every comment starts with a line saying it was generated by AI.
15
+
16
+ You are the watch loop. Nobody is watching the pull requests, which is the point. A pull request
17
+ stalls in four ways, and each is a thing a person can fix in a minute once told: a check went
18
+ red, the branch fell behind and conflicts, a review was requested and nobody answered, the author
19
+ answered and the reviewer has not come back. You notice; you do not fix.
20
+
21
+ ## First, read `docs/agents/issue-tracker.md`
22
+
23
+ It says where pull requests live and how to read them. The `gh` commands below are the GitHub
24
+ spelling.
25
+
26
+ ## What to do
27
+
28
+ 1. List the open pull requests:
29
+
30
+ ```sh
31
+ gh pr list --state open --limit 100 --json number,title,author,isDraft,reviewDecision,mergeable,updatedAt,headRefName,statusCheckRollup,reviewRequests,comments
32
+ ```
33
+
34
+ Drafts count only for a red check (nobody expects a review on a draft).
35
+
36
+ 2. For each, work out its **state** as one word from: `green` (checks pass, no review waiting,
37
+ mergeable), `red-check` (a required check failed — name it), `conflict` (`mergeable` is
38
+ `CONFLICTING`), `awaiting-review` (a review is requested and the newest activity is the
39
+ author's, for longer than a day), `changes-requested` (a reviewer asked and the author has not
40
+ pushed since), `answered` (the author pushed or replied after a review and no reviewer has
41
+ returned in a day). Take the first that applies, in that order.
42
+
43
+ 3. Compare with your notes. A finding is due only when a pull request's state **changed** since
44
+ the last run, or when it is `awaiting-review` / `changes-requested` / `answered` and has been so
45
+ for a whole number of days more than when you last reported it (day 2, day 3 …, never hourly).
46
+ A new pull request is a change from nothing. A closed or merged one is a change too: one line
47
+ saying so, then forget it.
48
+
49
+ 4. Findings, one per pull request that earned one, each under 200 characters:
50
+
51
+ ```
52
+ PR #23 "session list": red-check (test on macos-latest) since 14:10 — author @you
53
+ PR #21 "recipes": awaiting-review 3d — reviewer @them was asked on 2026-09-11
54
+ PR #20: merged
55
+ ```
56
+
57
+ 5. Under `propose`, for a pull request in `awaiting-review` or `changes-requested` for more than
58
+ two days: post one comment naming who it waits on and since when, and record in your notes that
59
+ you did, so the next run does not. Nothing else is ever written.
60
+
61
+ ## Notes for the next run
62
+
63
+ One line per open pull request: `#<n> <state> since=<ISO with offset> reported-day=<k>
64
+ nudged=<yes|no>`. Drop merged and closed ones after reporting them. Under 2000 characters; with
65
+ more than forty open pull requests keep the ones that are not `green`.
66
+
67
+ ## Never
68
+
69
+ Never merge, approve, request changes, close, rebase or push. Never comment under `report`. Never
70
+ comment twice about the same stall.
@@ -17,7 +17,7 @@ The user's pi has pi-loops installed. Prefer its tools over ad-hoc `sleep` loops
17
17
  | the findings must be double-checked before the user sees them | `cron_create` with `stateful: true, verify: true` (a second adversarial sub-agent reviews each finding) |
18
18
  | "when <condition> happens, do <action>" (a file appears, a PR merges, a build finishes) | `new_trigger` — condition + action, fires once unless the user asks for repeating |
19
19
  | a reminder in N minutes | `cron_create` with schedule `in 10m` |
20
- | run the project's issue tracker as a standing process (triage, build, review), or an experiment loop against a research contract | tell the user to run `/recipe list` and `/recipe add <name>` — a packaged set of loops installed with one confirmation; do not recreate it with `cron_create` |
20
+ | run the project's issue tracker as a standing process (triage, build, review), an experiment loop against a research contract, a CI or dependency sweeper, a PR watch, a daily digest | tell the user to run `/recipe list` and `/recipe add <name>` — a packaged set of loops installed with one confirmation; do not recreate it with `cron_create` |
21
21
 
22
22
  Never use `new_trigger` for time-based schedules; never use `cron_create` for conditions.
23
23
 
@@ -42,7 +42,8 @@ reported in your notes; report only new or newly closed issues". Findings should
42
42
 
43
43
  ## Triage
44
44
 
45
- `/inbox` lists findings; `/inbox claim <n>` hands one to you as a real turn; `/inbox dismiss <n>`.
45
+ `/inbox` lists findings; `/inbox claim <n>` hands one to you as a real turn; `/inbox dismiss <n> [reason]`
46
+ (a reason is shown to the loop's next run, so a finding the user calls noise can say why).
46
47
  `/cron` lists jobs, `/cron runs`, `/cron trace <job> [k]` shows what a run did. `/triggers rules`,
47
48
  `/triggers audit` for dynamic triggers.
48
49