@opengsd/gsd-path 1.0.0 → 1.1.0
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/AGENTS.md +28 -11
- package/DOCS.md +6 -5
- package/FULL.md +9 -13
- package/QUICK.md +3 -3
- package/README.md +151 -14
- package/RUNTIME.md +7 -1
- package/WORKFLOW.md +30 -14
- package/package.json +15 -1
- package/platforms/shared-agents/dispatch.md +6 -0
- package/scripts/archive_milestone.py +10 -118
- package/scripts/bootstrap_repository.py +25 -3
- package/scripts/build_recovery.py +287 -0
- package/scripts/check_docs_audit.py +5 -3
- package/scripts/check_handoffs.py +1 -1
- package/scripts/detect_project.py +11 -2
- package/scripts/dispatch_driver.py +4 -2
- package/scripts/git_guard.py +2 -1
- package/scripts/install.mjs +2 -0
- package/scripts/install.py +2 -0
- package/scripts/integration.py +28 -22
- package/scripts/isolation.py +9 -1
- package/scripts/lean_verification.py +1 -1
- package/scripts/pipeline_diagnose.py +12 -3
- package/scripts/pipeline_state.py +114 -186
- package/scripts/pipeline_undo.py +11 -7
- package/scripts/promote_lookahead.py +30 -162
- package/scripts/roadmap.py +321 -0
- package/scripts/skill-resources.json +79 -1
- package/scripts/state_checkpoint.py +109 -72
- package/scripts/state_promote.py +42 -44
- package/scripts/task_context.py +73 -0
- package/scripts/token_budget.py +40 -1
- package/scripts/workflow_run.py +116 -7
- package/skills/gsd-path/BUILD.md +47 -575
- package/skills/gsd-path/DECIDE.md +2 -6
- package/skills/gsd-path/DEFINE.md +10 -14
- package/skills/gsd-path/INSPECT.md +30 -8
- package/skills/gsd-path/PLAN.md +25 -17
- package/skills/gsd-path/RESEARCH.md +3 -6
- package/skills/gsd-path/ROADMAP.md +22 -58
- package/skills/gsd-path/SHIP.md +5 -4
- package/skills/gsd-path/SKILL.md +24 -14
- package/skills/gsd-path/references/build-abandon.md +33 -0
- package/skills/gsd-path/references/build-native.md +494 -0
- package/skills/gsd-path/references/build-recovery.md +78 -0
- package/skills/gsd-path/references/coder.md +8 -3
- package/skills/gsd-path/references/dispatch.md +6 -0
- package/skills/gsd-path/references/docs-auditor.md +14 -0
- package/skills/gsd-path/references/reviewer-final.md +21 -0
- package/skills/gsd-path/references/reviewer-panel.md +17 -0
- package/skills/gsd-path/references/reviewer-skeptic.md +20 -0
- package/skills/gsd-path/references/reviewer-wave.md +89 -0
- package/skills/gsd-path/references/reviewer.md +12 -150
- package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/discussion_records.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/integration.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/archive_milestone.py +10 -118
- package/skills/gsd-path/scripts/bootstrap_repository.py +25 -3
- package/skills/gsd-path/scripts/build_recovery.py +287 -0
- package/skills/gsd-path/scripts/check_docs_audit.py +5 -3
- package/skills/gsd-path/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path/scripts/detect_project.py +11 -2
- package/skills/gsd-path/scripts/dispatch_driver.py +4 -2
- package/skills/gsd-path/scripts/integration.py +28 -22
- package/skills/gsd-path/scripts/isolation.py +9 -1
- package/skills/gsd-path/scripts/lean_verification.py +1 -1
- package/skills/gsd-path/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path/scripts/promote_lookahead.py +30 -162
- package/skills/gsd-path/scripts/roadmap.py +321 -0
- package/skills/gsd-path/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path/scripts/state_promote.py +42 -44
- package/skills/gsd-path/scripts/task_context.py +73 -0
- package/skills/gsd-path/scripts/token_budget.py +40 -1
- package/skills/gsd-path/scripts/workflow_run.py +116 -7
- package/skills/gsd-path/templates/wave-review.md +1 -1
- package/skills/gsd-path-build/SKILL.md +47 -575
- package/skills/gsd-path-build/references/build-abandon.md +33 -0
- package/skills/gsd-path-build/references/build-native.md +494 -0
- package/skills/gsd-path-build/references/build-recovery.md +78 -0
- package/skills/gsd-path-build/references/coder.md +8 -3
- package/skills/gsd-path-build/references/dispatch.md +6 -0
- package/skills/gsd-path-build/references/reviewer-final.md +21 -0
- package/skills/gsd-path-build/references/reviewer-panel.md +17 -0
- package/skills/gsd-path-build/references/reviewer-skeptic.md +20 -0
- package/skills/gsd-path-build/references/reviewer-wave.md +89 -0
- package/skills/gsd-path-build/references/reviewer.md +12 -150
- package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/archive_milestone.py +10 -118
- package/skills/gsd-path-build/scripts/build_recovery.py +287 -0
- package/skills/gsd-path-build/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path-build/scripts/detect_project.py +1526 -0
- package/skills/gsd-path-build/scripts/dispatch_driver.py +4 -2
- package/skills/gsd-path-build/scripts/integration.py +28 -22
- package/skills/gsd-path-build/scripts/isolation.py +9 -1
- package/skills/gsd-path-build/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path-build/scripts/promote_lookahead.py +283 -0
- package/skills/gsd-path-build/scripts/roadmap.py +321 -0
- package/skills/gsd-path-build/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path-build/scripts/state_promote.py +42 -44
- package/skills/gsd-path-build/scripts/task_context.py +73 -0
- package/skills/gsd-path-build/scripts/token_budget.py +40 -1
- package/skills/gsd-path-build/scripts/workflow_run.py +116 -7
- package/skills/gsd-path-build/templates/wave-review.md +1 -1
- package/skills/gsd-path-decide/SKILL.md +2 -6
- package/skills/gsd-path-decide/references/dispatch.md +6 -0
- package/skills/gsd-path-decide/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/archive_milestone.py +2157 -0
- package/skills/gsd-path-decide/scripts/build_recovery.py +287 -0
- package/skills/gsd-path-decide/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path-decide/scripts/detect_project.py +1526 -0
- package/skills/gsd-path-decide/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path-decide/scripts/integration.py +1439 -0
- package/skills/gsd-path-decide/scripts/isolation.py +9 -1
- package/skills/gsd-path-decide/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path-decide/scripts/promote_lookahead.py +283 -0
- package/skills/gsd-path-decide/scripts/review_panel.py +553 -0
- package/skills/gsd-path-decide/scripts/roadmap.py +321 -0
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path-decide/scripts/state_promote.py +42 -44
- package/skills/gsd-path-define/SKILL.md +10 -14
- package/skills/gsd-path-define/references/build-recovery.md +78 -0
- package/skills/gsd-path-define/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/archive_milestone.py +2157 -0
- package/skills/gsd-path-define/scripts/build_recovery.py +287 -0
- package/skills/gsd-path-define/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path-define/scripts/detect_project.py +11 -2
- package/skills/gsd-path-define/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path-define/scripts/integration.py +1439 -0
- package/skills/gsd-path-define/scripts/isolation.py +9 -1
- package/skills/gsd-path-define/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path-define/scripts/promote_lookahead.py +283 -0
- package/skills/gsd-path-define/scripts/review_panel.py +553 -0
- package/skills/gsd-path-define/scripts/roadmap.py +321 -0
- package/skills/gsd-path-define/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path-define/scripts/state_promote.py +42 -44
- package/skills/gsd-path-docs-audit/references/dispatch.md +6 -0
- package/skills/gsd-path-docs-audit/references/docs-auditor.md +14 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +2157 -0
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +287 -0
- package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +5 -3
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path-docs-audit/scripts/detect_project.py +1526 -0
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path-docs-audit/scripts/integration.py +1439 -0
- package/skills/gsd-path-docs-audit/scripts/isolation.py +9 -1
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +283 -0
- package/skills/gsd-path-docs-audit/scripts/review_panel.py +553 -0
- package/skills/gsd-path-docs-audit/scripts/roadmap.py +321 -0
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path-docs-audit/scripts/state_promote.py +42 -44
- package/skills/gsd-path-forensics/scripts/__pycache__/pipeline_undo.cpython-312.pyc +0 -0
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +12 -3
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +11 -7
- package/skills/gsd-path-inspect/SKILL.md +30 -8
- package/skills/gsd-path-inspect/references/dispatch.md +6 -0
- package/skills/gsd-path-inspect/references/docs-auditor.md +14 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/archive_milestone.py +2157 -0
- package/skills/gsd-path-inspect/scripts/build_recovery.py +287 -0
- package/skills/gsd-path-inspect/scripts/check_docs_audit.py +5 -3
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path-inspect/scripts/detect_project.py +11 -2
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path-inspect/scripts/integration.py +1439 -0
- package/skills/gsd-path-inspect/scripts/isolation.py +9 -1
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path-inspect/scripts/promote_lookahead.py +283 -0
- package/skills/gsd-path-inspect/scripts/review_panel.py +553 -0
- package/skills/gsd-path-inspect/scripts/roadmap.py +321 -0
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path-inspect/scripts/state_promote.py +42 -44
- package/skills/gsd-path-inspect/scripts/workflow_run.py +226 -0
- package/skills/gsd-path-plan/SKILL.md +25 -17
- package/skills/gsd-path-plan/references/build-recovery.md +78 -0
- package/skills/gsd-path-plan/references/dispatch.md +6 -0
- package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/archive_milestone.py +2157 -0
- package/skills/gsd-path-plan/scripts/build_recovery.py +287 -0
- package/skills/gsd-path-plan/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path-plan/scripts/detect_project.py +1526 -0
- package/skills/gsd-path-plan/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path-plan/scripts/integration.py +1439 -0
- package/skills/gsd-path-plan/scripts/isolation.py +9 -1
- package/skills/gsd-path-plan/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path-plan/scripts/promote_lookahead.py +283 -0
- package/skills/gsd-path-plan/scripts/roadmap.py +321 -0
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path-plan/scripts/state_promote.py +42 -44
- package/skills/gsd-path-plan/scripts/workflow_run.py +116 -7
- package/skills/gsd-path-research/SKILL.md +3 -6
- package/skills/gsd-path-research/references/dispatch.md +6 -0
- package/skills/gsd-path-research/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/archive_milestone.py +2157 -0
- package/skills/gsd-path-research/scripts/build_recovery.py +287 -0
- package/skills/gsd-path-research/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path-research/scripts/detect_project.py +1526 -0
- package/skills/gsd-path-research/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path-research/scripts/integration.py +1439 -0
- package/skills/gsd-path-research/scripts/isolation.py +9 -1
- package/skills/gsd-path-research/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path-research/scripts/promote_lookahead.py +283 -0
- package/skills/gsd-path-research/scripts/review_panel.py +553 -0
- package/skills/gsd-path-research/scripts/roadmap.py +321 -0
- package/skills/gsd-path-research/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path-research/scripts/state_promote.py +42 -44
- package/skills/gsd-path-roadmap/SKILL.md +22 -58
- package/skills/gsd-path-roadmap/references/dispatch.md +6 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +10 -118
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +287 -0
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path-roadmap/scripts/detect_project.py +11 -2
- package/skills/gsd-path-roadmap/scripts/integration.py +28 -22
- package/skills/gsd-path-roadmap/scripts/isolation.py +9 -1
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +30 -162
- package/skills/gsd-path-roadmap/scripts/roadmap.py +321 -0
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path-roadmap/scripts/state_promote.py +42 -44
- package/skills/gsd-path-ship/SKILL.md +5 -4
- package/skills/gsd-path-ship/references/dispatch.md +6 -0
- package/skills/gsd-path-ship/references/reviewer-final.md +21 -0
- package/skills/gsd-path-ship/references/reviewer-panel.md +17 -0
- package/skills/gsd-path-ship/references/reviewer-skeptic.md +20 -0
- package/skills/gsd-path-ship/references/reviewer-wave.md +89 -0
- package/skills/gsd-path-ship/references/reviewer.md +12 -150
- package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/archive_milestone.py +10 -118
- package/skills/gsd-path-ship/scripts/build_recovery.py +287 -0
- package/skills/gsd-path-ship/scripts/check_handoffs.py +1 -1
- package/skills/gsd-path-ship/scripts/detect_project.py +1526 -0
- package/skills/gsd-path-ship/scripts/integration.py +28 -22
- package/skills/gsd-path-ship/scripts/isolation.py +9 -1
- package/skills/gsd-path-ship/scripts/lean_verification.py +1 -1
- package/skills/gsd-path-ship/scripts/pipeline_state.py +114 -186
- package/skills/gsd-path-ship/scripts/promote_lookahead.py +283 -0
- package/skills/gsd-path-ship/scripts/roadmap.py +321 -0
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +109 -72
- package/skills/gsd-path-ship/scripts/state_promote.py +42 -44
- package/skills/gsd-path-ship/scripts/workflow_run.py +116 -7
- package/skills/gsd-path-ship/templates/wave-review.md +1 -1
- package/skills/gsd-path-undo/scripts/pipeline_undo.py +11 -7
- package/skills/path/BUILD.md +47 -575
- package/skills/path/DECIDE.md +2 -6
- package/skills/path/DEFINE.md +10 -14
- package/skills/path/INSPECT.md +30 -8
- package/skills/path/PLAN.md +25 -17
- package/skills/path/RESEARCH.md +3 -6
- package/skills/path/ROADMAP.md +22 -58
- package/skills/path/SHIP.md +5 -4
- package/skills/path/SKILL.md +24 -14
- package/skills/path/references/build-abandon.md +33 -0
- package/skills/path/references/build-native.md +494 -0
- package/skills/path/references/build-recovery.md +78 -0
- package/skills/path/references/coder.md +8 -3
- package/skills/path/references/dispatch.md +6 -0
- package/skills/path/references/docs-auditor.md +14 -0
- package/skills/path/references/reviewer-final.md +21 -0
- package/skills/path/references/reviewer-panel.md +17 -0
- package/skills/path/references/reviewer-skeptic.md +20 -0
- package/skills/path/references/reviewer-wave.md +89 -0
- package/skills/path/references/reviewer.md +12 -150
- package/skills/path/scripts/archive_milestone.py +10 -118
- package/skills/path/scripts/bootstrap_repository.py +25 -3
- package/skills/path/scripts/build_recovery.py +287 -0
- package/skills/path/scripts/check_docs_audit.py +5 -3
- package/skills/path/scripts/check_handoffs.py +1 -1
- package/skills/path/scripts/detect_project.py +11 -2
- package/skills/path/scripts/dispatch_driver.py +4 -2
- package/skills/path/scripts/integration.py +28 -22
- package/skills/path/scripts/isolation.py +9 -1
- package/skills/path/scripts/lean_verification.py +1 -1
- package/skills/path/scripts/pipeline_state.py +114 -186
- package/skills/path/scripts/promote_lookahead.py +30 -162
- package/skills/path/scripts/roadmap.py +321 -0
- package/skills/path/scripts/state_checkpoint.py +109 -72
- package/skills/path/scripts/state_promote.py +42 -44
- package/skills/path/scripts/task_context.py +73 -0
- package/skills/path/scripts/token_budget.py +40 -1
- package/skills/path/scripts/workflow_run.py +116 -7
- package/skills/path/templates/wave-review.md +1 -1
|
@@ -239,15 +239,10 @@ class ArchiveError(RuntimeError):
|
|
|
239
239
|
|
|
240
240
|
|
|
241
241
|
PIPELINE_MARKER = _common.PIPELINE_MARKER
|
|
242
|
-
PR_CREDIT_LINE = (
|
|
243
|
-
"PR prepared with [GSD Path](https://github.com/open-gsd/gsd-path)."
|
|
244
|
-
)
|
|
245
|
-
GITHUB_HOST = "github.com"
|
|
246
242
|
|
|
247
243
|
|
|
248
|
-
# discussion_validate
|
|
249
|
-
#
|
|
250
|
-
# import must stay below those definitions.
|
|
244
|
+
# discussion_validate calls back into this module and imports its constants
|
|
245
|
+
# and ArchiveError, so this import must stay below those definitions.
|
|
251
246
|
if __package__: # imported as scripts.archive_milestone
|
|
252
247
|
from .discussion_validate import (
|
|
253
248
|
record_sections,
|
|
@@ -280,42 +275,6 @@ if __package__: # imported as scripts.archive_milestone
|
|
|
280
275
|
render_manifest,
|
|
281
276
|
validate_manifest,
|
|
282
277
|
)
|
|
283
|
-
from .integration import (
|
|
284
|
-
resolve_remote_default,
|
|
285
|
-
live_remote_ref,
|
|
286
|
-
publish_bound_branch,
|
|
287
|
-
refresh_origin,
|
|
288
|
-
optional_ref,
|
|
289
|
-
integration_names,
|
|
290
|
-
registered_worktree,
|
|
291
|
-
remove_registered_worktree,
|
|
292
|
-
delete_integration_branch,
|
|
293
|
-
require_generated_integration_commit,
|
|
294
|
-
discard_unpublished_stale_integration,
|
|
295
|
-
create_integration_merge,
|
|
296
|
-
require_annotated_tag,
|
|
297
|
-
ensure_integration_tag,
|
|
298
|
-
github_repository,
|
|
299
|
-
require_github_authentication,
|
|
300
|
-
github_api_json,
|
|
301
|
-
require_pull_request_shape,
|
|
302
|
-
require_pull_request_pages,
|
|
303
|
-
pull_request_head_matches_repository,
|
|
304
|
-
require_pull_request_identity,
|
|
305
|
-
find_pull_request,
|
|
306
|
-
pull_request_body,
|
|
307
|
-
update_pull_request_body,
|
|
308
|
-
create_pull_request,
|
|
309
|
-
pull_request_tag_message,
|
|
310
|
-
pull_request_tag_metadata,
|
|
311
|
-
require_pull_request_merge,
|
|
312
|
-
require_pull_request_merge_provenance,
|
|
313
|
-
integrate_pull_request,
|
|
314
|
-
integrate,
|
|
315
|
-
find_integrate_commit,
|
|
316
|
-
validate_integrated,
|
|
317
|
-
require_published_integration,
|
|
318
|
-
)
|
|
319
278
|
else: # standalone script or sibling import
|
|
320
279
|
if __name__ == "__main__":
|
|
321
280
|
sys.modules.setdefault("archive_milestone", sys.modules[__name__])
|
|
@@ -351,42 +310,6 @@ else: # standalone script or sibling import
|
|
|
351
310
|
render_manifest,
|
|
352
311
|
validate_manifest,
|
|
353
312
|
)
|
|
354
|
-
from integration import (
|
|
355
|
-
resolve_remote_default,
|
|
356
|
-
live_remote_ref,
|
|
357
|
-
publish_bound_branch,
|
|
358
|
-
refresh_origin,
|
|
359
|
-
optional_ref,
|
|
360
|
-
integration_names,
|
|
361
|
-
registered_worktree,
|
|
362
|
-
remove_registered_worktree,
|
|
363
|
-
delete_integration_branch,
|
|
364
|
-
require_generated_integration_commit,
|
|
365
|
-
discard_unpublished_stale_integration,
|
|
366
|
-
create_integration_merge,
|
|
367
|
-
require_annotated_tag,
|
|
368
|
-
ensure_integration_tag,
|
|
369
|
-
github_repository,
|
|
370
|
-
require_github_authentication,
|
|
371
|
-
github_api_json,
|
|
372
|
-
require_pull_request_shape,
|
|
373
|
-
require_pull_request_pages,
|
|
374
|
-
pull_request_head_matches_repository,
|
|
375
|
-
require_pull_request_identity,
|
|
376
|
-
find_pull_request,
|
|
377
|
-
pull_request_body,
|
|
378
|
-
update_pull_request_body,
|
|
379
|
-
create_pull_request,
|
|
380
|
-
pull_request_tag_message,
|
|
381
|
-
pull_request_tag_metadata,
|
|
382
|
-
require_pull_request_merge,
|
|
383
|
-
require_pull_request_merge_provenance,
|
|
384
|
-
integrate_pull_request,
|
|
385
|
-
integrate,
|
|
386
|
-
find_integrate_commit,
|
|
387
|
-
validate_integrated,
|
|
388
|
-
require_published_integration,
|
|
389
|
-
)
|
|
390
313
|
except ImportError: # pragma: no cover - package import used by tests
|
|
391
314
|
from scripts.discussion_validate import (
|
|
392
315
|
record_sections,
|
|
@@ -419,42 +342,6 @@ else: # standalone script or sibling import
|
|
|
419
342
|
render_manifest,
|
|
420
343
|
validate_manifest,
|
|
421
344
|
)
|
|
422
|
-
from scripts.integration import (
|
|
423
|
-
resolve_remote_default,
|
|
424
|
-
live_remote_ref,
|
|
425
|
-
publish_bound_branch,
|
|
426
|
-
refresh_origin,
|
|
427
|
-
optional_ref,
|
|
428
|
-
integration_names,
|
|
429
|
-
registered_worktree,
|
|
430
|
-
remove_registered_worktree,
|
|
431
|
-
delete_integration_branch,
|
|
432
|
-
require_generated_integration_commit,
|
|
433
|
-
discard_unpublished_stale_integration,
|
|
434
|
-
create_integration_merge,
|
|
435
|
-
require_annotated_tag,
|
|
436
|
-
ensure_integration_tag,
|
|
437
|
-
github_repository,
|
|
438
|
-
require_github_authentication,
|
|
439
|
-
github_api_json,
|
|
440
|
-
require_pull_request_shape,
|
|
441
|
-
require_pull_request_pages,
|
|
442
|
-
pull_request_head_matches_repository,
|
|
443
|
-
require_pull_request_identity,
|
|
444
|
-
find_pull_request,
|
|
445
|
-
pull_request_body,
|
|
446
|
-
update_pull_request_body,
|
|
447
|
-
create_pull_request,
|
|
448
|
-
pull_request_tag_message,
|
|
449
|
-
pull_request_tag_metadata,
|
|
450
|
-
require_pull_request_merge,
|
|
451
|
-
require_pull_request_merge_provenance,
|
|
452
|
-
integrate_pull_request,
|
|
453
|
-
integrate,
|
|
454
|
-
find_integrate_commit,
|
|
455
|
-
validate_integrated,
|
|
456
|
-
require_published_integration,
|
|
457
|
-
)
|
|
458
345
|
|
|
459
346
|
|
|
460
347
|
@contextmanager
|
|
@@ -2238,6 +2125,11 @@ def parser() -> argparse.ArgumentParser:
|
|
|
2238
2125
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
2239
2126
|
arguments = parser().parse_args(argv)
|
|
2240
2127
|
try:
|
|
2128
|
+
if arguments.command in {"validate-integrated", "refresh-origin", "integrate"}:
|
|
2129
|
+
if __package__:
|
|
2130
|
+
from . import integration
|
|
2131
|
+
else:
|
|
2132
|
+
import integration
|
|
2241
2133
|
if arguments.command == "prepare":
|
|
2242
2134
|
result = prepare(arguments.repo, arguments.slug)
|
|
2243
2135
|
elif arguments.command == "render-manifest":
|
|
@@ -2247,11 +2139,11 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
2247
2139
|
elif arguments.command == "abandon":
|
|
2248
2140
|
result = abandon(arguments.repo, arguments.slug, arguments.reason)
|
|
2249
2141
|
elif arguments.command == "validate-integrated":
|
|
2250
|
-
result = validate_integrated(arguments.repo, arguments.slug)
|
|
2142
|
+
result = integration.validate_integrated(arguments.repo, arguments.slug)
|
|
2251
2143
|
elif arguments.command == "refresh-origin":
|
|
2252
|
-
result = refresh_origin(arguments.repo)
|
|
2144
|
+
result = integration.refresh_origin(arguments.repo)
|
|
2253
2145
|
elif arguments.command == "integrate":
|
|
2254
|
-
result = integrate(arguments.repo, arguments.slug)
|
|
2146
|
+
result = integration.integrate(arguments.repo, arguments.slug)
|
|
2255
2147
|
else:
|
|
2256
2148
|
result = validate(arguments.repo)
|
|
2257
2149
|
except (ArchiveError, OSError) as error:
|
|
@@ -32,6 +32,7 @@ class BootstrapRequest:
|
|
|
32
32
|
worktree: str
|
|
33
33
|
branch: str
|
|
34
34
|
description: Optional[str]
|
|
35
|
+
reuse_empty_worktree: bool = False
|
|
35
36
|
|
|
36
37
|
@property
|
|
37
38
|
def repository(self) -> str:
|
|
@@ -95,6 +96,8 @@ def request_from_arguments(arguments: argparse.Namespace) -> BootstrapRequest:
|
|
|
95
96
|
if arguments.visibility not in {"public", "private", "internal"}:
|
|
96
97
|
raise BootstrapError("visibility must be public, private, or internal")
|
|
97
98
|
|
|
99
|
+
if arguments.reuse_empty_worktree and arguments.worktree.is_symlink():
|
|
100
|
+
raise BootstrapError("linked worktree must not be a symlink")
|
|
98
101
|
workspace = arguments.workspace.resolve()
|
|
99
102
|
checkout = arguments.default_checkout.resolve()
|
|
100
103
|
worktree = arguments.worktree.resolve()
|
|
@@ -116,6 +119,7 @@ def request_from_arguments(arguments: argparse.Namespace) -> BootstrapRequest:
|
|
|
116
119
|
worktree=str(worktree),
|
|
117
120
|
branch="gsd-path/M001",
|
|
118
121
|
description=arguments.description,
|
|
122
|
+
reuse_empty_worktree=arguments.reuse_empty_worktree,
|
|
119
123
|
)
|
|
120
124
|
|
|
121
125
|
|
|
@@ -148,7 +152,10 @@ def transaction_directory(request: BootstrapRequest, create: bool = False) -> Pa
|
|
|
148
152
|
|
|
149
153
|
|
|
150
154
|
def request_payload(request: BootstrapRequest) -> dict:
|
|
151
|
-
|
|
155
|
+
payload = {"schema": "gsd-path/new-github/v1", **asdict(request)}
|
|
156
|
+
if not request.reuse_empty_worktree:
|
|
157
|
+
del payload["reuse_empty_worktree"]
|
|
158
|
+
return payload
|
|
152
159
|
|
|
153
160
|
|
|
154
161
|
def read_journal(request: BootstrapRequest) -> Optional[dict]:
|
|
@@ -272,6 +279,16 @@ def transaction_stages(request: BootstrapRequest) -> dict:
|
|
|
272
279
|
}
|
|
273
280
|
|
|
274
281
|
|
|
282
|
+
def reusable_empty_worktree(request: BootstrapRequest) -> bool:
|
|
283
|
+
path = request.worktree_path
|
|
284
|
+
return (
|
|
285
|
+
request.reuse_empty_worktree
|
|
286
|
+
and not path.is_symlink()
|
|
287
|
+
and path.is_dir()
|
|
288
|
+
and not any(path.iterdir())
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
275
292
|
def preview(request: BootstrapRequest) -> dict:
|
|
276
293
|
gh_authentication()
|
|
277
294
|
stages = transaction_stages(request)
|
|
@@ -288,8 +305,12 @@ def preview(request: BootstrapRequest) -> dict:
|
|
|
288
305
|
("default checkout", request.checkout_path),
|
|
289
306
|
("linked worktree", request.worktree_path),
|
|
290
307
|
):
|
|
308
|
+
if path == request.worktree_path and reusable_empty_worktree(request):
|
|
309
|
+
continue
|
|
291
310
|
if path.exists() or path.is_symlink():
|
|
292
|
-
raise BootstrapError(
|
|
311
|
+
raise BootstrapError(
|
|
312
|
+
f"{label} path already exists: {path}; preserve it and choose another path"
|
|
313
|
+
)
|
|
293
314
|
return {**request_payload(request), "mode": "create", "stages": stages}
|
|
294
315
|
|
|
295
316
|
|
|
@@ -448,7 +469,7 @@ def allowed_partial_project_status(worktree: Path) -> bool:
|
|
|
448
469
|
def create_or_verify_worktree(request: BootstrapRequest, base: str) -> None:
|
|
449
470
|
checkout = request.checkout_path
|
|
450
471
|
worktree = request.worktree_path
|
|
451
|
-
if not worktree.exists():
|
|
472
|
+
if not worktree.exists() or reusable_empty_worktree(request):
|
|
452
473
|
branch_ref = f"refs/heads/{request.branch}"
|
|
453
474
|
branch = run("git", "-C", str(checkout), "rev-parse", "--verify", branch_ref)
|
|
454
475
|
if branch.returncode == 0:
|
|
@@ -680,6 +701,7 @@ def parser() -> argparse.ArgumentParser:
|
|
|
680
701
|
argument_parser.add_argument("--default-checkout", type=Path, required=True)
|
|
681
702
|
argument_parser.add_argument("--worktree", type=Path, required=True)
|
|
682
703
|
argument_parser.add_argument("--description")
|
|
704
|
+
argument_parser.add_argument("--reuse-empty-worktree", action="store_true")
|
|
683
705
|
argument_parser.add_argument("--state-template", type=Path)
|
|
684
706
|
argument_parser.add_argument("--repository-template", type=Path)
|
|
685
707
|
return argument_parser
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# gsd-path project runtime
|
|
2
|
+
"""Guard a blocked build's return to Define or Plan using its committed base."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
EVENTS = {
|
|
12
|
+
"define": "build intent corrections requested",
|
|
13
|
+
"plan": "build plan repair requested",
|
|
14
|
+
}
|
|
15
|
+
MARKER = "build recovery: "
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def runtime():
|
|
19
|
+
if __package__:
|
|
20
|
+
from scripts import pipeline_state
|
|
21
|
+
else:
|
|
22
|
+
import pipeline_state
|
|
23
|
+
return pipeline_state
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def checkpoint_runtime():
|
|
27
|
+
if __package__:
|
|
28
|
+
from scripts import state_checkpoint
|
|
29
|
+
else:
|
|
30
|
+
import state_checkpoint
|
|
31
|
+
return state_checkpoint
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def context(repo: Path, text: str | None = None) -> dict | None:
|
|
35
|
+
state = runtime()
|
|
36
|
+
current, loaded, _ = state.load_state(repo)
|
|
37
|
+
text = loaded if text is None else text
|
|
38
|
+
recovery = None
|
|
39
|
+
for line in text.splitlines():
|
|
40
|
+
if " — " not in line:
|
|
41
|
+
continue
|
|
42
|
+
event = line.split(" — ", 2)[-1]
|
|
43
|
+
if event.startswith(MARKER):
|
|
44
|
+
try:
|
|
45
|
+
value = json.loads(event[len(MARKER):])
|
|
46
|
+
except ValueError as error:
|
|
47
|
+
raise state.PipelineStateError("invalid build recovery record") from error
|
|
48
|
+
if (not isinstance(value, dict) or set(value) != {"base", "branch", "kind"}
|
|
49
|
+
or value["kind"] not in EVENTS
|
|
50
|
+
or not isinstance(value["base"], str)
|
|
51
|
+
or not re.fullmatch(r"[0-9a-f]{40}", value["base"])):
|
|
52
|
+
raise state.PipelineStateError("invalid build recovery record")
|
|
53
|
+
recovery = {**value, "active": True} if value["branch"] == current.branch else None
|
|
54
|
+
elif event == "build started" and recovery:
|
|
55
|
+
recovery["active"] = False
|
|
56
|
+
if recovery:
|
|
57
|
+
if not state._is_ancestor(repo, recovery["base"], "HEAD"):
|
|
58
|
+
raise state.PipelineStateError("build recovery base is not an ancestor of HEAD")
|
|
59
|
+
source = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/STATE.md")
|
|
60
|
+
if source is None:
|
|
61
|
+
raise state.PipelineStateError("build recovery base has no STATE.md")
|
|
62
|
+
before = state._state_from_text(source)
|
|
63
|
+
if ((before.phase, before.status) != ("build", "blocked")
|
|
64
|
+
or before.branch != current.branch or before.milestone != current.milestone):
|
|
65
|
+
raise state.PipelineStateError("build recovery base does not prove this blocked milestone")
|
|
66
|
+
return recovery
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def task_fields(text: str, label: str) -> dict:
|
|
70
|
+
if __package__:
|
|
71
|
+
from scripts.isolation import task_frontmatter
|
|
72
|
+
else:
|
|
73
|
+
from isolation import task_frontmatter
|
|
74
|
+
fields, error = task_frontmatter(text)
|
|
75
|
+
if fields is None:
|
|
76
|
+
raise runtime().PipelineStateError(f"{label}: {error}")
|
|
77
|
+
return fields
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def settled_tasks(repo: Path) -> dict[str, tuple[str, dict]]:
|
|
81
|
+
"""Read task ownership without requiring the graph that Plan must repair."""
|
|
82
|
+
state = runtime()
|
|
83
|
+
directory = repo / ".project/tasks"
|
|
84
|
+
if directory.is_symlink() or not directory.is_dir():
|
|
85
|
+
raise state.PipelineStateError("build recovery requires real task artifacts")
|
|
86
|
+
tasks = {}
|
|
87
|
+
for path in sorted(directory.iterdir()):
|
|
88
|
+
if not re.fullmatch(r"T[0-9]{3}-[a-z0-9][a-z0-9-]*\.md", path.name):
|
|
89
|
+
raise state.PipelineStateError(f"invalid recovery task path: {path.name}")
|
|
90
|
+
text = state._read_real_file(path, path.name)
|
|
91
|
+
fields = task_fields(text, path.name)
|
|
92
|
+
if fields.get("status") not in {"pending", "done"}:
|
|
93
|
+
raise state.PipelineStateError(f"settle task ownership before recovery: {path.name}")
|
|
94
|
+
if fields["status"] == "pending" and any(
|
|
95
|
+
fields.get(key) != "null" for key in ("agent", "base", "worktree", "task_branch")
|
|
96
|
+
):
|
|
97
|
+
raise state.PipelineStateError(f"pending task retains dispatch metadata: {path.name}")
|
|
98
|
+
tasks[path.name] = (text, fields)
|
|
99
|
+
if not tasks:
|
|
100
|
+
raise state.PipelineStateError("build recovery requires task artifacts")
|
|
101
|
+
return tasks
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def begin(repo: Path, before, after, event: str) -> dict:
|
|
105
|
+
state = runtime()
|
|
106
|
+
if (before.phase, before.status) != ("build", "blocked") or after.phase not in EVENTS:
|
|
107
|
+
raise state.PipelineStateError("build recovery requires build/blocked")
|
|
108
|
+
if event != EVENTS[after.phase]:
|
|
109
|
+
raise state.PipelineStateError(f"state transition requires event: {EVENTS[after.phase]}")
|
|
110
|
+
if before.archive is not None or before.branch != state._current_branch(repo):
|
|
111
|
+
raise state.PipelineStateError("build recovery requires the active bound branch")
|
|
112
|
+
if state._worktree_changes(repo):
|
|
113
|
+
raise state.PipelineStateError("checkpoint the blocked build before recovery")
|
|
114
|
+
if any(state.transaction_journals(repo).values()):
|
|
115
|
+
raise state.PipelineStateError("finish pending transactions before build recovery")
|
|
116
|
+
records = records_root(repo)
|
|
117
|
+
for directory in {path.parent.parent for path in records.rglob("attempt-*/state.json")}:
|
|
118
|
+
latest = max(directory.glob("attempt-*/state.json"), key=lambda path: int(path.parent.name.split("-")[1]))
|
|
119
|
+
record = state._read_json(latest)
|
|
120
|
+
if (record.get("cleanup_pending")
|
|
121
|
+
or (record.get("worktree") and record.get("branch") and not record.get("cleanup_complete"))
|
|
122
|
+
or record.get("outcome") not in {"landed", "collected", "blocked", "resolved", "redispatched"}):
|
|
123
|
+
raise state.PipelineStateError(f"settle dispatch records before recovery: {latest}")
|
|
124
|
+
tasks = settled_tasks(repo)
|
|
125
|
+
state._read_real_file(repo / ".project/plan/PLAN.md", "PLAN.md")
|
|
126
|
+
state._read_real_file(repo / ".project/intent/INTENT.md", "INTENT.md")
|
|
127
|
+
base = state._run_git(repo, "rev-parse", "HEAD").stdout.strip()
|
|
128
|
+
done = [repo / ".project/tasks" / name for name, (_, fields) in tasks.items()
|
|
129
|
+
if fields["status"] == "done"]
|
|
130
|
+
if done:
|
|
131
|
+
if __package__:
|
|
132
|
+
from scripts.isolation import verify_landed_task_files
|
|
133
|
+
else:
|
|
134
|
+
from isolation import verify_landed_task_files
|
|
135
|
+
verify_landed_task_files(repo, done, ".project/tasks", base)
|
|
136
|
+
return {"kind": after.phase, "base": base, "branch": before.branch}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def records_root(repo: Path) -> Path:
|
|
140
|
+
if __package__:
|
|
141
|
+
from scripts import isolation
|
|
142
|
+
else:
|
|
143
|
+
import isolation
|
|
144
|
+
root = isolation.common_git_dir(repo) / "gsd-path/dispatch" / isolation.require_bound(repo).replace("/", "-")
|
|
145
|
+
recovery = context(repo)
|
|
146
|
+
return root / recovery["base"] if recovery else root
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def review_backup(repo: Path, recovery: dict) -> Path:
|
|
150
|
+
return repo / ".project/plan" / f"build-recovery-{recovery['base']}" / "review"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def validate_review_backup(repo: Path, recovery: dict, directory: Path) -> None:
|
|
154
|
+
state = runtime()
|
|
155
|
+
if directory.exists():
|
|
156
|
+
checkpoint_runtime()._tree_digest(directory) # Reject symlinks and special files before reading.
|
|
157
|
+
expected = {}
|
|
158
|
+
for path in state._run_git(repo, "ls-tree", "-r", "--name-only", "-z",
|
|
159
|
+
recovery["base"], "--", ".project/review").stdout.split("\0"):
|
|
160
|
+
if path:
|
|
161
|
+
expected[str(Path(path).relative_to(".project/review"))] = checkpoint_runtime()._git_text_at(repo, recovery["base"], path)
|
|
162
|
+
actual = {str(path.relative_to(directory)): path.read_text(encoding="utf-8")
|
|
163
|
+
for path in directory.rglob("*") if path.is_file()} if directory.exists() else {}
|
|
164
|
+
if actual != expected:
|
|
165
|
+
raise state.PipelineStateError("review backup differs from the recovery base")
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def prepare(repo: Path) -> dict:
|
|
169
|
+
"""Move old reviews once; the durable STATE record owns interruption recovery."""
|
|
170
|
+
state = runtime()
|
|
171
|
+
repo = state._repo_root(repo)
|
|
172
|
+
with state._state_lock(repo / ".project"):
|
|
173
|
+
recovery = context(repo)
|
|
174
|
+
if not recovery or not recovery["active"]:
|
|
175
|
+
raise state.PipelineStateError("no active build recovery")
|
|
176
|
+
target = review_backup(repo, recovery)
|
|
177
|
+
if (repo / ".project/plan").is_symlink() or target.parent.is_symlink() or target.is_symlink():
|
|
178
|
+
raise state.PipelineStateError("unsafe recovery review backup")
|
|
179
|
+
source = repo / ".project/review"
|
|
180
|
+
if source.is_symlink():
|
|
181
|
+
raise state.PipelineStateError("unsafe recovery review directory")
|
|
182
|
+
if not target.exists():
|
|
183
|
+
validate_review_backup(repo, recovery, source)
|
|
184
|
+
target.parent.mkdir(exist_ok=True)
|
|
185
|
+
if source.exists():
|
|
186
|
+
source.rename(target)
|
|
187
|
+
else:
|
|
188
|
+
target.mkdir()
|
|
189
|
+
validate_review_backup(repo, recovery, target)
|
|
190
|
+
source.mkdir(exist_ok=True)
|
|
191
|
+
return {"status": "prepared", "recovery": recovery, "path": str(target)}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def validate_plan(repo: Path) -> None:
|
|
195
|
+
state = runtime()
|
|
196
|
+
recovery = context(repo)
|
|
197
|
+
if not recovery or not recovery["active"]:
|
|
198
|
+
return
|
|
199
|
+
if not review_backup(repo, recovery).is_dir():
|
|
200
|
+
raise state.PipelineStateError("run prepare-build-recovery before planning")
|
|
201
|
+
validate_review_backup(repo, recovery, review_backup(repo, recovery))
|
|
202
|
+
source = checkpoint_runtime()._task_contracts_at(repo, recovery["base"], ".project/tasks")
|
|
203
|
+
current = settled_tasks(repo)
|
|
204
|
+
done = set()
|
|
205
|
+
for _, (path, text) in source.items():
|
|
206
|
+
fields = task_fields(text, path)
|
|
207
|
+
if fields.get("status") == "done":
|
|
208
|
+
name = Path(path).name
|
|
209
|
+
done.add(name)
|
|
210
|
+
if name not in current or current[name][0] != text:
|
|
211
|
+
raise state.PipelineStateError(f"build recovery must preserve landed task: {name}")
|
|
212
|
+
if any(fields["status"] == "done" and name not in done
|
|
213
|
+
for name, (_, fields) in current.items()):
|
|
214
|
+
raise state.PipelineStateError("build recovery cannot manufacture landed tasks")
|
|
215
|
+
if recovery["kind"] == "plan":
|
|
216
|
+
intent = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/intent/INTENT.md")
|
|
217
|
+
if state._read_real_file(repo / ".project/intent/INTENT.md", "INTENT.md") != intent:
|
|
218
|
+
raise state.PipelineStateError("plan repair cannot change approved intent")
|
|
219
|
+
unchanged = unchanged_waves(repo, recovery)
|
|
220
|
+
for path in (repo / ".project/review").iterdir():
|
|
221
|
+
match = re.match(r"wave-([1-9]\d*)[.]", path.name)
|
|
222
|
+
if match and int(match[1]) not in unchanged:
|
|
223
|
+
raise state.PipelineStateError("changed wave must be reviewed after build resumes")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def unchanged_waves(repo: Path, recovery: dict) -> set[int]:
|
|
227
|
+
"""Reuse evidence only when its intent, wave contract, and task bytes match."""
|
|
228
|
+
state = runtime()
|
|
229
|
+
project = repo / ".project"
|
|
230
|
+
old_intent = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/intent/INTENT.md")
|
|
231
|
+
if state._read_real_file(project / "intent/INTENT.md", "INTENT.md") != old_intent:
|
|
232
|
+
return set()
|
|
233
|
+
old_plan = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/plan/PLAN.md") or ""
|
|
234
|
+
new_plan = state._read_real_file(project / "plan/PLAN.md", "PLAN.md")
|
|
235
|
+
|
|
236
|
+
def sections(text):
|
|
237
|
+
return {match[1]: match[2] for match in re.finditer(
|
|
238
|
+
r"(?ms)^## ([^\n]+)\n(.*?)(?=^## |\Z)", text,
|
|
239
|
+
)}
|
|
240
|
+
|
|
241
|
+
old_sections, new_sections = sections(old_plan), sections(new_plan)
|
|
242
|
+
# Non-wave settings govern every wave, including coverage and surfaces.
|
|
243
|
+
if ({k: v for k, v in old_sections.items() if not k.startswith("Wave ")}
|
|
244
|
+
!= {k: v for k, v in new_sections.items() if not k.startswith("Wave ")}):
|
|
245
|
+
return set()
|
|
246
|
+
old_tasks = checkpoint_runtime()._task_contracts_at(repo, recovery["base"], ".project/tasks")
|
|
247
|
+
current = settled_tasks(repo)
|
|
248
|
+
unchanged = set()
|
|
249
|
+
for title, body in new_sections.items():
|
|
250
|
+
match = re.match(r"Wave ([1-9]\d*)\b", title)
|
|
251
|
+
if not match or old_sections.get(title) != body:
|
|
252
|
+
continue
|
|
253
|
+
wave = match[1]
|
|
254
|
+
before = {Path(path).name: text for path, text in old_tasks.values()
|
|
255
|
+
if task_fields(text, path).get("wave") == wave}
|
|
256
|
+
after = {name: text for name, (text, fields) in current.items() if fields.get("wave") == wave}
|
|
257
|
+
if before and before == after:
|
|
258
|
+
unchanged.add(int(wave))
|
|
259
|
+
return unchanged
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def restore_unchanged_reviews(repo: Path) -> None:
|
|
263
|
+
recovery = context(repo)
|
|
264
|
+
if not recovery or not recovery["active"]:
|
|
265
|
+
return
|
|
266
|
+
unchanged = unchanged_waves(repo, recovery)
|
|
267
|
+
for path in review_backup(repo, recovery).iterdir():
|
|
268
|
+
match = re.match(r"wave-([1-9]\d*)[.]", path.name)
|
|
269
|
+
if match and int(match[1]) in unchanged:
|
|
270
|
+
destination = repo / ".project/review" / path.name
|
|
271
|
+
content = path.read_text(encoding="utf-8")
|
|
272
|
+
if not destination.exists() or runtime()._read_real_file(destination, destination.name) != content:
|
|
273
|
+
runtime()._atomic_write(destination, content)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def inventory_checkpoint(repo: Path, head: str) -> bool:
|
|
277
|
+
state = runtime()
|
|
278
|
+
if state._run_git(repo, "show", "-s", "--format=%s", head).stdout.strip() != state.PLAN_APPROVAL_SUBJECT:
|
|
279
|
+
return False
|
|
280
|
+
text = checkpoint_runtime()._git_text_at(repo, head, ".project/STATE.md")
|
|
281
|
+
if text is None:
|
|
282
|
+
return False
|
|
283
|
+
approved = state._state_from_text(text)
|
|
284
|
+
if (approved.phase, approved.status) != ("plan", "done"):
|
|
285
|
+
return False
|
|
286
|
+
recovery = context(repo, text)
|
|
287
|
+
return bool(recovery and recovery["active"])
|
|
@@ -21,7 +21,8 @@ are rejected.
|
|
|
21
21
|
The frozen inventory travels in the dispatch brief; pass it with
|
|
22
22
|
`--inventory FILE` (one POSIX path per line, `-` for stdin). Without it the
|
|
23
23
|
gate derives the inventory from tracked Markdown files, excluding `.git`,
|
|
24
|
-
`node_modules`, `.project/archive/**`, installed `*/skills/gsd-path*`
|
|
24
|
+
`node_modules`, `.project/archive/**`, installed `*/skills/gsd-path*` and
|
|
25
|
+
`*/skills/path` bundles,
|
|
25
26
|
the audit itself, and — unless the
|
|
26
27
|
audit declares `Alignment mode: yes` — everything else under `.project/`.
|
|
27
28
|
"""
|
|
@@ -100,9 +101,10 @@ def _not_placeholder(value: str, label: str) -> None:
|
|
|
100
101
|
|
|
101
102
|
|
|
102
103
|
def _installed_skill(parts: Sequence[str]) -> bool:
|
|
103
|
-
"""
|
|
104
|
+
"""Exclude installed GSD Path bundles, including the path router alias."""
|
|
104
105
|
return any(
|
|
105
|
-
parts[index] == "skills" and
|
|
106
|
+
parts[index] == "skills" and
|
|
107
|
+
(parts[index + 1].startswith("gsd-path") or parts[index + 1] == "path")
|
|
106
108
|
for index in range(len(parts) - 2)
|
|
107
109
|
)
|
|
108
110
|
|
|
@@ -1462,7 +1462,7 @@ def validate_wave_evidence(
|
|
|
1462
1462
|
raise HandoffError(f"{name} Cycle field does not match its filename")
|
|
1463
1463
|
if _line_value(text, "Depth:") != expected_depth:
|
|
1464
1464
|
raise HandoffError(f"{name} Depth does not match PLAN.md")
|
|
1465
|
-
lens_fields = re.findall(r"(?m)^Lens
|
|
1465
|
+
lens_fields = re.findall(r"(?m)^Lens:[ \t]*(\S.*?)[ \t]*$", text)
|
|
1466
1466
|
if lens is None and lens_fields:
|
|
1467
1467
|
raise HandoffError(f"{name} must not declare a review lens")
|
|
1468
1468
|
if lens is not None and lens_fields != [lens]:
|
|
@@ -1439,7 +1439,7 @@ def write_state_anchored(
|
|
|
1439
1439
|
|
|
1440
1440
|
|
|
1441
1441
|
def initialize(
|
|
1442
|
-
repo: Path, template: Path, phase: Optional[str] = None
|
|
1442
|
+
repo: Path, template: Path, phase: Optional[str] = None, *, require_git: bool = False
|
|
1443
1443
|
) -> dict:
|
|
1444
1444
|
if phase is not None and phase not in {"inspect", "define"}:
|
|
1445
1445
|
raise DetectError(f"initialize phase must be inspect or define: {phase}")
|
|
@@ -1458,6 +1458,13 @@ def initialize(
|
|
|
1458
1458
|
if payload["verdict"] not in {"brownfield", "greenfield"}:
|
|
1459
1459
|
payload["wrote_state"] = False
|
|
1460
1460
|
return payload
|
|
1461
|
+
if require_git:
|
|
1462
|
+
git = subprocess.run(
|
|
1463
|
+
["git", "-C", str(root), "rev-parse", "--is-inside-work-tree"],
|
|
1464
|
+
env=git_environment(), capture_output=True, text=True, check=False,
|
|
1465
|
+
)
|
|
1466
|
+
if git.returncode != 0 or git.stdout.strip() != "true":
|
|
1467
|
+
return {**payload, "route": "setup-repository", "wrote_state": False}
|
|
1461
1468
|
expected = "inspect" if payload["verdict"] == "brownfield" else "define"
|
|
1462
1469
|
if phase is None:
|
|
1463
1470
|
phase = expected
|
|
@@ -1491,6 +1498,7 @@ def parser() -> argparse.ArgumentParser:
|
|
|
1491
1498
|
result.add_argument("--repo", type=Path, required=True)
|
|
1492
1499
|
result.add_argument("--template", type=Path)
|
|
1493
1500
|
result.add_argument("--phase", choices=("inspect", "define"))
|
|
1501
|
+
result.add_argument("--require-git", action="store_true")
|
|
1494
1502
|
return result
|
|
1495
1503
|
|
|
1496
1504
|
|
|
@@ -1503,7 +1511,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
1503
1511
|
if arguments.template is None:
|
|
1504
1512
|
raise DetectError("initialize requires --template")
|
|
1505
1513
|
payload = initialize(
|
|
1506
|
-
arguments.repo, arguments.template, arguments.phase
|
|
1514
|
+
arguments.repo, arguments.template, arguments.phase,
|
|
1515
|
+
require_git=arguments.require_git
|
|
1507
1516
|
)
|
|
1508
1517
|
return emit(payload, 2 if payload.get("error") else 0)
|
|
1509
1518
|
raise DetectError(f"unknown command: {arguments.command}")
|
|
@@ -31,13 +31,14 @@ from typing import BinaryIO, Callable, Dict, List, Optional
|
|
|
31
31
|
|
|
32
32
|
try:
|
|
33
33
|
from scripts import (_common, archive_milestone, build_state, discussion_validate, isolation,
|
|
34
|
-
pipeline_state, review_findings, review_panel, workflow_run)
|
|
34
|
+
pipeline_state, review_findings, review_panel, workflow_run, task_context)
|
|
35
35
|
from scripts import check_handoffs as contracts
|
|
36
36
|
except ImportError: # bundled copy inside a skill's scripts directory
|
|
37
37
|
import _common
|
|
38
38
|
import archive_milestone
|
|
39
39
|
import build_state
|
|
40
40
|
import check_handoffs as contracts
|
|
41
|
+
import task_context
|
|
41
42
|
import discussion_validate
|
|
42
43
|
import isolation
|
|
43
44
|
import pipeline_state
|
|
@@ -94,7 +95,7 @@ def milestone_slug(primary: Path) -> str:
|
|
|
94
95
|
|
|
95
96
|
|
|
96
97
|
def records_root(primary: Path) -> Path:
|
|
97
|
-
return
|
|
98
|
+
return pipeline_state._build_recovery().records_root(primary)
|
|
98
99
|
|
|
99
100
|
|
|
100
101
|
def budget_ledger(primary: Path) -> Path:
|
|
@@ -246,6 +247,7 @@ def brief_text(state: Dict[str, object], role_brief: Path, task_template: Path)
|
|
|
246
247
|
if state.get("answered"):
|
|
247
248
|
lines.append(f"The task Log now records an `{ANSWER_MARK}` to your earlier question; "
|
|
248
249
|
"continue from it.")
|
|
250
|
+
lines.append(task_context.render(worktree, worktree / str(state['task_file'])))
|
|
249
251
|
return "\n".join(lines) + "\n"
|
|
250
252
|
|
|
251
253
|
|
package/scripts/git_guard.py
CHANGED
|
@@ -114,7 +114,8 @@ def message_of(message_file):
|
|
|
114
114
|
lines.pop(0)
|
|
115
115
|
if not lines:
|
|
116
116
|
return "", ""
|
|
117
|
-
|
|
117
|
+
subject_end = lines.index("") if "" in lines else len(lines)
|
|
118
|
+
return " ".join(line.strip() for line in lines[:subject_end]), "\n".join(lines[subject_end:]).strip("\n")
|
|
118
119
|
|
|
119
120
|
|
|
120
121
|
def archive_root(path):
|
package/scripts/install.mjs
CHANGED
|
@@ -15,6 +15,7 @@ export const GUARD_SCRIPTS = ["guard_hook.py", "git_guard.py"];
|
|
|
15
15
|
export const GUARD_MARKER = "gsd-path guard";
|
|
16
16
|
export const PROJECT_RUNTIME_SCRIPTS = [
|
|
17
17
|
"pipeline_state.py",
|
|
18
|
+
"roadmap.py",
|
|
18
19
|
"check_handoffs.py",
|
|
19
20
|
"check_task_briefs.py",
|
|
20
21
|
"isolation.py",
|
|
@@ -27,6 +28,7 @@ export const PROJECT_RUNTIME_SCRIPTS = [
|
|
|
27
28
|
"archive_milestone.py",
|
|
28
29
|
"review_panel.py",
|
|
29
30
|
"_common.py",
|
|
31
|
+
"build_recovery.py",
|
|
30
32
|
"state_checkpoint.py",
|
|
31
33
|
"state_promote.py",
|
|
32
34
|
"discussion_validate.py",
|