@opengsd/gsd-path 1.0.0 → 1.2.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 +33 -13
- package/CHANGELOG.md +68 -0
- package/DOCS.md +75 -6
- package/FULL.md +12 -15
- package/HOOKS.md +18 -9
- package/QUICK.md +8 -6
- package/README.md +218 -14
- package/RUNTIME.md +16 -5
- package/UPDATE.md +30 -11
- package/WORKFLOW.md +35 -16
- package/package.json +23 -1
- package/platforms/claude/dispatch.md +4 -4
- package/platforms/copilot/dispatch.md +4 -4
- package/platforms/cursor/dispatch.md +5 -10
- package/platforms/grok/dispatch.md +4 -6
- package/platforms/kimi/dispatch.md +4 -4
- package/platforms/kiro/dispatch.md +4 -6
- package/platforms/opencode/dispatch.md +4 -4
- package/platforms/qwen/dispatch.md +4 -4
- package/platforms/shared-agents/dispatch.md +12 -19
- package/scripts/_common.py +5 -0
- package/scripts/archive_milestone.py +30 -124
- package/scripts/bootstrap_repository.py +33 -4
- package/scripts/build_recovery.py +292 -0
- package/scripts/build_state.py +2 -0
- package/scripts/check_docs_audit.py +7 -3
- package/scripts/check_handoffs.py +7 -1
- package/scripts/check_task_briefs.py +3 -0
- package/scripts/detect_project.py +22 -3
- package/scripts/discussion_records.py +3 -0
- package/scripts/discussion_validate.py +8 -3
- package/scripts/dispatch_driver.py +233 -22
- package/scripts/git_guard.py +14 -5
- package/scripts/guard_hook.py +11 -1
- package/scripts/install.mjs +54 -1448
- package/scripts/install.py +155 -188
- package/scripts/integration.py +54 -28
- package/scripts/isolation.py +24 -6
- package/scripts/jev_review.py +138 -0
- package/scripts/lean_verification.py +3 -1
- package/scripts/loop_run.py +2 -0
- package/scripts/model_policy.py +279 -0
- package/scripts/path_config.py +248 -0
- package/scripts/pipeline_diagnose.py +15 -3
- package/scripts/pipeline_git.py +3 -0
- package/scripts/pipeline_state.py +117 -186
- package/scripts/pipeline_undo.py +14 -7
- package/scripts/promote_lookahead.py +33 -162
- package/scripts/review_panel.py +11 -0
- package/scripts/roadmap.py +326 -0
- package/scripts/runtime_store.py +278 -0
- package/scripts/skill-resources.json +182 -4
- package/scripts/state_checkpoint.py +114 -72
- package/scripts/state_promote.py +47 -44
- package/scripts/status_runtime.py +92 -6
- package/scripts/task_context.py +73 -0
- package/scripts/token_budget.py +40 -1
- package/scripts/workflow_run.py +128 -8
- package/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path/BUILD.md +54 -575
- package/skills/gsd-path/DECIDE.md +4 -6
- package/skills/gsd-path/DEFINE.md +23 -21
- package/skills/gsd-path/DOCS-AUDIT.md +2 -0
- package/skills/gsd-path/INSPECT.md +32 -8
- package/skills/gsd-path/PLAN.md +29 -19
- package/skills/gsd-path/RESEARCH.md +5 -6
- package/skills/gsd-path/ROADMAP.md +24 -58
- package/skills/gsd-path/SHIP.md +29 -17
- package/skills/gsd-path/SKILL.md +46 -22
- package/skills/gsd-path/references/build-abandon.md +33 -0
- package/skills/gsd-path/references/build-native.md +502 -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/config.md +57 -0
- package/skills/gsd-path/references/dispatch.md +12 -19
- package/skills/gsd-path/references/docs-auditor.md +14 -0
- package/skills/gsd-path/references/jev-review.md +82 -0
- package/skills/gsd-path/references/model-policy.md +139 -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 -148
- package/skills/gsd-path/references/runtime-selection.md +25 -0
- package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/_common.py +5 -0
- package/skills/gsd-path/scripts/archive_milestone.py +30 -124
- package/skills/gsd-path/scripts/bootstrap_repository.py +33 -4
- package/skills/gsd-path/scripts/build_recovery.py +292 -0
- package/skills/gsd-path/scripts/build_state.py +2 -0
- package/skills/gsd-path/scripts/check_docs_audit.py +7 -3
- package/skills/gsd-path/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path/scripts/detect_project.py +22 -3
- package/skills/gsd-path/scripts/discussion_records.py +3 -0
- package/skills/gsd-path/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path/scripts/dispatch_driver.py +233 -22
- package/skills/gsd-path/scripts/git_guard.py +768 -0
- package/skills/gsd-path/scripts/integration.py +54 -28
- package/skills/gsd-path/scripts/isolation.py +24 -6
- package/skills/gsd-path/scripts/jev_review.py +138 -0
- package/skills/gsd-path/scripts/lean_verification.py +3 -1
- package/skills/gsd-path/scripts/loop_run.py +2 -0
- package/skills/gsd-path/scripts/model_policy.py +279 -0
- package/skills/gsd-path/scripts/path_config.py +248 -0
- package/skills/gsd-path/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path/scripts/promote_lookahead.py +33 -162
- package/skills/gsd-path/scripts/review_panel.py +11 -0
- package/skills/gsd-path/scripts/roadmap.py +326 -0
- package/skills/gsd-path/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path/scripts/state_promote.py +47 -44
- package/skills/gsd-path/scripts/status_runtime.py +208 -0
- 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 +128 -8
- package/skills/gsd-path/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path/templates/skeptic.md +1 -1
- package/skills/gsd-path/templates/task.md +2 -0
- package/skills/gsd-path/templates/wave-panel.md +1 -1
- package/skills/gsd-path/templates/wave-review.md +1 -1
- package/skills/gsd-path-build/SKILL.md +54 -575
- package/skills/gsd-path-build/references/build-abandon.md +33 -0
- package/skills/gsd-path-build/references/build-native.md +502 -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 +12 -19
- package/skills/gsd-path-build/references/jev-review.md +82 -0
- package/skills/gsd-path-build/references/model-policy.md +139 -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 -148
- package/skills/gsd-path-build/references/runtime-selection.md +25 -0
- package/skills/gsd-path-build/scripts/__pycache__/_common.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/_common.py +5 -0
- package/skills/gsd-path-build/scripts/archive_milestone.py +30 -124
- package/skills/gsd-path-build/scripts/build_recovery.py +292 -0
- package/skills/gsd-path-build/scripts/build_state.py +2 -0
- package/skills/gsd-path-build/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path-build/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-build/scripts/detect_project.py +1536 -0
- package/skills/gsd-path-build/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-build/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path-build/scripts/dispatch_driver.py +233 -22
- package/skills/gsd-path-build/scripts/integration.py +54 -28
- package/skills/gsd-path-build/scripts/isolation.py +24 -6
- package/skills/gsd-path-build/scripts/jev_review.py +138 -0
- package/skills/gsd-path-build/scripts/loop_run.py +2 -0
- package/skills/gsd-path-build/scripts/model_policy.py +279 -0
- package/skills/gsd-path-build/scripts/path_config.py +248 -0
- package/skills/gsd-path-build/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path-build/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path-build/scripts/promote_lookahead.py +286 -0
- package/skills/gsd-path-build/scripts/review_panel.py +11 -0
- package/skills/gsd-path-build/scripts/roadmap.py +326 -0
- package/skills/gsd-path-build/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path-build/scripts/state_promote.py +47 -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 +128 -8
- package/skills/gsd-path-build/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-build/templates/skeptic.md +1 -1
- package/skills/gsd-path-build/templates/task.md +2 -0
- package/skills/gsd-path-build/templates/wave-panel.md +1 -1
- package/skills/gsd-path-build/templates/wave-review.md +1 -1
- package/skills/gsd-path-decide/SKILL.md +4 -6
- package/skills/gsd-path-decide/references/dispatch.md +12 -19
- package/skills/gsd-path-decide/references/model-policy.md +139 -0
- package/skills/gsd-path-decide/references/runtime-selection.md +25 -0
- package/skills/gsd-path-decide/scripts/_common.py +5 -0
- package/skills/gsd-path-decide/scripts/archive_milestone.py +2171 -0
- package/skills/gsd-path-decide/scripts/build_recovery.py +292 -0
- package/skills/gsd-path-decide/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-decide/scripts/detect_project.py +1536 -0
- package/skills/gsd-path-decide/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-decide/scripts/discussion_validate.py +1326 -0
- package/skills/gsd-path-decide/scripts/integration.py +1459 -0
- package/skills/gsd-path-decide/scripts/isolation.py +24 -6
- package/skills/gsd-path-decide/scripts/loop_run.py +839 -0
- package/skills/gsd-path-decide/scripts/model_policy.py +279 -0
- package/skills/gsd-path-decide/scripts/path_config.py +248 -0
- package/skills/gsd-path-decide/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path-decide/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path-decide/scripts/promote_lookahead.py +286 -0
- package/skills/gsd-path-decide/scripts/review_panel.py +564 -0
- package/skills/gsd-path-decide/scripts/roadmap.py +326 -0
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path-decide/scripts/state_promote.py +47 -44
- package/skills/gsd-path-decide/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-define/SKILL.md +23 -21
- package/skills/gsd-path-define/references/build-recovery.md +78 -0
- package/skills/gsd-path-define/references/runtime-selection.md +25 -0
- package/skills/gsd-path-define/scripts/_common.py +5 -0
- package/skills/gsd-path-define/scripts/archive_milestone.py +2171 -0
- package/skills/gsd-path-define/scripts/build_recovery.py +292 -0
- package/skills/gsd-path-define/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path-define/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-define/scripts/detect_project.py +22 -3
- package/skills/gsd-path-define/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-define/scripts/discussion_validate.py +1326 -0
- package/skills/gsd-path-define/scripts/integration.py +1459 -0
- package/skills/gsd-path-define/scripts/isolation.py +24 -6
- package/skills/gsd-path-define/scripts/loop_run.py +839 -0
- package/skills/gsd-path-define/scripts/model_policy.py +279 -0
- package/skills/gsd-path-define/scripts/path_config.py +248 -0
- package/skills/gsd-path-define/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path-define/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path-define/scripts/promote_lookahead.py +286 -0
- package/skills/gsd-path-define/scripts/review_panel.py +564 -0
- package/skills/gsd-path-define/scripts/roadmap.py +326 -0
- package/skills/gsd-path-define/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path-define/scripts/state_promote.py +47 -44
- package/skills/gsd-path-define/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-discuss/SKILL.md +2 -0
- package/skills/gsd-path-discuss/references/runtime-selection.md +25 -0
- package/skills/gsd-path-discuss/scripts/_common.py +5 -0
- package/skills/gsd-path-discuss/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-discuss/scripts/loop_run.py +839 -0
- package/skills/gsd-path-discuss/scripts/model_policy.py +279 -0
- package/skills/gsd-path-discuss/scripts/path_config.py +248 -0
- package/skills/gsd-path-discuss/scripts/review_panel.py +564 -0
- package/skills/gsd-path-discuss/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-docs-audit/SKILL.md +2 -0
- package/skills/gsd-path-docs-audit/references/dispatch.md +12 -19
- package/skills/gsd-path-docs-audit/references/docs-auditor.md +14 -0
- package/skills/gsd-path-docs-audit/references/model-policy.md +139 -0
- package/skills/gsd-path-docs-audit/references/runtime-selection.md +25 -0
- package/skills/gsd-path-docs-audit/scripts/_common.py +5 -0
- package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +2171 -0
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +292 -0
- package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +7 -3
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-docs-audit/scripts/detect_project.py +1536 -0
- package/skills/gsd-path-docs-audit/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +1326 -0
- package/skills/gsd-path-docs-audit/scripts/integration.py +1459 -0
- package/skills/gsd-path-docs-audit/scripts/isolation.py +24 -6
- package/skills/gsd-path-docs-audit/scripts/loop_run.py +839 -0
- package/skills/gsd-path-docs-audit/scripts/model_policy.py +279 -0
- package/skills/gsd-path-docs-audit/scripts/path_config.py +248 -0
- package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +286 -0
- package/skills/gsd-path-docs-audit/scripts/review_panel.py +564 -0
- package/skills/gsd-path-docs-audit/scripts/roadmap.py +326 -0
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path-docs-audit/scripts/state_promote.py +47 -44
- package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-forensics/SKILL.md +9 -5
- package/skills/gsd-path-forensics/references/runtime-selection.md +25 -0
- package/skills/gsd-path-forensics/scripts/_common.py +5 -0
- package/skills/gsd-path-forensics/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-forensics/scripts/loop_run.py +839 -0
- package/skills/gsd-path-forensics/scripts/model_policy.py +279 -0
- package/skills/gsd-path-forensics/scripts/path_config.py +248 -0
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +15 -3
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +14 -7
- package/skills/gsd-path-forensics/scripts/review_panel.py +564 -0
- package/skills/gsd-path-forensics/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-inspect/SKILL.md +32 -8
- package/skills/gsd-path-inspect/references/dispatch.md +12 -19
- package/skills/gsd-path-inspect/references/docs-auditor.md +14 -0
- package/skills/gsd-path-inspect/references/model-policy.md +139 -0
- package/skills/gsd-path-inspect/references/runtime-selection.md +25 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/_common.py +5 -0
- package/skills/gsd-path-inspect/scripts/archive_milestone.py +2171 -0
- package/skills/gsd-path-inspect/scripts/build_recovery.py +292 -0
- package/skills/gsd-path-inspect/scripts/check_docs_audit.py +7 -3
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-inspect/scripts/detect_project.py +22 -3
- package/skills/gsd-path-inspect/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +1326 -0
- package/skills/gsd-path-inspect/scripts/integration.py +1459 -0
- package/skills/gsd-path-inspect/scripts/isolation.py +24 -6
- package/skills/gsd-path-inspect/scripts/loop_run.py +839 -0
- package/skills/gsd-path-inspect/scripts/model_policy.py +279 -0
- package/skills/gsd-path-inspect/scripts/path_config.py +248 -0
- package/skills/gsd-path-inspect/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path-inspect/scripts/promote_lookahead.py +286 -0
- package/skills/gsd-path-inspect/scripts/review_panel.py +564 -0
- package/skills/gsd-path-inspect/scripts/roadmap.py +326 -0
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path-inspect/scripts/state_promote.py +47 -44
- package/skills/gsd-path-inspect/scripts/workflow_run.py +237 -0
- package/skills/gsd-path-inspect/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-loop/SKILL.md +2 -0
- package/skills/gsd-path-loop/references/runtime-selection.md +25 -0
- package/skills/gsd-path-loop/scripts/_common.py +5 -0
- package/skills/gsd-path-loop/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-loop/scripts/loop_run.py +2 -0
- package/skills/gsd-path-loop/scripts/model_policy.py +279 -0
- package/skills/gsd-path-loop/scripts/path_config.py +248 -0
- package/skills/gsd-path-loop/scripts/review_panel.py +564 -0
- package/skills/gsd-path-loop/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-migrate/SKILL.md +2 -0
- package/skills/gsd-path-migrate/references/runtime-selection.md +25 -0
- package/skills/gsd-path-migrate/scripts/_common.py +185 -0
- package/skills/gsd-path-migrate/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-migrate/scripts/loop_run.py +839 -0
- package/skills/gsd-path-migrate/scripts/model_policy.py +279 -0
- package/skills/gsd-path-migrate/scripts/path_config.py +248 -0
- package/skills/gsd-path-migrate/scripts/review_panel.py +564 -0
- package/skills/gsd-path-plan/SKILL.md +29 -19
- package/skills/gsd-path-plan/references/build-recovery.md +78 -0
- package/skills/gsd-path-plan/references/dispatch.md +12 -19
- package/skills/gsd-path-plan/references/model-policy.md +139 -0
- package/skills/gsd-path-plan/references/runtime-selection.md +25 -0
- package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/_common.py +5 -0
- package/skills/gsd-path-plan/scripts/archive_milestone.py +2171 -0
- package/skills/gsd-path-plan/scripts/build_recovery.py +292 -0
- package/skills/gsd-path-plan/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-plan/scripts/detect_project.py +1536 -0
- package/skills/gsd-path-plan/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-plan/scripts/discussion_validate.py +1326 -0
- package/skills/gsd-path-plan/scripts/integration.py +1459 -0
- package/skills/gsd-path-plan/scripts/isolation.py +24 -6
- package/skills/gsd-path-plan/scripts/loop_run.py +839 -0
- package/skills/gsd-path-plan/scripts/model_policy.py +279 -0
- package/skills/gsd-path-plan/scripts/path_config.py +248 -0
- package/skills/gsd-path-plan/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path-plan/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path-plan/scripts/promote_lookahead.py +286 -0
- package/skills/gsd-path-plan/scripts/review_panel.py +11 -0
- package/skills/gsd-path-plan/scripts/roadmap.py +326 -0
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path-plan/scripts/state_promote.py +47 -44
- package/skills/gsd-path-plan/scripts/workflow_run.py +128 -8
- package/skills/gsd-path-plan/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-plan/templates/task.md +2 -0
- package/skills/gsd-path-research/SKILL.md +5 -6
- package/skills/gsd-path-research/references/dispatch.md +12 -19
- package/skills/gsd-path-research/references/model-policy.md +139 -0
- package/skills/gsd-path-research/references/runtime-selection.md +25 -0
- package/skills/gsd-path-research/scripts/_common.py +5 -0
- package/skills/gsd-path-research/scripts/archive_milestone.py +2171 -0
- package/skills/gsd-path-research/scripts/build_recovery.py +292 -0
- package/skills/gsd-path-research/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path-research/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-research/scripts/detect_project.py +1536 -0
- package/skills/gsd-path-research/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-research/scripts/discussion_validate.py +1326 -0
- package/skills/gsd-path-research/scripts/integration.py +1459 -0
- package/skills/gsd-path-research/scripts/isolation.py +24 -6
- package/skills/gsd-path-research/scripts/loop_run.py +839 -0
- package/skills/gsd-path-research/scripts/model_policy.py +279 -0
- package/skills/gsd-path-research/scripts/path_config.py +248 -0
- package/skills/gsd-path-research/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path-research/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path-research/scripts/promote_lookahead.py +286 -0
- package/skills/gsd-path-research/scripts/review_panel.py +564 -0
- package/skills/gsd-path-research/scripts/roadmap.py +326 -0
- package/skills/gsd-path-research/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path-research/scripts/state_promote.py +47 -44
- package/skills/gsd-path-research/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-roadmap/SKILL.md +24 -58
- package/skills/gsd-path-roadmap/references/dispatch.md +12 -19
- package/skills/gsd-path-roadmap/references/model-policy.md +139 -0
- package/skills/gsd-path-roadmap/references/runtime-selection.md +25 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +5 -0
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +30 -124
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +292 -0
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-roadmap/scripts/detect_project.py +22 -3
- package/skills/gsd-path-roadmap/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path-roadmap/scripts/integration.py +54 -28
- package/skills/gsd-path-roadmap/scripts/isolation.py +24 -6
- package/skills/gsd-path-roadmap/scripts/loop_run.py +839 -0
- package/skills/gsd-path-roadmap/scripts/model_policy.py +279 -0
- package/skills/gsd-path-roadmap/scripts/path_config.py +248 -0
- package/skills/gsd-path-roadmap/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +33 -162
- package/skills/gsd-path-roadmap/scripts/review_panel.py +11 -0
- package/skills/gsd-path-roadmap/scripts/roadmap.py +326 -0
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path-roadmap/scripts/state_promote.py +47 -44
- package/skills/gsd-path-roadmap/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-ship/SKILL.md +29 -17
- package/skills/gsd-path-ship/references/dispatch.md +12 -19
- package/skills/gsd-path-ship/references/jev-review.md +82 -0
- package/skills/gsd-path-ship/references/model-policy.md +139 -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 -148
- package/skills/gsd-path-ship/references/runtime-selection.md +25 -0
- package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/_common.py +5 -0
- package/skills/gsd-path-ship/scripts/archive_milestone.py +30 -124
- package/skills/gsd-path-ship/scripts/build_recovery.py +292 -0
- package/skills/gsd-path-ship/scripts/build_state.py +2 -0
- package/skills/gsd-path-ship/scripts/check_handoffs.py +7 -1
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-ship/scripts/detect_project.py +1536 -0
- package/skills/gsd-path-ship/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-ship/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path-ship/scripts/integration.py +54 -28
- package/skills/gsd-path-ship/scripts/isolation.py +24 -6
- package/skills/gsd-path-ship/scripts/jev_review.py +138 -0
- package/skills/gsd-path-ship/scripts/lean_verification.py +3 -1
- package/skills/gsd-path-ship/scripts/loop_run.py +839 -0
- package/skills/gsd-path-ship/scripts/model_policy.py +279 -0
- package/skills/gsd-path-ship/scripts/path_config.py +248 -0
- package/skills/gsd-path-ship/scripts/pipeline_git.py +3 -0
- package/skills/gsd-path-ship/scripts/pipeline_state.py +117 -186
- package/skills/gsd-path-ship/scripts/promote_lookahead.py +286 -0
- package/skills/gsd-path-ship/scripts/review_panel.py +11 -0
- package/skills/gsd-path-ship/scripts/roadmap.py +326 -0
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +114 -72
- package/skills/gsd-path-ship/scripts/state_promote.py +47 -44
- package/skills/gsd-path-ship/scripts/workflow_run.py +128 -8
- package/skills/gsd-path-ship/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-ship/templates/wave-review.md +1 -1
- package/skills/gsd-path-undo/SKILL.md +2 -0
- package/skills/gsd-path-undo/references/runtime-selection.md +25 -0
- package/skills/gsd-path-undo/scripts/_common.py +5 -0
- package/skills/gsd-path-undo/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-undo/scripts/loop_run.py +839 -0
- package/skills/gsd-path-undo/scripts/model_policy.py +279 -0
- package/skills/gsd-path-undo/scripts/path_config.py +248 -0
- package/skills/gsd-path-undo/scripts/pipeline_undo.py +14 -7
- package/skills/gsd-path-undo/scripts/review_panel.py +564 -0
- package/skills/gsd-path-undo/scripts/worktree_paths.py +94 -0
- package/skills/path/BUILD.md +54 -575
- package/skills/path/DECIDE.md +4 -6
- package/skills/path/DEFINE.md +23 -21
- package/skills/path/DOCS-AUDIT.md +2 -0
- package/skills/path/INSPECT.md +32 -8
- package/skills/path/PLAN.md +29 -19
- package/skills/path/RESEARCH.md +5 -6
- package/skills/path/ROADMAP.md +24 -58
- package/skills/path/SHIP.md +29 -17
- package/skills/path/SKILL.md +46 -22
- package/skills/path/references/build-abandon.md +33 -0
- package/skills/path/references/build-native.md +502 -0
- package/skills/path/references/build-recovery.md +78 -0
- package/skills/path/references/coder.md +8 -3
- package/skills/path/references/config.md +57 -0
- package/skills/path/references/dispatch.md +12 -19
- package/skills/path/references/docs-auditor.md +14 -0
- package/skills/path/references/jev-review.md +82 -0
- package/skills/path/references/model-policy.md +139 -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 -148
- package/skills/path/references/runtime-selection.md +25 -0
- package/skills/path/scripts/_common.py +5 -0
- package/skills/path/scripts/archive_milestone.py +30 -124
- package/skills/path/scripts/bootstrap_repository.py +33 -4
- package/skills/path/scripts/build_recovery.py +292 -0
- package/skills/path/scripts/build_state.py +2 -0
- package/skills/path/scripts/check_docs_audit.py +7 -3
- package/skills/path/scripts/check_handoffs.py +7 -1
- package/skills/path/scripts/check_task_briefs.py +3 -0
- package/skills/path/scripts/detect_project.py +22 -3
- package/skills/path/scripts/discussion_records.py +3 -0
- package/skills/path/scripts/discussion_validate.py +8 -3
- package/skills/path/scripts/dispatch_driver.py +233 -22
- package/skills/path/scripts/git_guard.py +768 -0
- package/skills/path/scripts/integration.py +54 -28
- package/skills/path/scripts/isolation.py +24 -6
- package/skills/path/scripts/jev_review.py +138 -0
- package/skills/path/scripts/lean_verification.py +3 -1
- package/skills/path/scripts/loop_run.py +2 -0
- package/skills/path/scripts/model_policy.py +279 -0
- package/skills/path/scripts/path_config.py +248 -0
- package/skills/path/scripts/pipeline_git.py +3 -0
- package/skills/path/scripts/pipeline_state.py +117 -186
- package/skills/path/scripts/promote_lookahead.py +33 -162
- package/skills/path/scripts/review_panel.py +11 -0
- package/skills/path/scripts/roadmap.py +326 -0
- package/skills/path/scripts/state_checkpoint.py +114 -72
- package/skills/path/scripts/state_promote.py +47 -44
- package/skills/path/scripts/status_runtime.py +208 -0
- 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 +128 -8
- package/skills/path/scripts/worktree_paths.py +94 -0
- package/skills/path/templates/skeptic.md +1 -1
- package/skills/path/templates/task.md +2 -0
- package/skills/path/templates/wave-panel.md +1 -1
- package/skills/path/templates/wave-review.md +1 -1
|
@@ -16,6 +16,9 @@ import re
|
|
|
16
16
|
import shutil
|
|
17
17
|
import subprocess
|
|
18
18
|
import sys
|
|
19
|
+
|
|
20
|
+
# Runtime helpers must not modify their immutable installation.
|
|
21
|
+
sys.dont_write_bytecode = True
|
|
19
22
|
from contextlib import contextmanager
|
|
20
23
|
from datetime import date
|
|
21
24
|
from pathlib import Path, PurePosixPath
|
|
@@ -239,15 +242,10 @@ class ArchiveError(RuntimeError):
|
|
|
239
242
|
|
|
240
243
|
|
|
241
244
|
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
245
|
|
|
247
246
|
|
|
248
|
-
# discussion_validate
|
|
249
|
-
#
|
|
250
|
-
# import must stay below those definitions.
|
|
247
|
+
# discussion_validate calls back into this module and imports its constants
|
|
248
|
+
# and ArchiveError, so this import must stay below those definitions.
|
|
251
249
|
if __package__: # imported as scripts.archive_milestone
|
|
252
250
|
from .discussion_validate import (
|
|
253
251
|
record_sections,
|
|
@@ -280,42 +278,6 @@ if __package__: # imported as scripts.archive_milestone
|
|
|
280
278
|
render_manifest,
|
|
281
279
|
validate_manifest,
|
|
282
280
|
)
|
|
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
281
|
else: # standalone script or sibling import
|
|
320
282
|
if __name__ == "__main__":
|
|
321
283
|
sys.modules.setdefault("archive_milestone", sys.modules[__name__])
|
|
@@ -351,42 +313,6 @@ else: # standalone script or sibling import
|
|
|
351
313
|
render_manifest,
|
|
352
314
|
validate_manifest,
|
|
353
315
|
)
|
|
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
316
|
except ImportError: # pragma: no cover - package import used by tests
|
|
391
317
|
from scripts.discussion_validate import (
|
|
392
318
|
record_sections,
|
|
@@ -419,42 +345,6 @@ else: # standalone script or sibling import
|
|
|
419
345
|
render_manifest,
|
|
420
346
|
validate_manifest,
|
|
421
347
|
)
|
|
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
348
|
|
|
459
349
|
|
|
460
350
|
@contextmanager
|
|
@@ -1856,6 +1746,12 @@ def require_clean_active_root(active_root: Path, archive: Path) -> None:
|
|
|
1856
1746
|
carried_forward = pending_ruling_count(archived_audit)
|
|
1857
1747
|
# Persistent project metadata ships but never archives with a milestone.
|
|
1858
1748
|
allowed = {"STATE.md", "REPOSITORY.md", "archive", "LESSONS.md", "next"}
|
|
1749
|
+
for name in ("config.json", "model-policy.json"):
|
|
1750
|
+
path = active_root / name
|
|
1751
|
+
if path.exists() or path.is_symlink():
|
|
1752
|
+
if path.is_symlink() or not path.is_file() or path.stat().st_nlink != 1:
|
|
1753
|
+
raise ArchiveError(f"project settings must be a regular unlinked file: {path}")
|
|
1754
|
+
allowed.add(name)
|
|
1859
1755
|
if (active_root / "CHARTER.md").is_file():
|
|
1860
1756
|
# Program flow: these persist at the .project/ top level.
|
|
1861
1757
|
allowed |= {"CHARTER.md", "ROADMAP.md", "SYNTHESIS.md"}
|
|
@@ -1921,6 +1817,7 @@ def prepared_transaction(
|
|
|
1921
1817
|
repo: Path,
|
|
1922
1818
|
phases: Sequence[str],
|
|
1923
1819
|
must_be_uncommitted: bool = False,
|
|
1820
|
+
*, historical: bool = False,
|
|
1924
1821
|
) -> tuple:
|
|
1925
1822
|
project = repo.resolve()
|
|
1926
1823
|
active_root = require_project_layout(project)
|
|
@@ -1929,7 +1826,7 @@ def prepared_transaction(
|
|
|
1929
1826
|
state, _, loaded_state_path = strict_state(project)
|
|
1930
1827
|
if loaded_state_path.resolve() != state_path.resolve():
|
|
1931
1828
|
raise ArchiveError("strict STATE.md loader returned an unexpected path")
|
|
1932
|
-
require_transaction_context(project, state, phases)
|
|
1829
|
+
require_transaction_context(project, state, phases, historical=historical)
|
|
1933
1830
|
|
|
1934
1831
|
configured = state.archive
|
|
1935
1832
|
if configured is None:
|
|
@@ -1993,10 +1890,11 @@ def require_canonical_commit_body(
|
|
|
1993
1890
|
raise ArchiveError(f"{label} commit body does not match required fields")
|
|
1994
1891
|
|
|
1995
1892
|
|
|
1996
|
-
def find_ship_commit(project: Path, archive_name: str) -> str:
|
|
1893
|
+
def find_ship_commit(project: Path, archive_name: str, *, historical: bool = False) -> str:
|
|
1997
1894
|
expected_subject = ship_subject(archive_name)
|
|
1998
1895
|
log = require_git_success(
|
|
1999
|
-
run_git(project, "log", "--first-parent"
|
|
1896
|
+
run_git(project, "log", *([] if historical else ["--first-parent"]),
|
|
1897
|
+
"--format=%H%x00%s", "HEAD"),
|
|
2000
1898
|
"inspect HEAD history for the ship commit",
|
|
2001
1899
|
)
|
|
2002
1900
|
matches = []
|
|
@@ -2039,19 +1937,22 @@ def validate_shipped_roadmap(text: str, state: PipelineState, archive: str) -> N
|
|
|
2039
1937
|
raise ArchiveError("ROADMAP.md must record Status: shipped and the exact Archive")
|
|
2040
1938
|
|
|
2041
1939
|
|
|
2042
|
-
def validate(repo: Path) -> dict:
|
|
1940
|
+
def validate(repo: Path, *, historical: bool = False) -> dict:
|
|
2043
1941
|
project, _, state, configured, archive, archived_files, reviewed_head = prepared_transaction(
|
|
2044
1942
|
repo,
|
|
2045
1943
|
("shipped",),
|
|
1944
|
+
historical=historical,
|
|
2046
1945
|
)
|
|
2047
1946
|
if state.status != "done":
|
|
2048
1947
|
raise ArchiveError("STATE.md is not shipped/done")
|
|
2049
1948
|
|
|
2050
|
-
project_status = require_git_success(
|
|
1949
|
+
project_status = require_git_success(
|
|
1950
|
+
run_git(project, "status", "--porcelain", *(["--", ".project"] if historical else [])),
|
|
1951
|
+
"inspect worktree status")
|
|
2051
1952
|
if project_status:
|
|
2052
1953
|
raise ArchiveError("ship transaction worktree is not clean")
|
|
2053
1954
|
|
|
2054
|
-
ship_commit = find_ship_commit(project, archive.name)
|
|
1955
|
+
ship_commit = find_ship_commit(project, archive.name, historical=historical)
|
|
2055
1956
|
|
|
2056
1957
|
parents = require_git_success(
|
|
2057
1958
|
run_git(project, "rev-list", "--parents", "-n", "1", ship_commit),
|
|
@@ -2238,6 +2139,11 @@ def parser() -> argparse.ArgumentParser:
|
|
|
2238
2139
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
2239
2140
|
arguments = parser().parse_args(argv)
|
|
2240
2141
|
try:
|
|
2142
|
+
if arguments.command in {"validate-integrated", "refresh-origin", "integrate"}:
|
|
2143
|
+
if __package__:
|
|
2144
|
+
from . import integration
|
|
2145
|
+
else:
|
|
2146
|
+
import integration
|
|
2241
2147
|
if arguments.command == "prepare":
|
|
2242
2148
|
result = prepare(arguments.repo, arguments.slug)
|
|
2243
2149
|
elif arguments.command == "render-manifest":
|
|
@@ -2247,11 +2153,11 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
2247
2153
|
elif arguments.command == "abandon":
|
|
2248
2154
|
result = abandon(arguments.repo, arguments.slug, arguments.reason)
|
|
2249
2155
|
elif arguments.command == "validate-integrated":
|
|
2250
|
-
result = validate_integrated(arguments.repo, arguments.slug)
|
|
2156
|
+
result = integration.validate_integrated(arguments.repo, arguments.slug)
|
|
2251
2157
|
elif arguments.command == "refresh-origin":
|
|
2252
|
-
result = refresh_origin(arguments.repo)
|
|
2158
|
+
result = integration.refresh_origin(arguments.repo)
|
|
2253
2159
|
elif arguments.command == "integrate":
|
|
2254
|
-
result = integrate(arguments.repo, arguments.slug)
|
|
2160
|
+
result = integration.integrate(arguments.repo, arguments.slug)
|
|
2255
2161
|
else:
|
|
2256
2162
|
result = validate(arguments.repo)
|
|
2257
2163
|
except (ArchiveError, OSError) as error:
|
|
@@ -0,0 +1,292 @@
|
|
|
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 sys
|
|
7
|
+
|
|
8
|
+
# Runtime helpers must not modify their immutable installation.
|
|
9
|
+
sys.dont_write_bytecode = True
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import re
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
EVENTS = {
|
|
17
|
+
"define": "build intent corrections requested",
|
|
18
|
+
"plan": "build plan repair requested",
|
|
19
|
+
}
|
|
20
|
+
MARKER = "build recovery: "
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def runtime():
|
|
24
|
+
if __package__:
|
|
25
|
+
from scripts import pipeline_state
|
|
26
|
+
else:
|
|
27
|
+
import pipeline_state
|
|
28
|
+
return pipeline_state
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def checkpoint_runtime():
|
|
32
|
+
if __package__:
|
|
33
|
+
from scripts import state_checkpoint
|
|
34
|
+
else:
|
|
35
|
+
import state_checkpoint
|
|
36
|
+
return state_checkpoint
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def context(repo: Path, text: str | None = None) -> dict | None:
|
|
40
|
+
state = runtime()
|
|
41
|
+
current, loaded, _ = state.load_state(repo)
|
|
42
|
+
text = loaded if text is None else text
|
|
43
|
+
recovery = None
|
|
44
|
+
for line in text.splitlines():
|
|
45
|
+
if " — " not in line:
|
|
46
|
+
continue
|
|
47
|
+
event = line.split(" — ", 2)[-1]
|
|
48
|
+
if event.startswith(MARKER):
|
|
49
|
+
try:
|
|
50
|
+
value = json.loads(event[len(MARKER):])
|
|
51
|
+
except ValueError as error:
|
|
52
|
+
raise state.PipelineStateError("invalid build recovery record") from error
|
|
53
|
+
if (not isinstance(value, dict) or set(value) != {"base", "branch", "kind"}
|
|
54
|
+
or value["kind"] not in EVENTS
|
|
55
|
+
or not isinstance(value["base"], str)
|
|
56
|
+
or not re.fullmatch(r"[0-9a-f]{40}", value["base"])):
|
|
57
|
+
raise state.PipelineStateError("invalid build recovery record")
|
|
58
|
+
recovery = {**value, "active": True} if value["branch"] == current.branch else None
|
|
59
|
+
elif event == "build started" and recovery:
|
|
60
|
+
recovery["active"] = False
|
|
61
|
+
if recovery:
|
|
62
|
+
if not state._is_ancestor(repo, recovery["base"], "HEAD"):
|
|
63
|
+
raise state.PipelineStateError("build recovery base is not an ancestor of HEAD")
|
|
64
|
+
source = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/STATE.md")
|
|
65
|
+
if source is None:
|
|
66
|
+
raise state.PipelineStateError("build recovery base has no STATE.md")
|
|
67
|
+
before = state._state_from_text(source)
|
|
68
|
+
if ((before.phase, before.status) != ("build", "blocked")
|
|
69
|
+
or before.branch != current.branch or before.milestone != current.milestone):
|
|
70
|
+
raise state.PipelineStateError("build recovery base does not prove this blocked milestone")
|
|
71
|
+
return recovery
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def task_fields(text: str, label: str) -> dict:
|
|
75
|
+
if __package__:
|
|
76
|
+
from scripts.isolation import task_frontmatter
|
|
77
|
+
else:
|
|
78
|
+
from isolation import task_frontmatter
|
|
79
|
+
fields, error = task_frontmatter(text)
|
|
80
|
+
if fields is None:
|
|
81
|
+
raise runtime().PipelineStateError(f"{label}: {error}")
|
|
82
|
+
return fields
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def settled_tasks(repo: Path) -> dict[str, tuple[str, dict]]:
|
|
86
|
+
"""Read task ownership without requiring the graph that Plan must repair."""
|
|
87
|
+
state = runtime()
|
|
88
|
+
directory = repo / ".project/tasks"
|
|
89
|
+
if directory.is_symlink() or not directory.is_dir():
|
|
90
|
+
raise state.PipelineStateError("build recovery requires real task artifacts")
|
|
91
|
+
tasks = {}
|
|
92
|
+
for path in sorted(directory.iterdir()):
|
|
93
|
+
if not re.fullmatch(r"T[0-9]{3}-[a-z0-9][a-z0-9-]*\.md", path.name):
|
|
94
|
+
raise state.PipelineStateError(f"invalid recovery task path: {path.name}")
|
|
95
|
+
text = state._read_real_file(path, path.name)
|
|
96
|
+
fields = task_fields(text, path.name)
|
|
97
|
+
if fields.get("status") not in {"pending", "done"}:
|
|
98
|
+
raise state.PipelineStateError(f"settle task ownership before recovery: {path.name}")
|
|
99
|
+
if fields["status"] == "pending" and any(
|
|
100
|
+
fields.get(key) != "null" for key in ("agent", "base", "worktree", "task_branch")
|
|
101
|
+
):
|
|
102
|
+
raise state.PipelineStateError(f"pending task retains dispatch metadata: {path.name}")
|
|
103
|
+
tasks[path.name] = (text, fields)
|
|
104
|
+
if not tasks:
|
|
105
|
+
raise state.PipelineStateError("build recovery requires task artifacts")
|
|
106
|
+
return tasks
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def begin(repo: Path, before, after, event: str) -> dict:
|
|
110
|
+
state = runtime()
|
|
111
|
+
if (before.phase, before.status) != ("build", "blocked") or after.phase not in EVENTS:
|
|
112
|
+
raise state.PipelineStateError("build recovery requires build/blocked")
|
|
113
|
+
if event != EVENTS[after.phase]:
|
|
114
|
+
raise state.PipelineStateError(f"state transition requires event: {EVENTS[after.phase]}")
|
|
115
|
+
if before.archive is not None or before.branch != state._current_branch(repo):
|
|
116
|
+
raise state.PipelineStateError("build recovery requires the active bound branch")
|
|
117
|
+
if state._worktree_changes(repo):
|
|
118
|
+
raise state.PipelineStateError("checkpoint the blocked build before recovery")
|
|
119
|
+
if any(state.transaction_journals(repo).values()):
|
|
120
|
+
raise state.PipelineStateError("finish pending transactions before build recovery")
|
|
121
|
+
records = records_root(repo)
|
|
122
|
+
for directory in {path.parent.parent for path in records.rglob("attempt-*/state.json")}:
|
|
123
|
+
latest = max(directory.glob("attempt-*/state.json"), key=lambda path: int(path.parent.name.split("-")[1]))
|
|
124
|
+
record = state._read_json(latest)
|
|
125
|
+
if (record.get("cleanup_pending")
|
|
126
|
+
or (record.get("worktree") and record.get("branch") and not record.get("cleanup_complete"))
|
|
127
|
+
or record.get("outcome") not in {"landed", "collected", "blocked", "resolved", "redispatched"}):
|
|
128
|
+
raise state.PipelineStateError(f"settle dispatch records before recovery: {latest}")
|
|
129
|
+
tasks = settled_tasks(repo)
|
|
130
|
+
state._read_real_file(repo / ".project/plan/PLAN.md", "PLAN.md")
|
|
131
|
+
state._read_real_file(repo / ".project/intent/INTENT.md", "INTENT.md")
|
|
132
|
+
base = state._run_git(repo, "rev-parse", "HEAD").stdout.strip()
|
|
133
|
+
done = [repo / ".project/tasks" / name for name, (_, fields) in tasks.items()
|
|
134
|
+
if fields["status"] == "done"]
|
|
135
|
+
if done:
|
|
136
|
+
if __package__:
|
|
137
|
+
from scripts.isolation import verify_landed_task_files
|
|
138
|
+
else:
|
|
139
|
+
from isolation import verify_landed_task_files
|
|
140
|
+
verify_landed_task_files(repo, done, ".project/tasks", base)
|
|
141
|
+
return {"kind": after.phase, "base": base, "branch": before.branch}
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def records_root(repo: Path) -> Path:
|
|
145
|
+
if __package__:
|
|
146
|
+
from scripts import isolation
|
|
147
|
+
else:
|
|
148
|
+
import isolation
|
|
149
|
+
root = isolation.common_git_dir(repo) / "gsd-path/dispatch" / isolation.require_bound(repo).replace("/", "-")
|
|
150
|
+
recovery = context(repo)
|
|
151
|
+
return root / recovery["base"] if recovery else root
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def review_backup(repo: Path, recovery: dict) -> Path:
|
|
155
|
+
return repo / ".project/plan" / f"build-recovery-{recovery['base']}" / "review"
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def validate_review_backup(repo: Path, recovery: dict, directory: Path) -> None:
|
|
159
|
+
state = runtime()
|
|
160
|
+
if directory.exists():
|
|
161
|
+
checkpoint_runtime()._tree_digest(directory) # Reject symlinks and special files before reading.
|
|
162
|
+
expected = {}
|
|
163
|
+
for path in state._run_git(repo, "ls-tree", "-r", "--name-only", "-z",
|
|
164
|
+
recovery["base"], "--", ".project/review").stdout.split("\0"):
|
|
165
|
+
if path:
|
|
166
|
+
expected[str(Path(path).relative_to(".project/review"))] = checkpoint_runtime()._git_text_at(repo, recovery["base"], path)
|
|
167
|
+
actual = {str(path.relative_to(directory)): path.read_text(encoding="utf-8")
|
|
168
|
+
for path in directory.rglob("*") if path.is_file()} if directory.exists() else {}
|
|
169
|
+
if actual != expected:
|
|
170
|
+
raise state.PipelineStateError("review backup differs from the recovery base")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def prepare(repo: Path) -> dict:
|
|
174
|
+
"""Move old reviews once; the durable STATE record owns interruption recovery."""
|
|
175
|
+
state = runtime()
|
|
176
|
+
repo = state._repo_root(repo)
|
|
177
|
+
with state._state_lock(repo / ".project"):
|
|
178
|
+
recovery = context(repo)
|
|
179
|
+
if not recovery or not recovery["active"]:
|
|
180
|
+
raise state.PipelineStateError("no active build recovery")
|
|
181
|
+
target = review_backup(repo, recovery)
|
|
182
|
+
if (repo / ".project/plan").is_symlink() or target.parent.is_symlink() or target.is_symlink():
|
|
183
|
+
raise state.PipelineStateError("unsafe recovery review backup")
|
|
184
|
+
source = repo / ".project/review"
|
|
185
|
+
if source.is_symlink():
|
|
186
|
+
raise state.PipelineStateError("unsafe recovery review directory")
|
|
187
|
+
if not target.exists():
|
|
188
|
+
validate_review_backup(repo, recovery, source)
|
|
189
|
+
target.parent.mkdir(exist_ok=True)
|
|
190
|
+
if source.exists():
|
|
191
|
+
source.rename(target)
|
|
192
|
+
else:
|
|
193
|
+
target.mkdir()
|
|
194
|
+
validate_review_backup(repo, recovery, target)
|
|
195
|
+
source.mkdir(exist_ok=True)
|
|
196
|
+
return {"status": "prepared", "recovery": recovery, "path": str(target)}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def validate_plan(repo: Path) -> None:
|
|
200
|
+
state = runtime()
|
|
201
|
+
recovery = context(repo)
|
|
202
|
+
if not recovery or not recovery["active"]:
|
|
203
|
+
return
|
|
204
|
+
if not review_backup(repo, recovery).is_dir():
|
|
205
|
+
raise state.PipelineStateError("run prepare-build-recovery before planning")
|
|
206
|
+
validate_review_backup(repo, recovery, review_backup(repo, recovery))
|
|
207
|
+
source = checkpoint_runtime()._task_contracts_at(repo, recovery["base"], ".project/tasks")
|
|
208
|
+
current = settled_tasks(repo)
|
|
209
|
+
done = set()
|
|
210
|
+
for _, (path, text) in source.items():
|
|
211
|
+
fields = task_fields(text, path)
|
|
212
|
+
if fields.get("status") == "done":
|
|
213
|
+
name = Path(path).name
|
|
214
|
+
done.add(name)
|
|
215
|
+
if name not in current or current[name][0] != text:
|
|
216
|
+
raise state.PipelineStateError(f"build recovery must preserve landed task: {name}")
|
|
217
|
+
if any(fields["status"] == "done" and name not in done
|
|
218
|
+
for name, (_, fields) in current.items()):
|
|
219
|
+
raise state.PipelineStateError("build recovery cannot manufacture landed tasks")
|
|
220
|
+
if recovery["kind"] == "plan":
|
|
221
|
+
intent = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/intent/INTENT.md")
|
|
222
|
+
if state._read_real_file(repo / ".project/intent/INTENT.md", "INTENT.md") != intent:
|
|
223
|
+
raise state.PipelineStateError("plan repair cannot change approved intent")
|
|
224
|
+
unchanged = unchanged_waves(repo, recovery)
|
|
225
|
+
for path in (repo / ".project/review").iterdir():
|
|
226
|
+
match = re.match(r"wave-([1-9]\d*)[.]", path.name)
|
|
227
|
+
if match and int(match[1]) not in unchanged:
|
|
228
|
+
raise state.PipelineStateError("changed wave must be reviewed after build resumes")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def unchanged_waves(repo: Path, recovery: dict) -> set[int]:
|
|
232
|
+
"""Reuse evidence only when its intent, wave contract, and task bytes match."""
|
|
233
|
+
state = runtime()
|
|
234
|
+
project = repo / ".project"
|
|
235
|
+
old_intent = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/intent/INTENT.md")
|
|
236
|
+
if state._read_real_file(project / "intent/INTENT.md", "INTENT.md") != old_intent:
|
|
237
|
+
return set()
|
|
238
|
+
old_plan = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/plan/PLAN.md") or ""
|
|
239
|
+
new_plan = state._read_real_file(project / "plan/PLAN.md", "PLAN.md")
|
|
240
|
+
|
|
241
|
+
def sections(text):
|
|
242
|
+
return {match[1]: match[2] for match in re.finditer(
|
|
243
|
+
r"(?ms)^## ([^\n]+)\n(.*?)(?=^## |\Z)", text,
|
|
244
|
+
)}
|
|
245
|
+
|
|
246
|
+
old_sections, new_sections = sections(old_plan), sections(new_plan)
|
|
247
|
+
# Non-wave settings govern every wave, including coverage and surfaces.
|
|
248
|
+
if ({k: v for k, v in old_sections.items() if not k.startswith("Wave ")}
|
|
249
|
+
!= {k: v for k, v in new_sections.items() if not k.startswith("Wave ")}):
|
|
250
|
+
return set()
|
|
251
|
+
old_tasks = checkpoint_runtime()._task_contracts_at(repo, recovery["base"], ".project/tasks")
|
|
252
|
+
current = settled_tasks(repo)
|
|
253
|
+
unchanged = set()
|
|
254
|
+
for title, body in new_sections.items():
|
|
255
|
+
match = re.match(r"Wave ([1-9]\d*)\b", title)
|
|
256
|
+
if not match or old_sections.get(title) != body:
|
|
257
|
+
continue
|
|
258
|
+
wave = match[1]
|
|
259
|
+
before = {Path(path).name: text for path, text in old_tasks.values()
|
|
260
|
+
if task_fields(text, path).get("wave") == wave}
|
|
261
|
+
after = {name: text for name, (text, fields) in current.items() if fields.get("wave") == wave}
|
|
262
|
+
if before and before == after:
|
|
263
|
+
unchanged.add(int(wave))
|
|
264
|
+
return unchanged
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def restore_unchanged_reviews(repo: Path) -> None:
|
|
268
|
+
recovery = context(repo)
|
|
269
|
+
if not recovery or not recovery["active"]:
|
|
270
|
+
return
|
|
271
|
+
unchanged = unchanged_waves(repo, recovery)
|
|
272
|
+
for path in review_backup(repo, recovery).iterdir():
|
|
273
|
+
match = re.match(r"wave-([1-9]\d*)[.]", path.name)
|
|
274
|
+
if match and int(match[1]) in unchanged:
|
|
275
|
+
destination = repo / ".project/review" / path.name
|
|
276
|
+
content = path.read_text(encoding="utf-8")
|
|
277
|
+
if not destination.exists() or runtime()._read_real_file(destination, destination.name) != content:
|
|
278
|
+
runtime()._atomic_write(destination, content)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def inventory_checkpoint(repo: Path, head: str) -> bool:
|
|
282
|
+
state = runtime()
|
|
283
|
+
if state._run_git(repo, "show", "-s", "--format=%s", head).stdout.strip() != state.PLAN_APPROVAL_SUBJECT:
|
|
284
|
+
return False
|
|
285
|
+
text = checkpoint_runtime()._git_text_at(repo, head, ".project/STATE.md")
|
|
286
|
+
if text is None:
|
|
287
|
+
return False
|
|
288
|
+
approved = state._state_from_text(text)
|
|
289
|
+
if (approved.phase, approved.status) != ("plan", "done"):
|
|
290
|
+
return False
|
|
291
|
+
recovery = context(repo, text)
|
|
292
|
+
return bool(recovery and recovery["active"])
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
# gsd-path project runtime
|
|
2
3
|
"""Build task readiness, landing reconciliation, and the verify ledger."""
|
|
3
4
|
|
|
4
5
|
from __future__ import annotations
|
|
@@ -7,6 +8,7 @@ import argparse
|
|
|
7
8
|
import json
|
|
8
9
|
import re
|
|
9
10
|
import sys
|
|
11
|
+
sys.dont_write_bytecode = True
|
|
10
12
|
from dataclasses import dataclass
|
|
11
13
|
from datetime import datetime, timezone
|
|
12
14
|
from pathlib import Path, PurePosixPath
|
|
@@ -7,6 +7,9 @@ import json
|
|
|
7
7
|
import re
|
|
8
8
|
import subprocess
|
|
9
9
|
import sys
|
|
10
|
+
|
|
11
|
+
# Runtime helpers must not modify their immutable installation.
|
|
12
|
+
sys.dont_write_bytecode = True
|
|
10
13
|
from pathlib import Path, PurePosixPath
|
|
11
14
|
from typing import Dict, List, Optional, Sequence, Set, Tuple
|
|
12
15
|
|
|
@@ -131,6 +134,9 @@ def _strict_frontmatter(text: str, label: str) -> Dict[str, object]:
|
|
|
131
134
|
while index < len(lines):
|
|
132
135
|
line = lines[index]
|
|
133
136
|
if line == "---":
|
|
137
|
+
for field in ('model', 'effort'):
|
|
138
|
+
if field in values and (not isinstance(values[field], str) or not values[field].strip()):
|
|
139
|
+
raise HandoffError(f'{label}: {field} must be a nonempty scalar or inherit')
|
|
134
140
|
return values
|
|
135
141
|
if not line.strip() or line.lstrip().startswith("#"):
|
|
136
142
|
index += 1
|
|
@@ -1462,7 +1468,7 @@ def validate_wave_evidence(
|
|
|
1462
1468
|
raise HandoffError(f"{name} Cycle field does not match its filename")
|
|
1463
1469
|
if _line_value(text, "Depth:") != expected_depth:
|
|
1464
1470
|
raise HandoffError(f"{name} Depth does not match PLAN.md")
|
|
1465
|
-
lens_fields = re.findall(r"(?m)^Lens
|
|
1471
|
+
lens_fields = re.findall(r"(?m)^Lens:[ \t]*(\S.*?)[ \t]*$", text)
|
|
1466
1472
|
if lens is None and lens_fields:
|
|
1467
1473
|
raise HandoffError(f"{name} must not declare a review lens")
|
|
1468
1474
|
if lens is not None and lens_fields != [lens]:
|
|
@@ -6,6 +6,9 @@ import argparse
|
|
|
6
6
|
import json
|
|
7
7
|
import re
|
|
8
8
|
import sys
|
|
9
|
+
|
|
10
|
+
# Runtime helpers must not modify their immutable installation.
|
|
11
|
+
sys.dont_write_bytecode = True
|
|
9
12
|
from pathlib import Path, PurePosixPath
|
|
10
13
|
from typing import Dict, List, Optional, Sequence, Set, Tuple
|
|
11
14
|
|