@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
|
@@ -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
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
+
# gsd-path project runtime
|
|
2
3
|
"""Gate a GSD Path docs audit (.project/research/DOCS-AUDIT.md) against its contract.
|
|
3
4
|
|
|
4
5
|
Checks the rules the docs-audit contract states in prose: the header lines,
|
|
@@ -21,7 +22,8 @@ are rejected.
|
|
|
21
22
|
The frozen inventory travels in the dispatch brief; pass it with
|
|
22
23
|
`--inventory FILE` (one POSIX path per line, `-` for stdin). Without it the
|
|
23
24
|
gate derives the inventory from tracked Markdown files, excluding `.git`,
|
|
24
|
-
`node_modules`, `.project/archive/**`, installed `*/skills/gsd-path*`
|
|
25
|
+
`node_modules`, `.project/archive/**`, installed `*/skills/gsd-path*` and
|
|
26
|
+
`*/skills/path` bundles,
|
|
25
27
|
the audit itself, and — unless the
|
|
26
28
|
audit declares `Alignment mode: yes` — everything else under `.project/`.
|
|
27
29
|
"""
|
|
@@ -31,6 +33,7 @@ import json
|
|
|
31
33
|
import re
|
|
32
34
|
import subprocess
|
|
33
35
|
import sys
|
|
36
|
+
sys.dont_write_bytecode = True
|
|
34
37
|
from datetime import date
|
|
35
38
|
from pathlib import Path
|
|
36
39
|
from typing import Dict, List, Optional, Sequence
|
|
@@ -100,9 +103,10 @@ def _not_placeholder(value: str, label: str) -> None:
|
|
|
100
103
|
|
|
101
104
|
|
|
102
105
|
def _installed_skill(parts: Sequence[str]) -> bool:
|
|
103
|
-
"""
|
|
106
|
+
"""Exclude installed GSD Path bundles, including the path router alias."""
|
|
104
107
|
return any(
|
|
105
|
-
parts[index] == "skills" and
|
|
108
|
+
parts[index] == "skills" and
|
|
109
|
+
(parts[index + 1].startswith("gsd-path") or parts[index + 1] == "path")
|
|
106
110
|
for index in range(len(parts) - 2)
|
|
107
111
|
)
|
|
108
112
|
|
|
@@ -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
|
|
|
@@ -34,6 +34,9 @@ import re
|
|
|
34
34
|
import stat
|
|
35
35
|
import subprocess
|
|
36
36
|
import sys
|
|
37
|
+
|
|
38
|
+
# Runtime helpers must not modify their immutable installation.
|
|
39
|
+
sys.dont_write_bytecode = True
|
|
37
40
|
from datetime import date
|
|
38
41
|
from pathlib import Path, PurePosixPath, PureWindowsPath
|
|
39
42
|
from typing import Iterable, NamedTuple, Optional, Sequence
|
|
@@ -1200,7 +1203,14 @@ def filled_state_template(template: str, slug: str, phase: str) -> str:
|
|
|
1200
1203
|
text = text.replace("YYYY-MM-DD — <phase> — project initialized", stamp)
|
|
1201
1204
|
if "<slug>" in text or "YYYY-MM-DD" in text:
|
|
1202
1205
|
raise DetectError("state template placeholder remains")
|
|
1203
|
-
|
|
1206
|
+
try:
|
|
1207
|
+
from .path_config import initial_state
|
|
1208
|
+
except ImportError:
|
|
1209
|
+
from path_config import initial_state
|
|
1210
|
+
try:
|
|
1211
|
+
return initial_state(text)
|
|
1212
|
+
except (OSError, ValueError, RuntimeError) as error:
|
|
1213
|
+
raise DetectError(str(error)) from error
|
|
1204
1214
|
|
|
1205
1215
|
|
|
1206
1216
|
def write_all(descriptor: int, payload: bytes) -> None:
|
|
@@ -1439,7 +1449,7 @@ def write_state_anchored(
|
|
|
1439
1449
|
|
|
1440
1450
|
|
|
1441
1451
|
def initialize(
|
|
1442
|
-
repo: Path, template: Path, phase: Optional[str] = None
|
|
1452
|
+
repo: Path, template: Path, phase: Optional[str] = None, *, require_git: bool = False
|
|
1443
1453
|
) -> dict:
|
|
1444
1454
|
if phase is not None and phase not in {"inspect", "define"}:
|
|
1445
1455
|
raise DetectError(f"initialize phase must be inspect or define: {phase}")
|
|
@@ -1458,6 +1468,13 @@ def initialize(
|
|
|
1458
1468
|
if payload["verdict"] not in {"brownfield", "greenfield"}:
|
|
1459
1469
|
payload["wrote_state"] = False
|
|
1460
1470
|
return payload
|
|
1471
|
+
if require_git:
|
|
1472
|
+
git = subprocess.run(
|
|
1473
|
+
["git", "-C", str(root), "rev-parse", "--is-inside-work-tree"],
|
|
1474
|
+
env=git_environment(), capture_output=True, text=True, check=False,
|
|
1475
|
+
)
|
|
1476
|
+
if git.returncode != 0 or git.stdout.strip() != "true":
|
|
1477
|
+
return {**payload, "route": "setup-repository", "wrote_state": False}
|
|
1461
1478
|
expected = "inspect" if payload["verdict"] == "brownfield" else "define"
|
|
1462
1479
|
if phase is None:
|
|
1463
1480
|
phase = expected
|
|
@@ -1491,6 +1508,7 @@ def parser() -> argparse.ArgumentParser:
|
|
|
1491
1508
|
result.add_argument("--repo", type=Path, required=True)
|
|
1492
1509
|
result.add_argument("--template", type=Path)
|
|
1493
1510
|
result.add_argument("--phase", choices=("inspect", "define"))
|
|
1511
|
+
result.add_argument("--require-git", action="store_true")
|
|
1494
1512
|
return result
|
|
1495
1513
|
|
|
1496
1514
|
|
|
@@ -1503,7 +1521,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
1503
1521
|
if arguments.template is None:
|
|
1504
1522
|
raise DetectError("initialize requires --template")
|
|
1505
1523
|
payload = initialize(
|
|
1506
|
-
arguments.repo, arguments.template, arguments.phase
|
|
1524
|
+
arguments.repo, arguments.template, arguments.phase,
|
|
1525
|
+
require_git=arguments.require_git
|
|
1507
1526
|
)
|
|
1508
1527
|
return emit(payload, 2 if payload.get("error") else 0)
|
|
1509
1528
|
raise DetectError(f"unknown command: {arguments.command}")
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
# gsd-path project runtime
|
|
3
3
|
"""Validate discussion records, reviews and the manifest for archive_milestone."""
|
|
4
4
|
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
# Runtime helpers must not modify their immutable installation.
|
|
8
|
+
sys.dont_write_bytecode = True
|
|
9
|
+
|
|
5
10
|
import json
|
|
6
11
|
import re
|
|
7
12
|
import shutil
|
|
@@ -409,7 +414,7 @@ def remove_interrupted_carry_copy(active_root: Path, archive: Path) -> None:
|
|
|
409
414
|
|
|
410
415
|
|
|
411
416
|
def require_transaction_context(
|
|
412
|
-
project: Path, state: PipelineState, phases: Sequence[str]
|
|
417
|
+
project: Path, state: PipelineState, phases: Sequence[str], *, historical: bool = False
|
|
413
418
|
) -> None:
|
|
414
419
|
phase = state.phase
|
|
415
420
|
if phase not in phases:
|
|
@@ -429,7 +434,7 @@ def require_transaction_context(
|
|
|
429
434
|
)
|
|
430
435
|
if archive_milestone.is_unset(expected_branch):
|
|
431
436
|
raise ArchiveError("STATE.md does not name a bound build branch")
|
|
432
|
-
if current_branch != expected_branch:
|
|
437
|
+
if current_branch != expected_branch and not historical:
|
|
433
438
|
raise ArchiveError(
|
|
434
439
|
f"current branch {current_branch!r} does not match STATE.branch {expected_branch!r}"
|
|
435
440
|
)
|
|
@@ -446,7 +451,7 @@ def require_transaction_context(
|
|
|
446
451
|
),
|
|
447
452
|
"inspect non-project worktree status",
|
|
448
453
|
)
|
|
449
|
-
if non_project_status:
|
|
454
|
+
if non_project_status and not historical:
|
|
450
455
|
raise ArchiveError("non-.project worktree changes block the archive transaction")
|
|
451
456
|
|
|
452
457
|
|