@garygentry/feature-forge 0.2.12 → 0.2.14
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/adapters/GENERATION-REPORT.md +1 -0
- package/adapters/claude/.feature-forge-bundle.json +1 -1
- package/adapters/claude/references/epic-manifest-schema.json +5 -0
- package/adapters/claude/references/forge-config-schema.json +5 -0
- package/adapters/claude/references/shared-conventions.md +12 -0
- package/adapters/claude/references/stacks/_generic.md +21 -0
- package/adapters/claude/references/stacks/go.md +19 -0
- package/adapters/claude/references/stacks/python.md +21 -0
- package/adapters/claude/references/stacks/rust.md +19 -0
- package/adapters/claude/references/stacks/typescript.md +23 -0
- package/adapters/claude/scripts/epic-manifest.py +284 -5
- package/adapters/claude/scripts/forge-bootstrap.py +1 -0
- package/adapters/claude/scripts/forge-init.sh +2 -0
- package/adapters/claude/scripts/forge-root.sh +55 -11
- package/adapters/claude/scripts/forge-session.py +38 -1
- package/adapters/claude/skills/forge/SKILL.md +9 -1
- package/adapters/claude/skills/forge/references/pipeline-state-schema.json +191 -0
- package/adapters/claude/skills/forge/references/process-overview.md +143 -0
- package/adapters/claude/skills/forge/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge/references/stage-exit-protocol.md +258 -0
- package/adapters/claude/skills/forge-0-epic/references/epic-manifest-subcommands.md +11 -0
- package/adapters/claude/skills/forge-0-epic/references/pipeline-state-schema.json +191 -0
- package/adapters/claude/skills/forge-0-epic/references/portable-root.md +71 -0
- package/adapters/claude/skills/forge-0-epic/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-0-epic/references/stage-exit-protocol.md +258 -0
- package/adapters/claude/skills/forge-1-prd/SKILL.md +2 -0
- package/adapters/claude/skills/forge-1-prd/references/pipeline-state-schema.json +191 -0
- package/adapters/claude/skills/forge-1-prd/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-1-prd/references/stage-exit-protocol.md +258 -0
- package/adapters/claude/skills/forge-2-tech/SKILL.md +3 -1
- package/adapters/claude/skills/forge-2-tech/references/pipeline-state-schema.json +191 -0
- package/adapters/claude/skills/forge-2-tech/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-2-tech/references/stack-resolution.md +54 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/_generic.md +111 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/go.md +157 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/python.md +184 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/rust.md +170 -0
- package/adapters/claude/skills/forge-2-tech/references/stacks/typescript.md +134 -0
- package/adapters/claude/skills/forge-2-tech/references/stage-exit-protocol.md +258 -0
- package/adapters/claude/skills/forge-3-specs/SKILL.md +2 -0
- package/adapters/claude/skills/forge-3-specs/references/pipeline-state-schema.json +191 -0
- package/adapters/claude/skills/forge-3-specs/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/_generic.md +111 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/go.md +157 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/python.md +184 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/rust.md +170 -0
- package/adapters/claude/skills/forge-3-specs/references/stacks/typescript.md +134 -0
- package/adapters/claude/skills/forge-3-specs/references/stage-exit-protocol.md +258 -0
- package/adapters/claude/skills/forge-4-backlog/SKILL.md +5 -0
- package/adapters/claude/skills/forge-4-backlog/references/forge-config-schema.json +236 -0
- package/adapters/claude/skills/forge-4-backlog/references/pipeline-state-schema.json +191 -0
- package/adapters/claude/skills/forge-4-backlog/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-4-backlog/references/stage-exit-protocol.md +258 -0
- package/adapters/claude/skills/forge-5-loop/SKILL.md +7 -9
- package/adapters/claude/skills/forge-5-loop/references/forge-config-schema.json +236 -0
- package/adapters/claude/skills/forge-5-loop/references/ralph-loop-contract.md +221 -0
- package/adapters/claude/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/claude/skills/forge-5-loop/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-5-loop/references/stage-exit-protocol.md +258 -0
- package/adapters/claude/skills/forge-6-docs/SKILL.md +4 -1
- package/adapters/claude/skills/forge-6-docs/references/pipeline-state-schema.json +191 -0
- package/adapters/claude/skills/forge-6-docs/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-fix/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-fix/references/stage-exit-protocol.md +258 -0
- package/adapters/claude/skills/forge-guide/references/forge-config-schema.json +236 -0
- package/adapters/claude/skills/forge-guide/references/process-overview.md +143 -0
- package/adapters/claude/skills/forge-guide/references/ralph-loop-contract.md +221 -0
- package/adapters/claude/skills/forge-guide/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-guide/references/stack-resolution.md +54 -0
- package/adapters/claude/skills/forge-guide/references/stacks/_generic.md +111 -0
- package/adapters/claude/skills/forge-guide/references/stacks/go.md +157 -0
- package/adapters/claude/skills/forge-guide/references/stacks/python.md +184 -0
- package/adapters/claude/skills/forge-guide/references/stacks/rust.md +170 -0
- package/adapters/claude/skills/forge-guide/references/stacks/typescript.md +134 -0
- package/adapters/claude/skills/forge-init/SKILL.md +1 -0
- package/adapters/claude/skills/forge-verify/SKILL.md +5 -4
- package/adapters/claude/skills/forge-verify/references/pipeline-state-schema.json +191 -0
- package/adapters/claude/skills/forge-verify/references/shared-conventions.md +295 -0
- package/adapters/claude/skills/forge-verify/references/verification-checklists.md +88 -2
- package/adapters/codex/.feature-forge-bundle.json +1 -1
- package/adapters/codex/references/epic-manifest-schema.json +5 -0
- package/adapters/codex/references/forge-config-schema.json +5 -0
- package/adapters/codex/references/shared-conventions.md +12 -0
- package/adapters/codex/references/stacks/_generic.md +21 -0
- package/adapters/codex/references/stacks/go.md +19 -0
- package/adapters/codex/references/stacks/python.md +21 -0
- package/adapters/codex/references/stacks/rust.md +19 -0
- package/adapters/codex/references/stacks/typescript.md +23 -0
- package/adapters/codex/scripts/epic-manifest.py +284 -5
- package/adapters/codex/scripts/forge-bootstrap.py +1 -0
- package/adapters/codex/scripts/forge-init.sh +2 -0
- package/adapters/codex/scripts/forge-root.sh +55 -11
- package/adapters/codex/scripts/forge-session.py +38 -1
- package/adapters/codex/skills/forge/SKILL.md +9 -1
- package/adapters/codex/skills/forge/references/pipeline-state-schema.json +191 -0
- package/adapters/codex/skills/forge/references/process-overview.md +143 -0
- package/adapters/codex/skills/forge/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge/references/stage-exit-protocol.md +258 -0
- package/adapters/codex/skills/forge-0-epic/references/epic-manifest-subcommands.md +11 -0
- package/adapters/codex/skills/forge-0-epic/references/pipeline-state-schema.json +191 -0
- package/adapters/codex/skills/forge-0-epic/references/portable-root.md +71 -0
- package/adapters/codex/skills/forge-0-epic/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-0-epic/references/stage-exit-protocol.md +258 -0
- package/adapters/codex/skills/forge-1-prd/SKILL.md +2 -0
- package/adapters/codex/skills/forge-1-prd/references/pipeline-state-schema.json +191 -0
- package/adapters/codex/skills/forge-1-prd/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-1-prd/references/stage-exit-protocol.md +258 -0
- package/adapters/codex/skills/forge-2-tech/SKILL.md +3 -1
- package/adapters/codex/skills/forge-2-tech/references/pipeline-state-schema.json +191 -0
- package/adapters/codex/skills/forge-2-tech/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-2-tech/references/stack-resolution.md +54 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/_generic.md +111 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/go.md +157 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/python.md +184 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/rust.md +170 -0
- package/adapters/codex/skills/forge-2-tech/references/stacks/typescript.md +134 -0
- package/adapters/codex/skills/forge-2-tech/references/stage-exit-protocol.md +258 -0
- package/adapters/codex/skills/forge-3-specs/SKILL.md +2 -0
- package/adapters/codex/skills/forge-3-specs/references/pipeline-state-schema.json +191 -0
- package/adapters/codex/skills/forge-3-specs/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/_generic.md +111 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/go.md +157 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/python.md +184 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/rust.md +170 -0
- package/adapters/codex/skills/forge-3-specs/references/stacks/typescript.md +134 -0
- package/adapters/codex/skills/forge-3-specs/references/stage-exit-protocol.md +258 -0
- package/adapters/codex/skills/forge-4-backlog/SKILL.md +5 -0
- package/adapters/codex/skills/forge-4-backlog/references/forge-config-schema.json +236 -0
- package/adapters/codex/skills/forge-4-backlog/references/pipeline-state-schema.json +191 -0
- package/adapters/codex/skills/forge-4-backlog/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-4-backlog/references/stage-exit-protocol.md +258 -0
- package/adapters/codex/skills/forge-5-loop/SKILL.md +7 -9
- package/adapters/codex/skills/forge-5-loop/references/forge-config-schema.json +236 -0
- package/adapters/codex/skills/forge-5-loop/references/ralph-loop-contract.md +221 -0
- package/adapters/codex/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/codex/skills/forge-5-loop/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-5-loop/references/stage-exit-protocol.md +258 -0
- package/adapters/codex/skills/forge-6-docs/SKILL.md +4 -1
- package/adapters/codex/skills/forge-6-docs/references/pipeline-state-schema.json +191 -0
- package/adapters/codex/skills/forge-6-docs/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-fix/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-fix/references/stage-exit-protocol.md +258 -0
- package/adapters/codex/skills/forge-guide/references/forge-config-schema.json +236 -0
- package/adapters/codex/skills/forge-guide/references/process-overview.md +143 -0
- package/adapters/codex/skills/forge-guide/references/ralph-loop-contract.md +221 -0
- package/adapters/codex/skills/forge-guide/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-guide/references/stack-resolution.md +54 -0
- package/adapters/codex/skills/forge-guide/references/stacks/_generic.md +111 -0
- package/adapters/codex/skills/forge-guide/references/stacks/go.md +157 -0
- package/adapters/codex/skills/forge-guide/references/stacks/python.md +184 -0
- package/adapters/codex/skills/forge-guide/references/stacks/rust.md +170 -0
- package/adapters/codex/skills/forge-guide/references/stacks/typescript.md +134 -0
- package/adapters/codex/skills/forge-init/SKILL.md +1 -0
- package/adapters/codex/skills/forge-verify/SKILL.md +5 -4
- package/adapters/codex/skills/forge-verify/references/pipeline-state-schema.json +191 -0
- package/adapters/codex/skills/forge-verify/references/shared-conventions.md +295 -0
- package/adapters/codex/skills/forge-verify/references/verification-checklists.md +88 -2
- package/adapters/copilot/.feature-forge-bundle.json +1 -1
- package/adapters/copilot/references/epic-manifest-schema.json +5 -0
- package/adapters/copilot/references/forge-config-schema.json +5 -0
- package/adapters/copilot/references/shared-conventions.md +12 -0
- package/adapters/copilot/references/stacks/_generic.md +21 -0
- package/adapters/copilot/references/stacks/go.md +19 -0
- package/adapters/copilot/references/stacks/python.md +21 -0
- package/adapters/copilot/references/stacks/rust.md +19 -0
- package/adapters/copilot/references/stacks/typescript.md +23 -0
- package/adapters/copilot/scripts/epic-manifest.py +284 -5
- package/adapters/copilot/scripts/forge-bootstrap.py +1 -0
- package/adapters/copilot/scripts/forge-init.sh +2 -0
- package/adapters/copilot/scripts/forge-root.sh +55 -11
- package/adapters/copilot/scripts/forge-session.py +38 -1
- package/adapters/copilot/skills/forge/forge.md +9 -1
- package/adapters/copilot/skills/forge/references/pipeline-state-schema.json +191 -0
- package/adapters/copilot/skills/forge/references/process-overview.md +143 -0
- package/adapters/copilot/skills/forge/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge/references/stage-exit-protocol.md +258 -0
- package/adapters/copilot/skills/forge-0-epic/references/epic-manifest-subcommands.md +11 -0
- package/adapters/copilot/skills/forge-0-epic/references/pipeline-state-schema.json +191 -0
- package/adapters/copilot/skills/forge-0-epic/references/portable-root.md +71 -0
- package/adapters/copilot/skills/forge-0-epic/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-0-epic/references/stage-exit-protocol.md +258 -0
- package/adapters/copilot/skills/forge-1-prd/forge-1-prd.md +2 -0
- package/adapters/copilot/skills/forge-1-prd/references/pipeline-state-schema.json +191 -0
- package/adapters/copilot/skills/forge-1-prd/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-1-prd/references/stage-exit-protocol.md +258 -0
- package/adapters/copilot/skills/forge-2-tech/forge-2-tech.md +3 -1
- package/adapters/copilot/skills/forge-2-tech/references/pipeline-state-schema.json +191 -0
- package/adapters/copilot/skills/forge-2-tech/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-2-tech/references/stack-resolution.md +54 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/_generic.md +111 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/go.md +157 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/python.md +184 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/rust.md +170 -0
- package/adapters/copilot/skills/forge-2-tech/references/stacks/typescript.md +134 -0
- package/adapters/copilot/skills/forge-2-tech/references/stage-exit-protocol.md +258 -0
- package/adapters/copilot/skills/forge-3-specs/forge-3-specs.md +2 -0
- package/adapters/copilot/skills/forge-3-specs/references/pipeline-state-schema.json +191 -0
- package/adapters/copilot/skills/forge-3-specs/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/_generic.md +111 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/go.md +157 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/python.md +184 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/rust.md +170 -0
- package/adapters/copilot/skills/forge-3-specs/references/stacks/typescript.md +134 -0
- package/adapters/copilot/skills/forge-3-specs/references/stage-exit-protocol.md +258 -0
- package/adapters/copilot/skills/forge-4-backlog/forge-4-backlog.md +5 -0
- package/adapters/copilot/skills/forge-4-backlog/references/forge-config-schema.json +236 -0
- package/adapters/copilot/skills/forge-4-backlog/references/pipeline-state-schema.json +191 -0
- package/adapters/copilot/skills/forge-4-backlog/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-4-backlog/references/stage-exit-protocol.md +258 -0
- package/adapters/copilot/skills/forge-5-loop/forge-5-loop.md +7 -9
- package/adapters/copilot/skills/forge-5-loop/references/forge-config-schema.json +236 -0
- package/adapters/copilot/skills/forge-5-loop/references/ralph-loop-contract.md +221 -0
- package/adapters/copilot/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/copilot/skills/forge-5-loop/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-5-loop/references/stage-exit-protocol.md +258 -0
- package/adapters/copilot/skills/forge-6-docs/forge-6-docs.md +4 -1
- package/adapters/copilot/skills/forge-6-docs/references/pipeline-state-schema.json +191 -0
- package/adapters/copilot/skills/forge-6-docs/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-fix/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-fix/references/stage-exit-protocol.md +258 -0
- package/adapters/copilot/skills/forge-guide/references/forge-config-schema.json +236 -0
- package/adapters/copilot/skills/forge-guide/references/process-overview.md +143 -0
- package/adapters/copilot/skills/forge-guide/references/ralph-loop-contract.md +221 -0
- package/adapters/copilot/skills/forge-guide/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-guide/references/stack-resolution.md +54 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/_generic.md +111 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/go.md +157 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/python.md +184 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/rust.md +170 -0
- package/adapters/copilot/skills/forge-guide/references/stacks/typescript.md +134 -0
- package/adapters/copilot/skills/forge-init/forge-init.md +1 -0
- package/adapters/copilot/skills/forge-verify/forge-verify.md +5 -4
- package/adapters/copilot/skills/forge-verify/references/pipeline-state-schema.json +191 -0
- package/adapters/copilot/skills/forge-verify/references/shared-conventions.md +295 -0
- package/adapters/copilot/skills/forge-verify/references/verification-checklists.md +88 -2
- package/adapters/cursor/.feature-forge-bundle.json +1 -1
- package/adapters/cursor/references/epic-manifest-schema.json +5 -0
- package/adapters/cursor/references/forge-config-schema.json +5 -0
- package/adapters/cursor/references/shared-conventions.md +12 -0
- package/adapters/cursor/references/stacks/_generic.md +21 -0
- package/adapters/cursor/references/stacks/go.md +19 -0
- package/adapters/cursor/references/stacks/python.md +21 -0
- package/adapters/cursor/references/stacks/rust.md +19 -0
- package/adapters/cursor/references/stacks/typescript.md +23 -0
- package/adapters/cursor/scripts/epic-manifest.py +284 -5
- package/adapters/cursor/scripts/forge-bootstrap.py +1 -0
- package/adapters/cursor/scripts/forge-init.sh +2 -0
- package/adapters/cursor/scripts/forge-root.sh +55 -11
- package/adapters/cursor/scripts/forge-session.py +38 -1
- package/adapters/cursor/skills/forge/forge.mdc +9 -1
- package/adapters/cursor/skills/forge/references/pipeline-state-schema.json +191 -0
- package/adapters/cursor/skills/forge/references/process-overview.md +143 -0
- package/adapters/cursor/skills/forge/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge/references/stage-exit-protocol.md +258 -0
- package/adapters/cursor/skills/forge-0-epic/references/epic-manifest-subcommands.md +11 -0
- package/adapters/cursor/skills/forge-0-epic/references/pipeline-state-schema.json +191 -0
- package/adapters/cursor/skills/forge-0-epic/references/portable-root.md +71 -0
- package/adapters/cursor/skills/forge-0-epic/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-0-epic/references/stage-exit-protocol.md +258 -0
- package/adapters/cursor/skills/forge-1-prd/forge-1-prd.mdc +2 -0
- package/adapters/cursor/skills/forge-1-prd/references/pipeline-state-schema.json +191 -0
- package/adapters/cursor/skills/forge-1-prd/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-1-prd/references/stage-exit-protocol.md +258 -0
- package/adapters/cursor/skills/forge-2-tech/forge-2-tech.mdc +3 -1
- package/adapters/cursor/skills/forge-2-tech/references/pipeline-state-schema.json +191 -0
- package/adapters/cursor/skills/forge-2-tech/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-2-tech/references/stack-resolution.md +54 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/_generic.md +111 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/go.md +157 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/python.md +184 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/rust.md +170 -0
- package/adapters/cursor/skills/forge-2-tech/references/stacks/typescript.md +134 -0
- package/adapters/cursor/skills/forge-2-tech/references/stage-exit-protocol.md +258 -0
- package/adapters/cursor/skills/forge-3-specs/forge-3-specs.mdc +2 -0
- package/adapters/cursor/skills/forge-3-specs/references/pipeline-state-schema.json +191 -0
- package/adapters/cursor/skills/forge-3-specs/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/_generic.md +111 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/go.md +157 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/python.md +184 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/rust.md +170 -0
- package/adapters/cursor/skills/forge-3-specs/references/stacks/typescript.md +134 -0
- package/adapters/cursor/skills/forge-3-specs/references/stage-exit-protocol.md +258 -0
- package/adapters/cursor/skills/forge-4-backlog/forge-4-backlog.mdc +5 -0
- package/adapters/cursor/skills/forge-4-backlog/references/forge-config-schema.json +236 -0
- package/adapters/cursor/skills/forge-4-backlog/references/pipeline-state-schema.json +191 -0
- package/adapters/cursor/skills/forge-4-backlog/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-4-backlog/references/stage-exit-protocol.md +258 -0
- package/adapters/cursor/skills/forge-5-loop/forge-5-loop.mdc +7 -9
- package/adapters/cursor/skills/forge-5-loop/references/forge-config-schema.json +236 -0
- package/adapters/cursor/skills/forge-5-loop/references/ralph-loop-contract.md +221 -0
- package/adapters/cursor/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/cursor/skills/forge-5-loop/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-5-loop/references/stage-exit-protocol.md +258 -0
- package/adapters/cursor/skills/forge-6-docs/forge-6-docs.mdc +4 -1
- package/adapters/cursor/skills/forge-6-docs/references/pipeline-state-schema.json +191 -0
- package/adapters/cursor/skills/forge-6-docs/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-fix/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-fix/references/stage-exit-protocol.md +258 -0
- package/adapters/cursor/skills/forge-guide/references/forge-config-schema.json +236 -0
- package/adapters/cursor/skills/forge-guide/references/process-overview.md +143 -0
- package/adapters/cursor/skills/forge-guide/references/ralph-loop-contract.md +221 -0
- package/adapters/cursor/skills/forge-guide/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-guide/references/stack-resolution.md +54 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/_generic.md +111 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/go.md +157 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/python.md +184 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/rust.md +170 -0
- package/adapters/cursor/skills/forge-guide/references/stacks/typescript.md +134 -0
- package/adapters/cursor/skills/forge-init/forge-init.mdc +1 -0
- package/adapters/cursor/skills/forge-verify/forge-verify.mdc +5 -4
- package/adapters/cursor/skills/forge-verify/references/pipeline-state-schema.json +191 -0
- package/adapters/cursor/skills/forge-verify/references/shared-conventions.md +295 -0
- package/adapters/cursor/skills/forge-verify/references/verification-checklists.md +88 -2
- package/adapters/gemini/.feature-forge-bundle.json +1 -1
- package/adapters/gemini/gemini-extension.json +1 -1
- package/adapters/gemini/references/epic-manifest-schema.json +5 -0
- package/adapters/gemini/references/forge-config-schema.json +5 -0
- package/adapters/gemini/references/shared-conventions.md +12 -0
- package/adapters/gemini/references/stacks/_generic.md +21 -0
- package/adapters/gemini/references/stacks/go.md +19 -0
- package/adapters/gemini/references/stacks/python.md +21 -0
- package/adapters/gemini/references/stacks/rust.md +19 -0
- package/adapters/gemini/references/stacks/typescript.md +23 -0
- package/adapters/gemini/scripts/epic-manifest.py +284 -5
- package/adapters/gemini/scripts/forge-bootstrap.py +1 -0
- package/adapters/gemini/scripts/forge-init.sh +2 -0
- package/adapters/gemini/scripts/forge-root.sh +55 -11
- package/adapters/gemini/scripts/forge-session.py +38 -1
- package/adapters/gemini/skills/forge/forge.md +9 -1
- package/adapters/gemini/skills/forge/references/pipeline-state-schema.json +191 -0
- package/adapters/gemini/skills/forge/references/process-overview.md +143 -0
- package/adapters/gemini/skills/forge/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge/references/stage-exit-protocol.md +258 -0
- package/adapters/gemini/skills/forge-0-epic/references/epic-manifest-subcommands.md +11 -0
- package/adapters/gemini/skills/forge-0-epic/references/pipeline-state-schema.json +191 -0
- package/adapters/gemini/skills/forge-0-epic/references/portable-root.md +71 -0
- package/adapters/gemini/skills/forge-0-epic/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-0-epic/references/stage-exit-protocol.md +258 -0
- package/adapters/gemini/skills/forge-1-prd/forge-1-prd.md +2 -0
- package/adapters/gemini/skills/forge-1-prd/references/pipeline-state-schema.json +191 -0
- package/adapters/gemini/skills/forge-1-prd/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-1-prd/references/stage-exit-protocol.md +258 -0
- package/adapters/gemini/skills/forge-2-tech/forge-2-tech.md +3 -1
- package/adapters/gemini/skills/forge-2-tech/references/pipeline-state-schema.json +191 -0
- package/adapters/gemini/skills/forge-2-tech/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-2-tech/references/stack-resolution.md +54 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/_generic.md +111 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/go.md +157 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/python.md +184 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/rust.md +170 -0
- package/adapters/gemini/skills/forge-2-tech/references/stacks/typescript.md +134 -0
- package/adapters/gemini/skills/forge-2-tech/references/stage-exit-protocol.md +258 -0
- package/adapters/gemini/skills/forge-3-specs/forge-3-specs.md +2 -0
- package/adapters/gemini/skills/forge-3-specs/references/pipeline-state-schema.json +191 -0
- package/adapters/gemini/skills/forge-3-specs/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/_generic.md +111 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/go.md +157 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/python.md +184 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/rust.md +170 -0
- package/adapters/gemini/skills/forge-3-specs/references/stacks/typescript.md +134 -0
- package/adapters/gemini/skills/forge-3-specs/references/stage-exit-protocol.md +258 -0
- package/adapters/gemini/skills/forge-4-backlog/forge-4-backlog.md +5 -0
- package/adapters/gemini/skills/forge-4-backlog/references/forge-config-schema.json +236 -0
- package/adapters/gemini/skills/forge-4-backlog/references/pipeline-state-schema.json +191 -0
- package/adapters/gemini/skills/forge-4-backlog/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-4-backlog/references/stage-exit-protocol.md +258 -0
- package/adapters/gemini/skills/forge-5-loop/forge-5-loop.md +7 -9
- package/adapters/gemini/skills/forge-5-loop/references/forge-config-schema.json +236 -0
- package/adapters/gemini/skills/forge-5-loop/references/ralph-loop-contract.md +221 -0
- package/adapters/gemini/skills/forge-5-loop/references/runner-contract.md +41 -0
- package/adapters/gemini/skills/forge-5-loop/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-5-loop/references/stage-exit-protocol.md +258 -0
- package/adapters/gemini/skills/forge-6-docs/forge-6-docs.md +4 -1
- package/adapters/gemini/skills/forge-6-docs/references/pipeline-state-schema.json +191 -0
- package/adapters/gemini/skills/forge-6-docs/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-fix/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-fix/references/stage-exit-protocol.md +258 -0
- package/adapters/gemini/skills/forge-guide/references/forge-config-schema.json +236 -0
- package/adapters/gemini/skills/forge-guide/references/process-overview.md +143 -0
- package/adapters/gemini/skills/forge-guide/references/ralph-loop-contract.md +221 -0
- package/adapters/gemini/skills/forge-guide/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-guide/references/stack-resolution.md +54 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/_generic.md +111 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/go.md +157 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/python.md +184 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/rust.md +170 -0
- package/adapters/gemini/skills/forge-guide/references/stacks/typescript.md +134 -0
- package/adapters/gemini/skills/forge-init/forge-init.md +1 -0
- package/adapters/gemini/skills/forge-verify/forge-verify.md +5 -4
- package/adapters/gemini/skills/forge-verify/references/pipeline-state-schema.json +191 -0
- package/adapters/gemini/skills/forge-verify/references/shared-conventions.md +295 -0
- package/adapters/gemini/skills/forge-verify/references/verification-checklists.md +88 -2
- package/package.json +1 -1
|
@@ -131,6 +131,7 @@ These files are transported byte-for-byte from canon into every `adapters/<agent
|
|
|
131
131
|
- `scripts/forge-root.sh` → `adapters/<agent>/scripts/forge-root.sh` (mode 0755, byte-identical — REQ-GEN-05).
|
|
132
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).
|
|
133
133
|
- each skill's own `references/` subdir → `adapters/<agent>/skills/<name>/references/` (verbatim, where present).
|
|
134
|
+
- each bundle-root SHARED reference a skill cites (e.g. `references/shared-conventions.md`, `references/stacks/`) → that skill's `adapters/<agent>/skills/<name>/references/` (verbatim), so a bare `references/X` prose read resolves skill-local on every install layout — including the non-plugin Claude tree with no `${CLAUDE_PLUGIN_ROOT}` (#132).
|
|
134
135
|
|
|
135
136
|
Regenerate all adapter output with `python3 scripts/build-adapters.py`.
|
|
136
137
|
|
|
@@ -74,6 +74,11 @@
|
|
|
74
74
|
"type": "array",
|
|
75
75
|
"items": { "$ref": "#/definitions/consumedContract" },
|
|
76
76
|
"description": "What this feature relies on from its dependencies (REQ-EPIC-03). May be empty."
|
|
77
|
+
},
|
|
78
|
+
"mutatesShared": {
|
|
79
|
+
"type": "array",
|
|
80
|
+
"items": { "type": "string" },
|
|
81
|
+
"description": "OPTIONAL precision hint for cross-member coupling detection (#144): project-root-relative file/glob paths this feature writes or migrates that are shared across the epic (e.g. a corpus a sibling's tests pin). When present, forge-verify CHECK-E10 uses it directly; when absent, CHECK-E10 falls back to grepping specs/backlog for mutated paths. Not a dependency edge — declarative only. May be omitted entirely."
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
},
|
|
@@ -51,6 +51,11 @@
|
|
|
51
51
|
"type": ["string", "null"],
|
|
52
52
|
"description": "Command to run tests. Null until set. Examples: 'bun test', 'pytest', 'go test ./...'. Used in acceptance criteria and verification."
|
|
53
53
|
},
|
|
54
|
+
"smokeCommand": {
|
|
55
|
+
"type": ["string", "null"],
|
|
56
|
+
"default": null,
|
|
57
|
+
"description": "Optional end-to-end smoke command that boots the wired application entrypoint and drives one happy-path request, exiting 0 on success. DISTINCT from testCommand (unit tests, which may self-bootstrap) and loopRunner.runCommand (the loop launcher). Consumed by impl-verify's runnability check (CHECK-I21): when set it is executed (pass iff exit 0); when null the check degrades to an advisory not-applicable finding, never a hard fail (mirrors how a null typeCheckCommand is treated). Examples: 'npm run smoke', './scripts/smoke.sh', 'curl -fsS localhost:3000/health'."
|
|
58
|
+
},
|
|
54
59
|
"loopIterationMultiplier": {
|
|
55
60
|
"type": "number",
|
|
56
61
|
"default": 1.5,
|
|
@@ -274,6 +274,18 @@ This write is **left uncommitted**: it is staged and committed as part of this s
|
|
|
274
274
|
|
|
275
275
|
**Incremental artifact tracking:** When a stage writes multiple files (e.g. forge-3-specs writing a suite of spec documents), update the `stages.{stage}.artifacts` array in `.pipeline-state.json` after writing each file — not just at stage completion. This is what makes the Interrupted inventory above precise about which files were successfully written.
|
|
276
276
|
|
|
277
|
+
## Stage-Completion Re-check
|
|
278
|
+
|
|
279
|
+
Invoke this block **at the head of any post-entry step that writes a stage artifact or runs the Scripted Stage Exit** — the Stage-Entry Guard runs only once, at the top of the skill. A **resumed or pasted mid-stage instruction** (e.g. "continue {stage}: write TRACEABILITY.md, run the stage exit") enters *below* the entry guard, so nothing re-checks completion before it overwrites a committed artifact and re-fires a finished exit — data-destructive if followed literally. This re-check is the idempotency backstop for that path. `{stage}` is the invoking skill's id.
|
|
280
|
+
|
|
281
|
+
**Re-read** `stages.{stage}` in `{resolvedFeatureDir}/.pipeline-state.json`, then classify by **provenance** — a legitimate completion runs in the same session that applied this stage's Entry Stamp; a replayed continuation finds a finished stage it did not produce:
|
|
282
|
+
|
|
283
|
+
1. **Proceed** when `stages.{stage}.status` is `"in-progress"` (this session's Entry Stamp — you are finishing the run you started) or absent/`pending`. Run the write / exit normally.
|
|
284
|
+
|
|
285
|
+
2. **Detect-and-refuse** when ALL of these hold: `stages.{stage}.status ∈ {"complete", "stale"}` **AND** the stage's artifacts (incl. `TRACEABILITY.md` for forge-3-specs) exist on disk **AND** a `commitHash` is recorded for the stage **AND** you did **not** author this stage earlier in the current session. This is a stale/replayed continuation of an already-finished, committed stage. Do **not** overwrite the artifact or re-run the exit. Route instead to the **Stage-Entry Guard**'s *Re-authoring* path: surface the same `AskUserQuestion` warning ("A completed {stage} artifact already exists for '{feature}' (v{n}{, marked stale}). Continuing will create a new version. Proceed?"). Only on explicit confirmation re-enter from the Entry Stamp (the version bumps at exit); otherwise **stop** and report that the stage is already complete — cite the recorded `commitHash` and offer `/feature-forge:forge {feature}` to see true state.
|
|
286
|
+
|
|
287
|
+
When you cannot confirm you authored the current run, treat it as a replay and refuse: a false refuse costs one confirmation click; a false proceed overwrites a committed artifact and re-churns a stage version. `--force` follows Force Mode (skip the gate, treat as a deliberate re-author).
|
|
288
|
+
|
|
277
289
|
## Force Mode
|
|
278
290
|
|
|
279
291
|
If the user passes `--force` as an argument, skip prerequisite validation with a warning:
|
|
@@ -79,6 +79,27 @@ Replace stack-specific checks with these generic equivalents:
|
|
|
79
79
|
| "JSDoc on every field" | "Documentation comments on every field" |
|
|
80
80
|
| "tsconfig.json extends root" | "Build configuration follows project conventions" |
|
|
81
81
|
|
|
82
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
83
|
+
|
|
84
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
85
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
86
|
+
Since no dedicated profile applies, identify these by role rather than exact filename:
|
|
87
|
+
|
|
88
|
+
- **Runtime entrypoints (a legitimate non-test call site):** the process/main entry the build's
|
|
89
|
+
run command actually launches (from the CI/`Makefile`/manifest "start" target), an HTTP/RPC handler
|
|
90
|
+
or route, a scheduled/worker task, or a CLI subcommand — **not** a file in the project's test
|
|
91
|
+
directory or matching its test-file convention.
|
|
92
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** any
|
|
93
|
+
module/hook the framework runs **once per process before serving** — a startup/lifecycle hook, an
|
|
94
|
+
import-time initializer, a global preload, or a shared test-setup module that bootstraps production
|
|
95
|
+
graphs. Side effects here run on every process start (and, under a dev/watch runtime, on every
|
|
96
|
+
reload).
|
|
97
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** database/ORM
|
|
98
|
+
clients and connection pools, message/queue clients, telemetry/observability SDKs, or a broad
|
|
99
|
+
whole-service-layer import. An init pulling any of these into a universal bootstrap entry is the
|
|
100
|
+
CHECK-I23 pattern — recommend lazy initialization at the first handler/worker that needs the graph,
|
|
101
|
+
not eagerly at process start.
|
|
102
|
+
|
|
82
103
|
## Acceptance Criteria Patterns
|
|
83
104
|
|
|
84
105
|
Use these placeholder patterns in backlog items. Replace `{typeCheckCommand}`, `{testCommand}`, and `{module}` with values from `forge.config.json` and the project structure:
|
|
@@ -84,6 +84,25 @@ When examining a Go project, check for:
|
|
|
84
84
|
- **Formatting**: `gofmt -l .` or `goimports -l .` (should produce no output)
|
|
85
85
|
- **Module tidiness**: `go mod tidy` (ensures `go.mod` and `go.sum` are consistent)
|
|
86
86
|
|
|
87
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
88
|
+
|
|
89
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
90
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
91
|
+
|
|
92
|
+
- **Runtime entrypoints (a legitimate non-test call site):** `func main()` in a `package main` under
|
|
93
|
+
`cmd/…` or the module root, an HTTP handler registered on a `*http.ServeMux` / router, a gRPC service
|
|
94
|
+
registration, or a worker/consumer goroutine started from `main` — not a `*_test.go` file.
|
|
95
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a package-level
|
|
96
|
+
`func init()` that constructs heavy clients, package-level `var` initializers that dial/connect at
|
|
97
|
+
import time, or a single `bootstrap`/`wire` module `main` calls before serving. `init()` and
|
|
98
|
+
package-var side effects run on **every** binary start, before `main` gets control.
|
|
99
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB drivers/pools
|
|
100
|
+
(`database/sql` + a driver, `pgx`, `gorm`, `mongo-driver`), message/queue clients (`sarama`/Kafka,
|
|
101
|
+
`amqp`, `go-redis`), telemetry SDKs (`go.opentelemetry.io/*`, `sentry-go`), or a broad internal
|
|
102
|
+
service package. An `init()` or package-var that dials any of these is the CHECK-I23 pattern —
|
|
103
|
+
recommend a `sync.Once` lazy constructor invoked from the first handler that needs it, not an
|
|
104
|
+
eager package-level connect.
|
|
105
|
+
|
|
87
106
|
## Testing
|
|
88
107
|
|
|
89
108
|
- **Framework**: `testing` stdlib package
|
|
@@ -111,6 +111,27 @@ async def refresh_session_token(
|
|
|
111
111
|
- **Import validation**: `mypy` with `--strict` or `--disallow-untyped-defs` catches missing type annotations
|
|
112
112
|
- **Module export validation**: `__all__` lists in `__init__.py` match spec's public API
|
|
113
113
|
|
|
114
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
115
|
+
|
|
116
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
117
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
118
|
+
|
|
119
|
+
- **Runtime entrypoints (a legitimate non-test call site):** a `if __name__ == "__main__":` block, a
|
|
120
|
+
`[project.scripts]` console-script target in `pyproject.toml`, an ASGI/WSGI app object
|
|
121
|
+
(`app = FastAPI()` / Django `wsgi.py` / `asgi.py`), a route/view/handler, a Celery/RQ task module, or
|
|
122
|
+
a management command — not a `test_*.py` / `conftest.py`.
|
|
123
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a package
|
|
124
|
+
`__init__.py` that eagerly instantiates heavy clients at import time, a framework startup hook
|
|
125
|
+
(FastAPI `lifespan` / `@app.on_event("startup")`, Django `AppConfig.ready()`, a Gunicorn/uvicorn
|
|
126
|
+
`--preload` module), or a `conftest.py` that bootstraps production graphs. Import-time side effects
|
|
127
|
+
here run on every process start.
|
|
128
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB/ORM engines
|
|
129
|
+
(`sqlalchemy.create_engine`, `psycopg`, Django ORM, `pymongo`, `redis`), task queues (`celery`,
|
|
130
|
+
`rq`, `kafka`), telemetry SDKs (`opentelemetry`, `sentry_sdk`), or a whole-service-layer package
|
|
131
|
+
import. An init pulling any of these at a universal entry is the CHECK-I23 pattern — recommend lazy
|
|
132
|
+
init (module-level `@lru_cache` factory, `Depends()` provider, or first-use lazy import) at the
|
|
133
|
+
handler that needs it.
|
|
134
|
+
|
|
114
135
|
## Testing
|
|
115
136
|
|
|
116
137
|
- **Framework**: pytest (with `conftest.py` for fixtures)
|
|
@@ -90,6 +90,25 @@ When examining a Rust project, check for:
|
|
|
90
90
|
- **Formatting**: `cargo fmt --check` (ensures code matches `rustfmt` style)
|
|
91
91
|
- **Unsafe audit**: `cargo geiger` (if security-sensitive — counts unsafe blocks)
|
|
92
92
|
|
|
93
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
94
|
+
|
|
95
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
96
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
97
|
+
|
|
98
|
+
- **Runtime entrypoints (a legitimate non-test call site):** `fn main()` in `main.rs` or a `[[bin]]`
|
|
99
|
+
target, an async runtime entry (`#[tokio::main]`), an HTTP handler registered on an axum/actix/warp
|
|
100
|
+
router, or a worker task spawned from `main` — not a `#[cfg(test)]` module or a `tests/` integration
|
|
101
|
+
file.
|
|
102
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** a `lazy_static!`
|
|
103
|
+
/ `once_cell::sync::Lazy` static that eagerly constructs heavy clients, a single `bootstrap`/`setup`
|
|
104
|
+
fn `main` calls before serving, or a `#[ctor]`-style pre-`main` initializer. A `Lazy` static is only
|
|
105
|
+
a risk when it is *forced* at startup rather than on first use.
|
|
106
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB pools
|
|
107
|
+
(`sqlx::Pool`, `diesel`, `deadpool`, `mongodb`), message/queue clients (`rdkafka`, `lapin`,
|
|
108
|
+
`redis`), telemetry SDKs (`opentelemetry`, `sentry`), or a broad internal service crate. Constructing
|
|
109
|
+
any of these eagerly at a universal entry is the CHECK-I23 pattern — recommend a `OnceCell`/`Lazy`
|
|
110
|
+
forced on **first use** by the handler that needs it, not at process start.
|
|
111
|
+
|
|
93
112
|
## Testing
|
|
94
113
|
|
|
95
114
|
- **Framework**: Built-in test harness (`#[test]`, `#[cfg(test)]`)
|
|
@@ -64,6 +64,29 @@ When examining a TypeScript project, check for:
|
|
|
64
64
|
- **Cross-package type checks**: `bun run typecheck` (or equivalent) passes for both the feature package AND packages that depend on it
|
|
65
65
|
- **Import path validation**: All import paths resolve correctly per the `exports` map in `package.json`
|
|
66
66
|
|
|
67
|
+
### Runtime Entrypoints & Bootstrap-Wiring Sites
|
|
68
|
+
|
|
69
|
+
Used by `CHECK-I22` (a runtime-required bootstrap needs a **non-test** caller on one of these) and
|
|
70
|
+
`CHECK-I23` (a heavy init wired into a **universal** bootstrap entry should move to a lazier site).
|
|
71
|
+
|
|
72
|
+
- **Runtime entrypoints (a legitimate non-test call site):** a `package.json` `bin` / CLI `main`; a
|
|
73
|
+
server entry that actually starts listening (`src/server.ts`, `src/index.ts` invoking `listen`); a
|
|
74
|
+
worker/consumer file; and for Next.js — `middleware.ts`, `app/**/route.ts` route handlers,
|
|
75
|
+
`app/**/{page,layout,template}.tsx`, server actions, and `instrumentation.ts`. Express/Hono/Fastify:
|
|
76
|
+
the file that constructs the app and calls `.listen()`.
|
|
77
|
+
- **Universal bootstrap entries (run on every startup — the `CHECK-I23` risk site):** Next.js
|
|
78
|
+
`instrumentation.ts` / `instrumentation.js` (its `register()` runs once per server process before
|
|
79
|
+
any request), an app-server preload/`register`/`--import` hook, a root `app/layout.tsx` that
|
|
80
|
+
eagerly imports server singletons, and global test setup (`vitest.setup.ts`, `jest.setup.ts`) if it
|
|
81
|
+
bootstraps production graphs. Wiring a heavy init here loads it on **every** cold start (and, under
|
|
82
|
+
the dev server, on every module re-evaluation).
|
|
83
|
+
- **Heavy server-only import markers (what makes an init "heavy" for `CHECK-I23`):** DB/ORM clients
|
|
84
|
+
(`drizzle-orm`, `@prisma/client`, `pg`, `mongoose`, `kysely`), queue/worker libs (`bullmq`, `bull`,
|
|
85
|
+
`kafkajs`, `ioredis`), telemetry/observability SDKs (`@opentelemetry/*`, `@sentry/node`), and a
|
|
86
|
+
whole-service-layer barrel (`import * as services from "@repo/services"`). An init that pulls any of
|
|
87
|
+
these into a universal bootstrap entry is the CHECK-I23 pattern — recommend lazy init at the first
|
|
88
|
+
route/handler/worker that needs it.
|
|
89
|
+
|
|
67
90
|
## Testing
|
|
68
91
|
|
|
69
92
|
- **Framework**: Vitest (most common in modern TS), Jest, or testing-library
|
|
@@ -27,6 +27,7 @@ import argparse
|
|
|
27
27
|
import json
|
|
28
28
|
import os
|
|
29
29
|
import re
|
|
30
|
+
import shutil
|
|
30
31
|
import sys
|
|
31
32
|
import tempfile
|
|
32
33
|
from datetime import datetime, timezone
|
|
@@ -46,6 +47,21 @@ PIPELINE_STATE_FILENAME: Final = ".pipeline-state.json"
|
|
|
46
47
|
MANIFEST_FILENAME: Final = "epic-manifest.json"
|
|
47
48
|
NARRATIVE_FILENAME: Final = "EPIC.md"
|
|
48
49
|
|
|
50
|
+
#: The authoritative forge-verify status vocabulary. SOURCE OF TRUTH:
|
|
51
|
+
#: references/pipeline-state-schema.json (definitions.verifyEntry.properties.status.enum).
|
|
52
|
+
#: A member state carrying a forge-verify-*.status outside this set is treated as
|
|
53
|
+
#: incomplete-for-orchestration but SURFACED (never guessed silently) by render_status
|
|
54
|
+
#: — a single typo'd status otherwise poisons the epic rollup + dependency gates (#148).
|
|
55
|
+
#: NOTE: forge-session.py keeps a byte-identical copy of this constant — flat, self-
|
|
56
|
+
#: contained scripts have no shared import module (each is copied verbatim into adapters).
|
|
57
|
+
KNOWN_VERIFY_STATUSES: Final = frozenset(
|
|
58
|
+
{"pending", "passed", "findings-reported", "findings-applied", "skipped"}
|
|
59
|
+
)
|
|
60
|
+
#: The subset of KNOWN_VERIFY_STATUSES that makes a member's forge-verify-impl count as
|
|
61
|
+
#: complete-for-orchestration (00 §7). A STRICT subset — 'findings-reported' (unfixed),
|
|
62
|
+
#: 'skipped', and 'pending' do NOT unblock dependents. Not collapsible into the set above.
|
|
63
|
+
_VERIFY_ORCH_COMPLETE: Final = frozenset({"passed", "findings-applied"})
|
|
64
|
+
|
|
49
65
|
|
|
50
66
|
# --------------------------------------------------------------------------- #
|
|
51
67
|
# Type Definitions (00-core-definitions.md §4, §5; 02 §8.4)
|
|
@@ -138,6 +154,10 @@ class RenderStatus(TypedDict):
|
|
|
138
154
|
rollup: Aggregate {complete, total} counts.
|
|
139
155
|
nextCommand: Recommended next command for the first actionable feature, or
|
|
140
156
|
None when nothing is actionable (all complete, empty epic, or paused).
|
|
157
|
+
warnings: Human-readable diagnostics that do NOT invalidate the graph but
|
|
158
|
+
need surfacing — currently, members carrying a ``forge-verify-*.status``
|
|
159
|
+
outside ``KNOWN_VERIFY_STATUSES`` (treated as incomplete, but silently so
|
|
160
|
+
would poison the rollup + dependency gates, #148). Empty in the common case.
|
|
141
161
|
"""
|
|
142
162
|
|
|
143
163
|
epic: str
|
|
@@ -147,6 +167,7 @@ class RenderStatus(TypedDict):
|
|
|
147
167
|
parallelEligible: list[str]
|
|
148
168
|
rollup: Rollup
|
|
149
169
|
nextCommand: str | None
|
|
170
|
+
warnings: list[str]
|
|
150
171
|
|
|
151
172
|
|
|
152
173
|
# --------------------------------------------------------------------------- #
|
|
@@ -541,6 +562,11 @@ _TOP_REQUIRED: Final = (
|
|
|
541
562
|
)
|
|
542
563
|
#: Required keys on each Feature object (00 §2.2).
|
|
543
564
|
_FEATURE_REQUIRED: Final = ("name", "charter", "dependsOn", "exposes", "consumes")
|
|
565
|
+
#: Optional keys on each Feature object. `mutatesShared` is the #144 precision
|
|
566
|
+
#: hint for cross-member coupling (array of project-root-relative path strings);
|
|
567
|
+
#: schema-legal when present, ignored when absent (mirrors
|
|
568
|
+
#: epic-manifest-schema.json definitions.feature.properties.mutatesShared).
|
|
569
|
+
_FEATURE_OPTIONAL: Final = ("mutatesShared",)
|
|
544
570
|
#: Required keys on each Contract (exposes[]) object (00 §2.3).
|
|
545
571
|
_CONTRACT_REQUIRED: Final = ("name", "kind", "summary")
|
|
546
572
|
#: Required keys on each ConsumedContract (consumes[]) object (00 §2.4).
|
|
@@ -616,11 +642,16 @@ def _schema_findings(manifest: dict) -> list[Finding]:
|
|
|
616
642
|
findings.append(_schema(f"feature {label!r} missing required key {key!r}", fname))
|
|
617
643
|
for key in feat:
|
|
618
644
|
# 'status' is rejected separately above via the dedicated 'cached-status' code.
|
|
619
|
-
if key not in _FEATURE_REQUIRED and key != "status":
|
|
645
|
+
if key not in _FEATURE_REQUIRED and key not in _FEATURE_OPTIONAL and key != "status":
|
|
620
646
|
findings.append(_schema(f"feature {label!r} has unknown key {key!r}", fname))
|
|
621
647
|
for key in ("name", "charter"):
|
|
622
648
|
if key in feat and not isinstance(feat[key], str):
|
|
623
649
|
findings.append(_schema(f"feature {label!r} {key} must be a string", fname))
|
|
650
|
+
if "mutatesShared" in feat and (
|
|
651
|
+
not isinstance(feat["mutatesShared"], list)
|
|
652
|
+
or not all(isinstance(p, str) for p in feat["mutatesShared"])
|
|
653
|
+
):
|
|
654
|
+
findings.append(_schema(f"feature {label!r} mutatesShared must be an array of strings", fname)) # noqa: E501
|
|
624
655
|
if "dependsOn" in feat:
|
|
625
656
|
if not isinstance(feat["dependsOn"], list) or not all(isinstance(d, str) for d in feat["dependsOn"]): # noqa: E501
|
|
626
657
|
findings.append(_schema(f"feature {label!r} dependsOn must be an array of strings", fname)) # noqa: E501
|
|
@@ -791,7 +822,41 @@ def is_complete_for_orchestration(state: dict) -> bool:
|
|
|
791
822
|
return True
|
|
792
823
|
if not isinstance(impl, dict):
|
|
793
824
|
return False
|
|
794
|
-
return impl.get("status") in
|
|
825
|
+
return impl.get("status") in _VERIFY_ORCH_COMPLETE
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
def _verify_status_warnings(name: str, state: dict) -> list[str]:
|
|
829
|
+
"""Flag any ``forge-verify-*.status`` outside the known vocabulary (#148).
|
|
830
|
+
|
|
831
|
+
An unrecognized status (e.g. the eye-slip ``findings-resolved``, conflated with the
|
|
832
|
+
adjacent ``findingsResolved`` count) is correctly treated as incomplete by
|
|
833
|
+
``is_complete_for_orchestration`` — but *silently*, so one typo on one member
|
|
834
|
+
under-reports the whole epic rollup and fabricates phantom ``unmetDeps`` on
|
|
835
|
+
dependents. Surfacing it turns that non-local corruption into a visible diagnostic.
|
|
836
|
+
|
|
837
|
+
Non-string / malformed status values (a list, an int) are also flagged, and the
|
|
838
|
+
membership test is guarded so an unhashable value never raises.
|
|
839
|
+
"""
|
|
840
|
+
stages = state.get("stages", {})
|
|
841
|
+
if not isinstance(stages, dict):
|
|
842
|
+
return []
|
|
843
|
+
warnings: list[str] = []
|
|
844
|
+
known = ", ".join(sorted(KNOWN_VERIFY_STATUSES))
|
|
845
|
+
for stage_name, entry in stages.items():
|
|
846
|
+
if not (isinstance(stage_name, str) and stage_name.startswith("forge-verify-")):
|
|
847
|
+
continue
|
|
848
|
+
if not isinstance(entry, dict):
|
|
849
|
+
continue
|
|
850
|
+
status = entry.get("status")
|
|
851
|
+
if status is None:
|
|
852
|
+
continue
|
|
853
|
+
if isinstance(status, str) and status in KNOWN_VERIFY_STATUSES:
|
|
854
|
+
continue
|
|
855
|
+
warnings.append(
|
|
856
|
+
f"{name}: unknown {stage_name} status {status!r} "
|
|
857
|
+
f"(treated as incomplete; expected one of {known})"
|
|
858
|
+
)
|
|
859
|
+
return warnings
|
|
795
860
|
|
|
796
861
|
|
|
797
862
|
def _read_state_safely(state_path: Path) -> dict:
|
|
@@ -926,14 +991,15 @@ def render_status(epic_dir: Path, specs_dir: Path) -> RenderStatus:
|
|
|
926
991
|
rows: list[FeatureStatus] = []
|
|
927
992
|
feature_dir_by_name: dict[str, Path] = {}
|
|
928
993
|
complete: dict[str, bool] = {}
|
|
994
|
+
warnings: list[str] = []
|
|
929
995
|
for feat in features:
|
|
930
996
|
name = feat["name"]
|
|
931
997
|
member_dir = contained_path(epic_dir, name)
|
|
932
998
|
feature_dir_by_name[name] = member_dir
|
|
933
999
|
rows.append(derive_status(member_dir))
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
)
|
|
1000
|
+
member_state = _read_state_safely(member_dir / PIPELINE_STATE_FILENAME)
|
|
1001
|
+
complete[name] = is_complete_for_orchestration(member_state)
|
|
1002
|
+
warnings.extend(_verify_status_warnings(name, member_state))
|
|
937
1003
|
|
|
938
1004
|
# (4) per-feature unmetDeps + blocked. A feature that is itself complete is
|
|
939
1005
|
# never "blocked" — unmet deps only matter for work not yet finished.
|
|
@@ -988,6 +1054,7 @@ def render_status(epic_dir: Path, specs_dir: Path) -> RenderStatus:
|
|
|
988
1054
|
"parallelEligible": parallel_eligible,
|
|
989
1055
|
"rollup": rollup,
|
|
990
1056
|
"nextCommand": next_command,
|
|
1057
|
+
"warnings": warnings,
|
|
991
1058
|
}
|
|
992
1059
|
|
|
993
1060
|
|
|
@@ -1195,6 +1262,180 @@ def set_status(epic_dir: Path, specs_dir: Path, status: str) -> list[Finding]:
|
|
|
1195
1262
|
return _bump_and_write(epic_dir, specs_dir, manifest)
|
|
1196
1263
|
|
|
1197
1264
|
|
|
1265
|
+
def _load_state_strict(state_path: Path) -> dict:
|
|
1266
|
+
"""Read a .pipeline-state.json, raising UsageError on a missing/corrupt file.
|
|
1267
|
+
|
|
1268
|
+
Unlike ``_read_state_safely`` (which downgrades a torn read to ``{}`` for the
|
|
1269
|
+
read-only dashboard), an adopt mutation must NOT silently discard the
|
|
1270
|
+
standalone's stage history — a corrupt source is a hard stop (exit 2) so the
|
|
1271
|
+
human fixes it before the irreversible move.
|
|
1272
|
+
"""
|
|
1273
|
+
try:
|
|
1274
|
+
parsed = json.loads(state_path.read_text(encoding="utf-8"))
|
|
1275
|
+
except OSError as exc:
|
|
1276
|
+
raise UsageError(f"cannot read {state_path}: {exc}")
|
|
1277
|
+
except json.JSONDecodeError as exc:
|
|
1278
|
+
raise UsageError(f"{state_path} is not valid JSON: {exc}")
|
|
1279
|
+
if not isinstance(parsed, dict):
|
|
1280
|
+
raise UsageError(f"{state_path} is not a JSON object")
|
|
1281
|
+
return parsed
|
|
1282
|
+
|
|
1283
|
+
|
|
1284
|
+
def _merge_member_state(stub: dict, standalone: dict, epic_name: str) -> dict:
|
|
1285
|
+
"""Merge a detached standalone's state onto an epic member stub (Issue #126).
|
|
1286
|
+
|
|
1287
|
+
The **stub** is the base — it carries the correct ``epic`` and ``branch``
|
|
1288
|
+
back-pointers that the epic minted. The **standalone** holds the real work, so
|
|
1289
|
+
its stage history / artifacts / currentStage overlay the stub. The ``epic``
|
|
1290
|
+
back-pointer is forced to the target epic; the stub's ``branch`` is preserved
|
|
1291
|
+
(only falling back to the standalone's when the stub has none).
|
|
1292
|
+
"""
|
|
1293
|
+
merged = dict(stub)
|
|
1294
|
+
for key in ("currentStage", "artifacts", "notes",
|
|
1295
|
+
"deferredDecisions", "epicChangeRequests"):
|
|
1296
|
+
if key in standalone:
|
|
1297
|
+
merged[key] = standalone[key]
|
|
1298
|
+
stages = dict(stub.get("stages") or {})
|
|
1299
|
+
stages.update(standalone.get("stages") or {})
|
|
1300
|
+
merged["stages"] = stages
|
|
1301
|
+
merged["epic"] = epic_name
|
|
1302
|
+
if not (isinstance(stub.get("branch"), str) and stub["branch"]) and \
|
|
1303
|
+
isinstance(standalone.get("branch"), str) and standalone["branch"]:
|
|
1304
|
+
merged["branch"] = standalone["branch"]
|
|
1305
|
+
return merged
|
|
1306
|
+
|
|
1307
|
+
|
|
1308
|
+
def adopt_feature(
|
|
1309
|
+
epic_dir: Path,
|
|
1310
|
+
specs_dir: Path,
|
|
1311
|
+
feature: str,
|
|
1312
|
+
charter: str | None,
|
|
1313
|
+
deps: list[str],
|
|
1314
|
+
) -> dict:
|
|
1315
|
+
"""Reconcile a detached standalone feature into an epic member (Issue #126).
|
|
1316
|
+
|
|
1317
|
+
The scripted recovery for a **split-brain epic** (#125): a feature that should
|
|
1318
|
+
be an epic member was forged as a flat standalone at ``{specsDir}/{feature}/``.
|
|
1319
|
+
This relocates it into the member slot ``{epicDir}/{feature}/``, merging state
|
|
1320
|
+
so the stub's ``epic``/``branch`` back-pointers survive, removes the flat dir
|
|
1321
|
+
(no residual), and adds the feature to the manifest if absent.
|
|
1322
|
+
|
|
1323
|
+
Operates on the CURRENT tree/branch — both the flat standalone and the epic
|
|
1324
|
+
manifest must already be present (the human brings a cross-branch standalone
|
|
1325
|
+
onto the epic's home branch first, per ``docs/recovery-detached-epic-member.md``;
|
|
1326
|
+
EPIC.md prose is regenerated separately via forge-0-epic). Deliberately ordered
|
|
1327
|
+
**relocate-then-manifest**: after the flat dir is gone the feature name maps to
|
|
1328
|
+
exactly one dir, so ``add_feature``'s global-uniqueness re-validation stays
|
|
1329
|
+
clean. Re-entrant — a half-finished run (files moved, manifest not yet updated)
|
|
1330
|
+
completes on re-run.
|
|
1331
|
+
|
|
1332
|
+
Args:
|
|
1333
|
+
epic_dir: The target epic subtree directory (must hold epic-manifest.json).
|
|
1334
|
+
specs_dir: The configured specs directory.
|
|
1335
|
+
feature: The detached standalone feature name to adopt.
|
|
1336
|
+
charter: Charter for the manifest entry when the feature is not yet a
|
|
1337
|
+
member; a default is used when omitted.
|
|
1338
|
+
deps: dependsOn for the new manifest entry (ignored if already a member).
|
|
1339
|
+
|
|
1340
|
+
Returns:
|
|
1341
|
+
A summary dict (``adopted``/``relocated``/``manifestUpdated``/…) on success.
|
|
1342
|
+
|
|
1343
|
+
Raises:
|
|
1344
|
+
UsageError: Unsafe name, missing manifest, nothing to adopt, or an I/O
|
|
1345
|
+
failure (exit 2).
|
|
1346
|
+
FindingsError: A blocking manifest finding from ``add_feature`` (e.g. an
|
|
1347
|
+
unknown dependency) after relocation — exit 1; re-run after fixing.
|
|
1348
|
+
"""
|
|
1349
|
+
assert_safe_name(feature)
|
|
1350
|
+
for dep in deps:
|
|
1351
|
+
assert_safe_name(dep)
|
|
1352
|
+
|
|
1353
|
+
# The epic manifest must exist on this tree (exit 2 if not — nothing to adopt into).
|
|
1354
|
+
load_manifest(epic_dir)
|
|
1355
|
+
|
|
1356
|
+
flat_dir = contained_path(specs_dir, feature)
|
|
1357
|
+
member_dir = contained_path(epic_dir, feature)
|
|
1358
|
+
if flat_dir == member_dir:
|
|
1359
|
+
raise UsageError(f"feature {feature!r} resolves to the epic dir itself")
|
|
1360
|
+
|
|
1361
|
+
flat_state = flat_dir / PIPELINE_STATE_FILENAME
|
|
1362
|
+
member_state = member_dir / PIPELINE_STATE_FILENAME
|
|
1363
|
+
flat_exists = flat_state.is_file()
|
|
1364
|
+
member_exists = member_state.is_file()
|
|
1365
|
+
|
|
1366
|
+
if not flat_exists and not member_exists:
|
|
1367
|
+
raise UsageError(
|
|
1368
|
+
f"nothing to adopt: neither a standalone {flat_dir} nor a member "
|
|
1369
|
+
f"{member_dir} has a {PIPELINE_STATE_FILENAME}"
|
|
1370
|
+
)
|
|
1371
|
+
|
|
1372
|
+
relocated = False
|
|
1373
|
+
if flat_exists:
|
|
1374
|
+
standalone = _load_state_strict(flat_state)
|
|
1375
|
+
if member_exists:
|
|
1376
|
+
merged = _merge_member_state(
|
|
1377
|
+
_read_state_safely(member_state), standalone, epic_dir.name
|
|
1378
|
+
)
|
|
1379
|
+
else:
|
|
1380
|
+
merged = dict(standalone)
|
|
1381
|
+
merged["epic"] = epic_dir.name
|
|
1382
|
+
# Move every artifact except the state file (merged separately) into the
|
|
1383
|
+
# member slot; the standalone (real work) wins on any name collision.
|
|
1384
|
+
member_dir.mkdir(parents=True, exist_ok=True)
|
|
1385
|
+
for child in sorted(flat_dir.iterdir()):
|
|
1386
|
+
if child.name == PIPELINE_STATE_FILENAME:
|
|
1387
|
+
continue
|
|
1388
|
+
dest = member_dir / child.name
|
|
1389
|
+
if dest.is_dir():
|
|
1390
|
+
shutil.rmtree(dest)
|
|
1391
|
+
elif dest.exists():
|
|
1392
|
+
dest.unlink()
|
|
1393
|
+
shutil.move(str(child), str(dest))
|
|
1394
|
+
atomic_write(member_state, merged)
|
|
1395
|
+
shutil.rmtree(flat_dir)
|
|
1396
|
+
relocated = True
|
|
1397
|
+
else:
|
|
1398
|
+
# Already nested (a prior run moved the files) — only ensure the back-pointer.
|
|
1399
|
+
stub = _read_state_safely(member_state)
|
|
1400
|
+
if stub.get("epic") != epic_dir.name:
|
|
1401
|
+
stub["epic"] = epic_dir.name
|
|
1402
|
+
atomic_write(member_state, stub)
|
|
1403
|
+
|
|
1404
|
+
# Ensure the feature is a manifest member (idempotent — the flat dir is gone now,
|
|
1405
|
+
# so add_feature's tree-uniqueness re-check sees exactly one dir for the name).
|
|
1406
|
+
manifest = load_manifest(epic_dir)
|
|
1407
|
+
already_member = any(
|
|
1408
|
+
isinstance(f, dict) and f.get("name") == feature
|
|
1409
|
+
for f in manifest.get("features", [])
|
|
1410
|
+
)
|
|
1411
|
+
manifest_updated = False
|
|
1412
|
+
if not already_member:
|
|
1413
|
+
findings = add_feature(
|
|
1414
|
+
epic_dir, specs_dir, feature,
|
|
1415
|
+
charter or f"Adopted into the {epic_dir.name} epic from a detached standalone.",
|
|
1416
|
+
deps,
|
|
1417
|
+
)
|
|
1418
|
+
if findings:
|
|
1419
|
+
raise FindingsError(findings)
|
|
1420
|
+
manifest_updated = True
|
|
1421
|
+
|
|
1422
|
+
return {
|
|
1423
|
+
"adopted": True,
|
|
1424
|
+
"epic": epic_dir.name,
|
|
1425
|
+
"feature": feature,
|
|
1426
|
+
"memberDir": str(member_dir),
|
|
1427
|
+
"relocated": relocated,
|
|
1428
|
+
"manifestUpdated": manifest_updated,
|
|
1429
|
+
"wasAlreadyMember": already_member,
|
|
1430
|
+
"nextSteps": [
|
|
1431
|
+
f"Regenerate EPIC.md prose via /feature-forge:forge-0-epic {epic_dir.name}",
|
|
1432
|
+
f"Confirm the dashboard via /feature-forge:forge {epic_dir.name}",
|
|
1433
|
+
f"Verify the member via /feature-forge:forge-verify {feature}",
|
|
1434
|
+
"Commit the surgery (stage the epic subtree and the removed flat dir).",
|
|
1435
|
+
],
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
|
|
1198
1439
|
# --------------------------------------------------------------------------- #
|
|
1199
1440
|
# CLI Dispatch (02 §9)
|
|
1200
1441
|
# --------------------------------------------------------------------------- #
|
|
@@ -1239,6 +1480,10 @@ def _print_status_table(status: RenderStatus) -> None:
|
|
|
1239
1480
|
marker = "⚠️ BLOCKING" if row["blockingEpicChangeRequests"] else "⚠️"
|
|
1240
1481
|
line += f" — {marker} {row['openEpicChangeRequests']} pending epic change(s)"
|
|
1241
1482
|
print(line)
|
|
1483
|
+
if status["warnings"]:
|
|
1484
|
+
print("Warnings:")
|
|
1485
|
+
for warning in status["warnings"]:
|
|
1486
|
+
print(f" ⚠️ {warning}")
|
|
1242
1487
|
if status["actionable"]:
|
|
1243
1488
|
print(f"Actionable: {', '.join(status['actionable'])}")
|
|
1244
1489
|
if status["parallelEligible"]:
|
|
@@ -1285,6 +1530,25 @@ def _dispatch(args: argparse.Namespace, specs_dir: Path) -> int:
|
|
|
1285
1530
|
_print_status_table(status)
|
|
1286
1531
|
return 0
|
|
1287
1532
|
|
|
1533
|
+
if cmd == "adopt-feature":
|
|
1534
|
+
epic_dir = contained_path(specs_dir, args.epic)
|
|
1535
|
+
summary = adopt_feature(
|
|
1536
|
+
epic_dir, specs_dir, args.name, args.charter,
|
|
1537
|
+
_split_list(args.depends_on),
|
|
1538
|
+
)
|
|
1539
|
+
if args.json_output:
|
|
1540
|
+
print(json.dumps(summary, indent=2, ensure_ascii=False))
|
|
1541
|
+
else:
|
|
1542
|
+
print(f"Adopted {summary['feature']!r} into epic {summary['epic']!r}.")
|
|
1543
|
+
print(f" member dir: {summary['memberDir']}")
|
|
1544
|
+
print(f" relocated files: {summary['relocated']}")
|
|
1545
|
+
print(f" manifest added: {summary['manifestUpdated']}"
|
|
1546
|
+
f"{' (already a member)' if summary['wasAlreadyMember'] else ''}")
|
|
1547
|
+
print("Next steps:")
|
|
1548
|
+
for step in summary["nextSteps"]:
|
|
1549
|
+
print(f" - {step}")
|
|
1550
|
+
return 0
|
|
1551
|
+
|
|
1288
1552
|
# Mutators ---------------------------------------------------------------
|
|
1289
1553
|
if cmd in {"add-feature", "remove-feature", "reorder", "set-dep", "set-status"}:
|
|
1290
1554
|
epic_dir = contained_path(specs_dir, args.epic)
|
|
@@ -1358,6 +1622,21 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1358
1622
|
add_specs_dir(p_add)
|
|
1359
1623
|
add_json(p_add)
|
|
1360
1624
|
|
|
1625
|
+
# adopt-feature --------------------------------------------------------- #
|
|
1626
|
+
p_adopt = sub.add_parser(
|
|
1627
|
+
"adopt-feature",
|
|
1628
|
+
help="Reconcile a detached standalone feature into an epic member (#126)",
|
|
1629
|
+
)
|
|
1630
|
+
p_adopt.add_argument("epic")
|
|
1631
|
+
p_adopt.add_argument("name")
|
|
1632
|
+
p_adopt.add_argument(
|
|
1633
|
+
"--charter", default=None,
|
|
1634
|
+
help="Charter for the manifest entry when the feature is not yet a member",
|
|
1635
|
+
)
|
|
1636
|
+
p_adopt.add_argument("--depends-on", dest="depends_on", default="", help="Comma list")
|
|
1637
|
+
add_specs_dir(p_adopt)
|
|
1638
|
+
add_json(p_adopt)
|
|
1639
|
+
|
|
1361
1640
|
# remove-feature -------------------------------------------------------- #
|
|
1362
1641
|
p_remove = sub.add_parser("remove-feature", help="Remove a member feature")
|
|
1363
1642
|
p_remove.add_argument("epic")
|
|
@@ -568,6 +568,7 @@ def write_config(answers: Answers, target: Path, sentinel: Sentinel) -> None:
|
|
|
568
568
|
"stack": None,
|
|
569
569
|
"typeCheckCommand": None,
|
|
570
570
|
"testCommand": None,
|
|
571
|
+
"smokeCommand": None,
|
|
571
572
|
"loopIterationMultiplier": 1.5,
|
|
572
573
|
# Navigator keys — kept in lockstep with forge-init.sh's emitted field set
|
|
573
574
|
# (00 §7, REQ-CFG-02) so a bootstrapped config == forge-init's + loopRunner.
|
|
@@ -21,6 +21,7 @@ cat > "$CONFIG_FILE" << 'EOF'
|
|
|
21
21
|
"stack": null,
|
|
22
22
|
"typeCheckCommand": null,
|
|
23
23
|
"testCommand": null,
|
|
24
|
+
"smokeCommand": null,
|
|
24
25
|
"loopIterationMultiplier": 1.5,
|
|
25
26
|
"autoInvokeNextStage": true,
|
|
26
27
|
"contextWindowTokens": null,
|
|
@@ -42,6 +43,7 @@ echo " commitPrefix: forge"
|
|
|
42
43
|
echo " stack: null (auto-detected during forge-2-tech)"
|
|
43
44
|
echo " typeCheckCommand: null (auto-detected during forge-2-tech)"
|
|
44
45
|
echo " testCommand: null (auto-detected during forge-2-tech)"
|
|
46
|
+
echo " smokeCommand: null (optional end-to-end smoke; enables impl-verify runnability check)"
|
|
45
47
|
echo " loopIterationMultiplier: 1.5 (multiplier for loop iterations)"
|
|
46
48
|
echo " autoInvokeNextStage: true (navigator auto-starts the next stage after you confirm)"
|
|
47
49
|
echo " contextWindowTokens: null (infer; set to 1000000 on a 1M-context model)"
|