@opengsd/gsd-path 1.1.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 +5 -2
- package/CHANGELOG.md +68 -0
- package/DOCS.md +69 -1
- package/FULL.md +3 -2
- package/HOOKS.md +18 -9
- package/QUICK.md +5 -3
- package/README.md +72 -5
- package/RUNTIME.md +9 -4
- package/UPDATE.md +30 -11
- package/WORKFLOW.md +5 -2
- package/package.json +9 -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 +6 -19
- package/scripts/_common.py +5 -0
- package/scripts/archive_milestone.py +20 -6
- package/scripts/bootstrap_repository.py +8 -1
- package/scripts/build_recovery.py +5 -0
- package/scripts/build_state.py +2 -0
- package/scripts/check_docs_audit.py +2 -0
- package/scripts/check_handoffs.py +6 -0
- package/scripts/check_task_briefs.py +3 -0
- package/scripts/detect_project.py +11 -1
- package/scripts/discussion_records.py +3 -0
- package/scripts/discussion_validate.py +8 -3
- package/scripts/dispatch_driver.py +230 -21
- package/scripts/git_guard.py +12 -4
- package/scripts/guard_hook.py +11 -1
- package/scripts/install.mjs +52 -1448
- package/scripts/install.py +155 -190
- package/scripts/integration.py +28 -8
- package/scripts/isolation.py +15 -5
- package/scripts/jev_review.py +138 -0
- package/scripts/lean_verification.py +2 -0
- 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 +3 -0
- package/scripts/pipeline_git.py +3 -0
- package/scripts/pipeline_state.py +4 -1
- package/scripts/pipeline_undo.py +3 -0
- package/scripts/promote_lookahead.py +3 -0
- package/scripts/review_panel.py +11 -0
- package/scripts/roadmap.py +5 -0
- package/scripts/runtime_store.py +278 -0
- package/scripts/skill-resources.json +104 -4
- package/scripts/state_checkpoint.py +5 -0
- package/scripts/state_promote.py +5 -0
- package/scripts/status_runtime.py +92 -6
- package/scripts/workflow_run.py +12 -1
- package/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path/BUILD.md +7 -0
- package/skills/gsd-path/DECIDE.md +2 -0
- package/skills/gsd-path/DEFINE.md +13 -7
- package/skills/gsd-path/DOCS-AUDIT.md +2 -0
- package/skills/gsd-path/INSPECT.md +2 -0
- package/skills/gsd-path/PLAN.md +4 -2
- package/skills/gsd-path/RESEARCH.md +2 -0
- package/skills/gsd-path/ROADMAP.md +2 -0
- package/skills/gsd-path/SHIP.md +24 -13
- package/skills/gsd-path/SKILL.md +23 -9
- package/skills/gsd-path/references/build-native.md +15 -7
- package/skills/gsd-path/references/config.md +57 -0
- package/skills/gsd-path/references/dispatch.md +6 -19
- 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-panel.md +1 -1
- package/skills/gsd-path/references/reviewer.md +2 -0
- 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 +20 -6
- package/skills/gsd-path/scripts/bootstrap_repository.py +8 -1
- package/skills/gsd-path/scripts/build_recovery.py +5 -0
- package/skills/gsd-path/scripts/build_state.py +2 -0
- package/skills/gsd-path/scripts/check_docs_audit.py +2 -0
- package/skills/gsd-path/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path/scripts/detect_project.py +11 -1
- 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 +230 -21
- package/skills/gsd-path/scripts/git_guard.py +768 -0
- package/skills/gsd-path/scripts/integration.py +28 -8
- package/skills/gsd-path/scripts/isolation.py +15 -5
- package/skills/gsd-path/scripts/jev_review.py +138 -0
- package/skills/gsd-path/scripts/lean_verification.py +2 -0
- 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 +4 -1
- package/skills/gsd-path/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path/scripts/review_panel.py +11 -0
- package/skills/gsd-path/scripts/roadmap.py +5 -0
- package/skills/gsd-path/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path/scripts/state_promote.py +5 -0
- package/skills/gsd-path/scripts/status_runtime.py +208 -0
- package/skills/gsd-path/scripts/workflow_run.py +12 -1
- 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-build/SKILL.md +7 -0
- package/skills/gsd-path-build/references/build-native.md +15 -7
- package/skills/gsd-path-build/references/dispatch.md +6 -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-panel.md +1 -1
- package/skills/gsd-path-build/references/reviewer.md +2 -0
- 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 +20 -6
- package/skills/gsd-path-build/scripts/build_recovery.py +5 -0
- package/skills/gsd-path-build/scripts/build_state.py +2 -0
- package/skills/gsd-path-build/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path-build/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-build/scripts/detect_project.py +11 -1
- 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 +230 -21
- package/skills/gsd-path-build/scripts/integration.py +28 -8
- package/skills/gsd-path-build/scripts/isolation.py +15 -5
- 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 +4 -1
- package/skills/gsd-path-build/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path-build/scripts/review_panel.py +11 -0
- package/skills/gsd-path-build/scripts/roadmap.py +5 -0
- package/skills/gsd-path-build/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path-build/scripts/state_promote.py +5 -0
- package/skills/gsd-path-build/scripts/workflow_run.py +12 -1
- 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-decide/SKILL.md +2 -0
- package/skills/gsd-path-decide/references/dispatch.md +6 -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 +20 -6
- package/skills/gsd-path-decide/scripts/build_recovery.py +5 -0
- package/skills/gsd-path-decide/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-decide/scripts/detect_project.py +11 -1
- package/skills/gsd-path-decide/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-decide/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path-decide/scripts/integration.py +28 -8
- package/skills/gsd-path-decide/scripts/isolation.py +15 -5
- 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 +4 -1
- package/skills/gsd-path-decide/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path-decide/scripts/review_panel.py +11 -0
- package/skills/gsd-path-decide/scripts/roadmap.py +5 -0
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path-decide/scripts/state_promote.py +5 -0
- package/skills/gsd-path-decide/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-define/SKILL.md +13 -7
- 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 +20 -6
- package/skills/gsd-path-define/scripts/build_recovery.py +5 -0
- package/skills/gsd-path-define/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path-define/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-define/scripts/detect_project.py +11 -1
- package/skills/gsd-path-define/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-define/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path-define/scripts/integration.py +28 -8
- package/skills/gsd-path-define/scripts/isolation.py +15 -5
- 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 +4 -1
- package/skills/gsd-path-define/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path-define/scripts/review_panel.py +11 -0
- package/skills/gsd-path-define/scripts/roadmap.py +5 -0
- package/skills/gsd-path-define/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path-define/scripts/state_promote.py +5 -0
- 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 +6 -19
- 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 +20 -6
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +5 -0
- package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +2 -0
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-docs-audit/scripts/detect_project.py +11 -1
- package/skills/gsd-path-docs-audit/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path-docs-audit/scripts/integration.py +28 -8
- package/skills/gsd-path-docs-audit/scripts/isolation.py +15 -5
- 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 +4 -1
- package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path-docs-audit/scripts/review_panel.py +11 -0
- package/skills/gsd-path-docs-audit/scripts/roadmap.py +5 -0
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path-docs-audit/scripts/state_promote.py +5 -0
- 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 +3 -0
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +3 -0
- 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 +2 -0
- package/skills/gsd-path-inspect/references/dispatch.md +6 -19
- 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 +20 -6
- package/skills/gsd-path-inspect/scripts/build_recovery.py +5 -0
- package/skills/gsd-path-inspect/scripts/check_docs_audit.py +2 -0
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-inspect/scripts/detect_project.py +11 -1
- package/skills/gsd-path-inspect/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path-inspect/scripts/integration.py +28 -8
- package/skills/gsd-path-inspect/scripts/isolation.py +15 -5
- 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 +4 -1
- package/skills/gsd-path-inspect/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path-inspect/scripts/review_panel.py +11 -0
- package/skills/gsd-path-inspect/scripts/roadmap.py +5 -0
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path-inspect/scripts/state_promote.py +5 -0
- package/skills/gsd-path-inspect/scripts/workflow_run.py +12 -1
- 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 +4 -2
- package/skills/gsd-path-plan/references/dispatch.md +6 -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 +20 -6
- package/skills/gsd-path-plan/scripts/build_recovery.py +5 -0
- package/skills/gsd-path-plan/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-plan/scripts/detect_project.py +11 -1
- package/skills/gsd-path-plan/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-plan/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path-plan/scripts/integration.py +28 -8
- package/skills/gsd-path-plan/scripts/isolation.py +15 -5
- 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 +4 -1
- package/skills/gsd-path-plan/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path-plan/scripts/review_panel.py +11 -0
- package/skills/gsd-path-plan/scripts/roadmap.py +5 -0
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path-plan/scripts/state_promote.py +5 -0
- package/skills/gsd-path-plan/scripts/workflow_run.py +12 -1
- 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 +2 -0
- package/skills/gsd-path-research/references/dispatch.md +6 -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 +20 -6
- package/skills/gsd-path-research/scripts/build_recovery.py +5 -0
- package/skills/gsd-path-research/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path-research/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-research/scripts/detect_project.py +11 -1
- package/skills/gsd-path-research/scripts/discussion_records.py +3 -0
- package/skills/gsd-path-research/scripts/discussion_validate.py +8 -3
- package/skills/gsd-path-research/scripts/integration.py +28 -8
- package/skills/gsd-path-research/scripts/isolation.py +15 -5
- 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 +4 -1
- package/skills/gsd-path-research/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path-research/scripts/review_panel.py +11 -0
- package/skills/gsd-path-research/scripts/roadmap.py +5 -0
- package/skills/gsd-path-research/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path-research/scripts/state_promote.py +5 -0
- package/skills/gsd-path-research/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-roadmap/SKILL.md +2 -0
- package/skills/gsd-path-roadmap/references/dispatch.md +6 -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 +20 -6
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +5 -0
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-roadmap/scripts/detect_project.py +11 -1
- 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 +28 -8
- package/skills/gsd-path-roadmap/scripts/isolation.py +15 -5
- 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 +4 -1
- package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path-roadmap/scripts/review_panel.py +11 -0
- package/skills/gsd-path-roadmap/scripts/roadmap.py +5 -0
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path-roadmap/scripts/state_promote.py +5 -0
- package/skills/gsd-path-roadmap/scripts/worktree_paths.py +94 -0
- package/skills/gsd-path-ship/SKILL.md +24 -13
- package/skills/gsd-path-ship/references/dispatch.md +6 -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-panel.md +1 -1
- package/skills/gsd-path-ship/references/reviewer.md +2 -0
- 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 +20 -6
- package/skills/gsd-path-ship/scripts/build_recovery.py +5 -0
- package/skills/gsd-path-ship/scripts/build_state.py +2 -0
- package/skills/gsd-path-ship/scripts/check_handoffs.py +6 -0
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +3 -0
- package/skills/gsd-path-ship/scripts/detect_project.py +11 -1
- 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 +28 -8
- package/skills/gsd-path-ship/scripts/isolation.py +15 -5
- package/skills/gsd-path-ship/scripts/jev_review.py +138 -0
- package/skills/gsd-path-ship/scripts/lean_verification.py +2 -0
- 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 +4 -1
- package/skills/gsd-path-ship/scripts/promote_lookahead.py +3 -0
- package/skills/gsd-path-ship/scripts/review_panel.py +11 -0
- package/skills/gsd-path-ship/scripts/roadmap.py +5 -0
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +5 -0
- package/skills/gsd-path-ship/scripts/state_promote.py +5 -0
- package/skills/gsd-path-ship/scripts/workflow_run.py +12 -1
- package/skills/gsd-path-ship/scripts/worktree_paths.py +94 -0
- 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 +3 -0
- 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 +7 -0
- package/skills/path/DECIDE.md +2 -0
- package/skills/path/DEFINE.md +13 -7
- package/skills/path/DOCS-AUDIT.md +2 -0
- package/skills/path/INSPECT.md +2 -0
- package/skills/path/PLAN.md +4 -2
- package/skills/path/RESEARCH.md +2 -0
- package/skills/path/ROADMAP.md +2 -0
- package/skills/path/SHIP.md +24 -13
- package/skills/path/SKILL.md +23 -9
- package/skills/path/references/build-native.md +15 -7
- package/skills/path/references/config.md +57 -0
- package/skills/path/references/dispatch.md +6 -19
- package/skills/path/references/jev-review.md +82 -0
- package/skills/path/references/model-policy.md +139 -0
- package/skills/path/references/reviewer-panel.md +1 -1
- package/skills/path/references/reviewer.md +2 -0
- package/skills/path/references/runtime-selection.md +25 -0
- package/skills/path/scripts/_common.py +5 -0
- package/skills/path/scripts/archive_milestone.py +20 -6
- package/skills/path/scripts/bootstrap_repository.py +8 -1
- package/skills/path/scripts/build_recovery.py +5 -0
- package/skills/path/scripts/build_state.py +2 -0
- package/skills/path/scripts/check_docs_audit.py +2 -0
- package/skills/path/scripts/check_handoffs.py +6 -0
- package/skills/path/scripts/check_task_briefs.py +3 -0
- package/skills/path/scripts/detect_project.py +11 -1
- 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 +230 -21
- package/skills/path/scripts/git_guard.py +768 -0
- package/skills/path/scripts/integration.py +28 -8
- package/skills/path/scripts/isolation.py +15 -5
- package/skills/path/scripts/jev_review.py +138 -0
- package/skills/path/scripts/lean_verification.py +2 -0
- 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 +4 -1
- package/skills/path/scripts/promote_lookahead.py +3 -0
- package/skills/path/scripts/review_panel.py +11 -0
- package/skills/path/scripts/roadmap.py +5 -0
- package/skills/path/scripts/state_checkpoint.py +5 -0
- package/skills/path/scripts/state_promote.py +5 -0
- package/skills/path/scripts/status_runtime.py +208 -0
- package/skills/path/scripts/workflow_run.py +12 -1
- 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/gsd-path/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/discussion_records.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/integration.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-decide/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-define/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-docs-audit/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-forensics/scripts/__pycache__/pipeline_undo.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-research/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-roadmap/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
|
@@ -7,6 +7,9 @@ import json
|
|
|
7
7
|
import re
|
|
8
8
|
import subprocess
|
|
9
9
|
import sys
|
|
10
|
+
|
|
11
|
+
# Runtime helpers must not modify their immutable installation.
|
|
12
|
+
sys.dont_write_bytecode = True
|
|
10
13
|
from pathlib import Path, PurePosixPath
|
|
11
14
|
from typing import Dict, List, Optional, Sequence, Set, Tuple
|
|
12
15
|
|
|
@@ -131,6 +134,9 @@ def _strict_frontmatter(text: str, label: str) -> Dict[str, object]:
|
|
|
131
134
|
while index < len(lines):
|
|
132
135
|
line = lines[index]
|
|
133
136
|
if line == "---":
|
|
137
|
+
for field in ('model', 'effort'):
|
|
138
|
+
if field in values and (not isinstance(values[field], str) or not values[field].strip()):
|
|
139
|
+
raise HandoffError(f'{label}: {field} must be a nonempty scalar or inherit')
|
|
134
140
|
return values
|
|
135
141
|
if not line.strip() or line.lstrip().startswith("#"):
|
|
136
142
|
index += 1
|
|
@@ -6,6 +6,9 @@ import argparse
|
|
|
6
6
|
import json
|
|
7
7
|
import re
|
|
8
8
|
import sys
|
|
9
|
+
|
|
10
|
+
# Runtime helpers must not modify their immutable installation.
|
|
11
|
+
sys.dont_write_bytecode = True
|
|
9
12
|
from pathlib import Path, PurePosixPath
|
|
10
13
|
from typing import Dict, List, Optional, Sequence, Set, Tuple
|
|
11
14
|
|
|
@@ -34,6 +34,9 @@ import re
|
|
|
34
34
|
import stat
|
|
35
35
|
import subprocess
|
|
36
36
|
import sys
|
|
37
|
+
|
|
38
|
+
# Runtime helpers must not modify their immutable installation.
|
|
39
|
+
sys.dont_write_bytecode = True
|
|
37
40
|
from datetime import date
|
|
38
41
|
from pathlib import Path, PurePosixPath, PureWindowsPath
|
|
39
42
|
from typing import Iterable, NamedTuple, Optional, Sequence
|
|
@@ -1200,7 +1203,14 @@ def filled_state_template(template: str, slug: str, phase: str) -> str:
|
|
|
1200
1203
|
text = text.replace("YYYY-MM-DD — <phase> — project initialized", stamp)
|
|
1201
1204
|
if "<slug>" in text or "YYYY-MM-DD" in text:
|
|
1202
1205
|
raise DetectError("state template placeholder remains")
|
|
1203
|
-
|
|
1206
|
+
try:
|
|
1207
|
+
from .path_config import initial_state
|
|
1208
|
+
except ImportError:
|
|
1209
|
+
from path_config import initial_state
|
|
1210
|
+
try:
|
|
1211
|
+
return initial_state(text)
|
|
1212
|
+
except (OSError, ValueError, RuntimeError) as error:
|
|
1213
|
+
raise DetectError(str(error)) from error
|
|
1204
1214
|
|
|
1205
1215
|
|
|
1206
1216
|
def write_all(descriptor: int, payload: bytes) -> None:
|
|
@@ -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,7 +31,7 @@ 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, task_context)
|
|
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
|
|
@@ -45,6 +45,7 @@ except ImportError: # bundled copy inside a skill's scripts directory
|
|
|
45
45
|
import review_findings
|
|
46
46
|
import review_panel
|
|
47
47
|
import workflow_run
|
|
48
|
+
import model_policy
|
|
48
49
|
|
|
49
50
|
if os.name == "nt":
|
|
50
51
|
import msvcrt
|
|
@@ -71,7 +72,7 @@ class DriverStop(RuntimeError):
|
|
|
71
72
|
|
|
72
73
|
STOP_ERRORS = (DriverStop, isolation.IsolationError, build_state.BuildStateError,
|
|
73
74
|
contracts.HandoffError, review_findings.ReviewFindingsError,
|
|
74
|
-
pipeline_state.PipelineStateError, archive_milestone.ArchiveError)
|
|
75
|
+
pipeline_state.PipelineStateError, archive_milestone.ArchiveError, model_policy.PolicyError)
|
|
75
76
|
RECOVERY_BLOCKED = "recovery blocked"
|
|
76
77
|
# The stops the build contract answers with build/blocked when the round raises them (steps 1 and 2);
|
|
77
78
|
# the same stops from review, panel, or skeptics stay with the parent.
|
|
@@ -251,10 +252,81 @@ def brief_text(state: Dict[str, object], role_brief: Path, task_template: Path)
|
|
|
251
252
|
return "\n".join(lines) + "\n"
|
|
252
253
|
|
|
253
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
|
+
|
|
254
322
|
def spawn(root: Path, state: Dict[str, object], options: argparse.Namespace,
|
|
255
323
|
brief: Callable[[Dict[str, object]], str], command: Optional[str] = None) -> Dict[str, object]:
|
|
256
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)
|
|
257
328
|
task_dir = root / str(state["task_id"])
|
|
329
|
+
argv, selection = selected_command(state, options, command or options.child_command)
|
|
258
330
|
attempt = max((attempt_number(path) for path in task_dir.glob("attempt-*")), default=0) + 1
|
|
259
331
|
attempt_dir = task_dir / f"attempt-{attempt}"
|
|
260
332
|
attempt_dir.mkdir(parents=True)
|
|
@@ -262,7 +334,9 @@ def spawn(root: Path, state: Dict[str, object], options: argparse.Namespace,
|
|
|
262
334
|
"usage_recorded")
|
|
263
335
|
state = {key: value for key, value in state.items()
|
|
264
336
|
if not key.startswith("_") and key not in stale}
|
|
265
|
-
|
|
337
|
+
if selection is not None:
|
|
338
|
+
state['model_selection'] = selection
|
|
339
|
+
state.update({"attempt": attempt, "command": argv,
|
|
266
340
|
"child_timeout": options.child_timeout, "outcome": None,
|
|
267
341
|
"dispatched_at": now(), "origin": "question" if state.get("answered") else "dispatch"})
|
|
268
342
|
state_path = attempt_dir / "state.json"
|
|
@@ -397,7 +471,8 @@ def finish_task(primary: Path, state: Dict[str, object]) -> Dict[str, object]:
|
|
|
397
471
|
|
|
398
472
|
def summary(state: Dict[str, object]) -> Dict[str, object]:
|
|
399
473
|
return {key: state.get(key) for key in
|
|
400
|
-
("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")}
|
|
401
476
|
|
|
402
477
|
|
|
403
478
|
def recover_report(primary: Path, project_dir: str, receipt: Dict[str, object]) -> Dict[str, object]:
|
|
@@ -558,14 +633,23 @@ class Round:
|
|
|
558
633
|
def settle(self) -> bool:
|
|
559
634
|
"""Classify every exited child. Returns True when a landing or redispatch changed the round."""
|
|
560
635
|
changed = False
|
|
636
|
+
rejected = {item['task_id'] for item in self.receipt['blocked']
|
|
637
|
+
if item.get('code') == 'model-selection'}
|
|
561
638
|
for state in latest_states(self.root):
|
|
562
639
|
if state.get("wave") != self.receipt["wave"]:
|
|
563
640
|
continue
|
|
564
641
|
outcome = state.get("outcome")
|
|
565
642
|
if outcome == "question":
|
|
566
643
|
if state.get("answered"):
|
|
644
|
+
if state['task_id'] in rejected:
|
|
645
|
+
continue
|
|
567
646
|
self.admit(str(state["task_id"]))
|
|
568
|
-
|
|
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
|
|
569
653
|
update_state(state, outcome="redispatched")
|
|
570
654
|
changed = True
|
|
571
655
|
else:
|
|
@@ -690,23 +774,33 @@ class Round:
|
|
|
690
774
|
heavy_busy = any(state.get("verify_heavy") for state in in_flight)
|
|
691
775
|
capacity = self.options.capacity
|
|
692
776
|
selected = []
|
|
777
|
+
rejected = {item["task_id"] for item in self.receipt["blocked"]
|
|
778
|
+
if item.get("code") == "model-selection"}
|
|
693
779
|
for task in ready["ready"]:
|
|
694
780
|
if capacity is not None and len(in_flight) + len(selected) >= capacity:
|
|
695
781
|
break
|
|
696
782
|
if task["wave"] != self.receipt["wave"]:
|
|
697
783
|
continue
|
|
698
|
-
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):
|
|
699
785
|
continue
|
|
700
|
-
heavy_busy = heavy_busy or task["verify_heavy"]
|
|
701
|
-
selected.append(task)
|
|
702
|
-
if not selected:
|
|
703
|
-
return False
|
|
704
|
-
for task in selected:
|
|
705
786
|
used = attempts_used(self.root, task["id"])
|
|
706
787
|
if used >= self.options.max_attempts:
|
|
707
788
|
raise DriverStop(f"task {task['id']} reached the attempt limit ({self.options.max_attempts}) "
|
|
708
789
|
"for this milestone; a person must rule", task=task["id"], attempts=used)
|
|
709
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
|
|
710
804
|
head = isolation.current_sha(self.primary)
|
|
711
805
|
self.runner("lint-round", head)
|
|
712
806
|
round_size = len(selected) + len(in_flight)
|
|
@@ -721,7 +815,8 @@ class Round:
|
|
|
721
815
|
"files": task["files"], "wave": task["wave"],
|
|
722
816
|
"verify_heavy": task["verify_heavy"], "base": head, "worktree": str(worktree),
|
|
723
817
|
"task_branch": isolate["task_branch"], "mode": isolate["mode"],
|
|
724
|
-
"sidecar": sidecar, "answered": False
|
|
818
|
+
"sidecar": sidecar, "answered": False,
|
|
819
|
+
"model_selection": task['model_selection']})
|
|
725
820
|
return False
|
|
726
821
|
|
|
727
822
|
def runner(self, action: str, head: str, task_id: Optional[str] = None,
|
|
@@ -738,7 +833,8 @@ class Round:
|
|
|
738
833
|
while True:
|
|
739
834
|
self.receipt["in_flight"] = []
|
|
740
835
|
changed = self.settle() or changed
|
|
741
|
-
if self.receipt["questions"] or
|
|
836
|
+
if self.receipt["questions"] or any(
|
|
837
|
+
item.get("code") != "model-selection" for item in self.receipt["blocked"]):
|
|
742
838
|
self.receipt["status"] = "question" if self.receipt["questions"] else "blocked"
|
|
743
839
|
return self.receipt
|
|
744
840
|
# ready is expensive and its inputs only move when a landing or redispatch happened
|
|
@@ -748,7 +844,8 @@ class Round:
|
|
|
748
844
|
changed = False
|
|
749
845
|
live = self.in_flight_states()
|
|
750
846
|
if deadline is None or time.monotonic() >= deadline or not live:
|
|
751
|
-
self.receipt["status"] = "
|
|
847
|
+
self.receipt["status"] = ("blocked" if self.receipt["blocked"]
|
|
848
|
+
else "in-flight" if live else "done")
|
|
752
849
|
return self.receipt
|
|
753
850
|
time.sleep(POLL_SECONDS)
|
|
754
851
|
except STOP_ERRORS as error:
|
|
@@ -803,6 +900,15 @@ def spawn_in_sidecar(root: Path, primary: Path, receipt: Dict[str, object], opti
|
|
|
803
900
|
name: str, state: Dict[str, object], brief: Callable[[Dict[str, object]], str],
|
|
804
901
|
command: Optional[str] = None) -> None:
|
|
805
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)
|
|
806
912
|
sidecar = isolation.isolate_verify(primary, str(state["base"]), name)
|
|
807
913
|
receipt["steps"].append({"script": "isolation.py isolate-verify", "result": sidecar})
|
|
808
914
|
spawn(root, dict(state, worktree=str(sidecar["worktree"]), branch=str(sidecar["branch"])), options, brief,
|
|
@@ -874,9 +980,21 @@ class Review:
|
|
|
874
980
|
return wave_tasks(self.primary, self.project_dir, self.wave, self.receipt)
|
|
875
981
|
|
|
876
982
|
def dispatch(self, lenses: Dict[str, str], depth: str, base: Optional[str] = None) -> None:
|
|
877
|
-
dirty =
|
|
878
|
-
if
|
|
879
|
-
|
|
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))
|
|
880
998
|
tasks = self.wave_tasks()
|
|
881
999
|
plan_text = (self.primary / self.project_dir / "plan/PLAN.md").read_text(encoding="utf-8")
|
|
882
1000
|
intent = (self.primary / self.project_dir / "intent/INTENT.md").read_text(encoding="utf-8")
|
|
@@ -957,7 +1075,8 @@ class Review:
|
|
|
957
1075
|
|
|
958
1076
|
def settle(self, states: Dict[str, Dict[str, object]]) -> None:
|
|
959
1077
|
"""Advance every lens and render the receipt from the records; settle is their only writer."""
|
|
960
|
-
|
|
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, {}
|
|
961
1080
|
for key, state in states.items():
|
|
962
1081
|
if not advance(state, self.collect):
|
|
963
1082
|
self.receipt["in_flight"].append(summary(state))
|
|
@@ -1037,7 +1156,10 @@ class Review:
|
|
|
1037
1156
|
states = self.current_states(list(lenses))
|
|
1038
1157
|
while True:
|
|
1039
1158
|
self.settle(states)
|
|
1040
|
-
|
|
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(
|
|
1041
1163
|
state.get("outcome") in ("collected", "blocked") for state in states.values()):
|
|
1042
1164
|
self.conclude()
|
|
1043
1165
|
return self.receipt
|
|
@@ -1102,7 +1224,8 @@ class Children:
|
|
|
1102
1224
|
def poll(self, states: Dict[str, Dict[str, object]], deadline: Optional[float]) -> bool:
|
|
1103
1225
|
"""Advance every child; True once all are terminal, False when the deadline passes first."""
|
|
1104
1226
|
while True:
|
|
1105
|
-
|
|
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, {}
|
|
1106
1229
|
for key, state in states.items():
|
|
1107
1230
|
if not advance(state, self.collect):
|
|
1108
1231
|
self.receipt["in_flight"].append(summary(state))
|
|
@@ -1220,6 +1343,12 @@ class Panel(Children):
|
|
|
1220
1343
|
else:
|
|
1221
1344
|
if states:
|
|
1222
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))
|
|
1223
1352
|
arguments = ["resolve", "--plan", str(project / "plan/PLAN.md"),
|
|
1224
1353
|
"--intent", str(project / "intent/INTENT.md"),
|
|
1225
1354
|
"--advertised", self.options.advertised]
|
|
@@ -1227,6 +1356,8 @@ class Panel(Children):
|
|
|
1227
1356
|
arguments += ["--parent-slug", self.options.parent_slug]
|
|
1228
1357
|
if (project / "CHARTER.md").exists():
|
|
1229
1358
|
arguments += ["--charter", str(project / "CHARTER.md")]
|
|
1359
|
+
for family in sorted(family for family in excluded if family):
|
|
1360
|
+
arguments += ['--exclude-family', family]
|
|
1230
1361
|
resolved = helper_json(self.receipt, "review_panel.py", *arguments, cwd=self.primary)
|
|
1231
1362
|
self.receipt["resolve"] = resolved
|
|
1232
1363
|
if resolved["status"] == "off":
|
|
@@ -1259,6 +1390,21 @@ class Panel(Children):
|
|
|
1259
1390
|
roster = {"families": [item["family"] for item in resolved["selected"]],
|
|
1260
1391
|
"slugs": {item["family"]: item["slug"] for item in resolved["selected"]},
|
|
1261
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']
|
|
1262
1408
|
save_state(roster_path, roster)
|
|
1263
1409
|
self.receipt["mode"] = roster["mode"]
|
|
1264
1410
|
if set(states) - set(roster["families"]) or any(
|
|
@@ -1275,10 +1421,16 @@ class Panel(Children):
|
|
|
1275
1421
|
state = {"task_id": self.family_name(family), "family": family, "slug": slug,
|
|
1276
1422
|
"wave": self.wave, "cycle": self.cycle, "base": base, "mode": roster["mode"],
|
|
1277
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)
|
|
1278
1430
|
spawn_in_sidecar(self.root, self.primary, self.receipt, self.options,
|
|
1279
1431
|
f"wave-{self.wave}-cycle-{self.cycle}-panel-{family}", state,
|
|
1280
1432
|
lambda fresh: self.brief(fresh, tasks, depth, review),
|
|
1281
|
-
command=
|
|
1433
|
+
command=command)
|
|
1282
1434
|
states = self.states()
|
|
1283
1435
|
if not self.poll(states, deadline):
|
|
1284
1436
|
return self.receipt
|
|
@@ -1656,6 +1808,43 @@ def answer(primary: Path, task_id: str, text: str) -> Dict[str, object]:
|
|
|
1656
1808
|
"next": "run `round` to redispatch the retained isolate"}
|
|
1657
1809
|
|
|
1658
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
|
+
|
|
1659
1848
|
def require_files(parser: argparse.ArgumentParser, arguments: argparse.Namespace, *names: str) -> None:
|
|
1660
1849
|
for name in names:
|
|
1661
1850
|
value = getattr(arguments, name)
|
|
@@ -1708,6 +1897,13 @@ def main(argv=None) -> int:
|
|
|
1708
1897
|
cycle_child.add_argument("--wait", type=float, help="seconds to keep polling before returning")
|
|
1709
1898
|
cycle_child.add_argument("--child-timeout", type=float, help="per-child wall clock in seconds")
|
|
1710
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')
|
|
1711
1907
|
review_parser = commands.add_parser("review", parents=[cycle_child],
|
|
1712
1908
|
help="run one wave review cycle at full or deep depth")
|
|
1713
1909
|
review_parser.add_argument("--repair-evidence", type=Path,
|
|
@@ -1741,6 +1937,14 @@ def main(argv=None) -> int:
|
|
|
1741
1937
|
answer_parser.add_argument("--answer", required=True)
|
|
1742
1938
|
status_parser = commands.add_parser("status", help="list dispatch records")
|
|
1743
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)
|
|
1744
1948
|
child_parser = commands.add_parser("_child", help=argparse.SUPPRESS)
|
|
1745
1949
|
child_parser.add_argument("--state", type=Path, required=True)
|
|
1746
1950
|
arguments = parser.parse_args(argv)
|
|
@@ -1797,6 +2001,11 @@ def main(argv=None) -> int:
|
|
|
1797
2001
|
result = {**current.receipt, "status": "landed" if current.receipt["landed"] else "blocked"}
|
|
1798
2002
|
elif arguments.action == "answer":
|
|
1799
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)
|
|
1800
2009
|
else:
|
|
1801
2010
|
result = {"status": "ok", "dispatches": [
|
|
1802
2011
|
{key: value for key, value in state.items() if key not in ("_path", "command")}
|