@open-tomato/rafa 0.28.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -153,9 +153,9 @@ the next one, so you rarely have to remember it.
153
153
  stored, one commit. A task that reports `blocked` is marked and the
154
154
  run stops with the reason; fix what it names and start again, and the
155
155
  blocked task goes first. After the last task a wrap-up session syncs
156
- with the base, bumps the version and the changelog when the project
157
- has them, pushes, opens the pull request and waits for CI, spending
158
- repair sessions on a red one.
156
+ with the base, commits a release fragment when the project has them
157
+ (the plan's level and notes), pushes, opens the pull request and waits
158
+ for CI, spending repair sessions on a red one.
159
159
 
160
160
  From another terminal: `rafa loop status`, `rafa loop pause` (after the
161
161
  running task), `rafa loop stop` (now).
@@ -163,14 +163,63 @@ the next one, so you rarely have to remember it.
163
163
  5. **Land it and look at what it cost.**
164
164
 
165
165
  ```bash
166
- rafa pr current # number, title, checks, URL
167
- rafa pr triage # why is it red, and is the fix simple (🪙 only with --resolve)
168
- rafa pr merge [--skip-checks] # asks y/N, merges, switches to the base, pulls, deletes both branches
169
- rafa release tag # tag the merged version
166
+ rafa pr current # number, title, checks, URL
167
+ rafa pr triage # why is it red, and is the fix simple (🪙 only with --resolve)
168
+ rafa pr merge [--skip-checks] # asks y/N, merges, switches to the base, pulls, deletes branches
169
+ rafa release settle # folds fragments to a version and pushes
170
+ rafa release tag # tag the released version (optional, depends on config)
170
171
  rafa effort collect && rafa effort report
171
172
  ```
172
173
 
173
- Then step 2 again, or `rafa plan create --next`.
174
+ Then step 2 again, or `rafa plan create --next`. When `rafa next` is used,
175
+ it runs settle and optionally tag as part of the workflow.
176
+
177
+ ### Release fragments and settling
178
+
179
+ A branch no longer owns a version number. Instead, the wrap-up commits a
180
+ release fragment (a small markdown file naming the plan, level and notes),
181
+ and `rafa release settle` assigns the version on the base branch after
182
+ merges. This lets several branches merge in any order without a version
183
+ collision: whichever machine settles first releases all waiting fragments
184
+ under one version.
185
+
186
+ The pull request shows a forecast of what version the branch would get if
187
+ merged now. The fragment is stored under `release.fragments` (default
188
+ `.changes/`), one file per plan. `rafa doctor` warns when fragments are
189
+ waiting and suggests running settle. `rafa pr list` marks forecasts as
190
+ `(base moved)` if the base branch changed since the forecast was written.
191
+
192
+ Every project starts with fragments settling to the base branch and tagging
193
+ by hand. You can configure settle to open a pending release pull request
194
+ instead (for protected base branches), and have settle tag the commit
195
+ automatically (for CI ownership of releases). Four keys, all optional:
196
+
197
+ ```yaml
198
+ # Empty config: fragments in .changes/, semver-by-level, settle pushes, tags by hand
199
+ # (This is the default for every new project.)
200
+
201
+ release:
202
+ tag: settle # settle tags the commit it pushed
203
+
204
+ release:
205
+ settle: pr # protected base branch: one pending release PR
206
+ pr:
207
+ versionCollision: refuse
208
+
209
+ release:
210
+ settle: pr
211
+ tag: settle
212
+ pr:
213
+ versionCollision: refuse
214
+ ```
215
+
216
+ The keys are: `release.fragments` (path, default `.changes/`), `release.strategy`
217
+ (fold strategy, default `semver-by-level`), `release.settle` (`push` or `pr`,
218
+ default `push`), `release.tag` (`manual` or `settle`, default `manual`).
219
+ `pr.versionCollision` controls what happens when a merge would collide
220
+ (default `report`). Read `context/release.md` for the full design,
221
+ `docs/ci-release-settle.md` for automating settle in CI, and
222
+ `rafa release settle --help` for the settle command.
174
223
 
175
224
  ### Tracking effort and learning from skills
176
225
 
@@ -180,7 +229,7 @@ were offered, invoked and worked. Key commands for the effort record:
180
229
  ```bash
181
230
  rafa effort collect # gather and store session logs
182
231
  rafa effort report # per-plan summary: sessions, tasks, costs
183
- rafa effort merge <file> # join another device's store into this one
232
+ rafa effort import <file> # merge another device's store into this one
184
233
  ```
185
234
 
186
235
  `rafa effort report` shows the per-plan tables (sessions by status and
@@ -211,10 +260,75 @@ The report opens with a fixed line explaining that it shows co-occurrence,
211
260
  not causation: a skill can be invoked and its failure recur for reasons the
212
261
  skill does not cover.
213
262
 
263
+ To see whether task sessions are getting more expensive over time, and
264
+ whether that follows the plan or the calendar:
265
+
266
+ ```bash
267
+ rafa effort report --trend # last 3 days against the 14 before, then the loops
268
+ rafa effort report --trend --loops=10 --by=effort
269
+ ```
270
+
271
+ The trend report has two parts. **Trend** compares the recent task
272
+ sessions with a baseline: median and p90 of minutes, output tokens,
273
+ cache-read tokens and turns per task, one row per day, and the sessions
274
+ over the outlier line (baseline median plus three robust standard
275
+ deviations). **Loops** lists one row per plan's loop, newest first: its
276
+ wall-clock and summed task time, number of tasks, minutes per task (min,
277
+ max, average, median), and average tokens and turns per task. Under the
278
+ rows, a drift reading tests each per-task figure across the loops in the
279
+ order they ran (the Mann-Kendall trend test). `no steady trend` means the
280
+ cost moves with the plan; `rising` means it grows whatever the plan.
281
+
282
+ To see everything at once, the loops running now included:
283
+
284
+ ```bash
285
+ rafa effort collect && rafa effort dashboard
286
+ rafa effort dashboard --output=json # one key per widget, for a dashboard
287
+ ```
288
+
289
+ The dashboard prints the running loops with their tasks done over total
290
+ and three estimates of the time left: **by task** (the plan's average task
291
+ session), **by progress** (the time since the plan first started, per task
292
+ done), and **this session** (what `rafa loop status` prints). By progress
293
+ counts the time between tasks too, so it is the one to compare against
294
+ the clock. Then come the trend, the loops, each plan's skills M1 and M2,
295
+ and the totals of every stored session.
296
+
297
+ ### Choosing how the effort store travels
298
+
299
+ An effort store can travel between a project's devices in different ways, each
300
+ suited to different setups. Set `effort.sync` in `.rafa/config.yaml`:
301
+
302
+ | Setup | Strategy | Why |
303
+ |---|---|---|
304
+ | One laptop | `local` | Nothing to sync |
305
+ | Two personal devices, occasional | `file` | No server, no account; copy a file |
306
+ | Personal projects, frequent | `git` | History and access from git, works offline |
307
+ | A company or several teams | `service` | One hub, live status, shared rules |
308
+ | Advanced, exploratory | `p2p` | Devices talk directly (spike) |
309
+
310
+ Configuration examples:
311
+
312
+ ```yaml
313
+ # Default: no sync, each device keeps its own store
314
+ # (nothing set, or effort: {sync: local})
315
+
316
+ # File exchange with no server
317
+ effort:
318
+ sync: file
319
+
320
+ # Hub-based sync (requires module and service URL)
321
+ effort:
322
+ sync: service
323
+ syncUrl: https://hub.example.com
324
+ ```
325
+
326
+ Only `local` and `file` ship with rafa; `git`, `service` and `p2p` need a module.
327
+
214
328
  ### Starting a second device
215
329
 
216
- To run rafa on a second device for the same project, bring the effort
217
- store over consistently:
330
+ To run rafa on a second device for the same project, bring the effort store over
331
+ consistently using `rafa effort copy` and `rafa effort import`:
218
332
 
219
333
  1. On the new device, check for an existing `.rafa/effort/` store and set
220
334
  it aside (rename or back up).
@@ -225,10 +339,11 @@ store over consistently:
225
339
  Or use `sqlite3 .rafa/effort/effort.sqlite ".backup <path>"`, but never
226
340
  a plain file copy during a write.
227
341
  3. Transfer the copied store and `.rafa/config.yaml` to the new device's
228
- `.rafa/` directory.
342
+ `.rafa/` directory. **The exported file holds private text — task
343
+ descriptions, findings, and notes — meant only for your own devices.**
229
344
  4. After both devices have recorded effort, bring them back together with:
230
345
  ```bash
231
- rafa effort merge <copied-store-file>
346
+ rafa effort import <copied-store-file>
232
347
  ```
233
348
 
234
349
  ### Which agents and skills are involved
@@ -303,11 +418,13 @@ with its section line, to change it.
303
418
  ### `effort`
304
419
 
305
420
  One setting says how long a command waits for another rafa process that
306
- is writing the effort store before it gives up with `SQLITE_BUSY`.
421
+ is writing the effort store before it gives up with `SQLITE_BUSY`, and
422
+ another names how the store travels between a project's devices.
307
423
 
308
424
  | Key | Default | What it sets |
309
425
  |---|---|---|
310
426
  | `effort.busyTimeoutMs` | `5000` | the wait in milliseconds, a whole number from 1 to 60000 |
427
+ | `effort.sync` | `local` | how the store travels: `local`, `file`, `git`, `service` or `p2p` |
311
428
 
312
429
  There is no unlimited wait, since a lock wait with no end can hang a
313
430
  loop; `0`, a fraction and a quoted `"5000"` are refused.
@@ -352,6 +469,56 @@ status:
352
469
  notice: false
353
470
  ```
354
471
 
472
+ ### `loop` and the guard
473
+
474
+ A loop guards itself: every turn, it watches the checkout's branch and HEAD
475
+ and halts if either changes externally (a branch switch in another terminal,
476
+ a pull that moved the base). The work stays committed and nothing is lost;
477
+ without the guard the edits would move with the checkout. The guard runs on
478
+ every loop and never interferes with the loop's own commits.
479
+
480
+ **Worktree use cases.** `loop start --as-worktree` runs the loop in a new
481
+ git worktree alongside your main checkout, so you stay on `main` in your
482
+ terminal and can keep working — reviewing, pulling or merging other PRs —
483
+ while the loop runs beside you. This is a git worktree, the second desk on
484
+ the same repository, with its own branch and its own working directory. Both
485
+ share one `.rafa/` and one effort store. The worktree is created under
486
+ `.rafa/worktrees/` by default, or in the directory `loop.worktreeDir` names
487
+ when configured. A loop without `--as-worktree` runs in the current checkout,
488
+ which is unchanged. The guard stops a loop if you switch away from it; with
489
+ a worktree, you are free to switch the main checkout to anything, and the
490
+ loop stays on its branch. Two loops run in two worktrees, each with its own
491
+ branch, both under the same `.rafa/`, allowing parallel work on two epics at
492
+ once. Merging a PR while a loop runs no longer offers `rafa self-update` right
493
+ away; `pr merge` names the running loop and asks you to update after it stops,
494
+ since the binary is what the loop spawns its sessions from, and swapping it
495
+ mid-run changes the engine while driving.
496
+
497
+ **Edge case: the 2026-09-29 incident.** A loop switched the main checkout to
498
+ its branch; 48 seconds later, `git checkout main` and `git pull` in another
499
+ terminal moved it back, and the edits followed. Both branches were at the
500
+ same commit, so `git switch <loop-branch>` recovered it. With the guard the
501
+ loop halts instead, keeping the work and the branch where it is while you
502
+ sort out what happened.
503
+
504
+ | Key | Default | What it sets |
505
+ |---|---|---|
506
+ | `loop.worktreeDir` | `.rafa/worktrees` | the directory where `--as-worktree` creates worktrees, absolute or relative to the project root |
507
+ | `dangerous.selfUpdateDuringLoop` | `false` | whether `rafa self-update` runs while a loop of this project is live |
508
+
509
+ Both take a string and a boolean as written; quoted values are refused.
510
+
511
+ ```yaml
512
+ # empty: loops run in the current checkout, guarded
513
+ # --as-worktree puts them in .rafa/worktrees (the default)
514
+
515
+ loop:
516
+ worktreeDir: ../rafa-loops # worktrees beside the repository
517
+
518
+ dangerous:
519
+ selfUpdateDuringLoop: true # update the binary even while a loop runs
520
+ ```
521
+
355
522
  ## Specs, issues and the roadmap
356
523
 
357
524
  You can plan from a local file and never touch a board. When you want
@@ -459,10 +626,13 @@ of `~/.bun/bin`; both warn when it is not, and `rafa doctor` checks it.
459
626
  Both exit 1 before building anything while a plan tracker in `plan.dir`
460
627
  (`.rafa/plans` unless `.rafa/config.yaml` names another) still holds an
461
628
  open or blocked task, and name every such tracker. `rafa self-update`
462
- runs only inside a project, so `rafa init` the checkout first. They exit
629
+ runs only inside a project, so `rafa init` the checkout first, and it
630
+ also exits 1 while a loop of the project is live, naming each loop's
631
+ branch and pid, unless `dangerous.selfUpdateDuringLoop` is true in the
632
+ config; `--force` does not override that wait. They exit
463
633
  2 when they could not run: a `package.json` that is not rafa's or cannot
464
- be read, a config or tracker they could not read, or a build, copy or
465
- link that failed.
634
+ be read, a config or tracker they could not read, a loop record
635
+ `rafa self-update` could not read, or a build, copy or link that failed.
466
636
 
467
637
  A version is installed once. Both also exit 1, before building, when
468
638
  `~/.rafa/runtime/<version>/` is already there, naming that directory and
@@ -519,7 +689,9 @@ and a line here is ticked by the change that finishes the feature.
519
689
  - ✅ Plan straight from the issue board, or from whatever is next on the roadmap
520
690
  - ✅ Review, merge and clean up pull requests from the command line
521
691
  - ✅ A failing pull request is diagnosed, and fixed when the fix is simple
522
- - ✅ A version bump and a changelog entry with every pull request
692
+ - ✅ Release fragments on branches, settled to a version on the base
693
+ branch after merges, with forecasts in the PR and protection for
694
+ multiple concurrent branches
523
695
  - ✅ Start a plan from the main branch and rafa makes the branch for you
524
696
  - ✅ One command takes you to the next step: merge, clean up, plan,
525
697
  branch, start
@@ -155,7 +155,7 @@ Recognized fields:
155
155
  - `stub` — The plan identifier (string, following the `rafa-<n>-<slug>` format). Used to organize findings and dedupe rows across runs.
156
156
  - `issue` — Optional GitHub issue number (integer, e.g., `123`). Links the work back to the `open-tomato/rafa` board.
157
157
  - `spec` — Optional path to the specification document that guided the plan.
158
- - `release` — Optional release level for the version bump this plan's pull request ships: `patch`, `minor`, `major` or `none`. Anything else is reported as `unusable-field` and read as if unset. Left out, the level is the highest among the change notes the plan's tasks stored, and `none` when they stored none.
158
+ - `release` — Optional release level for the version bump this plan's pull request ships: `patch`, `minor`, `major` or `none`. Anything else is reported as `unusable-field` and read as if unset. When absent, the level is the highest severity among the plan's collected change notes. The wrap-up writes this level to a release fragment file (under `release.fragments`, default `.changes/`) that `rafa release settle` will process to compute the version bump after the branch merges.
159
159
 
160
160
  Unknown keys are retained and ignored by the loop; they do not cause parsing to fail.
161
161
 
@@ -270,7 +270,7 @@ Report fields:
270
270
  | `skills_used` | list of strings | Names of skills referenced or applied |
271
271
  | `blockers` | list of objects | What blocked the task. Any entry marks the task `[BLOCKED]` whatever `status` says, so write `[]` when nothing did |
272
272
  | `out_of_scope_bugs` | list of objects | Bugs found that are outside this task's scope |
273
- | `changes` | list of objects | The changelog for this task's own diff, one entry per user-visible change (see below). An absent list is empty with no issue; a task whose diff a user would notice nothing of writes one entry at level `none` |
273
+ | `changes` | list of objects | Release notes for this task's own diff (contributing to the release fragment), one entry per user-visible change (see below). An absent list is empty with no issue; a task whose diff a user would notice nothing of writes one entry at level `none`. The wrap-up collects these from all tasks and writes them to the fragment that `rafa release settle` processes after the branch merges. |
274
274
 
275
275
  Finding entry fields:
276
276
 
@@ -414,13 +414,13 @@ The second example above has no `skills=` and is still a valid task declaration.
414
414
  invariant sweeps), live-seam runs, migrations, and close-out — these
415
415
  preserve resumability where a halt is most likely.
416
416
  * The RUNNER owns the push, the pull request, the merge with the base,
417
- the wait for CI, and the version bump and changelog entry. After the last
417
+ the wait for CI, and the release fragment creation. After the last
418
418
  task it runs a wrap-up session that promotes findings, compacts
419
419
  `progress.txt`, merges `origin/main`, commits, pushes and opens (or
420
420
  updates) the PR — and then polls that PR's checks, spending repair sessions
421
421
  on a red or conflicting result. So a plan must NOT carry a task that opens
422
422
  a PR, resolves a merge conflict, waits on CI, compacts `progress.txt`,
423
- or writes a version bump or changelog entry. Two openers race: measured,
423
+ or writes a release fragment. Two openers race: measured,
424
424
  one run cut a second branch and opened a second PR for a single plan. A
425
425
  close-out task SHOULD still take the mergeability reading (`git merge-tree
426
426
  --write-tree origin/main HEAD`) and assemble the body material — the gate