@garygentry/feature-forge 0.3.2 → 0.3.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 +1 -1
- package/adapters/claude/.feature-forge-bundle.json +1 -1
- package/adapters/claude/agents/forge-verifier.md +3 -1
- package/adapters/claude/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/claude/references/epic-state-schema.json +50 -0
- package/adapters/claude/references/forge-config-schema.json +20 -2
- package/adapters/claude/references/forge-decisions-schema.json +33 -0
- package/adapters/claude/references/pipeline-state-schema.json +34 -2
- package/adapters/claude/references/ralph-loop-contract.md +6 -3
- package/adapters/claude/references/shared-conventions.md +15 -4
- package/adapters/claude/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/scripts/epic-manifest.py +82 -4
- package/adapters/claude/scripts/fix-sweep.py +1180 -0
- package/adapters/claude/scripts/forge-session.py +1151 -32
- package/adapters/claude/skills/forge/SKILL.md +5 -5
- package/adapters/claude/skills/forge/references/pipeline-state-schema.json +34 -2
- package/adapters/claude/skills/forge/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-0-epic/references/edit-mode.md +5 -1
- package/adapters/claude/skills/forge-0-epic/references/epic-manifest-subcommands.md +5 -0
- package/adapters/claude/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
- package/adapters/claude/skills/forge-0-epic/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-0-epic/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-1-prd/SKILL.md +3 -1
- package/adapters/claude/skills/forge-1-prd/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-1-prd/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-2-tech/SKILL.md +5 -1
- package/adapters/claude/skills/forge-2-tech/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-2-tech/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-3-specs/SKILL.md +5 -1
- package/adapters/claude/skills/forge-3-specs/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-3-specs/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-4-backlog/SKILL.md +41 -3
- package/adapters/claude/skills/forge-4-backlog/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-4-backlog/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-5-loop/SKILL.md +36 -36
- package/adapters/claude/skills/forge-5-loop/references/agent-selection.md +16 -0
- package/adapters/claude/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
- package/adapters/claude/skills/forge-5-loop/references/recovery-procedure.md +349 -0
- package/adapters/claude/skills/forge-5-loop/references/result-reporting.md +40 -11
- package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +22 -4
- package/adapters/claude/skills/forge-5-loop/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-5-loop/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-6-docs/SKILL.md +29 -6
- package/adapters/claude/skills/forge-6-docs/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-6-docs/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-fix/SKILL.md +34 -0
- package/adapters/claude/skills/forge-fix/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-fix/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +20 -2
- package/adapters/claude/skills/forge-guide/references/ralph-loop-contract.md +6 -3
- package/adapters/claude/skills/forge-guide/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-verify/SKILL.md +10 -11
- package/adapters/claude/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/claude/skills/forge-verify/references/findings-template.md +30 -0
- package/adapters/claude/skills/forge-verify/references/shared-conventions.md +15 -4
- package/adapters/claude/skills/forge-verify/references/stage-exit-protocol.md +55 -11
- package/adapters/claude/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists/impl.md +85 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists/specs.md +43 -1
- package/adapters/codex/.feature-forge-bundle.json +1 -1
- package/adapters/codex/agents/forge-verifier.toml +3 -1
- package/adapters/codex/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/codex/references/epic-state-schema.json +50 -0
- package/adapters/codex/references/forge-config-schema.json +20 -2
- package/adapters/codex/references/forge-decisions-schema.json +33 -0
- package/adapters/codex/references/pipeline-state-schema.json +34 -2
- package/adapters/codex/references/process-overview.md +2 -2
- package/adapters/codex/references/ralph-loop-contract.md +6 -3
- package/adapters/codex/references/shared-conventions.md +44 -33
- package/adapters/codex/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/scripts/epic-manifest.py +82 -4
- package/adapters/codex/scripts/fix-sweep.py +1180 -0
- package/adapters/codex/scripts/forge-session.py +1151 -32
- package/adapters/codex/skills/forge/SKILL.md +8 -8
- package/adapters/codex/skills/forge/references/pipeline-state-schema.json +34 -2
- package/adapters/codex/skills/forge/references/process-overview.md +2 -2
- package/adapters/codex/skills/forge/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-0-epic/references/edit-mode.md +14 -10
- package/adapters/codex/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
- package/adapters/codex/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
- package/adapters/codex/skills/forge-0-epic/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-0-epic/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-1-prd/SKILL.md +3 -1
- package/adapters/codex/skills/forge-1-prd/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-1-prd/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-2-tech/SKILL.md +5 -1
- package/adapters/codex/skills/forge-2-tech/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-2-tech/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-3-specs/SKILL.md +5 -1
- package/adapters/codex/skills/forge-3-specs/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-3-specs/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-4-backlog/SKILL.md +41 -3
- package/adapters/codex/skills/forge-4-backlog/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-4-backlog/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-5-loop/SKILL.md +36 -36
- package/adapters/codex/skills/forge-5-loop/references/agent-selection.md +17 -1
- package/adapters/codex/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
- package/adapters/codex/skills/forge-5-loop/references/recovery-procedure.md +349 -0
- package/adapters/codex/skills/forge-5-loop/references/result-reporting.md +40 -11
- package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +26 -8
- package/adapters/codex/skills/forge-5-loop/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-5-loop/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-6-docs/SKILL.md +29 -6
- package/adapters/codex/skills/forge-6-docs/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-6-docs/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-fix/SKILL.md +34 -0
- package/adapters/codex/skills/forge-fix/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-fix/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-guide/SKILL.md +1 -1
- package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +20 -2
- package/adapters/codex/skills/forge-guide/references/process-overview.md +2 -2
- package/adapters/codex/skills/forge-guide/references/ralph-loop-contract.md +6 -3
- package/adapters/codex/skills/forge-guide/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-init/SKILL.md +1 -1
- package/adapters/codex/skills/forge-verify/SKILL.md +11 -12
- package/adapters/codex/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/codex/skills/forge-verify/references/findings-template.md +32 -2
- package/adapters/codex/skills/forge-verify/references/shared-conventions.md +44 -33
- package/adapters/codex/skills/forge-verify/references/stage-exit-protocol.md +64 -20
- package/adapters/codex/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists/epic.md +1 -1
- package/adapters/codex/skills/forge-verify/references/verification-checklists/impl.md +85 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists/specs.md +43 -1
- package/adapters/copilot/.feature-forge-bundle.json +1 -1
- package/adapters/copilot/agents/forge-verifier.md +3 -1
- package/adapters/copilot/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/copilot/references/epic-state-schema.json +50 -0
- package/adapters/copilot/references/forge-config-schema.json +20 -2
- package/adapters/copilot/references/forge-decisions-schema.json +33 -0
- package/adapters/copilot/references/pipeline-state-schema.json +34 -2
- package/adapters/copilot/references/process-overview.md +2 -2
- package/adapters/copilot/references/ralph-loop-contract.md +6 -3
- package/adapters/copilot/references/shared-conventions.md +44 -33
- package/adapters/copilot/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/scripts/epic-manifest.py +82 -4
- package/adapters/copilot/scripts/fix-sweep.py +1180 -0
- package/adapters/copilot/scripts/forge-session.py +1151 -32
- package/adapters/copilot/skills/forge/forge.md +8 -8
- package/adapters/copilot/skills/forge/references/pipeline-state-schema.json +34 -2
- package/adapters/copilot/skills/forge/references/process-overview.md +2 -2
- package/adapters/copilot/skills/forge/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-0-epic/references/edit-mode.md +14 -10
- package/adapters/copilot/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
- package/adapters/copilot/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
- package/adapters/copilot/skills/forge-0-epic/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-0-epic/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +3 -1
- package/adapters/copilot/skills/forge-1-prd/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-1-prd/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +5 -1
- package/adapters/copilot/skills/forge-2-tech/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-2-tech/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +5 -1
- package/adapters/copilot/skills/forge-3-specs/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-3-specs/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +41 -3
- package/adapters/copilot/skills/forge-4-backlog/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-4-backlog/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +36 -36
- package/adapters/copilot/skills/forge-5-loop/references/agent-selection.md +17 -1
- package/adapters/copilot/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
- package/adapters/copilot/skills/forge-5-loop/references/recovery-procedure.md +349 -0
- package/adapters/copilot/skills/forge-5-loop/references/result-reporting.md +40 -11
- package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +26 -8
- package/adapters/copilot/skills/forge-5-loop/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-5-loop/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-6-docs/forge-6-docs.md +29 -6
- package/adapters/copilot/skills/forge-6-docs/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-6-docs/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-fix/forge-fix.md +34 -0
- package/adapters/copilot/skills/forge-fix/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-fix/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-guide/forge-guide.md +1 -1
- package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +20 -2
- package/adapters/copilot/skills/forge-guide/references/process-overview.md +2 -2
- package/adapters/copilot/skills/forge-guide/references/ralph-loop-contract.md +6 -3
- package/adapters/copilot/skills/forge-guide/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-init/forge-init.md +1 -1
- package/adapters/copilot/skills/forge-verify/forge-verify.md +11 -12
- package/adapters/copilot/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/copilot/skills/forge-verify/references/findings-template.md +32 -2
- package/adapters/copilot/skills/forge-verify/references/shared-conventions.md +44 -33
- package/adapters/copilot/skills/forge-verify/references/stage-exit-protocol.md +64 -20
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/epic.md +1 -1
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/impl.md +85 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists/specs.md +43 -1
- package/adapters/cursor/.feature-forge-bundle.json +1 -1
- package/adapters/cursor/agents/forge-verifier.mdc +3 -1
- package/adapters/cursor/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/cursor/references/epic-state-schema.json +50 -0
- package/adapters/cursor/references/forge-config-schema.json +20 -2
- package/adapters/cursor/references/forge-decisions-schema.json +33 -0
- package/adapters/cursor/references/pipeline-state-schema.json +34 -2
- package/adapters/cursor/references/process-overview.md +2 -2
- package/adapters/cursor/references/ralph-loop-contract.md +6 -3
- package/adapters/cursor/references/shared-conventions.md +44 -33
- package/adapters/cursor/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/scripts/epic-manifest.py +82 -4
- package/adapters/cursor/scripts/fix-sweep.py +1180 -0
- package/adapters/cursor/scripts/forge-session.py +1151 -32
- package/adapters/cursor/skills/forge/forge.mdc +8 -8
- package/adapters/cursor/skills/forge/references/pipeline-state-schema.json +34 -2
- package/adapters/cursor/skills/forge/references/process-overview.md +2 -2
- package/adapters/cursor/skills/forge/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-0-epic/references/edit-mode.md +14 -10
- package/adapters/cursor/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
- package/adapters/cursor/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
- package/adapters/cursor/skills/forge-0-epic/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-0-epic/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +3 -1
- package/adapters/cursor/skills/forge-1-prd/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-1-prd/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +5 -1
- package/adapters/cursor/skills/forge-2-tech/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-2-tech/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +5 -1
- package/adapters/cursor/skills/forge-3-specs/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-3-specs/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +41 -3
- package/adapters/cursor/skills/forge-4-backlog/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-4-backlog/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +36 -36
- package/adapters/cursor/skills/forge-5-loop/references/agent-selection.md +17 -1
- package/adapters/cursor/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
- package/adapters/cursor/skills/forge-5-loop/references/recovery-procedure.md +349 -0
- package/adapters/cursor/skills/forge-5-loop/references/result-reporting.md +40 -11
- package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +26 -8
- package/adapters/cursor/skills/forge-5-loop/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-5-loop/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-6-docs/forge-6-docs.mdc +29 -6
- package/adapters/cursor/skills/forge-6-docs/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-6-docs/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-fix/forge-fix.mdc +34 -0
- package/adapters/cursor/skills/forge-fix/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-fix/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-guide/forge-guide.mdc +1 -1
- package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +20 -2
- package/adapters/cursor/skills/forge-guide/references/process-overview.md +2 -2
- package/adapters/cursor/skills/forge-guide/references/ralph-loop-contract.md +6 -3
- package/adapters/cursor/skills/forge-guide/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-init/forge-init.mdc +1 -1
- package/adapters/cursor/skills/forge-verify/forge-verify.mdc +11 -12
- package/adapters/cursor/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/cursor/skills/forge-verify/references/findings-template.md +32 -2
- package/adapters/cursor/skills/forge-verify/references/shared-conventions.md +44 -33
- package/adapters/cursor/skills/forge-verify/references/stage-exit-protocol.md +64 -20
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/epic.md +1 -1
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/impl.md +85 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists/specs.md +43 -1
- package/adapters/gemini/.feature-forge-bundle.json +1 -1
- package/adapters/gemini/agents/forge-verifier.md +3 -1
- package/adapters/gemini/gemini-extension.json +1 -1
- package/adapters/gemini/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/gemini/references/epic-state-schema.json +50 -0
- package/adapters/gemini/references/forge-config-schema.json +20 -2
- package/adapters/gemini/references/forge-decisions-schema.json +33 -0
- package/adapters/gemini/references/pipeline-state-schema.json +34 -2
- package/adapters/gemini/references/process-overview.md +2 -2
- package/adapters/gemini/references/ralph-loop-contract.md +6 -3
- package/adapters/gemini/references/shared-conventions.md +44 -33
- package/adapters/gemini/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/scripts/epic-manifest.py +82 -4
- package/adapters/gemini/scripts/fix-sweep.py +1180 -0
- package/adapters/gemini/scripts/forge-session.py +1151 -32
- package/adapters/gemini/skills/forge/forge.md +8 -8
- package/adapters/gemini/skills/forge/references/pipeline-state-schema.json +34 -2
- package/adapters/gemini/skills/forge/references/process-overview.md +2 -2
- package/adapters/gemini/skills/forge/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-0-epic/references/edit-mode.md +14 -10
- package/adapters/gemini/skills/forge-0-epic/references/epic-manifest-subcommands.md +8 -3
- package/adapters/gemini/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
- package/adapters/gemini/skills/forge-0-epic/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-0-epic/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +3 -1
- package/adapters/gemini/skills/forge-1-prd/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-1-prd/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +5 -1
- package/adapters/gemini/skills/forge-2-tech/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-2-tech/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +5 -1
- package/adapters/gemini/skills/forge-3-specs/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-3-specs/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +41 -3
- package/adapters/gemini/skills/forge-4-backlog/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-4-backlog/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +36 -36
- package/adapters/gemini/skills/forge-5-loop/references/agent-selection.md +17 -1
- package/adapters/gemini/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
- package/adapters/gemini/skills/forge-5-loop/references/recovery-procedure.md +349 -0
- package/adapters/gemini/skills/forge-5-loop/references/result-reporting.md +40 -11
- package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +26 -8
- package/adapters/gemini/skills/forge-5-loop/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-5-loop/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-6-docs/forge-6-docs.md +29 -6
- package/adapters/gemini/skills/forge-6-docs/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-6-docs/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-fix/forge-fix.md +34 -0
- package/adapters/gemini/skills/forge-fix/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-fix/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-guide/forge-guide.md +1 -1
- package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +20 -2
- package/adapters/gemini/skills/forge-guide/references/process-overview.md +2 -2
- package/adapters/gemini/skills/forge-guide/references/ralph-loop-contract.md +6 -3
- package/adapters/gemini/skills/forge-guide/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-init/forge-init.md +1 -1
- package/adapters/gemini/skills/forge-verify/forge-verify.md +11 -12
- package/adapters/gemini/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/gemini/skills/forge-verify/references/findings-template.md +32 -2
- package/adapters/gemini/skills/forge-verify/references/shared-conventions.md +44 -33
- package/adapters/gemini/skills/forge-verify/references/stage-exit-protocol.md +64 -20
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/epic.md +1 -1
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/impl.md +85 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists/specs.md +43 -1
- package/adapters/pi/.feature-forge-bundle.json +1 -1
- package/adapters/pi/agents/forge-verifier.md +3 -1
- package/adapters/pi/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/pi/references/epic-state-schema.json +50 -0
- package/adapters/pi/references/forge-config-schema.json +20 -2
- package/adapters/pi/references/forge-decisions-schema.json +33 -0
- package/adapters/pi/references/pipeline-state-schema.json +34 -2
- package/adapters/pi/references/process-overview.md +2 -2
- package/adapters/pi/references/ralph-loop-contract.md +6 -3
- package/adapters/pi/references/shared-conventions.md +33 -22
- package/adapters/pi/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/scripts/epic-manifest.py +82 -4
- package/adapters/pi/scripts/fix-sweep.py +1180 -0
- package/adapters/pi/scripts/forge-session.py +1151 -32
- package/adapters/pi/skills/forge/SKILL.md +5 -5
- package/adapters/pi/skills/forge/references/pipeline-state-schema.json +34 -2
- package/adapters/pi/skills/forge/references/process-overview.md +2 -2
- package/adapters/pi/skills/forge/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-0-epic/references/edit-mode.md +8 -4
- package/adapters/pi/skills/forge-0-epic/references/epic-manifest-subcommands.md +6 -1
- package/adapters/pi/skills/forge-0-epic/references/pipeline-state-schema.json +34 -2
- package/adapters/pi/skills/forge-0-epic/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-0-epic/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-1-prd/SKILL.md +3 -1
- package/adapters/pi/skills/forge-1-prd/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-1-prd/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-2-tech/SKILL.md +5 -1
- package/adapters/pi/skills/forge-2-tech/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-2-tech/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-3-specs/SKILL.md +5 -1
- package/adapters/pi/skills/forge-3-specs/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-3-specs/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-4-backlog/SKILL.md +41 -3
- package/adapters/pi/skills/forge-4-backlog/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-4-backlog/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-5-loop/SKILL.md +36 -36
- package/adapters/pi/skills/forge-5-loop/references/agent-selection.md +16 -0
- package/adapters/pi/skills/forge-5-loop/references/ralph-loop-contract.md +6 -3
- package/adapters/pi/skills/forge-5-loop/references/recovery-procedure.md +349 -0
- package/adapters/pi/skills/forge-5-loop/references/result-reporting.md +40 -11
- package/adapters/pi/skills/forge-5-loop/references/runner-contract.md +25 -7
- package/adapters/pi/skills/forge-5-loop/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-5-loop/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-6-docs/SKILL.md +29 -6
- package/adapters/pi/skills/forge-6-docs/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-6-docs/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-fix/SKILL.md +34 -0
- package/adapters/pi/skills/forge-fix/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-fix/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-guide/references/forge-config-schema.json +20 -2
- package/adapters/pi/skills/forge-guide/references/process-overview.md +2 -2
- package/adapters/pi/skills/forge-guide/references/ralph-loop-contract.md +6 -3
- package/adapters/pi/skills/forge-guide/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-verify/SKILL.md +10 -11
- package/adapters/pi/skills/forge-verify/references/decisions/single-writer-threat-model.md +53 -0
- package/adapters/pi/skills/forge-verify/references/findings-template.md +31 -1
- package/adapters/pi/skills/forge-verify/references/shared-conventions.md +33 -22
- package/adapters/pi/skills/forge-verify/references/stage-exit-protocol.md +63 -19
- package/adapters/pi/skills/forge-verify/references/verification-checklists/backlog.md +73 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists/epic.md +1 -1
- package/adapters/pi/skills/forge-verify/references/verification-checklists/impl.md +85 -0
- package/adapters/pi/skills/forge-verify/references/verification-checklists/specs.md +43 -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
|
@@ -9,7 +9,7 @@ alwaysApply: false
|
|
|
9
9
|
|
|
10
10
|
Execute the autonomous coding loop against a forge feature's backlog. The loop spawns a fresh agent session per backlog item, implementing each task with full verification.
|
|
11
11
|
|
|
12
|
-
The loop **runner** is configured, not hardcoded. feature-forge talks to it through the `loopRunner` block in `forge.config.json`; rauf is the default and reference implementation (see `references/ralph-loop-contract.md`). Every command below is rendered from `loopRunner` with token substitution —
|
|
12
|
+
The loop **runner** is configured, not hardcoded. feature-forge talks to it through the `loopRunner` block in `forge.config.json`; rauf is the default and reference implementation (see `references/ralph-loop-contract.md`). Every command below is rendered from `loopRunner` with token substitution — no hardcoded `rauf …` commands; even the human log filename is tokenized (`{loopRunner.logFile}`).
|
|
13
13
|
|
|
14
14
|
## Resolve the loop runner
|
|
15
15
|
|
|
@@ -42,13 +42,15 @@ Read and follow `references/shared-conventions.md` for feature name validation,
|
|
|
42
42
|
|
|
43
43
|
Read `{resolvedFeatureDir}/.pipeline-state.json`. If not in force mode, `stages.forge-4-backlog` must be `complete`. If not, STOP and tell the user: "Backlog hasn't been created yet. Run `/feature-forge:forge-4-backlog {feature}` first."
|
|
44
44
|
|
|
45
|
+
If the state's `notes` is non-empty, surface it before proceeding and treat it as run input — often backlog-time constraints; it never overrides specs or config (raise any conflict).
|
|
46
|
+
|
|
45
47
|
### 1b. Verification Check
|
|
46
48
|
|
|
47
49
|
Read `stages.forge-verify-backlog` and branch on its status — **four** cases, in this order (the pending case must be tested *before* the generic one, or owed-and-dropped debt gets reported as never-scheduled):
|
|
48
50
|
|
|
49
51
|
1. **`passed`** — proceed with no prompt.
|
|
50
|
-
2. **`findings-applied`** — fixes were applied but nothing re-verified them: this status deliberately clears freshness, so the backlog's verification is still outstanding, not silently satisfied. Use the host's question mechanism to offer: **Re-verify first (recommended)** (`/feature-forge:forge-verify {feature} backlog`) · **Continue without re-verifying
|
|
51
|
-
3. **`auto-verify-pending`** — automatic verification *was* scheduled for the backlog stage and the debt *was* durably recorded; it simply has not run. Say exactly that, naming the served stage and the retry command: *"{feature}: automatic verification is still pending for forge-4-backlog; run `/feature-forge:forge-verify {feature} backlog` to resolve it."* Then use the host's question mechanism to offer the same two choices as case 4. Never report this as "hasn't been verified yet" — "nobody ever asked for this" and "this was owed and dropped" are different facts
|
|
52
|
+
2. **`findings-applied`** — fixes were applied but nothing re-verified them: this status deliberately clears freshness, so the backlog's verification is still outstanding, not silently satisfied. Use the host's question mechanism to offer: **Re-verify first (recommended)** (`/feature-forge:forge-verify {feature} backlog`) · **Continue without re-verifying**. On continue, write **nothing**: `state-verify` refuses demoting `findings-applied` to `skipped` (#203); the recorded status already says re-verification is outstanding.
|
|
53
|
+
3. **`auto-verify-pending`** — automatic verification *was* scheduled for the backlog stage and the debt *was* durably recorded; it simply has not run. Say exactly that, naming the served stage and the retry command: *"{feature}: automatic verification is still pending for forge-4-backlog; run `/feature-forge:forge-verify {feature} backlog` to resolve it."* Then use the host's question mechanism to offer the same two choices as case 4. Never report this as "hasn't been verified yet" — "nobody ever asked for this" and "this was owed and dropped" are different facts.
|
|
52
54
|
4. **Anything else** (absent, `pending`, `skipped`, `findings-reported`) — use the host's question mechanism to warn with the cost of skipping: "Backlog hasn't been verified yet. Recommended: run `/feature-forge:forge-verify {feature}` first — the loop implements items autonomously and commits as it goes, so a bad item (wrong scope, missing dependency, untestable acceptance criteria) is far cheaper to catch now than after several commits build on it. Continue anyway?"
|
|
53
55
|
|
|
54
56
|
Cases 3 and 4 offer the same choices: **Verify first (recommended)** · **Continue without verifying**. The proceed-anyway path is unchanged.
|
|
@@ -115,13 +117,17 @@ Verify the file exists on disk. If not, STOP and tell the user: "No backlog.json
|
|
|
115
117
|
|
|
116
118
|
The runner commits each item onto the current branch. Skip if not a git repo or `branchPerFeature` is false. Otherwise run the **Branch Reconciliation** block in `references/shared-conventions.md` (it runs `reconcile-branch` and, on `warn-drift` — you are on the default branch — strongly recommends creating `{branchPrefix}{feature}` via the host's question mechanism before the loop commits; on `adopt-current` it updates the recorded branch to the current one, never pushing you back to a stale/imposed branch). Never hard-stop.
|
|
117
119
|
|
|
120
|
+
### 1g. Stranded-Work Pre-flight (if using git)
|
|
121
|
+
|
|
122
|
+
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. rauf's own launch refusal remains the backstop.
|
|
123
|
+
|
|
118
124
|
## Step 2: Construct the Loop Command
|
|
119
125
|
|
|
120
126
|
### 2a. Analyze Backlog
|
|
121
127
|
|
|
122
|
-
Run the **list command** (`loopRunner.listCommand`, default `rauf backlog list . --backlog {backlogDir} --json`) and count items by status: `pending`, `in_progress`, `done`, `blocked`.
|
|
128
|
+
Run the **list command** (`loopRunner.listCommand`, default `rauf backlog list . --backlog {backlogDir} --json`) and count items by status: `pending`, `in_progress`, `done`, `blocked`. Pipe that same list-command JSON into `backlog-topology --items-stdin --json` (a `forge-session.py` verb — invoke it via Step 3a's `$R` fence) and read `maxChainDepth` to report alongside the iteration count — advisory only: no prompt, no operator decision.
|
|
123
129
|
|
|
124
|
-
Calculate the iteration count: `ceil((pending + in_progress) * loopIterationMultiplier)` where `loopIterationMultiplier` comes from `forge.config.json` (default: 1.5
|
|
130
|
+
Calculate the iteration count: `ceil((pending + in_progress) * loopIterationMultiplier)` where `loopIterationMultiplier` comes from `forge.config.json` (default: 1.5, headroom for retries).
|
|
125
131
|
|
|
126
132
|
If there are no pending or in_progress items, STOP and tell the user: "All backlog items are already done or blocked. Nothing to run."
|
|
127
133
|
|
|
@@ -134,8 +140,6 @@ If there are `blocked` items, note them — the user may want `--retry-blocked`.
|
|
|
134
140
|
- If `backlogDir` is set in config: use the per-feature subpath `{backlogDir}/{feature}` (matching the 1e composition rule and forge-4-backlog §6.2).
|
|
135
141
|
- Otherwise: use `{resolvedFeatureDir}` (the directory containing `backlog.json`).
|
|
136
142
|
|
|
137
|
-
**Example:** If `specsDir` is `./specs` and feature is `auth`, `{backlogDir}` is `specs/auth`.
|
|
138
|
-
|
|
139
143
|
### 2c. Build Command
|
|
140
144
|
|
|
141
145
|
Render the **run command** (`loopRunner.runCommand`) with token substitution, e.g. the rauf default becomes:
|
|
@@ -159,19 +163,20 @@ Backlog summary:
|
|
|
159
163
|
- Done: {done}
|
|
160
164
|
- Blocked: {blocked}
|
|
161
165
|
- Iterations: {iterationCount} ({activeItems} items x {loopIterationMultiplier} multiplier)
|
|
166
|
+
- Max chain depth: {maxChainDepth} — depth bounds achievable progress regardless of iteration budget
|
|
162
167
|
|
|
163
168
|
For the model-selection precedence (item.model > --model/options > project default >
|
|
164
169
|
provider default), read references/runner-contract.md.
|
|
165
170
|
```
|
|
166
171
|
|
|
167
|
-
**Run mode and full loop-runner contract:** follow `## Run mode (Step 2d, rauf)` and the remaining sections in `references/runner-contract.md` verbatim.
|
|
172
|
+
**Run mode and full loop-runner contract:** follow `## Run mode (Step 2d, rauf)` and the remaining sections in `references/runner-contract.md` verbatim; `loopRunner.reviewMode` (`"always"`/`"never"`) suppresses the Run-mode question — semantics live there.
|
|
168
173
|
|
|
169
174
|
#### Agent selection (gated on `loopRunner.agentArgument`)
|
|
170
175
|
|
|
171
176
|
**Capability gate.** Everything below applies **only when** the effective `loopRunner.agentArgument` is present and non-empty. **When it is absent or empty, Step 2d is exactly the confirmation above — no probe, no agent question, no availability listing, no `Agent:` line — byte-identical to today** (REQ-PLUG-02, REQ-COMPAT-01). The full algorithm, precedence, and verbatim message shapes are in `## Agent selection` of `references/agent-selection.md`; read it. When the gate is on, augment Step 2d in order:
|
|
172
177
|
|
|
173
178
|
- **(a) Probe once.** Before confirming, run `loopRunner.agentsProbeCommand` (default `{bin} agents --json`) **exactly once** (no retries, no second probe); it exits 0 with `{ agents: [...] }`. Parse `agents[]`; build the advertised set `{ row.id }` — this one parsed array drives (b)–(d).
|
|
174
|
-
- **(b) Agent question.** Add an **"agent"** question to the same the host's question mechanism surface: **one option per advertised row** labelled `"{displayName} ({id}) — available/not found"`, **plus an explicit `"default (claude-cli)"` choice mapping to `run_selection = None`**. Resolve the pick (run > project, empty/whitespace unset, an explicit runner-default pick collapses to the default path) into `{resolved.agent, resolved.source}`. Precedence: `item.provider > --agent > project defaultAgent > runner default` (forge never reads a backlog item's provider).
|
|
179
|
+
- **(b) Agent question.** Add an **"agent"** question to the same the host's question mechanism surface: **one option per advertised row** labelled `"{displayName} ({id}) — available/not found"`, **plus an explicit `"default (claude-cli)"` choice mapping to `run_selection = None`**. Resolve the pick (run > project, empty/whitespace unset, an explicit runner-default pick collapses to the default path) into `{resolved.agent, resolved.source}`. Precedence: `item.provider > --agent > project defaultAgent > runner default` (forge never reads a backlog item's provider). Under `loopRunner.agentMode: "auto"`, skip this question (`run_selection = None`); (a)/(c)/(d)/(d-model) still run — see `references/agent-selection.md`.
|
|
175
180
|
- **(c) Availability listing.** From the **same** parsed `agents[]` (no second probe), list `id` / `displayName` / available (`yes`/`no`, `detail` on unavailable rows).
|
|
176
181
|
- **(d) Verdict** — only for a **non-default** resolved agent (default path `None`/`claude-cli` → no probe, byte-identical to today). Classify by **membership** then `available` (never by exit code): **UNKNOWN** (`∉` set) → **hard-reject BEFORE any loop side-effect**, error lists the **sorted** valid ids, **NO proceed-anyway**; **UNAVAILABLE** (member, `available False`) → warn with `detail`, the host's question mechanism offering **proceed-anyway OR choose-another** (re-presents the same `agents[]`), never silent; **AVAILABLE** → proceed, the validated id fills `{agent}`; **probe failure** (non-zero exit / unparseable / missing or empty `agents[]` / row lacking `id`) → surface it, offer **choose-another OR abort**, **never launch the non-default agent unvalidated** and never silently fall back to the default.
|
|
177
182
|
- **(d-model) Claude-only model-alias guard.** Runs **only** when the resolved agent is **non-default** (not the default / `claude-cli` path). Read the backlog.json (Step 1e path); collect items whose `model` is a **Claude-specific alias** (tier `opus`/`sonnet`/`haiku` or a `claude-*` id). **If none, skip silently.** Otherwise warn before launch via the host's question mechanism (NOT prose): `item.model` outranks `--agent`, so the alias is forwarded verbatim to `{agent}`, which will likely reject it (e.g. codex 400 *"The 'sonnet' model is not supported…"*) — every spawn exits 1 and rauf circuit-breaks (*"3 consecutive infra failures — halting"*) with no hint of the cause. Offer: **(1) Strip `model` for this run (recommended)** — rewrite backlog.json removing the `model` key from each affected item (persistent edit; re-run forge-4-backlog to restore), then proceed; **(2) Proceed as-is** — only safe if `{agent}` understands the pinned ids. forge touches only `model`, never `provider`. Full rationale: `references/agent-selection.md`.
|
|
@@ -194,7 +199,7 @@ Then commit this state write before launching (mandatory). The runner refuses to
|
|
|
194
199
|
|
|
195
200
|
### 3b. Launch Background Process
|
|
196
201
|
|
|
197
|
-
Launch the loop **backgrounded** (the host's background-execution mechanism) 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 and rotates it per run), launch the **plain `runCommand`** with **no stdout redirect** and supervise the runner's **native** `events.ndjson` directly; do **not** redirect `--ndjson` into `{stateDir}` (it is redundant and collides with the runner's own writer — see `references/runner-contract.md`). 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).
|
|
202
|
+
Launch the loop **backgrounded** (the host's background-execution mechanism) 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 and rotates it per run), launch the **plain `runCommand`** with **no stdout redirect** and supervise the runner's **native** `events.ndjson` directly; do **not** redirect `--ndjson` into `{stateDir}` (it is redundant and collides with the runner's own writer — see `references/runner-contract.md`). 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`.
|
|
198
203
|
|
|
199
204
|
### 3c. Inform User
|
|
200
205
|
|
|
@@ -203,21 +208,12 @@ Follow the **Inform-user output template (Step 3c)** section of `references/runn
|
|
|
203
208
|
### 3d. Arm a Monitor on the event stream, and react to events
|
|
204
209
|
|
|
205
210
|
Arm the **host's monitoring mechanism** on the structured event stream (the NDJSON file, or the
|
|
206
|
-
human log as fallback)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
Each Monitor event arrives as a message; react per type — surface `needs_human` /
|
|
213
|
-
`loop_error` immediately with a `PushNotification`, coalesce `item_completed` into
|
|
214
|
-
milestones, and treat `llm_stuck_warning` as a hang warning. A `needs_human` /
|
|
215
|
-
`blocked` signal does **not** pause the loop — the runner sets the item aside and
|
|
216
|
-
keeps going.
|
|
217
|
-
|
|
218
|
-
For the exact Monitor commands (NDJSON `jq` filter and the log-fallback `grep`
|
|
219
|
-
prefixes), the coverage-complete filter event list, and the full per-event reaction
|
|
220
|
-
rules, read `references/runner-contract.md`.
|
|
211
|
+
human log as fallback) with **`persistent: true`**, a coverage-complete filter
|
|
212
|
+
matching every terminal and exception state (silence is not success), and react to
|
|
213
|
+
each event as it arrives. The exact Monitor commands, the filter event list, and the
|
|
214
|
+
full per-event reaction rules (`needs_human` / `loop_error` surfaced immediately with
|
|
215
|
+
a `PushNotification`, `item_completed` coalesced into milestones, `llm_stuck_warning`
|
|
216
|
+
as a hang warning) are in `references/runner-contract.md` — follow them verbatim.
|
|
221
217
|
|
|
222
218
|
### 3f. Reach completion
|
|
223
219
|
|
|
@@ -233,12 +229,16 @@ Run the **status-json command** (`loopRunner.statusJsonCommand`) and read
|
|
|
233
229
|
`backlogSummary` for the authoritative counts — it separates the three non-done
|
|
234
230
|
outcomes: genuine `blocked`, `needsHuman`, and runner-`deferred` ("false blocks").
|
|
235
231
|
Fall back to the **list command** (`loopRunner.listCommand`) if `statusJsonCommand`
|
|
236
|
-
is not configured.
|
|
232
|
+
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`.
|
|
237
233
|
|
|
238
234
|
### 4b. Report Results
|
|
239
235
|
|
|
240
236
|
Present a summary to the user. Pick **every** branch that applies (a run can be both
|
|
241
|
-
blocked and needs-human) and render its report. The five verbatim result-report output templates — **all-done**, **needs-human**, **blocked**, **deferred**, and **pending** (
|
|
237
|
+
blocked and needs-human) and render its report. The five verbatim result-report output templates — **all-done**, **needs-human**, **blocked**, **deferred**, and **pending** (with a conditional cause) — are in `references/result-reporting.md`, together with the Step 7 `LoopOutcome` ladder these same counts feed. The reports are descriptive only: they carry no next command, and the run does not end here. If the authoritative counts cannot be obtained at all (4a failed or its output does not parse), follow **Operational failure before the counts are known** in that same file: surface the failure and its recovery, and close nothing — no outcome, no stage exit, no terminal block.
|
|
238
|
+
|
|
239
|
+
### 4c. Post-Run Recovery Pass (unconditional)
|
|
240
|
+
|
|
241
|
+
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) collects answers early but is **not** the entry condition: a run that emitted no event still enters here — this is what makes the plain-blocked unblock reachable on blocked-only runs. With nothing to decide, its step 1 skips straight to the §4 tree reconciliation — silent on a clean tree. A run can strand uncommitted work with no signal (items failing a shared final acceptance criterion are never committed); this pass reconciles it — 1g's pre-flight 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.
|
|
242
242
|
|
|
243
243
|
## Step 5: Update Pipeline State
|
|
244
244
|
|
|
@@ -254,9 +254,9 @@ python3 "$R/scripts/forge-session.py" state-complete --feature "{feature}" --sta
|
|
|
254
254
|
|
|
255
255
|
## Step 5b: Offer Impl-Verify (standalone path)
|
|
256
256
|
|
|
257
|
-
**Gate:** run only if (a) the feature's `.pipeline-state.json` has **no** `epic` key **and** (b) Step 5 set `stages.forge-5-loop.status` to `complete`. Otherwise **skip** straight to Step 7 — a non-complete run has nothing to verify yet, and epic members get the equivalent offer in Step 6.1 (do **not** prompt twice). This standalone counterpart to Step 6.1 nudges verification interactively
|
|
257
|
+
**Gate:** run only if (a) the feature's `.pipeline-state.json` has **no** `epic` key **and** (b) Step 5 set `stages.forge-5-loop.status` to `complete`. Otherwise **skip** straight to Step 7 — a non-complete run has nothing to verify yet, and epic members get the equivalent offer in Step 6.1 (do **not** prompt twice). This standalone counterpart to Step 6.1 nudges verification interactively. Use the host's question mechanism (NOT inline prose) to offer: *"{feature}'s loop is complete. Recommended: run `/feature-forge:forge-verify {feature} impl` to audit the implementation before generating docs. Run it now, or skip to forge-6-docs?"* On **run**, invoke `feature-forge:forge-verify {feature} impl` with the literal `owner: nested` token in the dispatching prompt — this dispatch happens inside the loop stage, so **you** remain the sole terminal owner and the branch skill returns its structured result and prints no terminal block of its own (see "Branch ownership" and "Caller-side resumption" in `references/stage-exit-protocol.md`; a delegate-and-resume site — on return, control resumes here). A verifier return without its report structure is a dropped digest, not a result (issue #183) — apply "Truncated Verifier Returns" in forge-verify's `findings-template.md` reference (resume or re-dispatch) before recording anything. On **skip**, persist the skip through `state-verify` using the fence below (mirrors `forge-4-backlog`'s skip handling) — the forge-6-docs backstop re-surfaces the skip. Either way, do **not** name a next command here: Step 7 routes, and it routes differently depending on what this step recorded.
|
|
258
258
|
|
|
259
|
-
**The skip is written by `state-verify`, never by hand
|
|
259
|
+
**The skip is written by `state-verify`, never by hand — and never over a resolved entry** (`passed`/`findings-applied`: the verb refuses that demotion, #203 — skip the fence and continue). Add `--epic "{epic}"` when this feature is an epic member — required, per the Pipeline State Protocol in `references/shared-conventions.md`. On exit 2, surface the plain `Error:` line verbatim and stop: the skip is not persisted, so Step 7 would route on state that is not on disk.
|
|
260
260
|
|
|
261
261
|
```bash
|
|
262
262
|
R="$(bash -c 'for d in "${FEATURE_FORGE_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')"
|
|
@@ -268,7 +268,7 @@ python3 "$R/scripts/forge-session.py" state-verify --feature "{feature}" --stage
|
|
|
268
268
|
|
|
269
269
|
**Gate:** only run this step if (a) the resolved feature's `.pipeline-state.json` has an `epic` key **and** (b) Step 5 set `stages.forge-5-loop.status` to `complete` (all backlog items done). If either is false, **skip** straight to Step 7 — standalone completed features are handled by Step 5b, and a non-complete run has no handoff to make (REQ-COMPAT-01).
|
|
270
270
|
|
|
271
|
-
1. **Offer impl-verify first (recommended, skippable).** Per the completion rule (`00-core-definitions.md §7`), a feature whose `forge-verify-impl.status == findings-reported` does **not** unblock dependents. Use the host's question mechanism (NOT inline prose) to offer: *"{feature}'s loop is done. Recommended: run `/feature-forge:forge-verify {feature} impl` before unblocking dependents. Run it now, or skip and continue the handoff?"* On **run**,
|
|
271
|
+
1. **Offer impl-verify first (recommended, skippable).** Per the completion rule (`00-core-definitions.md §7`), a feature whose `forge-verify-impl.status == findings-reported` does **not** unblock dependents. Use the host's question mechanism (NOT inline prose) to offer: *"{feature}'s loop is done. Recommended: run `/feature-forge:forge-verify {feature} impl` before unblocking dependents. Run it now, or skip and continue the handoff?"* On **run**, dispatch exactly as Step 5b does — the same literal `owner: nested` token in the dispatching prompt (you remain the sole terminal owner), the same truncated-return guard before recording anything, and the same declared resume (control returns here; the handoff continues at 2). On **skip**, persist the skip through `state-verify --status skipped` using Step 5b's fence (add `--epic "{epic}"` — required for members) so the Step 7 exit reads a recorded decision instead of re-asking the question the user just answered; completion is then judged on the §7 rule with impl-verify explicitly skipped.
|
|
272
272
|
2. **Recompute and announce.** Run `render-status "{epic}" --specs-dir "{specsDir}" --json`. Announce the feature's completion and the epic rollup (e.g. "2/4 features complete") — derived live from disk, never re-computed in prose.
|
|
273
273
|
3. **Announce what is actionable — do not route.** Read `render-status`'s `actionable` set (every dependency now complete, not itself complete) and say plainly which members can start now, or which are still blocked and on which dependencies. This is context for the user, not a handoff: the Step 7 exit consumes the same live payload and fences the one authoritative next command itself, so do **not** present a next-feature picker, offer to author a member's PRD, or repeat a member's `nextCommand` here. Two competing actions is exactly the ambiguity the scripted exit removes.
|
|
274
274
|
4. **Commit (REQ-OBS-01).** When `gitCommitAfterStage` is true, commit the Step 5 completion write (and any manifest `updatedAt` bump) via the shared-conventions **Git Commit Protocol**, staging the epic subtree so the member state change commits atomically: `git add {specsDir}/{epic}/` then `{commitPrefix}({feature}): complete loop`. If `gitCommitAfterStage` is false, skip the commit. Then fall through to Step 7 — the epic handoff closes there, once, like every other path.
|
|
@@ -277,7 +277,7 @@ python3 "$R/scripts/forge-session.py" state-verify --feature "{feature}" --stage
|
|
|
277
277
|
|
|
278
278
|
Every loop run ends here, and ends here **exactly once** — standalone or epic member, complete or not.
|
|
279
279
|
|
|
280
|
-
First select the single `LoopOutcome` with the ladder in `references/result-reporting.md` (`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.
|
|
280
|
+
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.
|
|
281
281
|
|
|
282
282
|
**Close this stage with the Scripted Stage Exit** (contract: `references/stage-exit-protocol.md`; do not improvise a "Next steps" list). Run:
|
|
283
283
|
|
|
@@ -293,13 +293,13 @@ Add `--epic "{epic}"` when this feature is an epic member — required, per the
|
|
|
293
293
|
|
|
294
294
|
## Gotchas
|
|
295
295
|
|
|
296
|
-
- **Plugin-root discovery (1b-epic helper) covers installed paths, not workspace-dev checkouts.** The `forge-root.sh` search
|
|
296
|
+
- **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." Expected in a dev environment; run the epic-manifest script from the checkout directly (`python3 <checkout>/scripts/epic-manifest.py …`). The bootstrap prelude wraps its candidate loop in `bash -c` so the `~/.claude/plugins/*/feature-forge` glob is zsh-safe: an empty expansion no longer aborts the loop under zsh's `nomatch`.
|
|
297
297
|
- `{backlogDir}` is a **directory path**, not a file path. Pass `specs/auth`, not `specs/auth/backlog.json`.
|
|
298
|
-
- rauf resolves `RAUF.md` with fallback (`{backlogDir}/.rauf/RAUF.md` first, then the project's `.rauf/RAUF.md`)
|
|
299
|
-
- If the session disconnects
|
|
300
|
-
- Never run the run command in the foreground (without the host's background-execution mechanism) — 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 host's monitoring mechanism (3d)
|
|
298
|
+
- 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.
|
|
299
|
+
- If the session disconnects mid-loop, the runner process continues independently — check results later with the status / list commands. A stale lock from a previous run may need `--force` to clear.
|
|
300
|
+
- Never run the run command in the foreground (without the host's background-execution mechanism) — 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 host's monitoring mechanism (3d) — `persistent: true`, the **structured** surface (`events.ndjson`), never raw `RAUF_*` tokens (they false-match in agent prose). 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. See `references/runner-contract.md` for the full monitoring rules.
|
|
301
301
|
- The version gate (1c) uses the `--json` form on purpose; never parse `rauf version`'s human output.
|
|
302
|
-
- **Implementation artifacts must not cite specs.** The loop should **read**
|
|
302
|
+
- **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.
|
|
303
303
|
|
|
304
304
|
---
|
|
305
305
|
|
|
@@ -21,6 +21,22 @@ Step 3c are byte-identical to today (capability gate;
|
|
|
21
21
|
item.provider > --agent (run selection) > loopRunner.defaultAgent (project) > runner default (claude-cli)
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
+
**`loopRunner.agentMode` gate (`"prompt"` default | `"auto"`).** `"prompt"`
|
|
25
|
+
presents the Step 2d agent question (SKILL sub-step b) — byte-identical to today.
|
|
26
|
+
`"auto"` suppresses **only the interactive pick**: skip the agent question and
|
|
27
|
+
resolve as if the user made no per-run selection (`run_selection = None`, so
|
|
28
|
+
`defaultAgent` — or the runner default when unset — applies). Everything else on
|
|
29
|
+
this surface **still runs under `"auto"`**: the single probe, the availability
|
|
30
|
+
listing, the verdict classification below (UNKNOWN hard-reject before any loop
|
|
31
|
+
side-effect, UNAVAILABLE with its proceed-anyway/choose-another question,
|
|
32
|
+
probe-failure handling), and the Claude-only model-alias guard — those questions
|
|
33
|
+
are safety surfaces, not the pick, and are never suppressed. The resolved
|
|
34
|
+
`Agent: {id} (source: …)` line still shows in the confirmation and the Step 3c
|
|
35
|
+
template, so the choice is never hidden. Meaningless when
|
|
36
|
+
`loopRunner.agentArgument` is absent — the capability gate above already removes
|
|
37
|
+
the entire surface, and `agentMode` adds no second gate. An unrecognized value
|
|
38
|
+
behaves as `"prompt"`.
|
|
39
|
+
|
|
24
40
|
**Run-layer mapping — why forge never re-implements rauf's resolver.** forge owns
|
|
25
41
|
**only** its run and project layers and collapses them into **one** value
|
|
26
42
|
(`resolve()`: `run_selection or defaultAgent or none`), which it emits as a single
|
|
@@ -78,7 +94,7 @@ supported when using Codex with a ChatGPT account."* — so **every** spawn exit
|
|
|
78
94
|
rauf reports *"Circuit breaker: 3 consecutive infra failures — halting"* with no hint
|
|
79
95
|
of the real cause. forge-5-loop therefore detects Claude-specific `model` aliases in
|
|
80
96
|
the backlog (tier aliases `opus`/`sonnet`/`haiku` or `claude-*` ids) and, before
|
|
81
|
-
launch, **warns** and offers (via
|
|
97
|
+
launch, **warns** and offers (via the host's question mechanism) to **strip `model` for this run**
|
|
82
98
|
(remove the key from each affected item so each spawn uses the agent's own default) or
|
|
83
99
|
**proceed as-is**. forge only ever touches the `model` field — never `provider`. The
|
|
84
100
|
default / `claude-cli` path skips this guard (the aliases are valid there).
|
|
@@ -58,9 +58,12 @@ defined authoritatively in rauf's
|
|
|
58
58
|
> item aside and **keeps working other runnable items to completion** (rauf:
|
|
59
59
|
> `runner.ts` needs_human handler). So a supervising session can surface those
|
|
60
60
|
> events live (visibility) and cancel early, but it cannot inject an answer and
|
|
61
|
-
> resume the set-aside item mid-run — resolution is
|
|
62
|
-
>
|
|
63
|
-
>
|
|
61
|
+
> resume the set-aside item mid-run — resolution is the **Post-Run Recovery
|
|
62
|
+
> Procedure** (`skills/forge-5-loop/references/recovery-procedure.md`): record the
|
|
63
|
+
> answer via `decision-record` at the moment of collection, then drive recovery
|
|
64
|
+
> from the record after the run ends. A first-class pause/resume-with-answer
|
|
65
|
+
> capability is a desirable runner enhancement (see
|
|
66
|
+
> `plans/rauf-enhancement-recommendations.md`).
|
|
64
67
|
|
|
65
68
|
## rauf is the default and reference implementation
|
|
66
69
|
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
# forge-5-loop — Post-Run Recovery Procedure
|
|
2
|
+
|
|
3
|
+
The named procedure that turns a needs-human / blocked loop stop into a resumable backlog
|
|
4
|
+
without losing the operator's decision. It runs **after** a loop run ends — entered
|
|
5
|
+
**unconditionally** from SKILL Step 4c on every run close, whatever the counts say (the
|
|
6
|
+
`needs_human` / `item_blocked` live-event handling in `runner-contract.md` collects
|
|
7
|
+
answers early for it, but is **not** the entry condition) — and it runs again as the
|
|
8
|
+
**re-entry point** on a fresh session (§3).
|
|
9
|
+
Its seven ordered steps: **enumerate → cluster → consolidated prompts →
|
|
10
|
+
record-at-collection → apply → prove → gate & exit**.
|
|
11
|
+
|
|
12
|
+
Notation: `{backlogDir}` is the resolved backlog directory (SKILL Step 2b);
|
|
13
|
+
`{stateDir}` is the effective-config `loopRunner.stateDir` (default `.rauf`); `$R` is the
|
|
14
|
+
plugin root the SKILL's bootstrap prelude resolves; runner commands are the substituted
|
|
15
|
+
`loopRunner.*Command` forms with the SKILL's token substitution (`{bin}` etc.).
|
|
16
|
+
|
|
17
|
+
## 1. Scope and the failure rule
|
|
18
|
+
|
|
19
|
+
The procedure orchestrates scripted substrate; it never improvises state. Decisions live
|
|
20
|
+
in `{backlogDir}/{stateDir}/forge-decisions.json` — append-only, written **only** by the
|
|
21
|
+
`decision-record` / `decision-list` / `decision-apply` verbs of
|
|
22
|
+
`scripts/forge-session.py` (schema: `references/forge-decisions-schema.json`), never by
|
|
23
|
+
hand. Being under the git-ignored state dir, the record survives session end and context
|
|
24
|
+
clear but never dirties the working tree that §4 inspects.
|
|
25
|
+
|
|
26
|
+
**The failure rule (applies to every step).** Any scripted step that exits non-zero, and
|
|
27
|
+
any runner invocation that errors or returns unparseable output, is surfaced **verbatim**
|
|
28
|
+
and **STOPS** the procedure with a **failed recovery** report — never reported as
|
|
29
|
+
recorded/succeeded. A failed *apply* (step 5) is distinguishable from a
|
|
30
|
+
ran-but-nothing-moved *proof* failure (step 6) because the former never reaches step 6
|
|
31
|
+
(§6).
|
|
32
|
+
|
|
33
|
+
## 2. The seven steps
|
|
34
|
+
|
|
35
|
+
### Step 1 — Enumerate
|
|
36
|
+
|
|
37
|
+
- **Input:** `{backlogDir}`; the runner's authoritative item list.
|
|
38
|
+
- **CLI:**
|
|
39
|
+
```
|
|
40
|
+
python3 "$R/scripts/forge-session.py" decision-list --backlog-dir {backlogDir} --unapplied --json
|
|
41
|
+
{bin} backlog list . --backlog {backlogDir} --json # the substituted listCommand
|
|
42
|
+
```
|
|
43
|
+
The unapplied set is the **latest entry per `itemId` with `appliedAt == null`** —
|
|
44
|
+
deferrals included, applied items excluded.
|
|
45
|
+
- **Decision point:** if the unapplied set is **empty** and no item is
|
|
46
|
+
`blocked`/`needsHuman`, there is nothing to decide or apply: **skip steps 2–6 and go
|
|
47
|
+
straight to step 7 — never exit around it.** Step 7's §4 tree reconciliation still
|
|
48
|
+
runs (it is what catches work stranded without any signal), and its `resolved` gate
|
|
49
|
+
does not apply — **an empty affected set never selects `resolved`**; the SKILL Step 7
|
|
50
|
+
ladder falls through to its count-based rungs. Combined with the clean-tree silence
|
|
51
|
+
of §4.1, this keeps a happy-path run free of any new prompt; the only new happy-path
|
|
52
|
+
output is the Step 2a depth line.
|
|
53
|
+
- **Output:** the unapplied-decision set (each entry's `itemId`, `question`,
|
|
54
|
+
`answer|null`, `deferred`, `clusterId?`), and the live blocked/needs-human item set.
|
|
55
|
+
- **Error:** a `decision-list` exit 2 (unknown dir, unparseable record) stops the
|
|
56
|
+
procedure. A failed `listCommand` read stops it as a failed recovery.
|
|
57
|
+
|
|
58
|
+
### Step 2 — Cluster
|
|
59
|
+
|
|
60
|
+
- **Input:** the blocked/needs-human items from step 1, each carrying its
|
|
61
|
+
`blockedReason` (where the runner lands the `RAUF_NEEDS_HUMAN:<reason>` text).
|
|
62
|
+
- **CLI:**
|
|
63
|
+
```
|
|
64
|
+
python3 "$R/scripts/forge-session.py" backlog-topology --items-stdin --cluster --json < items.json
|
|
65
|
+
```
|
|
66
|
+
fed the **same** `listCommand` JSON already obtained (single data source — never a
|
|
67
|
+
`backlog.json` path). Returns `clusters[]`: each with `memberIds`, `memberReasons`,
|
|
68
|
+
`sharedTokens`, and the **union** of members' gated subtrees (`gatedIds` +
|
|
69
|
+
`gatedCount`).
|
|
70
|
+
- **Decision point:** you **may merge or refine** candidate clusters by judgment —
|
|
71
|
+
under-clustering is the deliberately-chosen failure direction of the scripted helper,
|
|
72
|
+
so its clusters are a floor, not a ceiling. You have no scripted *split* authority.
|
|
73
|
+
- **Output:** the final cluster set (scripted candidates ± your merges), each with its
|
|
74
|
+
member ids and blast-radius numbers.
|
|
75
|
+
- **Error:** a `backlog-topology` exit 2 stops the procedure.
|
|
76
|
+
|
|
77
|
+
### Step 3 — Consolidated prompts
|
|
78
|
+
|
|
79
|
+
- **Input:** the final cluster set from step 2.
|
|
80
|
+
- **Mechanism:** the host's question mechanism (never inline prose).
|
|
81
|
+
- For any cluster of **two or more** items: emit **exactly one** consolidated question
|
|
82
|
+
that **names every affected item id** and states the **full gated subtree** the
|
|
83
|
+
cluster gates. Frame it by **blast radius** — e.g. *"This one decision gates 13 of
|
|
84
|
+
16 backlog items (items 2, 3, …). Answer it once."* — never one prompt per member.
|
|
85
|
+
- Singleton clusters prompt per item (today's per-item shape).
|
|
86
|
+
- **Security:** prompts **MUST NOT solicit secrets**. Ask for the *decision* (which
|
|
87
|
+
path, which policy), never a credential/token/key value. The decision record has no
|
|
88
|
+
credential-shaped field and is treated as repo-visible content.
|
|
89
|
+
- **Decision point:** the operator may **answer**, **defer** the decision, or request
|
|
90
|
+
**cancel the run early** — all three branches proceed to step 4 (nothing is acted on
|
|
91
|
+
before it is recorded).
|
|
92
|
+
- **Output:** per cluster/item, one of {answer text, deferral, cancel-early}.
|
|
93
|
+
- **Citation:** the blast-radius framing is derived from `backlog-topology --cluster`
|
|
94
|
+
gated-subtree output (member ids + counts) — the prompt cites that source; a
|
|
95
|
+
"gates N/M" claim the topology output contradicts is a defect.
|
|
96
|
+
|
|
97
|
+
### Step 4 — Record at collection
|
|
98
|
+
|
|
99
|
+
- **Input:** every branch outcome from step 3.
|
|
100
|
+
- **CLI (one call per decision, BEFORE anything is applied):**
|
|
101
|
+
```
|
|
102
|
+
# answered singleton
|
|
103
|
+
python3 "$R/scripts/forge-session.py" decision-record --backlog-dir {backlogDir} \
|
|
104
|
+
--item ID --question "Q" --answer "A"
|
|
105
|
+
# deferred, or cancel-early (both record a deferral: no --answer)
|
|
106
|
+
python3 "$R/scripts/forge-session.py" decision-record --backlog-dir {backlogDir} \
|
|
107
|
+
--item ID --question "Q" --deferred
|
|
108
|
+
# consolidated answer: one entry per affected item, shared clusterId
|
|
109
|
+
python3 "$R/scripts/forge-session.py" decision-record --backlog-dir {backlogDir} \
|
|
110
|
+
--item ID1 --item ID2 --item ID3 --question "Q" --answer "A" --cluster c1
|
|
111
|
+
```
|
|
112
|
+
(`--actor` defaults to `forge-5-loop@<host>` — a machine label, never user identity.)
|
|
113
|
+
- **Decision point:** a decision is recorded on **every** branch — answered, deferred,
|
|
114
|
+
**and** cancel-early — and it is recorded **before** step 5 acts on anything. A
|
|
115
|
+
cancel-early is recorded as a **deferral** (`answer: null`, `deferred: true`,
|
|
116
|
+
`question` carrying the original needs-human text) — there is no third entry form. A
|
|
117
|
+
recorded-but-unapplied entry (`appliedAt == null`) is exactly what step 1 re-surfaces
|
|
118
|
+
on the next launch (§3).
|
|
119
|
+
- **Consolidated:** one entry per affected item, all sharing one `clusterId` (minted
|
|
120
|
+
`c` + lowest member id). Items stay **independently re-decidable**: a later per-item
|
|
121
|
+
entry supersedes the cluster entry for that item only.
|
|
122
|
+
- **Output:** durable append-only entries in `forge-decisions.json`; the write is
|
|
123
|
+
atomic.
|
|
124
|
+
- **Error:** any `decision-record` exit 2 (both/neither of `--answer`/`--deferred`,
|
|
125
|
+
unknown dir, failed atomic write) is surfaced verbatim and stops the procedure. The
|
|
126
|
+
answer is **not** applied if it was not recorded.
|
|
127
|
+
|
|
128
|
+
### Step 5 — Apply
|
|
129
|
+
|
|
130
|
+
- **Version probe (once, at the start of this step):** run the substituted
|
|
131
|
+
`loopRunner.versionCommand` (default `{bin} version --json`), parse
|
|
132
|
+
`{ "version": "<semver>" }`, and numerically semver-compare it against
|
|
133
|
+
`RECOVERY_MIN_RUNNER_VERSION` (a `scripts/forge-session.py` module constant, `0.14.0`
|
|
134
|
+
— the capability threshold for `{bin} backlog answer`; **not**
|
|
135
|
+
`loopRunner.minRunnerVersion`, which stays the launch floor). A probe miss
|
|
136
|
+
(missing/old/unparseable version) is **never** a hard failure — it selects the
|
|
137
|
+
degraded path and is reported with `loopRunner.installHint`.
|
|
138
|
+
- **Apply per item** (full dispatch table in §5):
|
|
139
|
+
- needs-human item, runner **≥** threshold →
|
|
140
|
+
`{bin} backlog answer . {id} "{answer}" --backlog {backlogDir} --json`
|
|
141
|
+
(the answer text is threaded into the next iteration's prompt).
|
|
142
|
+
- needs-human item, runner **<** threshold → **degraded path:**
|
|
143
|
+
`{bin} backlog unblock . {id} --backlog {backlogDir} --json` — the item is genuinely
|
|
144
|
+
unblocked and the answer stays durable in `forge-decisions.json`, but the recovery
|
|
145
|
+
report **must state explicitly** that the answer was **not** injected into the next
|
|
146
|
+
iteration's prompt, with the `installHint` upgrade hint attached.
|
|
147
|
+
- plain (non-needs-human) blocked item → `{bin} backlog unblock` at **every** runner
|
|
148
|
+
version.
|
|
149
|
+
- **Stamp:** after each runner apply **succeeds**, run
|
|
150
|
+
```
|
|
151
|
+
python3 "$R/scripts/forge-session.py" decision-apply --backlog-dir {backlogDir} --item ID
|
|
152
|
+
```
|
|
153
|
+
which stamps `appliedAt`/`appliedBy` on the item's latest entry. `decision-apply` is
|
|
154
|
+
called **only after** the runner apply returned success — a stamped record means the
|
|
155
|
+
runner actually accepted the change.
|
|
156
|
+
- **Error:** a runner apply that **errors** (non-zero exit — item missing, not
|
|
157
|
+
`blocked`, or any failure) is a **failed apply**: surface it verbatim, do **not** call
|
|
158
|
+
`decision-apply`, stop the procedure, report failed recovery. This is distinct from a
|
|
159
|
+
version-probe miss (which routes to the degraded path, not a failure) and from step
|
|
160
|
+
6's ran-but-nothing-moved failure (§6).
|
|
161
|
+
|
|
162
|
+
### Step 6 — Prove
|
|
163
|
+
|
|
164
|
+
- **Input:** the affected item set that step 5 applied.
|
|
165
|
+
- **CLI:** re-read per-item state via the substituted `loopRunner.listCommand`
|
|
166
|
+
(`{bin} backlog list . --backlog {backlogDir} --json`) and test **each** affected
|
|
167
|
+
item: `status != "blocked"` — which, per the runner's derivation
|
|
168
|
+
(needs-human ⇔ `status=="blocked" && needsHuman==true`), also removes it from the
|
|
169
|
+
needs-human count, so the single test covers both flags. Aggregate `backlogSummary`
|
|
170
|
+
counts are **never** the test. An affected item **missing** from the re-read counts
|
|
171
|
+
as a non-mover.
|
|
172
|
+
- **Decision point:** **all** affected items moved → proceed to step 7. **Any**
|
|
173
|
+
non-mover — including a partial move where some items moved and others did not — is a
|
|
174
|
+
**failed recovery**: report it, **naming the movers and the non-movers** from their
|
|
175
|
+
item `status` fields.
|
|
176
|
+
- **Output:** either "all moved → continue" or a failed-recovery report.
|
|
177
|
+
- **Citation:** the movers/non-movers are named from the per-item `listCommand` re-read
|
|
178
|
+
(`status` fields), never from aggregate counts — a report that contradicts the
|
|
179
|
+
per-item read is a defect.
|
|
180
|
+
|
|
181
|
+
### Step 7 — Gate & exit
|
|
182
|
+
|
|
183
|
+
- **Tree reconciliation first.** Before any outcome is selected, run the **Post-Run
|
|
184
|
+
Tree Reconciliation** section (§4). It runs on every recovery pass — including passes
|
|
185
|
+
with no needs-human items — and is silent on a clean tree.
|
|
186
|
+
- **Evaluate the `resolved` gate — all three must hold:**
|
|
187
|
+
1. `decision-list --unapplied` is **empty for the affected items**. The verb returns
|
|
188
|
+
the **global** latest-unapplied-per-item set, so **intersect** that payload's
|
|
189
|
+
entries (each carries `itemId`) with this session's affected-item set and test
|
|
190
|
+
only that intersection for emptiness — an unrelated item's stray deferral must not
|
|
191
|
+
suppress a legitimate `resolved`.
|
|
192
|
+
2. `git status --porcelain` is **clean** (git-ignored `{stateDir}` artifacts are
|
|
193
|
+
invisible to porcelain — the exclusion holds by construction).
|
|
194
|
+
3. the per-item re-read (step 6) shows **every** affected item left
|
|
195
|
+
`blocked`/`needsHuman`.
|
|
196
|
+
- **Select the outcome:** on all-three-pass, select `resolved` — the first rung of the
|
|
197
|
+
ladder in `result-reporting.md`, so a resolved stop never re-triggers the needs-human
|
|
198
|
+
branch its own recovery just cleared. **Any one gate failing falls the ladder
|
|
199
|
+
through** to `needs-human` / `blocked` / `deferred` / `partial` / `complete` exactly
|
|
200
|
+
as today. `resolved` routes **resume** — its NEXT-STEPS block fences
|
|
201
|
+
`/feature-forge:forge-5-loop {feature}`, never the navigator.
|
|
202
|
+
- **CLI:** the close runs through the Scripted Stage Exit (SKILL Step 7):
|
|
203
|
+
`stage-exit … --outcome resolved …`. `stage-exit` does **not** re-verify the gate
|
|
204
|
+
server-side (it has no runner access) — enforcement is procedural: this step.
|
|
205
|
+
- **Citation:** the `resolved` outcome text cites the three gate evaluations
|
|
206
|
+
(`decision-list --unapplied` empty, porcelain empty, per-item re-read all-moved).
|
|
207
|
+
Claiming `resolved` without those preconditions is a reportable defect.
|
|
208
|
+
|
|
209
|
+
## 3. Fresh-session re-entry
|
|
210
|
+
|
|
211
|
+
The procedure is the **re-entry point** on a fresh session / next launch — this is what
|
|
212
|
+
makes a decision survive session end and context clear.
|
|
213
|
+
|
|
214
|
+
On a new session, **step 1** enumerates every entry with `appliedAt == null` from a
|
|
215
|
+
*previous* session — answered-but-not-yet-applied decisions, deferrals, and cancel-early
|
|
216
|
+
deferrals alike. Those entries are re-surfaced:
|
|
217
|
+
|
|
218
|
+
- An entry that already carries an **answer** (`answer != null`, `deferred == false`,
|
|
219
|
+
`appliedAt == null`) **skips step 3's prompt** for that item — the operator already
|
|
220
|
+
decided; the procedure proceeds straight to step 5 (apply) and step 6 (prove). The
|
|
221
|
+
answer collected last session is applied this session without re-asking.
|
|
222
|
+
- A **deferral** (`deferred == true`) re-surfaces through step 3 as an open decision —
|
|
223
|
+
the operator is asked again, and their new answer appends a **new** entry
|
|
224
|
+
(append-only); the deferral's audit fields are never destroyed.
|
|
225
|
+
|
|
226
|
+
Because entries are durable and untracked, a session boundary, crash, or context clear
|
|
227
|
+
between "operator answered" and "answer applied" never costs the decision — step 1 of
|
|
228
|
+
the next launch finds it.
|
|
229
|
+
|
|
230
|
+
## 4. Post-Run Tree Reconciliation
|
|
231
|
+
|
|
232
|
+
Invoked from step 7 after the run ends and **before** any outcome is selected. It runs
|
|
233
|
+
on **every** recovery pass — including passes with no needs-human items, which step 1
|
|
234
|
+
routes here directly, and SKILL Step 4c enters the procedure on every run close —
|
|
235
|
+
because it is the "tree" half of recovery. Four sub-steps.
|
|
236
|
+
|
|
237
|
+
### 4.1 Detect
|
|
238
|
+
|
|
239
|
+
- **CLI:** `git status --porcelain`.
|
|
240
|
+
- **Clean tree → SILENT.** Empty output ⇒ no prompt, no output, no operator decision.
|
|
241
|
+
The decision record and all runner state under `{stateDir}` are git-ignored and
|
|
242
|
+
therefore never appear in porcelain output — decision writes never dirty the tree
|
|
243
|
+
this step inspects.
|
|
244
|
+
- **Dirty tree → proceed to 4.2.**
|
|
245
|
+
- **Error:** a `git status` failure (not a git repo, git error) is surfaced verbatim;
|
|
246
|
+
reconciliation is skipped (there is nothing git-native to reconcile), the rest of the
|
|
247
|
+
procedure continues.
|
|
248
|
+
|
|
249
|
+
### 4.2 Attribute (best-effort, runner-native)
|
|
250
|
+
|
|
251
|
+
Best-effort attribution of dirty paths to the backlog item(s) that produced them, from
|
|
252
|
+
runner-native evidence — reliable per-item provenance is **not** a prerequisite.
|
|
253
|
+
|
|
254
|
+
- **Read `{backlogDir}/{stateDir}/state.json`** (the runner's loop state):
|
|
255
|
+
`baseCommitHash` (the HEAD captured at run start — the baseline for
|
|
256
|
+
`git log {baseCommitHash}..HEAD`), `completedItems` / `blockedItems` (item ids that
|
|
257
|
+
finished / blocked), `currentItem` (the item in flight when the run stopped — a
|
|
258
|
+
strong candidate for uncommitted changes), `startedAt` and
|
|
259
|
+
`iteration`/`maxIterations` (run identity + budget).
|
|
260
|
+
- **Read `{backlogDir}/{stateDir}/events.ndjson`** — one JSON object per line; parse
|
|
261
|
+
line-by-line (there is **no** runner CLI for events; the file is read directly). The
|
|
262
|
+
per-iteration `item_selected`, `llm_spawned`, and `llm_exited` records — each
|
|
263
|
+
carrying an `itemId` and a `timestamp` — name which items ran during the window and
|
|
264
|
+
in what order.
|
|
265
|
+
- **Map dirty paths → candidate items:** the `currentItem` and the most recent
|
|
266
|
+
`item_selected`/`llm_spawned` without a matching clean `llm_exited` are the items "in
|
|
267
|
+
flight when the run died"; `git log {baseCommitHash}..HEAD` names what was already
|
|
268
|
+
committed for which item (the runner commits `[rauf] <id>: <title>`). Present the
|
|
269
|
+
mapping as **CANDIDATES, never asserted**.
|
|
270
|
+
- **Degradation (detection never aborts):** if `state.json` or `events.ndjson` is
|
|
271
|
+
missing, unreadable, or unparseable, **degrade** to the fully-unattributed path —
|
|
272
|
+
everything goes into 4.3's single consolidated decision. Detection (4.1) is never
|
|
273
|
+
aborted by an evidence-parse failure.
|
|
274
|
+
- **Citation:** the presentation cites `git status --porcelain` paths +
|
|
275
|
+
`{stateDir}/state.json` / `events.ndjson` run evidence, with every attribution
|
|
276
|
+
explicitly labelled a **candidate**.
|
|
277
|
+
|
|
278
|
+
### 4.3 Decide
|
|
279
|
+
|
|
280
|
+
- **Mechanism:** the host's question mechanism (never inline prose).
|
|
281
|
+
- **One question per attributed item-group:** for each candidate item-group from 4.2,
|
|
282
|
+
offer **commit-for-that-item** / **stash** / **discard**.
|
|
283
|
+
- **Unattributable changes → ONE consolidated decision:** everything that could not
|
|
284
|
+
be attributed is presented as a single grouped question, not dropped.
|
|
285
|
+
- **Discard guard:** **discard is NEVER the default** and requires its **own explicit
|
|
286
|
+
confirmation** — a second, dedicated question via the host's question mechanism confirming the specific paths
|
|
287
|
+
to be discarded before any `git checkout`/`git restore`/`git clean` runs. No path is
|
|
288
|
+
discarded on a single click.
|
|
289
|
+
- **Output:** per group, an executed reconciliation (commit / stash / confirmed
|
|
290
|
+
discard) or a deferral the operator can revisit.
|
|
291
|
+
|
|
292
|
+
### 4.4 Launch blocker
|
|
293
|
+
|
|
294
|
+
The next launch's `### 1g. Stranded-Work Pre-flight` (SKILL Step 1) STOPS on a dirty
|
|
295
|
+
tree when a prior run's `{backlogDir}/{stateDir}/state.json` exists, names that run
|
|
296
|
+
(its `startedAt`, `currentItem`, `blockedItems`), and points at this section to
|
|
297
|
+
commit / stash / discard the stranded work before relaunch. The runner's own
|
|
298
|
+
uncommitted-changes launch refusal remains the backstop for a dirty tree with no
|
|
299
|
+
prior-run state.
|
|
300
|
+
|
|
301
|
+
## 5. Apply-mechanism dispatch (version gate & the degraded path)
|
|
302
|
+
|
|
303
|
+
| Runner version | Item kind | Apply mechanism | What the report says |
|
|
304
|
+
|---|---|---|---|
|
|
305
|
+
| `≥ RECOVERY_MIN_RUNNER_VERSION` | needs-human (has an answer) | `{bin} backlog answer . {id} "{answer}" --backlog {backlogDir} --json` | Answer applied and threaded into the next iteration's prompt. |
|
|
306
|
+
| `≥ RECOVERY_MIN_RUNNER_VERSION` | plain blocked | `{bin} backlog unblock . {id} --backlog {backlogDir} --json` | Item unblocked. |
|
|
307
|
+
| `< RECOVERY_MIN_RUNNER_VERSION` (or probe miss) | needs-human | **DEGRADE:** `{bin} backlog unblock . {id} --backlog {backlogDir} --json` | Item unblocked; **answer was NOT injected into the next prompt** (durable in `forge-decisions.json`); `{installHint}` — upgrade to a runner that ships `backlog answer` to thread it. |
|
|
308
|
+
| any version (incl. probe miss) | plain blocked | `{bin} backlog unblock . {id} --backlog {backlogDir} --json` | Item unblocked. |
|
|
309
|
+
|
|
310
|
+
Key properties:
|
|
311
|
+
|
|
312
|
+
- **Plain blocked items always use `unblock`, at every version** — they carry no answer
|
|
313
|
+
to thread. The version gate only ever changes the needs-human path.
|
|
314
|
+
- **The degraded needs-human path genuinely unblocks** (the runner clears
|
|
315
|
+
`status`/`blockedReason`/`needsHuman`/`deferred`), so recovery works across the whole
|
|
316
|
+
supported runner floor. The only capability lost below the threshold is
|
|
317
|
+
prompt-threading — the answer remains durable in the decision record and re-surfaces
|
|
318
|
+
via `decision-list --unapplied` if re-decided.
|
|
319
|
+
- **The report is honest either way:** the degraded path states explicitly that the
|
|
320
|
+
answer was not threaded, with the upgrade hint.
|
|
321
|
+
|
|
322
|
+
## 6. Failure taxonomy
|
|
323
|
+
|
|
324
|
+
| Failure | When it occurs | Reaches the step-6 per-item test? | Report |
|
|
325
|
+
|---|---|---|---|
|
|
326
|
+
| **Failed apply** | `{bin} backlog answer` / `unblock` exits non-zero (corrupt backlog, I/O error, not-blocked/not-found refusal); or the post-apply re-read is unparseable | **No** — stops *before* the test | Verbatim runner error + which item; **failed recovery**; procedure stops; never claimed succeeded |
|
|
327
|
+
| **Ran-but-nothing-moved** | Every apply exited 0, but the step-6 per-item test finds a non-mover | **Yes** — *is* the test failing | Movers/non-movers named from `status` fields; **failed recovery** |
|
|
328
|
+
| **Version-probe miss** | `versionCommand` missing/unparseable, or version `< RECOVERY_MIN_RUNNER_VERSION` | N/A — selects the degraded path (§5) | Degraded path proceeds; not-threaded caveat + `installHint`; **not** a failed recovery |
|
|
329
|
+
|
|
330
|
+
Rules: never report recorded/succeeded past a failed step; a failed apply stops before
|
|
331
|
+
the per-item test, so a runner that errored is never conflated with a runner that ran
|
|
332
|
+
cleanly but moved nothing; `decision-apply` is not called for a failed item — the record
|
|
333
|
+
stays unapplied and re-surfaces next launch; a probe miss degrades, it never fails
|
|
334
|
+
recovery.
|
|
335
|
+
|
|
336
|
+
## 7. Report citations (REQ-OBS-01)
|
|
337
|
+
|
|
338
|
+
Every report surface this procedure produces names the authoritative source it derived
|
|
339
|
+
its claims from; a claim that source contradicts is a reportable defect. Each report
|
|
340
|
+
surface names the authoritative source it derives its claims from:
|
|
341
|
+
|
|
342
|
+
| Report surface | Authoritative citation basis |
|
|
343
|
+
|---|---|
|
|
344
|
+
| Pending / starvation template | `backlogSummary` counts + `backlog-topology` output over `listCommand` JSON; iteration counters from `state.json` (`iteration`/`maxIterations`) |
|
|
345
|
+
| Failed-recovery report (§2 step 6) | The per-item `listCommand` re-read — movers/non-movers named from item `status`, never aggregate counts |
|
|
346
|
+
| `resolved` outcome text | The three gate evaluations: `decision-list --unapplied` (empty), `git status --porcelain` (empty), per-item re-read (all affected left `blocked`) |
|
|
347
|
+
| Consolidated blast-radius prompt (§2 step 3) | `backlog-topology --cluster` gated-subtree output (member ids + counts) |
|
|
348
|
+
| Tree-reconciliation presentation (§4) | `git status --porcelain` paths + `state.json`/`events.ndjson` run evidence, attributions explicitly presented as **candidates** |
|
|
349
|
+
| Step 2a depth line | The same `backlog-topology` output (`maxChainDepth`) |
|