@garygentry/feature-forge 0.2.3 → 0.2.5
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 +6 -1
- package/adapters/GENERATION-REPORT.md +5 -1
- package/adapters/claude/.feature-forge-bundle.json +1 -1
- package/adapters/claude/references/forge-config-schema.json +25 -3
- package/adapters/claude/references/pipeline-state-schema.json +3 -2
- package/adapters/claude/references/portable-root.md +6 -3
- package/adapters/claude/references/process-overview.md +10 -0
- package/adapters/claude/references/shared-conventions.md +27 -9
- package/adapters/claude/references/stage-exit-protocol.md +206 -0
- package/adapters/claude/scripts/epic-manifest.py +10 -0
- package/adapters/claude/scripts/forge-bootstrap.py +94 -16
- package/adapters/claude/scripts/forge-init.sh +7 -1
- package/adapters/claude/scripts/forge-root.sh +20 -1
- package/adapters/claude/scripts/forge-session.py +866 -31
- package/adapters/claude/skills/forge/SKILL.md +29 -15
- package/adapters/claude/skills/forge-0-epic/SKILL.md +19 -24
- package/adapters/claude/skills/forge-0-epic/references/edit-mode.md +6 -4
- package/adapters/claude/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/claude/skills/forge-1-prd/SKILL.md +13 -4
- package/adapters/claude/skills/forge-2-tech/SKILL.md +13 -3
- package/adapters/claude/skills/forge-3-specs/SKILL.md +13 -3
- package/adapters/claude/skills/forge-4-backlog/SKILL.md +15 -5
- package/adapters/claude/skills/forge-5-loop/SKILL.md +19 -21
- package/adapters/claude/skills/forge-5-loop/references/result-reporting.md +10 -5
- package/adapters/claude/skills/forge-6-docs/SKILL.md +6 -6
- package/adapters/claude/skills/forge-bootstrap/SKILL.md +4 -4
- package/adapters/claude/skills/forge-fix/SKILL.md +29 -6
- package/adapters/claude/skills/forge-guide/SKILL.md +182 -0
- package/adapters/claude/skills/forge-init/SKILL.md +29 -1
- package/adapters/claude/skills/forge-verify/SKILL.md +46 -15
- package/adapters/claude/skills/forge-verify/references/verification-checklists.md +1 -1
- package/adapters/codex/.feature-forge-bundle.json +1 -1
- package/adapters/codex/references/forge-config-schema.json +25 -3
- package/adapters/codex/references/pipeline-state-schema.json +3 -2
- package/adapters/codex/references/portable-root.md +6 -3
- package/adapters/codex/references/process-overview.md +10 -0
- package/adapters/codex/references/shared-conventions.md +27 -9
- package/adapters/codex/references/stage-exit-protocol.md +206 -0
- package/adapters/codex/scripts/epic-manifest.py +10 -0
- package/adapters/codex/scripts/forge-bootstrap.py +94 -16
- package/adapters/codex/scripts/forge-init.sh +7 -1
- package/adapters/codex/scripts/forge-root.sh +20 -1
- package/adapters/codex/scripts/forge-session.py +866 -31
- package/adapters/codex/skills/forge/SKILL.md +34 -20
- package/adapters/codex/skills/forge-0-epic/SKILL.md +20 -25
- package/adapters/codex/skills/forge-0-epic/references/edit-mode.md +6 -4
- package/adapters/codex/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/codex/skills/forge-1-prd/SKILL.md +13 -4
- package/adapters/codex/skills/forge-2-tech/SKILL.md +14 -4
- package/adapters/codex/skills/forge-3-specs/SKILL.md +13 -3
- package/adapters/codex/skills/forge-4-backlog/SKILL.md +15 -5
- package/adapters/codex/skills/forge-5-loop/SKILL.md +21 -23
- package/adapters/codex/skills/forge-5-loop/references/result-reporting.md +10 -5
- package/adapters/codex/skills/forge-6-docs/SKILL.md +6 -6
- package/adapters/codex/skills/forge-bootstrap/SKILL.md +4 -4
- package/adapters/codex/skills/forge-fix/SKILL.md +29 -6
- package/adapters/codex/skills/forge-guide/SKILL.md +191 -0
- package/adapters/codex/skills/forge-init/SKILL.md +29 -1
- package/adapters/codex/skills/forge-verify/SKILL.md +45 -14
- package/adapters/codex/skills/forge-verify/references/verification-checklists.md +1 -1
- package/adapters/copilot/.feature-forge-bundle.json +1 -1
- package/adapters/copilot/references/forge-config-schema.json +25 -3
- package/adapters/copilot/references/pipeline-state-schema.json +3 -2
- package/adapters/copilot/references/portable-root.md +6 -3
- package/adapters/copilot/references/process-overview.md +10 -0
- package/adapters/copilot/references/shared-conventions.md +27 -9
- package/adapters/copilot/references/stage-exit-protocol.md +206 -0
- package/adapters/copilot/scripts/epic-manifest.py +10 -0
- package/adapters/copilot/scripts/forge-bootstrap.py +94 -16
- package/adapters/copilot/scripts/forge-init.sh +7 -1
- package/adapters/copilot/scripts/forge-root.sh +20 -1
- package/adapters/copilot/scripts/forge-session.py +866 -31
- package/adapters/copilot/skills/forge/forge.md +34 -20
- package/adapters/copilot/skills/forge-0-epic/forge-0-epic.md +20 -25
- package/adapters/copilot/skills/forge-0-epic/references/edit-mode.md +6 -4
- package/adapters/copilot/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +13 -4
- package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +14 -4
- package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +13 -3
- package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +15 -5
- package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +21 -23
- package/adapters/copilot/skills/forge-5-loop/references/result-reporting.md +10 -5
- package/adapters/copilot/skills/forge-6-docs/forge-6-docs.md +6 -6
- package/adapters/copilot/skills/forge-bootstrap/forge-bootstrap.md +4 -4
- package/adapters/copilot/skills/forge-fix/forge-fix.md +29 -6
- package/adapters/copilot/skills/forge-guide/forge-guide.md +191 -0
- package/adapters/copilot/skills/forge-init/forge-init.md +29 -1
- package/adapters/copilot/skills/forge-verify/forge-verify.md +45 -14
- package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +1 -1
- package/adapters/cursor/.feature-forge-bundle.json +1 -1
- package/adapters/cursor/references/forge-config-schema.json +25 -3
- package/adapters/cursor/references/pipeline-state-schema.json +3 -2
- package/adapters/cursor/references/portable-root.md +6 -3
- package/adapters/cursor/references/process-overview.md +10 -0
- package/adapters/cursor/references/shared-conventions.md +27 -9
- package/adapters/cursor/references/stage-exit-protocol.md +206 -0
- package/adapters/cursor/scripts/epic-manifest.py +10 -0
- package/adapters/cursor/scripts/forge-bootstrap.py +94 -16
- package/adapters/cursor/scripts/forge-init.sh +7 -1
- package/adapters/cursor/scripts/forge-root.sh +20 -1
- package/adapters/cursor/scripts/forge-session.py +866 -31
- package/adapters/cursor/skills/forge/forge.mdc +34 -20
- package/adapters/cursor/skills/forge-0-epic/forge-0-epic.mdc +20 -25
- package/adapters/cursor/skills/forge-0-epic/references/edit-mode.md +6 -4
- package/adapters/cursor/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +13 -4
- package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +14 -4
- package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +13 -3
- package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +15 -5
- package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +21 -23
- package/adapters/cursor/skills/forge-5-loop/references/result-reporting.md +10 -5
- package/adapters/cursor/skills/forge-6-docs/forge-6-docs.mdc +6 -6
- package/adapters/cursor/skills/forge-bootstrap/forge-bootstrap.mdc +4 -4
- package/adapters/cursor/skills/forge-fix/forge-fix.mdc +29 -6
- package/adapters/cursor/skills/forge-guide/forge-guide.mdc +192 -0
- package/adapters/cursor/skills/forge-init/forge-init.mdc +29 -1
- package/adapters/cursor/skills/forge-verify/forge-verify.mdc +45 -14
- package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +1 -1
- package/adapters/gemini/.feature-forge-bundle.json +1 -1
- package/adapters/gemini/gemini-extension.json +5 -1
- package/adapters/gemini/references/forge-config-schema.json +25 -3
- package/adapters/gemini/references/pipeline-state-schema.json +3 -2
- package/adapters/gemini/references/portable-root.md +6 -3
- package/adapters/gemini/references/process-overview.md +10 -0
- package/adapters/gemini/references/shared-conventions.md +27 -9
- package/adapters/gemini/references/stage-exit-protocol.md +206 -0
- package/adapters/gemini/scripts/epic-manifest.py +10 -0
- package/adapters/gemini/scripts/forge-bootstrap.py +94 -16
- package/adapters/gemini/scripts/forge-init.sh +7 -1
- package/adapters/gemini/scripts/forge-root.sh +20 -1
- package/adapters/gemini/scripts/forge-session.py +866 -31
- package/adapters/gemini/skills/forge/forge.md +34 -20
- package/adapters/gemini/skills/forge-0-epic/forge-0-epic.md +20 -25
- package/adapters/gemini/skills/forge-0-epic/references/edit-mode.md +6 -4
- package/adapters/gemini/skills/forge-0-epic/references/epic-manifest-subcommands.md +1 -1
- package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +13 -4
- package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +14 -4
- package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +13 -3
- package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +15 -5
- package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +21 -23
- package/adapters/gemini/skills/forge-5-loop/references/result-reporting.md +10 -5
- package/adapters/gemini/skills/forge-6-docs/forge-6-docs.md +6 -6
- package/adapters/gemini/skills/forge-bootstrap/forge-bootstrap.md +4 -4
- package/adapters/gemini/skills/forge-fix/forge-fix.md +29 -6
- package/adapters/gemini/skills/forge-guide/forge-guide.md +191 -0
- package/adapters/gemini/skills/forge-init/forge-init.md +29 -1
- package/adapters/gemini/skills/forge-verify/forge-verify.md +45 -14
- package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +1 -1
- package/dist/apply.js +34 -8
- package/dist/cli.js +40 -4
- package/dist/fsutil.d.ts +0 -12
- package/dist/fsutil.js +10 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/plan.js +22 -2
- package/dist/rauf.d.ts +4 -4
- package/dist/rauf.js +3 -3
- package/dist/report.js +1 -1
- package/dist/types.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,13 +71,18 @@ Claude Code users can alternatively install via the plugin marketplace:
|
|
|
71
71
|
|
|
72
72
|
The default loop runner is [**rauf**](https://github.com/garygentry/rauf), published as
|
|
73
73
|
[`@garygentry/rauf`](https://www.npmjs.com/package/@garygentry/rauf). The installer runs a
|
|
74
|
-
read-only resolvability preflight on the pin (`@garygentry/rauf@0.
|
|
74
|
+
read-only resolvability preflight on the pin (`@garygentry/rauf@0.12.0`) and records it; pass
|
|
75
75
|
`--skip-rauf` to defer the check (e.g. offline installs). Install the rauf CLI itself with
|
|
76
76
|
`npx @garygentry/rauf` or its
|
|
77
77
|
[binary script](https://github.com/garygentry/rauf#install). See the
|
|
78
78
|
[feature-forge README](https://github.com/garygentry/feature-forge#readme) for the full
|
|
79
79
|
pipeline documentation and per-agent setup guides.
|
|
80
80
|
|
|
81
|
+
The installer never writes a `forge.config.json` — it deploys only the bundled skills and
|
|
82
|
+
runtime helpers. Configuration authorship belongs to `/feature-forge:forge-init`, which creates
|
|
83
|
+
the config and surfaces the auto-verify opt-in (whether to run `forge-verify` automatically after
|
|
84
|
+
each stage) at setup time.
|
|
85
|
+
|
|
81
86
|
## License
|
|
82
87
|
|
|
83
88
|
MIT
|
|
@@ -33,6 +33,7 @@ _No dropped constructs — every canonical construct is representable in this ag
|
|
|
33
33
|
| `skills/forge-6-docs/SKILL.md` | `argument-hint` | no confirmed Codex invocation-hint field (TQ-1) |
|
|
34
34
|
| `skills/forge-bootstrap/SKILL.md` | `argument-hint` | no confirmed Codex invocation-hint field (TQ-1) |
|
|
35
35
|
| `skills/forge-fix/SKILL.md` | `argument-hint` | no confirmed Codex invocation-hint field (TQ-1) |
|
|
36
|
+
| `skills/forge-guide/SKILL.md` | `argument-hint` | no confirmed Codex invocation-hint field (TQ-1) |
|
|
36
37
|
| `skills/forge-verify/SKILL.md` | `argument-hint` | no confirmed Codex invocation-hint field (TQ-1) |
|
|
37
38
|
| `skills/forge/SKILL.md` | `argument-hint` | no confirmed Codex invocation-hint field (TQ-1) |
|
|
38
39
|
|
|
@@ -61,6 +62,7 @@ _No dropped constructs — every canonical construct is representable in this ag
|
|
|
61
62
|
| `skills/forge-6-docs/SKILL.md` | `argument-hint` | no known Copilot invocation-hint field (TQ-1) |
|
|
62
63
|
| `skills/forge-bootstrap/SKILL.md` | `argument-hint` | no known Copilot invocation-hint field (TQ-1) |
|
|
63
64
|
| `skills/forge-fix/SKILL.md` | `argument-hint` | no known Copilot invocation-hint field (TQ-1) |
|
|
65
|
+
| `skills/forge-guide/SKILL.md` | `argument-hint` | no known Copilot invocation-hint field (TQ-1) |
|
|
64
66
|
| `skills/forge-verify/SKILL.md` | `argument-hint` | no known Copilot invocation-hint field (TQ-1) |
|
|
65
67
|
| `skills/forge/SKILL.md` | `argument-hint` | no known Copilot invocation-hint field (TQ-1) |
|
|
66
68
|
|
|
@@ -89,6 +91,7 @@ _No dropped constructs — every canonical construct is representable in this ag
|
|
|
89
91
|
| `skills/forge-6-docs/SKILL.md` | `argument-hint` | no Cursor .mdc invocation-hint field |
|
|
90
92
|
| `skills/forge-bootstrap/SKILL.md` | `argument-hint` | no Cursor .mdc invocation-hint field |
|
|
91
93
|
| `skills/forge-fix/SKILL.md` | `argument-hint` | no Cursor .mdc invocation-hint field |
|
|
94
|
+
| `skills/forge-guide/SKILL.md` | `argument-hint` | no Cursor .mdc invocation-hint field |
|
|
92
95
|
| `skills/forge-verify/SKILL.md` | `argument-hint` | no Cursor .mdc invocation-hint field |
|
|
93
96
|
| `skills/forge/SKILL.md` | `argument-hint` | no Cursor .mdc invocation-hint field |
|
|
94
97
|
|
|
@@ -117,6 +120,7 @@ _No dropped constructs — every canonical construct is representable in this ag
|
|
|
117
120
|
| `skills/forge-6-docs/SKILL.md` | `argument-hint` | Gemini manifest hint field unconfirmed (TQ-1) |
|
|
118
121
|
| `skills/forge-bootstrap/SKILL.md` | `argument-hint` | Gemini manifest hint field unconfirmed (TQ-1) |
|
|
119
122
|
| `skills/forge-fix/SKILL.md` | `argument-hint` | Gemini manifest hint field unconfirmed (TQ-1) |
|
|
123
|
+
| `skills/forge-guide/SKILL.md` | `argument-hint` | Gemini manifest hint field unconfirmed (TQ-1) |
|
|
120
124
|
| `skills/forge-verify/SKILL.md` | `argument-hint` | Gemini manifest hint field unconfirmed (TQ-1) |
|
|
121
125
|
| `skills/forge/SKILL.md` | `argument-hint` | Gemini manifest hint field unconfirmed (TQ-1) |
|
|
122
126
|
|
|
@@ -125,7 +129,7 @@ _No dropped constructs — every canonical construct is representable in this ag
|
|
|
125
129
|
These files are transported byte-for-byte from canon into every `adapters/<agent>/` bundle and intentionally carry **no** provenance header (a header would break byte-identity / corrupt parsed files):
|
|
126
130
|
|
|
127
131
|
- `scripts/forge-root.sh` → `adapters/<agent>/scripts/forge-root.sh` (mode 0755, byte-identical — REQ-GEN-05).
|
|
128
|
-
- the whole repo-root `references/` tree (
|
|
132
|
+
- the whole repo-root `references/` tree (all root files plus the `stacks/` and `templates/specs-hygiene/` subtrees) → `adapters/<agent>/references/` (verbatim — REQ-GEN-04 / D5).
|
|
129
133
|
- each skill's own `references/` subdir → `adapters/<agent>/skills/<name>/references/` (verbatim, where present).
|
|
130
134
|
|
|
131
135
|
Regenerate all adapter output with `python3 scripts/build-adapters.py`.
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"backlogDir": {
|
|
18
18
|
"type": ["string", "null"],
|
|
19
|
-
"
|
|
19
|
+
"default": null,
|
|
20
|
+
"description": "Optional override for the backlog root. If set, the backlog is written to {backlogDir}/{feature}/backlog.json — the {feature} subdirectory is always composed in so multi-feature epics never collide. Default behavior (null): backlog.json is written to {specsDir}/{feature}/backlog.json with the feature specs."
|
|
20
21
|
},
|
|
21
22
|
"gitCommitAfterStage": {
|
|
22
23
|
"type": "boolean",
|
|
@@ -61,6 +62,27 @@
|
|
|
61
62
|
"default": true,
|
|
62
63
|
"description": "When true (default), the /feature-forge:forge navigator auto-invokes the next pipeline stage via the Skill tool after the user confirms it, instead of only printing the command to copy. Set false to keep the old copy-paste behavior (the navigator suggests the command but never launches it). Ignored on non-Claude hosts, which always fall back to printing the command."
|
|
63
64
|
},
|
|
65
|
+
"autoVerify": {
|
|
66
|
+
"type": "boolean",
|
|
67
|
+
"default": false,
|
|
68
|
+
"description": "When true, the /feature-forge:forge navigator automatically runs forge-verify after a stage completes, with no prompt. forge-verify runs in a fresh forge-verifier subagent (clean-room), so it never needs a context clear and costs the current session only a compact findings digest. Default false preserves today's manual-gate behavior. Ignored on non-Claude hosts, which always fall back to printing the verify command. Per-stage overrides in autoVerifyStages take precedence."
|
|
69
|
+
},
|
|
70
|
+
"autoVerifyStages": {
|
|
71
|
+
"type": "object",
|
|
72
|
+
"default": {},
|
|
73
|
+
"description": "Per-stage overrides for autoVerify. Maps a production stage id to a boolean; the effective value for a stage is autoVerifyStages[stage] if present, else autoVerify. Keys are constrained to the five verify-capable stages so a typo (e.g. 'forge-1-prod') is a schema error, not a silent no-op. forge-6-docs has no verify step and is not a valid key.",
|
|
74
|
+
"propertyNames": {
|
|
75
|
+
"enum": ["forge-1-prd", "forge-2-tech", "forge-3-specs", "forge-4-backlog", "forge-5-loop"]
|
|
76
|
+
},
|
|
77
|
+
"additionalProperties": {
|
|
78
|
+
"type": "boolean"
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
"autoFix": {
|
|
82
|
+
"type": "boolean",
|
|
83
|
+
"default": false,
|
|
84
|
+
"description": "When true, the navigator chains forge-fix automatically after an auto-verify that finds issues. Honored ONLY when auto-verify is effectively on for the stage, and ONLY when preconditions hold (findings doc has zero unresolved decision points, working tree is clean, and a mandatory re-verify passes) — otherwise it falls back to surfacing the findings digest and prompting. Default false keeps fixing human-gated."
|
|
85
|
+
},
|
|
64
86
|
"contextWindowTokens": {
|
|
65
87
|
"type": ["integer", "null"],
|
|
66
88
|
"default": null,
|
|
@@ -190,8 +212,8 @@
|
|
|
190
212
|
},
|
|
191
213
|
"installHint": {
|
|
192
214
|
"type": "string",
|
|
193
|
-
"default": "Provision rauf for a multi-agent setup with the cross-agent installer: `npx @garygentry/feature-forge install` (records the pinned @garygentry/rauf@0.
|
|
194
|
-
"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.
|
|
215
|
+
"default": "Provision rauf for a multi-agent setup with the cross-agent installer: `npx @garygentry/feature-forge install` (records the pinned @garygentry/rauf@0.12.0 default). Or install/upgrade just the rauf CLI: `npx @garygentry/rauf@0.12.0 --version`, or `curl -fsSL https://raw.githubusercontent.com/garygentry/rauf/main/scripts/install-binary.sh | bash`.",
|
|
216
|
+
"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.12.0), 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."
|
|
195
217
|
},
|
|
196
218
|
"schemaVersion": {
|
|
197
219
|
"type": "string",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"completedAt": { "type": ["string", "null"], "format": "date-time" },
|
|
81
81
|
"commitHash": {
|
|
82
82
|
"type": ["string", "null"],
|
|
83
|
-
"description": "Git commit SHA
|
|
83
|
+
"description": "Git commit SHA of this stage's artifact commit (Commit 1 of the two-commit Git Commit Protocol in shared-conventions.md). Written by a small follow-up commit so it points at the artifact commit itself, never at an orphaned amend or the hash-recording commit. null until recorded (or when there was no new artifact commit)."
|
|
84
84
|
},
|
|
85
85
|
"basedOnVersions": {
|
|
86
86
|
"type": "object",
|
|
@@ -107,7 +107,8 @@
|
|
|
107
107
|
},
|
|
108
108
|
"verifiedAt": { "type": ["string", "null"], "format": "date-time" },
|
|
109
109
|
"fixedAt": { "type": ["string", "null"], "format": "date-time" },
|
|
110
|
-
"commitHash": { "type": ["string", "null"] }
|
|
110
|
+
"commitHash": { "type": ["string", "null"], "description": "Git commit SHA of the verify/fix artifact commit. Recorded via the two-commit Git Commit Protocol (shared-conventions.md) so it points at the artifact commit, never an orphaned amend." },
|
|
111
|
+
"verifiedStageVersion": { "type": ["integer", "null"], "description": "The production stage's `version` at the moment this verify was resolved. The navigator's freshness ledger compares it to the stage's current `version`: equal means the verify is fresh; a mismatch (artifact revised since) or an absent field (legacy state) means stale, so auto-verify re-fires. Recorded by forge-verify/forge-fix when writing a passed/findings-applied status." }
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
}
|
|
@@ -12,7 +12,7 @@ against the fenced block here, byte-for-byte.
|
|
|
12
12
|
## Canonical bootstrap prelude
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
R="$(for d in "$HOME"/.claude/skills/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)"
|
|
15
|
+
R="$(bash -c 'for d in "$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')"
|
|
16
16
|
[ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
|
|
17
17
|
```
|
|
18
18
|
|
|
@@ -24,7 +24,7 @@ makes several calls, add the prelude once and reuse `$R` for each. A fresh block
|
|
|
24
24
|
prelude (per-block re-resolution). Worked example:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
R="$(for d in "$HOME"/.claude/skills/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)"
|
|
27
|
+
R="$(bash -c 'for d in "$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')"
|
|
28
28
|
[ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
|
|
29
29
|
python3 "$R/scripts/epic-manifest.py" render-status "{epic}" --specs-dir "{specsDir}" --json
|
|
30
30
|
```
|
|
@@ -43,7 +43,10 @@ python3 "$R/scripts/epic-manifest.py" render-status "{epic}" --specs-dir "{specs
|
|
|
43
43
|
3. **Prelude candidate set is an agent-neutral bootstrap subset.** The prelude's `for d` list
|
|
44
44
|
exists only to bootstrap-discover `forge-root.sh`; the authoritative multi-root probe lives in
|
|
45
45
|
`forge-root.sh` step 2. The list enumerates install roots across agents — the Claude
|
|
46
|
-
skill/plugin dirs
|
|
46
|
+
skill/plugin dirs — including the marketplace-cache layout
|
|
47
|
+
`~/.claude/plugins/cache/<marketplace>/feature-forge/<version>/`, listed before the
|
|
48
|
+
single-star plugins glob so a versioned cache install always beats the marketplace clone —
|
|
49
|
+
**and** the agent-neutral `.agents/skills/feature-forge` dirs (`$HOME` and the
|
|
47
50
|
project-relative `./.agents/...`) — so a non-Claude install (e.g. Codex under `.agents/skills`)
|
|
48
51
|
can still discover the resolver. When adding an install root, update `forge-root.sh` first;
|
|
49
52
|
extend the prelude only if the new root is needed to bootstrap-discover `forge-root.sh` itself.
|
|
@@ -36,6 +36,16 @@ The pipeline compiles a fuzzy feature idea into a machine-executable `backlog.js
|
|
|
36
36
|
**Output:** `{specsDir}/{feature}/.verification/VERIFY-<stage>-<timestamp>.md` (includes both findings and a Fix Execution Plan)
|
|
37
37
|
**Method:** Clean-context analysis producing actionable findings with an ordered fix plan.
|
|
38
38
|
|
|
39
|
+
**Manual or automatic.** By default the navigator offers verification after each stage. Because
|
|
40
|
+
it runs in a fresh, read-only subagent (clean-room by construction, never needs a `/clear`), it is
|
|
41
|
+
safe to automate: set `autoVerify: true` (or per-stage via `autoVerifyStages`) in
|
|
42
|
+
`forge.config.json` and the navigator runs `forge-verify` automatically once a stage completes,
|
|
43
|
+
returning only a compact digest to the session. The freshness ledger (verify entries record the
|
|
44
|
+
artifact `version` they ran against) means a stage is re-verified only when its artifact changes,
|
|
45
|
+
and an explicitly `skipped` verify is respected. Fixing stays human-gated unless `autoFix: true` is
|
|
46
|
+
also set, which chains `forge-fix` only when preconditions hold (zero unresolved decisions, clean
|
|
47
|
+
tree, passing re-verify). See `references/shared-conventions.md` for the config keys.
|
|
48
|
+
|
|
39
49
|
After verification, fixes can be applied via:
|
|
40
50
|
- `/feature-forge:forge-fix <feature>` — reads the Fix Execution Plan from the findings document and applies changes (works in any session)
|
|
41
51
|
- Plan mode workflow — enter plan mode, run verify, review plan, exit and execute
|
|
@@ -71,6 +71,9 @@ Extract these config values (use defaults if not present):
|
|
|
71
71
|
- `autoInvokeNextStage` (default: `true` — the `/feature-forge:forge` navigator auto-invokes the next stage via the `Skill` tool after the user confirms; `false` keeps copy-paste behavior. Navigator-only.)
|
|
72
72
|
- `contextWindowTokens` (default: `null` — context window used by the navigator's context-usage check; `null` infers from the session model and falls back to 200000. Set to the model's window, e.g. `1000000` on a 1M model. Navigator-only.)
|
|
73
73
|
- `contextWarnThreshold` (default: `0.7` — fraction of the window past which the navigator recommends a clean session. Navigator-only.)
|
|
74
|
+
- `autoVerify` (default: `false` — when `true`, `forge-verify` runs automatically after a stage completes, no prompt. **In-stage-primary:** the just-completed authoring stage runs it itself, in-session, before the exit block (honoring the verify-before-clear principle). The navigator runs it only as a **catch-up** when verify is still pending (a host that could not dispatch a clean-room subagent, or a stage run before this behavior landed). Either way it runs in a fresh clean-room subagent, so it never needs a `/clear` and costs only a compact digest.)
|
|
75
|
+
- `autoVerifyStages` (default: `{}` — per-stage overrides for `autoVerify`, e.g. `{"forge-1-prd": false}`. Effective value = `autoVerifyStages[stage]` if present, else `autoVerify`. Keys are constrained to the five verify-capable stages; a typo is a config error surfaced as `invalidAutoVerifyKeys`. Both the in-stage run and the navigator catch-up read this same effective value.)
|
|
76
|
+
- `autoFix` (default: `false` — when `true`, `forge-fix` is chained after an auto-verify that finds issues — by the in-stage run (primary) or the navigator catch-up — but only when auto-verify is on for that stage AND preconditions hold (zero unresolved decisions, clean tree, passing re-verify); otherwise a digest is surfaced and the gate is presented.)
|
|
74
77
|
- `loopRunner` (optional object — the loop runner to drive; **defaults to rauf** when absent, with every command templated. See `references/forge-config-schema.json` and `references/ralph-loop-contract.md`.)
|
|
75
78
|
|
|
76
79
|
## Feature Directory Resolution
|
|
@@ -78,7 +81,7 @@ Extract these config values (use defaults if not present):
|
|
|
78
81
|
Before any file I/O against a feature's artifacts, resolve its directory through the deterministic helper rather than hardcoding `{specsDir}/{feature}/`. This makes flat (`{specsDir}/{feature}/`) and nested (`{specsDir}/{epic}/{feature}/`) layouts both resolve from a bare feature name (REQ-DIR-03), with standalone features behaving exactly as today.
|
|
79
82
|
|
|
80
83
|
```bash
|
|
81
|
-
R="$(for d in "$HOME"/.claude/skills/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)"
|
|
84
|
+
R="$(bash -c 'for d in "$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')"
|
|
82
85
|
[ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
|
|
83
86
|
resolvedFeatureDir=$(python3 "$R/scripts/epic-manifest.py" \
|
|
84
87
|
resolve "<feature>" --specs-dir "<specsDir>")
|
|
@@ -90,6 +93,19 @@ resolvedFeatureDir=$(python3 "$R/scripts/epic-manifest.py" \
|
|
|
90
93
|
|
|
91
94
|
In both failure cases, do not fall back to a guessed path.
|
|
92
95
|
|
|
96
|
+
**On `not-found`, check other branches before stopping.** With `branchPerFeature`, the feature's directory (and its `.pipeline-state.json`) may exist only on its topic branch — invisible from the default branch of a fresh clone. Before concluding the pipeline does not exist, run the read-only cross-branch discovery:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
R="$(bash -c 'for d in "$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')"
|
|
100
|
+
[ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
|
|
101
|
+
python3 "$R/scripts/forge-session.py" discover-feature "<feature>" --specs-dir "<specsDir>" --json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
- **Candidates found** (`candidates` and/or `remoteCandidates` non-empty): summarize them as text (branch, recorded stage, whether the state's own `branch` field matches), then use `AskUserQuestion`: **Switch to `{branch}` (recommended)** — run the candidate's `switchCommand` · **Fetch + switch** — for a `needsFetch` remote candidate, run its `fetchCommand` then `switchCommand` (note its contents were matched by name only, not inspected) · **Treat `{feature}` as new on this branch** · **Stop**. A checkout is a mutation inside an otherwise read-only flow: perform it ONLY on the user's explicit accept AND with a clean working tree (`git status --porcelain` prints nothing) — never auto-switch, never with uncommitted changes. After a successful switch, re-run this Feature Directory Resolution block from the top.
|
|
105
|
+
- **Nothing found** (both lists empty): the pipeline genuinely does not exist anywhere discoverable — STOP and surface the original `not-found` stderr line verbatim (or, where the caller offers to start a new pipeline, offer that).
|
|
106
|
+
|
|
107
|
+
**Anti-fabrication guard.** Never describe pipeline state that resolution or discovery did not return: if both come back empty, the pipeline does not exist — say exactly that, and never reconstruct stages, backlogs, or history from conversational memory.
|
|
108
|
+
|
|
93
109
|
**Resolution algorithm (summary; full spec in `02-manifest-helper-cli.md §4`):**
|
|
94
110
|
1. Reject the name if unsafe (path separator, `..`, absolute, or failing `SAFE_NAME_RE`) — before any filesystem access.
|
|
95
111
|
2. If `{specsDir}/{name}/.pipeline-state.json` exists → return that flat path.
|
|
@@ -108,7 +124,7 @@ Whenever a stage creates the specs tree for the first time (the first PRD or epi
|
|
|
108
124
|
Run this after creating the feature/epic directory, before the stage's git commit:
|
|
109
125
|
|
|
110
126
|
```bash
|
|
111
|
-
R="$(for d in "$HOME"/.claude/skills/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)"
|
|
127
|
+
R="$(bash -c 'for d in "$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')"
|
|
112
128
|
[ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
|
|
113
129
|
mkdir -p "<specsDir>"
|
|
114
130
|
[ -f "<specsDir>/AGENTS.md" ] || cp "$R/references/templates/specs-hygiene/AGENTS.md" "<specsDir>/AGENTS.md"
|
|
@@ -135,7 +151,7 @@ After resolving the feature directory, check the feature's `.pipeline-state.json
|
|
|
135
151
|
To obtain the manifest contracts and the live completion status of each dependency in one deterministic call, run `render-status` and read the per-feature `status` and the `consumes`/`exposes` arrays rather than re-deriving them:
|
|
136
152
|
|
|
137
153
|
```bash
|
|
138
|
-
R="$(for d in "$HOME"/.claude/skills/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)"
|
|
154
|
+
R="$(bash -c 'for d in "$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')"
|
|
139
155
|
[ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
|
|
140
156
|
python3 "$R/scripts/epic-manifest.py" \
|
|
141
157
|
render-status "<epic>" --specs-dir "<specsDir>" --json
|
|
@@ -180,16 +196,18 @@ Invoke this block at the **very start** of a pipeline entry point — `forge-1-p
|
|
|
180
196
|
|
|
181
197
|
## Git Commit Protocol
|
|
182
198
|
|
|
183
|
-
When `gitCommitAfterStage` is true, follow this exact order to avoid state inconsistency
|
|
199
|
+
When `gitCommitAfterStage` is true, follow this exact order to avoid state inconsistency.
|
|
200
|
+
|
|
201
|
+
**Why two commits.** The stage's `.pipeline-state.json` is itself part of the staged commit, but the stage's `commitHash` cannot be known until *after* that commit is made. Recording it *inside* the same commit is a chicken-and-egg with no single-commit solution. Resolve it with a **deterministic two-commit sequence**, and **never** with `git commit --amend`: amending rewrites HEAD, so a hash captured before the amend points at an orphaned commit that is not in the final history (the exact defect this protocol exists to prevent).
|
|
184
202
|
|
|
185
203
|
1. **Stage specific files only:** `git add {specsDir}/{feature}/` — never use `git add -A` or `git add .`
|
|
186
|
-
2. **
|
|
187
|
-
3. **If
|
|
188
|
-
4. **If
|
|
204
|
+
2. **Commit 1 — artifacts + state, hash not yet known:** In `.pipeline-state.json`, set this stage's `status: "complete"` and `commitHash: null`, then `git commit -m "{commitPrefix}({feature}): <action>"`. This is the stage's **artifact commit**; its hash is the provenance hash callers rely on.
|
|
205
|
+
3. **If Commit 1 succeeds — Commit 2 records the hash:** Capture the hash of Commit 1 (`git rev-parse HEAD`). Write it into this stage's `commitHash` in `.pipeline-state.json`, then commit only that one-line change: `git add {specsDir}/{feature}/.pipeline-state.json && git commit -m "{commitPrefix}({feature}): record stage commit hash"`. The stored `commitHash` now points at the artifact commit (Commit 1) — never at Commit 2, and never at an orphaned amend. The working tree is clean afterward, so the next stage's dirty-tree check passes.
|
|
206
|
+
4. **If Commit 1 fails:** do NOT update pipeline state to complete. Report the error to the user and leave state as `in-progress` so the stage can be resumed. Common failure causes:
|
|
189
207
|
- **Pre-commit hook failure:** Report the hook output. Never use `--no-verify` to bypass. Help the user fix the underlying issue.
|
|
190
208
|
- **Merge conflicts:** Report conflicting files. Suggest resolution steps appropriate to the conflict.
|
|
191
|
-
- **Nothing to commit:** If all artifacts were already committed, this is fine —
|
|
192
|
-
5. **Never** use `git add -A`, `--no-verify`, or `--force` flags
|
|
209
|
+
- **Nothing to commit:** If all artifacts were already committed, this is fine — mark the stage `complete`, leave `commitHash` at its existing value (or `null` if there was never an artifact commit), and skip Commit 2. There is no new artifact commit to record.
|
|
210
|
+
5. **Never** use `git add -A`, `--amend`, `--no-verify`, or `--force` flags
|
|
193
211
|
|
|
194
212
|
## Crash Recovery
|
|
195
213
|
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Stage Exit Protocol
|
|
2
|
+
|
|
3
|
+
The single source of truth for how every forge **authoring** stage closes. It
|
|
4
|
+
replaces the old ad-hoc "Next steps:" bullet lists with one fixed, correctly-ordered
|
|
5
|
+
sequence: **verify (if missing or stale) → `/clear` → run the next command.**
|
|
6
|
+
|
|
7
|
+
Two principles this protocol encodes (do not relitigate — they are locked product
|
|
8
|
+
decisions):
|
|
9
|
+
|
|
10
|
+
1. **Clearing is recommended on its own merits at every stage boundary** — a clean
|
|
11
|
+
start for the next stage — *not* as a proxy for a full context window. Window
|
|
12
|
+
fullness only changes *how emphatically* the clear is recommended, never *whether*
|
|
13
|
+
it is.
|
|
14
|
+
2. **Verify happens before the clear, never after** — in the authoring session, whether
|
|
15
|
+
manual **or** auto. Verify's clean-room subagent is dispatched from the *current*
|
|
16
|
+
session, so the findings digest and any fix decision land where the context to act on
|
|
17
|
+
them still exists. This holds for auto-verify too: the stage skill dispatches the
|
|
18
|
+
clean-room verify (and any autoFix) at stage end, in-session, before the exit — it is
|
|
19
|
+
**not** deferred to the navigator, which runs *after* the `/clear` with none of the
|
|
20
|
+
authoring context. Clearing first throws that context away.
|
|
21
|
+
|
|
22
|
+
## How this file is used
|
|
23
|
+
|
|
24
|
+
The five authoring stages (`forge-0-epic` … `forge-4-backlog`) close with the
|
|
25
|
+
**Scripted Stage Exit**: a short stamped block (below) that runs
|
|
26
|
+
`forge-session.py stage-exit`, obeys the DIRECTIVES it prints per the **directive
|
|
27
|
+
contract** in this file, and prints the script-emitted NEXT-STEPS block verbatim as the
|
|
28
|
+
absolute last output. All the conditional logic the old prose blocks asked the model to
|
|
29
|
+
compute (effective auto-verify, freshness collapse, gate selection, host wording) now
|
|
30
|
+
lives in the script, deterministically; only genuinely interactive work (clean-room
|
|
31
|
+
subagent dispatch, `AskUserQuestion` gates) remains prose — specified once here, not
|
|
32
|
+
per stage.
|
|
33
|
+
|
|
34
|
+
The loop (`forge-5-loop`) keeps its bespoke exits: it stamps the **standard block**
|
|
35
|
+
(step-6 epic-member handoff) and the **warm variant** (all-done closing) below,
|
|
36
|
+
verbatim. `forge-6-docs` is **terminal** — it stamps no exit block.
|
|
37
|
+
|
|
38
|
+
A drift-guard test (`tests/test_stage_exit_protocol.py`) asserts each stamp site still
|
|
39
|
+
contains its block, so an edit here must be mirrored into every stamp site (and
|
|
40
|
+
vice-versa).
|
|
41
|
+
|
|
42
|
+
## Stamp sites
|
|
43
|
+
|
|
44
|
+
| Stamp site | Block |
|
|
45
|
+
|---|---|
|
|
46
|
+
| `forge-0-epic` … `forge-4-backlog` | scripted-stage-exit stamp |
|
|
47
|
+
| `forge-5-loop` (step-6 epic-member handoff) | standard |
|
|
48
|
+
| `forge-5-loop` (all-done closing → docs) | warm |
|
|
49
|
+
|
|
50
|
+
The scripted stamp fills one build-time slot, `{stage-exit-args}` — the per-stage
|
|
51
|
+
argument list (e.g. `--feature "{feature}" --stage forge-2-tech`; the epic stage passes
|
|
52
|
+
`--feature "{epic}" --stage forge-0-epic --next-feature "{first-actionable-feature}"`).
|
|
53
|
+
`{feature}` / `{epic}` / `{specsDir}` / `{first-actionable-feature}` remain runtime
|
|
54
|
+
placeholders the skill resolves before running the command, exactly as elsewhere.
|
|
55
|
+
|
|
56
|
+
<!-- BEGIN: scripted-stage-exit-stamp -->
|
|
57
|
+
**Close this stage with the Scripted Stage Exit** (contract: `references/stage-exit-protocol.md`; do not improvise a "Next steps" list). Run:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
R="$(bash -c 'for d in "$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')"
|
|
61
|
+
[ -n "$R" ] || { echo "feature-forge: cannot locate plugin root" >&2; exit 1; }
|
|
62
|
+
python3 "$R/scripts/forge-session.py" stage-exit {stage-exit-args} --specs-dir "{specsDir}" --host claude
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Obey the DIRECTIVES it prints, in order, per the directive contract: `runInStageVerify: true` → dispatch the in-stage clean-room verify now (honoring `autoFixEligible`); `verifyGate: "standard"` → present the Standard Verify Gate; `verifyGate: "manual-print"` → print the `verifyCommand` for the user; non-empty `invalidAutoVerifyKeys` → print a one-line warning. Then **print the NEXT-STEPS block verbatim as your absolute last output — nothing after its sentinel line.**
|
|
66
|
+
<!-- END: scripted-stage-exit-stamp -->
|
|
67
|
+
|
|
68
|
+
## Directive contract
|
|
69
|
+
|
|
70
|
+
`stage-exit` emits a DIRECTIVES object and a NEXT-STEPS block. The skill executes the
|
|
71
|
+
directives **in this order**; the script has already computed every conditional, so a
|
|
72
|
+
directive is an instruction, not a question to re-derive.
|
|
73
|
+
|
|
74
|
+
### `invalidAutoVerifyKeys` (non-empty)
|
|
75
|
+
|
|
76
|
+
Print a one-line warning first (e.g. "⚠️ forge.config.json `autoVerifyStages` has
|
|
77
|
+
unknown keys: … — they are ignored; fix the typo").
|
|
78
|
+
|
|
79
|
+
### `runInStageVerify: true` — in-stage auto-verify {stageNoun}
|
|
80
|
+
|
|
81
|
+
Auto-verify is effective for this stage and verification is outstanding — verify **now,
|
|
82
|
+
in this session** (principle #2 applied to auto-verify: the digest and any fix decision
|
|
83
|
+
land here, where the authoring context still exists — not deferred to a post-`/clear`
|
|
84
|
+
navigator):
|
|
85
|
+
|
|
86
|
+
1. **Clean-room verify (require-clean).** Dispatch the clean-room `forge-verifier`
|
|
87
|
+
subagent from this session in require-clean mode — the same path the navigator uses
|
|
88
|
+
(`skills/forge-verify/SKILL.md`). It inherits none of this session's context, so no
|
|
89
|
+
`/clear` is needed and only a compact digest returns. **Clean-room unavailable** (no
|
|
90
|
+
`Agent` tool, `forge-verifier` not dispatchable, or a sentinel returned): do **not**
|
|
91
|
+
run inline — leave verify **pending** so the navigator catch-up fires on a later
|
|
92
|
+
Claude-host `/feature-forge:forge`, print the `verifyCommand` for the user to run,
|
|
93
|
+
and continue to the NEXT-STEPS block.
|
|
94
|
+
2. **Verify passed / no findings** → the fresh verify state is recorded by the
|
|
95
|
+
clean-room run; continue to the NEXT-STEPS block.
|
|
96
|
+
3. **Verify found findings** →
|
|
97
|
+
- **`autoFixEligible: true` AND the findings document has zero unresolved decision
|
|
98
|
+
points** → chain `feature-forge:forge-fix` in-session (it owns its own commit +
|
|
99
|
+
step tracking), then run a **mandatory re-verify** in require-clean mode. Continue
|
|
100
|
+
to the NEXT-STEPS block only if the re-verify passes. On any precondition miss, a
|
|
101
|
+
forge-fix early stop, or a red re-verify, fall through to the digest gate below —
|
|
102
|
+
never a silent partial mutation. (`autoFixEligible` already folds in the config
|
|
103
|
+
`autoFix` flag and the clean-tree precondition; a dirty tree or
|
|
104
|
+
`gitCommitAfterStage: false` arrives here as `false`.)
|
|
105
|
+
- **`autoFixEligible: false`, or unresolved decision points** → surface a **compact
|
|
106
|
+
findings digest** as text, then present the gate via `AskUserQuestion`: **Run
|
|
107
|
+
`forge-fix` now** *(recommended — you are in-context and the digest is right
|
|
108
|
+
here)* / **Clear + advance anyway** (leave the findings for later) / **Stop
|
|
109
|
+
here**. Do **not** hard-stop and do **not** silently walk past. Act on the choice,
|
|
110
|
+
then continue to the NEXT-STEPS block.
|
|
111
|
+
|
|
112
|
+
### `verifyGate: "standard"` — the Standard Verify Gate
|
|
113
|
+
|
|
114
|
+
Auto-verify is off for this stage and verification is outstanding (`verifyState` is
|
|
115
|
+
`never`, `stale`, or `failing`). Verify **now, before clearing**, using
|
|
116
|
+
`AskUserQuestion` with exactly these three options — but only when the host has a
|
|
117
|
+
question mechanism **and** the clean-room path is available (the `Agent` tool plus a
|
|
118
|
+
dispatchable `forge-verifier` subagent); otherwise degrade exactly as `manual-print`
|
|
119
|
+
below:
|
|
120
|
+
|
|
121
|
+
- **Verify {stageNoun} now** *(recommended)* — dispatch the clean-room `forge-verifier`
|
|
122
|
+
subagent from this session in require-clean mode; the digest returns here so any fix
|
|
123
|
+
decision keeps its context. One-time — it does **not** change config.
|
|
124
|
+
- **Verify now + enable auto-verify going forward** — verify now **and** patch
|
|
125
|
+
`"autoVerify": true` into `forge.config.json` in place (preserve formatting and every
|
|
126
|
+
other key) so future stages verify automatically, no prompt. This complements the
|
|
127
|
+
`forge-init` opt-in. **Do not auto-commit this config change** — treat it like
|
|
128
|
+
`notes`: a user-facing edit the user commits on their own cadence, never folded into
|
|
129
|
+
a stage's artifact commit.
|
|
130
|
+
- **Skip for now** — go straight to the NEXT-STEPS block without verifying. Record this
|
|
131
|
+
stage's verify status as `"skipped"` in pipeline state (mirroring the existing skip
|
|
132
|
+
handling) **only** on an explicit skip — a skip does not go stale.
|
|
133
|
+
|
|
134
|
+
If verify runs and finds findings, handle them exactly as in the in-stage flow above
|
|
135
|
+
(digest + `AskUserQuestion` gate; `autoFixEligible` applies unchanged).
|
|
136
|
+
|
|
137
|
+
### `verifyGate: "manual-print"`
|
|
138
|
+
|
|
139
|
+
Verification is outstanding but the host cannot present the gate or dispatch
|
|
140
|
+
clean-room. Do **not** run verify inline — print the `verifyCommand` for the user to
|
|
141
|
+
run (mirroring `autoInvokeNextStage`), offer the auto-verify enable as plain text only
|
|
142
|
+
if a config write is possible, and continue to the NEXT-STEPS block. Verify state stays
|
|
143
|
+
outstanding, so the navigator catch-up can fire later.
|
|
144
|
+
|
|
145
|
+
### `verifyGate: "none"`
|
|
146
|
+
|
|
147
|
+
Verification is already resolved (fresh or explicitly skipped) or the in-stage run
|
|
148
|
+
above covers it. Say so in one line and continue to the NEXT-STEPS block.
|
|
149
|
+
|
|
150
|
+
### The NEXT-STEPS block (always last)
|
|
151
|
+
|
|
152
|
+
Print the script's NEXT-STEPS block **verbatim as your absolute last output**. Nothing
|
|
153
|
+
follows its final sentinel line (`─ forge: end of stage ─`) — no caveats, no summary,
|
|
154
|
+
no sign-off. The block already carries the `/clear` recommendation (host-aware wording
|
|
155
|
+
via `--host`) and the exact next command, so trailing prose can only push the user's
|
|
156
|
+
next action out of view.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Standard block
|
|
161
|
+
|
|
162
|
+
Stamped at the loop's step-6 epic-member handoff (finishing feature A → starting
|
|
163
|
+
feature B's PRD). It self-adapts: step 1's verify gate only fires when verification is
|
|
164
|
+
actually outstanding, so at a boundary where verify already ran (or was explicitly
|
|
165
|
+
skipped, or auto-verify is on) it silently collapses to just the `/clear` →
|
|
166
|
+
next-command steps.
|
|
167
|
+
|
|
168
|
+
Slots: `{stage}` (a lowercase noun phrase), `{verify-command}`, `{next-command}`.
|
|
169
|
+
|
|
170
|
+
<!-- BEGIN: standard-exit-block -->
|
|
171
|
+
**This stage is done — walk the user through the Stage Exit Protocol** before moving on. The order is fixed, and step 2 is something only the user can do:
|
|
172
|
+
|
|
173
|
+
1. **Verify {stage} first — if it isn't already verified.** If verify already ran in this session — via the in-stage auto-verify on the authoring stages, or the interactive impl-verify offered above on the loop — or is already fresh on record, or the stage was explicitly skipped, say so and go straight to step 2. Only when `autoVerify` is off for this stage **and** verify is **missing or stale** do you present the **Standard Verify Gate**: verify **now, before clearing**, using `AskUserQuestion` with exactly these three options — but only when the host has a question mechanism **and** the clean-room path is available (the `Agent` tool plus a dispatchable `forge-verifier` subagent):
|
|
174
|
+
- **Verify {stage} now** *(recommended)* — dispatch the clean-room `forge-verifier` subagent from this session in require-clean mode; the digest returns here so any fix decision keeps its context. One-time — it does **not** change config.
|
|
175
|
+
- **Verify now + enable auto-verify going forward** — verify now **and** patch `"autoVerify": true` into `forge.config.json` in place (preserve formatting and every other key) so future stages verify automatically, no prompt. This complements the `forge-init` opt-in. **Do not auto-commit this config change** — treat it like `notes`: a user-facing edit the user commits on their own cadence, never folded into a stage's artifact commit.
|
|
176
|
+
- **Skip for now** — go straight to `/clear` and the next command without verifying. Record this stage's verify status as `"skipped"` in pipeline state (mirroring the existing skip handling) **only** on an explicit skip — a skip does not go stale.
|
|
177
|
+
|
|
178
|
+
**Host / clean-room fallback (not a user-selectable option):** if the question mechanism, the `Agent` tool, or the `forge-verifier` subagent is unavailable, do **not** run clean-room — degrade to printing `{verify-command}` for the user to run inline/manually (mirroring `autoInvokeNextStage`), and offer the auto-verify enable as plain text only if a config write is possible.
|
|
179
|
+
2. **Then `/clear`.** Recommended **unconditionally** at this boundary for a clean start — independent of how full the context window is. Every artifact is on disk, so the work survives the clear. **I can't `/clear` for you — you have to run it yourself.**
|
|
180
|
+
3. **Then run `{next-command}`** in the fresh session — or re-run `/feature-forge:forge` to let the navigator resume from disk.
|
|
181
|
+
<!-- END: standard-exit-block -->
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Warm-acceptable variant
|
|
186
|
+
|
|
187
|
+
Stamp this only at the `forge-5-loop → forge-6-docs` boundary (the all-done result
|
|
188
|
+
report). Here clearing is **optional**: the docs stage benefits from the still-warm
|
|
189
|
+
context of what the loop actually did, and impl-verify is already offered interactively
|
|
190
|
+
by the loop itself, so this block defers rather than re-presenting a gate.
|
|
191
|
+
|
|
192
|
+
> **Note — no literal `/clear` here.** The warm block lives in `result-reporting.md`, a
|
|
193
|
+
> skill-*own* reference that the adapter build copies **verbatim** (unlike skill bodies,
|
|
194
|
+
> it is not host-term translated), so a literal `/clear` would reach non-Claude adapters
|
|
195
|
+
> undegraded. The warm variant says "clearing is optional" anyway, so it is phrased
|
|
196
|
+
> host-neutrally without the token on purpose — do not reintroduce `/clear` here. (The
|
|
197
|
+
> standard block *does* use `/clear`; that is fine because every standard stamp site is a
|
|
198
|
+
> skill **body**, where `scripts/build-adapters.py` degrades it.)
|
|
199
|
+
|
|
200
|
+
<!-- BEGIN: warm-exit-block -->
|
|
201
|
+
**The loop is complete — this is the one boundary where clearing before the next stage is optional.**
|
|
202
|
+
|
|
203
|
+
1. **Verify is already offered above.** Impl-verify is offered interactively right after this report (Step 5b for a standalone feature, Step 6.1 for an epic member) — run it there rather than as a second gate. It runs clean-room, so it needs no fresh session.
|
|
204
|
+
2. **Clearing is optional here — warm is fine.** `forge-6-docs` benefits from the still-warm context of what the loop actually did, so continuing in this same session is the easy default. A cold start also works — every artifact is on disk — but there is no need to force it.
|
|
205
|
+
3. **Then run `{next-command}`** — in this warm session, or a fresh one if you prefer.
|
|
206
|
+
<!-- END: warm-exit-block -->
|
|
@@ -312,6 +312,16 @@ def atomic_write(path: Path, data: dict) -> None:
|
|
|
312
312
|
handle.flush()
|
|
313
313
|
os.fsync(handle.fileno())
|
|
314
314
|
os.replace(tmp_path, path)
|
|
315
|
+
# fsync the parent dir so the rename itself is durable on crash, not just
|
|
316
|
+
# the file bytes (best-effort — some filesystems reject O_RDONLY dir fsync).
|
|
317
|
+
try:
|
|
318
|
+
dir_fd = os.open(parent, os.O_RDONLY)
|
|
319
|
+
try:
|
|
320
|
+
os.fsync(dir_fd)
|
|
321
|
+
finally:
|
|
322
|
+
os.close(dir_fd)
|
|
323
|
+
except OSError:
|
|
324
|
+
pass
|
|
315
325
|
except OSError as exc:
|
|
316
326
|
tmp_path.unlink(missing_ok=True)
|
|
317
327
|
raise UsageError(f"atomic write to {path} failed: {exc}")
|