@garygentry/feature-forge 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/.feature-forge-bundle.json +1 -1
- package/adapters/claude/hooks/hooks.json +15 -0
- package/adapters/claude/references/forge-config-schema.json +2 -2
- package/adapters/claude/references/preflight-and-self-heal.md +1 -1
- package/adapters/claude/references/ralph-loop-contract.md +5 -3
- package/adapters/claude/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/references/vendor-construct-inventory.md +1 -1
- package/adapters/claude/scripts/forge-session.py +6 -1
- package/adapters/claude/scripts/forge_session/cli.py +2 -1
- package/adapters/claude/scripts/forge_session/doctor.py +42 -2
- package/adapters/claude/scripts/forge_session/exit.py +5 -5
- package/adapters/claude/scripts/forge_session/routes.py +30 -3
- package/adapters/claude/scripts/session-check.sh +16 -0
- package/adapters/claude/skills/forge/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/skills/forge-0-epic/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/skills/forge-1-prd/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/skills/forge-2-tech/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/skills/forge-3-specs/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/skills/forge-4-backlog/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/skills/forge-5-loop/SKILL.md +12 -22
- package/adapters/claude/skills/forge-5-loop/references/preflight-and-self-heal.md +1 -1
- package/adapters/claude/skills/forge-5-loop/references/ralph-loop-contract.md +5 -3
- package/adapters/claude/skills/forge-5-loop/references/result-reporting.md +78 -16
- package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +111 -12
- package/adapters/claude/skills/forge-5-loop/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/skills/forge-6-docs/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/skills/forge-fix/references/stage-exit-protocol.md +1 -1
- package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/claude/skills/forge-guide/references/preflight-and-self-heal.md +1 -1
- package/adapters/claude/skills/forge-guide/references/ralph-loop-contract.md +5 -3
- package/adapters/claude/skills/forge-init/SKILL.md +7 -5
- package/adapters/claude/skills/forge-init/references/preflight-and-self-heal.md +1 -1
- package/adapters/claude/skills/forge-verify/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/.feature-forge-bundle.json +1 -1
- package/adapters/codex/references/forge-config-schema.json +2 -2
- package/adapters/codex/references/preflight-and-self-heal.md +1 -1
- package/adapters/codex/references/ralph-loop-contract.md +5 -3
- package/adapters/codex/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/references/vendor-construct-inventory.md +1 -1
- package/adapters/codex/scripts/forge-session.py +6 -1
- package/adapters/codex/scripts/forge_session/cli.py +2 -1
- package/adapters/codex/scripts/forge_session/doctor.py +42 -2
- package/adapters/codex/scripts/forge_session/exit.py +5 -5
- package/adapters/codex/scripts/forge_session/routes.py +30 -3
- package/adapters/codex/skills/forge/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/skills/forge-0-epic/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/skills/forge-1-prd/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/skills/forge-2-tech/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/skills/forge-3-specs/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/skills/forge-4-backlog/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/skills/forge-5-loop/SKILL.md +12 -22
- package/adapters/codex/skills/forge-5-loop/references/preflight-and-self-heal.md +1 -1
- package/adapters/codex/skills/forge-5-loop/references/ralph-loop-contract.md +5 -3
- package/adapters/codex/skills/forge-5-loop/references/result-reporting.md +78 -16
- package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +111 -12
- package/adapters/codex/skills/forge-5-loop/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/skills/forge-6-docs/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/skills/forge-fix/references/stage-exit-protocol.md +1 -1
- package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/codex/skills/forge-guide/references/preflight-and-self-heal.md +1 -1
- package/adapters/codex/skills/forge-guide/references/ralph-loop-contract.md +5 -3
- package/adapters/codex/skills/forge-init/SKILL.md +7 -5
- package/adapters/codex/skills/forge-init/references/preflight-and-self-heal.md +1 -1
- package/adapters/codex/skills/forge-verify/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/.feature-forge-bundle.json +1 -1
- package/adapters/copilot/references/forge-config-schema.json +2 -2
- package/adapters/copilot/references/preflight-and-self-heal.md +1 -1
- package/adapters/copilot/references/ralph-loop-contract.md +5 -3
- package/adapters/copilot/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/references/vendor-construct-inventory.md +1 -1
- package/adapters/copilot/scripts/forge-session.py +6 -1
- package/adapters/copilot/scripts/forge_session/cli.py +2 -1
- package/adapters/copilot/scripts/forge_session/doctor.py +42 -2
- package/adapters/copilot/scripts/forge_session/exit.py +5 -5
- package/adapters/copilot/scripts/forge_session/routes.py +30 -3
- package/adapters/copilot/skills/forge/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/skills/forge-0-epic/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/skills/forge-1-prd/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/skills/forge-2-tech/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/skills/forge-3-specs/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/skills/forge-4-backlog/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +12 -22
- package/adapters/copilot/skills/forge-5-loop/references/preflight-and-self-heal.md +1 -1
- package/adapters/copilot/skills/forge-5-loop/references/ralph-loop-contract.md +5 -3
- package/adapters/copilot/skills/forge-5-loop/references/result-reporting.md +78 -16
- package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +111 -12
- package/adapters/copilot/skills/forge-5-loop/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/skills/forge-6-docs/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/skills/forge-fix/references/stage-exit-protocol.md +1 -1
- package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/copilot/skills/forge-guide/references/preflight-and-self-heal.md +1 -1
- package/adapters/copilot/skills/forge-guide/references/ralph-loop-contract.md +5 -3
- package/adapters/copilot/skills/forge-init/forge-init.md +7 -5
- package/adapters/copilot/skills/forge-init/references/preflight-and-self-heal.md +1 -1
- package/adapters/copilot/skills/forge-verify/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/.feature-forge-bundle.json +1 -1
- package/adapters/cursor/references/forge-config-schema.json +2 -2
- package/adapters/cursor/references/preflight-and-self-heal.md +1 -1
- package/adapters/cursor/references/ralph-loop-contract.md +5 -3
- package/adapters/cursor/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/references/vendor-construct-inventory.md +1 -1
- package/adapters/cursor/scripts/forge-session.py +6 -1
- package/adapters/cursor/scripts/forge_session/cli.py +2 -1
- package/adapters/cursor/scripts/forge_session/doctor.py +42 -2
- package/adapters/cursor/scripts/forge_session/exit.py +5 -5
- package/adapters/cursor/scripts/forge_session/routes.py +30 -3
- package/adapters/cursor/skills/forge/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/skills/forge-0-epic/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/skills/forge-1-prd/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/skills/forge-2-tech/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/skills/forge-3-specs/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/skills/forge-4-backlog/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +12 -22
- package/adapters/cursor/skills/forge-5-loop/references/preflight-and-self-heal.md +1 -1
- package/adapters/cursor/skills/forge-5-loop/references/ralph-loop-contract.md +5 -3
- package/adapters/cursor/skills/forge-5-loop/references/result-reporting.md +78 -16
- package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +111 -12
- package/adapters/cursor/skills/forge-5-loop/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/skills/forge-6-docs/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/skills/forge-fix/references/stage-exit-protocol.md +1 -1
- package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/cursor/skills/forge-guide/references/preflight-and-self-heal.md +1 -1
- package/adapters/cursor/skills/forge-guide/references/ralph-loop-contract.md +5 -3
- package/adapters/cursor/skills/forge-init/forge-init.mdc +7 -5
- package/adapters/cursor/skills/forge-init/references/preflight-and-self-heal.md +1 -1
- package/adapters/cursor/skills/forge-verify/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/.feature-forge-bundle.json +1 -1
- package/adapters/gemini/gemini-extension.json +1 -1
- package/adapters/gemini/references/forge-config-schema.json +2 -2
- package/adapters/gemini/references/preflight-and-self-heal.md +1 -1
- package/adapters/gemini/references/ralph-loop-contract.md +5 -3
- package/adapters/gemini/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/references/vendor-construct-inventory.md +1 -1
- package/adapters/gemini/scripts/forge-session.py +6 -1
- package/adapters/gemini/scripts/forge_session/cli.py +2 -1
- package/adapters/gemini/scripts/forge_session/doctor.py +42 -2
- package/adapters/gemini/scripts/forge_session/exit.py +5 -5
- package/adapters/gemini/scripts/forge_session/routes.py +30 -3
- package/adapters/gemini/skills/forge/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/skills/forge-0-epic/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/skills/forge-1-prd/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/skills/forge-2-tech/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/skills/forge-3-specs/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/skills/forge-4-backlog/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +12 -22
- package/adapters/gemini/skills/forge-5-loop/references/preflight-and-self-heal.md +1 -1
- package/adapters/gemini/skills/forge-5-loop/references/ralph-loop-contract.md +5 -3
- package/adapters/gemini/skills/forge-5-loop/references/result-reporting.md +78 -16
- package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +111 -12
- package/adapters/gemini/skills/forge-5-loop/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/skills/forge-6-docs/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/skills/forge-fix/references/stage-exit-protocol.md +1 -1
- package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/gemini/skills/forge-guide/references/preflight-and-self-heal.md +1 -1
- package/adapters/gemini/skills/forge-guide/references/ralph-loop-contract.md +5 -3
- package/adapters/gemini/skills/forge-init/forge-init.md +7 -5
- package/adapters/gemini/skills/forge-init/references/preflight-and-self-heal.md +1 -1
- package/adapters/gemini/skills/forge-verify/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/.feature-forge-bundle.json +1 -1
- package/adapters/pi/references/forge-config-schema.json +2 -2
- package/adapters/pi/references/preflight-and-self-heal.md +1 -1
- package/adapters/pi/references/ralph-loop-contract.md +5 -3
- package/adapters/pi/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/references/vendor-construct-inventory.md +1 -1
- package/adapters/pi/scripts/forge-session.py +6 -1
- package/adapters/pi/scripts/forge_session/cli.py +2 -1
- package/adapters/pi/scripts/forge_session/doctor.py +42 -2
- package/adapters/pi/scripts/forge_session/exit.py +5 -5
- package/adapters/pi/scripts/forge_session/routes.py +30 -3
- package/adapters/pi/skills/forge/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/skills/forge-0-epic/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/skills/forge-1-prd/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/skills/forge-2-tech/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/skills/forge-3-specs/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/skills/forge-4-backlog/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/skills/forge-5-loop/SKILL.md +12 -22
- package/adapters/pi/skills/forge-5-loop/references/preflight-and-self-heal.md +1 -1
- package/adapters/pi/skills/forge-5-loop/references/ralph-loop-contract.md +5 -3
- package/adapters/pi/skills/forge-5-loop/references/result-reporting.md +78 -16
- package/adapters/pi/skills/forge-5-loop/references/runner-contract.md +111 -12
- package/adapters/pi/skills/forge-5-loop/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/skills/forge-6-docs/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/skills/forge-fix/references/stage-exit-protocol.md +1 -1
- package/adapters/pi/skills/forge-guide/references/forge-config-schema.json +2 -2
- package/adapters/pi/skills/forge-guide/references/preflight-and-self-heal.md +1 -1
- package/adapters/pi/skills/forge-guide/references/ralph-loop-contract.md +5 -3
- package/adapters/pi/skills/forge-init/SKILL.md +7 -5
- package/adapters/pi/skills/forge-init/references/preflight-and-self-heal.md +1 -1
- package/adapters/pi/skills/forge-verify/references/stage-exit-protocol.md +1 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/rauf.d.ts +3 -3
- package/dist/rauf.js +2 -2
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
|
@@ -34,6 +34,16 @@ Runner review pass: {itemsCreated} fix item(s) created and implemented.
|
|
|
34
34
|
Omit this line when no `review_completed` event was emitted (no review flag passed).
|
|
35
35
|
The created items are already counted in the totals above.
|
|
36
36
|
|
|
37
|
+
**Review pending** (`reviewPending: true` — the review pass failed or was interrupted,
|
|
38
|
+
and the user chose **Stop here** at the Pending review offer in
|
|
39
|
+
`references/runner-contract.md`). Never render the all-done report for this run, even
|
|
40
|
+
when every item is `done`:
|
|
41
|
+
```
|
|
42
|
+
Loop finished the backlog for {feature}, but its review pass did not complete.
|
|
43
|
+
Completed: {done}/{total}
|
|
44
|
+
Review pending: {reviewItemIds count} item(s) ({reason from the last `review_failed` event, if any})
|
|
45
|
+
```
|
|
46
|
+
|
|
37
47
|
**Some items need a human:**
|
|
38
48
|
```
|
|
39
49
|
Loop completed for {feature}.
|
|
@@ -71,8 +81,11 @@ Loop completed for {feature}.
|
|
|
71
81
|
Pending: {pending} items ({cause})
|
|
72
82
|
Blocked: {blocked} items
|
|
73
83
|
```
|
|
74
|
-
Render `{cause}`
|
|
75
|
-
|
|
84
|
+
Render `{cause}` from the runner's terminal state first (see **Runner terminal states**
|
|
85
|
+
below): "stopped on request", "the run crashed (stale lock)", or "usage limit — resume
|
|
86
|
+
after {sleepUntil}". Otherwise render it as "iteration limit reached" **only** when
|
|
87
|
+
`loopState` is `ITERATIONS_COMPLETE` (the runner's own attestation, when reported) or
|
|
88
|
+
`iteration == maxIterations`, AND `selectable > 0` — cite the `iteration`/`maxIterations` counters from
|
|
76
89
|
`{loopRunner.stateDir}/state.json` and `selectable` from `backlog-topology --items-stdin
|
|
77
90
|
--json` run over the same authoritative item JSON as the counts above. Otherwise —
|
|
78
91
|
`selectable == 0` with items still pending while `iteration < maxIterations` — the
|
|
@@ -93,6 +106,37 @@ the `backlog-topology` output (`selectable`, `blockingRoots`, `gatedCount`,
|
|
|
93
106
|
`itemCount`). A cause any of those counters contradicts — e.g. "iteration limit
|
|
94
107
|
reached" while `iteration < maxIterations` — is a reportable defect.
|
|
95
108
|
|
|
109
|
+
## Runner terminal states (Step 4a)
|
|
110
|
+
|
|
111
|
+
`status --json` may also carry `loopState`, `lock`, `sleepUntil`, `reviewPending` and
|
|
112
|
+
`reviewItemIds`. All are optional: when a field is absent, skip its row and read the
|
|
113
|
+
counts as before. These mirror the rows of rauf's supervisor decision table
|
|
114
|
+
(`drive-rauf-loop`) that forge-5-loop decides the same way.
|
|
115
|
+
|
|
116
|
+
**Clean runner finish.** When `loopState` is reported, only `COMPLETE` or `IDLE` is a
|
|
117
|
+
clean terminal success. Every other value — `ERROR`, `PAUSED` (on request or with a
|
|
118
|
+
stale lock), `ITERATIONS_COMPLETE`, `PAUSED_USAGE_LIMIT`, `WEEKLY_LIMIT`,
|
|
119
|
+
`SLEEPING_LIMIT`, `PAUSED_HUMAN`, a still-live `RUNNING`/`REVIEWING`, or anything
|
|
120
|
+
unrecognized — means the runner did **not** finish cleanly, and the run is **never**
|
|
121
|
+
`complete`, even when `done == total > 0` (e.g. a process killed after its last commit
|
|
122
|
+
but before writing its final state reads `PAUSED` + stale lock with every item done).
|
|
123
|
+
The ladder's rung 2 (runner not finished) catches it, above the needs-human / blocked / deferred rungs. When `loopState` is absent (an older or
|
|
124
|
+
non-rauf runner), this gate does not apply and the counts decide, as before.
|
|
125
|
+
|
|
126
|
+
| Runner state | What it means here |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `backlogSummary.total == 0` (any `loopState`) | Empty **or unreadable** backlog — a read failure reports all-zero counts. Never `complete`: run the **validate command** and treat it as an **operational failure** (below). |
|
|
129
|
+
| `reviewPending: true` | Not complete, whatever the counts: offer the resume (**Pending review**, `references/runner-contract.md`). If the user stops here, ladder rung 2 closes `partial --cause review-pending` — even with blocked, needs-human or deferred items, which are still reported alongside. |
|
|
130
|
+
| `ITERATIONS_COMPLETE` | Iteration budget spent with eligible work left — the "iteration limit reached" `partial` cause. The next loop run gets a fresh budget. |
|
|
131
|
+
| `PAUSED`, `lock.stale` not true | Stopped on request (Ctrl-C, `SIGTERM`, a stop command). Report "stopped on request"; do not offer to relaunch unless the user asks. |
|
|
132
|
+
| `PAUSED`, `lock.stale: true` | The run died mid-iteration and left its lock. rauf's `{bin} resume . --backlog {backlogDir}` clears the stale lock and continues. Never reach for `--force` or `reset` first. |
|
|
133
|
+
| `PAUSED_USAGE_LIMIT` / `WEEKLY_LIMIT` | Halted on a usage limit: "resume after {sleepUntil}". Not a failure. |
|
|
134
|
+
| `ERROR` | Crash or circuit-breaker halt (`loop_error`). Surface the error alongside the count reports; never `complete`. With work left, the next loop run re-runs it; with none, re-entry offers rauf's resume (**Unfinished runner**, `references/runner-contract.md`). |
|
|
135
|
+
| `COMPLETE` with `done < total` | No eligible work left, but items are unfinished (blocked, needs-human, deferred, or pending behind a blocked dependency). The ladder's non-complete rungs apply; never reset the backlog. |
|
|
136
|
+
|
|
137
|
+
**Exit codes are not the outcome.** rauf exits 1 both for a crash and for a failed review
|
|
138
|
+
pass, and 0 for a requested stop or a spent budget. Read the fields above, never `$?`.
|
|
139
|
+
|
|
96
140
|
## Selecting the one `LoopOutcome` (Step 7)
|
|
97
141
|
|
|
98
142
|
After Step 5's `state-complete`, select exactly **one** `LoopOutcome` from Step 4a's
|
|
@@ -106,26 +150,43 @@ authoritative final counts. Walk this ladder in order and stop at the first matc
|
|
|
106
150
|
stop the recovery just cleared is not re-reported as still needing a human. (Step
|
|
107
151
|
4c runs the procedure on every close, so an empty affected set is the common case —
|
|
108
152
|
it never selects `resolved`; fall through.)
|
|
109
|
-
2. **`
|
|
153
|
+
2. **`partial` (runner not finished)** — otherwise, `reviewPending` is true, **or**
|
|
154
|
+
`loopState` is reported and is not a clean finish (**Clean runner finish**, above).
|
|
155
|
+
Pass `--cause review-pending` when `reviewPending` is true. Otherwise pass
|
|
156
|
+
`--cause runner-stopped`, except for `ITERATIONS_COMPLETE`, which is the plain
|
|
157
|
+
iteration-limit `partial` (no `--cause`). This rung fires **whatever the counts
|
|
158
|
+
say** — including `done == total`, and **above** the needs-human / blocked / deferred
|
|
159
|
+
rungs, following rauf's supervisor table: a crashed, stopped, limit-halted or
|
|
160
|
+
budget-spent runner, or an unfinished review, is recovered or resumed first. It does
|
|
161
|
+
not hide the backlog: the Step 4b needs-human / blocked / deferred reports still
|
|
162
|
+
render alongside it, so every set-aside item is surfaced in the same close (e.g.
|
|
163
|
+
**Stop here** on a pending review with blocked items closes `partial --cause
|
|
164
|
+
review-pending` and still lists the blocked items).
|
|
165
|
+
3. **`needs-human`** — otherwise, `needsHuman > 0`. This wins even when blocked
|
|
110
166
|
items also exist: a decision only a human can make outranks work that merely
|
|
111
167
|
could not proceed.
|
|
112
|
-
|
|
113
|
-
|
|
168
|
+
4. **`blocked`** — otherwise, genuine `blocked > 0`.
|
|
169
|
+
5. **`deferred`** — otherwise, runner-deferred items exist (the "false blocks" the
|
|
114
170
|
runner gave up on after retries).
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
171
|
+
6. **`partial`** — otherwise, `pending`/`in_progress` items remain (the report above
|
|
172
|
+
names the cause). Pass `--cause dependency-starvation` only on the starvation report.
|
|
173
|
+
7. **`complete`** — otherwise, and **only** when `total > 0`, every item is `done`, no
|
|
174
|
+
review is pending, and the runner finished cleanly (or reports no `loopState`).
|
|
118
175
|
|
|
119
176
|
This is a priority order, not a set. A run reporting both a needs-human and a blocked
|
|
120
|
-
count renders both reports above and still exits `needs-human
|
|
177
|
+
count renders both reports above and still exits `needs-human`; a run whose runner did
|
|
178
|
+
not finish renders every applicable count report and still exits rung 2's `partial`.
|
|
121
179
|
|
|
122
180
|
**The runner's process exit code is not the outcome.** A loop runner that exits 0 has
|
|
123
181
|
reported only that its process finished; the final backlog state decides. A clean
|
|
124
|
-
exit 0 that still leaves pending items is `partial`, never `complete
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
182
|
+
exit 0 that still leaves pending items is `partial`, never `complete`; an exit 1 from a
|
|
183
|
+
failed review with every item `done` is `partial` (`--cause review-pending`), not an
|
|
184
|
+
error; and a crash or stale-lock stop with every item `done` is `partial` (`--cause
|
|
185
|
+
runner-stopped`). `complete` is legitimate only when the counts show every item `done`,
|
|
186
|
+
no review is pending, and the runner finished cleanly.
|
|
187
|
+
|
|
188
|
+
**Retrying the non-complete outcomes.** `partial` (every cause), `deferred`, and
|
|
189
|
+
`resolved` fence the loop resume; `blocked` and `needs-human` fence the navigator. Whichever you land on, the
|
|
129
190
|
runner's own retry flags still apply to the next run — e.g. rauf's `--retry-blocked`
|
|
130
191
|
picks the set-aside blocked and deferred items back up at Step 2d. Mention that as
|
|
131
192
|
plain prose in the report if it helps; never as a second command block.
|
|
@@ -133,8 +194,9 @@ plain prose in the report if it helps; never as a second command block.
|
|
|
133
194
|
## Operational failure before the counts are known
|
|
134
195
|
|
|
135
196
|
If the run cannot produce authoritative counts at all — the status/list command fails,
|
|
136
|
-
its output does not parse, the state directory is gone,
|
|
137
|
-
|
|
197
|
+
its output does not parse, the state directory is gone, the summary reports
|
|
198
|
+
`total == 0` (an empty or unreadable backlog — show the validate command's output), or
|
|
199
|
+
the process died in a way that leaves the backlog unreadable — **do not pick an outcome and do not close the
|
|
138
200
|
stage.** There is nothing to select from, and guessing one would record a pipeline
|
|
139
201
|
position that never happened.
|
|
140
202
|
|
|
@@ -152,12 +152,18 @@ maximum `timeout_ms` (1 hour), and a bounded timeout would silently stop watchin
|
|
|
152
152
|
still-running loop.
|
|
153
153
|
|
|
154
154
|
**Coverage-complete filter (silence is not success).** The filter MUST match every
|
|
155
|
-
terminal and
|
|
156
|
-
looks identical to "still running."
|
|
155
|
+
terminal, exception, and pause/limit event, not just the happy path — otherwise a crash,
|
|
156
|
+
hang, or hours-long usage sleep looks identical to "still running." The list below covers
|
|
157
|
+
every such type in rauf's event schema (terminal: `loop_completed` / `loop_error` /
|
|
158
|
+
`loop_cancelled` / `loop_paused`; exception: `item_blocked` / `needs_human` /
|
|
159
|
+
`review_failed` / `llm_stuck_warning`; pause/limit: `usage_limit_hit` /
|
|
160
|
+
`usage_limit_cleared` / `sleep_start` / `sleep_end`); only per-iteration narration
|
|
161
|
+
(`iteration_start`, `llm_*` activity, `item_selected`, …) is left out. Monitor command
|
|
162
|
+
(NDJSON path):
|
|
157
163
|
|
|
158
164
|
```
|
|
159
165
|
tail -n +1 -F {backlogDir}/{loopRunner.stateDir}/events.ndjson 2>/dev/null \
|
|
160
|
-
| jq -rc --unbuffered 'select(.type | test("item_completed|item_blocked|needs_human|signal_parsed|loop_completed|loop_error|loop_cancelled|llm_stuck_warning"))'
|
|
166
|
+
| jq -rc --unbuffered 'select(.type | test("item_completed|item_blocked|needs_human|signal_parsed|loop_completed|loop_error|loop_cancelled|loop_paused|review_failed|llm_stuck_warning|usage_limit_hit|usage_limit_cleared|sleep_start|sleep_end"))'
|
|
161
167
|
```
|
|
162
168
|
|
|
163
169
|
> **Use `tail -F` (follow by name), not `-f` (follow by descriptor).** The runner
|
|
@@ -174,15 +180,20 @@ tail -n +1 -F {backlogDir}/{loopRunner.stateDir}/events.ndjson 2>/dev/null \
|
|
|
174
180
|
|
|
175
181
|
```
|
|
176
182
|
tail -n +1 -F {backlogDir}/{loopRunner.stateDir}/{loopRunner.logFile} 2>/dev/null \
|
|
177
|
-
| grep -E --line-buffered 'Item [^ ]+ (completed|blocked):|Item [^ ]+ needs human input|Loop completed|Loop error:|Circuit breaker
|
|
183
|
+
| grep -E --line-buffered 'Item [^ ]+ (completed|blocked):|Item [^ ]+ needs human input|Loop completed|Loop error:|Circuit breaker:|Loop cancelled|Review pass (cancelled|stopped)|Review returned unexpected signal|for review:|[Uu]sage limit'
|
|
178
184
|
```
|
|
179
185
|
|
|
180
186
|
(Match `needs human input` **without** a trailing colon — the runner writes
|
|
181
|
-
`needs human input (set aside):`.
|
|
187
|
+
`needs human input (set aside):`. `Loop cancelled` also matches *"Loop cancelled during
|
|
188
|
+
review pass (review pending)"* and the between-iteration / mid-sleep cancels;
|
|
189
|
+
`[Uu]sage limit` matches the 5-hour sleep, weekly limit, detection and wake lines.)
|
|
182
190
|
|
|
183
191
|
If the Monitor is ever auto-stopped for event volume, re-arm with a tighter filter
|
|
184
192
|
(drop `item_completed`, keep the exception/terminal events).
|
|
185
193
|
|
|
194
|
+
An older runner that never emits a listed type (e.g. `review_failed`) simply never
|
|
195
|
+
matches it — the filter needs no version gate.
|
|
196
|
+
|
|
186
197
|
## React to events as they land (Step 3e)
|
|
187
198
|
|
|
188
199
|
Each Monitor event arrives as a message. React per type — but keep the user signal
|
|
@@ -210,13 +221,101 @@ high and the noise low:
|
|
|
210
221
|
- **`loop_error`** → a real failure (this is also what a circuit-breaker halt — too many
|
|
211
222
|
consecutive infra failures — emits). Surface now and `PushNotification`. Offer
|
|
212
223
|
inspection / `--force` / re-run as appropriate.
|
|
213
|
-
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
`
|
|
219
|
-
|
|
224
|
+
- **`review_failed`** → the post-loop review pass failed or was stopped by a usage limit
|
|
225
|
+
(payload `reason`). Surface it now with a `PushNotification`. The run is **not**
|
|
226
|
+
complete: the review stays pending (`status --json` → `reviewPending: true`). A failed
|
|
227
|
+
review makes `loop run --review` exit **1**; a usage-limit stop is a resumable limit
|
|
228
|
+
stop, not that exit-1 case. A review **cancelled** mid-pass emits **no**
|
|
229
|
+
`review_failed` — only `loop_cancelled` (human log: *"Loop cancelled during review pass
|
|
230
|
+
(review pending)"*) with `reviewPending` still set — so never rely on this event alone.
|
|
231
|
+
Nothing to do live — Step 4a reads `reviewPending` and offers the resume (**Pending
|
|
232
|
+
review**, below).
|
|
233
|
+
- **Stall detection** → rauf emits an **`llm_stuck_warning`** event when an iteration's
|
|
234
|
+
output stream goes silent. Surface it live as a stall warning, not yet a failure. Read
|
|
235
|
+
its optional `currentTool` / `toolRunningMs` (absent on older runners — then report
|
|
236
|
+
the plain `silentMs` and treat it as a possible hang):
|
|
237
|
+
- `currentTool: null` → **the model itself went silent** with no tool in flight — a
|
|
238
|
+
likely hang. Offer `--force` / re-run if it persists.
|
|
239
|
+
- `currentTool` set → **a quiet tool call outlived the tool ceiling** — report it as
|
|
240
|
+
e.g. *"Bash running 31m"* (`toolRunningMs` rounded to minutes). This is usually a
|
|
241
|
+
slow verification gate, not a hung LLM: say so, and let it run unless it is clearly
|
|
242
|
+
wedged.
|
|
243
|
+
|
|
244
|
+
The thresholds are the runner's `.rauf.json` `options.stuckThresholdMs` (silence
|
|
245
|
+
before the warning, default 5 min) and `options.toolStuckThresholdMs` (how long a
|
|
246
|
+
quiet in-flight tool holds the warning off, default 30 min, measured from the tool's
|
|
247
|
+
start). A repo whose verification gate legitimately runs longer than 30 min should
|
|
248
|
+
raise `toolStuckThresholdMs` rather than learn to ignore the warning. If you instead
|
|
249
|
+
want to probe on quiet, run `{rendered watchCommand}` and key off `health.stuckWarning`.
|
|
250
|
+
Do **not** infer a stall from `state.json.updatedAt` alone — it is not a liveness
|
|
251
|
+
proof.
|
|
252
|
+
- **Usage-limit waits are not stalls.** `usage_limit_hit` (`limitType`, optional
|
|
253
|
+
`reason: "usage_api_disagreement"`) followed by `sleep_start` (`sleepUntil`, `reason`)
|
|
254
|
+
means the runner is sleeping until the limit resets (`SLEEPING_LIMIT`) and will resume
|
|
255
|
+
on its own: surface it once with its `sleepUntil`, and send a `PushNotification` when
|
|
256
|
+
the sleep is long (a 5-hour window). A `sleep_start` whose `reason` begins *"Usage-limit
|
|
257
|
+
banner unconfirmed"* is a 30 s / 60 s backoff (`status --json` still `RUNNING` with
|
|
258
|
+
`sleepUntil`) — narrate it at most briefly. `sleep_end` / `usage_limit_cleared` → the
|
|
259
|
+
loop is working again. A weekly or no-sleep limit halts the run instead (the process
|
|
260
|
+
exits; Step 4a reads `WEEKLY_LIMIT` / `PAUSED_USAGE_LIMIT`). None of these is a stall:
|
|
261
|
+
never offer `--force`.
|
|
262
|
+
- **`loop_paused`** (`reason: "needs_human"`, `itemId`) → the run halted on a needs-human
|
|
263
|
+
item (only under rauf's opt-in `--pause-on-needs-human`; forge does not pass it by
|
|
264
|
+
default). Unlike the default set-aside mode, the loop **is** now stopped: surface it
|
|
265
|
+
with a `PushNotification`; the process exits and Step 4c's recovery pass handles the
|
|
266
|
+
answer.
|
|
267
|
+
|
|
268
|
+
**Pending review (Steps 2a / 4a, rauf).** A rauf `--review` run whose review pass failed, was cancelled, or was stopped by a usage
|
|
269
|
+
limit leaves `status --json` with `reviewPending: true` and `reviewItemIds` (the review's
|
|
270
|
+
exact scope). The loop state can read `COMPLETE`/`IDLE` with every item `done`, or
|
|
271
|
+
`PAUSED`/`PAUSED_USAGE_LIMIT` — either way the run is **not complete**, and the process
|
|
272
|
+
exit code (1 for a failed review) does not decide it. Both fields are optional: a runner
|
|
273
|
+
that never reports them never takes this path.
|
|
274
|
+
|
|
275
|
+
rauf's `resume` re-runs exactly that review (`rauf loop review --items <reviewItemIds>`),
|
|
276
|
+
not a fresh loop. So when Step 4a or Step 2a sees `reviewPending: true`:
|
|
277
|
+
|
|
278
|
+
1. Report it: *"The review pass for {feature} did not finish ({reason from the last
|
|
279
|
+
`review_failed` event, if any}); it covers {reviewItemIds}."*
|
|
280
|
+
2. Via host's question mechanism, offer **Resume the review now (recommended)** · **Stop here**. On a
|
|
281
|
+
usage-limit stop (`PAUSED_USAGE_LIMIT` / `WEEKLY_LIMIT`), say the resume only helps
|
|
282
|
+
once the limit resets (`sleepUntil`). A stop the user requested (`PAUSED`, lock
|
|
283
|
+
released) is theirs: offer, never auto-resume.
|
|
284
|
+
3. **Resume:** launch `{bin} resume . --backlog {backlogDir}` backgrounded, exactly as a
|
|
285
|
+
run command (Step 3b launch guards, 3d Monitor, 3f completion), then return to Step 4a.
|
|
286
|
+
A successful review may file fix items (`review_completed.itemsCreated`); those are
|
|
287
|
+
ordinary pending work for the next loop run. rauf reviews **before** relaunching: when
|
|
288
|
+
pending items also remain (the review ran after the iteration budget ran out, or
|
|
289
|
+
blocked items strand their dependents), `resume` re-runs only the review and the
|
|
290
|
+
remaining items wait for the next loop run — say so, never launch a fresh `loop run`
|
|
291
|
+
first (it overwrites the state and drops the pending review).
|
|
292
|
+
4. **Stop here:** Step 7 closes `partial` with `--cause review-pending` (see
|
|
293
|
+
`references/result-reporting.md`) — a resume route whose Step 2a re-offers this.
|
|
294
|
+
|
|
295
|
+
At rung 3 (no question mechanism), do not launch: print the rendered resume command and
|
|
296
|
+
close as in 4.
|
|
297
|
+
|
|
298
|
+
**Unfinished runner (Step 2a re-entry, rauf).** When Step 2a finds no pending or
|
|
299
|
+
in_progress items and no pending review, but `status --json` reports a `loopState` that
|
|
300
|
+
is not a clean finish (**Clean runner finish** in `references/result-reporting.md` —
|
|
301
|
+
e.g. `ERROR`, `PAUSED` with `lock.stale`, `ITERATIONS_COMPLETE`, a usage halt), the
|
|
302
|
+
prior run closed as `partial` with `--cause runner-stopped` (or the plain iteration-limit
|
|
303
|
+
`partial`), and "Nothing to run" would strand it. Instead:
|
|
304
|
+
|
|
305
|
+
1. Report the state: *"The last run for {feature} did not finish cleanly ({loopState}
|
|
306
|
+
{— stale lock | — resets {sleepUntil}})."*
|
|
307
|
+
2. Via host's question mechanism, offer **Resume the runner (recommended)** · **Stop here**. For a
|
|
308
|
+
usage halt, the resume helps only once the limit resets; for `PAUSED` on request
|
|
309
|
+
(lock released), the stop was the user's — offer, never auto-resume.
|
|
310
|
+
3. **Resume:** launch `{bin} resume . --backlog {backlogDir}` backgrounded exactly as in
|
|
311
|
+
**Pending review** step 3 (it clears a stale lock and finishes the run's bookkeeping,
|
|
312
|
+
including any post-loop review), then continue at Step 4a. rauf's recovery for
|
|
313
|
+
`ERROR` is `resume` or `reset` + re-run — never reach for `reset` or `--force` first.
|
|
314
|
+
4. **Stop here**, or rung 3 (print the rendered command, do not launch): STOP without
|
|
315
|
+
touching the stage — it is already recorded `in-progress`.
|
|
316
|
+
|
|
317
|
+
When `loopState` is absent, this never fires: an older or non-rauf runner keeps the
|
|
318
|
+
plain "Nothing to run" stop.
|
|
220
319
|
|
|
221
320
|
## Inform-user output template (Step 3c)
|
|
222
321
|
|
|
@@ -47,7 +47,7 @@ an epic member. Only the flags below are stage-specific; pass no others.
|
|
|
47
47
|
|---|---|---|
|
|
48
48
|
| `forge-0-epic` | `forge-0-epic` | `--next-feature "{member}"` when a concrete member exists |
|
|
49
49
|
| `forge-1-prd` … `forge-4-backlog` | that stage's own id | none beyond identity/capability |
|
|
50
|
-
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation` with `--outcome partial` |
|
|
50
|
+
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation`, `review-pending` or `runner-stopped` with `--outcome partial` |
|
|
51
51
|
| `forge-6-docs` | `forge-6-docs` | `--outcome` — `complete`, `blocked`, or `skipped` (deliberate docs skip, persisted via `state-skip` before the exit; routes like `complete` with honest wording) |
|
|
52
52
|
| direct `forge-verify` | `forge-verify` | `--owner direct`, `--outcome` (`passed`, `findings`, `skipped`, `failed`), and served-stage metadata |
|
|
53
53
|
| nested `forge-verify` | `forge-verify` | `--owner nested`, plus the same outcome and served-stage metadata |
|
|
@@ -47,7 +47,7 @@ an epic member. Only the flags below are stage-specific; pass no others.
|
|
|
47
47
|
|---|---|---|
|
|
48
48
|
| `forge-0-epic` | `forge-0-epic` | `--next-feature "{member}"` when a concrete member exists |
|
|
49
49
|
| `forge-1-prd` … `forge-4-backlog` | that stage's own id | none beyond identity/capability |
|
|
50
|
-
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation` with `--outcome partial` |
|
|
50
|
+
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation`, `review-pending` or `runner-stopped` with `--outcome partial` |
|
|
51
51
|
| `forge-6-docs` | `forge-6-docs` | `--outcome` — `complete`, `blocked`, or `skipped` (deliberate docs skip, persisted via `state-skip` before the exit; routes like `complete` with honest wording) |
|
|
52
52
|
| direct `forge-verify` | `forge-verify` | `--owner direct`, `--outcome` (`passed`, `findings`, `skipped`, `failed`), and served-stage metadata |
|
|
53
53
|
| nested `forge-verify` | `forge-verify` | `--owner nested`, plus the same outcome and served-stage metadata |
|
|
@@ -47,7 +47,7 @@ an epic member. Only the flags below are stage-specific; pass no others.
|
|
|
47
47
|
|---|---|---|
|
|
48
48
|
| `forge-0-epic` | `forge-0-epic` | `--next-feature "{member}"` when a concrete member exists |
|
|
49
49
|
| `forge-1-prd` … `forge-4-backlog` | that stage's own id | none beyond identity/capability |
|
|
50
|
-
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation` with `--outcome partial` |
|
|
50
|
+
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation`, `review-pending` or `runner-stopped` with `--outcome partial` |
|
|
51
51
|
| `forge-6-docs` | `forge-6-docs` | `--outcome` — `complete`, `blocked`, or `skipped` (deliberate docs skip, persisted via `state-skip` before the exit; routes like `complete` with honest wording) |
|
|
52
52
|
| direct `forge-verify` | `forge-verify` | `--owner direct`, `--outcome` (`passed`, `findings`, `skipped`, `failed`), and served-stage metadata |
|
|
53
53
|
| nested `forge-verify` | `forge-verify` | `--owner nested`, plus the same outcome and served-stage metadata |
|
|
@@ -235,8 +235,8 @@
|
|
|
235
235
|
},
|
|
236
236
|
"installHint": {
|
|
237
237
|
"type": "string",
|
|
238
|
-
"default": "Provision rauf for a multi-agent setup with the cross-agent installer: `npx @garygentry/feature-forge install` (records the pinned @garygentry/rauf@0.
|
|
239
|
-
"description": "Shown when the runner BINARY is missing or too old (version gate fails, minRunnerVersion floor) — how to obtain/upgrade the CLI itself. Names two distinct binary-provisioning paths: (1) the cross-agent installer (`npx @garygentry/feature-forge install`, the multi-agent provisioning path that pins @garygentry/rauf@0.
|
|
238
|
+
"default": "Provision rauf for a multi-agent setup with the cross-agent installer: `npx @garygentry/feature-forge install` (records the pinned @garygentry/rauf@0.17.1 default). Or install/upgrade just the rauf CLI: `npx @garygentry/rauf@0.17.1 --version`, or `curl -fsSL https://raw.githubusercontent.com/garygentry/rauf/main/scripts/install-binary.sh | bash`.",
|
|
239
|
+
"description": "Shown when the runner BINARY is missing or too old (version gate fails, minRunnerVersion floor) — how to obtain/upgrade the CLI itself. Names two distinct binary-provisioning paths: (1) the cross-agent installer (`npx @garygentry/feature-forge install`, the multi-agent provisioning path that pins @garygentry/rauf@0.17.1), and (2) the direct rauf-CLI install/upgrade one-liner. Distinct from setupHint (which installs per-project artifacts); a version-gate failure is ALWAYS this hint, never setupHint."
|
|
240
240
|
},
|
|
241
241
|
"schemaVersion": {
|
|
242
242
|
"type": "string",
|
|
@@ -6,7 +6,7 @@ silent unasked mutation. It runs whenever a skill gates on `doctor`'s structured
|
|
|
6
6
|
`checks[]` (`roadmap/self-healing-resilience.md` §5.2); today that is `forge-5-loop`'s
|
|
7
7
|
gates 1c/1d (`skills/forge-5-loop/SKILL.md`), which resolve the loop runner **before**
|
|
8
8
|
touching it, `forge-init`'s install preflight (`skills/forge-init/SKILL.md`), where
|
|
9
|
-
|
|
9
|
+
no check is a stop, and `forge-guide --doctor` (`skills/forge-guide/SKILL.md`), the
|
|
10
10
|
operator-facing repair surface, which gates nothing at all. All three are callers, not
|
|
11
11
|
the procedure's scope: any skill that gates on `checks[]` follows it in full. Its seven
|
|
12
12
|
ordered steps: **enumerate → cluster → consolidated prompts → record → apply → prove →
|
|
@@ -40,9 +40,11 @@ defined authoritatively in rauf's
|
|
|
40
40
|
- **A machine-readable event stream** for live supervision (`loopRunner.eventStreamCommand`,
|
|
41
41
|
rauf: `loop run … --ndjson`): one JSON event per line with a stable `type`
|
|
42
42
|
vocabulary — `item_completed` / `item_blocked` / `needs_human` / `signal_parsed`
|
|
43
|
-
/ `loop_completed` / `loop_error` / `loop_cancelled` / `
|
|
44
|
-
circuit-breaker halt surfaces as `loop_error`) — plus a
|
|
45
|
-
derived-status JSON (`loopRunner.statusJsonCommand`, rauf: `status … --json
|
|
43
|
+
/ `loop_completed` / `loop_error` / `loop_cancelled` / `review_failed` /
|
|
44
|
+
`llm_stuck_warning` (a circuit-breaker halt surfaces as `loop_error`) — plus a
|
|
45
|
+
derived-status JSON (`loopRunner.statusJsonCommand`, rauf: `status … --json`;
|
|
46
|
+
forge-5-loop also reads its optional `loopState` / `lock` / `sleepUntil` /
|
|
47
|
+
`reviewPending` fields when present, and degrades to the counts when absent) and
|
|
46
48
|
per-iteration telemetry with a `stuckWarning` flag (`loopRunner.watchCommand`,
|
|
47
49
|
rauf: `status … --json` — the `loop watch` verb was removed in v0.5.0). `forge-5-loop` supervises the run through these,
|
|
48
50
|
**not** by parsing the human log. `followCommand` / `logCommand` are
|
|
@@ -78,11 +78,11 @@ Once the config exists, check the tooling this project's pipeline will lean on:
|
|
|
78
78
|
R="$(bash -c '[ -z "${FEATURE_FORGE_ROOT:-}" ] || [ -x "$FEATURE_FORGE_ROOT/scripts/forge-root.sh" ] || { echo "feature-forge: FEATURE_FORGE_ROOT=$FEATURE_FORGE_ROOT has no scripts/forge-root.sh" >&2; exit 2; }; for d in "${FEATURE_FORGE_ROOT:-}" "${CLAUDE_PLUGIN_ROOT:-}" "$HOME"/.claude/skills/feature-forge "$HOME"/.claude/plugins/cache/*/feature-forge/* "$HOME"/.claude/plugins/*/feature-forge "$HOME"/.agents/skills/feature-forge ./.agents/skills/feature-forge; do [ -x "$d/scripts/forge-root.sh" ] && exec "$d/scripts/forge-root.sh"; done')"
|
|
79
79
|
[ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
|
|
80
80
|
python3 "$R/scripts/forge-session.py" doctor --json \
|
|
81
|
-
--check root-version-skew --check gh-available
|
|
81
|
+
--check plugin-root --check root-version-skew --check gh-available
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Follow `references/preflight-and-self-heal.md` with that result: all `ok`/`na` → say nothing and
|
|
85
|
-
move on. Otherwise cluster and report. **
|
|
85
|
+
move on. Otherwise cluster and report. **No check here is a stop for `forge-init`** — report and
|
|
86
86
|
continue; a `warn` here never aborts initialization (the procedure returns statuses; the caller
|
|
87
87
|
owns its own gate). `gh-available` matters because the tooling-feedback step below tells agents
|
|
88
88
|
to file issues with `gh issue create`; when `gh` is missing or unauthenticated, say so once — its
|
|
@@ -93,9 +93,11 @@ interrogated about it on every init.
|
|
|
93
93
|
`root-version-skew` is reported the same way: a second install shadowing this one is worth
|
|
94
94
|
knowing about before the first stage runs.
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
resolver fails, so
|
|
98
|
-
|
|
96
|
+
`plugin-root` is reported the same way, **advisory here despite its `blocking` severity**. The
|
|
97
|
+
prelude above already exits 1 when the resolver fails, so below it the check warns only when the
|
|
98
|
+
resolved root is un-built canon (no `.feature-forge-bundle.json` — a repo checkout loaded
|
|
99
|
+
directly): its skills cannot read their shared references and will break mid-stage. Say so once
|
|
100
|
+
with the check's `remedy.description` (advise-only, `network`) and continue.
|
|
99
101
|
|
|
100
102
|
## Root hygiene — tooling feedback
|
|
101
103
|
|
|
@@ -6,7 +6,7 @@ silent unasked mutation. It runs whenever a skill gates on `doctor`'s structured
|
|
|
6
6
|
`checks[]` (`roadmap/self-healing-resilience.md` §5.2); today that is `forge-5-loop`'s
|
|
7
7
|
gates 1c/1d (`skills/forge-5-loop/SKILL.md`), which resolve the loop runner **before**
|
|
8
8
|
touching it, `forge-init`'s install preflight (`skills/forge-init/SKILL.md`), where
|
|
9
|
-
|
|
9
|
+
no check is a stop, and `forge-guide --doctor` (`skills/forge-guide/SKILL.md`), the
|
|
10
10
|
operator-facing repair surface, which gates nothing at all. All three are callers, not
|
|
11
11
|
the procedure's scope: any skill that gates on `checks[]` follows it in full. Its seven
|
|
12
12
|
ordered steps: **enumerate → cluster → consolidated prompts → record → apply → prove →
|
|
@@ -47,7 +47,7 @@ an epic member. Only the flags below are stage-specific; pass no others.
|
|
|
47
47
|
|---|---|---|
|
|
48
48
|
| `forge-0-epic` | `forge-0-epic` | `--next-feature "{member}"` when a concrete member exists |
|
|
49
49
|
| `forge-1-prd` … `forge-4-backlog` | that stage's own id | none beyond identity/capability |
|
|
50
|
-
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation` with `--outcome partial` |
|
|
50
|
+
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation`, `review-pending` or `runner-stopped` with `--outcome partial` |
|
|
51
51
|
| `forge-6-docs` | `forge-6-docs` | `--outcome` — `complete`, `blocked`, or `skipped` (deliberate docs skip, persisted via `state-skip` before the exit; routes like `complete` with honest wording) |
|
|
52
52
|
| direct `forge-verify` | `forge-verify` | `--owner direct`, `--outcome` (`passed`, `findings`, `skipped`, `failed`), and served-stage metadata |
|
|
53
53
|
| nested `forge-verify` | `forge-verify` | `--owner nested`, plus the same outcome and served-stage metadata |
|
|
@@ -235,8 +235,8 @@
|
|
|
235
235
|
},
|
|
236
236
|
"installHint": {
|
|
237
237
|
"type": "string",
|
|
238
|
-
"default": "Provision rauf for a multi-agent setup with the cross-agent installer: `npx @garygentry/feature-forge install` (records the pinned @garygentry/rauf@0.
|
|
239
|
-
"description": "Shown when the runner BINARY is missing or too old (version gate fails, minRunnerVersion floor) — how to obtain/upgrade the CLI itself. Names two distinct binary-provisioning paths: (1) the cross-agent installer (`npx @garygentry/feature-forge install`, the multi-agent provisioning path that pins @garygentry/rauf@0.
|
|
238
|
+
"default": "Provision rauf for a multi-agent setup with the cross-agent installer: `npx @garygentry/feature-forge install` (records the pinned @garygentry/rauf@0.17.1 default). Or install/upgrade just the rauf CLI: `npx @garygentry/rauf@0.17.1 --version`, or `curl -fsSL https://raw.githubusercontent.com/garygentry/rauf/main/scripts/install-binary.sh | bash`.",
|
|
239
|
+
"description": "Shown when the runner BINARY is missing or too old (version gate fails, minRunnerVersion floor) — how to obtain/upgrade the CLI itself. Names two distinct binary-provisioning paths: (1) the cross-agent installer (`npx @garygentry/feature-forge install`, the multi-agent provisioning path that pins @garygentry/rauf@0.17.1), and (2) the direct rauf-CLI install/upgrade one-liner. Distinct from setupHint (which installs per-project artifacts); a version-gate failure is ALWAYS this hint, never setupHint."
|
|
240
240
|
},
|
|
241
241
|
"schemaVersion": {
|
|
242
242
|
"type": "string",
|
|
@@ -6,7 +6,7 @@ silent unasked mutation. It runs whenever a skill gates on `doctor`'s structured
|
|
|
6
6
|
`checks[]` (`roadmap/self-healing-resilience.md` §5.2); today that is `forge-5-loop`'s
|
|
7
7
|
gates 1c/1d (`skills/forge-5-loop/SKILL.md`), which resolve the loop runner **before**
|
|
8
8
|
touching it, `forge-init`'s install preflight (`skills/forge-init/SKILL.md`), where
|
|
9
|
-
|
|
9
|
+
no check is a stop, and `forge-guide --doctor` (`skills/forge-guide/SKILL.md`), the
|
|
10
10
|
operator-facing repair surface, which gates nothing at all. All three are callers, not
|
|
11
11
|
the procedure's scope: any skill that gates on `checks[]` follows it in full. Its seven
|
|
12
12
|
ordered steps: **enumerate → cluster → consolidated prompts → record → apply → prove →
|
|
@@ -40,9 +40,11 @@ defined authoritatively in rauf's
|
|
|
40
40
|
- **A machine-readable event stream** for live supervision (`loopRunner.eventStreamCommand`,
|
|
41
41
|
rauf: `loop run … --ndjson`): one JSON event per line with a stable `type`
|
|
42
42
|
vocabulary — `item_completed` / `item_blocked` / `needs_human` / `signal_parsed`
|
|
43
|
-
/ `loop_completed` / `loop_error` / `loop_cancelled` / `
|
|
44
|
-
circuit-breaker halt surfaces as `loop_error`) — plus a
|
|
45
|
-
derived-status JSON (`loopRunner.statusJsonCommand`, rauf: `status … --json
|
|
43
|
+
/ `loop_completed` / `loop_error` / `loop_cancelled` / `review_failed` /
|
|
44
|
+
`llm_stuck_warning` (a circuit-breaker halt surfaces as `loop_error`) — plus a
|
|
45
|
+
derived-status JSON (`loopRunner.statusJsonCommand`, rauf: `status … --json`;
|
|
46
|
+
forge-5-loop also reads its optional `loopState` / `lock` / `sleepUntil` /
|
|
47
|
+
`reviewPending` fields when present, and degrades to the counts when absent) and
|
|
46
48
|
per-iteration telemetry with a `stuckWarning` flag (`loopRunner.watchCommand`,
|
|
47
49
|
rauf: `status … --json` — the `loop watch` verb was removed in v0.5.0). `forge-5-loop` supervises the run through these,
|
|
48
50
|
**not** by parsing the human log. `followCommand` / `logCommand` are
|
|
@@ -47,7 +47,7 @@ an epic member. Only the flags below are stage-specific; pass no others.
|
|
|
47
47
|
|---|---|---|
|
|
48
48
|
| `forge-0-epic` | `forge-0-epic` | `--next-feature "{member}"` when a concrete member exists |
|
|
49
49
|
| `forge-1-prd` … `forge-4-backlog` | that stage's own id | none beyond identity/capability |
|
|
50
|
-
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation` with `--outcome partial` |
|
|
50
|
+
| `forge-5-loop` | `forge-5-loop` | `--outcome` — one of `complete`, `partial`, `blocked`, `needs-human`, `deferred`, `resolved`; optional `--cause dependency-starvation`, `review-pending` or `runner-stopped` with `--outcome partial` |
|
|
51
51
|
| `forge-6-docs` | `forge-6-docs` | `--outcome` — `complete`, `blocked`, or `skipped` (deliberate docs skip, persisted via `state-skip` before the exit; routes like `complete` with honest wording) |
|
|
52
52
|
| direct `forge-verify` | `forge-verify` | `--owner direct`, `--outcome` (`passed`, `findings`, `skipped`, `failed`), and served-stage metadata |
|
|
53
53
|
| nested `forge-verify` | `forge-verify` | `--owner nested`, plus the same outcome and served-stage metadata |
|
|
@@ -27,7 +27,7 @@ vocabulary defined in `00-core-definitions.md` §8. No free-form values are perm
|
|
|
27
27
|
| `${CLAUDE_PLUGIN_ROOT}` — sanctioned residual | 1 occurrence in `scripts/forge-root.sh` (env-fallback, REQ-RES-02 step 3) | `preserved-as-spec-allowed` | The single sanctioned residual: the resolver's documented Claude-compat fallback (REQ-RES-03 / REQ-RES-05). Exempt from the residual-var scan (`00-core-definitions.md` §6 `RESIDUAL_VAR_EXEMPT`). |
|
|
28
28
|
| `${CLAUDE_PLUGIN_ROOT:-}` — bootstrap-prelude first-hint (Chunk 2b) | 1 occurrence per prelude across every canonical stamp site (the byte-pinned `BOOTSTRAP_PRELUDE`) | `preserved-as-spec-allowed` | The prelude's first resolver candidate — exact, glob-free root resolution on any Claude layout; expands to empty and is skipped when unset. Rule 3 allows it by stripping the byte-pinned prelude before its scan (detection is by `${CLAUDE_PLUGIN_ROOT` prefix, so the `:-}` default form is not an escape hatch elsewhere). `forge-agent-adapters-build` translates it to `${FEATURE_FORGE_ROOT:-}` in non-Claude bundles. |
|
|
29
29
|
| `${CLAUDE_PLUGIN_ROOT}` — in `hooks/hooks.json` | 1 occurrence in `hooks/hooks.json` | `out-of-canon` | Non-canonical Claude artifact (REQ-VND-04). Not a canonical surface; exempt from the REQ-RES-03 scan. Left in place. |
|
|
30
|
-
| `hooks/hooks.json` SessionStart wiring | 1 file (`hooks/hooks.json`) — Claude `SessionStart` → `bash ${CLAUDE_PLUGIN_ROOT}/scripts/session-check.sh` | `out-of-canon` | Claude-specific plugin hook wiring (REQ-VND-04, decision D3). Preserved + documented so `forge-agent-adapters-build` treats it as a Claude artifact, not portable canon. |
|
|
30
|
+
| `hooks/hooks.json` SessionStart wiring | 1 file (`hooks/hooks.json`) — Claude `SessionStart` → `bash "${CLAUDE_PLUGIN_ROOT}/scripts/session-check.sh"` | `out-of-canon` | Claude-specific plugin hook wiring (REQ-VND-04, decision D3). Preserved + documented so `forge-agent-adapters-build` treats it as a Claude artifact, not portable canon. |
|
|
31
31
|
| (contingency) any other vendor invocation directive | none found in the audit | — | REQ-VND-02 contingency did not fire (see Notes). If one is later surfaced, add a row with `removed` or `out-of-canon` per `02-frontmatter-purity-and-inventory.md` §3. |
|
|
32
32
|
|
|
33
33
|
## Notes
|
|
@@ -14,7 +14,8 @@ root navigator:
|
|
|
14
14
|
python3 forge-session.py check-epic-base --feature F [--specs-dir DIR] \
|
|
15
15
|
[--config FILE] [--epic E] [--json]
|
|
16
16
|
python3 forge-session.py stage-exit --feature F --stage S [--owner direct|nested] \
|
|
17
|
-
[--outcome O] [--cause dependency-starvation
|
|
17
|
+
[--outcome O] [--cause dependency-starvation|review-pending|runner-stopped] \
|
|
18
|
+
[--verify-mode M] \
|
|
18
19
|
[--served-stage S] [--verify-capability interactive|manual] [--specs-dir DIR] \
|
|
19
20
|
[--config FILE] [--epic E] [--next-feature N] [--host claude|generic|pi] [--json]
|
|
20
21
|
python3 forge-session.py select-outcome --feature F --served-stage S \
|
|
@@ -470,6 +471,8 @@ from forge_session.routes import ( # noqa: E402
|
|
|
470
471
|
_LOOP_COMPLETE_SETTLED,
|
|
471
472
|
_LOOP_COMPLETE_TEXT,
|
|
472
473
|
_LOOP_OUTCOME_TEXT,
|
|
474
|
+
_LOOP_PARTIAL_REVIEW_PENDING_TEXT,
|
|
475
|
+
_LOOP_PARTIAL_RUNNER_STOPPED_TEXT,
|
|
473
476
|
_LOOP_PARTIAL_STARVED_TEXT,
|
|
474
477
|
_LOOP_ROUTE_KIND,
|
|
475
478
|
_NO_FINDINGS_RESOLVED_TEXT,
|
|
@@ -767,6 +770,8 @@ __all__ = [
|
|
|
767
770
|
"_LOOP_COMPLETE_SETTLED",
|
|
768
771
|
"_LOOP_COMPLETE_TEXT",
|
|
769
772
|
"_LOOP_OUTCOME_TEXT",
|
|
773
|
+
"_LOOP_PARTIAL_REVIEW_PENDING_TEXT",
|
|
774
|
+
"_LOOP_PARTIAL_RUNNER_STOPPED_TEXT",
|
|
770
775
|
"_LOOP_PARTIAL_STARVED_TEXT",
|
|
771
776
|
"_LOOP_ROUTE_KIND",
|
|
772
777
|
"_NO_FINDINGS_RESOLVED_TEXT",
|
|
@@ -467,7 +467,8 @@ def main() -> int:
|
|
|
467
467
|
p_exit.add_argument("--outcome", default=None,
|
|
468
468
|
help="Stage-specific outcome (loop/docs/verify/fix only)")
|
|
469
469
|
p_exit.add_argument(
|
|
470
|
-
"--cause", default=None, dest="cause",
|
|
470
|
+
"--cause", default=None, dest="cause",
|
|
471
|
+
choices=("dependency-starvation", "review-pending", "runner-stopped"),
|
|
471
472
|
help="Pending-attribution cause; valid only with "
|
|
472
473
|
"--stage forge-5-loop --outcome partial",
|
|
473
474
|
)
|
|
@@ -686,8 +686,37 @@ def _feature_label(feat: dict) -> str:
|
|
|
686
686
|
return feat["name"] + (f" [{feat['epic']}]" if feat.get("epic") else "")
|
|
687
687
|
|
|
688
688
|
|
|
689
|
+
#: Remedy for a resolved root that is un-built canon. Every route loads a BUILT bundle and names
|
|
690
|
+
#: what it writes (#283/#317) — the npx installer's writes mirror ``installer/src`` (the sibling
|
|
691
|
+
#: ``manifestPath`` manifest + every ``resolvePlacements`` destination, both scopes), pinned by
|
|
692
|
+
#: ``installer/test/doctor-canon-remedy.test.ts`` against the installer's typed contract.
|
|
693
|
+
#: Tier = the most conservative route (``network``: the marketplace and npm fetch).
|
|
694
|
+
_CANON_ROOT_REMEDY: Final[str] = (
|
|
695
|
+
"Load a built bundle, not the repo checkout. Install: the Claude marketplace "
|
|
696
|
+
"(`/plugin install feature-forge@feature-forge`, ships ./adapters/claude; writes under "
|
|
697
|
+
"~/.claude/plugins and enables it in ~/.claude/settings.json) or "
|
|
698
|
+
"`npx @garygentry/feature-forge install` (under the install scope's root — the project, or ~ "
|
|
699
|
+
"for a global install — writes the host's bundle dir, e.g. .claude/skills/feature-forge, plus "
|
|
700
|
+
"a sibling .feature-forge.<scope>.json manifest; also Codex agent files in .codex/agents, a "
|
|
701
|
+
"managed block in .github/copilot-instructions.md for Copilot, Pi agent files in .pi/agents "
|
|
702
|
+
"(global: ~/.pi/agent/agents)). Source dogfood: rebuild with "
|
|
703
|
+
"`python3 scripts/build-adapters.py` (rewrites adapters/), then `claude --plugin-dir "
|
|
704
|
+
"adapters/claude` or FEATURE_FORGE_ROOT=adapters/<host> (neither writes files), "
|
|
705
|
+
"`pi install ./adapters/pi -l` (writes .pi/settings.json), or `scripts/dev-plugin.sh` "
|
|
706
|
+
"(writes an out-of-repo plugin dir, default ~/.cache/feature-forge-dev/claude) — see "
|
|
707
|
+
"docs/DOGFOODING.md"
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
|
|
689
711
|
def _check_plugin_root(ctx: _CheckContext) -> dict:
|
|
690
|
-
"""The sibling ``forge-root.sh`` resolves an install root (never ``na``).
|
|
712
|
+
"""The sibling ``forge-root.sh`` resolves an install root (never ``na``).
|
|
713
|
+
|
|
714
|
+
A resolved root without the neutral ``.feature-forge-bundle.json`` sentinel is
|
|
715
|
+
un-built canon (the repo checkout): its skills' prose ``Read references/X`` resolves
|
|
716
|
+
skill-local, where canon carries none of the shared refs the build fans in, so they
|
|
717
|
+
dead-reference mid-stage (#122/#305/#314). That warns — never fails (#244 policy), and
|
|
718
|
+
doctor still exits 0 (INV-3), so the repo's own ``doctor --json`` smoke stays green.
|
|
719
|
+
"""
|
|
691
720
|
root = ctx.plugin_root
|
|
692
721
|
evidence = dict(root)
|
|
693
722
|
if root.get("resolved"):
|
|
@@ -695,7 +724,18 @@ def _check_plugin_root(ctx: _CheckContext) -> dict:
|
|
|
695
724
|
channel = root.get("channel")
|
|
696
725
|
suffix = f" (version {version})" if version else " (no version manifest)"
|
|
697
726
|
chan = f" via {channel}" if channel else ""
|
|
698
|
-
|
|
727
|
+
built = (Path(str(root.get("root"))) / ".feature-forge-bundle.json").is_file()
|
|
728
|
+
evidence["bundleSentinel"] = built
|
|
729
|
+
if built:
|
|
730
|
+
return _result("ok", f"resolved {root.get('root')}{chan}{suffix}", evidence)
|
|
731
|
+
return _result(
|
|
732
|
+
"warn",
|
|
733
|
+
f"resolved {root.get('root')}{chan}{suffix} is un-built canon (no "
|
|
734
|
+
".feature-forge-bundle.json): skills loaded from it cannot Read their shared "
|
|
735
|
+
"references (references/shared-conventions.md, …) skill-local (#305/#314)",
|
|
736
|
+
evidence,
|
|
737
|
+
_remedy(_CANON_ROOT_REMEDY, None, "network"),
|
|
738
|
+
)
|
|
699
739
|
return _result(
|
|
700
740
|
"warn",
|
|
701
741
|
f"plugin root unresolved: {root.get('error', 'unknown')}",
|