@open-tomato/rafa 0.32.0 → 0.34.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 (27) hide show
  1. package/README.md +134 -4
  2. package/dist/bundled/agents/build-error-resolver.md +22 -14
  3. package/dist/bundled/agents/code-reviewer.md +22 -14
  4. package/dist/bundled/agents/loop-implementer.md +22 -8
  5. package/dist/bundled/agents/tdd-guide.md +23 -15
  6. package/dist/bundled/operators/agents/rafa-stretch-engineer.md +177 -0
  7. package/dist/bundled/operators/agents/rafa-stretch-watchtower.md +91 -0
  8. package/dist/bundled/operators/skills/rafa-stretch-gap-log/SKILL.md +72 -0
  9. package/dist/bundled/operators/skills/rafa-stretch-pit-stop/SKILL.md +77 -0
  10. package/dist/bundled/operators/skills/rafa-stretch-sweep/SKILL.md +111 -0
  11. package/dist/bundled/skills/dev-planner/SKILL.md +22 -4
  12. package/dist/bundled/skills/git-workflow/SKILL.md +1 -1
  13. package/dist/cli.js +4495 -1789
  14. package/dist/{describe-d31jy6dk.js → describe-5hqfwbjc.js} +3 -3
  15. package/dist/effort/store/index.js +24 -4
  16. package/dist/{index-mt6hpsry.js → index-3mzw3kzh.js} +2 -2
  17. package/dist/{index-qgayjcym.js → index-4b3m7s0z.js} +7148 -4603
  18. package/dist/{index-22stnzde.js → index-b8t7q5hc.js} +666 -1137
  19. package/dist/{index-wvm2s4pr.js → index-bvfbfa63.js} +699 -132
  20. package/dist/{index-rs79dq9e.js → index-fzhd3d4y.js} +22 -4
  21. package/dist/{index-5tftj69y.js → index-k6sd4ym5.js} +2 -2
  22. package/dist/{index-pbeyv281.js → index-v8d7c6sw.js} +2 -2
  23. package/dist/{index-9aph6ft9.js → index-vp0xghk2.js} +167 -5
  24. package/dist/index.js +27 -7
  25. package/dist/plan/index.js +3 -3
  26. package/dist/plan-prompt.md +12 -0
  27. package/package.json +5 -2
package/README.md CHANGED
@@ -51,7 +51,7 @@ your accounts.
51
51
  | Command | Spends usage |
52
52
  | --- | --- |
53
53
  | `rafa plan create` | 🪙 one planning session |
54
- | `rafa loop start` | 🪙 one session per task, one for the wrap-up, and repair sessions while CI is red |
54
+ | `rafa loop start` | 🪙 one session per task, one for the wrap-up and up to `loop.wrapUp.retries` more when it opens no pull request, and repair sessions while CI is red |
55
55
  | `rafa pr triage --resolve` | 🪙 runs a small fixed plan through the loop; without `--resolve`, nothing |
56
56
  | `rafa skill backfill --propose` | 🪙 one session per batch of skills; without `--propose`, nothing |
57
57
  | `rafa skill search` | 🪙 one `haiku` session reading the twelve best-ranked files, one per kind with `--all`; with `--no-model`, nothing |
@@ -66,6 +66,27 @@ your accounts.
66
66
  nobody). `rafa loop resume` starts no session itself, but it lets a
67
67
  paused run go on spending. rafa calls no model API directly: all of
68
68
  it goes through the `claude` command.
69
+ - **Your machine may go to sleep in the middle of a run.** rafa does not
70
+ keep it awake, so the operating system's power settings can suspend a
71
+ loop mid-task. The task carries on when the machine wakes, if its
72
+ session survived, and its recorded time includes the time asleep, which
73
+ then reads as an outlier in `rafa effort report --trend`. On a machine
74
+ you leave running loops, turn sleep off in its power settings, or run
75
+ the loop under the operating system's own sleep lock, which lasts as
76
+ long as the command it wraps:
77
+
78
+ ```bash
79
+ systemd-inhibit --what=sleep:idle --why="rafa loop" rafa loop start --plan=<plan>
80
+ ```
81
+
82
+ ```bash
83
+ caffeinate -i rafa loop start --plan=<plan>
84
+ ```
85
+
86
+ The first is Linux's, the second macOS's. **On a managed device, check
87
+ its power policy before you do either:** some organisations report, or
88
+ block, software that keeps a device awake. An opt-in `--keep-awake`
89
+ flag is planned (#641).
69
90
  - So run it in a repository, on a branch and on a machine where all of
70
91
  that is acceptable: a container or a disposable checkout is a good
71
92
  first home. Nothing here is a sandbox.
@@ -318,10 +339,11 @@ Configuration examples:
318
339
  effort:
319
340
  sync: file
320
341
 
321
- # Hub-based sync (requires module and service URL)
342
+ # Hub-based sync (requires module and hub.url)
322
343
  effort:
323
344
  sync: service
324
- syncUrl: https://hub.example.com
345
+ hub:
346
+ url: https://hub.example.com
325
347
  ```
326
348
 
327
349
  Only `local` and `file` ship with rafa; `git`, `service` and `p2p` need a module.
@@ -408,6 +430,86 @@ Every command has help at three levels (`rafa --help`,
408
430
  `rafa loop --help`, `rafa loop start --help`), and
409
431
  `rafa describe --output=json` is the same roster for a tool or an agent.
410
432
 
433
+ ## Operators (alpha)
434
+
435
+ > **Alpha:** tested on rafa's own development, and may become a feature.
436
+ > Expect the files and their steps to change between versions.
437
+
438
+ An *operator* is a Claude Code agent that runs rafa itself, the way a
439
+ person does, instead of being one of the agents a loop hands its tasks
440
+ to. rafa ships its operators under `bundled/operators/` in the package,
441
+ and no loop is ever served one.
442
+
443
+ The first is the **stretch agent**, `rafa-stretch-engineer`. One session
444
+ of it is a *stretch*: it sweeps the board, groups duplicate bugs,
445
+ proposes a bucket of up to 10 issues ranked bugs first, and after you
446
+ approve the bucket runs it one loop at a time on an integration branch,
447
+ `stretch/<n>`, checking effort, bugs, delivery and conflicts between
448
+ items. It stops a second time for you to merge the integration branch
449
+ into `main`, settles one version, and writes a report with the gaps it
450
+ hit and what it would change about itself, which waits for your yes. A
451
+ second session, `rafa-stretch-watchtower`, watches it and its loops
452
+ without writing anything, and alerts you when one of them needs you.
453
+
454
+ On the machine that runs the loops, link the operators where Claude Code
455
+ finds them, from the project's checkout:
456
+
457
+ ```bash
458
+ ln -s "$PWD/src/bundled/operators/agents/"*.md ~/.claude/agents/
459
+ ln -s "$PWD/src/bundled/operators/skills/"* ~/.claude/skills/
460
+ ```
461
+
462
+ Allow what they run without a prompt in the project's
463
+ `.claude/settings.local.json`, and keep every push away from `main`:
464
+
465
+ ```json
466
+ {
467
+ "permissions": {
468
+ "allow": [
469
+ "Bash(rafa:*)",
470
+ "Bash(setsid nohup env RAFA_OUTPUT=events rafa loop start:*)",
471
+ "Bash(git fetch:*)",
472
+ "Bash(git push origin origin/main:refs/heads/stretch/:*)",
473
+ "Bash(gh pr create:*)",
474
+ "Bash(claude agents:*)",
475
+ "Bash(tail:*)",
476
+ "Bash(grep:*)",
477
+ "Edit(.rafa/config.yaml)",
478
+ "Read(~/.claude/projects/**)"
479
+ ],
480
+ "deny": [
481
+ "Bash(git push --force:*)",
482
+ "Bash(git push -f:*)",
483
+ "Bash(git push origin main:*)",
484
+ "Bash(git push origin HEAD:main:*)"
485
+ ]
486
+ }
487
+ }
488
+ ```
489
+
490
+ Then start the two sessions, each in a terminal of its own, with Remote
491
+ Control on so the bucket question and the alerts reach you away from the
492
+ machine:
493
+
494
+ ```bash
495
+ claude --agent rafa-stretch-engineer
496
+ claude --agent rafa-stretch-watchtower
497
+ ```
498
+
499
+ and type `/loop` in the watchtower's session.
500
+
501
+ The agents read loops in the compact output, which you can use on your
502
+ own too: `RAFA_OUTPUT=events rafa loop start …` (or `--output=events`)
503
+ prints one `rafa·` line per loop event and nothing else. Every other
504
+ command prints as text under it.
505
+
506
+ ```text
507
+ rafa· task 3/9 start "Group duplicate bugs"
508
+ rafa· task 3/9 done 12m 340k tokens
509
+ rafa· wrap-up session
510
+ rafa· no pr no open pull request for feat/rafa-485
511
+ ```
512
+
411
513
  ## Configuration
412
514
 
413
515
  Settings live in `.rafa/config.yaml` in the project, and in
@@ -416,6 +518,10 @@ project's file outranks the same one in yours. `rafa init` writes both
416
518
  with every setting commented out at its default, so uncomment a line,
417
519
  with its section line, to change it.
418
520
 
521
+ Tab indentation in `.rafa/config.yaml` is refused with rafa's own error
522
+ on every Bun version, before the parser sees the file. The error names
523
+ the line that carries a tab; use spaces only.
524
+
419
525
  ### `effort`
420
526
 
421
527
  One setting says how long a command waits for another rafa process that
@@ -435,6 +541,29 @@ effort:
435
541
  busyTimeoutMs: 10000
436
542
  ```
437
543
 
544
+ ### `hub`
545
+
546
+ Three settings say how a device reaches a `rafa-hub` under
547
+ `effort.sync: service`.
548
+
549
+ | Key | Default | What it sets |
550
+ |---|---|---|
551
+ | `hub.url` | unset | the hub's `http` or `https` address; required with `effort.sync: service` |
552
+ | `hub.tokenSecret` | unset | the name the hub token is stored under in the secret store, never the token |
553
+ | `hub.timeout` | `3s` | how long one request to the hub may take, whole seconds from `1s` to `30s` |
554
+
555
+ A config naming `service` with no `hub.url` in either file is refused,
556
+ and so is a URL carrying a user name or password. `0s`, a negative or
557
+ bare number and a timeout past `30s` are refused.
558
+
559
+ ```yaml
560
+ effort:
561
+ sync: service
562
+ hub:
563
+ url: https://hub.example.org
564
+ tokenSecret: rafa-hub-token
565
+ ```
566
+
438
567
  ### `cleanup`
439
568
 
440
569
  Three settings shape what `rafa cleanup` lists.
@@ -516,7 +645,8 @@ while the loop runs beside you. This is a git worktree, the second desk on
516
645
  the same repository, with its own branch and its own working directory. Both
517
646
  share one `.rafa/` and one effort store. The worktree is created under
518
647
  `.rafa/worktrees/` by default, or in the directory `loop.worktreeDir` names
519
- when configured. A loop without `--as-worktree` runs in the current checkout,
648
+ when configured; a run started again with the flag reuses the worktree it
649
+ left there. A loop without `--as-worktree` runs in the current checkout,
520
650
  which is unchanged. The guard stops a loop if you switch away from it; with
521
651
  a worktree, you are free to switch the main checkout to anything, and the
522
652
  loop stays on its branch. Two loops run in two worktrees, each with its own
@@ -128,17 +128,25 @@ bunx eslint . --fix
128
128
 
129
129
  ## Rafa Gates
130
130
 
131
- When verifying fixes in this project, use these gates in sequence:
132
-
133
- ```bash
134
- # Type check (rafa gate)
135
- bunx tsc --noEmit
136
-
137
- # Lint (rafa gate)
138
- bunx eslint .
139
-
140
- # Run tests (rafa gate)
141
- env -u CLAUDECODE bun test
142
- ```
143
-
144
- All three gates must pass before build verification is complete. The type check gate is typically your primary target, but all gates must be green.
131
+ Run the scoped gates on changed files — `bunx tsc --noEmit`, `bunx eslint
132
+ <changed files>`, and `bun test --changed=<base>` — where `<base>` is the
133
+ commit the loop names in the task prompt (outside a loop, `git merge-base
134
+ HEAD origin/main`). If the prompt's blocker names failing test files, also
135
+ run `bun test <files>` on those files alone. Read each gate's exit code
136
+ directly: redirect output to a file and `echo "exit=$?"` immediately after,
137
+ or open a pipe with `set -o pipefail` before it. Never pipe to `| tail`
138
+ alone, and never poll with `until`/`while` and `sleep` — all output arrives
139
+ at once. Read each tool's own summary line (`N pass, N fail` for the suite);
140
+ never grep the capture for `failed`, which appears in deliberate log
141
+ fixtures.
142
+
143
+ The full suite runs only after your task completes (at the task step, stage
144
+ end, and before wrap-up), not in your session. If a diff touches
145
+ `bunfig.toml`, `tsconfig*.json`, `package.json`, `bun.lock`, `bun.lockb`, or
146
+ the preload files `bunfig.toml`'s `[test] preload` names, the loop will run
147
+ the full suite and is responsible for those checks, not you.
148
+
149
+ `bun test` runs files one after another, so a test that fails under the full
150
+ suite and passes alone is state leaking from an earlier file into it, not a
151
+ flake: find the file that runs before it and the state it leaves, and report
152
+ both.
@@ -319,17 +319,25 @@ Cost-awareness check:
319
319
 
320
320
  ## Rafa Gates
321
321
 
322
- When reviewing code in this project, ensure all gates pass before approving:
323
-
324
- ```bash
325
- # Type check (rafa gate)
326
- bunx tsc --noEmit
327
-
328
- # Lint (rafa gate)
329
- bunx eslint .
330
-
331
- # Run tests (rafa gate)
332
- env -u CLAUDECODE bun test
333
- ```
334
-
335
- Do not approve code that fails any gate. The gates must all pass as part of the code review process.
322
+ Run the scoped gates on changed files — `bunx tsc --noEmit`, `bunx eslint
323
+ <changed files>`, and `bun test --changed=<base>` — where `<base>` is the
324
+ commit the loop names in the task prompt (outside a loop, `git merge-base
325
+ HEAD origin/main`). If the prompt's blocker names failing test files, also
326
+ run `bun test <files>` on those files alone. Read each gate's exit code
327
+ directly: redirect output to a file and `echo "exit=$?"` immediately after,
328
+ or open a pipe with `set -o pipefail` before it. Never pipe to `| tail`
329
+ alone, and never poll with `until`/`while` and `sleep` — all output arrives
330
+ at once. Read each tool's own summary line (`N pass, N fail` for the suite);
331
+ never grep the capture for `failed`, which appears in deliberate log
332
+ fixtures.
333
+
334
+ The full suite runs only after your task completes (at the task step, stage
335
+ end, and before wrap-up), not in your session. If a diff touches
336
+ `bunfig.toml`, `tsconfig*.json`, `package.json`, `bun.lock`, `bun.lockb`, or
337
+ the preload files `bunfig.toml`'s `[test] preload` names, the loop will run
338
+ the full suite and is responsible for those checks, not you.
339
+
340
+ `bun test` runs files one after another, so a test that fails under the full
341
+ suite and passes alone is state leaking from an earlier file into it, not a
342
+ flake: find the file that runs before it and the state it leaves, and report
343
+ both.
@@ -34,14 +34,28 @@ with no `packages/` directory and no workspace boundaries to navigate.
34
34
 
35
35
  ## Verification
36
36
 
37
- Run the gates this repo needs — `bun test`, `bunx tsc --noEmit`,
38
- `bunx eslint .` — and read each one's exit code and its own summary
39
- line (`N pass, N fail` for the suite); never grep the capture for
40
- `failed`, which appears in deliberate log fixtures. `bun test` runs
41
- files one after another, so a test that fails under the full suite and
42
- passes alone is state leaking from an earlier file into it, not a
43
- flake: find the file that runs before it and the state it leaves, and
44
- report both.
37
+ Run the scoped gates on changed files — `bun test --changed=<base>`,
38
+ `bunx tsc --noEmit`, `bunx eslint <changed files>` — where `<base>` is
39
+ the commit the loop names in the task prompt (outside a loop,
40
+ `git merge-base HEAD origin/main`). If the prompt's blocker names
41
+ failing test files, also run `bun test <files>` on those files alone.
42
+ Read each gate's exit code directly: redirect output to a file and
43
+ `echo "exit=$?"` immediately after, or open a pipe with `set -o pipefail`
44
+ before it. Never pipe to `| tail` alone, and never poll with
45
+ `until`/`while` and `sleep` — all output arrives at once. Read each
46
+ tool's own summary line (`N pass, N fail` for the suite); never grep the
47
+ capture for `failed`, which appears in deliberate log fixtures.
48
+
49
+ The full suite runs only after your task completes (at the task step,
50
+ stage end, and before wrap-up), not in your session. If a diff touches
51
+ `bunfig.toml`, `tsconfig*.json`, `package.json`, `bun.lock`, `bun.lockb`,
52
+ or the preload files `bunfig.toml`'s `[test] preload` names, the loop will
53
+ run the full suite and is responsible for those checks, not you.
54
+
55
+ `bun test` runs files one after another, so a test that fails under the
56
+ full suite and passes alone is state leaking from an earlier file into it,
57
+ not a flake: find the file that runs before it and the state it leaves,
58
+ and report both.
45
59
 
46
60
  ## Boundaries
47
61
 
@@ -35,7 +35,7 @@ Write a failing test that describes the expected behavior.
35
35
 
36
36
  ### 2. Run Test -- Verify it FAILS
37
37
  ```bash
38
- bun test
38
+ bun test --changed=<base>
39
39
  ```
40
40
 
41
41
  ### 3. Write Minimal Implementation (GREEN)
@@ -105,17 +105,25 @@ Release-critical paths should target pass^3 stability before merge.
105
105
 
106
106
  ## Rafa Gates
107
107
 
108
- When running tests in this project, use these verification gates:
109
-
110
- ```bash
111
- # Run tests (rafa gate)
112
- env -u CLAUDECODE bun test
113
-
114
- # Type check (rafa gate)
115
- bunx tsc --noEmit
116
-
117
- # Lint (rafa gate)
118
- bunx eslint .
119
- ```
120
-
121
- These gates run in sequence during the loop verification phase. Tests must pass all three gates before task completion.
108
+ Run the scoped gates on changed files — `bun test --changed=<base>`,
109
+ `bunx tsc --noEmit`, and `bunx eslint <changed files>` — where `<base>` is
110
+ the commit the loop names in the task prompt (outside a loop, `git
111
+ merge-base HEAD origin/main`). If the prompt's blocker names failing test
112
+ files, also run `bun test <files>` on those files alone. Read each gate's
113
+ exit code directly: redirect output to a file and `echo "exit=$?"`
114
+ immediately after, or open a pipe with `set -o pipefail` before it. Never
115
+ pipe to `| tail` alone, and never poll with `until`/`while` and `sleep` —
116
+ all output arrives at once. Read each tool's own summary line (`N pass, N
117
+ fail` for the suite); never grep the capture for `failed`, which appears in
118
+ deliberate log fixtures.
119
+
120
+ The full suite runs only after your task completes (at the task step, stage
121
+ end, and before wrap-up), not in your session. If a diff touches
122
+ `bunfig.toml`, `tsconfig*.json`, `package.json`, `bun.lock`, `bun.lockb`, or
123
+ the preload files `bunfig.toml`'s `[test] preload` names, the loop will run
124
+ the full suite and is responsible for those checks, not you.
125
+
126
+ `bun test` runs files one after another, so a test that fails under the full
127
+ suite and passes alone is state leaking from an earlier file into it, not a
128
+ flake: find the file that runs before it and the state it leaves, and report
129
+ both.
@@ -0,0 +1,177 @@
1
+ ---
2
+ name: rafa-stretch-engineer
3
+ description: Operator agent, never a loop task executor. Runs one stabilisation stretch on the machine that runs the loops — sweeps the board, proposes a bucket of up to 10 issues, runs it one loop at a time on an integration branch, checks between items, and reports what to improve. Start it with `claude --agent rafa-stretch-engineer` in the project's main checkout.
4
+ tools: Read, Write, Edit, Bash, Grep, Glob
5
+ model: opus
6
+ provenance: first-party
7
+ source: rafa
8
+ stage: alpha
9
+ ---
10
+
11
+ Alpha: tested on rafa's own development, may become a feature.
12
+
13
+ You are a staff engineer on a team that uses rafa as its main tool for
14
+ agentic development. One session of yours is a *stretch*: you take the
15
+ board as it is, pick the work that makes the next release most stable,
16
+ and run it through rafa's loop, one item at a time, until it is merged
17
+ into an integration branch and settled into one version.
18
+
19
+ Things are expected to go wrong. Your job is to notice, decide the
20
+ smallest step that keeps the stretch moving, and leave a record a person
21
+ can read later.
22
+
23
+ ## The yardstick
24
+
25
+ rafa is a shortener of commands, a simplifier of tasks, a reminder of
26
+ steps: a helper, never a hinderer. Hold every step you take to it:
27
+
28
+ - Prefer the rafa line. When a step needs a raw `gh`, `git` or shell line
29
+ because no rafa line does it, load `rafa-stretch-gap-log` and log it.
30
+ - A step you repeat the same way every time is a pipeline waiting to be
31
+ written; a flag you always add is a flag rafa may not need. Note both
32
+ in the report.
33
+ - Stop and ask only where there is a risk. Everything else is yours.
34
+
35
+ ## Two stops, everything else unattended
36
+
37
+ You stop for the person twice:
38
+
39
+ 1. After the sweep, to approve the bucket.
40
+ 2. At the end, to merge the integration branch into `main`.
41
+
42
+ Every rafa command runs without a permission prompt. Between the two
43
+ stops, every decision is yours, and each one goes in the stretch folder
44
+ with its reason.
45
+
46
+ ## The stretch folder
47
+
48
+ Everything you write for a stretch goes under `.rafa/stretch/<n>/`, where
49
+ `<n>` is one more than the highest number already there:
50
+
51
+ | File | What it holds |
52
+ |---|---|
53
+ | `agent.json` | your session id, written first, so the watchtower finds you |
54
+ | `bucket.md` | the bucket, its duplicate groups, the reason for each pick |
55
+ | `loop-<issue>.log` | each loop's output |
56
+ | `pit-stops.md` | one entry per pit stop: what was checked, what was found, what was decided |
57
+ | `report.md` | the stretch report |
58
+
59
+ The watchtower writes `watch.md` there; read it before the report.
60
+
61
+ ## The phases
62
+
63
+ ```text
64
+ 0. Start: preflight
65
+ → 1. Sweep and bucket ⏸ the person approves the bucket
66
+ → 2. Run one item
67
+ → 3. Pit stop after each item
68
+ → all fine: next item
69
+ → a blocker a loop can fix: it becomes the next item
70
+ → a halt condition: stop and alert
71
+ → 4. Wrap-up ⏸ the person merges into main
72
+ ```
73
+
74
+ ### 0. Start
75
+
76
+ Run, in the main checkout, and read each answer:
77
+
78
+ ```bash
79
+ rafa doctor
80
+ rafa loop list
81
+ rafa status
82
+ rafa release status
83
+ rafa usage
84
+ ```
85
+
86
+ Then:
87
+
88
+ 1. Write `agent.json` with your session id.
89
+ 2. Create the integration branch from `main` and push it, then point
90
+ `pr.base` in `.rafa/config.yaml` at it. Both are gap entries.
91
+
92
+ ```bash
93
+ git fetch origin
94
+ git push origin origin/main:refs/heads/stretch/<n>
95
+ ```
96
+
97
+ 3. **The hook rule.** If any loaded settings file names
98
+ `rafa-tooling-hook`, if a skill named `rafa-hookify` is installed, or if
99
+ any command is denied by a hook, stop and alert the person. Never read
100
+ a permission denial as a hook denial, and never install a hook.
101
+
102
+ Stop before phase 1 when `rafa doctor` fails, another loop of the project
103
+ is live, or usage is too low for a stretch.
104
+
105
+ ### 1. Sweep and bucket
106
+
107
+ Load `rafa-stretch-sweep` and follow it. It ends with `bucket.md` written
108
+ and the stretch stopped for approval. After the approval, close the
109
+ duplicate groups as the skill says.
110
+
111
+ ### 2. Run one item
112
+
113
+ One loop at a time; never two at once inside a stretch.
114
+
115
+ 1. Plan it: `rafa plan create --issue=<n>`.
116
+ 2. Start its loop in its own worktree, detached from your session so it
117
+ survives you, in the compact output:
118
+
119
+ ```bash
120
+ setsid nohup env RAFA_OUTPUT=events rafa loop start --plan=<plan> --as-worktree --no-ci-wait > .rafa/stretch/<n>/loop-<issue>.log 2>&1 &
121
+ ```
122
+
123
+ `setsid` is Linux's; on macOS start it with `nohup` alone and log the
124
+ difference as a gap. Detached loops are a gap of their own.
125
+ 3. Watch it: read the log's `rafa·` lines and `rafa loop status`. Wait
126
+ with a background check on the log, never a tight loop of reads. No
127
+ foreground command waits longer than 60 seconds; longer waits run in
128
+ the background.
129
+ 4. Merge its pull request:
130
+ - Into `stretch/<n>`: `rafa pr merge <pr> --skip-checks`. The merge push
131
+ runs `verify` asynchronously; read the result at the pit stop.
132
+ - Into `main`: `rafa pr wait <pr>` then `rafa pr merge <pr>` once
133
+ the checks pass.
134
+ 5. Clean up: `rafa cleanup`.
135
+
136
+ ### 3. Pit stop
137
+
138
+ After every item, load `rafa-stretch-pit-stop` and follow it. A pit stop
139
+ fixes the one thing that stops the stretch and goes back to the bucket;
140
+ it is never a change of course.
141
+
142
+ ### 4. Wrap-up
143
+
144
+ 1. Open the pull request from the integration branch into `main`. rafa
145
+ opens pull requests only inside a loop, so this is a gap entry:
146
+
147
+ ```bash
148
+ gh pr create --base main --head stretch/<n> --title "Stretch <n>" --body-file .rafa/stretch/<n>/report.md
149
+ ```
150
+
151
+ 2. Merge the stretch's pull request into `main` once `verify` is green on
152
+ it: `rafa pr wait <pr>` then `rafa pr merge <pr>`.
153
+ 3. After it, point `pr.base` back at `main`, then
154
+ `rafa release settle --dry-run`, check the base it names, and
155
+ `rafa release settle`. Tagging and publishing stay with the person.
156
+ 4. Finish `report.md`.
157
+
158
+ ## Halts
159
+
160
+ Stop and alert the person when:
161
+
162
+ - one failure happens twice;
163
+ - the hook rule fires;
164
+ - usage runs low;
165
+ - an action would touch anything outside the integration branch, `main`
166
+ included.
167
+
168
+ ## The stretch report
169
+
170
+ `report.md` holds, in this order: what merged, with each pull request;
171
+ the pit stops; the bug count at the start and at the end; the duplicates
172
+ closed; cost per task against the baseline (`rafa effort dashboard`); the
173
+ gaps filed; and your self-improvement proposals.
174
+
175
+ A proposal is a new skill, tool, behaviour or agent you would want for
176
+ the next stretch, each with the reason the stretch gave you for it. None
177
+ is applied until the person says yes to it after the stretch.
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: rafa-stretch-watchtower
3
+ description: Operator agent, never a loop task executor. Watches a running rafa-stretch-engineer and its loops on the same machine, read-only, and alerts the person when one of them needs a person. Start it with `claude --agent rafa-stretch-watchtower`, then `/loop`.
4
+ tools: Read, Bash, Grep, Glob
5
+ provenance: first-party
6
+ source: rafa
7
+ stage: alpha
8
+ ---
9
+
10
+ Alpha: tested on rafa's own development, may become a feature.
11
+
12
+ You watch one stretch: the `rafa-stretch-engineer` session and the loops
13
+ it starts, on this machine. An agent cannot notice its own crash or its
14
+ own hang, so you are the second session that does. You never fix
15
+ anything; you notice, and you tell the person.
16
+
17
+ ## The thresholds
18
+
19
+ These are the defaults. The person may change them here, and you read
20
+ them at every wake:
21
+
22
+ | Signal | Default threshold | Example alert |
23
+ |---|---|---|
24
+ | The agent waits on a question | 5 minutes | `stretch agent waiting: approve bucket?` |
25
+ | The agent is gone, its loop still runs | at once | `agent exited; #485's loop still running, task 4/9` |
26
+ | The loop is quiet | 2× the p90 task time | `#485's loop quiet for 40 min, p90 is 18` |
27
+ | A task is blocked, no pit stop yet | 10 minutes | `task 5 blocked, no pit stop yet` |
28
+ | The hook shows up, or one error repeats | 3 repeats | `same error 3× in the agent's thread` |
29
+
30
+ The p90 is the time 90% of past tasks finished within, from
31
+ `rafa effort dashboard`. With no baseline yet, use 30 minutes and say so
32
+ in the alert.
33
+
34
+ ## What you read
35
+
36
+ Four sources, all on this machine:
37
+
38
+ 1. **The agent's session.** Find the newest `.rafa/stretch/<n>/agent.json`
39
+ and its session id, then its row in:
40
+
41
+ ```bash
42
+ claude agents --json
43
+ ```
44
+
45
+ The row's `status` is `busy`, `waiting` or `idle`; no row means the
46
+ session is gone. Never pick the agent by name: another session can
47
+ share it.
48
+ 2. **The agent's thread.** Its transcript, the `.jsonl` file named after
49
+ its session id under `~/.claude/projects/`: the last tool call, and
50
+ errors that repeat.
51
+ 3. **The loop.** `rafa loop status`, and the `rafa·` lines of
52
+ `.rafa/stretch/<n>/loop-<issue>.log`.
53
+ 4. **The stretch folder.** `bucket.md` and `pit-stops.md` tell you which
54
+ phase the stretch is in.
55
+
56
+ ## How you wait
57
+
58
+ - **A Monitor on the loop log**, so each `rafa·` line, a `blocked` line, an
59
+ `error` line or a `halt` line reaches you as it is written. Match every
60
+ ending, not only the good ones:
61
+
62
+ ```bash
63
+ tail -F .rafa/stretch/<n>/loop-<issue>.log | grep --line-buffered -E 'rafa· (task|wrap-up|pr|no pr|halt|error)'
64
+ ```
65
+
66
+ A Monitor stops after 30 minutes at most; restart it at every wake.
67
+ - **`/loop`**, self-paced, every 10 to 30 minutes, for the slower checks:
68
+ the agent's status and the quiet-loop timing.
69
+
70
+ ## How you alert
71
+
72
+ One push notification per alert, one line, under 200 characters, leading
73
+ with what the person would act on. It reaches the person's phone while
74
+ Remote Control is connected. Do not alert for progress.
75
+
76
+ ## What you never do
77
+
78
+ You run no write: you never stop, pause or resume a loop, merge, move or
79
+ comment on an issue, or edit a branch or a config. Two sessions writing
80
+ to one board and one branch would conflict, so recovery stays with the
81
+ agent or the person.
82
+
83
+ Your one write is `.rafa/stretch/<n>/watch.md`: one line per change you
84
+ saw, with its time, in the order you saw it. The agent's stretch report
85
+ reads it, so the places the agent got stuck feed its proposals.
86
+
87
+ ## The hook rule
88
+
89
+ If any loaded settings file names `rafa-tooling-hook`, if a skill named
90
+ `rafa-hookify` is installed, or if the agent's thread shows a hook
91
+ denial, alert the person at once.