@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
|
@@ -9,6 +9,7 @@ sys.dont_write_bytecode = True
|
|
|
9
9
|
|
|
10
10
|
import json
|
|
11
11
|
import re
|
|
12
|
+
import contextlib
|
|
12
13
|
from pathlib import Path, PurePosixPath
|
|
13
14
|
from typing import Optional
|
|
14
15
|
|
|
@@ -300,7 +301,9 @@ def discard_unpublished_stale_integration(
|
|
|
300
301
|
project, "merge-base", "--is-ancestor", merge_commit, remote_default_sha
|
|
301
302
|
)
|
|
302
303
|
if published.returncode == 0:
|
|
303
|
-
raise ArchiveError(
|
|
304
|
+
raise ArchiveError(
|
|
305
|
+
f"stale integration merge is already published on origin/{default_name}"
|
|
306
|
+
)
|
|
304
307
|
if published.returncode != 1:
|
|
305
308
|
archive_milestone.require_git_success(published, "inspect stale integration publication")
|
|
306
309
|
|
|
@@ -658,9 +661,10 @@ def require_pull_request_identity(
|
|
|
658
661
|
pull: dict,
|
|
659
662
|
branch: str,
|
|
660
663
|
ship_commit: str,
|
|
664
|
+
base: str = "main",
|
|
661
665
|
) -> None:
|
|
662
|
-
if pull["base"].get("ref") !=
|
|
663
|
-
raise ArchiveError("GitHub pull request base is not
|
|
666
|
+
if pull["base"].get("ref") != base:
|
|
667
|
+
raise ArchiveError(f"GitHub pull request base is not {base}")
|
|
664
668
|
if pull["head"].get("ref") != branch:
|
|
665
669
|
raise ArchiveError("GitHub pull request head is not the bound branch")
|
|
666
670
|
if pull["head"].get("sha") != ship_commit:
|
|
@@ -669,7 +673,9 @@ def require_pull_request_identity(
|
|
|
669
673
|
raise ArchiveError("GitHub pull request head repository is not origin")
|
|
670
674
|
|
|
671
675
|
|
|
672
|
-
def find_pull_request(
|
|
676
|
+
def find_pull_request(
|
|
677
|
+
repository: str, branch: str, ship_commit: str, base: str = "main"
|
|
678
|
+
) -> Optional[dict]:
|
|
673
679
|
pulls = require_pull_request_pages(
|
|
674
680
|
github_api_json(
|
|
675
681
|
f"repos/{repository}/pulls",
|
|
@@ -678,7 +684,7 @@ def find_pull_request(repository: str, branch: str, ship_commit: str) -> Optiona
|
|
|
678
684
|
"-f",
|
|
679
685
|
"state=all",
|
|
680
686
|
"-f",
|
|
681
|
-
"base=
|
|
687
|
+
f"base={base}",
|
|
682
688
|
"--paginate",
|
|
683
689
|
"--slurp",
|
|
684
690
|
)
|
|
@@ -686,7 +692,7 @@ def find_pull_request(repository: str, branch: str, ship_commit: str) -> Optiona
|
|
|
686
692
|
pulls = [
|
|
687
693
|
pull
|
|
688
694
|
for pull in pulls
|
|
689
|
-
if pull["base"].get("ref") ==
|
|
695
|
+
if pull["base"].get("ref") == base
|
|
690
696
|
and (
|
|
691
697
|
pull["head"].get("sha") == ship_commit
|
|
692
698
|
or (
|
|
@@ -696,17 +702,19 @@ def find_pull_request(repository: str, branch: str, ship_commit: str) -> Optiona
|
|
|
696
702
|
)
|
|
697
703
|
]
|
|
698
704
|
if len(pulls) > 1:
|
|
699
|
-
raise ArchiveError("multiple pull requests target
|
|
705
|
+
raise ArchiveError(f"multiple pull requests target {base} from the ship commit")
|
|
700
706
|
if not pulls:
|
|
701
707
|
return None
|
|
702
708
|
pull = pulls[0]
|
|
703
|
-
require_pull_request_identity(repository, pull, branch, ship_commit)
|
|
709
|
+
require_pull_request_identity(repository, pull, branch, ship_commit, base)
|
|
704
710
|
return pull
|
|
705
711
|
|
|
706
712
|
|
|
707
|
-
def pull_request_body(
|
|
713
|
+
def pull_request_body(
|
|
714
|
+
archive_path: str, ship_commit: str, branch: str, base: str = "main"
|
|
715
|
+
) -> str:
|
|
708
716
|
return (
|
|
709
|
-
f"{integrate_commit_body(archive_path, ship_commit,
|
|
717
|
+
f"{integrate_commit_body(archive_path, ship_commit, base, branch)}"
|
|
710
718
|
f"\n\n---\n{PR_CREDIT_LINE}"
|
|
711
719
|
)
|
|
712
720
|
|
|
@@ -718,8 +726,12 @@ def update_pull_request_body(
|
|
|
718
726
|
ship_commit: str,
|
|
719
727
|
branch: str,
|
|
720
728
|
expected_body: Optional[str] = None,
|
|
729
|
+
base: str = "main",
|
|
721
730
|
) -> dict:
|
|
722
|
-
expected =
|
|
731
|
+
expected = (
|
|
732
|
+
expected_body if expected_body is not None
|
|
733
|
+
else pull_request_body(archive_path, ship_commit, branch, base)
|
|
734
|
+
)
|
|
723
735
|
current = pull.get("body")
|
|
724
736
|
if isinstance(current, str) and current.rstrip().endswith(PR_CREDIT_LINE):
|
|
725
737
|
return pull
|
|
@@ -731,7 +743,7 @@ def update_pull_request_body(
|
|
|
731
743
|
f"body={expected}",
|
|
732
744
|
)
|
|
733
745
|
updated = require_pull_request_shape(response)
|
|
734
|
-
require_pull_request_identity(repository, updated, branch, ship_commit)
|
|
746
|
+
require_pull_request_identity(repository, updated, branch, ship_commit, base)
|
|
735
747
|
if updated.get("body") != expected:
|
|
736
748
|
raise ArchiveError("updated GitHub pull request is missing the credit footer")
|
|
737
749
|
return updated
|
|
@@ -743,23 +755,24 @@ def create_pull_request(
|
|
|
743
755
|
archive_path: str,
|
|
744
756
|
archive_name: str,
|
|
745
757
|
ship_commit: str,
|
|
758
|
+
base: str = "main",
|
|
746
759
|
) -> dict:
|
|
747
|
-
body = pull_request_body(archive_path, ship_commit, branch)
|
|
760
|
+
body = pull_request_body(archive_path, ship_commit, branch, base)
|
|
748
761
|
response = github_api_json(
|
|
749
762
|
f"repos/{repository}/pulls",
|
|
750
763
|
"--method",
|
|
751
764
|
"POST",
|
|
752
765
|
"-f",
|
|
753
|
-
f"title={integrate_subject(archive_name,
|
|
766
|
+
f"title={integrate_subject(archive_name, base)}",
|
|
754
767
|
"-f",
|
|
755
768
|
f"head={branch}",
|
|
756
769
|
"-f",
|
|
757
|
-
"base=
|
|
770
|
+
f"base={base}",
|
|
758
771
|
"-f",
|
|
759
772
|
f"body={body}",
|
|
760
773
|
)
|
|
761
774
|
pull = require_pull_request_shape(response)
|
|
762
|
-
require_pull_request_identity(repository, pull, branch, ship_commit)
|
|
775
|
+
require_pull_request_identity(repository, pull, branch, ship_commit, base)
|
|
763
776
|
return pull
|
|
764
777
|
|
|
765
778
|
|
|
@@ -797,6 +810,36 @@ def pull_request_tag_metadata(project: Path, tag_ref: str) -> dict[str, str]:
|
|
|
797
810
|
return fields
|
|
798
811
|
|
|
799
812
|
|
|
813
|
+
def external_landing_tag_message(
|
|
814
|
+
archive_name: str, ship_commit: str, landing: str
|
|
815
|
+
) -> str:
|
|
816
|
+
return (
|
|
817
|
+
f"milestone {archive_name}\n\n"
|
|
818
|
+
"Mode: external-landing\n"
|
|
819
|
+
f"Ship: {ship_commit}\n"
|
|
820
|
+
f"Landing: {landing}"
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
def external_landing_tag_metadata(project: Path, tag_ref: str) -> dict[str, str]:
|
|
825
|
+
message = archive_milestone.require_git_success(
|
|
826
|
+
run_git(project, "for-each-ref", "--format=%(contents)", tag_ref),
|
|
827
|
+
"inspect external-landing milestone tag",
|
|
828
|
+
)
|
|
829
|
+
fields: dict[str, str] = {}
|
|
830
|
+
for line in message.splitlines():
|
|
831
|
+
match = re.fullmatch(r"(Mode|Ship|Landing): (.+)", line)
|
|
832
|
+
if match:
|
|
833
|
+
if match.group(1) in fields:
|
|
834
|
+
raise ArchiveError("external-landing milestone tag repeats metadata")
|
|
835
|
+
fields[match.group(1)] = match.group(2)
|
|
836
|
+
if set(fields) != {"Mode", "Ship", "Landing"}:
|
|
837
|
+
raise ArchiveError("external-landing milestone tag is missing metadata")
|
|
838
|
+
if fields["Mode"] != "external-landing":
|
|
839
|
+
raise ArchiveError("external-landing milestone tag has the wrong mode")
|
|
840
|
+
return fields
|
|
841
|
+
|
|
842
|
+
|
|
800
843
|
def require_pull_request_merge(
|
|
801
844
|
project: Path,
|
|
802
845
|
merge_commit: str,
|
|
@@ -819,7 +862,9 @@ def require_pull_request_merge(
|
|
|
819
862
|
"inspect remote-default first-parent history",
|
|
820
863
|
).splitlines()
|
|
821
864
|
if merge_commit not in first_parent:
|
|
822
|
-
raise ArchiveError(
|
|
865
|
+
raise ArchiveError(
|
|
866
|
+
f"pull-request merge is not on {remote_default} first-parent history"
|
|
867
|
+
)
|
|
823
868
|
|
|
824
869
|
|
|
825
870
|
def require_pull_request_merge_provenance(
|
|
@@ -936,7 +981,8 @@ def integrate_pull_request(
|
|
|
936
981
|
raise ArchiveError("pull-request integration requires a bound branch")
|
|
937
982
|
require_github_authentication()
|
|
938
983
|
repository = github_repository(project)
|
|
939
|
-
|
|
984
|
+
base = state.default_branch
|
|
985
|
+
pull = find_pull_request(repository, branch, ship_commit, base)
|
|
940
986
|
if pull is None:
|
|
941
987
|
publish_bound_branch(project, branch, ship_commit)
|
|
942
988
|
pull = create_pull_request(
|
|
@@ -945,6 +991,7 @@ def integrate_pull_request(
|
|
|
945
991
|
archive_path,
|
|
946
992
|
archive_name,
|
|
947
993
|
ship_commit,
|
|
994
|
+
base,
|
|
948
995
|
)
|
|
949
996
|
elif pull["merged_at"] is None:
|
|
950
997
|
if pull["state"] != "open":
|
|
@@ -955,6 +1002,7 @@ def integrate_pull_request(
|
|
|
955
1002
|
archive_path,
|
|
956
1003
|
ship_commit,
|
|
957
1004
|
branch,
|
|
1005
|
+
base=base,
|
|
958
1006
|
)
|
|
959
1007
|
if pull["merged_at"] is None:
|
|
960
1008
|
if pull["state"] != "open":
|
|
@@ -985,6 +1033,7 @@ def integrate_pull_request(
|
|
|
985
1033
|
archive_path,
|
|
986
1034
|
ship_commit,
|
|
987
1035
|
branch,
|
|
1036
|
+
base=base,
|
|
988
1037
|
)
|
|
989
1038
|
tag_name = f"milestone/{archive_name}"
|
|
990
1039
|
message = pull_request_tag_message(
|
|
@@ -1021,6 +1070,83 @@ def integrate_pull_request(
|
|
|
1021
1070
|
return validate_integrated(project, archive_milestone.milestone_slug(state))
|
|
1022
1071
|
|
|
1023
1072
|
|
|
1073
|
+
def integrate_external_landing(
|
|
1074
|
+
project: Path,
|
|
1075
|
+
state: PipelineState,
|
|
1076
|
+
archive_path: str,
|
|
1077
|
+
archive_name: str,
|
|
1078
|
+
ship_commit: str,
|
|
1079
|
+
remote_default: str,
|
|
1080
|
+
default_name: str,
|
|
1081
|
+
bound_branch: str,
|
|
1082
|
+
) -> dict:
|
|
1083
|
+
if state.branch is None:
|
|
1084
|
+
raise ArchiveError("external-landing integration requires a bound branch")
|
|
1085
|
+
publishable_bound_branch(project, bound_branch, ship_commit)
|
|
1086
|
+
contains_ship = run_git(
|
|
1087
|
+
project, "merge-base", "--is-ancestor", ship_commit, remote_default
|
|
1088
|
+
)
|
|
1089
|
+
if contains_ship.returncode == 1:
|
|
1090
|
+
publish_bound_branch(project, bound_branch, ship_commit)
|
|
1091
|
+
return {
|
|
1092
|
+
"status": "awaiting-merge",
|
|
1093
|
+
"mode": "external-landing",
|
|
1094
|
+
"archive": archive_path,
|
|
1095
|
+
"commit": ship_commit,
|
|
1096
|
+
"branch": bound_branch,
|
|
1097
|
+
"merge_subject": integrate_subject(archive_name, default_name),
|
|
1098
|
+
"merge_body": integrate_commit_body(
|
|
1099
|
+
archive_path, ship_commit, default_name, bound_branch
|
|
1100
|
+
),
|
|
1101
|
+
}
|
|
1102
|
+
if contains_ship.returncode != 0:
|
|
1103
|
+
archive_milestone.require_git_success(
|
|
1104
|
+
contains_ship, "inspect ship ancestry on the remote default"
|
|
1105
|
+
)
|
|
1106
|
+
raise AssertionError("unreachable")
|
|
1107
|
+
|
|
1108
|
+
merge_commit = find_integrate_commit(
|
|
1109
|
+
project, remote_default, archive_name, ship_commit
|
|
1110
|
+
)
|
|
1111
|
+
require_generated_integration_commit(
|
|
1112
|
+
project,
|
|
1113
|
+
merge_commit,
|
|
1114
|
+
archive_path,
|
|
1115
|
+
archive_name,
|
|
1116
|
+
ship_commit,
|
|
1117
|
+
default_name,
|
|
1118
|
+
bound_branch,
|
|
1119
|
+
)
|
|
1120
|
+
tag_name = f"milestone/{archive_name}"
|
|
1121
|
+
message = external_landing_tag_message(archive_name, ship_commit, merge_commit)
|
|
1122
|
+
tag_object, tag_published = ensure_integration_tag(
|
|
1123
|
+
project,
|
|
1124
|
+
tag_name,
|
|
1125
|
+
merge_commit,
|
|
1126
|
+
message,
|
|
1127
|
+
)
|
|
1128
|
+
if not tag_published:
|
|
1129
|
+
archive_milestone.require_git_success(
|
|
1130
|
+
run_git(
|
|
1131
|
+
project,
|
|
1132
|
+
"push",
|
|
1133
|
+
"origin",
|
|
1134
|
+
f"refs/tags/{tag_name}:refs/tags/{tag_name}",
|
|
1135
|
+
),
|
|
1136
|
+
"push milestone tag",
|
|
1137
|
+
)
|
|
1138
|
+
archive_milestone.require_git_success(
|
|
1139
|
+
run_git(
|
|
1140
|
+
project,
|
|
1141
|
+
"update-ref",
|
|
1142
|
+
f"refs/remotes/origin/tags/{tag_name}",
|
|
1143
|
+
tag_object,
|
|
1144
|
+
),
|
|
1145
|
+
"refresh local milestone-tag ref",
|
|
1146
|
+
)
|
|
1147
|
+
return validate_integrated(project, archive_milestone.milestone_slug(state))
|
|
1148
|
+
|
|
1149
|
+
|
|
1024
1150
|
def integrate(repo: Path, slug: str) -> dict:
|
|
1025
1151
|
project = repo.resolve()
|
|
1026
1152
|
active_root = archive_milestone.require_project_layout(project)
|
|
@@ -1055,8 +1181,10 @@ def integrate(repo: Path, slug: str) -> dict:
|
|
|
1055
1181
|
|
|
1056
1182
|
remote_default = refresh_origin(project)["remote_default"]
|
|
1057
1183
|
default_name = default_branch_name(remote_default)
|
|
1058
|
-
if default_name !=
|
|
1059
|
-
raise ArchiveError(
|
|
1184
|
+
if default_name != state.default_branch:
|
|
1185
|
+
raise ArchiveError(
|
|
1186
|
+
f"remote default must be {state.default_branch}, got {default_name!r}"
|
|
1187
|
+
)
|
|
1060
1188
|
if bound_branch == default_name:
|
|
1061
1189
|
raise ArchiveError("bound branch must not be the remote default")
|
|
1062
1190
|
remote_default_sha = archive_milestone.require_git_success(
|
|
@@ -1070,6 +1198,17 @@ def integrate(repo: Path, slug: str) -> dict:
|
|
|
1070
1198
|
archive_name,
|
|
1071
1199
|
ship_commit,
|
|
1072
1200
|
)
|
|
1201
|
+
if state.integration == "external-landing":
|
|
1202
|
+
return integrate_external_landing(
|
|
1203
|
+
project,
|
|
1204
|
+
state,
|
|
1205
|
+
archive_path,
|
|
1206
|
+
archive_name,
|
|
1207
|
+
ship_commit,
|
|
1208
|
+
remote_default,
|
|
1209
|
+
default_name,
|
|
1210
|
+
bound_branch,
|
|
1211
|
+
)
|
|
1073
1212
|
publishable_bound_branch(project, bound_branch, ship_commit)
|
|
1074
1213
|
integration_branch, worktree = integration_names(project, archive_name, pin=True)
|
|
1075
1214
|
interrupted_worktree = registered_worktree(project, integration_branch)
|
|
@@ -1152,7 +1291,7 @@ def integrate(repo: Path, slug: str) -> dict:
|
|
|
1152
1291
|
"origin",
|
|
1153
1292
|
f"{merge_commit}:refs/heads/{default_name}",
|
|
1154
1293
|
),
|
|
1155
|
-
"push integration merge to
|
|
1294
|
+
f"push integration merge to {default_name}",
|
|
1156
1295
|
)
|
|
1157
1296
|
archive_milestone.require_git_success(
|
|
1158
1297
|
run_git(project, "update-ref", f"refs/remotes/{remote_default}", merge_commit),
|
|
@@ -1297,10 +1436,12 @@ def validate_integrated(
|
|
|
1297
1436
|
if bound_branch == default_name:
|
|
1298
1437
|
raise ArchiveError(
|
|
1299
1438
|
f"bound branch {bound_branch!r} is the remote default; "
|
|
1300
|
-
"ship merges onto
|
|
1439
|
+
"ship merges onto the default branch, never onto the work branch"
|
|
1440
|
+
)
|
|
1441
|
+
if default_name != state.default_branch:
|
|
1442
|
+
raise ArchiveError(
|
|
1443
|
+
f"remote default must be {state.default_branch}, got {default_name!r}"
|
|
1301
1444
|
)
|
|
1302
|
-
if default_name != "main":
|
|
1303
|
-
raise ArchiveError(f"remote default must be main, got {default_name!r}")
|
|
1304
1445
|
|
|
1305
1446
|
# (b) Resolve the integration proof selected before build.
|
|
1306
1447
|
tag_name = f"milestone/{archive_name}"
|
|
@@ -1316,7 +1457,7 @@ def validate_integrated(
|
|
|
1316
1457
|
pull_request = metadata["Pull-Request"]
|
|
1317
1458
|
require_github_authentication()
|
|
1318
1459
|
repository = github_repository(project)
|
|
1319
|
-
pull = find_pull_request(repository, bound_branch, ship_commit)
|
|
1460
|
+
pull = find_pull_request(repository, bound_branch, ship_commit, default_name)
|
|
1320
1461
|
if pull is None:
|
|
1321
1462
|
raise ArchiveError("GitHub pull request for the ship commit is missing")
|
|
1322
1463
|
if pull["html_url"] != pull_request:
|
|
@@ -1345,6 +1486,32 @@ def validate_integrated(
|
|
|
1345
1486
|
merge_commit,
|
|
1346
1487
|
),
|
|
1347
1488
|
)
|
|
1489
|
+
elif state.integration == "external-landing":
|
|
1490
|
+
merge_commit = find_integrate_commit(
|
|
1491
|
+
project, remote_default, archive_name, ship_commit
|
|
1492
|
+
)
|
|
1493
|
+
archive_milestone.require_canonical_commit_body(
|
|
1494
|
+
project,
|
|
1495
|
+
merge_commit,
|
|
1496
|
+
integrate_subject(archive_name, default_name),
|
|
1497
|
+
integrate_commit_body(configured, ship_commit, default_name, bound_branch),
|
|
1498
|
+
"integration",
|
|
1499
|
+
)
|
|
1500
|
+
require_pull_request_merge(project, merge_commit, ship_commit, remote_default)
|
|
1501
|
+
if run_git(project, "rev-parse", "--verify", "--quiet", tag_ref).returncode != 0:
|
|
1502
|
+
raise ArchiveError(f"missing milestone tag: {tag_name}")
|
|
1503
|
+
metadata = external_landing_tag_metadata(project, tag_ref)
|
|
1504
|
+
if metadata["Ship"] != ship_commit:
|
|
1505
|
+
raise ArchiveError("external-landing milestone tag names the wrong ship commit")
|
|
1506
|
+
if metadata["Landing"] != merge_commit:
|
|
1507
|
+
raise ArchiveError("external-landing milestone tag names the wrong landing")
|
|
1508
|
+
require_annotated_tag(
|
|
1509
|
+
project,
|
|
1510
|
+
tag_ref,
|
|
1511
|
+
merge_commit,
|
|
1512
|
+
f"milestone tag {tag_name}",
|
|
1513
|
+
external_landing_tag_message(archive_name, ship_commit, merge_commit),
|
|
1514
|
+
)
|
|
1348
1515
|
else:
|
|
1349
1516
|
merge_commit = find_integrate_commit(
|
|
1350
1517
|
project, remote_default, archive_name, ship_commit
|
|
@@ -1381,7 +1548,8 @@ def validate_integrated(
|
|
|
1381
1548
|
ship_commit,
|
|
1382
1549
|
tag_name,
|
|
1383
1550
|
merge_commit,
|
|
1384
|
-
allow_missing_bound=state.integration
|
|
1551
|
+
allow_missing_bound=state.integration in ("pull-request", "external-landing")
|
|
1552
|
+
or (historical and has_origin),
|
|
1385
1553
|
retired=historical,
|
|
1386
1554
|
)
|
|
1387
1555
|
|
|
@@ -1482,21 +1650,23 @@ def require_published_integration(
|
|
|
1482
1650
|
)
|
|
1483
1651
|
|
|
1484
1652
|
|
|
1485
|
-
def _member_names(coordinator: Path, member: str, archive_path: str) -> tuple[Path, str, str, str, str]:
|
|
1486
|
-
"""A member's checkout, coordinator project, bound branch, merge subject,
|
|
1653
|
+
def _member_names(coordinator: Path, member: str, archive_path: str) -> tuple[Path, str, str, str, str, str]:
|
|
1654
|
+
"""A member's checkout, coordinator project, bound branch, merge subject, tag,
|
|
1655
|
+
and the default branch that MEMBERS.md records for it."""
|
|
1487
1656
|
try:
|
|
1488
1657
|
import isolation
|
|
1489
1658
|
except ImportError: # pragma: no cover - package import used by tests
|
|
1490
1659
|
from scripts import isolation
|
|
1491
1660
|
try:
|
|
1492
1661
|
checkout, project = isolation.member_checkout(coordinator.resolve(), member)
|
|
1662
|
+
default = isolation.member_default_branch(coordinator.resolve(), member)
|
|
1493
1663
|
except isolation.IsolationError as error:
|
|
1494
1664
|
raise ArchiveError(str(error)) from error
|
|
1495
1665
|
archive_name = PurePosixPath(archive_path).name
|
|
1496
1666
|
identifier = milestone_id(milestone_number(archive_name))
|
|
1497
1667
|
bound = f"gsd-path/{project}-{identifier}"
|
|
1498
|
-
subject = f"integrate: {project} {identifier} — merge {bound} into
|
|
1499
|
-
return checkout, project, bound, subject, f"milestone/{project}-{archive_name}"
|
|
1668
|
+
subject = f"integrate: {project} {identifier} — merge {bound} into {default}"
|
|
1669
|
+
return checkout, project, bound, subject, f"milestone/{project}-{archive_name}", default
|
|
1500
1670
|
|
|
1501
1671
|
|
|
1502
1672
|
def _member_merge_body(archive_path: str, member: str, reviewed_head: str) -> str:
|
|
@@ -1523,16 +1693,16 @@ def member_pull_request_tag_metadata(contents: str) -> dict[str, str]:
|
|
|
1523
1693
|
return fields
|
|
1524
1694
|
|
|
1525
1695
|
|
|
1526
|
-
def _find_member_merge(checkout: Path, subject: str, body: str, reviewed_head: str) -> Optional[str]:
|
|
1527
|
-
"""The member merge in
|
|
1696
|
+
def _find_member_merge(checkout: Path, subject: str, body: str, reviewed_head: str, default: str) -> Optional[str]:
|
|
1697
|
+
"""The member merge in the first-parent history of origin/<default>, if published."""
|
|
1528
1698
|
log = archive_milestone.require_git_success(
|
|
1529
|
-
run_git(checkout, "log", "--first-parent", "--format=%H%x00%s", "origin/
|
|
1530
|
-
"inspect member origin/
|
|
1699
|
+
run_git(checkout, "log", "--first-parent", "--format=%H%x00%s", f"origin/{default}"),
|
|
1700
|
+
f"inspect member origin/{default} first-parent history",
|
|
1531
1701
|
)
|
|
1532
1702
|
matches = [commit for commit, _, text in (line.partition("\x00") for line in log.splitlines())
|
|
1533
1703
|
if text == subject]
|
|
1534
1704
|
if len(matches) > 1:
|
|
1535
|
-
raise ArchiveError(f"multiple member merges named {subject!r} on origin/
|
|
1705
|
+
raise ArchiveError(f"multiple member merges named {subject!r} on origin/{default}")
|
|
1536
1706
|
if not matches:
|
|
1537
1707
|
return None
|
|
1538
1708
|
_require_member_merge(checkout, matches[0], subject, body, reviewed_head)
|
|
@@ -1587,7 +1757,7 @@ def _create_member_merge(checkout: Path, project: str, archive_path: str, subjec
|
|
|
1587
1757
|
+ (", ".join(conflicts) or (merged.stderr or merged.stdout).strip()))
|
|
1588
1758
|
merge = archive_milestone.require_git_success(run_git(worktree, "rev-parse", "HEAD"), "resolve member merge")
|
|
1589
1759
|
if _require_member_merge(worktree, merge, subject, body, reviewed_head) != origin_main:
|
|
1590
|
-
raise ArchiveError("member merge first parent is not origin
|
|
1760
|
+
raise ArchiveError("member merge first parent is not the origin default branch")
|
|
1591
1761
|
remove_registered_worktree(checkout, branch, worktree)
|
|
1592
1762
|
return merge
|
|
1593
1763
|
|
|
@@ -1614,34 +1784,39 @@ def _publish_member_bound(checkout: Path, project: str, member: str, bound: str,
|
|
|
1614
1784
|
raise ArchiveError(f"member {member} origin/{bound} moved: {remote_bound}")
|
|
1615
1785
|
|
|
1616
1786
|
|
|
1617
|
-
def _require_member_close(checkout: Path, member: str, bound: str, reviewed_head: str) -> None:
|
|
1787
|
+
def _require_member_close(checkout: Path, member: str, bound: str, reviewed_head: str, default: str) -> None:
|
|
1618
1788
|
if optional_ref(checkout, f"refs/heads/{bound}") != reviewed_head:
|
|
1619
1789
|
raise ArchiveError(f"member {member} {bound} is not at its reviewed HEAD {reviewed_head}")
|
|
1620
|
-
|
|
1621
|
-
|
|
1790
|
+
live = default_branch_name(refresh_origin(checkout)["remote_default"])
|
|
1791
|
+
if live != default:
|
|
1792
|
+
raise ArchiveError(f"member {member} remote default must be {default}, got {live!r}")
|
|
1622
1793
|
|
|
1623
1794
|
|
|
1624
1795
|
def integrate_member(coordinator: Path, member: str, archive_path: str, reviewed_head: str) -> dict:
|
|
1625
|
-
"""Direct-mode member close: publish the bound branch, merge it into
|
|
1626
|
-
|
|
1796
|
+
"""Direct-mode member close: publish the bound branch, merge it into the member
|
|
1797
|
+
default branch, then tag."""
|
|
1798
|
+
checkout, project, bound, subject, tag_name, default = _member_names(coordinator, member, archive_path)
|
|
1799
|
+
origin_default = f"origin/{default}"
|
|
1627
1800
|
body = _member_merge_body(archive_path, member, reviewed_head)
|
|
1628
|
-
_require_member_close(checkout, member, bound, reviewed_head)
|
|
1629
|
-
merge = _find_member_merge(checkout, subject, body, reviewed_head)
|
|
1801
|
+
_require_member_close(checkout, member, bound, reviewed_head, default)
|
|
1802
|
+
merge = _find_member_merge(checkout, subject, body, reviewed_head, default)
|
|
1630
1803
|
if merge is None:
|
|
1631
1804
|
origin_main = archive_milestone.require_git_success(
|
|
1632
|
-
run_git(checkout, "rev-parse",
|
|
1805
|
+
run_git(checkout, "rev-parse", origin_default), f"resolve member {origin_default}")
|
|
1633
1806
|
ancestry = run_git(checkout, "merge-base", "--is-ancestor", reviewed_head, origin_main)
|
|
1634
1807
|
if ancestry.returncode == 0:
|
|
1635
|
-
raise ArchiveError(
|
|
1808
|
+
raise ArchiveError(
|
|
1809
|
+
f"member reviewed HEAD reached {origin_default} outside Path; user-approved recovery required")
|
|
1636
1810
|
if ancestry.returncode != 1:
|
|
1637
1811
|
archive_milestone.require_git_success(ancestry, "inspect member reviewed HEAD ancestry")
|
|
1638
1812
|
merge = _create_member_merge(checkout, project, archive_path, subject, body, reviewed_head, origin_main)
|
|
1639
|
-
# Publish in order: bound branch,
|
|
1813
|
+
# Publish in order: bound branch, default branch, tag; a rerun publishes only what is missing.
|
|
1640
1814
|
_publish_member_bound(checkout, project, member, bound, reviewed_head)
|
|
1641
|
-
if run_git(checkout, "merge-base", "--is-ancestor", merge,
|
|
1642
|
-
_push_member_ref(checkout, project, merge, "refs/heads/
|
|
1815
|
+
if run_git(checkout, "merge-base", "--is-ancestor", merge, origin_default).returncode != 0:
|
|
1816
|
+
_push_member_ref(checkout, project, merge, f"refs/heads/{default}", merge, None)
|
|
1643
1817
|
archive_milestone.require_git_success(
|
|
1644
|
-
run_git(checkout, "update-ref", "refs/remotes/
|
|
1818
|
+
run_git(checkout, "update-ref", f"refs/remotes/{origin_default}", merge),
|
|
1819
|
+
f"refresh member {origin_default}")
|
|
1645
1820
|
tag_object, tag_published = ensure_integration_tag(checkout, tag_name, merge)
|
|
1646
1821
|
if not tag_published:
|
|
1647
1822
|
_push_member_ref(checkout, project, f"refs/tags/{tag_name}", f"refs/tags/{tag_name}", tag_object, None)
|
|
@@ -1655,12 +1830,12 @@ def integrate_member(coordinator: Path, member: str, archive_path: str, reviewed
|
|
|
1655
1830
|
|
|
1656
1831
|
def integrate_member_pull_request(coordinator: Path, member: str, archive_path: str, reviewed_head: str) -> dict:
|
|
1657
1832
|
"""Pull-request member close: open one PR for the bound branch; after a human merge, tag it."""
|
|
1658
|
-
checkout, project, bound, subject, tag_name = _member_names(coordinator, member, archive_path)
|
|
1833
|
+
checkout, project, bound, subject, tag_name, default = _member_names(coordinator, member, archive_path)
|
|
1659
1834
|
body = _member_merge_body(archive_path, member, reviewed_head)
|
|
1660
|
-
_require_member_close(checkout, member, bound, reviewed_head)
|
|
1835
|
+
_require_member_close(checkout, member, bound, reviewed_head, default)
|
|
1661
1836
|
require_github_authentication()
|
|
1662
1837
|
repository = github_repository(checkout)
|
|
1663
|
-
pull = find_pull_request(repository, bound, reviewed_head)
|
|
1838
|
+
pull = find_pull_request(repository, bound, reviewed_head, default)
|
|
1664
1839
|
if pull is None or pull["merged_at"] is None:
|
|
1665
1840
|
if pull is not None and pull["state"] != "open":
|
|
1666
1841
|
raise ArchiveError(f"member {member} pull request was closed without merging")
|
|
@@ -1668,12 +1843,12 @@ def integrate_member_pull_request(coordinator: Path, member: str, archive_path:
|
|
|
1668
1843
|
if pull is None:
|
|
1669
1844
|
pull = require_pull_request_shape(github_api_json(
|
|
1670
1845
|
f"repos/{repository}/pulls", "--method", "POST", "-f", f"title={subject}",
|
|
1671
|
-
"-f", f"head={bound}", "-f", "base=
|
|
1672
|
-
require_pull_request_identity(repository, pull, bound, reviewed_head)
|
|
1846
|
+
"-f", f"head={bound}", "-f", f"base={default}", "-f", f"body={body}\n\n---\n{PR_CREDIT_LINE}"))
|
|
1847
|
+
require_pull_request_identity(repository, pull, bound, reviewed_head, default)
|
|
1673
1848
|
else:
|
|
1674
1849
|
pull = update_pull_request_body(
|
|
1675
1850
|
repository, pull, archive_path, reviewed_head, bound,
|
|
1676
|
-
f"{body}\n\n---\n{PR_CREDIT_LINE}",
|
|
1851
|
+
f"{body}\n\n---\n{PR_CREDIT_LINE}", default,
|
|
1677
1852
|
)
|
|
1678
1853
|
require_pull_request_merge_provenance(repository, pull["number"])
|
|
1679
1854
|
return {"status": "awaiting-merge", "mode": "pull-request", "member": member,
|
|
@@ -1683,10 +1858,10 @@ def integrate_member_pull_request(coordinator: Path, member: str, archive_path:
|
|
|
1683
1858
|
raise ArchiveError(f"member {member} pull request merge metadata is invalid")
|
|
1684
1859
|
require_pull_request_merge_provenance(repository, pull["number"], merge)
|
|
1685
1860
|
refresh_origin(checkout)
|
|
1686
|
-
require_pull_request_merge(checkout, merge, reviewed_head, "origin/
|
|
1861
|
+
require_pull_request_merge(checkout, merge, reviewed_head, f"origin/{default}")
|
|
1687
1862
|
pull = update_pull_request_body(
|
|
1688
1863
|
repository, pull, archive_path, reviewed_head, bound,
|
|
1689
|
-
f"{body}\n\n---\n{PR_CREDIT_LINE}",
|
|
1864
|
+
f"{body}\n\n---\n{PR_CREDIT_LINE}", default,
|
|
1690
1865
|
)
|
|
1691
1866
|
message = member_pull_request_tag_message(tag_name, pull["html_url"], reviewed_head, merge)
|
|
1692
1867
|
tag_object, tag_published = ensure_integration_tag(checkout, tag_name, merge, message)
|
|
@@ -1701,8 +1876,8 @@ def integrate_member_pull_request(coordinator: Path, member: str, archive_path:
|
|
|
1701
1876
|
def validate_member_integrated(
|
|
1702
1877
|
coordinator: Path, member: str, archive_path: str, reviewed_head: str, *, refresh: bool = True
|
|
1703
1878
|
) -> dict:
|
|
1704
|
-
"""Prove the member merge is on
|
|
1705
|
-
checkout, _, bound, subject, tag_name = _member_names(coordinator, member, archive_path)
|
|
1879
|
+
"""Prove the member merge is on the member default branch and its published tag points at it."""
|
|
1880
|
+
checkout, _, bound, subject, tag_name, default = _member_names(coordinator, member, archive_path)
|
|
1706
1881
|
tracking = f"refs/remotes/origin/tags/{tag_name}"
|
|
1707
1882
|
if refresh:
|
|
1708
1883
|
refresh_origin(checkout)
|
|
@@ -1725,7 +1900,7 @@ def validate_member_integrated(
|
|
|
1725
1900
|
raise ArchiveError(f"member {member} tag {tag_name} names a different landing")
|
|
1726
1901
|
require_github_authentication()
|
|
1727
1902
|
repository = github_repository(checkout)
|
|
1728
|
-
pull = find_pull_request(repository, bound, reviewed_head)
|
|
1903
|
+
pull = find_pull_request(repository, bound, reviewed_head, default)
|
|
1729
1904
|
if pull is None:
|
|
1730
1905
|
raise ArchiveError(f"member {member} GitHub pull request is missing")
|
|
1731
1906
|
if pull["html_url"] != metadata["Pull-Request"]:
|
|
@@ -1734,16 +1909,16 @@ def validate_member_integrated(
|
|
|
1734
1909
|
or pull["merge_commit_sha"] != merge):
|
|
1735
1910
|
raise ArchiveError(f"member {member} GitHub pull request is not merged at the tagged landing")
|
|
1736
1911
|
require_pull_request_merge_provenance(repository, pull["number"], merge)
|
|
1737
|
-
require_pull_request_merge(checkout, merge, reviewed_head, "origin/
|
|
1912
|
+
require_pull_request_merge(checkout, merge, reviewed_head, f"origin/{default}")
|
|
1738
1913
|
require_annotated_tag(
|
|
1739
1914
|
checkout, tracking, merge, f"member tag {tag_name}",
|
|
1740
1915
|
member_pull_request_tag_message(tag_name, pull["html_url"], reviewed_head, merge),
|
|
1741
1916
|
)
|
|
1742
1917
|
else:
|
|
1743
1918
|
merge = _find_member_merge(checkout, subject, _member_merge_body(archive_path, member, reviewed_head),
|
|
1744
|
-
reviewed_head)
|
|
1919
|
+
reviewed_head, default)
|
|
1745
1920
|
if merge is None:
|
|
1746
|
-
raise ArchiveError(f"member {member} has no {subject!r} merge on origin/
|
|
1921
|
+
raise ArchiveError(f"member {member} has no {subject!r} merge on origin/{default}")
|
|
1747
1922
|
if published is None:
|
|
1748
1923
|
raise ArchiveError(f"member {member} tag {tag_name} is not published")
|
|
1749
1924
|
require_annotated_tag(checkout, tracking, merge, f"member tag {tag_name}")
|
|
@@ -1757,7 +1932,7 @@ def retire_member(coordinator: Path, member: str, archive_path: str, reviewed_he
|
|
|
1757
1932
|
import members
|
|
1758
1933
|
except ImportError: # pragma: no cover - package import used by tests
|
|
1759
1934
|
from scripts import members
|
|
1760
|
-
checkout, project, bound, _, tag_name = _member_names(coordinator, member, archive_path)
|
|
1935
|
+
checkout, project, bound, _, tag_name, default = _member_names(coordinator, member, archive_path)
|
|
1761
1936
|
proven = validate_member_integrated(coordinator, member, archive_path, reviewed_head)
|
|
1762
1937
|
if (proven["merge"], proven["tag"]) != (merge, tag):
|
|
1763
1938
|
raise ArchiveError(f"member {member} integration differs from the ship commit")
|
|
@@ -1796,9 +1971,13 @@ def retire_member(coordinator: Path, member: str, archive_path: str, reviewed_he
|
|
|
1796
1971
|
archive_milestone.require_git_success(run_git(checkout, "update-ref", "-d", bound_ref, reviewed_head),
|
|
1797
1972
|
"delete member bound branch")
|
|
1798
1973
|
run_git(checkout, "update-ref", "-d", f"refs/remotes/origin/{bound}")
|
|
1799
|
-
for kind, ref in (("push", bound_ref), ("push", "refs/heads/
|
|
1974
|
+
for kind, ref in (("push", bound_ref), ("push", f"refs/heads/{default}"), ("push", f"refs/tags/{tag_name}"),
|
|
1800
1975
|
("delete", bound_ref)):
|
|
1801
1976
|
members.clear_authorization(checkout, project, kind, ref)
|
|
1977
|
+
# The last sidecar retires here at milestone end; isolation's retires also
|
|
1978
|
+
# release the pin whenever the workspace is idle, so a new root can apply.
|
|
1979
|
+
with contextlib.suppress(ValueError, OSError):
|
|
1980
|
+
worktree_paths.release_workspace(checkout)
|
|
1802
1981
|
return {"member": member, "retired": bound}
|
|
1803
1982
|
|
|
1804
1983
|
|