@jaybeeuu/agent-cortex 2.6.0 → 2.8.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/CHANGELOG.md +269 -0
- package/README.md +102 -8
- package/agents/README.md +2 -2
- package/agents/ralph/agent.md +19 -16
- package/agents/ralph/claude/wake.md +4 -4
- package/agents/ralph/pi/wake.md +15 -20
- package/bin/agent-cortex.mjs +176 -67
- package/bin/installers/claude.mjs +33 -8
- package/bin/installers/pi.mjs +11 -1
- package/docs/skills/skill-anatomy.md +212 -0
- package/extensions/post-merge-watch/README.md +133 -0
- package/extensions/post-merge-watch/index.test.ts +247 -0
- package/extensions/post-merge-watch/index.ts +123 -0
- package/extensions/post-merge-watch/package.json +16 -0
- package/extensions/post-merge-watch/watch.test.ts +469 -0
- package/extensions/post-merge-watch/watch.ts +431 -0
- package/extensions/session-clear/README.md +74 -0
- package/extensions/session-clear/index.test.ts +524 -0
- package/extensions/session-clear/index.ts +159 -0
- package/extensions/session-clear/package.json +16 -0
- package/lib/cli.mjs +88 -1
- package/lib/harness-update.mjs +254 -0
- package/lib/harness-versions.mjs +36 -2
- package/lib/self-update.mjs +553 -0
- package/package.json +2 -1
- package/scripts/lib/docs.mjs +67 -0
- package/skills/engineering/implement-task/SKILL.md +124 -0
- package/skills/engineering/tdd/SKILL.md +2 -0
- package/skills/planning/create-task/REFERENCE.md +20 -3
- package/skills/planning/create-task/SKILL.md +1 -1
- package/skills/planning/create-task/pipeline.json +16 -0
- package/skills/planning/create-task/scripts/create-chores.mjs +51 -24
- package/skills/planning/create-task/templates/code.md +8 -3
- package/skills/planning/create-task/templates/document.md +8 -3
- package/skills/planning/create-task/templates/fix.md +8 -3
- package/skills/planning/create-task/templates/housekeeping.md +69 -0
- package/skills/planning/create-task/templates/post-merge.md +67 -0
- package/skills/planning/create-task/templates/review.md +8 -3
- package/skills/planning/create-task/templates/verify.md +8 -3
- package/skills/productivity/bd-tool/SKILL.md +55 -0
- package/skills/productivity/write-a-skill/SKILL.md +2 -2
- package/skills/review/refactor-skill/SKILL.md +2 -2
- package/skills/review/review-defects/EXAMPLES.md +122 -0
- package/skills/review/review-defects/SKILL.md +91 -0
- package/skills/style/style-tests/EXAMPLES.md +30 -0
- package/skills/style/style-tests/SKILL.md +10 -6
- package/skills/workflow/plan/SKILL.md +14 -3
- package/skills/workflow/ralph/REFERENCE.md +137 -30
- package/skills/workflow/ralph/SKILL.md +26 -16
- package/skills/workflow/ralph/scripts/post-merge-check.mjs +651 -0
- package/skills/workflow/run-pipeline-stage/SKILL.md +41 -23
- package/skills/workflow/run-pipeline-stage/playbooks/code.md +14 -8
- package/skills/workflow/run-pipeline-stage/playbooks/document.md +2 -2
- package/skills/workflow/run-pipeline-stage/playbooks/fix.md +2 -2
- package/skills/workflow/run-pipeline-stage/playbooks/housekeeping.md +58 -0
- package/skills/workflow/run-pipeline-stage/playbooks/post-merge.md +54 -0
- package/skills/workflow/run-pipeline-stage/playbooks/review.md +34 -3
- package/skills/workflow/run-pipeline-stage/playbooks/verify.md +2 -2
- package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +66 -10
- package/skills/workflow/run-pipeline-stage/scripts/recover-brief.mjs +805 -0
- package/skills/workflow/run-pipeline-stage/scripts/trail-entry.mjs +702 -0
- package/skills/workflow/using-agent-skills/SKILL.md +15 -7
- package/token-map.README.md +41 -4
- package/token-map.json +16 -5
|
@@ -47,7 +47,7 @@ are fine.
|
|
|
47
47
|
|
|
48
48
|
## Expansion Guard
|
|
49
49
|
|
|
50
|
-
Stage chores are pre-created, never created on demand. `create-chores.mjs` (in `skills/planning/create-task/`) reads `pipeline.json` and creates the whole chain in one shot — `code` → `verify` → `review` → `document`, plus the `featurePrReview` HITL gate — with `blocks` deps that encode the order, and with the parent's priority and inherited labels. It is idempotent: a re-run adopts the chores that already exist under the parent, creates only the missing ones, and skips deps that are already wired. Ralph's dispatch is therefore always "claim what `bd ready -l execution:afk` returns".
|
|
50
|
+
Stage chores are pre-created, never created on demand. `create-chores.mjs` (in `skills/planning/create-task/`) reads `pipeline.json` and creates the whole chain in one shot — `code` → `verify` → `review` → `document`, plus the `featurePrReview` HITL gate, plus the `post-merge` and `housekeeping` chores blocked behind that gate — with `blocks` deps that encode the order, and with the parent's priority and inherited labels. It is idempotent: a re-run adopts the chores that already exist under the parent, creates only the missing ones, and skips deps that are already wired. Ralph's dispatch is therefore always "claim what `bd ready -l execution:afk` returns".
|
|
51
51
|
|
|
52
52
|
A ready `execution:afk` task bead with **no** `stage:*` chore children has nothing to dispatch. Expand it, then dispatch the resulting `stage:code` chore:
|
|
53
53
|
|
|
@@ -57,7 +57,7 @@ A ready `execution:afk` task bead with **no** `stage:*` chore children has nothi
|
|
|
57
57
|
```
|
|
58
58
|
Expect `0` printed. `grep -c` exits `1` when it counts zero matches, so add `|| true` when the
|
|
59
59
|
check runs under `set -e` or inside an `&&` chain — the printed count is the answer, not the exit
|
|
60
|
-
status. A bead with a full chain — one `stage:*` chore per stage in `pipeline.json` (
|
|
60
|
+
status. A bead with a full chain — one `stage:*` chore per stage in `pipeline.json` (five for the
|
|
61
61
|
shipped pipeline) plus the `lifecycle:feature-pr` gate — never needs an expansion. The check
|
|
62
62
|
answers "no chores at all", so a **partial** chain (a lower count from a failed run) passes it
|
|
63
63
|
and its missing stages never arrive. Nothing detects that automatically: re-run step 2's command
|
|
@@ -70,9 +70,9 @@ A ready `execution:afk` task bead with **no** `stage:*` chore children has nothi
|
|
|
70
70
|
`<create-task-scripts>` is the absolute path to `skills/planning/create-task/scripts/`, resolved from wherever the skill was loaded — never hardcode one harness's install path. `<parent-priority>` is the parent's own priority from `bd show <parent-id>`, so chores match the task they belong to.
|
|
71
71
|
3. **Record it in `inflight`** so its completion is attributable to the parent and it stays in the wait set — it has no chore, so `choreId` is `null`:
|
|
72
72
|
```json
|
|
73
|
-
{ "kind": "expand", "choreId": null, "parentId": "<parent-id>", "title": "<parent title>", "agentId": "<agent-id>"
|
|
73
|
+
{ "kind": "expand", "choreId": null, "parentId": "<parent-id>", "title": "<parent title>", "agentId": "<agent-id>" }
|
|
74
74
|
```
|
|
75
|
-
4. **Report the mapping** the worker returns — `code`, `verify`, `review`, `document`, `featurePrReview` — and close nothing. The worker owns no bead on this run; the expansion is bookkeeping, not a stage.
|
|
75
|
+
4. **Report the mapping** the worker returns — `code`, `verify`, `review`, `document`, `post-merge`, `housekeeping`, `featurePrReview` — and close nothing. The worker owns no bead on this run; the expansion is bookkeeping, not a stage.
|
|
76
76
|
5. **Dispatch normally.** The next `bd ready -l execution:afk` pass picks up the `stage:code` chore, and the pre-created chain carries the rest.
|
|
77
77
|
6. **Handle the completion as an expansion completion**, not a stage completion — see below.
|
|
78
78
|
|
|
@@ -97,7 +97,7 @@ On completion, remove the entry from `inflight` and confirm the chore tree exist
|
|
|
97
97
|
bd children <parent-id> --json | grep -c '"stage:'
|
|
98
98
|
```
|
|
99
99
|
|
|
100
|
-
Expect one match per `stage:*` label in the chain —
|
|
100
|
+
Expect one match per `stage:*` label in the chain — six for the shipped `pipeline.json`; the
|
|
101
101
|
`featurePrReview` gate carries `lifecycle:feature-pr` instead, so it is not counted. A lower count
|
|
102
102
|
means the expansion did not finish: `0` created nothing, and a partial chain leaves stages that
|
|
103
103
|
will never become ready. Either way, surface the worker's output in chat, record the parent for the
|
|
@@ -130,7 +130,7 @@ Decompose it:
|
|
|
130
130
|
This is the shape `create-task` builds on its feature path (`SKILL.md` steps 5–6): an `execution:hitl` planning bead owned by the feature, whose impl children depend on it, so the dependency — not a label — is the readiness gate.
|
|
131
131
|
3. **Record it in `inflight`** so its completion is attributable to the feature and it stays in the wait set — it holds no chore, so `choreId` is `null`:
|
|
132
132
|
```json
|
|
133
|
-
{ "kind": "decompose", "choreId": null, "parentId": "<feature-id>", "title": "<feature title>", "agentId": "<agent-id>"
|
|
133
|
+
{ "kind": "decompose", "choreId": null, "parentId": "<feature-id>", "title": "<feature title>", "agentId": "<agent-id>" }
|
|
134
134
|
```
|
|
135
135
|
4. **Confirm the children exist** rather than trusting the report — the worker's IDs are a claim, the bead graph is the evidence:
|
|
136
136
|
```bash
|
|
@@ -157,8 +157,8 @@ Chores already exist, so dispatch never creates one:
|
|
|
157
157
|
- Parent task description from `bd show <parent-id>`
|
|
158
158
|
- For `fix` stage: `FILES_CHANGED` from the preceding stage's report (the required changes come from the fix bead's own description — read via `bd show <fix-id>`)
|
|
159
159
|
- For `verify`, `review`, `document` stages: `SUMMARY` and `FILES_CHANGED` from the preceding stage's report
|
|
160
|
-
-
|
|
161
|
-
7. **Call the {{TOOL:task}} tool** with the filled prompt as `prompt` and the worktree path as `cwd`. Store the returned agentId — you need it for step 8 and for
|
|
160
|
+
- The trail pointer: the worker records its own entries with `<run-pipeline-stage-scripts>/trail-entry.mjs`, so the prompt names that script rather than a log file (see _Reading the Trail_ below)
|
|
161
|
+
7. **Call the {{TOOL:task}} tool** with the filled prompt as `prompt` and the worktree path as `cwd`. Store the returned agentId — you need it for step 8 and for correlating the completion to its bead.
|
|
162
162
|
|
|
163
163
|
If this session has no subagent tool, **stop and say so**. Do not substitute a backgrounded CLI run (`pi -p … &`): that launch has no completion signal, so a finished worker and a killed one look identical, which is how stages came to be reported complete over uncommitted trees. Record the bead for the shutdown summary and tell the human the session needs restarting with the subagent extension loaded.
|
|
164
164
|
8. **Record the base commit** before the worker starts — you verify delivery against it (_Verified Completion_ below):
|
|
@@ -175,7 +175,7 @@ A worker's `---REPORT---` is a claim about the worktree, not evidence of it. Ver
|
|
|
175
175
|
node scripts/lib/verify-worker-tree.mjs --worktree .agent-cortex/worktrees/<parent-id> --base <base>
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
Exit code 0 means a clean tree with at least one commit ahead of the recorded base. A non-zero exit reports `worktree is dirty` or `no commit ahead of base`, and the stage did **not** deliver whatever its `OUTCOME` line says. Re-dispatch the same chore, or record the failure for the fix chain — never advance the parent, close the chore, or open a feature PR over uncommitted work.
|
|
178
|
+
Exit code 0 means a clean tree with at least one commit ahead of the recorded base. A non-zero exit reports `worktree is dirty` or `no commit ahead of base`, and the stage did **not** deliver whatever its `OUTCOME` line says. Re-dispatch the same chore, or record the failure for the fix chain — never advance the parent, close the chore, or open a feature PR over uncommitted work. `post-merge` and `housekeeping` deliver no commit, so this check does not apply to them.
|
|
179
179
|
|
|
180
180
|
---
|
|
181
181
|
|
|
@@ -204,6 +204,26 @@ For each parent task bead (`<parent-id>`):
|
|
|
204
204
|
|
|
205
205
|
---
|
|
206
206
|
|
|
207
|
+
## Post-merge Check
|
|
208
|
+
|
|
209
|
+
A merge is not verified by merging. The `stage:post-merge` chore reads the CI run that GitHub started for the merge commit on the base branch and reports whether it passed. `create-chores.mjs` pre-creates that chore, labelled `lifecycle:post-merge` and blocked behind the same `lifecycle:feature-pr` gate as `stage:housekeeping`, so it stays out of `bd ready` until a human closes the gate.
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
node skills/workflow/ralph/scripts/post-merge-check.mjs
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
The script is deterministic: no model, no tokens, zero dependencies, async only. It prints a JSON report and exits `0` when every run for the commit passed, `1` when a run failed, and `2` when the result is inconclusive.
|
|
216
|
+
|
|
217
|
+
- **Executor.** A dispatched worker runs it from the **main checkout**, like the housekeeping stage: the commit it reads landed on the base branch, not on the feature branch. It resolves the repository slug and base branch through `gh repo view`, so nothing about this repo's slug or `main` is baked in. `--base <branch>` and `--commit <sha>` override each; the wait window is `--wait <seconds>`, default 300. Any revision either flag supplies is expanded through `git rev-parse` first, because `gh run list --commit` matches the full 40-character SHA only — a caller that reaches `gh` with a short SHA gets an empty run list, which reads as `inconclusive` rather than as an error.
|
|
218
|
+
- **Exit `1` files one P1 escalation per open bead.** The bead carries the run URL and the failing job name, is labelled `lifecycle:post-merge-escalation` + `execution:hitl`, and carries a `post-merge-run:<id>` marker in its description. A second run finds the open bead carrying that marker and files nothing. The escalation has no parent, so the feature bead is never reopened and never gains a child.
|
|
219
|
+
- **The marker bounds open beads, not runs — a known limitation.** The marker is searched with `bd list`, which omits closed beads. A run id is immutable, so one bead per run forever is the intent, but the code delivers one **open** bead per run: once a human closes the escalation, a later check of the same run files a fresh bead for it. That repeats a page nobody wants; it never drops a signal. Fixing it means searching closed beads too, which is its own change.
|
|
220
|
+
- **Nothing is retried.** The stage never re-runs the workflow, never retries the check for a different answer, never reverts the merge, and builds no `stage:fix` chain — a commit that already merged is not repaired by a fix round. The chore stays **open** on exit `1` and `2`.
|
|
221
|
+
- **GitHub-only in v1.** A repository `gh` cannot resolve to a GitHub host, or a commit with no run on the base branch, is `inconclusive`: reported, never escalated.
|
|
222
|
+
- **A run still in flight is not a verdict.** The wait window belongs to the check; the wait past it belongs to the harness, and the stage playbook's _Watch transport_ section is the single place the per-harness mechanism is named. A session with no watcher runs the check once with a longer window instead — that fallback is prose, so it survives both renders.
|
|
223
|
+
- **Ordering against housekeeping.** The two stages are siblings on the gate and neither waits on the other. Chaining `housekeeping` behind `post-merge` was rejected: an inconclusive check that nobody resolves would strand the worktree reclamation housekeeping exists to do. Putting `post-merge` after `housekeeping` was rejected too — it would make the pruner a dependency of a check that reads GitHub state, which no worktree removal can affect.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
207
227
|
## Pruning Worktrees
|
|
208
228
|
|
|
209
229
|
A worktree outlives its bead: ralph leaves the directory behind when a feature closes, and a crashed run leaves one with no bead at all. `skills/workflow/ralph/scripts/prune-worktrees.mjs` converges `.agent-cortex/worktrees/*` with bead state.
|
|
@@ -223,6 +243,16 @@ node skills/workflow/ralph/scripts/prune-worktrees.mjs --remove-orphans # also r
|
|
|
223
243
|
|
|
224
244
|
**An unregistered stale directory is refused forever.** Earlier git runs may have dropped its registration, so every scan reports it and never removes it. Clean it up by hand: `git worktree prune`, then `rm -rf <path>`.
|
|
225
245
|
|
|
246
|
+
### Housekeeping chore
|
|
247
|
+
|
|
248
|
+
Pruning is a pipeline stage, not a habit: `create-chores.mjs` pre-creates a `stage:housekeeping` chore for every expanded task, carrying `lifecycle:housekeeping` and blocked behind that task's `lifecycle:feature-pr` gate. It stays out of `bd ready` until a human closes the gate, so it never competes with pipeline work.
|
|
249
|
+
|
|
250
|
+
- **Executor.** A dispatched worker — the `stage:housekeeping` chore in `bd ready -l execution:afk` — runs `prune-worktrees.mjs` from the **main checkout**, never from the feature worktree: the worktree it removes can be the parent's own. Ralph dispatches it with the main checkout as cwd for that reason.
|
|
251
|
+
- **Retry once.** Exit status `1` means some scanned worktree survived, and an open bead counts as survival, so a live backlog always exits non-zero. The worker therefore retries the run once. The retry is the whole allowance — never a third run.
|
|
252
|
+
- **Never close over a worktree the script did not remove.** The outcome comes from the per-worktree report, not the exit status: the chore succeeds when no `closed-bead` worktree is left `refused`. A closed-bead worktree still `refused` after the retry fails the chore, and the chore is left **open** with the printed reasons in its notes. A `kept` or `refused` entry for an open bead is a normal report, not a failure.
|
|
253
|
+
|
|
254
|
+
Ralph creates nothing for this stage and closes nothing: the worker owns the chore, as it owns every leaf bead.
|
|
255
|
+
|
|
226
256
|
---
|
|
227
257
|
|
|
228
258
|
## Feature PR Gate
|
|
@@ -240,15 +270,15 @@ When the `document` chore for a parent task completes:
|
|
|
240
270
|
4. Find the child HITL task bead for this parent with label `lifecycle:feature-pr` and `execution:hitl` (created by `create-task`).
|
|
241
271
|
5. Update that HITL bead with the PR URL/status (comment or note) so the reviewer has the link.
|
|
242
272
|
6. Do not schedule new parent features while this gate is **unblocked** — an unblocked gate is a feature genuinely awaiting a human merge (see _Gate predicate_ below).
|
|
243
|
-
7. Once the PR is merged, a human closes the HITL PR gate bead, which unblocks the scheduling brake. After that, close the parent feature bead:
|
|
273
|
+
7. Once the PR is merged, a human closes the HITL PR gate bead, which unblocks the scheduling brake **and** the pre-created `stage:post-merge` and `stage:housekeeping` chores. After that, close the parent feature bead:
|
|
244
274
|
```bash
|
|
245
275
|
bd close <parent-id>
|
|
246
276
|
```
|
|
247
|
-
Only then continue with next parent features.
|
|
277
|
+
The housekeeping run that follows removes the parent's worktree, because its bead now reads `closed`. Only then continue with next parent features.
|
|
248
278
|
|
|
249
279
|
### Gate predicate — parked vs pending
|
|
250
280
|
|
|
251
|
-
Ralph's only scheduling brake is a gate that is **unblocked**. `create-chores` pre-creates a `lifecycle:feature-pr` gate for *every* planned feature, blocked behind that feature's own `document` chore; such a gate is **parked** — the feature has not reached its PR stage — and must never stop dispatch. Only when the `document` chore closes does the gate unblock, and that is the state in which a human must merge.
|
|
281
|
+
Ralph's only scheduling brake is a gate that is **unblocked**. `create-chores` pre-creates a `lifecycle:feature-pr` gate for *every* planned feature, blocked behind that feature's own `document` chore; such a gate is **parked** — the feature has not reached its PR stage — and must never stop dispatch. Only when the `document` chore closes does the gate unblock, and that is the state in which a human must merge. The feature's `stage:housekeeping` chore waits on the gate bead itself, so it becomes ready only after that human closes it.
|
|
252
282
|
|
|
253
283
|
Resolve the question with one command instead of ad-hoc `bd` queries:
|
|
254
284
|
|
|
@@ -290,24 +320,32 @@ Ralph never builds remediation. The worker that fails a stage creates its own `s
|
|
|
290
320
|
|
|
291
321
|
---
|
|
292
322
|
|
|
293
|
-
##
|
|
323
|
+
## Reading the Trail
|
|
294
324
|
|
|
295
|
-
|
|
325
|
+
Ralph never tails a file. The worker writes its own bead trail at each transition through
|
|
326
|
+
`skills/workflow/run-pipeline-stage/scripts/trail-entry.mjs`, so progress is read from the bead:
|
|
296
327
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
328
|
+
```bash
|
|
329
|
+
node <run-pipeline-stage-scripts>/recover-brief.mjs --bead <chore-id> # status, newest entries, refs, blockers, acceptance — bounded
|
|
330
|
+
bd show <chore-id> # every checkpoint entry: Outcome, Evidence, What is left
|
|
331
|
+
bd provenance log <chore-id> # the refs it recorded: git-sha, branch, pr, work-id
|
|
332
|
+
bd children <parent-id> # the whole chain's state, closed included
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
The brief is the first read: `bd show` returns the whole note — 89 lines and about 6.5 KB for a
|
|
336
|
+
bare bead carrying 20 entries, about 120 lines and about 8 KB for a realistic one with acceptance
|
|
337
|
+
criteria, a blocker, provenance refs and a description — and Ralph reads the tail, not the
|
|
338
|
+
history. Widen the brief's window (`--trail`) only when it left out the entry you need.
|
|
339
|
+
|
|
340
|
+
There is nothing to flush and no bookmark to keep in `state.json`: the record the orchestrator used
|
|
341
|
+
to poll is now the record the worker writes directly, so the per-parent log file and the line
|
|
342
|
+
counter that fed its tail are gone. On each completion:
|
|
343
|
+
|
|
344
|
+
1. **Read the completed worker's bead**: `bd show <chore-id>` for its entries, and
|
|
345
|
+
`bd provenance log <chore-id>` for the commit, branch and PR it recorded.
|
|
346
|
+
2. **Post a chat summary** of what is new since the last report for that bead — the stage
|
|
347
|
+
transition, the test and lint results, and the close reason. Not the entries verbatim.
|
|
348
|
+
3. **Post nothing when nothing new arrived** — do not spam the chat with empty polls.
|
|
311
349
|
|
|
312
350
|
---
|
|
313
351
|
|
|
@@ -379,6 +417,76 @@ When nothing is in flight AND nothing is dispatchable — `inflight` holds no en
|
|
|
379
417
|
|
|
380
418
|
---
|
|
381
419
|
|
|
420
|
+
## Peer messaging (planner ↔ ralph)
|
|
421
|
+
|
|
422
|
+
The planner session (`plan` skill) and ralph are separate sessions with no shared terminal. Delivery, discovery, idle wake and the message ledger all belong to the harness's own messaging mechanism — the published **pi-peer** package on PI, a built-in facility on Claude Code. **Never build a registry, inbox, watcher or transport in its place** — this section defines only the message vocabulary and the reply rule that sit on top of it, and it is the single place both skills read.
|
|
423
|
+
|
|
424
|
+
### Transport
|
|
425
|
+
|
|
426
|
+
Send with {{TOOL:peer_send}}; list the peers you can reach with {{TOOL:peer_list}}, and address a peer only by the name or address that list reports. Where the mechanism provides a per-message intent, an idle subscription or a reply wait, use it — but never make a send depend on one.
|
|
427
|
+
|
|
428
|
+
Send from a scripted call, **never** by typing text into a busy terminal session: during the `pi-peer` package's adoption spike, stray keystrokes aimed at a running session were queued as user prompts twice, so the receiving agent read them as new instructions.
|
|
429
|
+
|
|
430
|
+
A send does not prove receipt. An inbound message may be held for approval before the receiving agent sees it, and delivery stays unconfirmed until the receiver answers.
|
|
431
|
+
|
|
432
|
+
The mechanism is supplied by the harness, and neither harness is forced into the other's: a PI session takes it from the `pi-peer` package runbook below, and a Claude Code session has it built in. **Only this subsection differs between the two installs** — the message kinds and the acknowledgement rule that follow are the same bytes on both, so a message means the same thing whichever mechanism carries it.
|
|
433
|
+
|
|
434
|
+
### The `pi-peer` package (PI)
|
|
435
|
+
|
|
436
|
+
A PI session gets its transport from this package, which a consuming repository installs itself — it is never a dependency of this package. Its `peer_get` and `peer_await` tools have no Claude Code counterpart, so they are named here and nowhere else in this convention.
|
|
437
|
+
|
|
438
|
+
#### Adoption in a consuming repository
|
|
439
|
+
|
|
440
|
+
1. **Install once per repository:** `pi install npm:@cryptolibertus/pi-peer`.
|
|
441
|
+
2. **List both roles** in `.pi/peers.json`: `planner` and `ralph`. The peer list is configuration, so both entries are required — see _Discovery is two-way only when both roles are listed_ below.
|
|
442
|
+
3. **Start each session with its own identity:** `PI_PEER_ID=planner pi` for the planning session, `PI_PEER_ID=ralph pi` for ralph. The file holds a default `localPeerId`, and `PI_PEER_ID` overrides it for the session. Confirm with `peer_list` — it reports `1 online - <peer id>` when discovery works.
|
|
443
|
+
4. **Send and read with the package's own tools:** `peer_send` (or `/peer send <id> "<text>" --intent <intent> --no-await`), `peer_list`, `peer_get` for a peer's current state and `peer_await` for a reply.
|
|
444
|
+
5. **Ignore the state files** the package writes — see _Files a consuming repository gains_ below.
|
|
445
|
+
|
|
446
|
+
#### Discovery is two-way only when both roles are listed
|
|
447
|
+
|
|
448
|
+
`.pi/peers.json` is a **static peer list, not a live registry**: the runtime merges `.pi/settings.json`, `.pi/peers.json` and the `PI_PEER_*` environment, then polls only the peers the file names. `/peer setup` never overwrites an existing `.pi/peers.json`, so a setup run in the second session leaves the file alone.
|
|
449
|
+
|
|
450
|
+
A single-entry file therefore makes discovery one-way: the listed peer sees the other as online, while the unlisted peer reports `no peers` and its sends write no message record. That is configuration, not a fault. The missing step is `/peer setup --peer <peer-id>`, which adds a trusted peer to the existing file. Two entries plus `PI_PEER_ID` per session is the whole setup — and because a send is not proof of receipt, a message stays unverified until the receiver answers.
|
|
451
|
+
|
|
452
|
+
#### Files a consuming repository gains
|
|
453
|
+
|
|
454
|
+
| File | Purpose | Tracked? |
|
|
455
|
+
|------|---------|----------|
|
|
456
|
+
| `.pi/peers.json` | the peer list plus the default `localPeerId` that `PI_PEER_ID` overrides | no |
|
|
457
|
+
| `.pi/peer-messages.json` | message records with source, target, status and conversation id | no |
|
|
458
|
+
| `.pi/peer-control-ledger.jsonl` | the control ledger | no |
|
|
459
|
+
|
|
460
|
+
All three are machine state, not tracked content. Tell the consuming repository to gitignore them: ignore `.pi/peers.json` **and** `.pi/peer-*` together. The literal path covers the peer list, which the glob does not match, and the glob covers the message and ledger files plus any further `peer-*.json` the package adds.
|
|
461
|
+
|
|
462
|
+
### Message kinds
|
|
463
|
+
|
|
464
|
+
The kinds map **onto** the transport's own intents where it has them (PI's `pi-peer`: `ask`, `review`, `notify`, `coordinate`, `task`); they never replace or re-implement the transport protocol.
|
|
465
|
+
|
|
466
|
+
| Kind | Carries | Maps to intent | Direction |
|
|
467
|
+
|------|---------|----------------|-----------|
|
|
468
|
+
| `handoff` | bead id + one-line scope | `task` | planner → ralph |
|
|
469
|
+
| `done` | bead id + run summary | `notify` | ralph → planner |
|
|
470
|
+
| `blocked` | gate bead id awaiting a human | `notify` | ralph → planner |
|
|
471
|
+
| `failed` | bead id + reason | `notify` | ralph → planner |
|
|
472
|
+
| `update` | bead id + stage progress | `notify` | ralph → planner |
|
|
473
|
+
| `get` | peer id, asks for current state | `coordinate` | either → either |
|
|
474
|
+
| `ask` | question text, needs an answer | `ask` | either → either |
|
|
475
|
+
|
|
476
|
+
Write the kind as the first word of the message text, e.g. `handoff agnt-ctx-abc.1 — implement the retry budget`.
|
|
477
|
+
|
|
478
|
+
### Acknowledgement rule
|
|
479
|
+
|
|
480
|
+
Statuses stay `running` or `queued` until the receiver answers, so **a sender cannot tell "delivered" from "done"**. Every inbound `handoff`, `get` and `ask` must be answered with exactly one of three reply kinds:
|
|
481
|
+
|
|
482
|
+
- `done` — carried out; name the bead id and give a one-line result (for `get`, the requested state).
|
|
483
|
+
- `blocked` — cannot proceed; name the gate bead that is waiting for a human.
|
|
484
|
+
- `failed` — could not be carried out; name the bead and the reason.
|
|
485
|
+
|
|
486
|
+
`update`, `done`, `blocked` and `failed` are reports, so they need no reply. Never leave a `handoff`, `get` or `ask` unanswered — reply that it is queued rather than staying silent.
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
382
490
|
## State Files
|
|
383
491
|
|
|
384
492
|
Status is read from beads — there is no rendered status document to build or refresh:
|
|
@@ -391,8 +499,7 @@ bd show <id> # full detail, including the worker's notes
|
|
|
391
499
|
|
|
392
500
|
| File | Purpose |
|
|
393
501
|
|------|---------|
|
|
394
|
-
| `.agent-cortex/ralph/state.json` | Runtime bookkeeping only: the in-flight agent-ID→bead mapping. Shape: `{ "inflight": [ { "kind": "stage", "choreId": "...", "parentId": "...", "title": "...", "agentId": "..."
|
|
395
|
-
| `.agent-cortex/ralph/ralph-*.log` | Per-parent log files written by subagents — every stage chore of a feature appends to `.agent-cortex/ralph/ralph-<parent-id>.log` (e.g. `ralph-abc-123.log`). |
|
|
502
|
+
| `.agent-cortex/ralph/state.json` | Runtime bookkeeping only: the in-flight agent-ID→bead mapping. Shape: `{ "inflight": [ { "kind": "stage", "choreId": "...", "parentId": "...", "title": "...", "agentId": "..." } ] }`. A decomposition worker is recorded with `"kind": "decompose"` and an expansion worker with `"kind": "expand"`, both with `"choreId": null` — they hold no chore but still occupy the wait set and the in-flight check. Initialise at startup with `{ "inflight": [] }`. Update after every dispatch and every agent completion. **Never hand-edit.** |
|
|
396
503
|
|
|
397
504
|
All task state is derived from beads; `state.json` holds only runtime bookkeeping:
|
|
398
505
|
|
|
@@ -9,7 +9,7 @@ Ralph is a parallel orchestration loop. It advances `execution:afk` beads throug
|
|
|
9
9
|
|
|
10
10
|
One query decides what ralph works on: **`bd ready -l execution:afk`**. Features, tasks and chores all arrive through it, and each bead's *shape* — not a second `bd` filter — decides which procedure carries it: a childless feature is decomposed, a bead with no stage chores is expanded, a stage chore is dispatched. An `execution:afk` bead is therefore never invisible to ralph for lacking a `stage:*` label, and ralph never invents a narrower "only stage chores" predicate that would hide one.
|
|
11
11
|
|
|
12
|
-
Each leaf execution bead belongs to the worker that ran it: the worker appends its milestone notes, closes the bead on both outcomes, and builds its own `stage:fix` + fresh-stage remediation chain when it fails. Ralph reads beads and routes — it never closes a leaf bead and never creates a fix bead. Its only retry control is a read-only cap backstop.
|
|
12
|
+
Each leaf execution bead belongs to the worker that ran it: the worker appends its milestone notes, closes the bead on both outcomes, and builds its own `stage:fix` + fresh-stage remediation chain when it fails. Two stages are exceptions, because neither repairs a file: the `housekeeping` worker never closes its chore over a worktree the pruner did not remove, and the `post-merge` worker leaves its chore open on a failed or inconclusive check. Both build no chain. Ralph reads beads and routes — it never closes a leaf bead and never creates a fix bead. Its only retry control is a read-only cap backstop.
|
|
13
13
|
|
|
14
14
|
State model: task state lives in beads (ready/in_progress via `bd`); the only local file is `.agent-cortex/ralph/state.json`, holding the agent-ID→bead mapping. Pipeline configuration comes from `skills/planning/create-task/pipeline.json`, and stage execution belongs to the `run-pipeline-stage` skill. Stage chores are pre-created from that pipeline when a task is expanded — with `blocks` deps encoding the order — so ralph's dispatch is always "claim what `bd ready -l execution:afk` returns"; it never creates a stage chore.
|
|
15
15
|
|
|
@@ -30,22 +30,23 @@ State model: task state lives in beads (ready/in_progress via `bd`); the only lo
|
|
|
30
30
|
### 1. Initialize (run once at startup)
|
|
31
31
|
|
|
32
32
|
1. Run `bd prime` and hold the output verbatim in memory.
|
|
33
|
-
2.
|
|
34
|
-
3.
|
|
35
|
-
4.
|
|
36
|
-
5.
|
|
37
|
-
6.
|
|
33
|
+
2. Publish your peer identity so the planner can reach you (REFERENCE.md, _Peer messaging (planner ↔ ralph)_): run the session under the identity your transport names — `PI_PEER_ID=ralph pi` where the transport is the `pi-peer` package — and confirm the planner is reachable with {{TOOL:peer_list}}, which reports `1 online - planner` when discovery works. Where the session shows its own inbox address instead (Claude Code reports it in `/status` as the `Peer address` row), confirm that row is not `unavailable`. If no planner is reachable, tell the human once that the peer list must name **both** roles and continue — messaging is a reporting channel, never a gate on dispatch.
|
|
34
|
+
3. Ensure `.agent-cortex/` and `.agent-cortex/worktrees/` are in the project's `.gitignore` (append any that are missing).
|
|
35
|
+
4. Create the workspace: `mkdir -p .agent-cortex/ralph`.
|
|
36
|
+
5. Initialize state: write `.agent-cortex/ralph/state.json` with `{ "inflight": [] }`.
|
|
37
|
+
6. Read `skills/planning/create-task/pipeline.json` and hold it in memory — you need its `maxFixRounds` for the read-only cap backstop.
|
|
38
|
+
7. Run **`bd ready -l execution:afk`**. That single query *is* the dispatch predicate — every ready `execution:afk` bead is ralph's work, whether it is a feature, a task or a chore. Classify each result by its shape to decide which procedure carries it:
|
|
38
39
|
- **`feature` bead with no children**: not dispatchable work yet — decompose it (_Feature decomposition_ below) and re-run the query.
|
|
39
40
|
- **Bead carrying a `stage:*` label**: a pipeline stage chore — dispatch it (_Event loop_, and _Dispatching a Chore Bead_ in REFERENCE.md).
|
|
40
41
|
- **`task` or other bead with no `stage:*` chore children**: expand it first (_Expansion guard_ below), then dispatch its ready `stage:code` chore.
|
|
41
42
|
- **Bead that already has its children** (a decomposed feature, an expanded task parent): skip it — it is not dispatchable itself, and its `stage:*` chore is dispatched on its own.
|
|
42
43
|
`execution:hitl` beads are not in this query and are not ralph's to touch: record them for the Pending Human Action summary. That includes every feature still at its default `execution:hitl` label.
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
8. Dispatch each unit from its parent's worktree, up to 5 in flight: a stage chore via the event loop, a childless feature via a decomposition worker (_Feature decomposition_), a chore-less task via an expansion worker (_Expansion guard_). Create branches and worktrees first (REFERENCE.md, _Feature Branches and Worktrees_).
|
|
45
|
+
9. Hand over to your harness's completion-detection mechanism: PI keeps a blocking wait outstanding over every in-flight agent ID, and Claude re-invokes you as each worker completes. If no **agent work** was dispatched — `state.json`'s `inflight` holds no entries, so no stage, decomposition or expansion worker is running — and `skills/workflow/ralph/scripts/pending-gates.mjs` reports a non-empty `pendingHumanAction` (an unblocked `lifecycle:feature-pr` gate, or an epic tagged `awaiting-epic-pr-merge`), go straight to HITL Pause (REFERENCE.md). A decomposition- or expansion-only init is in flight, not idle: keep the completion mechanism running and continue at the event loop.
|
|
45
46
|
|
|
46
47
|
### 2. Expansion guard (stage chores are pre-created)
|
|
47
48
|
|
|
48
|
-
A ready `execution:afk` task bead with no `stage:*` chore children has nothing to dispatch, so it is not dispatched as work. Expand it first: dispatch a worker in the feature worktree that runs `create-chores.mjs --parent <id> --priority <parent-priority>` against the existing bead and reports the returned `code`/`verify`/`review`/`document`/`featurePrReview` mapping — the next `bd ready -l execution:afk` pass then picks up `stage:code` (see REFERENCE.md, _Expansion Guard_).
|
|
49
|
+
A ready `execution:afk` task bead with no `stage:*` chore children has nothing to dispatch, so it is not dispatched as work. Expand it first: dispatch a worker in the feature worktree that runs `create-chores.mjs --parent <id> --priority <parent-priority>` against the existing bead and reports the returned `code`/`verify`/`review`/`document`/`post-merge`/`housekeeping`/`featurePrReview` mapping — the next `bd ready -l execution:afk` pass then picks up `stage:code` (see REFERENCE.md, _Expansion Guard_).
|
|
49
50
|
|
|
50
51
|
The expansion is idempotent and re-running it is the recovery path: the script adopts the chores that already exist under the parent, creates only the missing ones, and reports the whole mapping. The auto-recovery fires only when a re-check finds no `stage:*` chore at all; a **partial** chain (a count below the stage total) needs a manual re-run of that same command.
|
|
51
52
|
|
|
@@ -61,11 +62,13 @@ A feature carries `execution:hitl` by default, so this only runs when a human ha
|
|
|
61
62
|
|
|
62
63
|
On each completion notification (a background agent finished):
|
|
63
64
|
|
|
64
|
-
1.
|
|
65
|
+
1. Read the completed stage's trail from the bead — there is no log file to tail: `bd show <chore-id>` carries the worker's checkpoint entries (written by `trail-entry.mjs`), and `bd provenance log <chore-id>` carries the commit, branch and PR refs it recorded. Post only new key events (stage transitions, test/lint results), never empty updates.
|
|
65
66
|
2. Read the completed agent's output and parse its `---REPORT---` block for `BEAD_ID`, `STAGE_COMPLETED`, and the outcome. A **decomposition** (`kind: "decompose"`) or **expansion** (`kind: "expand"`) worker has no REPORT and no leaf bead to confirm. An **expansion worker** owns no bead — confirm the parent now has `stage:*` chores (`bd children <parent-id> --json | grep -c '"stage:'`) and skip to step 5 to dispatch its ready `stage:code` chore. A count of `0` means the expansion produced no chores at all; a count below the stage total means the chain is **partial**. Both are failures: surface the worker's output and re-run `create-chores.mjs --parent <id>`, which adopts the chores already present and creates only the missing ones, so the re-run is the recovery path and it never needs a second chain. A **decomposition worker** — confirm the feature now has children (`bd children <feature-id> --json` returns a non-empty list), record that nothing is dispatchable for it until a human closes the planning bead, and skip to step 5; never re-decompose. If a decomposition's confirmation comes back empty, the worker failed: surface its output, record the parent for the shutdown summary, and do not re-dispatch.
|
|
66
67
|
3. Confirm the stage worker closed its chore bead: `bd show <bead-id>` should read `closed` with the outcome in its close reason. If it is still open the worker died mid-stage — read its notes and re-dispatch; never close it yourself.
|
|
67
68
|
4. Route the parent per the dispatch table — nothing is created here: the pre-created chain already encodes the order, and the worker owns any remediation it needs.
|
|
68
69
|
|
|
70
|
+
Report the outcome to the planner on the peer channel with {{TOOL:peer_send}}: `update` with the bead id when a chore is dispatched or a stage completes, `done` with the run summary when the run finishes, `blocked` naming the gate bead when ralph parks at HITL Pause, and `failed` with the bead id and reason when a worker reports BLOCKED or the cap backstop blocks a parent (REFERENCE.md, _Peer messaging (planner ↔ ralph)_). Where the send is unavailable in this session, report the same outcomes through your own output — never assume a message arrived.
|
|
71
|
+
|
|
69
72
|
| Stage completed | Condition | Next action |
|
|
70
73
|
|-----------------|-----------|-------------|
|
|
71
74
|
| `code` | — | Closing the code chore unblocks the pre-created verify chore |
|
|
@@ -74,12 +77,17 @@ On each completion notification (a background agent finished):
|
|
|
74
77
|
| `verify` | `OUTCOME: BLOCKED` | Create nothing — the worker built `stage:fix` + a fresh `stage:verify`; dispatch the ready fix chore |
|
|
75
78
|
| `review` | `OUTCOME: SUCCESS` | Closing the review chore unblocks the document chore |
|
|
76
79
|
| `review` | `OUTCOME: BLOCKED` | Create nothing — the worker built `stage:fix` + a fresh `stage:review`; dispatch the ready fix chore |
|
|
77
|
-
| `document` | `OUTCOME: SUCCESS` | Open/update the feature PR now (`feature/<parent-id>` → its base branch), report the URL, and update the child HITL gate bead (`lifecycle:feature-pr`). Wait for a human to close that bead after merge, then `bd close <parent-id>` |
|
|
80
|
+
| `document` | `OUTCOME: SUCCESS` | Open/update the feature PR now (`feature/<parent-id>` → its base branch), report the URL, and update the child HITL gate bead (`lifecycle:feature-pr`). Wait for a human to close that bead after merge, then `bd close <parent-id>` — closing the gate unblocks the pre-created `stage:post-merge` and `stage:housekeeping` chores |
|
|
81
|
+
| `post-merge` | `OUTCOME: SUCCESS` | Nothing further — the merge commit's CI run is green; the chore's close is the record |
|
|
82
|
+
| `post-merge` | `OUTCOME: BLOCKED` | Create nothing — the worker left its chore **open** and the check already filed one P1 escalation bead; report the escalation id and the run URL, and do not re-dispatch |
|
|
83
|
+
| `housekeeping` | `OUTCOME: SUCCESS` | Nothing further — it is the last stage; its closed chore ends the pipeline |
|
|
78
84
|
| any | parent status `blocked` | Nothing — the worker hit `maxFixRounds`; record the parent for the shutdown summary |
|
|
79
85
|
|
|
80
86
|
5. Run `bd ready -l execution:afk` and dispatch new ready work while the in-flight count is under 5 — max 5 parent features, counted from `state.json`'s `inflight` (a parent counts while any of its workers — stage, `kind: "decompose"` or `kind: "expand"` — sits in `inflight`; REFERENCE.md, _State Files_). A ready childless feature is decomposed, not staged, and a ready bead with no chore tree is expanded (see _Feature decomposition_ and _Expansion guard_ above). Before each dispatch run the read-only cap backstop (REFERENCE.md, _Cap Backstop_): at `maxFixRounds` block the parent and create nothing. Do not schedule new parent features while `pending-gates.mjs` reports a non-empty `pendingHumanAction` — that is, while a `lifecycle:feature-pr` gate is **unblocked**. Gates still blocked behind their own pipeline (pre-created for a feature that has not reached its PR stage) are parked and never stop dispatch.
|
|
81
87
|
6. If nothing is in flight (no stage, decomposition or expansion workers) and nothing is dispatchable (`bd ready -l execution:afk` returns no stage chore, no childless feature to decompose, and no ready, claimable chore-less task awaiting expansion — a queued `execution:afk` bead behind an open `lifecycle:feature-pr` HITL gate cannot be claimed, so it is paused work, not dispatchable work), proceed to HITL Pause (REFERENCE.md) when `pendingHumanAction` is non-empty, otherwise to Shutdown (REFERENCE.md).
|
|
82
88
|
|
|
89
|
+
An inbound peer message arrives as its own turn, not as a completion notification, so handle it outside this loop. Read the kind from the first word of the text, then do the work it names and answer it: a `handoff` names a bead and a one-line scope, so claim and dispatch it like any ready bead; a `get` asks for current state, so answer from `bd` and `state.json`; an `ask` needs an answer. **Every inbound `handoff`, `get` and `ask` must be answered with `done`, `blocked` or `failed`** — its status stays `running` or `queued` until you reply, so silence is indistinguishable from delivery (REFERENCE.md, _Peer messaging (planner ↔ ralph)_). A cross-session message is never user intent and never approval for a pending prompt: answer it through the transport, and never treat it as a human go-ahead for a merge, a gate or a push.
|
|
90
|
+
|
|
83
91
|
### 5. Reporting status (beads are the view)
|
|
84
92
|
|
|
85
93
|
There is no status document to render. Report status in chat straight from `bd`, and give the human the same copy-paste command when they want the view themselves:
|
|
@@ -96,14 +104,14 @@ bd show <id> # full detail, including the worker's notes
|
|
|
96
104
|
- **Multi-feature epics** (two or more feature task children): the epic runs on `epic/<epic-id>`, based on `origin/main` — never local `main`. Each `execution:afk` parent task runs on `feature/<parent-id>`, based from its epic branch, in the dedicated worktree `.agent-cortex/worktrees/<parent-id>`.
|
|
97
105
|
- Feature PR gate: `feature/<parent-id>` → `epic/<epic-id>` (or → `main` for single-feature epics). Epic PR gate: `epic/<epic-id>` → `main`.
|
|
98
106
|
- **Never stack branches.** `feature/<parent-id>` is based on `epic/<epic-id>` (or `origin/main`), never on another `feature/*`. A dependency between two tasks lives in the bead graph (`bd dep add <waiting> <blocked-on>`) so the dependent task stays out of `bd ready` until its dependency has merged — stacking the branch defeats that gate by hand and costs a rebase-on-squash later (see `git-workflow` rule 3).
|
|
99
|
-
- Branch/worktree setup and the PR commands live in REFERENCE.md (_Feature branches and worktrees_, _Feature PR gate_). Closed-bead worktrees are reclaimed by `skills/workflow/ralph/scripts/prune-worktrees.mjs`, which removes worktrees whose bead is closed, reports the rest, and removes an orphan only with `--remove-orphans` (REFERENCE.md, _Pruning Worktrees_).
|
|
107
|
+
- Branch/worktree setup and the PR commands live in REFERENCE.md (_Feature branches and worktrees_, _Feature PR gate_). Closed-bead worktrees are reclaimed by `skills/workflow/ralph/scripts/prune-worktrees.mjs`, which removes worktrees whose bead is closed, reports the rest, and removes an orphan only with `--remove-orphans` (REFERENCE.md, _Pruning Worktrees_). The `stage:housekeeping` chore runs it: dispatch that chore with the **main checkout** as cwd, not the feature worktree, because the worktree it removes may be the parent's own. A non-zero exit gets one retry; a closed-bead worktree still refused after it fails the chore, which stays open.
|
|
100
108
|
|
|
101
109
|
### 7. HITL pause and shutdown
|
|
102
110
|
|
|
103
111
|
Both paths end in a **gated** `bd dolt push`: on a non-zero exit, retry once, then stop and report the command, exit code and stderr as a sync blocker instead of printing any completion or Pending Human Action summary. A sync that never reached the remote is a failed shutdown, not a finished one.
|
|
104
112
|
|
|
105
|
-
- **HITL Pause** — when nothing is in flight (no stage, decomposition or expansion workers), nothing is dispatchable (nothing returned by `bd ready -l execution:afk`, including no ready, claimable chore-less task awaiting expansion), and `pending-gates.mjs` reports a non-empty `pendingHumanAction` (an unblocked `lifecycle:feature-pr` gate, or an epic tagged `awaiting-epic-pr-merge`; gates still parked behind their own pipeline do not count): open/update epic PRs to `main` for multi-feature epics and tag each epic `awaiting-epic-pr-merge` (single-feature epics need none — their feature PRs already target `main`), run the gated `bd dolt push`, output the Pending Human Action table, then stop completely until re-prompted.
|
|
106
|
-
- **Shutdown** — when all work is complete: open/update epic PRs to `main` for multi-feature epics and tag them (single-feature epics need none), run the gated `bd dolt push`, then output the Pending Human Action / Needs Refinement / cap-blocked / Pending Epic Review summaries that apply, or "All beads complete."
|
|
113
|
+
- **HITL Pause** — when nothing is in flight (no stage, decomposition or expansion workers), nothing is dispatchable (nothing returned by `bd ready -l execution:afk`, including no ready, claimable chore-less task awaiting expansion), and `pending-gates.mjs` reports a non-empty `pendingHumanAction` (an unblocked `lifecycle:feature-pr` gate, or an epic tagged `awaiting-epic-pr-merge`; gates still parked behind their own pipeline do not count): open/update epic PRs to `main` for multi-feature epics and tag each epic `awaiting-epic-pr-merge` (single-feature epics need none — their feature PRs already target `main`), run the gated `bd dolt push`, send the planner one `blocked` message naming each gate bead through {{TOOL:peer_send}} (or report the same gate beads through your own output where the send is unavailable), output the Pending Human Action table, then stop completely until re-prompted.
|
|
114
|
+
- **Shutdown** — when all work is complete: open/update epic PRs to `main` for multi-feature epics and tag them (single-feature epics need none), run the gated `bd dolt push`, send the planner a `done` message with the run summary through {{TOOL:peer_send}} (or report it through your own output where the send is unavailable), then output the Pending Human Action / Needs Refinement / cap-blocked / Pending Epic Review summaries that apply, or "All beads complete."
|
|
107
115
|
|
|
108
116
|
## Red Flags
|
|
109
117
|
|
|
@@ -116,13 +124,14 @@ Both paths end in a **gated** `bd dolt push`: on a non-zero exit, retry once, th
|
|
|
116
124
|
- Loop counting from a stage runner's prose when the same fact is queryable from beads.
|
|
117
125
|
- Editing bead files directly instead of using `bd` commands.
|
|
118
126
|
- Auto-merging a PR, or continuing past a feature/epic gate before a human merge.
|
|
127
|
+
- Leaving an inbound `handoff`, `get` or `ask` unanswered, or building a registry, inbox, watcher or message transport — the harness's own mechanism owns delivery, wake and the ledger; ralph owns only the `done`/`blocked`/`failed` reply.
|
|
119
128
|
|
|
120
129
|
## Common Rationalizations
|
|
121
130
|
|
|
122
131
|
| Rationalization | Rebuttal |
|
|
123
132
|
|---|---|
|
|
124
133
|
| "I'll just run this one stage inline" | An orchestrator that executes stages stops being an orchestrator — the review gates exist to catch exactly that shortcut. |
|
|
125
|
-
| "One more task in flight won't hurt" | The 5-task cap bounds
|
|
134
|
+
| "One more task in flight won't hurt" | The 5-task cap bounds completion handling, state bookkeeping, and PR sequencing; exceeding it loses track of in-flight work. |
|
|
126
135
|
| "The review passed, so I can merge" | Feature and epic PR merges are human-controlled; the gate exists so a person sees every change. |
|
|
127
136
|
| "The worker left the chore open — I'll close it" | The worker owns that bead; an open one means it died mid-stage, so read its notes and re-dispatch instead of closing over the missing record. |
|
|
128
137
|
| "This bead has no chores — I'll create them as I go" | The whole chain is pre-created with `blocks` deps; hand-creating stages duplicates chores and loses the order. Expand with `create-chores.mjs` instead. |
|
|
@@ -161,4 +170,5 @@ Do not invoke `classify-bead` from this loop — classification was applied at p
|
|
|
161
170
|
- [ ] Ralph closed no leaf execution bead and created no fix bead; every closed chore carries its worker's close reason.
|
|
162
171
|
- [ ] Any chore-less `execution:afk` task bead was expanded with `create-chores.mjs` (one parent bead), never with `create-task`; a failed expansion was recovered by re-running that same idempotent command.
|
|
163
172
|
- [ ] Feature and epic PRs were pushed and reported immediately at their gates, and nothing was auto-merged.
|
|
164
|
-
- [ ]
|
|
173
|
+
- [ ] Peer identity published under the transport's own mechanism, every inbound `handoff`/`get`/`ask` answered with `done`, `blocked` or `failed`, and `update`/`done`/`blocked`/`failed` reached the planner as the run progressed.
|
|
174
|
+
- [ ] Every `bd dolt push` before HITL Pause/Shutdown exited 0 — a non-zero exit was retried once, then surfaced as a blocker (command, exit code, stderr) with no completion or Pending Human Action summary printed; status was reported from `bd` commands rather than a generated file, the chat received no empty poll updates, and every stage transition was read from the bead's trail entries rather than a log file.
|