@opengsd/gsd-path 1.4.0 → 1.6.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/CHANGELOG.md +460 -341
- package/DOCS.md +16 -5
- package/FULL.md +67 -6
- package/HOOKS.md +158 -10
- package/MIGRATE.md +12 -0
- package/QUICK.md +4 -2
- package/README.md +136 -116
- package/RUNTIME.md +24 -6
- package/UPDATE.md +39 -3
- package/WORKFLOW.md +80 -4
- package/package.json +5 -2
- package/platforms/shared-agents/dispatch.md +13 -0
- package/scripts/_common.py +256 -2
- package/scripts/archive_milestone.py +14 -13
- package/scripts/build_recovery.py +155 -58
- package/scripts/build_state.py +274 -8
- package/scripts/check_docs_audit.py +77 -4
- package/scripts/check_handoffs.py +635 -57
- package/scripts/check_task_briefs.py +293 -32
- package/scripts/detect_project.py +260 -20
- package/scripts/discussion_records.py +9 -1
- package/scripts/discussion_validate.py +93 -35
- package/scripts/dispatch_driver.py +549 -53
- package/scripts/git_guard.py +27 -8
- package/scripts/guard_hook.py +2630 -210
- package/scripts/install.mjs +99 -11
- package/scripts/install.py +231 -46
- package/scripts/integration.py +244 -65
- package/scripts/isolation.py +539 -70
- package/scripts/loop_run.py +16 -4
- package/scripts/members.py +77 -32
- package/scripts/migrate_core.py +2 -0
- package/scripts/model_policy.py +5 -1
- package/scripts/path_config.py +3 -3
- package/scripts/pipeline_diagnose.py +17 -2
- package/scripts/pipeline_git.py +131 -42
- package/scripts/pipeline_state.py +139 -39
- package/scripts/pipeline_undo.py +118 -16
- package/scripts/promote_lookahead.py +3 -3
- package/scripts/review_findings.py +180 -44
- package/scripts/roadmap.py +9 -1
- package/scripts/runtime_store.py +21 -2
- package/scripts/skill-resources.json +9 -2
- package/scripts/state_checkpoint.py +39 -36
- package/scripts/state_promote.py +15 -4
- package/scripts/status_runtime.py +22 -4
- package/scripts/task_context.py +14 -3
- package/scripts/wizard.mjs +1 -0
- package/scripts/workflow_run.py +14 -4
- package/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path/DEFINE.md +34 -5
- package/skills/gsd-path/PLAN.md +14 -3
- package/skills/gsd-path/RESEARCH.md +11 -0
- package/skills/gsd-path/ROADMAP.md +16 -6
- package/skills/gsd-path/SHIP.md +32 -5
- package/skills/gsd-path/SKILL.md +52 -24
- package/skills/gsd-path/references/build-native.md +112 -10
- package/skills/gsd-path/references/build-recovery.md +13 -0
- package/skills/gsd-path/references/coder.md +7 -3
- package/skills/gsd-path/references/dispatch.md +13 -0
- package/skills/gsd-path/references/docs-auditor.md +5 -0
- package/skills/gsd-path/references/operating-rules.md +11 -3
- package/skills/gsd-path/references/planner.md +26 -4
- package/skills/gsd-path/references/reviewer-final.md +20 -2
- package/skills/gsd-path/references/reviewer-wave.md +56 -12
- package/skills/gsd-path/references/reviewer.md +3 -1
- package/skills/gsd-path/references/runtime-selection.md +9 -0
- package/skills/gsd-path/references/spec-probes.md +150 -0
- package/skills/gsd-path/scripts/_common.py +256 -2
- package/skills/gsd-path/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path/scripts/build_recovery.py +155 -58
- package/skills/gsd-path/scripts/build_state.py +274 -8
- package/skills/gsd-path/scripts/check_docs_audit.py +77 -4
- package/skills/gsd-path/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path/scripts/detect_project.py +260 -20
- package/skills/gsd-path/scripts/discussion_records.py +9 -1
- package/skills/gsd-path/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path/scripts/dispatch_driver.py +549 -53
- package/skills/gsd-path/scripts/git_guard.py +27 -8
- package/skills/gsd-path/scripts/integration.py +244 -65
- package/skills/gsd-path/scripts/isolation.py +539 -70
- package/skills/gsd-path/scripts/loop_run.py +16 -4
- package/skills/gsd-path/scripts/members.py +77 -32
- package/skills/gsd-path/scripts/model_policy.py +5 -1
- package/skills/gsd-path/scripts/path_config.py +3 -3
- package/skills/gsd-path/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path/scripts/review_findings.py +180 -44
- package/skills/gsd-path/scripts/roadmap.py +9 -1
- package/skills/gsd-path/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path/scripts/state_promote.py +15 -4
- package/skills/gsd-path/scripts/status_runtime.py +22 -4
- package/skills/gsd-path/scripts/task_context.py +14 -3
- package/skills/gsd-path/scripts/workflow_run.py +14 -4
- package/skills/gsd-path/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path/templates/docs-audit.md +5 -1
- package/skills/gsd-path/templates/final-review.md +11 -2
- package/skills/gsd-path/templates/intent.md +30 -0
- package/skills/gsd-path/templates/patch-findings.md +3 -3
- package/skills/gsd-path/templates/plan.md +17 -2
- package/skills/gsd-path/templates/research-handoff.md +14 -3
- package/skills/gsd-path/templates/state.md +2 -1
- package/skills/gsd-path/templates/task.md +1 -0
- package/skills/gsd-path/templates/wave-review.md +28 -9
- package/skills/gsd-path-build/references/build-native.md +112 -10
- package/skills/gsd-path-build/references/build-recovery.md +13 -0
- package/skills/gsd-path-build/references/coder.md +7 -3
- package/skills/gsd-path-build/references/dispatch.md +13 -0
- package/skills/gsd-path-build/references/operating-rules.md +11 -3
- package/skills/gsd-path-build/references/reviewer-final.md +20 -2
- package/skills/gsd-path-build/references/reviewer-wave.md +56 -12
- package/skills/gsd-path-build/references/reviewer.md +3 -1
- package/skills/gsd-path-build/references/runtime-selection.md +9 -0
- package/skills/gsd-path-build/references/spec-probes.md +150 -0
- package/skills/gsd-path-build/scripts/_common.py +256 -2
- package/skills/gsd-path-build/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-build/scripts/build_recovery.py +155 -58
- package/skills/gsd-path-build/scripts/build_state.py +274 -8
- package/skills/gsd-path-build/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path-build/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path-build/scripts/detect_project.py +260 -20
- package/skills/gsd-path-build/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-build/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path-build/scripts/dispatch_driver.py +549 -53
- package/skills/gsd-path-build/scripts/integration.py +244 -65
- package/skills/gsd-path-build/scripts/isolation.py +539 -70
- package/skills/gsd-path-build/scripts/loop_run.py +16 -4
- package/skills/gsd-path-build/scripts/members.py +77 -32
- package/skills/gsd-path-build/scripts/model_policy.py +5 -1
- package/skills/gsd-path-build/scripts/path_config.py +3 -3
- package/skills/gsd-path-build/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path-build/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path-build/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path-build/scripts/review_findings.py +180 -44
- package/skills/gsd-path-build/scripts/roadmap.py +9 -1
- package/skills/gsd-path-build/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path-build/scripts/state_promote.py +15 -4
- package/skills/gsd-path-build/scripts/task_context.py +14 -3
- package/skills/gsd-path-build/scripts/workflow_run.py +14 -4
- package/skills/gsd-path-build/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-build/templates/task.md +1 -0
- package/skills/gsd-path-build/templates/wave-review.md +28 -9
- package/skills/gsd-path-decide/references/dispatch.md +13 -0
- package/skills/gsd-path-decide/references/operating-rules.md +11 -3
- package/skills/gsd-path-decide/references/runtime-selection.md +9 -0
- package/skills/gsd-path-decide/scripts/_common.py +256 -2
- package/skills/gsd-path-decide/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-decide/scripts/build_recovery.py +155 -58
- package/skills/gsd-path-decide/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path-decide/scripts/detect_project.py +260 -20
- package/skills/gsd-path-decide/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-decide/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path-decide/scripts/integration.py +244 -65
- package/skills/gsd-path-decide/scripts/isolation.py +539 -70
- package/skills/gsd-path-decide/scripts/loop_run.py +16 -4
- package/skills/gsd-path-decide/scripts/members.py +77 -32
- package/skills/gsd-path-decide/scripts/model_policy.py +5 -1
- package/skills/gsd-path-decide/scripts/path_config.py +3 -3
- package/skills/gsd-path-decide/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path-decide/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path-decide/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path-decide/scripts/roadmap.py +9 -1
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path-decide/scripts/state_promote.py +15 -4
- package/skills/gsd-path-decide/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-define/SKILL.md +34 -5
- package/skills/gsd-path-define/references/build-recovery.md +13 -0
- package/skills/gsd-path-define/references/operating-rules.md +11 -3
- package/skills/gsd-path-define/references/runtime-selection.md +9 -0
- package/skills/gsd-path-define/references/spec-probes.md +150 -0
- package/skills/gsd-path-define/scripts/_common.py +256 -2
- package/skills/gsd-path-define/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-define/scripts/build_recovery.py +155 -58
- package/skills/gsd-path-define/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path-define/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path-define/scripts/detect_project.py +260 -20
- package/skills/gsd-path-define/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-define/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path-define/scripts/integration.py +244 -65
- package/skills/gsd-path-define/scripts/isolation.py +539 -70
- package/skills/gsd-path-define/scripts/loop_run.py +16 -4
- package/skills/gsd-path-define/scripts/members.py +77 -32
- package/skills/gsd-path-define/scripts/model_policy.py +5 -1
- package/skills/gsd-path-define/scripts/path_config.py +3 -3
- package/skills/gsd-path-define/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path-define/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path-define/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path-define/scripts/roadmap.py +9 -1
- package/skills/gsd-path-define/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path-define/scripts/state_promote.py +15 -4
- package/skills/gsd-path-define/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-define/templates/intent.md +30 -0
- package/skills/gsd-path-define/templates/state.md +2 -1
- package/skills/gsd-path-discuss/references/operating-rules.md +11 -3
- package/skills/gsd-path-discuss/references/runtime-selection.md +9 -0
- package/skills/gsd-path-discuss/scripts/_common.py +256 -2
- package/skills/gsd-path-discuss/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-discuss/scripts/loop_run.py +16 -4
- package/skills/gsd-path-discuss/scripts/model_policy.py +5 -1
- package/skills/gsd-path-discuss/scripts/path_config.py +3 -3
- package/skills/gsd-path-discuss/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-docs-audit/references/dispatch.md +13 -0
- package/skills/gsd-path-docs-audit/references/docs-auditor.md +5 -0
- package/skills/gsd-path-docs-audit/references/operating-rules.md +11 -3
- package/skills/gsd-path-docs-audit/references/runtime-selection.md +9 -0
- package/skills/gsd-path-docs-audit/scripts/_common.py +256 -2
- package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +155 -58
- package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +77 -4
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path-docs-audit/scripts/detect_project.py +260 -20
- package/skills/gsd-path-docs-audit/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path-docs-audit/scripts/integration.py +244 -65
- package/skills/gsd-path-docs-audit/scripts/isolation.py +539 -70
- package/skills/gsd-path-docs-audit/scripts/loop_run.py +16 -4
- package/skills/gsd-path-docs-audit/scripts/members.py +77 -32
- package/skills/gsd-path-docs-audit/scripts/model_policy.py +5 -1
- package/skills/gsd-path-docs-audit/scripts/path_config.py +3 -3
- package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path-docs-audit/scripts/roadmap.py +9 -1
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path-docs-audit/scripts/state_promote.py +15 -4
- package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-docs-audit/templates/docs-audit.md +5 -1
- package/skills/gsd-path-forensics/references/operating-rules.md +11 -3
- package/skills/gsd-path-forensics/references/runtime-selection.md +9 -0
- package/skills/gsd-path-forensics/scripts/_common.py +256 -2
- package/skills/gsd-path-forensics/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-forensics/scripts/loop_run.py +16 -4
- package/skills/gsd-path-forensics/scripts/model_policy.py +5 -1
- package/skills/gsd-path-forensics/scripts/path_config.py +3 -3
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +17 -2
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +118 -16
- package/skills/gsd-path-forensics/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-inspect/references/dispatch.md +13 -0
- package/skills/gsd-path-inspect/references/docs-auditor.md +5 -0
- package/skills/gsd-path-inspect/references/operating-rules.md +11 -3
- package/skills/gsd-path-inspect/references/runtime-selection.md +9 -0
- package/skills/gsd-path-inspect/scripts/_common.py +256 -2
- package/skills/gsd-path-inspect/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-inspect/scripts/build_recovery.py +155 -58
- package/skills/gsd-path-inspect/scripts/check_docs_audit.py +77 -4
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path-inspect/scripts/detect_project.py +260 -20
- package/skills/gsd-path-inspect/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path-inspect/scripts/integration.py +244 -65
- package/skills/gsd-path-inspect/scripts/isolation.py +539 -70
- package/skills/gsd-path-inspect/scripts/loop_run.py +16 -4
- package/skills/gsd-path-inspect/scripts/members.py +77 -32
- package/skills/gsd-path-inspect/scripts/model_policy.py +5 -1
- package/skills/gsd-path-inspect/scripts/path_config.py +3 -3
- package/skills/gsd-path-inspect/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path-inspect/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path-inspect/scripts/roadmap.py +9 -1
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path-inspect/scripts/state_promote.py +15 -4
- package/skills/gsd-path-inspect/scripts/workflow_run.py +14 -4
- package/skills/gsd-path-inspect/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-inspect/templates/docs-audit.md +5 -1
- package/skills/gsd-path-inspect/templates/state.md +2 -1
- package/skills/gsd-path-loop/references/operating-rules.md +11 -3
- package/skills/gsd-path-loop/references/runtime-selection.md +9 -0
- package/skills/gsd-path-loop/scripts/_common.py +256 -2
- package/skills/gsd-path-loop/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-loop/scripts/loop_run.py +16 -4
- package/skills/gsd-path-loop/scripts/model_policy.py +5 -1
- package/skills/gsd-path-loop/scripts/path_config.py +3 -3
- package/skills/gsd-path-loop/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-migrate/SKILL.md +8 -0
- package/skills/gsd-path-migrate/references/operating-rules.md +11 -3
- package/skills/gsd-path-migrate/references/runtime-selection.md +9 -0
- package/skills/gsd-path-migrate/scripts/_common.py +256 -2
- package/skills/gsd-path-migrate/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-migrate/scripts/loop_run.py +16 -4
- package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -0
- package/skills/gsd-path-migrate/scripts/model_policy.py +5 -1
- package/skills/gsd-path-migrate/scripts/path_config.py +3 -3
- package/skills/gsd-path-plan/SKILL.md +14 -3
- package/skills/gsd-path-plan/references/build-recovery.md +13 -0
- package/skills/gsd-path-plan/references/dispatch.md +13 -0
- package/skills/gsd-path-plan/references/operating-rules.md +11 -3
- package/skills/gsd-path-plan/references/planner.md +26 -4
- package/skills/gsd-path-plan/references/runtime-selection.md +9 -0
- package/skills/gsd-path-plan/references/spec-probes.md +150 -0
- package/skills/gsd-path-plan/scripts/_common.py +256 -2
- package/skills/gsd-path-plan/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-plan/scripts/build_recovery.py +155 -58
- package/skills/gsd-path-plan/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path-plan/scripts/detect_project.py +260 -20
- package/skills/gsd-path-plan/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-plan/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path-plan/scripts/integration.py +244 -65
- package/skills/gsd-path-plan/scripts/isolation.py +539 -70
- package/skills/gsd-path-plan/scripts/loop_run.py +16 -4
- package/skills/gsd-path-plan/scripts/members.py +77 -32
- package/skills/gsd-path-plan/scripts/model_policy.py +5 -1
- package/skills/gsd-path-plan/scripts/path_config.py +3 -3
- package/skills/gsd-path-plan/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path-plan/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path-plan/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path-plan/scripts/roadmap.py +9 -1
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path-plan/scripts/state_promote.py +15 -4
- package/skills/gsd-path-plan/scripts/workflow_run.py +14 -4
- package/skills/gsd-path-plan/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-plan/templates/patch-findings.md +3 -3
- package/skills/gsd-path-plan/templates/plan.md +17 -2
- package/skills/gsd-path-plan/templates/task.md +1 -0
- package/skills/gsd-path-research/SKILL.md +11 -0
- package/skills/gsd-path-research/references/dispatch.md +13 -0
- package/skills/gsd-path-research/references/operating-rules.md +11 -3
- package/skills/gsd-path-research/references/runtime-selection.md +9 -0
- package/skills/gsd-path-research/scripts/_common.py +256 -2
- package/skills/gsd-path-research/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-research/scripts/build_recovery.py +155 -58
- package/skills/gsd-path-research/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path-research/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path-research/scripts/detect_project.py +260 -20
- package/skills/gsd-path-research/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-research/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path-research/scripts/integration.py +244 -65
- package/skills/gsd-path-research/scripts/isolation.py +539 -70
- package/skills/gsd-path-research/scripts/loop_run.py +16 -4
- package/skills/gsd-path-research/scripts/members.py +77 -32
- package/skills/gsd-path-research/scripts/model_policy.py +5 -1
- package/skills/gsd-path-research/scripts/path_config.py +3 -3
- package/skills/gsd-path-research/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path-research/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path-research/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path-research/scripts/roadmap.py +9 -1
- package/skills/gsd-path-research/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path-research/scripts/state_promote.py +15 -4
- package/skills/gsd-path-research/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-research/templates/research-handoff.md +14 -3
- package/skills/gsd-path-roadmap/SKILL.md +16 -6
- package/skills/gsd-path-roadmap/references/dispatch.md +13 -0
- package/skills/gsd-path-roadmap/references/operating-rules.md +11 -3
- package/skills/gsd-path-roadmap/references/runtime-selection.md +9 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +256 -2
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +155 -58
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path-roadmap/scripts/detect_project.py +260 -20
- package/skills/gsd-path-roadmap/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path-roadmap/scripts/integration.py +244 -65
- package/skills/gsd-path-roadmap/scripts/isolation.py +539 -70
- package/skills/gsd-path-roadmap/scripts/loop_run.py +16 -4
- package/skills/gsd-path-roadmap/scripts/members.py +77 -32
- package/skills/gsd-path-roadmap/scripts/model_policy.py +5 -1
- package/skills/gsd-path-roadmap/scripts/path_config.py +3 -3
- package/skills/gsd-path-roadmap/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path-roadmap/scripts/roadmap.py +9 -1
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path-roadmap/scripts/state_promote.py +15 -4
- package/skills/gsd-path-roadmap/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-ship/SKILL.md +32 -5
- package/skills/gsd-path-ship/references/dispatch.md +13 -0
- package/skills/gsd-path-ship/references/operating-rules.md +11 -3
- package/skills/gsd-path-ship/references/reviewer-final.md +20 -2
- package/skills/gsd-path-ship/references/reviewer-wave.md +56 -12
- package/skills/gsd-path-ship/references/reviewer.md +3 -1
- package/skills/gsd-path-ship/references/runtime-selection.md +9 -0
- package/skills/gsd-path-ship/references/spec-probes.md +150 -0
- package/skills/gsd-path-ship/scripts/_common.py +256 -2
- package/skills/gsd-path-ship/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-ship/scripts/build_recovery.py +155 -58
- package/skills/gsd-path-ship/scripts/build_state.py +274 -8
- package/skills/gsd-path-ship/scripts/check_handoffs.py +635 -57
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +293 -32
- package/skills/gsd-path-ship/scripts/detect_project.py +260 -20
- package/skills/gsd-path-ship/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-ship/scripts/discussion_validate.py +93 -35
- package/skills/gsd-path-ship/scripts/integration.py +244 -65
- package/skills/gsd-path-ship/scripts/isolation.py +539 -70
- package/skills/gsd-path-ship/scripts/loop_run.py +16 -4
- package/skills/gsd-path-ship/scripts/members.py +77 -32
- package/skills/gsd-path-ship/scripts/model_policy.py +5 -1
- package/skills/gsd-path-ship/scripts/path_config.py +3 -3
- package/skills/gsd-path-ship/scripts/pipeline_git.py +131 -42
- package/skills/gsd-path-ship/scripts/pipeline_state.py +139 -39
- package/skills/gsd-path-ship/scripts/promote_lookahead.py +3 -3
- package/skills/gsd-path-ship/scripts/roadmap.py +9 -1
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +39 -36
- package/skills/gsd-path-ship/scripts/state_promote.py +15 -4
- package/skills/gsd-path-ship/scripts/workflow_run.py +14 -4
- package/skills/gsd-path-ship/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-ship/templates/final-review.md +11 -2
- package/skills/gsd-path-ship/templates/patch-findings.md +3 -3
- package/skills/gsd-path-ship/templates/wave-review.md +28 -9
- package/skills/gsd-path-undo/references/operating-rules.md +11 -3
- package/skills/gsd-path-undo/references/runtime-selection.md +9 -0
- package/skills/gsd-path-undo/scripts/_common.py +256 -2
- package/skills/gsd-path-undo/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-undo/scripts/loop_run.py +16 -4
- package/skills/gsd-path-undo/scripts/model_policy.py +5 -1
- package/skills/gsd-path-undo/scripts/path_config.py +3 -3
- package/skills/gsd-path-undo/scripts/pipeline_undo.py +118 -16
- package/skills/gsd-path-undo/scripts/worktree_paths.py +54 -2
- package/skills/path/DEFINE.md +34 -5
- package/skills/path/PLAN.md +14 -3
- package/skills/path/RESEARCH.md +11 -0
- package/skills/path/ROADMAP.md +16 -6
- package/skills/path/SHIP.md +32 -5
- package/skills/path/SKILL.md +52 -24
- package/skills/path/references/build-native.md +112 -10
- package/skills/path/references/build-recovery.md +13 -0
- package/skills/path/references/coder.md +7 -3
- package/skills/path/references/dispatch.md +13 -0
- package/skills/path/references/docs-auditor.md +5 -0
- package/skills/path/references/operating-rules.md +11 -3
- package/skills/path/references/planner.md +26 -4
- package/skills/path/references/reviewer-final.md +20 -2
- package/skills/path/references/reviewer-wave.md +56 -12
- package/skills/path/references/reviewer.md +3 -1
- package/skills/path/references/runtime-selection.md +9 -0
- package/skills/path/references/spec-probes.md +150 -0
- package/skills/path/scripts/_common.py +256 -2
- package/skills/path/scripts/archive_milestone.py +14 -13
- package/skills/path/scripts/build_recovery.py +155 -58
- package/skills/path/scripts/build_state.py +274 -8
- package/skills/path/scripts/check_docs_audit.py +77 -4
- package/skills/path/scripts/check_handoffs.py +635 -57
- package/skills/path/scripts/check_task_briefs.py +293 -32
- package/skills/path/scripts/detect_project.py +260 -20
- package/skills/path/scripts/discussion_records.py +9 -1
- package/skills/path/scripts/discussion_validate.py +93 -35
- package/skills/path/scripts/dispatch_driver.py +549 -53
- package/skills/path/scripts/git_guard.py +27 -8
- package/skills/path/scripts/integration.py +244 -65
- package/skills/path/scripts/isolation.py +539 -70
- package/skills/path/scripts/loop_run.py +16 -4
- package/skills/path/scripts/members.py +77 -32
- package/skills/path/scripts/model_policy.py +5 -1
- package/skills/path/scripts/path_config.py +3 -3
- package/skills/path/scripts/pipeline_git.py +131 -42
- package/skills/path/scripts/pipeline_state.py +139 -39
- package/skills/path/scripts/promote_lookahead.py +3 -3
- package/skills/path/scripts/review_findings.py +180 -44
- package/skills/path/scripts/roadmap.py +9 -1
- package/skills/path/scripts/state_checkpoint.py +39 -36
- package/skills/path/scripts/state_promote.py +15 -4
- package/skills/path/scripts/status_runtime.py +22 -4
- package/skills/path/scripts/task_context.py +14 -3
- package/skills/path/scripts/workflow_run.py +14 -4
- package/skills/path/scripts/worktree_paths.py +54 -2
- package/skills/path/templates/docs-audit.md +5 -1
- package/skills/path/templates/final-review.md +11 -2
- package/skills/path/templates/intent.md +30 -0
- package/skills/path/templates/patch-findings.md +3 -3
- package/skills/path/templates/plan.md +17 -2
- package/skills/path/templates/research-handoff.md +14 -3
- package/skills/path/templates/state.md +2 -1
- package/skills/path/templates/task.md +1 -0
- package/skills/path/templates/wave-review.md +28 -9
- 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-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-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# gsd-path project runtime
|
|
2
|
-
"""Guard
|
|
2
|
+
"""Guard return to Define or Plan using a committed recovery base."""
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
@@ -17,7 +17,13 @@ EVENTS = {
|
|
|
17
17
|
"define": "build intent corrections requested",
|
|
18
18
|
"plan": "build plan repair requested",
|
|
19
19
|
}
|
|
20
|
+
PLAN_INTENT_EVENT = "plan intent corrections requested"
|
|
20
21
|
MARKER = "build recovery: "
|
|
22
|
+
RECOVERY_KEYS = frozenset({"base", "branch", "kind", "source"})
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Only the value of a cap line is exempt; a comment or a line separator after it stays in the compared text.
|
|
26
|
+
CAP_VALUE = re.compile(r"(?m)^(-[ \t]*(?:max_review_cycles|wave_budget):)[ \t\w.,$/:]*")
|
|
21
27
|
|
|
22
28
|
|
|
23
29
|
def runtime():
|
|
@@ -36,9 +42,51 @@ def checkpoint_runtime():
|
|
|
36
42
|
return state_checkpoint
|
|
37
43
|
|
|
38
44
|
|
|
45
|
+
def _parse_recovery_record(value: object) -> dict:
|
|
46
|
+
if (
|
|
47
|
+
not isinstance(value, dict)
|
|
48
|
+
or not {"base", "branch", "kind"}.issubset(value)
|
|
49
|
+
or not set(value).issubset(RECOVERY_KEYS)
|
|
50
|
+
or not isinstance(value["base"], str)
|
|
51
|
+
or not re.fullmatch(r"[0-9a-f]{40}", value["base"])
|
|
52
|
+
):
|
|
53
|
+
raise runtime().PipelineStateError("invalid build recovery record")
|
|
54
|
+
source = value.get("source", "build")
|
|
55
|
+
if source not in {"build", "plan"}:
|
|
56
|
+
raise runtime().PipelineStateError("invalid build recovery record")
|
|
57
|
+
if source == "plan":
|
|
58
|
+
if value["kind"] != "define":
|
|
59
|
+
raise runtime().PipelineStateError("invalid build recovery record")
|
|
60
|
+
elif value["kind"] not in EVENTS:
|
|
61
|
+
raise runtime().PipelineStateError("invalid build recovery record")
|
|
62
|
+
return {**value, "source": source}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _recovery_base_proves_milestone(before, current, source: str, active: bool = True) -> bool:
|
|
66
|
+
if before.branch != current.branch:
|
|
67
|
+
return False
|
|
68
|
+
if before.milestone != current.milestone and (
|
|
69
|
+
# Issue #374: a legacy base records milestone null when define never
|
|
70
|
+
# named it. The owner names it at ship, after the build resumed, so
|
|
71
|
+
# only a closed build recovery accepts an unset base milestone; the
|
|
72
|
+
# bound branch already encodes M00N. A milestone named by the base
|
|
73
|
+
# must still equal the current one.
|
|
74
|
+
active or source != "build" or before.milestone is not None
|
|
75
|
+
):
|
|
76
|
+
return False
|
|
77
|
+
if source == "plan":
|
|
78
|
+
return (before.phase, before.status) in {("plan", "active"), ("plan", "blocked")}
|
|
79
|
+
return (before.phase, before.status) == ("build", "blocked")
|
|
80
|
+
|
|
81
|
+
|
|
39
82
|
def context(repo: Path, text: str | None = None) -> dict | None:
|
|
40
83
|
state = runtime()
|
|
41
|
-
|
|
84
|
+
try:
|
|
85
|
+
current, loaded, _ = state.load_state(repo)
|
|
86
|
+
except state.PipelineStateError as error:
|
|
87
|
+
if "STATE.md must be a real file" not in str(error):
|
|
88
|
+
raise
|
|
89
|
+
return None
|
|
42
90
|
text = loaded if text is None else text
|
|
43
91
|
recovery = None
|
|
44
92
|
for line in text.splitlines():
|
|
@@ -47,26 +95,26 @@ def context(repo: Path, text: str | None = None) -> dict | None:
|
|
|
47
95
|
event = line.split(" — ", 2)[-1]
|
|
48
96
|
if event.startswith(MARKER):
|
|
49
97
|
try:
|
|
50
|
-
value = json.loads(event[len(MARKER):])
|
|
98
|
+
value = _parse_recovery_record(json.loads(event[len(MARKER):]))
|
|
51
99
|
except ValueError as error:
|
|
52
100
|
raise state.PipelineStateError("invalid build recovery record") from error
|
|
53
|
-
if (not isinstance(value, dict) or set(value) != {"base", "branch", "kind"}
|
|
54
|
-
or value["kind"] not in EVENTS
|
|
55
|
-
or not isinstance(value["base"], str)
|
|
56
|
-
or not re.fullmatch(r"[0-9a-f]{40}", value["base"])):
|
|
57
|
-
raise state.PipelineStateError("invalid build recovery record")
|
|
58
101
|
recovery = {**value, "active": True} if value["branch"] == current.branch else None
|
|
59
|
-
elif
|
|
60
|
-
recovery
|
|
102
|
+
elif recovery and recovery.get("active"):
|
|
103
|
+
source = recovery.get("source", "build")
|
|
104
|
+
if source == "build" and event == "build started":
|
|
105
|
+
recovery["active"] = False
|
|
106
|
+
elif source == "plan" and event == "plan approved":
|
|
107
|
+
recovery["active"] = False
|
|
61
108
|
if recovery:
|
|
62
109
|
if not state._is_ancestor(repo, recovery["base"], "HEAD"):
|
|
63
110
|
raise state.PipelineStateError("build recovery base is not an ancestor of HEAD")
|
|
64
|
-
|
|
65
|
-
if
|
|
111
|
+
base_text = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/STATE.md")
|
|
112
|
+
if base_text is None:
|
|
66
113
|
raise state.PipelineStateError("build recovery base has no STATE.md")
|
|
67
|
-
before = state._state_from_text(
|
|
68
|
-
if
|
|
69
|
-
|
|
114
|
+
before = state._state_from_text(base_text)
|
|
115
|
+
if not _recovery_base_proves_milestone(
|
|
116
|
+
before, current, recovery.get("source", "build"), recovery["active"]
|
|
117
|
+
):
|
|
70
118
|
raise state.PipelineStateError("build recovery base does not prove this blocked milestone")
|
|
71
119
|
return recovery
|
|
72
120
|
|
|
@@ -108,18 +156,20 @@ def settled_tasks(repo: Path) -> dict[str, tuple[str, dict]]:
|
|
|
108
156
|
return tasks
|
|
109
157
|
|
|
110
158
|
|
|
111
|
-
def
|
|
159
|
+
def _begin_common(repo: Path, before, event: str) -> str:
|
|
112
160
|
state = runtime()
|
|
113
|
-
if (before.phase, before.status) != ("build", "blocked") or after.phase not in EVENTS:
|
|
114
|
-
raise state.PipelineStateError("build recovery requires build/blocked")
|
|
115
|
-
if event != EVENTS[after.phase]:
|
|
116
|
-
raise state.PipelineStateError(f"state transition requires event: {EVENTS[after.phase]}")
|
|
117
161
|
if before.archive is not None or before.branch != state._current_branch(repo):
|
|
118
162
|
raise state.PipelineStateError("build recovery requires the active bound branch")
|
|
119
163
|
if state._worktree_changes(repo):
|
|
120
164
|
raise state.PipelineStateError("checkpoint the blocked build before recovery")
|
|
121
165
|
if any(state.transaction_journals(repo).values()):
|
|
122
166
|
raise state.PipelineStateError("finish pending transactions before build recovery")
|
|
167
|
+
state._read_real_file(repo / ".project/intent/INTENT.md", "INTENT.md")
|
|
168
|
+
return state._run_git(repo, "rev-parse", "HEAD").stdout.strip()
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _settle_build_dispatch(repo: Path) -> None:
|
|
172
|
+
state = runtime()
|
|
123
173
|
records = records_root(repo)
|
|
124
174
|
for directory in {path.parent.parent for path in records.rglob("attempt-*/state.json")}:
|
|
125
175
|
latest = max(directory.glob("attempt-*/state.json"), key=lambda path: int(path.parent.name.split("-")[1]))
|
|
@@ -128,19 +178,49 @@ def begin(repo: Path, before, after, event: str) -> dict:
|
|
|
128
178
|
or (record.get("worktree") and record.get("branch") and not record.get("cleanup_complete"))
|
|
129
179
|
or record.get("outcome") not in {"landed", "collected", "blocked", "resolved", "redispatched"}):
|
|
130
180
|
raise state.PipelineStateError(f"settle dispatch records before recovery: {latest}")
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _verify_done_tasks(repo: Path, tasks: dict[str, tuple[str, dict]], base: str) -> None:
|
|
184
|
+
state = runtime()
|
|
135
185
|
done = [repo / ".project/tasks" / name for name, (_, fields) in tasks.items()
|
|
136
186
|
if fields["status"] == "done"]
|
|
137
|
-
if done:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
187
|
+
if not done:
|
|
188
|
+
return
|
|
189
|
+
if __package__:
|
|
190
|
+
from scripts.isolation import verify_landed_task_files
|
|
191
|
+
else:
|
|
192
|
+
from isolation import verify_landed_task_files
|
|
193
|
+
verify_landed_task_files(repo, done, ".project/tasks", base)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def begin(repo: Path, before, after, event: str) -> dict:
|
|
197
|
+
state = runtime()
|
|
198
|
+
if (before.phase, before.status) == ("build", "blocked") and after.phase in EVENTS:
|
|
199
|
+
if event != EVENTS[after.phase]:
|
|
200
|
+
raise state.PipelineStateError(f"state transition requires event: {EVENTS[after.phase]}")
|
|
201
|
+
_settle_build_dispatch(repo)
|
|
202
|
+
tasks = settled_tasks(repo)
|
|
203
|
+
state._read_real_file(repo / ".project/plan/PLAN.md", "PLAN.md")
|
|
204
|
+
base = _begin_common(repo, before, event)
|
|
205
|
+
_verify_done_tasks(repo, tasks, base)
|
|
206
|
+
return {"kind": after.phase, "base": base, "branch": before.branch, "source": "build"}
|
|
207
|
+
if (
|
|
208
|
+
before.phase == "plan"
|
|
209
|
+
and before.status in {"active", "blocked"}
|
|
210
|
+
and (after.phase, after.status) == ("define", "active")
|
|
211
|
+
):
|
|
212
|
+
if event != PLAN_INTENT_EVENT:
|
|
213
|
+
raise state.PipelineStateError(f"state transition requires event: {PLAN_INTENT_EVENT}")
|
|
214
|
+
state._read_real_file(repo / ".project/plan/PLAN.md", "PLAN.md")
|
|
215
|
+
tasks_dir = repo / ".project/tasks"
|
|
216
|
+
tasks = settled_tasks(repo) if tasks_dir.is_dir() and any(
|
|
217
|
+
path.is_file() and not state._common.is_ignored_junk(path)
|
|
218
|
+
for path in tasks_dir.iterdir()
|
|
219
|
+
) else {}
|
|
220
|
+
base = _begin_common(repo, before, event)
|
|
221
|
+
_verify_done_tasks(repo, tasks, base)
|
|
222
|
+
return {"kind": "define", "base": base, "branch": before.branch, "source": "plan"}
|
|
223
|
+
raise state.PipelineStateError("build recovery requires build/blocked")
|
|
144
224
|
|
|
145
225
|
|
|
146
226
|
def records_root(repo: Path) -> Path:
|
|
@@ -224,61 +304,78 @@ def validate_plan(repo: Path) -> None:
|
|
|
224
304
|
intent = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/intent/INTENT.md")
|
|
225
305
|
if state._read_real_file(repo / ".project/intent/INTENT.md", "INTENT.md") != intent:
|
|
226
306
|
raise state.PipelineStateError("plan repair cannot change approved intent")
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
if match and int(match[1]) not in unchanged:
|
|
231
|
-
raise state.PipelineStateError("changed wave must be reviewed after build resumes")
|
|
307
|
+
if stale_review_reasons(repo, recovery, _review_waves(repo / ".project/review")):
|
|
308
|
+
raise state.PipelineStateError("changed wave must be reviewed after build resumes")
|
|
309
|
+
|
|
232
310
|
|
|
311
|
+
def _review_waves(directory: Path) -> set[int]:
|
|
312
|
+
return {int(match[1]) for path in directory.iterdir()
|
|
313
|
+
if (match := re.match(r"wave-([1-9]\d*)[.]", path.name))}
|
|
233
314
|
|
|
234
|
-
|
|
235
|
-
|
|
315
|
+
|
|
316
|
+
def stale_review_reasons(repo: Path, recovery: dict, waves: set[int]) -> dict[int, str]:
|
|
317
|
+
"""Say why a wave's preserved review is stale; a wave not returned has matching intent, plan, and task bytes."""
|
|
236
318
|
state = runtime()
|
|
237
319
|
project = repo / ".project"
|
|
238
320
|
old_intent = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/intent/INTENT.md")
|
|
239
321
|
if state._read_real_file(project / "intent/INTENT.md", "INTENT.md") != old_intent:
|
|
240
|
-
return
|
|
322
|
+
return dict.fromkeys(waves, "INTENT.md changed")
|
|
241
323
|
old_plan = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/plan/PLAN.md") or ""
|
|
242
324
|
new_plan = state._read_real_file(project / "plan/PLAN.md", "PLAN.md")
|
|
325
|
+
panel_lines = r"(?m)^-[ \t]*review_panel:.*$"
|
|
326
|
+
if re.findall(panel_lines, old_plan) != re.findall(panel_lines, new_plan):
|
|
327
|
+
return dict.fromkeys(waves, "PLAN.md `review_panel` setting changed")
|
|
243
328
|
|
|
244
329
|
def sections(text):
|
|
245
330
|
return {match[1]: match[2] for match in re.finditer(
|
|
246
331
|
r"(?ms)^## ([^\n]+)\n(.*?)(?=^## |\Z)", text,
|
|
247
332
|
)}
|
|
248
333
|
|
|
334
|
+
def governing(plan):
|
|
335
|
+
# Non-wave settings govern every wave, including coverage and surfaces. Caps only bound
|
|
336
|
+
# later work and Dependency notes are prose, so neither changes what a passed review judged.
|
|
337
|
+
return {title: CAP_VALUE.sub(r"\1", body) if title == "Config" else body
|
|
338
|
+
for title, body in plan.items()
|
|
339
|
+
if not title.startswith("Wave ") and title != "Dependency notes"}
|
|
340
|
+
|
|
249
341
|
old_sections, new_sections = sections(old_plan), sections(new_plan)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
342
|
+
old_governing, new_governing = governing(old_sections), governing(new_sections)
|
|
343
|
+
for title in sorted(old_governing.keys() | new_governing.keys()):
|
|
344
|
+
if old_governing.get(title) != new_governing.get(title):
|
|
345
|
+
return dict.fromkeys(waves, f"PLAN.md section `{title}` changed")
|
|
254
346
|
old_tasks = checkpoint_runtime()._task_contracts_at(repo, recovery["base"], ".project/tasks")
|
|
255
347
|
current = settled_tasks(repo)
|
|
256
|
-
|
|
257
|
-
for
|
|
258
|
-
|
|
259
|
-
if not
|
|
348
|
+
reasons = {}
|
|
349
|
+
for wave in waves:
|
|
350
|
+
section = {title: body for title, body in new_sections.items() if re.match(rf"Wave {wave}\b", title)}
|
|
351
|
+
if not section or any(old_sections.get(title) != body for title, body in section.items()):
|
|
352
|
+
reasons[wave] = f"PLAN.md section `Wave {wave}` changed"
|
|
260
353
|
continue
|
|
261
|
-
wave = match[1]
|
|
262
354
|
before = {Path(path).name: text for path, text in old_tasks.values()
|
|
263
|
-
if task_fields(text, path).get("wave") == wave}
|
|
264
|
-
after = {name: text for name, (text, fields) in current.items() if fields.get("wave") == wave}
|
|
265
|
-
if before
|
|
266
|
-
|
|
267
|
-
return
|
|
355
|
+
if task_fields(text, path).get("wave") == str(wave)}
|
|
356
|
+
after = {name: text for name, (text, fields) in current.items() if fields.get("wave") == str(wave)}
|
|
357
|
+
if not before or before != after:
|
|
358
|
+
reasons[wave] = f"wave {wave} task contracts changed"
|
|
359
|
+
return reasons
|
|
268
360
|
|
|
269
361
|
|
|
270
|
-
def restore_unchanged_reviews(repo: Path) -> None:
|
|
362
|
+
def restore_unchanged_reviews(repo: Path) -> dict | None:
|
|
363
|
+
"""Restore reusable wave reviews and report every preserved wave, so a dropped review is visible at approval."""
|
|
271
364
|
recovery = context(repo)
|
|
272
365
|
if not recovery or not recovery["active"]:
|
|
273
|
-
return
|
|
274
|
-
|
|
275
|
-
|
|
366
|
+
return None
|
|
367
|
+
backup = review_backup(repo, recovery)
|
|
368
|
+
waves = _review_waves(backup)
|
|
369
|
+
reasons = stale_review_reasons(repo, recovery, waves)
|
|
370
|
+
for path in backup.iterdir():
|
|
276
371
|
match = re.match(r"wave-([1-9]\d*)[.]", path.name)
|
|
277
|
-
if match and int(match[1]) in
|
|
372
|
+
if match and int(match[1]) not in reasons:
|
|
278
373
|
destination = repo / ".project/review" / path.name
|
|
279
374
|
content = path.read_text(encoding="utf-8")
|
|
280
375
|
if not destination.exists() or runtime()._read_real_file(destination, destination.name) != content:
|
|
281
376
|
runtime()._atomic_write(destination, content)
|
|
377
|
+
return {"restored": sorted(waves - reasons.keys()),
|
|
378
|
+
"review_again": [{"wave": wave, "reason": reasons[wave]} for wave in sorted(reasons)]}
|
|
282
379
|
|
|
283
380
|
|
|
284
381
|
def inventory_checkpoint(repo: Path, head: str) -> bool:
|