@jaybeeuu/agent-cortex 2.0.0 → 2.2.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 +437 -0
- package/README.md +46 -15
- package/agents/README.md +22 -10
- package/agents/plan/agent.md +2 -2
- package/agents/ralph/agent.md +128 -91
- package/agents/ralph/claude/frontmatter.json +3 -3
- package/agents/ralph/claude/wake.md +46 -0
- package/agents/ralph/pi/{polling.md → wake.md} +35 -20
- package/agents/ralph-plan/agent.md +23 -11
- package/bin/agent-cortex.mjs +66 -4
- package/bin/installers/claude.mjs +262 -77
- package/bin/installers/pi.mjs +200 -24
- package/extensions/agent-guardrails/README.md +1 -1
- package/extensions/agent-guardrails/guardrails.ts +1 -1
- package/extensions/agent-guardrails/index.ts +1 -1
- package/extensions/agent-modes/discover.test.ts +2 -2
- package/extensions/output-style/README.md +58 -0
- package/extensions/output-style/index.ts +23 -0
- package/extensions/output-style/output-style.md +43 -0
- package/extensions/output-style/output-style.test.ts +50 -0
- package/extensions/output-style/output-style.ts +32 -0
- package/extensions/output-style/package.json +19 -0
- package/hooks/claude/scripts/notify.mjs +8 -1
- package/lib/cli.mjs +91 -2
- package/package.json +29 -4
- package/scripts/check-changeset.mjs +4 -0
- package/scripts/check-no-sync.mjs +5 -1
- package/scripts/check-skill-frontmatter.mjs +21 -0
- package/scripts/lib/compose-agent.mjs +69 -9
- package/scripts/lib/fs.mjs +10 -2
- package/scripts/lib/migrate.mjs +736 -0
- package/scripts/lib/release-gates.mjs +21 -1
- package/scripts/lib/skill-frontmatter.mjs +152 -0
- package/scripts/lib/skill-links.mjs +52 -0
- package/scripts/lib/verify-worker-tree.mjs +25 -0
- package/scripts/resolve-publish-command.mjs +4 -0
- package/skills/engineering/design-an-interface/SKILL.md +3 -3
- package/skills/engineering/grill-with-docs/SKILL.md +3 -5
- package/skills/engineering/improve-codebase-architecture/SKILL.md +5 -4
- package/skills/engineering/tdd/SKILL.md +5 -7
- package/skills/engineering/technical-direction/SKILL.md +5 -5
- package/skills/planning/classify-bead/REFERENCE.md +3 -3
- package/skills/planning/classify-bead/SKILL.md +14 -9
- package/skills/planning/classify-bead/scripts/classify-bead.mjs +5 -4
- package/skills/planning/create-task/REFERENCE.md +52 -3
- package/skills/planning/create-task/SKILL.md +70 -26
- package/skills/planning/create-task/scripts/{create-chores.ts → create-chores.mjs} +48 -23
- package/skills/planning/flesh-out-idea/REFERENCE.md +116 -0
- package/skills/planning/flesh-out-idea/SKILL.md +146 -0
- package/skills/planning/prd-to-tasks/REFERENCE.md +54 -5
- package/skills/planning/prd-to-tasks/SKILL.md +22 -16
- package/skills/planning/record-idea/FORMAT.md +66 -17
- package/skills/planning/record-idea/SKILL.md +48 -24
- package/skills/planning/request-refactor-plan/SKILL.md +4 -4
- package/skills/planning/spec-to-tickets/SKILL.md +5 -5
- package/skills/planning/write-a-prd/SKILL.md +8 -9
- package/skills/planning/write-a-ticket/SKILL.md +4 -6
- package/skills/productivity/bd-tool/SKILL.md +15 -10
- package/skills/productivity/grill-me/SKILL.md +5 -0
- package/skills/productivity/init-beads/SKILL.md +3 -3
- package/skills/productivity/migration/REFERENCE.md +227 -0
- package/skills/productivity/migration/SKILL.md +188 -0
- package/skills/productivity/validate-agent-dir/scripts/{validate-agent-dir.ts → validate-agent-dir.mjs} +49 -28
- package/skills/productivity/write-a-skill/SKILL.md +5 -5
- package/skills/review/maintain-agent-docs/SKILL.md +5 -7
- package/skills/review/refactor-skill/SKILL.md +4 -3
- package/skills/review/review-security/SKILL.md +3 -5
- package/skills/style/style-code/SKILL.md +5 -10
- package/skills/style/style-comms/SKILL.md +5 -7
- package/skills/style/style-documentation/SKILL.md +5 -6
- package/skills/style/style-tests/SKILL.md +5 -7
- package/skills/workflow/git-workflow/SKILL.md +28 -4
- package/skills/workflow/hitl-collab/SKILL.md +4 -6
- package/skills/workflow/plan/SKILL.md +41 -30
- package/skills/workflow/ralph/REFERENCE.md +209 -55
- package/skills/workflow/ralph/SKILL.md +89 -50
- package/skills/workflow/ralph/scripts/pending-gates.mjs +134 -0
- package/skills/workflow/run-pipeline-stage/SKILL.md +72 -71
- package/skills/workflow/run-pipeline-stage/playbooks/code.md +11 -0
- package/skills/workflow/run-pipeline-stage/playbooks/document.md +12 -0
- package/skills/workflow/run-pipeline-stage/playbooks/fix.md +12 -0
- package/skills/workflow/run-pipeline-stage/playbooks/review.md +11 -0
- package/skills/workflow/run-pipeline-stage/playbooks/verify.md +13 -0
- package/skills/workflow/run-pipeline-stage/prompts/stage-runner.md +73 -1
- package/token-map.README.md +9 -4
- package/token-map.json +13 -6
- package/.agents/skills/style-versioning/SKILL.md +0 -172
- package/.changeset/README.md +0 -8
- package/.changeset/config.json +0 -11
- package/.github/workflows/ci.yml +0 -302
- package/.junie/guidelines.md +0 -87
- package/.junie/mcp/mcp.json +0 -10
- package/AGENTS.md +0 -151
- package/agents/ralph/claude/polling.md +0 -31
- package/agents-native/ralph.md +0 -193
- package/docs/claude-hooks.md +0 -128
- package/docs/commit-signing.md +0 -231
- package/docs/ideas/auto-model-selection.md +0 -75
- package/docs/ideas/ban-content-assertion-tests.md +0 -75
- package/docs/ideas/bead-progress-notes-for-session-resumption.md +0 -75
- package/docs/ideas/classify-beads-via-deterministic-script-with-agent-fallback.md +0 -70
- package/docs/ideas/ctx-alias-version-and-update-commands.md +0 -65
- package/docs/ideas/ctx-feedback-command-for-capturing-ideas-from-any-machine.md +0 -67
- package/docs/ideas/dedicated-review-tests-skill.md +0 -71
- package/docs/ideas/deployment-beads-for-post-merge-pipelines.md +0 -36
- package/docs/ideas/design-and-delivery-as-separate-bead-trees/design-and-delivery-as-separate-bead-trees.md +0 -99
- package/docs/ideas/enable-signed-commit-enforcement-changesets-bot-commits-must-be-signed.md +0 -42
- package/docs/ideas/extension-manifests.md +0 -121
- package/docs/ideas/improve-pi-system-prompt.md +0 -37
- package/docs/ideas/map-every-cortex-tool-to-a-real-pi-equivalent.md +0 -95
- package/docs/ideas/model-selector.md +0 -40
- package/docs/ideas/pi-task-memory.md +0 -34
- package/docs/ideas/qa-extension.md +0 -43
- package/docs/ideas/record-idea-implementation-questions.md +0 -68
- package/docs/ideas/record-ideas-as-beads.md +0 -115
- package/docs/ideas/self-flushing-session.md +0 -72
- package/docs/ideas/state-in-beads-not-md.md +0 -69
- package/docs/ideas/subagent-visibility.md +0 -72
- package/docs/ideas/task-session-context.local.md +0 -95
- package/docs/ideas/task-session-context.md +0 -89
- package/docs/ideas/test-data-creation-helpers.md +0 -92
- package/docs/ideas/test-planning-stage-bead-in-feature-planning.md +0 -68
- package/docs/ideas/unified-ci-pipeline.md +0 -28
- package/docs/ideas/unify-the-ticket-and-task-decomposition-workflows.md +0 -71
- package/docs/ideas/worktree-housekeeping-for-closed-beads.md +0 -39
- package/docs/inspirations.md +0 -33
- package/docs/research/disable-model-invocation.md +0 -82
- package/docs/research/gas-city-research.md +0 -256
- package/docs/research/skill-design-research.md +0 -286
- package/docs/research/skill-improvements-analysis.md +0 -117
- package/docs/research/skill-patterns-research.md +0 -24
- package/docs/skills/skill-anatomy.md +0 -197
- package/docs/technical-direction/harness-config.md +0 -177
- package/docs/technical-direction/migration-execution-labels.md +0 -177
- package/docs/user-preferences.md +0 -17
- package/extensions/agent-guardrails/tsconfig.json +0 -12
- package/extensions/agent-modes/tsconfig.json +0 -12
- package/extensions/session-start/tsconfig.json +0 -12
- package/extensions/skill-stats/pnpm-lock.yaml +0 -329
- package/extensions/skill-stats/tsconfig.json +0 -12
- package/extensions/subagent/agents/planner.md +0 -37
- package/extensions/subagent/agents/reviewer.md +0 -35
- package/extensions/subagent/agents/scout.md +0 -50
- package/extensions/subagent/agents/worker.md +0 -24
- package/extensions/subagent/prompts/implement-and-review.md +0 -10
- package/extensions/subagent/prompts/implement.md +0 -10
- package/extensions/subagent/prompts/scout-and-plan.md +0 -9
- package/extensions/subagent/tsconfig.json +0 -12
- package/pi/keybindings.json +0 -3
- package/pi/settings.json +0 -34
- package/pnpm-workspace.yaml +0 -14
- package/skills/planning/create-task/scripts/package.json +0 -14
- package/skills/planning/create-task/scripts/pnpm-lock.yaml +0 -342
- package/skills/planning/create-task/scripts/tsconfig.json +0 -12
- package/skills/planning/record-idea/scripts/new-idea.sh +0 -89
- package/skills/productivity/validate-agent-dir/scripts/package.json +0 -15
- package/skills/productivity/validate-agent-dir/scripts/pnpm-lock.yaml +0 -329
- package/skills/productivity/validate-agent-dir/scripts/pnpm-workspace.yaml +0 -2
- package/skills/productivity/validate-agent-dir/scripts/tsconfig.json +0 -19
- package/skills/productivity/validate-agent-dir/scripts/validate-agent-dir.test.ts +0 -317
- package/skills/workflow/run-pipeline-stage/REFERENCE.md +0 -22
- package/skills/workflow/run-pipeline-stage/scripts/generate-progress.test.ts +0 -682
- package/skills/workflow/run-pipeline-stage/scripts/generate-progress.ts +0 -588
- package/skills/workflow/run-pipeline-stage/scripts/package.json +0 -15
- package/skills/workflow/run-pipeline-stage/scripts/pnpm-lock.yaml +0 -342
- package/skills/workflow/run-pipeline-stage/scripts/pnpm-workspace.yaml +0 -2
- package/skills/workflow/run-pipeline-stage/scripts/tsconfig.json +0 -12
- package/test/classify-bead.test.mjs +0 -277
- package/test/claude-installer.test.mjs +0 -308
- package/test/cli.test.mjs +0 -314
- package/test/create-chores.test.mjs +0 -85
- package/test/fake-bd.test.mjs +0 -72
- package/test/fs.test.mjs +0 -76
- package/test/helpers/fake-bd.mjs +0 -87
- package/test/helpers/fake-claude.mjs +0 -152
- package/test/helpers/fake-pi.mjs +0 -132
- package/test/installer.test.mjs +0 -664
- package/test/notify-hook.test.mjs +0 -63
- package/test/pi-installer.test.mjs +0 -785
- package/test/pi-register.test.mjs +0 -342
- package/test/pi-sweep.test.mjs +0 -619
- package/test/release-gates.test.mjs +0 -190
- package/test/verify-worker-tree.test.mjs +0 -120
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ralph
|
|
3
|
-
description: "Run
|
|
3
|
+
description: "Run every ready execution:afk bead end-to-end — decomposing features, expanding tasks into pipeline-stage chores, and driving up to 5 concurrent workers in dedicated worktrees while opening review-gated PRs immediately and pausing for human merges. Use when you want to \"run the backlog\", \"run all beads\", or \"run ralph\" with human approval gates."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Ralph
|
|
7
7
|
|
|
8
|
-
Ralph is a parallel orchestration loop. It advances
|
|
8
|
+
Ralph is a parallel orchestration loop. It advances `execution:afk` beads through pipeline-stage chores (`code` → `verify` → `review` → `document`), dispatching each unit as a background subagent in its parent's worktree, then opens review-gated feature and epic PRs and pauses for human merges. Ralph never writes code itself — it dispatches, tracks, and reports.
|
|
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
|
+
|
|
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.
|
|
13
|
+
|
|
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.
|
|
11
15
|
|
|
12
16
|
## When to use
|
|
13
17
|
|
|
14
18
|
- "Run the backlog", "run all beads", "run ralph", "run everything pending".
|
|
15
|
-
- A planning session has produced classified, unblocked
|
|
19
|
+
- A planning session has produced classified, unblocked `execution:afk` beads and you want autonomous execution with human review gates.
|
|
16
20
|
- You want the full pipeline (implement → verify → review → document) orchestrated concurrently, with fix loops on failure.
|
|
17
21
|
|
|
18
22
|
## When NOT to use
|
|
@@ -29,61 +33,85 @@ State model: task state lives in beads (ready/in_progress via `bd`); the only lo
|
|
|
29
33
|
2. Ensure `.agent-cortex/` and `.agent-cortex/worktrees/` are in the project's `.gitignore` (append any that are missing).
|
|
30
34
|
3. Create the workspace: `mkdir -p .agent-cortex/ralph`.
|
|
31
35
|
4. Initialize state: write `.agent-cortex/ralph/state.json` with `{ "inflight": [] }`.
|
|
32
|
-
5. Read `skills/planning/create-task/pipeline.json` and hold it in memory — you need its `maxFixRounds` for the
|
|
33
|
-
6. Run
|
|
34
|
-
-
|
|
35
|
-
- **
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
36
|
+
5. Read `skills/planning/create-task/pipeline.json` and hold it in memory — you need its `maxFixRounds` for the read-only cap backstop.
|
|
37
|
+
6. 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
|
+
- **`feature` bead with no children**: not dispatchable work yet — decompose it (_Feature decomposition_ below) and re-run the query.
|
|
39
|
+
- **Bead carrying a `stage:*` label**: a pipeline stage chore — dispatch it (_Event loop_, and _Dispatching a Chore Bead_ in REFERENCE.md).
|
|
40
|
+
- **`task` or other bead with no `stage:*` chore children**: expand it first (_Expansion guard_ below), then dispatch its ready `stage:code` chore.
|
|
41
|
+
- **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
|
+
`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
|
+
7. 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_).
|
|
44
|
+
8. Hand over to your harness's completion-detection mechanism: PI keeps a {{TOOL:wait_for_agents}} call 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
|
+
### 2. Expansion guard (stage chores are pre-created)
|
|
47
|
+
|
|
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
|
+
|
|
50
|
+
**Never expand an existing bead with `create-task`.** It creates a bead first and expands second, so the original would stay chore-less and gain a **second parent** beside it. `create-chores.mjs` expands; only `create-task` creates parents.
|
|
51
|
+
|
|
52
|
+
### 3. Feature decomposition
|
|
53
|
+
|
|
54
|
+
A ready `execution:afk` bead of type `feature` with no children is not dispatchable work — there is nothing to stage and nothing to expand. Decompose it: dispatch a worker in `.agent-cortex/worktrees/<feature-id>` that turns the feature into one `execution:hitl` planning bead plus `execution:afk` implementation children that depend on it, then continue (REFERENCE.md, _Feature Decomposition_).
|
|
55
|
+
|
|
56
|
+
A feature carries `execution:hitl` by default, so this only runs when a human has deliberately relabelled a feature `execution:afk` to make its delivery agent-routable. Decomposition is then the last thing ralph does with it for a while: the children depend on the planning bead, so once the worker reports, `bd ready -l execution:afk` returns only the feature itself — no dispatchable child — and skipping that row is the *correct* behaviour, not re-expanding. **Never re-dispatch a decomposition**: the bead creation it runs is not idempotent, so a second run duplicates the whole hierarchy.
|
|
57
|
+
|
|
58
|
+
### 4. Event loop
|
|
47
59
|
|
|
48
60
|
On each completion notification (a background agent finished):
|
|
49
61
|
|
|
50
62
|
1. Poll that parent's log for new lines — `tail -n +<nextLine> .agent-cortex/ralph/ralph-<parent-id>.log` (logs are keyed by parent bead ID, so every stage of a feature shares one file) — and post only new key events (stage transitions, test/lint results), never empty updates.
|
|
51
|
-
2. Read the completed agent's output with {{TOOL:read_agent}} and parse its `---REPORT---` block for `BEAD_ID`, `STAGE_COMPLETED`, and the outcome.
|
|
52
|
-
3.
|
|
53
|
-
4.
|
|
63
|
+
2. Read the completed agent's output with {{TOOL:read_agent}} 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 **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 either confirmation comes back empty, the worker failed: surface its output, record the parent for the shutdown summary, and do not re-dispatch.
|
|
64
|
+
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.
|
|
65
|
+
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.
|
|
54
66
|
|
|
55
67
|
| Stage completed | Condition | Next action |
|
|
56
68
|
|-----------------|-----------|-------------|
|
|
57
|
-
| `code` | — | Closing the code chore unblocks the verify chore |
|
|
58
|
-
| `fix` | — | Closing the fix chore unblocks the
|
|
59
|
-
| `verify` | `
|
|
60
|
-
| `verify` | `
|
|
61
|
-
| `review` | `
|
|
62
|
-
| `review` | `
|
|
63
|
-
| `document` |
|
|
69
|
+
| `code` | — | Closing the code chore unblocks the pre-created verify chore |
|
|
70
|
+
| `fix` | — | Closing the fix chore unblocks the fresh stage chore it was wired to |
|
|
71
|
+
| `verify` | `OUTCOME: SUCCESS` | Closing the verify chore unblocks the review chore |
|
|
72
|
+
| `verify` | `OUTCOME: BLOCKED` | Create nothing — the worker built `stage:fix` + a fresh `stage:verify`; dispatch the ready fix chore |
|
|
73
|
+
| `review` | `OUTCOME: SUCCESS` | Closing the review chore unblocks the document chore |
|
|
74
|
+
| `review` | `OUTCOME: BLOCKED` | Create nothing — the worker built `stage:fix` + a fresh `stage:review`; dispatch the ready fix chore |
|
|
75
|
+
| `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>` |
|
|
76
|
+
| any | parent status `blocked` | Nothing — the worker hit `maxFixRounds`; record the parent for the shutdown summary |
|
|
77
|
+
|
|
78
|
+
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.
|
|
79
|
+
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).
|
|
80
|
+
|
|
81
|
+
### 5. Reporting status (beads are the view)
|
|
64
82
|
|
|
65
|
-
|
|
66
|
-
6. If no chores are in-flight and `bd ready` returns no `stage:*` chores, proceed to HITL Pause (REFERENCE.md) when HITL gate beads are pending, otherwise to Shutdown (REFERENCE.md).
|
|
67
|
-
7. Regenerate `.agent-cortex/ralph/progress.md`.
|
|
83
|
+
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:
|
|
68
84
|
|
|
69
|
-
|
|
85
|
+
```bash
|
|
86
|
+
bd list --parent <parent-id> # every chore for a feature, with its state
|
|
87
|
+
bd ready -l execution:afk # the next dispatchable work
|
|
88
|
+
bd show <id> # full detail, including the worker's notes
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 6. Branching and review model
|
|
70
92
|
|
|
71
93
|
- **Single-feature epics** (exactly one feature task child — count with `bd children <epic-id>`, excluding chores): no epic branch. `feature/<parent-id>` is based directly on `origin/main`, its feature PR targets `main`, and no epic PR is opened.
|
|
72
|
-
- **Multi-feature epics** (two or more feature task children): the epic runs on `epic/<epic-id>`, based on `origin/main` — never local `main`. Each
|
|
94
|
+
- **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>`.
|
|
73
95
|
- Feature PR gate: `feature/<parent-id>` → `epic/<epic-id>` (or → `main` for single-feature epics). Epic PR gate: `epic/<epic-id>` → `main`.
|
|
96
|
+
- **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).
|
|
74
97
|
- Branch/worktree setup and the PR commands live in REFERENCE.md (_Feature branches and worktrees_, _Feature PR gate_).
|
|
75
98
|
|
|
76
|
-
###
|
|
99
|
+
### 7. HITL pause and shutdown
|
|
100
|
+
|
|
101
|
+
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.
|
|
77
102
|
|
|
78
|
-
- **HITL Pause** — when
|
|
79
|
-
- **Shutdown** — when all work is complete:
|
|
103
|
+
- **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.
|
|
104
|
+
- **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."
|
|
80
105
|
|
|
81
106
|
## Red Flags
|
|
82
107
|
|
|
83
108
|
- Running as a background or delegated subagent instead of the interactive foreground agent.
|
|
84
109
|
- Spawning a stage runner via {{TOOL:bash}} or running a stage inline instead of {{TOOL:task}}.
|
|
85
|
-
-
|
|
86
|
-
-
|
|
110
|
+
- Closing a leaf execution bead, or creating a `stage:fix`/feedback bead — both belong to the worker.
|
|
111
|
+
- Creating a stage chore, or running `create-task` for an existing bead — the chain is pre-created and `create-task` would add a second parent.
|
|
112
|
+
- Re-dispatching a decomposition because "the feature still looks idle" — the children are blocked on the planning bead, which is Q5b working, not a failure. Never re-create the hierarchy.
|
|
113
|
+
- Reaching for a narrower dispatch query (`bd ready -t chore`, "chores with `stage:*`") — an `execution:afk` bead with no stage chores is work waiting for expansion, not work that does not exist.
|
|
114
|
+
- Loop counting from a stage runner's prose when the same fact is queryable from beads.
|
|
87
115
|
- Editing bead files directly instead of using `bd` commands.
|
|
88
116
|
- Auto-merging a PR, or continuing past a feature/epic gate before a human merge.
|
|
89
117
|
|
|
@@ -94,29 +122,40 @@ On each completion notification (a background agent finished):
|
|
|
94
122
|
| "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. |
|
|
95
123
|
| "One more task in flight won't hurt" | The 5-task cap bounds log polling, state bookkeeping, and PR sequencing; exceeding it loses track of in-flight work. |
|
|
96
124
|
| "The review passed, so I can merge" | Feature and epic PR merges are human-controlled; the gate exists so a person sees every change. |
|
|
125
|
+
| "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. |
|
|
126
|
+
| "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. |
|
|
127
|
+
| "That feature came back ready again, so I'll decompose it a second time" | The implementation children depend on the planning bead, so nothing is ready until a human closes it. Re-decomposing duplicates the hierarchy; idling *is* the correct outcome. |
|
|
97
128
|
|
|
98
129
|
## Philosophy / rationale
|
|
99
130
|
|
|
100
131
|
- Orchestration state derives from beads so a crash loses nothing but an in-memory ID mapping: `state.json` holds only the agent-ID→bead mapping, never task state.
|
|
101
|
-
-
|
|
132
|
+
- Every control-flow predicate — is anything in flight, is there work, is there room, may we pause or shut down — derives from `state.json`'s `inflight` (`kind: "stage"`, `kind: "decompose"` or `kind: "expand"`) plus `bd ready -l execution:afk`, never from a chore-type `bd` query: an expansion or decomposition worker owns no stage chore, so a chore-only predicate reports idle while an agent is running (REFERENCE.md, _The in-flight invariant_).
|
|
133
|
+
- One predicate answers both *who runs this* and *when*: `execution:afk` says it is agent work and the bead graph says it is ready, so ralph needs no label that encodes "not really work yet" — a childless feature is routed by its type, not excluded by a filter. Classification happens up-front at planning time (`create-task`), so the loop can trust the query instead of re-interpreting each bead's intent.
|
|
102
134
|
- Human review gates are non-negotiable because agent-authored changes still need a person to merge them.
|
|
135
|
+
- One owner per lifecycle: the actor that did the work records the outcome, so beads never carry an outcome written by someone who was not there.
|
|
103
136
|
|
|
104
137
|
## Phase-gate checklists
|
|
105
138
|
|
|
106
|
-
- [ ] Init complete: `bd prime` held, gitignore entries present, workspace and state.json created, pipeline.json read, first `bd ready` classified, first
|
|
107
|
-
- [ ] Event loop handed off: completion-detection mechanism running (or HITL Pause/Shutdown started)
|
|
139
|
+
- [ ] Init complete: `bd prime` held, gitignore entries present, workspace and state.json created, pipeline.json read, first `bd ready -l execution:afk` classified, first agent work dispatched (a stage chore, a decomposition worker or an expansion worker).
|
|
140
|
+
- [ ] Event loop handed off: completion-detection mechanism running (or HITL Pause/Shutdown started) — a decomposition- or expansion-only init is in flight, not idle.
|
|
141
|
+
|
|
142
|
+
## See Also
|
|
108
143
|
|
|
109
|
-
|
|
144
|
+
- [run-pipeline-stage](../run-pipeline-stage/SKILL.md) — owns the stage-runner prompt, per-stage playbooks, the `---REPORT---` format, and the worker's remediation chain. Read it before dispatching any stage.
|
|
145
|
+
- [create-task](../../planning/create-task/SKILL.md) — owns `pipeline.json` and the HITL gate beads; its `scripts/pending-gates.mjs` is the only home of the gate queries.
|
|
146
|
+
- [prd-to-tasks](../../planning/prd-to-tasks/SKILL.md) — load when the decomposition worker expands a feature into slices.
|
|
110
147
|
|
|
111
|
-
|
|
112
|
-
- The `create-task` skill owns `pipeline.json` and creates the `lifecycle:feature-pr` HITL gate beads.
|
|
113
|
-
- Do not invoke `classify-bead` from this loop — classification was already applied at planning time.
|
|
148
|
+
Do not invoke `classify-bead` from this loop — classification was applied at planning time.
|
|
114
149
|
|
|
115
150
|
## Verification checklist
|
|
116
151
|
|
|
117
|
-
- [ ] Init ran once with observable results: `state.json` exists, `bd ready` was parsed, and
|
|
152
|
+
- [ ] Init ran once with observable results: `state.json` exists, `bd ready -l execution:afk` was parsed, and agent work is in flight — a stage chore claimed, or a decomposition or expansion worker dispatched for a bead that needed one.
|
|
118
153
|
- [ ] Every stage runner was spawned with {{TOOL:task}} from the parent's feature worktree, with the `---REPORT---` instruction in its prompt.
|
|
119
|
-
- [ ] Every
|
|
120
|
-
- [ ]
|
|
154
|
+
- [ ] Every dispatch came from `bd ready -l execution:afk` — no narrower chore-only filter was used to decide that a bead was not work.
|
|
155
|
+
- [ ] A childless `execution:afk` feature was decomposed exactly once into a planning bead plus `execution:afk` children depending on it, and was not re-decomposed on later idle passes.
|
|
156
|
+
- [ ] In-flight count never exceeded 5, and the read-only cap backstop ran before every `stage:*` dispatch.
|
|
157
|
+
- [ ] Every in-flight, dispatchable-work, HITL-pause, and shutdown predicate was read from `state.json`'s `inflight` (stage, decomposition **and** expansion workers) plus `bd ready -l execution:afk`, with the ready, claimable scope of a chore-less `execution:afk` task bead — never from a chore-only `bd` query, and never counting a bead an open `lifecycle:feature-pr` gate blocks from being claimed.
|
|
158
|
+
- [ ] Ralph closed no leaf execution bead and created no fix bead; every closed chore carries its worker's close reason.
|
|
159
|
+
- [ ] Any chore-less `execution:afk` task bead was expanded with `create-chores.mjs` (one parent bead), never with `create-task`.
|
|
121
160
|
- [ ] Feature and epic PRs were pushed and reported immediately at their gates, and nothing was auto-merged.
|
|
122
|
-
- [ ] `bd dolt push`
|
|
161
|
+
- [ ] 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, and the chat received no empty poll updates.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Resolves which beads are waiting on a human, and which are merely parked
|
|
2
|
+
// behind their own pipeline.
|
|
3
|
+
//
|
|
4
|
+
// Ralph schedules new parent features only when nothing is pending human action.
|
|
5
|
+
// The distinction that matters is *unblocked*, not *open*: `create-chores`
|
|
6
|
+
// pre-creates a `lifecycle:feature-pr` gate for every planned feature, blocked
|
|
7
|
+
// behind that feature's own document chore. Such a gate is parked — the feature
|
|
8
|
+
// has not reached its PR stage — and must never stop dispatch. Only once the
|
|
9
|
+
// document chore closes does the gate unblock, and that is the state in which a
|
|
10
|
+
// feature genuinely awaits a human merge.
|
|
11
|
+
//
|
|
12
|
+
// Zero dependencies: run with `node pending-gates.mjs`.
|
|
13
|
+
|
|
14
|
+
import { execFile } from 'node:child_process';
|
|
15
|
+
import { promisify } from 'node:util';
|
|
16
|
+
import { pathToFileURL } from 'node:url';
|
|
17
|
+
|
|
18
|
+
/** The structural label every feature PR gate carries, whatever its axis. */
|
|
19
|
+
export const FEATURE_GATE_LABEL = 'lifecycle:feature-pr';
|
|
20
|
+
|
|
21
|
+
/** Epics whose PR into `main` awaits a human merge. */
|
|
22
|
+
export const EPIC_GATE_LABEL = 'awaiting-epic-pr-merge';
|
|
23
|
+
|
|
24
|
+
// Two-axis migration: gates created before the `execution:afk/hitl` migration
|
|
25
|
+
// carry `implementation-type:hitl`, gates created after carry `execution:hitl`.
|
|
26
|
+
// Both mean "a human must act", so the query matches either.
|
|
27
|
+
export const HITL_GATE_LABELS = ['execution:hitl', 'implementation-type:hitl'];
|
|
28
|
+
|
|
29
|
+
/** @typedef {{ id: string, title?: string }} Bead */
|
|
30
|
+
/** @typedef {{ id: string, title: string, kind: 'feature-pr' | 'epic-pr' }} PendingAction */
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {object} GateState
|
|
34
|
+
* @property {PendingAction[]} pendingHumanAction Beads a human must act on now — non-empty means "do not schedule new parent features".
|
|
35
|
+
* @property {Array<{ id: string, title: string }>} parked Open gates still blocked behind their own pipeline; informational only.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Split open gates into those awaiting a human now and those still parked.
|
|
40
|
+
*
|
|
41
|
+
* @param {object} input
|
|
42
|
+
* @param {Bead[]} input.readyGates Open gates with no active blockers.
|
|
43
|
+
* @param {Bead[]} input.openGates Every open gate, blocked or not.
|
|
44
|
+
* @param {Bead[]} input.epicsAwaitingMerge Epics tagged `awaiting-epic-pr-merge`.
|
|
45
|
+
* @returns {GateState}
|
|
46
|
+
*/
|
|
47
|
+
export function resolveGateState({ readyGates, openGates, epicsAwaitingMerge }) {
|
|
48
|
+
const actionable = new Set(readyGates.map((bead) => bead.id));
|
|
49
|
+
|
|
50
|
+
const pendingHumanAction = [
|
|
51
|
+
...readyGates.map((bead) => pendingAction(bead, 'feature-pr')),
|
|
52
|
+
...epicsAwaitingMerge.map((bead) => pendingAction(bead, 'epic-pr')),
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
const parked = openGates
|
|
56
|
+
.filter((bead) => !actionable.has(bead.id))
|
|
57
|
+
.map((bead) => ({ id: bead.id, title: bead.title ?? '' }));
|
|
58
|
+
|
|
59
|
+
return { pendingHumanAction, parked };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @param {Bead} bead
|
|
64
|
+
* @param {'feature-pr' | 'epic-pr'} kind
|
|
65
|
+
* @returns {PendingAction}
|
|
66
|
+
*/
|
|
67
|
+
function pendingAction(bead, kind) {
|
|
68
|
+
return { id: bead.id, title: bead.title ?? '', kind };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// BD_PATH env override lets machines where bd is not on PATH pin a location.
|
|
72
|
+
const BD_PATH = process.env.BD_PATH || 'bd';
|
|
73
|
+
const execFileAsync = promisify(execFile);
|
|
74
|
+
|
|
75
|
+
/** @type {(args: string[]) => Promise<string>} */
|
|
76
|
+
async function defaultExec(args) {
|
|
77
|
+
const { stdout } = await execFileAsync(BD_PATH, args, { encoding: 'utf-8' });
|
|
78
|
+
return stdout;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Query bd for the current gate state.
|
|
83
|
+
*
|
|
84
|
+
* @param {object} [options]
|
|
85
|
+
* @param {(args: string[]) => Promise<string>} [options.exec] bd runner; injectable for tests.
|
|
86
|
+
* @returns {Promise<GateState>}
|
|
87
|
+
*/
|
|
88
|
+
export async function fetchGateState({ exec = defaultExec } = {}) {
|
|
89
|
+
const gateFilter = ['-l', FEATURE_GATE_LABEL, '--label-any', HITL_GATE_LABELS.join(',')];
|
|
90
|
+
|
|
91
|
+
// `bd ready` is blocker-aware: it returns only gates whose document chore has
|
|
92
|
+
// closed, i.e. the ones genuinely awaiting a human merge.
|
|
93
|
+
const readyGates = JSON.parse(await exec(['ready', '--json', ...gateFilter]));
|
|
94
|
+
const openGates = JSON.parse(await exec(['list', '--json', '--limit', '0', ...gateFilter]));
|
|
95
|
+
const epicsAwaitingMerge = JSON.parse(await exec(['list', '--json', '--limit', '0', '-l', EPIC_GATE_LABEL]));
|
|
96
|
+
|
|
97
|
+
return resolveGateState({ readyGates, openGates, epicsAwaitingMerge });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ─── CLI ──────────────────────────────────────────────────────────────────────
|
|
101
|
+
|
|
102
|
+
const USAGE = 'Usage: node pending-gates.mjs\n\nPrints { pendingHumanAction, parked } as JSON.\n';
|
|
103
|
+
|
|
104
|
+
class UsageError extends Error {}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @param {object} options
|
|
108
|
+
* @param {string[]} options.argv CLI arguments (without node/script).
|
|
109
|
+
* @param {(args: string[]) => Promise<string>} [options.exec] bd runner; injectable for tests.
|
|
110
|
+
* @param {(chunk: string) => void} [options.write] stdout sink; injectable for tests.
|
|
111
|
+
* @returns {Promise<number>} process exit code.
|
|
112
|
+
*/
|
|
113
|
+
export async function runCli({ argv, exec = defaultExec, write = (chunk) => process.stdout.write(chunk) }) {
|
|
114
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
115
|
+
write(USAGE);
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
if (argv.length > 0) throw new UsageError(`unexpected argument: ${argv[0]}`);
|
|
119
|
+
|
|
120
|
+
write(`${JSON.stringify(await fetchGateState({ exec }), null, 2)}\n`);
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Only run the CLI when invoked directly, so tests can import the pure resolver.
|
|
125
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
126
|
+
runCli({ argv: process.argv.slice(2) })
|
|
127
|
+
.then((code) => {
|
|
128
|
+
process.exitCode = code;
|
|
129
|
+
})
|
|
130
|
+
.catch((error) => {
|
|
131
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
132
|
+
process.exitCode = error instanceof UsageError ? 2 : 1;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: run-pipeline-stage
|
|
3
|
-
description: Execute a single pipeline stage for a bead (code, verify, review, document) via its playbook and
|
|
3
|
+
description: Execute a single pipeline stage for a bead (code, verify, review, document) via its playbook and read-only routing, reporting the outcome in a fixed REPORT shape. Use when you want to "work through beads inline", "pick up a specific stage bead", or run one stage "without the full ralph orchestrator".
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Run Pipeline Stage
|
|
7
7
|
|
|
8
|
-
Stage beads (`stage:code`, `stage:verify`, `stage:review`, `stage:document`, `stage:fix`)
|
|
8
|
+
Stage beads (`stage:code`, `stage:verify`, `stage:review`, `stage:document`, `stage:fix`) come from the pipeline definition in `skills/planning/create-task/pipeline.json`. This skill executes one stage and owns that bead's lifecycle: the runner that does the work appends its own notes, closes its own leaf execution bead on **both** outcomes, and builds its own remediation children when the stage fails. The orchestrator dispatches and routes — it never edits or closes a leaf bead.
|
|
9
9
|
|
|
10
10
|
## When to use
|
|
11
11
|
|
|
@@ -20,65 +20,66 @@ Stage beads (`stage:code`, `stage:verify`, `stage:review`, `stage:document`, `st
|
|
|
20
20
|
- Creating or planning beads — use `create-task` or `plan`.
|
|
21
21
|
- Exploratory bead bookkeeping without a stage — that is `bd-tool`.
|
|
22
22
|
|
|
23
|
+
## Ownership split
|
|
24
|
+
|
|
25
|
+
| Actor | Owns |
|
|
26
|
+
|---|---|
|
|
27
|
+
| Stage runner (worker) | Milestone notes, progress-log lines, closing its leaf bead on both outcomes, remediation children, the REPORT |
|
|
28
|
+
| Orchestrator (ralph or you) | Claim, dispatch, forward routing, the read-only cap backstop, the PR gate |
|
|
29
|
+
|
|
30
|
+
A leaf bead is the worker's record of what happened. The orchestrator reads it; it does not write it.
|
|
31
|
+
|
|
23
32
|
## Workflow
|
|
24
33
|
|
|
25
34
|
1. Run `bd prime` and hold the output; subagents can run it themselves if they need context.
|
|
26
35
|
2. If no bead was specified, run `bd ready` and ask the user which to work on.
|
|
27
36
|
3. Claim the bead with `bd update <id> --claim` — the bead, not the prompt, is the source of truth. Append a stage-start note immediately (see _Milestone Notes_).
|
|
28
37
|
4. Read the bead's `stage:*` label — it names the stage to execute.
|
|
29
|
-
5. Load the universal stage-runner prompt (`skills/workflow/run-pipeline-stage/prompts/stage-runner.md`), populate `<stage>`, and read the matching playbook (`
|
|
30
|
-
6. Tag the stage
|
|
38
|
+
5. Load the universal stage-runner prompt (`skills/workflow/run-pipeline-stage/prompts/stage-runner.md`) — it carries the lifecycle, remediation, and REPORT contract — populate `<stage>`, and read the matching playbook (`playbooks/<stage>.md`) for stage-specific rules.
|
|
39
|
+
6. Tag the stage — beads are the only status view:
|
|
31
40
|
|
|
32
41
|
```bash
|
|
33
42
|
bd tag <id> stage:<stage>
|
|
34
|
-
# workspace must be the absolute path of the worktree — never . or $(pwd)
|
|
35
|
-
workspace="/absolute/path/to/worktree"
|
|
36
|
-
pnpm --prefix skills/workflow/run-pipeline-stage/scripts exec tsx generate-progress.ts --workspace "$workspace" > "$workspace/.agent-cortex/ralph/progress.md"
|
|
37
43
|
```
|
|
38
44
|
|
|
39
|
-
7. Dispatch one subagent per stage with the composed prompt — spawn it with {{TOOL:task}}
|
|
45
|
+
7. Dispatch one subagent per stage with the composed prompt — spawn it with {{TOOL:task}}. The prompt already ends with the REPORT contract; never compose one without it. A session with no subagent tool cannot dispatch: stop and say so rather than substituting a backgrounded CLI run, which has no completion signal.
|
|
40
46
|
8. Verify the delivery against the worktree — `node scripts/lib/verify-worker-tree.mjs --worktree <abs-worktree> --base <base>` exits 0 only for a clean tree with a commit ahead of base. A worker whose REPORT claims `OUTCOME: SUCCESS` over a dirty tree has not delivered; re-dispatch it instead of advancing.
|
|
41
|
-
9.
|
|
47
|
+
9. On the REPORT: route forward per _Routing_ below. Do **not** close the leaf bead — the worker closed it before reporting.
|
|
42
48
|
|
|
43
|
-
Each step leaves observable state — a claimed bead, a `stage:` tag, a
|
|
49
|
+
Each step leaves observable state — a claimed bead, a `stage:` tag, worker notes and a closed bead, a verified commit, and a REPORT that drives the next action.
|
|
44
50
|
|
|
45
|
-
##
|
|
51
|
+
## Routing (read-only)
|
|
46
52
|
|
|
47
|
-
|
|
48
|
-
|---|---|---|
|
|
49
|
-
| `code` | — | Run **verify** |
|
|
50
|
-
| `verify` | `OUTCOME: SUCCESS` | Run **review** |
|
|
51
|
-
| `verify` | `OUTCOME: BLOCKED` | Create a `stage:fix` chore (see _Feedback Beads_) |
|
|
52
|
-
| `review` | `OUTCOME: SUCCESS` | Run **document** |
|
|
53
|
-
| `review` | `OUTCOME: BLOCKED` and fixRounds < maxFixRounds | Create a `stage:fix` chore; increment fixRounds |
|
|
54
|
-
| `review` | `OUTCOME: BLOCKED` and fixRounds ≥ maxFixRounds | `bd update <parent-id> --status blocked --notes "max fix rounds reached"`; record for shutdown |
|
|
55
|
-
| `document` | — | `bd close <id>` — done |
|
|
56
|
-
|
|
57
|
-
`maxFixRounds` is read from `skills/planning/create-task/pipeline.json` (currently `4`). A fix chore re-runs the stage that failed — fix after verify → verify; fix after review → review.
|
|
53
|
+
The worker built anything this stage needed; routing picks up whatever `bd ready` returns.
|
|
58
54
|
|
|
59
|
-
|
|
55
|
+
| Stage complete | Condition | Next action |
|
|
56
|
+
| `code` | `OUTCOME: SUCCESS` | Dispatch the ready `stage:verify` chore |
|
|
57
|
+
| `verify` | `OUTCOME: SUCCESS` | Dispatch the ready `stage:review` chore |
|
|
58
|
+
| `review` | `OUTCOME: SUCCESS` | Dispatch the ready `stage:document` chore |
|
|
59
|
+
| `fix` | `OUTCOME: SUCCESS` | Dispatch the ready re-run chore that was blocked on the fix |
|
|
60
|
+
| `document` | `OUTCOME: SUCCESS` | Open/update the feature PR and the HITL gate bead |
|
|
61
|
+
| `code` / `verify` / `review` / `fix` / `document` | `OUTCOME: BLOCKED` | Create nothing — the worker built `stage:fix` + a fresh instance of its own stage; dispatch the ready fix chore |
|
|
62
|
+
| any | parent status `blocked` | Nothing — the worker or the cap backstop blocked it; record it for shutdown |
|
|
60
63
|
|
|
61
|
-
|
|
64
|
+
Cap backstop (read-only): before dispatching a `stage:*` chore, count the parent's fix rounds and, if already at `maxFixRounds` (`skills/planning/create-task/pipeline.json`, currently `4`), block the parent and create nothing:
|
|
62
65
|
|
|
63
66
|
```bash
|
|
64
|
-
|
|
65
|
-
--description "Requirements: see <parent-id> acceptance criteria (`bd show <parent-id>`).
|
|
66
|
-
|
|
67
|
-
<feedback content from REPORT>" --priority <parent-priority> -q)
|
|
68
|
-
bd tag $new_id stage:<next-stage>
|
|
69
|
-
bd tag $new_id workflow:ralph
|
|
70
|
-
bd dep add $new_id <parent-id> --type parent-child
|
|
67
|
+
fix_rounds=$(bd children <parent-id> --json | grep -c '"stage:fix"')
|
|
71
68
|
```
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
## Remediation Children (worker-built)
|
|
71
|
+
|
|
72
|
+
On `OUTCOME: BLOCKED` the worker creates its own chain — a `stage:fix` chore carrying the feedback, plus a **fresh instance of the stage that failed**, blocked on the fix. A fresh instance is not optional: the failed chore is already closed, so the pre-created chain cannot re-verify a fix.
|
|
75
73
|
|
|
76
|
-
|
|
|
77
|
-
|
|
78
|
-
| `
|
|
79
|
-
| `
|
|
74
|
+
| Failed stage | Chain |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `code` | `stage:fix` → fresh `stage:code` |
|
|
77
|
+
| `verify` | `stage:fix` → fresh `stage:verify` |
|
|
78
|
+
| `review` | `stage:fix` → fresh `stage:review` |
|
|
79
|
+
| `document` | `stage:fix` → fresh `stage:document` |
|
|
80
|
+
| `fix` | `stage:fix` → fresh `stage:fix` |
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
Webbing, at cap, and the exact commands live in `prompts/stage-runner.md` and each playbook's _Remediation and close_ section. In short: both beads are children of the parent task (`--parent <parent-id>`), the fresh stage is `bd dep add <fresh> <fix> --type blocks`, and any not-yet-run later stage is re-blocked behind the fresh bead. Chores carry no required sections, so the feedback description points at the parent/requirements bead (`<parent-id>`) rather than restating its acceptance criteria.
|
|
82
83
|
|
|
83
84
|
## Progress Logging
|
|
84
85
|
|
|
@@ -106,23 +107,21 @@ bd update <id> --append-notes "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] [<stage>] <what
|
|
|
106
107
|
|
|
107
108
|
## Report Format
|
|
108
109
|
|
|
109
|
-
Every
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
> ---
|
|
125
|
-
> ```
|
|
110
|
+
Every stage-runner prompt ends with the literal REPORT instruction (see `prompts/stage-runner.md`):
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
---REPORT---
|
|
114
|
+
BEAD_ID: <id>
|
|
115
|
+
STAGE_COMPLETED: <code|verify|review|document|fix>
|
|
116
|
+
SUMMARY: <2-3 sentence summary of what was done>
|
|
117
|
+
FILES_CHANGED: <comma-separated list, or "none">
|
|
118
|
+
COMMIT: <sha of the stage commit, or "none">
|
|
119
|
+
PUSHED: <yes|no>
|
|
120
|
+
PR: <feature PR url, or "none">
|
|
121
|
+
OUTCOME: <SUCCESS|BLOCKED>
|
|
122
|
+
BLOCKING_ISSUES: ← only if OUTCOME is BLOCKED
|
|
123
|
+
- <specific blocking issue>
|
|
124
|
+
```
|
|
126
125
|
|
|
127
126
|
Subagents report facts — do not ask them to suggest or predict the next step. A REPORT claiming
|
|
128
127
|
`OUTCOME: SUCCESS` with `PUSHED: no`, or a `COMMIT` that is not on the branch, records a failed
|
|
@@ -131,8 +130,9 @@ stage: the commit is the evidence, the report is the claim.
|
|
|
131
130
|
## Red Flags
|
|
132
131
|
|
|
133
132
|
- Dispatching a subagent before tagging the stage — beads are the only source of truth for stage progress.
|
|
133
|
+
- Closing a leaf bead from the orchestrator — the worker closed it; a second close hides who owned the outcome.
|
|
134
|
+
- Creating a feedback or fix bead from the orchestrator — that is the worker's contract and bypasses its cap accounting.
|
|
134
135
|
- Composing a subagent prompt without the REPORT contract — the output cannot be routed.
|
|
135
|
-
- Creating a feedback bead and then dispatching anyway — bypasses the fix-round accounting and parent-child dependency graph.
|
|
136
136
|
- Skipping the playbook "because the stage is simple" — playbooks carry the per-stage discipline the pipeline relies on.
|
|
137
137
|
- Advancing on `OUTCOME: SUCCESS` without verifying the worktree — the commit is the evidence, the report is the claim.
|
|
138
138
|
- Leaving bead notes to the end of the stage — a session that dies mid-stage then loses the whole trail.
|
|
@@ -140,34 +140,35 @@ stage: the commit is the evidence, the report is the claim.
|
|
|
140
140
|
## Common Rationalizations
|
|
141
141
|
|
|
142
142
|
| "The stage is small — I'll just run it directly" | The stage skills and playbook keep output conformant; skipping them produces work that loops back as a fix chore. |
|
|
143
|
-
| "I'll
|
|
144
|
-
| "
|
|
143
|
+
| "I'll close the chore — the worker forgot" | The worker closing it is the contract; an unclosed bead means the worker died, so read its notes and re-dispatch, do not paper over it. |
|
|
144
|
+
| "I'll wire the fix chain myself — faster" | Two owners of one lifecycle is what this skill exists to remove; worker-built chains keep cap accounting and the dependency graph truthful. |
|
|
145
|
+
| "The stage is obvious from the prompt — no tag needed" | Beads are the source of truth; prompt drift desynchronises the log and downstream dispatch. |
|
|
146
|
+
|
|
147
|
+
## See Also
|
|
145
148
|
|
|
146
|
-
|
|
149
|
+
- [create-task](../../planning/create-task/SKILL.md) — owns `pipeline.json`: stages, `maxFixRounds`, and the stage templates.
|
|
150
|
+
- [bd-tool](../../productivity/bd-tool/SKILL.md) — bead mechanics beyond the commands used here.
|
|
151
|
+
- [ralph](../ralph/SKILL.md) — runs many stage beads concurrently and consumes this skill's log and REPORT contracts.
|
|
147
152
|
|
|
148
|
-
|
|
149
|
-
- `bd-tool` covers bead mechanics beyond the commands in this skill.
|
|
150
|
-
- `ralph` runs many stage beads concurrently and consumes this skill's log and REPORT contracts.
|
|
151
|
-
- Playbooks delegate to stage skills: `tdd`/`style-code`/`style-tests` (code), `review-security` (review), `style-documentation` (document).
|
|
153
|
+
Each playbook delegates to a stage skill: `tdd`/`style-code`/`style-tests` (code, fix), `review-security` (review), `style-documentation` (document).
|
|
152
154
|
|
|
153
155
|
## Examples
|
|
154
156
|
|
|
155
|
-
- Input: a `stage:verify`
|
|
156
|
-
- Input: a `stage:document`
|
|
157
|
+
- Input: a `stage:verify` runner reporting `OUTCOME: BLOCKED` with two failing tests → Output: a `Fix (round 1)` chore plus a fresh `Verify (re-run 1)` chore blocked on it, `review` re-blocked behind the fresh verify, verify's own bead closed, and one `---REPORT---` block.
|
|
158
|
+
- Input: a `stage:document` runner reporting `OUTCOME: SUCCESS` → Output: document's bead already closed by the runner; the orchestrator opens the feature PR and updates the HITL gate bead.
|
|
157
159
|
|
|
158
160
|
## Philosophy / rationale
|
|
159
161
|
|
|
160
162
|
- Beads hold all orchestration state — there is no separate state file to drift out of sync.
|
|
161
|
-
-
|
|
162
|
-
-
|
|
163
|
+
- Whoever does the work owns the record: a worker that closes its own bead leaves an auditable trail the orchestrator can only read.
|
|
164
|
+
- Self-contained remediation keeps the dependency graph truthful, so `bd ready` alone is enough to schedule the next step.
|
|
163
165
|
|
|
164
166
|
## Verification checklist
|
|
165
167
|
|
|
166
168
|
- [ ] Bead claimed (`bd update <id> --claim`) before any executor works it.
|
|
167
|
-
- [ ] `bd tag <id> stage:<stage>` run
|
|
169
|
+
- [ ] `bd tag <id> stage:<stage>` run before dispatch.
|
|
168
170
|
- [ ] Correct playbook read and followed for the stage; stage skills invoked where the playbook says.
|
|
169
|
-
- [ ] Composed prompt
|
|
171
|
+
- [ ] Composed prompt carries the lifecycle, remediation, and REPORT contract from `prompts/stage-runner.md`.
|
|
170
172
|
- [ ] Progress log lines written for every stage transition and key result.
|
|
171
173
|
- [ ] Milestone notes appended at stage start and each milestone with `--append-notes` (not `--notes`).
|
|
172
|
-
- [ ]
|
|
173
|
-
- [ ] Feedback beads carry the full BLOCKING_ISSUES content and the parent-child dependency.
|
|
174
|
+
- [ ] The orchestrator closed no leaf bead and created no fix bead; the cap backstop ran before any dispatch.
|