@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 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
|
-
)
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Launch the project status runtime across an atomic refresh handoff."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import hashlib
|
|
6
|
+
import errno
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Sequence
|
|
14
|
+
|
|
15
|
+
PROJECT_STATUS_MARKER = "gsd-path project status launcher"
|
|
16
|
+
INSTALL_LOCK_OWNER = "owner.json"
|
|
17
|
+
INSTALL_LOCK_SCHEMA = "gsd-path/install-lock/v2"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def process_identity(pid: int):
|
|
21
|
+
if os.name == "nt":
|
|
22
|
+
command = [
|
|
23
|
+
"powershell.exe",
|
|
24
|
+
"-NoProfile",
|
|
25
|
+
"-NonInteractive",
|
|
26
|
+
"-Command",
|
|
27
|
+
f"(Get-Process -Id {pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks",
|
|
28
|
+
]
|
|
29
|
+
else:
|
|
30
|
+
command = ["ps", "-o", "lstart=", "-p", str(pid)]
|
|
31
|
+
try:
|
|
32
|
+
result = subprocess.run(command, capture_output=True, text=True, check=False)
|
|
33
|
+
except OSError:
|
|
34
|
+
return None
|
|
35
|
+
value = result.stdout.strip()
|
|
36
|
+
return f"{os.name}:{value}" if result.returncode == 0 and value else None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def runtime_identity(runtime: Path):
|
|
40
|
+
try:
|
|
41
|
+
status = runtime.stat()
|
|
42
|
+
except FileNotFoundError:
|
|
43
|
+
return None
|
|
44
|
+
return (status.st_dev, status.st_ino, status.st_mtime_ns, status.st_size)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def process_alive(pid: int) -> bool:
|
|
48
|
+
try:
|
|
49
|
+
os.kill(pid, 0)
|
|
50
|
+
except OSError as error:
|
|
51
|
+
return error.errno == errno.EPERM
|
|
52
|
+
return True
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def install_lock_active(lock: Path) -> bool:
|
|
56
|
+
if lock.is_symlink() or not lock.is_dir():
|
|
57
|
+
return False
|
|
58
|
+
try:
|
|
59
|
+
owner = json.loads((lock / INSTALL_LOCK_OWNER).read_text(encoding="utf-8"))
|
|
60
|
+
except (OSError, UnicodeError, json.JSONDecodeError):
|
|
61
|
+
return False
|
|
62
|
+
pid = owner.get("pid")
|
|
63
|
+
valid_pid = isinstance(pid, int) and not isinstance(pid, bool) and pid > 0
|
|
64
|
+
identity = owner.get("identity")
|
|
65
|
+
if (
|
|
66
|
+
owner.get("schema") != INSTALL_LOCK_SCHEMA
|
|
67
|
+
or not valid_pid
|
|
68
|
+
or not isinstance(identity, str)
|
|
69
|
+
):
|
|
70
|
+
return False
|
|
71
|
+
current_identity = process_identity(pid)
|
|
72
|
+
if current_identity is None:
|
|
73
|
+
return process_alive(pid)
|
|
74
|
+
return current_identity == identity
|
|
75
|
+
|
|
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
|
+
|
|
146
|
+
def launch(repo: Path) -> int:
|
|
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"})
|
|
156
|
+
runtime = parent / "runtime" / "pipeline_state.py"
|
|
157
|
+
lock = repo / ".gsd-path-install-lock"
|
|
158
|
+
command = [sys.executable, "-B", str(runtime), "status", "--repo", str(repo)]
|
|
159
|
+
environment = os.environ.copy()
|
|
160
|
+
environment["GIT_OPTIONAL_LOCKS"] = "0"
|
|
161
|
+
while True:
|
|
162
|
+
if install_lock_active(lock):
|
|
163
|
+
time.sleep(0.05)
|
|
164
|
+
continue
|
|
165
|
+
before = runtime_identity(runtime)
|
|
166
|
+
if before is None:
|
|
167
|
+
if install_lock_active(lock):
|
|
168
|
+
continue
|
|
169
|
+
if runtime_identity(runtime) is not None:
|
|
170
|
+
continue
|
|
171
|
+
if install_lock_active(lock):
|
|
172
|
+
continue
|
|
173
|
+
print(f"GSD Path status runtime is unavailable: {runtime}", file=sys.stderr)
|
|
174
|
+
return 2
|
|
175
|
+
try:
|
|
176
|
+
result = subprocess.run(
|
|
177
|
+
command, capture_output=True, check=False, env=environment
|
|
178
|
+
)
|
|
179
|
+
except OSError as error:
|
|
180
|
+
print(f"GSD Path status runtime failed: {error}", file=sys.stderr)
|
|
181
|
+
return 2
|
|
182
|
+
if install_lock_active(lock) or runtime_identity(runtime) != before:
|
|
183
|
+
continue
|
|
184
|
+
sys.stdout.write(result.stdout.decode())
|
|
185
|
+
sys.stderr.write(result.stderr.decode())
|
|
186
|
+
return result.returncode
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def main(argv: Sequence[str] = sys.argv[1:]) -> int:
|
|
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():
|
|
195
|
+
print("GSD Path status repository must be absolute", file=sys.stderr)
|
|
196
|
+
return 2
|
|
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())
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
if __name__ == "__main__":
|
|
208
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Render a read-only coder intent view from the approved task and intent."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import hashlib
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from scripts import check_handoffs as contracts
|
|
12
|
+
except ImportError:
|
|
13
|
+
import check_handoffs as contracts
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def render(repo: Path, task: Path) -> str:
|
|
17
|
+
intent_path = repo.resolve() / '.project/intent/INTENT.md'
|
|
18
|
+
task = task.resolve()
|
|
19
|
+
intent = intent_path.read_bytes().decode('utf-8')
|
|
20
|
+
task_text = task.read_bytes().decode('utf-8')
|
|
21
|
+
owned = contracts._owned_criteria(task_text, task.stem)
|
|
22
|
+
criteria = contracts._success_criteria(intent)
|
|
23
|
+
unknown = set(owned) - set(criteria)
|
|
24
|
+
if unknown:
|
|
25
|
+
raise contracts.HandoffError('unknown owned criteria: ' + ', '.join(sorted(unknown)))
|
|
26
|
+
projected, mode = intent, 'full-intent'
|
|
27
|
+
sections = list(re.finditer(r'(?ms)^## Success criteria[ \t]*\n(?P<body>.*?)(?=^## |\Z)', intent))
|
|
28
|
+
if len(sections) == 1:
|
|
29
|
+
section = sections[0]
|
|
30
|
+
body = section['body']
|
|
31
|
+
items = list(re.finditer(r'(?m)^(\d+)\. .+\n?', body))
|
|
32
|
+
# Project only simple numbered prose. Preserve unfamiliar Markdown in full.
|
|
33
|
+
simple = (items and not body[:items[0].start()].strip()
|
|
34
|
+
and '```' not in body and '~~~' not in body and '<!--' not in body
|
|
35
|
+
and [f'SC{item[1]}' for item in items] == list(criteria))
|
|
36
|
+
chunks = [body[item.start():items[n + 1].start() if n + 1 < len(items) else len(body)]
|
|
37
|
+
for n, item in enumerate(items)]
|
|
38
|
+
simple = simple and all(not line.strip() or line[:1].isspace()
|
|
39
|
+
for chunk in chunks for line in chunk.splitlines()[1:])
|
|
40
|
+
if simple:
|
|
41
|
+
before, after = intent[:section.start('body')], intent[section.end('body'):]
|
|
42
|
+
# Corrections and task interfaces can explicitly refer to another SC.
|
|
43
|
+
required = set(owned) | set(re.findall(r'\bSC\d+\b', before + after + task_text))
|
|
44
|
+
selected = ''.join(chunk for item, chunk in zip(items, chunks) if f'SC{item[1]}' in required)
|
|
45
|
+
projected = before + '\n' + (selected or '- No criteria assigned.\n\n') + after
|
|
46
|
+
mode = 'owned-criteria'
|
|
47
|
+
return ('## Generated intent context\n\n'
|
|
48
|
+
f'Source: {intent_path}\n'
|
|
49
|
+
f'Intent SHA256: {hashlib.sha256(intent.encode()).hexdigest()}\n'
|
|
50
|
+
f'Task: {task}\n'
|
|
51
|
+
f'Task SHA256: {hashlib.sha256(task_text.encode()).hexdigest()}\n'
|
|
52
|
+
f'Mode: {mode}\n\n'
|
|
53
|
+
'Use this view for intent. All non-criterion text is verbatim; selected criteria retain '
|
|
54
|
+
'their original numbers. The source remains authoritative. Read it if a dependency '
|
|
55
|
+
'or ambiguity requires omitted criteria.\n\n' + projected)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def main() -> int:
|
|
59
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
60
|
+
parser.add_argument('--repo', type=Path, required=True)
|
|
61
|
+
parser.add_argument('--task', type=Path, required=True)
|
|
62
|
+
args = parser.parse_args()
|
|
63
|
+
try:
|
|
64
|
+
output = render(args.repo, args.task)
|
|
65
|
+
except (OSError, contracts.HandoffError) as error:
|
|
66
|
+
print(str(error), file=sys.stderr)
|
|
67
|
+
return 1
|
|
68
|
+
print(output, end='')
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
if __name__ == '__main__':
|
|
73
|
+
raise SystemExit(main())
|
|
@@ -69,6 +69,24 @@ def output_usage(path: Path) -> int:
|
|
|
69
69
|
return sum(usages)
|
|
70
70
|
|
|
71
71
|
|
|
72
|
+
def cumulative_counter(path: Path):
|
|
73
|
+
"""One completed CLI invocation, with the thread identity needed for resume."""
|
|
74
|
+
identities, counters = set(), []
|
|
75
|
+
for line in path.read_text(encoding="utf-8").splitlines():
|
|
76
|
+
event = json.loads(line)
|
|
77
|
+
if "raw" in event:
|
|
78
|
+
event = json.loads(event["raw"])
|
|
79
|
+
if event.get("type") == "thread.started":
|
|
80
|
+
identities.add(event["thread_id"])
|
|
81
|
+
if event.get("type") == "turn.completed":
|
|
82
|
+
counters.append(event["usage"]["output_tokens"])
|
|
83
|
+
if len(identities) != 1 or len(counters) != 1:
|
|
84
|
+
raise ValueError("cumulative usage requires one identified, completed CLI invocation")
|
|
85
|
+
if type(counters[0]) is not int or counters[0] < 0:
|
|
86
|
+
raise ValueError("invalid cumulative output counter")
|
|
87
|
+
return identities.pop(), counters[0]
|
|
88
|
+
|
|
89
|
+
|
|
72
90
|
def operate(args):
|
|
73
91
|
ledger = args.ledger.absolute()
|
|
74
92
|
if ledger.is_symlink():
|
|
@@ -92,9 +110,28 @@ def operate(args):
|
|
|
92
110
|
if args.action == "record":
|
|
93
111
|
source = str(args.events.resolve())
|
|
94
112
|
observation = {"task": args.task, "output_tokens": output_usage(args.events)}
|
|
113
|
+
prior_events = getattr(args, "previous_events", None)
|
|
114
|
+
if prior_events is not None:
|
|
115
|
+
prior_source = str(prior_events.resolve())
|
|
116
|
+
prior = data["observations"].get(prior_source)
|
|
117
|
+
identity, total = cumulative_counter(args.events)
|
|
118
|
+
prior_identity, prior_total = cumulative_counter(prior_events)
|
|
119
|
+
if source == prior_source or identity != prior_identity or total < prior_total:
|
|
120
|
+
raise ValueError("cumulative resume has a different thread or a reset counter")
|
|
121
|
+
if prior is None or prior.get("reported_output_tokens", prior["output_tokens"]) != prior_total:
|
|
122
|
+
raise ValueError("previous cumulative invocation must be recorded unchanged first")
|
|
123
|
+
if any(key != source and row.get("previous_events") == prior_source
|
|
124
|
+
for key, row in data["observations"].items()):
|
|
125
|
+
raise ValueError("previous cumulative invocation already has a recorded successor")
|
|
126
|
+
observation.update(output_tokens=total - prior_total, reported_output_tokens=total,
|
|
127
|
+
previous_events=prior_source, session_id=identity)
|
|
95
128
|
previous = data["observations"].get(source)
|
|
129
|
+
if previous and previous != observation and any(
|
|
130
|
+
row.get("previous_events") == source for row in data["observations"].values()):
|
|
131
|
+
raise ValueError("a cumulative predecessor cannot change after its successor is recorded")
|
|
96
132
|
if previous and (previous["task"] != args.task or
|
|
97
|
-
previous["output_tokens"] > observation["output_tokens"]
|
|
133
|
+
previous["output_tokens"] > observation["output_tokens"] or
|
|
134
|
+
previous.get("previous_events") != observation.get("previous_events")):
|
|
98
135
|
raise ValueError("observation cannot change task or reduce recorded usage")
|
|
99
136
|
data["observations"][source] = observation
|
|
100
137
|
atomic_write(ledger, json.dumps(data, indent=2) + "\n")
|
|
@@ -131,6 +168,8 @@ def main(argv=None):
|
|
|
131
168
|
command.add_argument("--task", required=True)
|
|
132
169
|
if name == "record":
|
|
133
170
|
command.add_argument("--events", type=Path, required=True)
|
|
171
|
+
command.add_argument("--previous-events", type=Path,
|
|
172
|
+
help="previous recorded invocation when this CLI reports cumulative resume counters")
|
|
134
173
|
if name == "admit":
|
|
135
174
|
command.add_argument("--require-hard-cap", action="store_true")
|
|
136
175
|
try:
|