@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
|
@@ -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
|
|
|
@@ -31,19 +31,21 @@ from typing import BinaryIO, Callable, Dict, List, Optional
|
|
|
31
31
|
|
|
32
32
|
try:
|
|
33
33
|
from scripts import (_common, archive_milestone, build_state, discussion_validate, isolation,
|
|
34
|
-
pipeline_state, review_findings, review_panel, workflow_run)
|
|
34
|
+
pipeline_state, review_findings, review_panel, workflow_run, task_context, model_policy)
|
|
35
35
|
from scripts import check_handoffs as contracts
|
|
36
36
|
except ImportError: # bundled copy inside a skill's scripts directory
|
|
37
37
|
import _common
|
|
38
38
|
import archive_milestone
|
|
39
39
|
import build_state
|
|
40
40
|
import check_handoffs as contracts
|
|
41
|
+
import task_context
|
|
41
42
|
import discussion_validate
|
|
42
43
|
import isolation
|
|
43
44
|
import pipeline_state
|
|
44
45
|
import review_findings
|
|
45
46
|
import review_panel
|
|
46
47
|
import workflow_run
|
|
48
|
+
import model_policy
|
|
47
49
|
|
|
48
50
|
if os.name == "nt":
|
|
49
51
|
import msvcrt
|
|
@@ -70,7 +72,7 @@ class DriverStop(RuntimeError):
|
|
|
70
72
|
|
|
71
73
|
STOP_ERRORS = (DriverStop, isolation.IsolationError, build_state.BuildStateError,
|
|
72
74
|
contracts.HandoffError, review_findings.ReviewFindingsError,
|
|
73
|
-
pipeline_state.PipelineStateError, archive_milestone.ArchiveError)
|
|
75
|
+
pipeline_state.PipelineStateError, archive_milestone.ArchiveError, model_policy.PolicyError)
|
|
74
76
|
RECOVERY_BLOCKED = "recovery blocked"
|
|
75
77
|
# The stops the build contract answers with build/blocked when the round raises them (steps 1 and 2);
|
|
76
78
|
# the same stops from review, panel, or skeptics stay with the parent.
|
|
@@ -94,7 +96,7 @@ def milestone_slug(primary: Path) -> str:
|
|
|
94
96
|
|
|
95
97
|
|
|
96
98
|
def records_root(primary: Path) -> Path:
|
|
97
|
-
return
|
|
99
|
+
return pipeline_state._build_recovery().records_root(primary)
|
|
98
100
|
|
|
99
101
|
|
|
100
102
|
def budget_ledger(primary: Path) -> Path:
|
|
@@ -246,13 +248,85 @@ def brief_text(state: Dict[str, object], role_brief: Path, task_template: Path)
|
|
|
246
248
|
if state.get("answered"):
|
|
247
249
|
lines.append(f"The task Log now records an `{ANSWER_MARK}` to your earlier question; "
|
|
248
250
|
"continue from it.")
|
|
251
|
+
lines.append(task_context.render(worktree, worktree / str(state['task_file'])))
|
|
249
252
|
return "\n".join(lines) + "\n"
|
|
250
253
|
|
|
251
254
|
|
|
255
|
+
def selected_command(state: Dict[str, object], options: argparse.Namespace, command: str):
|
|
256
|
+
"""Shared selection for every CLI child; legacy unconfigured commands remain unchanged."""
|
|
257
|
+
primary = Path(getattr(options, 'repo', state['worktree']))
|
|
258
|
+
project_dir = getattr(options, 'project_dir', '.project')
|
|
259
|
+
overrides = (model_policy.task_settings(Path(str(state['worktree'])) / str(state['task_file']))
|
|
260
|
+
if state.get('task_file') else {})
|
|
261
|
+
for field in model_policy.FIELDS:
|
|
262
|
+
value = getattr(options, field, None)
|
|
263
|
+
if value is not None:
|
|
264
|
+
if field in overrides and overrides[field] != value:
|
|
265
|
+
raise model_policy.PolicyError('dispatch choice conflicts with the task contract')
|
|
266
|
+
overrides[field] = value
|
|
267
|
+
overrides_path = getattr(options, 'model_overrides', None)
|
|
268
|
+
if overrides_path:
|
|
269
|
+
assignments = model_policy.read_json(overrides_path)
|
|
270
|
+
for assignment, values in assignments.items():
|
|
271
|
+
model_policy.settings(values, f'assignment {assignment}')
|
|
272
|
+
for field, value in assignments.get(str(state['task_id']), {}).items():
|
|
273
|
+
if field in overrides and overrides[field] != value:
|
|
274
|
+
raise model_policy.PolicyError('assignment overrides conflict with explicit task/dispatch settings')
|
|
275
|
+
overrides[field] = value
|
|
276
|
+
caps_path = getattr(options, 'model_capabilities', None)
|
|
277
|
+
previous = state.get('model_selection')
|
|
278
|
+
if previous is None and state.get('command'):
|
|
279
|
+
if overrides:
|
|
280
|
+
raise model_policy.PolicyError('legacy assignment requires a new explicit assignment for model controls')
|
|
281
|
+
return list(state['command']), None
|
|
282
|
+
if caps_path is None:
|
|
283
|
+
if overrides or previous or model_policy.policy_path(primary, project_dir).exists():
|
|
284
|
+
raise model_policy.PolicyError('model policy requires --model-capabilities for this dispatch')
|
|
285
|
+
return shlex.split(command), None
|
|
286
|
+
caps = model_policy.read_json(caps_path)
|
|
287
|
+
action = getattr(options, 'action', 'round')
|
|
288
|
+
role = {'round': 'coder', 'review': 'reviewer', 'panel': 'review_panel',
|
|
289
|
+
'skeptics': 'skeptic'}.get(action, action)
|
|
290
|
+
if state.get('family'):
|
|
291
|
+
role = 'review_panel'
|
|
292
|
+
selection = model_policy.resolve(primary, project_dir, role, caps, overrides, previous)
|
|
293
|
+
if state.get('family'):
|
|
294
|
+
if selection['selected']['model'] == 'inherit' and selection['sources']['model'] == 'default':
|
|
295
|
+
# The panel roster owns the default model, not parent inheritance.
|
|
296
|
+
selection = model_policy.resolve(primary, project_dir, role, caps,
|
|
297
|
+
dict(overrides, model=state['slug']), previous)
|
|
298
|
+
model_policy.validate_panel(selection, state['family'], state.get('excluded_families', []))
|
|
299
|
+
state['slug'] = selection['selected']['model']
|
|
300
|
+
return model_policy.command_args(shlex.split(command), selection), selection
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def retain_selection(root: Path, state: Dict[str, object]) -> Dict[str, object]:
|
|
304
|
+
state = dict(state)
|
|
305
|
+
task_dir = root / str(state['task_id'])
|
|
306
|
+
if not state.get('model_selection'):
|
|
307
|
+
attempts = sorted(task_dir.glob('attempt-*/state.json'), key=lambda path: attempt_number(path.parent))
|
|
308
|
+
if attempts:
|
|
309
|
+
prior = read_attempt(attempts[-1])
|
|
310
|
+
if child_running(prior):
|
|
311
|
+
raise DriverStop('cannot redispatch while the previous child is active')
|
|
312
|
+
if prior.get('model_selection'):
|
|
313
|
+
state['model_selection'] = prior['model_selection']
|
|
314
|
+
else:
|
|
315
|
+
state['command'] = prior['command']
|
|
316
|
+
reassignment = task_dir / 'model-reassignment.json'
|
|
317
|
+
if reassignment.exists():
|
|
318
|
+
state['model_selection'] = load_state(reassignment)['selection']
|
|
319
|
+
return state
|
|
320
|
+
|
|
321
|
+
|
|
252
322
|
def spawn(root: Path, state: Dict[str, object], options: argparse.Namespace,
|
|
253
323
|
brief: Callable[[Dict[str, object]], str], command: Optional[str] = None) -> Dict[str, object]:
|
|
254
324
|
"""Create the next attempt record, write the brief the callback builds, and start the wrapper."""
|
|
325
|
+
if child_running(state):
|
|
326
|
+
raise DriverStop('cannot redispatch while the previous child is active')
|
|
327
|
+
state = retain_selection(root, state)
|
|
255
328
|
task_dir = root / str(state["task_id"])
|
|
329
|
+
argv, selection = selected_command(state, options, command or options.child_command)
|
|
256
330
|
attempt = max((attempt_number(path) for path in task_dir.glob("attempt-*")), default=0) + 1
|
|
257
331
|
attempt_dir = task_dir / f"attempt-{attempt}"
|
|
258
332
|
attempt_dir.mkdir(parents=True)
|
|
@@ -260,7 +334,9 @@ def spawn(root: Path, state: Dict[str, object], options: argparse.Namespace,
|
|
|
260
334
|
"usage_recorded")
|
|
261
335
|
state = {key: value for key, value in state.items()
|
|
262
336
|
if not key.startswith("_") and key not in stale}
|
|
263
|
-
|
|
337
|
+
if selection is not None:
|
|
338
|
+
state['model_selection'] = selection
|
|
339
|
+
state.update({"attempt": attempt, "command": argv,
|
|
264
340
|
"child_timeout": options.child_timeout, "outcome": None,
|
|
265
341
|
"dispatched_at": now(), "origin": "question" if state.get("answered") else "dispatch"})
|
|
266
342
|
state_path = attempt_dir / "state.json"
|
|
@@ -395,7 +471,8 @@ def finish_task(primary: Path, state: Dict[str, object]) -> Dict[str, object]:
|
|
|
395
471
|
|
|
396
472
|
def summary(state: Dict[str, object]) -> Dict[str, object]:
|
|
397
473
|
return {key: state.get(key) for key in
|
|
398
|
-
("task_id", "attempt", "mode", "worktree", "wave", "cycle", "verify_heavy", "dispatched_at"
|
|
474
|
+
("task_id", "attempt", "mode", "worktree", "wave", "cycle", "verify_heavy", "dispatched_at",
|
|
475
|
+
"model_selection")}
|
|
399
476
|
|
|
400
477
|
|
|
401
478
|
def recover_report(primary: Path, project_dir: str, receipt: Dict[str, object]) -> Dict[str, object]:
|
|
@@ -556,14 +633,23 @@ class Round:
|
|
|
556
633
|
def settle(self) -> bool:
|
|
557
634
|
"""Classify every exited child. Returns True when a landing or redispatch changed the round."""
|
|
558
635
|
changed = False
|
|
636
|
+
rejected = {item['task_id'] for item in self.receipt['blocked']
|
|
637
|
+
if item.get('code') == 'model-selection'}
|
|
559
638
|
for state in latest_states(self.root):
|
|
560
639
|
if state.get("wave") != self.receipt["wave"]:
|
|
561
640
|
continue
|
|
562
641
|
outcome = state.get("outcome")
|
|
563
642
|
if outcome == "question":
|
|
564
643
|
if state.get("answered"):
|
|
644
|
+
if state['task_id'] in rejected:
|
|
645
|
+
continue
|
|
565
646
|
self.admit(str(state["task_id"]))
|
|
566
|
-
|
|
647
|
+
try:
|
|
648
|
+
self.launch(dict(state, answered=True))
|
|
649
|
+
except model_policy.PolicyError as error:
|
|
650
|
+
self.receipt['blocked'].append({'task_id': state['task_id'], 'reason': str(error),
|
|
651
|
+
'code': 'model-selection'})
|
|
652
|
+
continue
|
|
567
653
|
update_state(state, outcome="redispatched")
|
|
568
654
|
changed = True
|
|
569
655
|
else:
|
|
@@ -688,23 +774,33 @@ class Round:
|
|
|
688
774
|
heavy_busy = any(state.get("verify_heavy") for state in in_flight)
|
|
689
775
|
capacity = self.options.capacity
|
|
690
776
|
selected = []
|
|
777
|
+
rejected = {item["task_id"] for item in self.receipt["blocked"]
|
|
778
|
+
if item.get("code") == "model-selection"}
|
|
691
779
|
for task in ready["ready"]:
|
|
692
780
|
if capacity is not None and len(in_flight) + len(selected) >= capacity:
|
|
693
781
|
break
|
|
694
782
|
if task["wave"] != self.receipt["wave"]:
|
|
695
783
|
continue
|
|
696
|
-
if task["id"] in active_ids or (task["verify_heavy"] and heavy_busy):
|
|
784
|
+
if task["id"] in active_ids | rejected or (task["verify_heavy"] and heavy_busy):
|
|
697
785
|
continue
|
|
698
|
-
heavy_busy = heavy_busy or task["verify_heavy"]
|
|
699
|
-
selected.append(task)
|
|
700
|
-
if not selected:
|
|
701
|
-
return False
|
|
702
|
-
for task in selected:
|
|
703
786
|
used = attempts_used(self.root, task["id"])
|
|
704
787
|
if used >= self.options.max_attempts:
|
|
705
788
|
raise DriverStop(f"task {task['id']} reached the attempt limit ({self.options.max_attempts}) "
|
|
706
789
|
"for this milestone; a person must rule", task=task["id"], attempts=used)
|
|
707
790
|
self.admit(task["id"])
|
|
791
|
+
candidate = retain_selection(self.root, {'task_id': task['id'], 'task_file': task['task_file'],
|
|
792
|
+
'worktree': str(self.primary)})
|
|
793
|
+
try:
|
|
794
|
+
_, selection = selected_command(candidate, self.options, self.options.child_command)
|
|
795
|
+
except model_policy.PolicyError as error:
|
|
796
|
+
self.receipt['blocked'].append({'task_id': task['id'], 'reason': str(error),
|
|
797
|
+
'code': 'model-selection'})
|
|
798
|
+
continue
|
|
799
|
+
task['model_selection'] = selection
|
|
800
|
+
heavy_busy = heavy_busy or task['verify_heavy']
|
|
801
|
+
selected.append(task)
|
|
802
|
+
if not selected:
|
|
803
|
+
return False
|
|
708
804
|
head = isolation.current_sha(self.primary)
|
|
709
805
|
self.runner("lint-round", head)
|
|
710
806
|
round_size = len(selected) + len(in_flight)
|
|
@@ -719,7 +815,8 @@ class Round:
|
|
|
719
815
|
"files": task["files"], "wave": task["wave"],
|
|
720
816
|
"verify_heavy": task["verify_heavy"], "base": head, "worktree": str(worktree),
|
|
721
817
|
"task_branch": isolate["task_branch"], "mode": isolate["mode"],
|
|
722
|
-
"sidecar": sidecar, "answered": False
|
|
818
|
+
"sidecar": sidecar, "answered": False,
|
|
819
|
+
"model_selection": task['model_selection']})
|
|
723
820
|
return False
|
|
724
821
|
|
|
725
822
|
def runner(self, action: str, head: str, task_id: Optional[str] = None,
|
|
@@ -736,7 +833,8 @@ class Round:
|
|
|
736
833
|
while True:
|
|
737
834
|
self.receipt["in_flight"] = []
|
|
738
835
|
changed = self.settle() or changed
|
|
739
|
-
if self.receipt["questions"] or
|
|
836
|
+
if self.receipt["questions"] or any(
|
|
837
|
+
item.get("code") != "model-selection" for item in self.receipt["blocked"]):
|
|
740
838
|
self.receipt["status"] = "question" if self.receipt["questions"] else "blocked"
|
|
741
839
|
return self.receipt
|
|
742
840
|
# ready is expensive and its inputs only move when a landing or redispatch happened
|
|
@@ -746,7 +844,8 @@ class Round:
|
|
|
746
844
|
changed = False
|
|
747
845
|
live = self.in_flight_states()
|
|
748
846
|
if deadline is None or time.monotonic() >= deadline or not live:
|
|
749
|
-
self.receipt["status"] = "
|
|
847
|
+
self.receipt["status"] = ("blocked" if self.receipt["blocked"]
|
|
848
|
+
else "in-flight" if live else "done")
|
|
750
849
|
return self.receipt
|
|
751
850
|
time.sleep(POLL_SECONDS)
|
|
752
851
|
except STOP_ERRORS as error:
|
|
@@ -801,6 +900,15 @@ def spawn_in_sidecar(root: Path, primary: Path, receipt: Dict[str, object], opti
|
|
|
801
900
|
name: str, state: Dict[str, object], brief: Callable[[Dict[str, object]], str],
|
|
802
901
|
command: Optional[str] = None) -> None:
|
|
803
902
|
"""Cut a verify sidecar at the state's base and start one child in it."""
|
|
903
|
+
candidate = retain_selection(root, dict(state, worktree=str(primary)))
|
|
904
|
+
try:
|
|
905
|
+
_, selection = selected_command(candidate, options, command or options.child_command)
|
|
906
|
+
except model_policy.PolicyError as error:
|
|
907
|
+
receipt['blocked'].append({'task_id': state['task_id'], 'reason': str(error),
|
|
908
|
+
'code': 'model-selection'})
|
|
909
|
+
return
|
|
910
|
+
if selection is not None:
|
|
911
|
+
state = dict(state, model_selection=selection)
|
|
804
912
|
sidecar = isolation.isolate_verify(primary, str(state["base"]), name)
|
|
805
913
|
receipt["steps"].append({"script": "isolation.py isolate-verify", "result": sidecar})
|
|
806
914
|
spawn(root, dict(state, worktree=str(sidecar["worktree"]), branch=str(sidecar["branch"])), options, brief,
|
|
@@ -872,9 +980,21 @@ class Review:
|
|
|
872
980
|
return wave_tasks(self.primary, self.project_dir, self.wave, self.receipt)
|
|
873
981
|
|
|
874
982
|
def dispatch(self, lenses: Dict[str, str], depth: str, base: Optional[str] = None) -> None:
|
|
875
|
-
dirty =
|
|
876
|
-
if
|
|
877
|
-
|
|
983
|
+
dirty = isolation.uncommitted_paths(self.primary)
|
|
984
|
+
if base is not None:
|
|
985
|
+
paths = review_findings.review_paths(self.primary / self.project_dir, self.wave, self.cycle, depth)
|
|
986
|
+
for key, state in self.current_states(list(paths)).items():
|
|
987
|
+
if state.get('outcome') != 'collected':
|
|
988
|
+
continue
|
|
989
|
+
path = paths[key]
|
|
990
|
+
relative = path.relative_to(self.primary).as_posix()
|
|
991
|
+
if (state.get('base') != base or state.get('relative') != relative
|
|
992
|
+
or not path.is_file()
|
|
993
|
+
or hashlib.sha256(path.read_bytes()).hexdigest() != state.get('validated_sha256')):
|
|
994
|
+
raise DriverStop('canonical review artifact changed after collection', path=relative)
|
|
995
|
+
dirty.discard(relative)
|
|
996
|
+
if dirty:
|
|
997
|
+
raise DriverStop("review collection base requires a clean primary", paths=sorted(dirty))
|
|
878
998
|
tasks = self.wave_tasks()
|
|
879
999
|
plan_text = (self.primary / self.project_dir / "plan/PLAN.md").read_text(encoding="utf-8")
|
|
880
1000
|
intent = (self.primary / self.project_dir / "intent/INTENT.md").read_text(encoding="utf-8")
|
|
@@ -955,7 +1075,8 @@ class Review:
|
|
|
955
1075
|
|
|
956
1076
|
def settle(self, states: Dict[str, Dict[str, object]]) -> None:
|
|
957
1077
|
"""Advance every lens and render the receipt from the records; settle is their only writer."""
|
|
958
|
-
|
|
1078
|
+
rejected = [item for item in self.receipt['blocked'] if item.get('code') == 'model-selection']
|
|
1079
|
+
self.receipt["in_flight"], self.receipt["blocked"], self.receipt["lenses"] = [], rejected, {}
|
|
959
1080
|
for key, state in states.items():
|
|
960
1081
|
if not advance(state, self.collect):
|
|
961
1082
|
self.receipt["in_flight"].append(summary(state))
|
|
@@ -1035,7 +1156,10 @@ class Review:
|
|
|
1035
1156
|
states = self.current_states(list(lenses))
|
|
1036
1157
|
while True:
|
|
1037
1158
|
self.settle(states)
|
|
1038
|
-
|
|
1159
|
+
settled = {state['task_id'] for state in states.values()}
|
|
1160
|
+
settled.update(item['task_id'] for item in self.receipt['blocked']
|
|
1161
|
+
if item.get('code') == 'model-selection')
|
|
1162
|
+
if settled == {self.lens_name(key) for key in lenses} and all(
|
|
1039
1163
|
state.get("outcome") in ("collected", "blocked") for state in states.values()):
|
|
1040
1164
|
self.conclude()
|
|
1041
1165
|
return self.receipt
|
|
@@ -1100,7 +1224,8 @@ class Children:
|
|
|
1100
1224
|
def poll(self, states: Dict[str, Dict[str, object]], deadline: Optional[float]) -> bool:
|
|
1101
1225
|
"""Advance every child; True once all are terminal, False when the deadline passes first."""
|
|
1102
1226
|
while True:
|
|
1103
|
-
|
|
1227
|
+
rejected = [item for item in self.receipt['blocked'] if item.get('code') == 'model-selection']
|
|
1228
|
+
self.receipt["in_flight"], self.receipt["blocked"], self.receipt[self.results] = [], rejected, {}
|
|
1104
1229
|
for key, state in states.items():
|
|
1105
1230
|
if not advance(state, self.collect):
|
|
1106
1231
|
self.receipt["in_flight"].append(summary(state))
|
|
@@ -1218,6 +1343,12 @@ class Panel(Children):
|
|
|
1218
1343
|
else:
|
|
1219
1344
|
if states:
|
|
1220
1345
|
raise DriverStop("panel records exist without a persisted roster")
|
|
1346
|
+
excluded = {review_panel.family_of_slug(self.options.parent_slug or '')}
|
|
1347
|
+
for canonical in latest_states(records_root(self.primary) / 'reviews'):
|
|
1348
|
+
if canonical.get('wave') == self.wave and canonical.get('cycle') == self.cycle:
|
|
1349
|
+
model = canonical.get('model_selection', {}).get('effective_model')
|
|
1350
|
+
if model:
|
|
1351
|
+
excluded.add(review_panel.family_of_slug(model))
|
|
1221
1352
|
arguments = ["resolve", "--plan", str(project / "plan/PLAN.md"),
|
|
1222
1353
|
"--intent", str(project / "intent/INTENT.md"),
|
|
1223
1354
|
"--advertised", self.options.advertised]
|
|
@@ -1225,6 +1356,8 @@ class Panel(Children):
|
|
|
1225
1356
|
arguments += ["--parent-slug", self.options.parent_slug]
|
|
1226
1357
|
if (project / "CHARTER.md").exists():
|
|
1227
1358
|
arguments += ["--charter", str(project / "CHARTER.md")]
|
|
1359
|
+
for family in sorted(family for family in excluded if family):
|
|
1360
|
+
arguments += ['--exclude-family', family]
|
|
1228
1361
|
resolved = helper_json(self.receipt, "review_panel.py", *arguments, cwd=self.primary)
|
|
1229
1362
|
self.receipt["resolve"] = resolved
|
|
1230
1363
|
if resolved["status"] == "off":
|
|
@@ -1257,6 +1390,21 @@ class Panel(Children):
|
|
|
1257
1390
|
roster = {"families": [item["family"] for item in resolved["selected"]],
|
|
1258
1391
|
"slugs": {item["family"]: item["slug"] for item in resolved["selected"]},
|
|
1259
1392
|
"mode": resolved["mode"], "base": isolation.current_sha(self.primary)}
|
|
1393
|
+
roster['excluded_families'] = sorted(family for family in excluded if family)
|
|
1394
|
+
roster['selections'] = {}
|
|
1395
|
+
for family in roster['families']:
|
|
1396
|
+
candidate = {'task_id': self.family_name(family), 'family': family,
|
|
1397
|
+
'slug': roster['slugs'][family], 'worktree': str(self.primary),
|
|
1398
|
+
'excluded_families': roster['excluded_families']}
|
|
1399
|
+
command = self.options.child_command
|
|
1400
|
+
if not getattr(self.options, 'model_capabilities', None):
|
|
1401
|
+
command = command.replace('{model}', candidate['slug'])
|
|
1402
|
+
_, selection = selected_command(candidate, self.options, command)
|
|
1403
|
+
model_policy.validate_panel(selection or {'effective_model': candidate['slug']},
|
|
1404
|
+
family, roster['excluded_families'])
|
|
1405
|
+
if selection is not None:
|
|
1406
|
+
roster['selections'][family] = selection
|
|
1407
|
+
roster['slugs'][family] = candidate['slug']
|
|
1260
1408
|
save_state(roster_path, roster)
|
|
1261
1409
|
self.receipt["mode"] = roster["mode"]
|
|
1262
1410
|
if set(states) - set(roster["families"]) or any(
|
|
@@ -1273,10 +1421,16 @@ class Panel(Children):
|
|
|
1273
1421
|
state = {"task_id": self.family_name(family), "family": family, "slug": slug,
|
|
1274
1422
|
"wave": self.wave, "cycle": self.cycle, "base": base, "mode": roster["mode"],
|
|
1275
1423
|
"relative": self.relative(family)}
|
|
1424
|
+
if roster.get('selections', {}).get(family):
|
|
1425
|
+
state['model_selection'] = roster['selections'][family]
|
|
1426
|
+
state['excluded_families'] = roster.get('excluded_families', [])
|
|
1427
|
+
command = self.options.child_command
|
|
1428
|
+
if not getattr(self.options, 'model_capabilities', None):
|
|
1429
|
+
command = command.replace('{model}', slug)
|
|
1276
1430
|
spawn_in_sidecar(self.root, self.primary, self.receipt, self.options,
|
|
1277
1431
|
f"wave-{self.wave}-cycle-{self.cycle}-panel-{family}", state,
|
|
1278
1432
|
lambda fresh: self.brief(fresh, tasks, depth, review),
|
|
1279
|
-
command=
|
|
1433
|
+
command=command)
|
|
1280
1434
|
states = self.states()
|
|
1281
1435
|
if not self.poll(states, deadline):
|
|
1282
1436
|
return self.receipt
|
|
@@ -1654,6 +1808,43 @@ def answer(primary: Path, task_id: str, text: str) -> Dict[str, object]:
|
|
|
1654
1808
|
"next": "run `round` to redispatch the retained isolate"}
|
|
1655
1809
|
|
|
1656
1810
|
|
|
1811
|
+
def reassign_model(root: Path, task_id: str, options: argparse.Namespace) -> Dict[str, object]:
|
|
1812
|
+
states = [state for state in latest_states(root) if state['task_id'] == task_id]
|
|
1813
|
+
if not states:
|
|
1814
|
+
raise DriverStop('assignment has no recorded attempt')
|
|
1815
|
+
state = states[0]
|
|
1816
|
+
if child_running(state) or state.get('finished_at') is None:
|
|
1817
|
+
raise DriverStop('cannot reassign an active or unproven child; reconcile its lifecycle first')
|
|
1818
|
+
if state.get('family') or state.get('outcome') in ('landed', 'collected'):
|
|
1819
|
+
raise DriverStop('completed work or a pinned panel roster requires a new legal review/task assignment')
|
|
1820
|
+
if not options.ruling or not options.ruling.strip() or not options.model_capabilities:
|
|
1821
|
+
raise DriverStop('reassignment requires an owner ruling and advertised capabilities')
|
|
1822
|
+
old = state.get('model_selection')
|
|
1823
|
+
if old is None:
|
|
1824
|
+
raise DriverStop('legacy assignment has no proven model selection to reassign')
|
|
1825
|
+
path = root / task_id / 'model-reassignment.json'
|
|
1826
|
+
history = load_state(path).get('history', []) if path.exists() else []
|
|
1827
|
+
if path.exists():
|
|
1828
|
+
old = load_state(path)['selection']
|
|
1829
|
+
changes = {field: getattr(options, field) for field in model_policy.FIELDS
|
|
1830
|
+
if getattr(options, field) is not None}
|
|
1831
|
+
if not changes:
|
|
1832
|
+
raise DriverStop('reassignment requires an explicit model or effort')
|
|
1833
|
+
task = (model_policy.task_settings(Path(state['worktree']) / state['task_file'])
|
|
1834
|
+
if state.get('task_file') else {})
|
|
1835
|
+
if any(field in task and task[field] != value for field, value in changes.items()):
|
|
1836
|
+
raise DriverStop('reassignment conflicts with the approved task contract')
|
|
1837
|
+
selection = model_policy.resolve(options.repo, options.project_dir, old['role'],
|
|
1838
|
+
model_policy.read_json(options.model_capabilities),
|
|
1839
|
+
dict(old['selected'], **changes))
|
|
1840
|
+
for field in model_policy.FIELDS:
|
|
1841
|
+
if field not in changes:
|
|
1842
|
+
selection['sources'][field] = old['sources'][field]
|
|
1843
|
+
save_state(path, {'selection': selection, 'history': [*history, {
|
|
1844
|
+
'selection': old, 'ruling': options.ruling, 'attempt': state['attempt']} ]})
|
|
1845
|
+
return {'status': 'reassigned', 'task': task_id, 'selection': selection, 'record': str(path)}
|
|
1846
|
+
|
|
1847
|
+
|
|
1657
1848
|
def require_files(parser: argparse.ArgumentParser, arguments: argparse.Namespace, *names: str) -> None:
|
|
1658
1849
|
for name in names:
|
|
1659
1850
|
value = getattr(arguments, name)
|
|
@@ -1706,6 +1897,13 @@ def main(argv=None) -> int:
|
|
|
1706
1897
|
cycle_child.add_argument("--wait", type=float, help="seconds to keep polling before returning")
|
|
1707
1898
|
cycle_child.add_argument("--child-timeout", type=float, help="per-child wall clock in seconds")
|
|
1708
1899
|
cycle_child.add_argument("--role-brief", type=Path, default=default_resource("references/reviewer.md"))
|
|
1900
|
+
for child in (round_parser, cycle_child):
|
|
1901
|
+
child.add_argument('--model-capabilities', type=Path,
|
|
1902
|
+
help='advertised model/effort controls; required with project model policy')
|
|
1903
|
+
cycle_child.add_argument('--model', help='explicit model override for this review assignment')
|
|
1904
|
+
cycle_child.add_argument('--effort', help='explicit effort override for this review assignment')
|
|
1905
|
+
cycle_child.add_argument('--model-overrides', type=Path,
|
|
1906
|
+
help='JSON mapping logical assignment names to explicit model/effort overrides')
|
|
1709
1907
|
review_parser = commands.add_parser("review", parents=[cycle_child],
|
|
1710
1908
|
help="run one wave review cycle at full or deep depth")
|
|
1711
1909
|
review_parser.add_argument("--repair-evidence", type=Path,
|
|
@@ -1739,6 +1937,14 @@ def main(argv=None) -> int:
|
|
|
1739
1937
|
answer_parser.add_argument("--answer", required=True)
|
|
1740
1938
|
status_parser = commands.add_parser("status", help="list dispatch records")
|
|
1741
1939
|
status_parser.add_argument("--repo", type=Path, required=True)
|
|
1940
|
+
model_parser = commands.add_parser('reassign-model', help='record an explicit model reassignment')
|
|
1941
|
+
model_parser.add_argument('--repo', type=Path, required=True)
|
|
1942
|
+
model_parser.add_argument('--project-dir', default='.project')
|
|
1943
|
+
model_parser.add_argument('--task-id', required=True)
|
|
1944
|
+
model_parser.add_argument('--model-capabilities', type=Path, required=True)
|
|
1945
|
+
model_parser.add_argument('--model')
|
|
1946
|
+
model_parser.add_argument('--effort')
|
|
1947
|
+
model_parser.add_argument('--ruling', required=True)
|
|
1742
1948
|
child_parser = commands.add_parser("_child", help=argparse.SUPPRESS)
|
|
1743
1949
|
child_parser.add_argument("--state", type=Path, required=True)
|
|
1744
1950
|
arguments = parser.parse_args(argv)
|
|
@@ -1795,6 +2001,11 @@ def main(argv=None) -> int:
|
|
|
1795
2001
|
result = {**current.receipt, "status": "landed" if current.receipt["landed"] else "blocked"}
|
|
1796
2002
|
elif arguments.action == "answer":
|
|
1797
2003
|
result = answer(primary, arguments.task_id, arguments.answer)
|
|
2004
|
+
elif arguments.action == 'reassign-model':
|
|
2005
|
+
roots = [records_root(primary), *(records_root(primary) / name
|
|
2006
|
+
for name in ('reviews', 'panels', 'skeptics'))]
|
|
2007
|
+
root = next((root for root in roots if (root / arguments.task_id).is_dir()), roots[0])
|
|
2008
|
+
result = reassign_model(root, arguments.task_id, arguments)
|
|
1798
2009
|
else:
|
|
1799
2010
|
result = {"status": "ok", "dispatches": [
|
|
1800
2011
|
{key: value for key, value in state.items() if key not in ("_path", "command")}
|