@garygentry/feature-forge 0.3.8 → 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
package/README.md
CHANGED
|
@@ -74,7 +74,7 @@ Claude Code users can alternatively install via the plugin marketplace:
|
|
|
74
74
|
|
|
75
75
|
The default loop runner is [**rauf**](https://github.com/garygentry/rauf), published as
|
|
76
76
|
[`@garygentry/rauf`](https://www.npmjs.com/package/@garygentry/rauf). The installer runs a
|
|
77
|
-
read-only resolvability preflight on the pin (`@garygentry/rauf@0.
|
|
77
|
+
read-only resolvability preflight on the pin (`@garygentry/rauf@0.17.1`) and records it; pass
|
|
78
78
|
`--skip-rauf` to defer the check (e.g. offline installs). Install the rauf CLI itself with
|
|
79
79
|
`npx @garygentry/rauf` or its
|
|
80
80
|
[binary script](https://github.com/garygentry/rauf#install). See the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "feature-forge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "End-to-end feature development pipeline: PRD → tech spec → implementation specs → backlog → documentation, with verification gates, pipeline state tracking, and specialized subagents for verification and codebase research.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Gary Gentry"
|
|
@@ -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')}",
|
|
@@ -329,10 +329,10 @@ def stage_exit(
|
|
|
329
329
|
capability is permission, not tool presence: a dispatch permitted
|
|
330
330
|
only once the user has asked is still `interactive`, because the
|
|
331
331
|
`standard` gate's own prompt supplies that request.
|
|
332
|
-
cause: Pending-attribution annotation (`dependency-starvation
|
|
333
|
-
only with `--stage forge-5-loop --outcome
|
|
334
|
-
It swaps the partial next-steps sentence
|
|
335
|
-
and changes no routing.
|
|
332
|
+
cause: Pending-attribution annotation (`dependency-starvation`,
|
|
333
|
+
`review-pending` or `runner-stopped`), valid only with `--stage forge-5-loop --outcome
|
|
334
|
+
partial` (REQ-ATTR-04, #339). It swaps the partial next-steps sentence
|
|
335
|
+
for the matching variant and changes no routing.
|
|
336
336
|
|
|
337
337
|
Returns:
|
|
338
338
|
A JSON-serializable `StageExitPayload` dictionary.
|
|
@@ -486,7 +486,7 @@ def stage_exit(
|
|
|
486
486
|
# argparse `choices` already restricts the value; this restricts the combination.
|
|
487
487
|
if cause is not None and not (stage == "forge-5-loop" and outcome == "partial"):
|
|
488
488
|
raise UsageError(
|
|
489
|
-
"--cause
|
|
489
|
+
f"--cause {cause} is valid only with "
|
|
490
490
|
"--stage forge-5-loop --outcome partial"
|
|
491
491
|
)
|
|
492
492
|
|
|
@@ -799,6 +799,27 @@ _LOOP_PARTIAL_STARVED_TEXT: Final[str] = (
|
|
|
799
799
|
"resumable and nothing downstream is ready: unblock the roots named in the "
|
|
800
800
|
"starvation report above, then run the loop again below to continue."
|
|
801
801
|
)
|
|
802
|
+
#: The pending-review variant of the `partial` next-steps sentence: every item may be
|
|
803
|
+
#: done, but the runner's review pass failed or was interrupted, so the run is not
|
|
804
|
+
#: complete. Selected only by ``--cause review-pending`` (issue #339); the route is
|
|
805
|
+
#: still the loop resume, whose Step 2a re-runs exactly that review.
|
|
806
|
+
_LOOP_PARTIAL_REVIEW_PENDING_TEXT: Final[str] = (
|
|
807
|
+
"The loop for {feature} is not finished — its review pass failed or was "
|
|
808
|
+
"interrupted, so the review is still pending even if every item is done. The "
|
|
809
|
+
"recorded state is resumable and nothing downstream is ready: run the loop again "
|
|
810
|
+
"below to re-run the pending review."
|
|
811
|
+
)
|
|
812
|
+
#: The unfinished-runner variant of the `partial` next-steps sentence: the runner's
|
|
813
|
+
#: own terminal state (a crash, a stop, a stale lock, a limit halt) says it did not
|
|
814
|
+
#: finish cleanly, so the run is not complete even when every item reads `done`.
|
|
815
|
+
#: Selected only by ``--cause runner-stopped`` (issue #339); the route is still the
|
|
816
|
+
#: loop resume, whose Step 2a offers the runner's own resume.
|
|
817
|
+
_LOOP_PARTIAL_RUNNER_STOPPED_TEXT: Final[str] = (
|
|
818
|
+
"The loop runner for {feature} did not reach a clean finish — it crashed, was "
|
|
819
|
+
"stopped, or halted on a limit — so the run is not complete even if every item "
|
|
820
|
+
"reads done. The recorded state is resumable and nothing downstream is ready: run "
|
|
821
|
+
"the loop again below to resume the runner."
|
|
822
|
+
)
|
|
802
823
|
#: The `complete` preamble, selected by where the handoff actually lands. The epic
|
|
803
824
|
#: rows name the epic and its live rollup, so the operator can see WHY the handoff is
|
|
804
825
|
#: this member's own documentation rather than another member (or vice versa).
|
|
@@ -956,9 +977,11 @@ def _loop_route(
|
|
|
956
977
|
handoff: findings already exist at this exact revision, so the fenced
|
|
957
978
|
action is applying them, exactly as on a production re-exit.
|
|
958
979
|
cause: The already-validated attribution annotation — only
|
|
959
|
-
``"dependency-starvation"
|
|
960
|
-
|
|
961
|
-
|
|
980
|
+
``"dependency-starvation"``, ``"review-pending"`` or
|
|
981
|
+
``"runner-stopped"`` with
|
|
982
|
+
``outcome == "partial"``, else None. Swaps the partial next-steps
|
|
983
|
+
sentence for the matching variant; the route itself is unchanged
|
|
984
|
+
(partial stays a resume either way).
|
|
962
985
|
|
|
963
986
|
Returns:
|
|
964
987
|
`(primary_canonical, deferred_canonical, outcome_text, advancing)`, matching
|
|
@@ -985,6 +1008,10 @@ def _loop_route(
|
|
|
985
1008
|
)
|
|
986
1009
|
if outcome == "partial" and cause == "dependency-starvation":
|
|
987
1010
|
text = _LOOP_PARTIAL_STARVED_TEXT.format(feature=feature)
|
|
1011
|
+
elif outcome == "partial" and cause == "review-pending":
|
|
1012
|
+
text = _LOOP_PARTIAL_REVIEW_PENDING_TEXT.format(feature=feature)
|
|
1013
|
+
elif outcome == "partial" and cause == "runner-stopped":
|
|
1014
|
+
text = _LOOP_PARTIAL_RUNNER_STOPPED_TEXT.format(feature=feature)
|
|
988
1015
|
else:
|
|
989
1016
|
text = _LOOP_OUTCOME_TEXT[outcome].format(feature=feature)
|
|
990
1017
|
return primary, None, text, False
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Check if forge.config.json exists in the current working directory
|
|
5
|
+
if [[ -f "forge.config.json" ]]; then
|
|
6
|
+
exit 0
|
|
7
|
+
fi
|
|
8
|
+
|
|
9
|
+
# Check if any pipeline state files exist at any depth under specs/. Use find, not a
|
|
10
|
+
# `specs/**/…` glob: without `shopt -s globstar` the `**` collapses to a single level
|
|
11
|
+
# and would miss epic-member state (specs/<epic>/<member>/.pipeline-state.json).
|
|
12
|
+
if find specs/ -name ".pipeline-state.json" -print -quit 2>/dev/null | grep -q .; then
|
|
13
|
+
echo "⚠ Feature forge pipeline state found but no forge.config.json. Run /feature-forge:forge-init to create configuration."
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
exit 0
|
|
@@ -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 |
|
|
@@ -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 |
|
|
@@ -110,7 +110,7 @@ The runner commits each item onto the current branch. Skip if not a git repo or
|
|
|
110
110
|
|
|
111
111
|
### 1g. Stranded-Work Pre-flight (if using git)
|
|
112
112
|
|
|
113
|
-
Run `git status --porcelain`. If it reports changes **and** `{backlogDir}/{loopRunner.stateDir}/state.json` exists from a previous run, **STOP**: name that run (its `startedAt`, `currentItem`, and `blockedItems` from `state.json`) and point the user at the **Post-Run Tree Reconciliation** section of `references/recovery-procedure.md` to commit / stash / discard the stranded work before relaunch — never auto-pass `--force`. If the tree is dirty with **no** prior-run `state.json`, keep today's behavior (surface it; let the user commit/stash or pass `--force`). A clean tree is silent.
|
|
113
|
+
Run `git status --porcelain`. If it reports changes **and** `{backlogDir}/{loopRunner.stateDir}/state.json` exists from a previous run, **STOP**: name that run (its `startedAt`, `currentItem`, and `blockedItems` from `state.json`) and point the user at the **Post-Run Tree Reconciliation** section of `references/recovery-procedure.md` to commit / stash / discard the stranded work before relaunch — never auto-pass `--force`. If the tree is dirty with **no** prior-run `state.json`, keep today's behavior (surface it; let the user commit/stash or pass `--force`). A clean tree is silent.
|
|
114
114
|
|
|
115
115
|
## Step 2: Construct the Loop Command
|
|
116
116
|
|
|
@@ -120,7 +120,7 @@ Run the **list command** (`loopRunner.listCommand`, default `rauf backlog list .
|
|
|
120
120
|
|
|
121
121
|
Calculate the iteration count: `ceil((pending + in_progress) * loopIterationMultiplier)` where `loopIterationMultiplier` comes from `forge.config.json` (default: 1.5, headroom for retries).
|
|
122
122
|
|
|
123
|
-
|
|
123
|
+
Then, **whatever the counts**, run the **status-json command**: `reviewPending: true` (optional field) means a prior review pass failed or was interrupted — follow **Pending review** in `references/runner-contract.md` before any fresh run (which would drop it). Otherwise, with no pending or in_progress items: a reported `loopState` that is not a clean finish → follow **Unfinished runner** in that file; else STOP and tell the user: "All backlog items are already done or blocked. Nothing to run."
|
|
124
124
|
|
|
125
125
|
If there are `blocked` items, note them — the user may want `--retry-blocked`.
|
|
126
126
|
|
|
@@ -186,11 +186,11 @@ R="$(bash -c '[ -z "${FEATURE_FORGE_ROOT:-}" ] || [ -x "$FEATURE_FORGE_ROOT/scri
|
|
|
186
186
|
python3 "$R/scripts/forge-session.py" state-enter --feature "{feature}" --stage forge-5-loop --specs-dir "{specsDir}"
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
-
Then commit this state write before launching (mandatory
|
|
189
|
+
Then commit this state write before launching (mandatory — the runner refuses a dirty tree, and this marker is itself a change) via the shared-conventions **Git Commit Protocol** (epic members: stage `{specsDir}/{epic}/`): `{commitPrefix}({feature}): forge-5-loop in-progress`, regardless of `gitCommitAfterStage`. Unrelated leftover changes still trip the refusal; surface it, never auto-pass `--force`.
|
|
190
190
|
|
|
191
191
|
### 3b. Launch Background Process
|
|
192
192
|
|
|
193
|
-
Launch the loop **backgrounded** (`run_in_background`: true) so it survives session end and does not block the session. For a runner that **persists its own structured event file** (the default — rauf writes `{stateDir}/events.ndjson` natively
|
|
193
|
+
Launch the loop **backgrounded** (`run_in_background`: true) so it survives session end and does not block the session. For a runner that **persists its own structured event file** (the default — rauf writes and rotates `{stateDir}/events.ndjson` natively), launch the **plain `runCommand`** with **no stdout redirect** and supervise that native file; never redirect `--ndjson` into `{stateDir}` (it collides with the runner's own writer). Only a stdout-only runner (no native event file) uses `eventStreamCommand`, redirected to a file **outside** `{stateDir}`. The background task's exit notification is the single authoritative terminal signal (Step 4). For the exact launch commands (incl. the `mkdir -p` state-dir guard and the root→`IS_SANDBOX` sandbox guard) and the self-persisting vs. stdout-only detail, read `references/runner-contract.md`.
|
|
194
194
|
|
|
195
195
|
### 3c. Inform User
|
|
196
196
|
|
|
@@ -198,21 +198,11 @@ Follow the **Inform-user output template (Step 3c)** section of `references/runn
|
|
|
198
198
|
|
|
199
199
|
### 3d. Arm a Monitor on the event stream, and react to events
|
|
200
200
|
|
|
201
|
-
Arm the **`Monitor` tool** on the structured event stream (the NDJSON file, or the
|
|
202
|
-
human log as fallback) with **`persistent: true`**, a coverage-complete filter
|
|
203
|
-
matching every terminal and exception state (silence is not success), and react to
|
|
204
|
-
each event as it arrives. Each NDJSON line is a JSON object whose event kind lives
|
|
205
|
-
under the **`type`** field (rauf's schema — e.g. `{"type":"item_completed",…}`), so
|
|
206
|
-
the filter must select on **`.type`**, **not** `kind`: a `kind`-keyed filter surfaces
|
|
207
|
-
nothing (no `grep` match; `jq` aborts on the null `.kind`), so the watch stays dark. The exact Monitor
|
|
208
|
-
commands, the filter event list, and the full per-event reaction rules (`needs_human`
|
|
209
|
-
/ `loop_error` surfaced immediately with a `PushNotification`, `item_completed`
|
|
210
|
-
coalesced into milestones, `llm_stuck_warning` as a hang warning) are in
|
|
211
|
-
`references/runner-contract.md` — follow them verbatim.
|
|
201
|
+
Arm the **`Monitor` tool** (`persistent: true`) on the structured event stream (the NDJSON file, or the human log as fallback) with a coverage-complete filter matching every terminal and exception state (silence is not success). The filter selects on each NDJSON line's **`.type`** field, **never** `kind` (a `kind`-keyed filter matches nothing and the watch stays dark). The exact commands, the filter list (incl. `review_failed` and usage-limit events), and the per-event reactions (`needs_human` / `loop_error` / `review_failed` surfaced with a `PushNotification`, `item_completed` coalesced into milestones, `llm_stuck_warning` reported with its in-flight tool) are in `references/runner-contract.md` — follow them verbatim.
|
|
212
202
|
|
|
213
203
|
### 3f. Reach completion
|
|
214
204
|
|
|
215
|
-
Step 4 is reached when the backgrounded process exits (its completion notification is authoritative
|
|
205
|
+
Step 4 is reached when the backgrounded process exits (its completion notification is authoritative; a `loop_completed` / `loop_error` / `loop_cancelled` event is the live heads-up). Stop the Monitor (`TaskStop` if it has not ended) and proceed. Do NOT foreground-sleep or poll — the harness drives both signals.
|
|
216
206
|
|
|
217
207
|
## Step 4: Check Results
|
|
218
208
|
|
|
@@ -224,7 +214,7 @@ Run the **status-json command** (`loopRunner.statusJsonCommand`) and read
|
|
|
224
214
|
`backlogSummary` for the authoritative counts — it separates the three non-done
|
|
225
215
|
outcomes: genuine `blocked`, `needsHuman`, and runner-`deferred` ("false blocks").
|
|
226
216
|
Fall back to the **list command** (`loopRunner.listCommand`) if `statusJsonCommand`
|
|
227
|
-
is not configured. If the run used a review flag (e.g. rauf's `--review`), also read any `review_completed` event (event stream, or `{loopRunner.stateDir}/events.ndjson`) for its `itemsCreated`/`summary` to surface in 4b — see `references/result-reporting.md`.
|
|
217
|
+
is not configured. Also read the optional `loopState`, `reviewPending`, `lock` fields (absent on older runners = unset); **Runner terminal states** in `references/result-reporting.md` maps them. **`reviewPending: true` is never complete, whatever the counts** — offer the resume per **Pending review** in `references/runner-contract.md` before 4b. If the run used a review flag (e.g. rauf's `--review`), also read any `review_completed` event (event stream, or `{loopRunner.stateDir}/events.ndjson`) for its `itemsCreated`/`summary` to surface in 4b — see `references/result-reporting.md`.
|
|
228
218
|
|
|
229
219
|
### 4b. Report Results
|
|
230
220
|
|
|
@@ -233,11 +223,11 @@ blocked and needs-human) and render its report. The five verbatim result-report
|
|
|
233
223
|
|
|
234
224
|
### 4c. Post-Run Recovery Pass (unconditional)
|
|
235
225
|
|
|
236
|
-
Run the **Post-Run Recovery Procedure** (`references/recovery-procedure.md`) now — on **every** run close, before Step 5 writes state, so the tree it inspects is exactly what the run left. The live `needs_human` handler (3d)
|
|
226
|
+
Run the **Post-Run Recovery Procedure** (`references/recovery-procedure.md`) now — on **every** run close, before Step 5 writes state, so the tree it inspects is exactly what the run left. The live `needs_human` handler (3d) is **not** the entry condition: a run that emitted no event still enters here, so blocked-only runs reach the unblock. With nothing to decide, its step 1 skips to the §4 tree reconciliation — silent on a clean tree — which also reconciles work a run stranded uncommitted with no signal (1g is only the next-launch backstop). Its step-7 gate feeds Step 7's `resolved` rung; the stage still closes exactly once, in Step 7.
|
|
237
227
|
|
|
238
228
|
## Step 5: Update Pipeline State
|
|
239
229
|
|
|
240
|
-
Record completion by running `state-complete` (below). Evaluate "all backlog items are `done`" yourself and pass the result as `--status`: `complete`
|
|
230
|
+
Record completion by running `state-complete` (below). Evaluate "all backlog items are `done`" yourself and pass the result as `--status`: `complete` only when Step 7's ladder selects `complete` (every item `done`, `total > 0`, no pending review, a clean runner finish per `references/result-reporting.md`), else `in-progress`. The verb records `completedAt`, the version, `basedOnVersions` and `artifacts`, and refreshes `updatedAt`. Add `--epic "{epic}"` when this feature is an epic member — required, per the Pipeline State Protocol.
|
|
241
231
|
|
|
242
232
|
```bash
|
|
243
233
|
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')"
|
|
@@ -272,7 +262,7 @@ python3 "$R/scripts/forge-session.py" state-verify --feature "{feature}" --stage
|
|
|
272
262
|
|
|
273
263
|
Every loop run ends here, and ends here **exactly once** — standalone or epic member, complete or not.
|
|
274
264
|
|
|
275
|
-
First select the single `LoopOutcome` with the ladder in `references/result-reporting.md` (`resolved` → `needs-human` → `blocked` → `deferred` → `partial` → `complete`, first match wins), reading it from Step 4a's authoritative counts and never from the runner's process exit code. If those counts were never obtained, follow that file's operational-failure rule instead: report the failure and its recovery and run no exit at all.
|
|
265
|
+
First select the single `LoopOutcome` with the ladder in `references/result-reporting.md` (`resolved` → unfinished-runner `partial` → `needs-human` → `blocked` → `deferred` → `partial` → `complete`, first match wins, plus any `--cause` it names), reading it from Step 4a's authoritative counts and never from the runner's process exit code. If those counts were never obtained, follow that file's operational-failure rule instead: report the failure and its recovery and run no exit at all.
|
|
276
266
|
|
|
277
267
|
**Close this stage with the Scripted Stage Exit** (contract: `references/stage-exit-protocol.md`; do not improvise a "Next steps" list). Run:
|
|
278
268
|
|
|
@@ -291,7 +281,7 @@ Add `--epic "{epic}"` when this feature is an epic member — required, per the
|
|
|
291
281
|
- **Plugin-root discovery (1b-epic helper) covers installed paths, not workspace-dev checkouts.** The `forge-root.sh` search probes the locations of an **installed** plugin only, so a feature-forge **source checkout** (e.g. `~/workspace/feature-forge`) exits "cannot locate plugin root" unless `FEATURE_FORGE_ROOT` points at it (#323). Expected in a dev environment; run the epic-manifest script from the checkout directly (`python3 <checkout>/scripts/epic-manifest.py …`).
|
|
292
282
|
- `{backlogDir}` is a **directory path**, not a file path. Pass `specs/auth`, not `specs/auth/backlog.json`.
|
|
293
283
|
- rauf resolves `RAUF.md` with fallback (`{backlogDir}/.rauf/RAUF.md` first, then the project's `.rauf/RAUF.md`). State files (state.json, {loopRunner.logFile}, etc.) land at `{backlogDir}/{loopRunner.stateDir}/`, isolated per backlog dir, so concurrent features don't collide.
|
|
294
|
-
- If the session disconnects mid-loop, the runner process continues independently — check results later with the status / list commands. A stale lock
|
|
295
|
-
- Never run the run command in the foreground (without `run_in_background`) — it blocks and will hit the Bash tool timeout for any non-trivial backlog. "Don't block the foreground" is NOT "stay silent": supervise via the `Monitor` tool (3d). A `needs_human`/`blocked`/`review` signal does **not** pause the loop — the runner sets the item aside and keeps going; surface it live but don't tell the user the loop is waiting.
|
|
284
|
+
- If the session disconnects mid-loop, the runner process continues independently — check results later with the status / list commands. A crashed run's stale lock (`PAUSED` + `lock.stale`) is cleared by rauf's `resume`, not `--force` (see `references/result-reporting.md`).
|
|
285
|
+
- Never run the run command in the foreground (without `run_in_background`) — it blocks and will hit the Bash tool timeout for any non-trivial backlog. "Don't block the foreground" is NOT "stay silent": supervise via the `Monitor` tool (3d). A `needs_human`/`blocked`/`review` signal does **not** pause the loop — the runner sets the item aside and keeps going; surface it live but don't tell the user the loop is waiting.
|
|
296
286
|
- The version gate (1c) uses the `--json` form on purpose; never parse `rauf version`'s human output.
|
|
297
287
|
- **Implementation artifacts must not cite specs.** The loop should **read** specs and `backlog.json` freely — they are the source of truth, and the backlog rightly cites specs for provenance. But artifacts the loop **writes into the target repo** (source code, generated `SKILL.md`/agent files, configs, code comments) must be **self-contained**: no references to feature-forge spec files (no `See specs/{feature}/NN-*.md`, no "source spec" provenance notes) — specs are pre-implementation inputs that may be archived or deleted once the feature ships. This applies only to shipped implementation output, never to the backlog or spec documents, which keep citing specs.
|
|
@@ -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
|