@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
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
# Runtime helpers must not modify their immutable installation.
|
|
10
|
+
sys.dont_write_bytecode = True
|
|
11
|
+
|
|
7
12
|
import hashlib
|
|
8
13
|
import json
|
|
9
14
|
import os
|
|
@@ -20,6 +25,43 @@ except ModuleNotFoundError as error: # pragma: no cover - package imports used
|
|
|
20
25
|
from scripts.isolation import IsolationError, checkpoint as isolation_checkpoint, require_commit, require_full_sha, PROJECT_ENTRIES
|
|
21
26
|
|
|
22
27
|
|
|
28
|
+
if __package__:
|
|
29
|
+
from . import roadmap
|
|
30
|
+
else:
|
|
31
|
+
import roadmap
|
|
32
|
+
|
|
33
|
+
if __package__: # imported as scripts.state_checkpoint
|
|
34
|
+
from . import pipeline_state
|
|
35
|
+
from .pipeline_state import (
|
|
36
|
+
CHECKPOINT_JOURNAL_NAME,
|
|
37
|
+
CHECKPOINT_KINDS,
|
|
38
|
+
CHECKPOINT_SCHEMA,
|
|
39
|
+
PLAN_APPROVAL_SUBJECT,
|
|
40
|
+
PROMOTION_TRACKS,
|
|
41
|
+
SLUG_RE,
|
|
42
|
+
TASK_FILE_RE,
|
|
43
|
+
TASK_ID_RE,
|
|
44
|
+
ApprovedTaskContract,
|
|
45
|
+
PipelineState,
|
|
46
|
+
PipelineStateError,
|
|
47
|
+
)
|
|
48
|
+
else: # standalone script or sibling import
|
|
49
|
+
import pipeline_state
|
|
50
|
+
from pipeline_state import (
|
|
51
|
+
CHECKPOINT_JOURNAL_NAME,
|
|
52
|
+
CHECKPOINT_KINDS,
|
|
53
|
+
CHECKPOINT_SCHEMA,
|
|
54
|
+
PLAN_APPROVAL_SUBJECT,
|
|
55
|
+
PROMOTION_TRACKS,
|
|
56
|
+
SLUG_RE,
|
|
57
|
+
TASK_FILE_RE,
|
|
58
|
+
TASK_ID_RE,
|
|
59
|
+
ApprovedTaskContract,
|
|
60
|
+
PipelineState,
|
|
61
|
+
PipelineStateError,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
23
65
|
def _sha256(content: str) -> str:
|
|
24
66
|
return hashlib.sha256(content.encode("utf-8")).hexdigest()
|
|
25
67
|
|
|
@@ -65,41 +107,29 @@ def _checkpoint_artifact_digest(project: Path, mutable_paths: set[str]) -> str:
|
|
|
65
107
|
return digest.hexdigest()
|
|
66
108
|
|
|
67
109
|
|
|
68
|
-
def _roadmap_status(lines: list[str], start: int, end: int) -> str:
|
|
69
|
-
values = []
|
|
70
|
-
for line in lines[start:end]:
|
|
71
|
-
match = re.fullmatch(r"Status:\s*(\S+)\s*", line.rstrip("\r\n"))
|
|
72
|
-
if match:
|
|
73
|
-
values.append(match.group(1))
|
|
74
|
-
if len(values) != 1:
|
|
75
|
-
raise PipelineStateError("ROADMAP.md milestone must have one Status field")
|
|
76
|
-
return values[0]
|
|
77
|
-
|
|
78
|
-
|
|
79
110
|
def _activate_roadmap_milestone(text: str, milestone: str) -> str:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
active = [
|
|
85
|
-
slug
|
|
86
|
-
for slug, (_, start, end) in sections.items()
|
|
87
|
-
if _roadmap_status(lines, start, end) == "active"
|
|
88
|
-
]
|
|
89
|
-
if active:
|
|
90
|
-
raise PipelineStateError(
|
|
91
|
-
"ROADMAP.md already has an active milestone: " + ", ".join(active)
|
|
92
|
-
)
|
|
93
|
-
_, start, end = sections[milestone]
|
|
94
|
-
pipeline_state._replace_roadmap_field(lines, start, end, "Status", {"pending"}, "active")
|
|
95
|
-
return "".join(lines)
|
|
96
|
-
|
|
111
|
+
try:
|
|
112
|
+
return roadmap.activate_milestone(text, milestone)
|
|
113
|
+
except roadmap.RoadmapError as error:
|
|
114
|
+
raise PipelineStateError(str(error)) from error
|
|
97
115
|
|
|
98
116
|
def _approval_details(
|
|
99
117
|
kind: str,
|
|
100
118
|
state: PipelineState,
|
|
101
119
|
selected_milestone: Optional[str],
|
|
102
120
|
) -> tuple[dict[str, Optional[str]], str, str, str]:
|
|
121
|
+
if kind == "roadmap-reslice":
|
|
122
|
+
if state.status != "active" or state.archive is not None:
|
|
123
|
+
raise PipelineStateError("roadmap re-slice requires active, unarchived state")
|
|
124
|
+
if state.phase in {"inspect", "define"} and selected_milestone == state.milestone and state.milestone:
|
|
125
|
+
changes = {}
|
|
126
|
+
event = "program roadmap re-slice approved"
|
|
127
|
+
elif state.phase == "roadmap" and state.milestone is None and selected_milestone and SLUG_RE.fullmatch(selected_milestone):
|
|
128
|
+
changes = {"phase": "inspect", "milestone": selected_milestone}
|
|
129
|
+
event = "roadmap approved after abandoned milestone"
|
|
130
|
+
else:
|
|
131
|
+
raise PipelineStateError("roadmap re-slice must preserve the active milestone or select after abandon")
|
|
132
|
+
return changes, event, "roadmap: program roadmap approved", "Why: approved roadmap checkpoint"
|
|
103
133
|
if kind == "plan":
|
|
104
134
|
if selected_milestone is not None:
|
|
105
135
|
raise PipelineStateError("plan approval does not accept a selected milestone")
|
|
@@ -126,6 +156,12 @@ def _approval_details(
|
|
|
126
156
|
raise PipelineStateError(f"unsupported approval kind: {kind}")
|
|
127
157
|
|
|
128
158
|
|
|
159
|
+
def _reslice_roadmap(before: str, candidate: str, state: PipelineState, selected: str) -> str:
|
|
160
|
+
try:
|
|
161
|
+
return roadmap.reslice(before, candidate, state.phase, selected)
|
|
162
|
+
except roadmap.RoadmapError as error:
|
|
163
|
+
raise PipelineStateError(str(error)) from error
|
|
164
|
+
|
|
129
165
|
def _checkpoint_request_fields(
|
|
130
166
|
kind: str,
|
|
131
167
|
project_dir: str,
|
|
@@ -175,6 +211,10 @@ def _approval_journal(repo: Path, journal: Mapping[str, object]) -> dict[str, ob
|
|
|
175
211
|
"roadmap_before",
|
|
176
212
|
"roadmap_after",
|
|
177
213
|
}
|
|
214
|
+
if journal.get("kind") == "roadmap-reslice":
|
|
215
|
+
expected_keys.add("baseline_before")
|
|
216
|
+
if not isinstance(journal.get("baseline_before"), str):
|
|
217
|
+
raise PipelineStateError("checkpoint journal has an invalid re-slice baseline")
|
|
178
218
|
if set(journal) != expected_keys:
|
|
179
219
|
raise PipelineStateError("checkpoint journal has invalid fields")
|
|
180
220
|
if journal.get("schema") != CHECKPOINT_SCHEMA or journal.get("repo") != str(repo):
|
|
@@ -253,12 +293,14 @@ def _approval_journal(repo: Path, journal: Mapping[str, object]) -> dict[str, ob
|
|
|
253
293
|
or journal["state_after"] != rendered
|
|
254
294
|
):
|
|
255
295
|
raise PipelineStateError("checkpoint journal approval contract is invalid")
|
|
256
|
-
if
|
|
296
|
+
if journal["kind"] in {"roadmap", "roadmap-reslice"}:
|
|
257
297
|
if project_dir != ".project":
|
|
258
298
|
raise PipelineStateError("roadmap approval is legal only on the active track")
|
|
259
299
|
if journal["roadmap_path"] != ".project/ROADMAP.md":
|
|
260
300
|
raise PipelineStateError("checkpoint journal has an invalid ROADMAP path")
|
|
261
|
-
expected_roadmap =
|
|
301
|
+
expected_roadmap = _reslice_roadmap(
|
|
302
|
+
str(journal["baseline_before"]), str(journal["roadmap_before"]), before, str(selected),
|
|
303
|
+
) if journal["kind"] == "roadmap-reslice" else _activate_roadmap_milestone(
|
|
262
304
|
str(journal["roadmap_before"]),
|
|
263
305
|
str(selected),
|
|
264
306
|
)
|
|
@@ -307,6 +349,12 @@ def _resume_checkpoint_locked(
|
|
|
307
349
|
transaction = _approval_journal(repo, journal)
|
|
308
350
|
state_path = _checkpoint_file(repo, str(transaction["state_path"]), "STATE.md")
|
|
309
351
|
mutable = {state_path.relative_to(project).as_posix()}
|
|
352
|
+
baseline = project / "ROADMAP.before-reslice.md" if transaction["kind"] == "roadmap-reslice" else None
|
|
353
|
+
if baseline is not None:
|
|
354
|
+
mutable.add(baseline.name)
|
|
355
|
+
if baseline.exists() or baseline.is_symlink():
|
|
356
|
+
if pipeline_state._read_real_file(baseline, "re-slice baseline") != transaction["baseline_before"]:
|
|
357
|
+
raise PipelineStateError("re-slice baseline drifted during checkpoint recovery")
|
|
310
358
|
roadmap_path: Optional[Path] = None
|
|
311
359
|
if transaction["roadmap_path"] is not None:
|
|
312
360
|
roadmap_path = _checkpoint_file(
|
|
@@ -354,6 +402,8 @@ def _resume_checkpoint_locked(
|
|
|
354
402
|
raise PipelineStateError("completed checkpoint has the wrong ROADMAP.md")
|
|
355
403
|
|
|
356
404
|
try:
|
|
405
|
+
if baseline is not None and baseline.exists():
|
|
406
|
+
baseline.unlink()
|
|
357
407
|
committed = isolation_checkpoint(
|
|
358
408
|
repo,
|
|
359
409
|
expected_head,
|
|
@@ -362,6 +412,8 @@ def _resume_checkpoint_locked(
|
|
|
362
412
|
[".project"],
|
|
363
413
|
)
|
|
364
414
|
except IsolationError as error:
|
|
415
|
+
if baseline is not None and pipeline_state._run_git(repo, "rev-parse", "HEAD").stdout.strip() == expected_head:
|
|
416
|
+
pipeline_state._atomic_write(baseline, str(transaction["baseline_before"]))
|
|
365
417
|
raise PipelineStateError(f"approval checkpoint failed: {error}") from error
|
|
366
418
|
if committed.get("commit") != pipeline_state._run_git(repo, "rev-parse", "HEAD").stdout.strip():
|
|
367
419
|
raise PipelineStateError("approval checkpoint returned the wrong commit")
|
|
@@ -381,6 +433,8 @@ def _resume_checkpoint_locked(
|
|
|
381
433
|
def _validate_plan_briefs(repo: Path, kind: str, project_dir: str) -> None:
|
|
382
434
|
if kind != "plan":
|
|
383
435
|
return
|
|
436
|
+
if project_dir == ".project":
|
|
437
|
+
pipeline_state._build_recovery().validate_plan(repo)
|
|
384
438
|
head = pipeline_state._run_git(repo, "rev-parse", "--verify", "HEAD", check=False)
|
|
385
439
|
if head.returncode != 0:
|
|
386
440
|
# Pre-Git approval defers base-dependent checks to build.
|
|
@@ -476,6 +530,8 @@ def checkpoint_approval(
|
|
|
476
530
|
state, state_before, state_path = pipeline_state.load_state(resolved, project_dir)
|
|
477
531
|
changes, event, subject, body = _approval_details(kind, state, selected_milestone)
|
|
478
532
|
_validate_plan_briefs(resolved, kind, project_dir)
|
|
533
|
+
if kind == "plan" and project_dir == ".project":
|
|
534
|
+
pipeline_state._build_recovery().restore_unchanged_reviews(resolved)
|
|
479
535
|
expected = {
|
|
480
536
|
"phase": state.phase,
|
|
481
537
|
"status": state.status,
|
|
@@ -497,13 +553,18 @@ def checkpoint_approval(
|
|
|
497
553
|
roadmap_path: Optional[Path] = None
|
|
498
554
|
roadmap_before: Optional[str] = None
|
|
499
555
|
roadmap_after: Optional[str] = None
|
|
500
|
-
|
|
556
|
+
baseline_before = None
|
|
557
|
+
if kind in {"roadmap", "roadmap-reslice"}:
|
|
501
558
|
if project_dir != ".project":
|
|
502
559
|
raise PipelineStateError("roadmap approval is legal only on the active track")
|
|
503
560
|
assert selected_milestone is not None
|
|
504
561
|
roadmap_path = project / "ROADMAP.md"
|
|
505
562
|
roadmap_before = pipeline_state._read_real_file(roadmap_path, "ROADMAP.md")
|
|
506
|
-
|
|
563
|
+
if kind == "roadmap-reslice":
|
|
564
|
+
baseline_before = pipeline_state._read_real_file(project / "ROADMAP.before-reslice.md", "re-slice baseline")
|
|
565
|
+
roadmap_after = _reslice_roadmap(
|
|
566
|
+
baseline_before, roadmap_before, state, selected_milestone,
|
|
567
|
+
) if kind == "roadmap-reslice" else _activate_roadmap_milestone(
|
|
507
568
|
roadmap_before,
|
|
508
569
|
selected_milestone,
|
|
509
570
|
)
|
|
@@ -512,6 +573,8 @@ def checkpoint_approval(
|
|
|
512
573
|
mutable = {state_relative}
|
|
513
574
|
if roadmap_path is not None:
|
|
514
575
|
mutable.add(roadmap_path.relative_to(project).as_posix())
|
|
576
|
+
if baseline_before is not None:
|
|
577
|
+
mutable.add("ROADMAP.before-reslice.md")
|
|
515
578
|
journal: dict[str, object] = {
|
|
516
579
|
"schema": CHECKPOINT_SCHEMA,
|
|
517
580
|
"repo": str(resolved),
|
|
@@ -528,6 +591,8 @@ def checkpoint_approval(
|
|
|
528
591
|
"roadmap_before": roadmap_before,
|
|
529
592
|
"roadmap_after": roadmap_after,
|
|
530
593
|
}
|
|
594
|
+
if baseline_before is not None:
|
|
595
|
+
journal["baseline_before"] = baseline_before
|
|
531
596
|
pipeline_state._write_json(journal_path, journal)
|
|
532
597
|
return _resume_checkpoint_locked(resolved, project, journal_path, journal)
|
|
533
598
|
|
|
@@ -576,6 +641,10 @@ def defer_approval(
|
|
|
576
641
|
with pipeline_state._state_lock(project):
|
|
577
642
|
state, state_before, state_path = pipeline_state.load_state(resolved, project_dir)
|
|
578
643
|
changes, event, _, _ = _approval_details(kind, state, selected_milestone)
|
|
644
|
+
if project_dir == ".project":
|
|
645
|
+
recovery = pipeline_state._build_recovery().context(resolved, state_before)
|
|
646
|
+
if recovery and recovery["active"]:
|
|
647
|
+
raise PipelineStateError("build recovery requires a plan checkpoint")
|
|
579
648
|
_validate_plan_briefs(resolved, kind, project_dir)
|
|
580
649
|
if patch:
|
|
581
650
|
event = "patch plan approved"
|
|
@@ -595,17 +664,25 @@ def defer_approval(
|
|
|
595
664
|
project_dir,
|
|
596
665
|
approval_kind=kind,
|
|
597
666
|
)
|
|
598
|
-
|
|
667
|
+
baseline = None
|
|
668
|
+
if kind in {"roadmap", "roadmap-reslice"}:
|
|
599
669
|
if project_dir != ".project":
|
|
600
670
|
raise PipelineStateError("roadmap approval is legal only on the active track")
|
|
601
671
|
assert selected_milestone is not None
|
|
602
672
|
roadmap_path = project / "ROADMAP.md"
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
673
|
+
roadmap_before = pipeline_state._read_real_file(roadmap_path, "ROADMAP.md")
|
|
674
|
+
if kind == "roadmap-reslice":
|
|
675
|
+
baseline = project / "ROADMAP.before-reslice.md"
|
|
676
|
+
roadmap_after = _reslice_roadmap(
|
|
677
|
+
pipeline_state._read_real_file(baseline, "re-slice baseline"),
|
|
678
|
+
roadmap_before, state, selected_milestone,
|
|
679
|
+
)
|
|
680
|
+
else:
|
|
681
|
+
roadmap_after = _activate_roadmap_milestone(roadmap_before, selected_milestone)
|
|
607
682
|
pipeline_state._atomic_write(roadmap_path, roadmap_after)
|
|
608
683
|
pipeline_state._atomic_write(state_path, state_after)
|
|
684
|
+
if baseline is not None:
|
|
685
|
+
baseline.unlink()
|
|
609
686
|
return {
|
|
610
687
|
"schema": CHECKPOINT_SCHEMA,
|
|
611
688
|
"status": "approved",
|
|
@@ -1002,38 +1079,3 @@ def _classify_plan_drift(
|
|
|
1002
1079
|
"contract_paths": sorted(contract_paths),
|
|
1003
1080
|
"reason": "; ".join(reasons) if reasons else "approval contracts and declared paths are unchanged",
|
|
1004
1081
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
# pipeline_state imports this module, so the parent is resolved after the
|
|
1008
|
-
# definitions above. Parent functions are looked up on the module at call time
|
|
1009
|
-
# so patches applied to pipeline_state stay visible here.
|
|
1010
|
-
if __package__: # imported as scripts.state_checkpoint
|
|
1011
|
-
from . import pipeline_state
|
|
1012
|
-
from .pipeline_state import (
|
|
1013
|
-
CHECKPOINT_JOURNAL_NAME,
|
|
1014
|
-
CHECKPOINT_KINDS,
|
|
1015
|
-
CHECKPOINT_SCHEMA,
|
|
1016
|
-
PLAN_APPROVAL_SUBJECT,
|
|
1017
|
-
PROMOTION_TRACKS,
|
|
1018
|
-
SLUG_RE,
|
|
1019
|
-
TASK_FILE_RE,
|
|
1020
|
-
TASK_ID_RE,
|
|
1021
|
-
ApprovedTaskContract,
|
|
1022
|
-
PipelineState,
|
|
1023
|
-
PipelineStateError,
|
|
1024
|
-
)
|
|
1025
|
-
else: # standalone script or sibling import
|
|
1026
|
-
import pipeline_state
|
|
1027
|
-
from pipeline_state import (
|
|
1028
|
-
CHECKPOINT_JOURNAL_NAME,
|
|
1029
|
-
CHECKPOINT_KINDS,
|
|
1030
|
-
CHECKPOINT_SCHEMA,
|
|
1031
|
-
PLAN_APPROVAL_SUBJECT,
|
|
1032
|
-
PROMOTION_TRACKS,
|
|
1033
|
-
SLUG_RE,
|
|
1034
|
-
TASK_FILE_RE,
|
|
1035
|
-
TASK_ID_RE,
|
|
1036
|
-
ApprovedTaskContract,
|
|
1037
|
-
PipelineState,
|
|
1038
|
-
PipelineStateError,
|
|
1039
|
-
)
|
package/scripts/state_promote.py
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
# Runtime helpers must not modify their immutable installation.
|
|
10
|
+
sys.dont_write_bytecode = True
|
|
11
|
+
|
|
7
12
|
import json
|
|
8
13
|
import re
|
|
9
14
|
import shutil
|
|
@@ -11,6 +16,31 @@ from pathlib import Path, PurePosixPath
|
|
|
11
16
|
from typing import Mapping, Optional
|
|
12
17
|
|
|
13
18
|
|
|
19
|
+
if __package__: # imported as scripts.state_promote
|
|
20
|
+
from . import pipeline_state, state_checkpoint
|
|
21
|
+
from .pipeline_state import (
|
|
22
|
+
NULL,
|
|
23
|
+
PROMOTION_RESIDUAL,
|
|
24
|
+
PROMOTION_SCHEMA,
|
|
25
|
+
PROMOTION_TRACKS,
|
|
26
|
+
SLUG_RE,
|
|
27
|
+
PipelineState,
|
|
28
|
+
PipelineStateError,
|
|
29
|
+
)
|
|
30
|
+
else: # standalone script or sibling import
|
|
31
|
+
import pipeline_state
|
|
32
|
+
import state_checkpoint
|
|
33
|
+
from pipeline_state import (
|
|
34
|
+
NULL,
|
|
35
|
+
PROMOTION_RESIDUAL,
|
|
36
|
+
PROMOTION_SCHEMA,
|
|
37
|
+
PROMOTION_TRACKS,
|
|
38
|
+
SLUG_RE,
|
|
39
|
+
PipelineState,
|
|
40
|
+
PipelineStateError,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
14
44
|
def _render_roadmap(
|
|
15
45
|
text: str,
|
|
16
46
|
previous_milestone: str,
|
|
@@ -243,9 +273,9 @@ def _completed_promotion(
|
|
|
243
273
|
if _head_with_exact_message(repo, subject, fields) != head:
|
|
244
274
|
raise PipelineStateError("completed promotion has the wrong commit message")
|
|
245
275
|
|
|
246
|
-
active_text =
|
|
247
|
-
next_text =
|
|
248
|
-
roadmap_text =
|
|
276
|
+
active_text = state_checkpoint._git_text_at(repo, base, ".project/STATE.md")
|
|
277
|
+
next_text = state_checkpoint._git_text_at(repo, base, ".project/next/STATE.md")
|
|
278
|
+
roadmap_text = state_checkpoint._git_text_at(repo, base, ".project/ROADMAP.md")
|
|
249
279
|
if active_text is None or next_text is None or roadmap_text is None:
|
|
250
280
|
raise PipelineStateError("promotion parent is missing required metadata")
|
|
251
281
|
active_state = pipeline_state._state_from_text(active_text, "promotion parent STATE.md")
|
|
@@ -265,7 +295,7 @@ def _completed_promotion(
|
|
|
265
295
|
)
|
|
266
296
|
|
|
267
297
|
_promotion_tree_mapping(repo, base, head)
|
|
268
|
-
drift =
|
|
298
|
+
drift = state_checkpoint._classify_plan_drift(repo, next_state, base, landing)
|
|
269
299
|
expected_roadmap = _render_roadmap(
|
|
270
300
|
roadmap_text,
|
|
271
301
|
active_state.milestone,
|
|
@@ -273,11 +303,11 @@ def _completed_promotion(
|
|
|
273
303
|
branch,
|
|
274
304
|
landing,
|
|
275
305
|
)
|
|
276
|
-
current_roadmap =
|
|
306
|
+
current_roadmap = state_checkpoint._git_text_at(repo, head, ".project/ROADMAP.md")
|
|
277
307
|
if current_roadmap != expected_roadmap:
|
|
278
308
|
raise PipelineStateError("completed promotion has invalid ROADMAP.md")
|
|
279
309
|
|
|
280
|
-
current_state =
|
|
310
|
+
current_state = state_checkpoint._git_text_at(repo, head, ".project/STATE.md")
|
|
281
311
|
if current_state is None:
|
|
282
312
|
raise PipelineStateError("completed promotion has no STATE.md")
|
|
283
313
|
event = re.escape(_promotion_event(drift, branch, landing, base))
|
|
@@ -438,10 +468,10 @@ def _prepare_promotion(
|
|
|
438
468
|
if source.exists() or source.is_symlink():
|
|
439
469
|
if destination.exists() or destination.is_symlink():
|
|
440
470
|
raise PipelineStateError(f"promotion destination already exists: {destination}")
|
|
441
|
-
tracks[name] =
|
|
471
|
+
tracks[name] = state_checkpoint._tree_digest(source)
|
|
442
472
|
if not tracks:
|
|
443
473
|
raise PipelineStateError("lookahead track has no promotable artifacts")
|
|
444
|
-
drift =
|
|
474
|
+
drift = state_checkpoint._classify_plan_drift(repo, next_state, base, landing)
|
|
445
475
|
roadmap_path = project / "ROADMAP.md"
|
|
446
476
|
roadmap_text = pipeline_state._read_real_file(roadmap_path, "ROADMAP.md")
|
|
447
477
|
target_state = _promotion_state(
|
|
@@ -464,10 +494,10 @@ def _prepare_promotion(
|
|
|
464
494
|
"landing": landing,
|
|
465
495
|
"base": base,
|
|
466
496
|
"tracks": tracks,
|
|
467
|
-
"residual_sha256":
|
|
468
|
-
"next_state_sha256":
|
|
469
|
-
"active_state_sha256":
|
|
470
|
-
"roadmap_sha256":
|
|
497
|
+
"residual_sha256": state_checkpoint._tree_digest(next_root, tuple(tracks)),
|
|
498
|
+
"next_state_sha256": state_checkpoint._sha256(next_text),
|
|
499
|
+
"active_state_sha256": state_checkpoint._sha256(active_text),
|
|
500
|
+
"roadmap_sha256": state_checkpoint._sha256(roadmap_text),
|
|
471
501
|
"target_state": target_state,
|
|
472
502
|
"target_roadmap": target_roadmap,
|
|
473
503
|
"drift": drift,
|
|
@@ -521,7 +551,7 @@ def _resume_track_moves(project: Path, journal: dict[str, object], path: Path) -
|
|
|
521
551
|
if not source_exists and not destination_exists:
|
|
522
552
|
raise PipelineStateError(f"promotion lost both source and destination: {name}")
|
|
523
553
|
candidate = source if source_exists else destination
|
|
524
|
-
if
|
|
554
|
+
if state_checkpoint._tree_digest(candidate) != expected_digest:
|
|
525
555
|
raise PipelineStateError(f"promotion track drifted: {name}")
|
|
526
556
|
if source_exists:
|
|
527
557
|
source.rename(destination)
|
|
@@ -542,7 +572,7 @@ def _resume_metadata(project: Path, journal: dict[str, object], path: Path) -> N
|
|
|
542
572
|
raise PipelineStateError("promotion journal has invalid metadata")
|
|
543
573
|
if current == desired:
|
|
544
574
|
continue
|
|
545
|
-
if
|
|
575
|
+
if state_checkpoint._sha256(current) != expected_original:
|
|
546
576
|
raise PipelineStateError(f"promotion metadata drifted: {target}")
|
|
547
577
|
pipeline_state._atomic_write(target, desired)
|
|
548
578
|
journal["stage"] = f"updated:{target.name}"
|
|
@@ -560,9 +590,9 @@ def _resume_next_removal(
|
|
|
560
590
|
if residual.exists() or residual.is_symlink():
|
|
561
591
|
raise PipelineStateError("promotion has both next track and residual staging")
|
|
562
592
|
next_text = pipeline_state._read_real_file(next_root / "STATE.md", "next/STATE.md")
|
|
563
|
-
if
|
|
593
|
+
if state_checkpoint._sha256(next_text) != journal.get("next_state_sha256"):
|
|
564
594
|
raise PipelineStateError("lookahead STATE drifted during promotion")
|
|
565
|
-
if
|
|
595
|
+
if state_checkpoint._tree_digest(next_root) != journal.get("residual_sha256"):
|
|
566
596
|
raise PipelineStateError("lookahead residual drifted during promotion")
|
|
567
597
|
next_root.rename(residual)
|
|
568
598
|
journal["stage"] = "next-staged"
|
|
@@ -574,7 +604,7 @@ def _resume_next_removal(
|
|
|
574
604
|
raise PipelineStateError("promotion lost next track and residual staging")
|
|
575
605
|
if residual.is_symlink() or not residual.is_dir():
|
|
576
606
|
raise PipelineStateError(f"promotion residual must be a real directory: {residual}")
|
|
577
|
-
if
|
|
607
|
+
if state_checkpoint._tree_digest(residual) != journal.get("residual_sha256"):
|
|
578
608
|
raise PipelineStateError("staged lookahead residual drifted during promotion")
|
|
579
609
|
|
|
580
610
|
|
|
@@ -714,30 +744,3 @@ def promote_next(
|
|
|
714
744
|
"commit": commit,
|
|
715
745
|
"drift": journal["drift"],
|
|
716
746
|
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
# pipeline_state imports this module, so the parent is resolved after the
|
|
720
|
-
# definitions above. Parent functions are looked up on the module at call time
|
|
721
|
-
# so patches applied to pipeline_state stay visible here.
|
|
722
|
-
if __package__: # imported as scripts.state_promote
|
|
723
|
-
from . import pipeline_state
|
|
724
|
-
from .pipeline_state import (
|
|
725
|
-
NULL,
|
|
726
|
-
PROMOTION_RESIDUAL,
|
|
727
|
-
PROMOTION_SCHEMA,
|
|
728
|
-
PROMOTION_TRACKS,
|
|
729
|
-
SLUG_RE,
|
|
730
|
-
PipelineState,
|
|
731
|
-
PipelineStateError,
|
|
732
|
-
)
|
|
733
|
-
else: # standalone script or sibling import
|
|
734
|
-
import pipeline_state
|
|
735
|
-
from pipeline_state import (
|
|
736
|
-
NULL,
|
|
737
|
-
PROMOTION_RESIDUAL,
|
|
738
|
-
PROMOTION_SCHEMA,
|
|
739
|
-
PROMOTION_TRACKS,
|
|
740
|
-
SLUG_RE,
|
|
741
|
-
PipelineState,
|
|
742
|
-
PipelineStateError,
|
|
743
|
-
)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""Launch the project status runtime across an atomic refresh handoff."""
|
|
3
3
|
|
|
4
|
+
import argparse
|
|
5
|
+
import hashlib
|
|
4
6
|
import errno
|
|
5
7
|
import json
|
|
6
8
|
import os
|
|
@@ -72,8 +74,85 @@ def install_lock_active(lock: Path) -> bool:
|
|
|
72
74
|
return current_identity == identity
|
|
73
75
|
|
|
74
76
|
|
|
77
|
+
RUNTIME_SCHEMA = "gsd-path/runtime/v1"
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def runtime_home() -> Path:
|
|
81
|
+
return Path.home() / ".gsd-path" / "runtimes"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def declaration(repo: Path) -> dict:
|
|
85
|
+
path = repo / ".gsd-path" / "runtime.json"
|
|
86
|
+
if path.is_symlink() or path.parent.is_symlink():
|
|
87
|
+
raise ValueError(f"unsafe runtime declaration: {path}")
|
|
88
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
89
|
+
if (not isinstance(data, dict) or data.get("schema") != RUNTIME_SCHEMA
|
|
90
|
+
or not isinstance(data.get("version"), str)
|
|
91
|
+
or not isinstance(data.get("digest"), str)
|
|
92
|
+
or len(data["digest"]) != 64
|
|
93
|
+
or any(c not in "0123456789abcdef" for c in data["digest"])):
|
|
94
|
+
raise ValueError(f"invalid runtime declaration: {path}")
|
|
95
|
+
return data
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def manifest_digest(manifest: dict) -> str:
|
|
99
|
+
return hashlib.sha256(json.dumps(manifest, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def validate_runtime(data: dict) -> Path:
|
|
103
|
+
root = runtime_home() / data["digest"]
|
|
104
|
+
hint = (f"Runtime {data['version']} ({data['digest']}) is unavailable or invalid at {root}. "
|
|
105
|
+
"Run gsd-path --runtime-restore --project <project-root> "
|
|
106
|
+
"--source-root <matching-package-root> to restore this exact runtime.")
|
|
107
|
+
try:
|
|
108
|
+
if any(p.is_symlink() for p in (root, root.parent, root.parent.parent)):
|
|
109
|
+
raise ValueError("symlinked runtime storage")
|
|
110
|
+
manifest_path = root / "manifest.json"
|
|
111
|
+
if manifest_path.is_symlink():
|
|
112
|
+
raise ValueError("symlinked manifest")
|
|
113
|
+
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
|
114
|
+
if (not isinstance(manifest, dict) or manifest_digest(manifest) != data["digest"]
|
|
115
|
+
or manifest.get("version") != data["version"]):
|
|
116
|
+
raise ValueError("manifest identity mismatch")
|
|
117
|
+
files = manifest.get("files")
|
|
118
|
+
if not isinstance(files, dict) or "pipeline_state.py" not in files:
|
|
119
|
+
raise ValueError("invalid runtime file inventory")
|
|
120
|
+
for name, expected in files.items():
|
|
121
|
+
if Path(name).name != name or not name.endswith(".py"):
|
|
122
|
+
raise ValueError("invalid runtime file name")
|
|
123
|
+
path = root / name
|
|
124
|
+
if path.is_symlink() or hashlib.sha256(path.read_bytes()).hexdigest() != expected:
|
|
125
|
+
raise ValueError(f"runtime file changed: {name}")
|
|
126
|
+
if set(p.name for p in root.iterdir()) != set(files) | {"manifest.json"}:
|
|
127
|
+
raise ValueError("unexpected runtime files")
|
|
128
|
+
except (OSError, ValueError, TypeError) as error:
|
|
129
|
+
raise ValueError(hint + f" ({error})") from error
|
|
130
|
+
return root
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def resolve_runtime(repo: Path) -> Path:
|
|
134
|
+
return validate_runtime(declaration(repo))
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def run_guard(repo: Path, name: str) -> None:
|
|
138
|
+
# Keep the guard's project identity while executing verified external bytes.
|
|
139
|
+
root = resolve_runtime(repo)
|
|
140
|
+
sys.dont_write_bytecode = True
|
|
141
|
+
sys.path.insert(0, str(root))
|
|
142
|
+
code = compile((root / name).read_bytes(), str(root / name), "exec")
|
|
143
|
+
exec(code, {"__name__": "__main__", "__file__": str(repo / ".gsd-path" / name)})
|
|
144
|
+
|
|
145
|
+
|
|
75
146
|
def launch(repo: Path) -> int:
|
|
76
147
|
parent = repo / ".gsd-path"
|
|
148
|
+
if os.path.lexists(parent / "runtime.json"):
|
|
149
|
+
try:
|
|
150
|
+
runtime = resolve_runtime(repo) / "pipeline_state.py"
|
|
151
|
+
except (OSError, ValueError) as error:
|
|
152
|
+
print(str(error), file=sys.stderr)
|
|
153
|
+
return 2
|
|
154
|
+
return subprocess.call([sys.executable, "-B", str(runtime), "status", "--repo", str(repo)],
|
|
155
|
+
env={**os.environ, "GIT_OPTIONAL_LOCKS": "0"})
|
|
77
156
|
runtime = parent / "runtime" / "pipeline_state.py"
|
|
78
157
|
lock = repo / ".gsd-path-install-lock"
|
|
79
158
|
command = [sys.executable, "-B", str(runtime), "status", "--repo", str(repo)]
|
|
@@ -108,14 +187,21 @@ def launch(repo: Path) -> int:
|
|
|
108
187
|
|
|
109
188
|
|
|
110
189
|
def main(argv: Sequence[str] = sys.argv[1:]) -> int:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if not repo.is_absolute():
|
|
190
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
191
|
+
parser.add_argument("--repo", required=True, type=Path)
|
|
192
|
+
parser.add_argument("--runtime-path", action="store_true")
|
|
193
|
+
args = parser.parse_args(argv)
|
|
194
|
+
if not args.repo.is_absolute():
|
|
116
195
|
print("GSD Path status repository must be absolute", file=sys.stderr)
|
|
117
196
|
return 2
|
|
118
|
-
|
|
197
|
+
if args.runtime_path:
|
|
198
|
+
try:
|
|
199
|
+
print(resolve_runtime(args.repo.resolve()))
|
|
200
|
+
return 0
|
|
201
|
+
except (OSError, ValueError) as error:
|
|
202
|
+
print(str(error), file=sys.stderr)
|
|
203
|
+
return 2
|
|
204
|
+
return launch(args.repo.resolve())
|
|
119
205
|
|
|
120
206
|
|
|
121
207
|
if __name__ == "__main__":
|