@opengsd/gsd-path 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -207
- package/CHANGELOG.md +294 -0
- package/DOCS.md +19 -10
- package/FULL.md +9 -3
- package/GUIDE.md +1 -1
- package/HOOKS.md +9 -6
- package/QUICK.md +4 -1
- package/README.md +63 -11
- package/RUNTIME.md +1 -1
- package/UPDATE.md +61 -12
- package/WORKFLOW.md +22 -6
- package/package.json +9 -8
- package/scripts/_common.py +472 -19
- package/scripts/archive_milestone.py +146 -73
- package/scripts/bootstrap_repository.py +8 -5
- package/scripts/build_recovery.py +4 -1
- package/scripts/build_state.py +66 -23
- package/scripts/check_docs_audit.py +14 -3
- package/scripts/check_handoffs.py +116 -20
- package/scripts/check_task_briefs.py +60 -8
- package/scripts/check_update.py +2 -2
- package/scripts/core_hook_gate.py +10 -1
- package/scripts/core_hook_settings.py +11 -4
- package/scripts/detect_project.py +345 -15
- package/scripts/discussion_records.py +19 -13
- package/scripts/discussion_validate.py +66 -21
- package/scripts/dispatch_driver.py +111 -26
- package/scripts/git_guard.py +116 -29
- package/scripts/guard_hook.py +151 -29
- package/scripts/install.mjs +55 -39
- package/scripts/install.py +564 -41
- package/scripts/integration.py +434 -24
- package/scripts/isolation.py +1077 -28
- package/scripts/lean_verification.py +99 -19
- package/scripts/loop_run.py +20 -30
- package/scripts/members.py +704 -0
- package/scripts/migrate_core.py +2 -2
- package/scripts/path_config.py +2 -2
- package/scripts/pipeline_diagnose.py +82 -31
- package/scripts/pipeline_git.py +61 -22
- package/scripts/pipeline_state.py +176 -54
- package/scripts/pipeline_undo.py +117 -22
- package/scripts/promote_lookahead.py +1 -1
- package/scripts/review_panel.py +1 -1
- package/scripts/runtime_store.py +49 -11
- package/scripts/skill-resources.json +28 -3
- package/scripts/state_checkpoint.py +140 -31
- package/scripts/state_promote.py +2 -0
- package/scripts/status_runtime.py +36 -6
- package/scripts/sync_skill_resources.py +8 -7
- package/scripts/token_budget.py +12 -8
- package/scripts/workflow_run.py +103 -29
- package/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path/BUILD.md +7 -1
- package/skills/gsd-path/DECIDE.md +11 -4
- package/skills/gsd-path/DEFINE.md +8 -1
- package/skills/gsd-path/DOCS-AUDIT.md +6 -4
- package/skills/gsd-path/INSPECT.md +5 -4
- package/skills/gsd-path/PLAN.md +7 -1
- package/skills/gsd-path/RESEARCH.md +2 -1
- package/skills/gsd-path/ROADMAP.md +2 -1
- package/skills/gsd-path/SHIP.md +27 -10
- package/skills/gsd-path/SKILL.md +45 -1
- package/skills/gsd-path/references/build-native.md +47 -14
- package/skills/gsd-path/references/codebase-mapper.md +2 -1
- package/skills/gsd-path/references/docs-auditor.md +2 -1
- package/skills/gsd-path/references/operating-rules.md +214 -0
- package/skills/gsd-path/references/planner.md +8 -0
- package/skills/gsd-path/references/reviewer-wave.md +3 -1
- package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
- package/skills/gsd-path/scripts/_common.py +472 -19
- package/skills/gsd-path/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path/scripts/bootstrap_repository.py +8 -5
- package/skills/gsd-path/scripts/build_recovery.py +4 -1
- package/skills/gsd-path/scripts/build_state.py +66 -23
- package/skills/gsd-path/scripts/check_docs_audit.py +14 -3
- package/skills/gsd-path/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path/scripts/check_update.py +2 -2
- package/skills/gsd-path/scripts/detect_project.py +345 -15
- package/skills/gsd-path/scripts/discussion_records.py +19 -13
- package/skills/gsd-path/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path/scripts/dispatch_driver.py +111 -26
- package/skills/gsd-path/scripts/git_guard.py +116 -29
- package/skills/gsd-path/scripts/integration.py +434 -24
- package/skills/gsd-path/scripts/isolation.py +1077 -28
- package/skills/gsd-path/scripts/lean_verification.py +99 -19
- package/skills/gsd-path/scripts/loop_run.py +20 -30
- package/skills/gsd-path/scripts/members.py +704 -0
- package/skills/gsd-path/scripts/path_config.py +2 -2
- package/skills/gsd-path/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path/scripts/review_panel.py +1 -1
- package/skills/gsd-path/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path/scripts/state_promote.py +2 -0
- package/skills/gsd-path/scripts/status_runtime.py +36 -6
- package/skills/gsd-path/scripts/token_budget.py +12 -8
- package/skills/gsd-path/scripts/workflow_run.py +103 -29
- package/skills/gsd-path/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path/templates/codebase.md +1 -1
- package/skills/gsd-path/templates/docs-audit.md +1 -1
- package/skills/gsd-path/templates/final-review.md +5 -2
- package/skills/gsd-path/templates/gap-review.md +2 -1
- package/skills/gsd-path/templates/task.md +1 -0
- package/skills/gsd-path/templates/wave-review.md +3 -1
- package/skills/gsd-path-build/SKILL.md +7 -1
- package/skills/gsd-path-build/references/build-native.md +47 -14
- package/skills/gsd-path-build/references/operating-rules.md +214 -0
- package/skills/gsd-path-build/references/reviewer-wave.md +3 -1
- package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
- package/skills/gsd-path-build/scripts/_common.py +472 -19
- package/skills/gsd-path-build/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-build/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-build/scripts/build_state.py +66 -23
- package/skills/gsd-path-build/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-build/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-build/scripts/detect_project.py +345 -15
- package/skills/gsd-path-build/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-build/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-build/scripts/dispatch_driver.py +111 -26
- package/skills/gsd-path-build/scripts/integration.py +434 -24
- package/skills/gsd-path-build/scripts/isolation.py +1077 -28
- package/skills/gsd-path-build/scripts/loop_run.py +20 -30
- package/skills/gsd-path-build/scripts/members.py +704 -0
- package/skills/gsd-path-build/scripts/path_config.py +2 -2
- package/skills/gsd-path-build/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-build/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path-build/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-build/scripts/review_panel.py +1 -1
- package/skills/gsd-path-build/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path-build/scripts/state_promote.py +2 -0
- package/skills/gsd-path-build/scripts/token_budget.py +12 -8
- package/skills/gsd-path-build/scripts/workflow_run.py +103 -29
- package/skills/gsd-path-build/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-build/templates/task.md +1 -0
- package/skills/gsd-path-build/templates/wave-review.md +3 -1
- package/skills/gsd-path-decide/SKILL.md +11 -4
- package/skills/gsd-path-decide/references/operating-rules.md +214 -0
- package/skills/gsd-path-decide/scripts/_common.py +472 -19
- package/skills/gsd-path-decide/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-decide/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-decide/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-decide/scripts/detect_project.py +345 -15
- package/skills/gsd-path-decide/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-decide/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-decide/scripts/integration.py +434 -24
- package/skills/gsd-path-decide/scripts/isolation.py +1077 -28
- package/skills/gsd-path-decide/scripts/loop_run.py +20 -30
- package/skills/gsd-path-decide/scripts/members.py +704 -0
- package/skills/gsd-path-decide/scripts/path_config.py +2 -2
- package/skills/gsd-path-decide/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-decide/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path-decide/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-decide/scripts/review_panel.py +1 -1
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path-decide/scripts/state_promote.py +2 -0
- package/skills/gsd-path-decide/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-define/SKILL.md +8 -1
- package/skills/gsd-path-define/references/operating-rules.md +214 -0
- package/skills/gsd-path-define/scripts/_common.py +472 -19
- package/skills/gsd-path-define/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-define/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-define/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-define/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-define/scripts/detect_project.py +345 -15
- package/skills/gsd-path-define/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-define/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-define/scripts/integration.py +434 -24
- package/skills/gsd-path-define/scripts/isolation.py +1077 -28
- package/skills/gsd-path-define/scripts/loop_run.py +20 -30
- package/skills/gsd-path-define/scripts/members.py +704 -0
- package/skills/gsd-path-define/scripts/path_config.py +2 -2
- package/skills/gsd-path-define/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-define/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path-define/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-define/scripts/review_panel.py +1 -1
- package/skills/gsd-path-define/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path-define/scripts/state_promote.py +2 -0
- package/skills/gsd-path-define/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-discuss/SKILL.md +2 -1
- package/skills/gsd-path-discuss/references/operating-rules.md +214 -0
- package/skills/gsd-path-discuss/scripts/_common.py +472 -19
- package/skills/gsd-path-discuss/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-discuss/scripts/loop_run.py +20 -30
- package/skills/gsd-path-discuss/scripts/path_config.py +2 -2
- package/skills/gsd-path-discuss/scripts/review_panel.py +1 -1
- package/skills/gsd-path-discuss/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-docs-audit/SKILL.md +6 -4
- package/skills/gsd-path-docs-audit/references/docs-auditor.md +2 -1
- package/skills/gsd-path-docs-audit/references/operating-rules.md +214 -0
- package/skills/gsd-path-docs-audit/scripts/_common.py +472 -19
- package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +14 -3
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-docs-audit/scripts/detect_project.py +345 -15
- package/skills/gsd-path-docs-audit/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-docs-audit/scripts/integration.py +434 -24
- package/skills/gsd-path-docs-audit/scripts/isolation.py +1077 -28
- package/skills/gsd-path-docs-audit/scripts/loop_run.py +20 -30
- package/skills/gsd-path-docs-audit/scripts/members.py +704 -0
- package/skills/gsd-path-docs-audit/scripts/path_config.py +2 -2
- package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-docs-audit/scripts/review_panel.py +1 -1
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path-docs-audit/scripts/state_promote.py +2 -0
- package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-docs-audit/templates/docs-audit.md +1 -1
- package/skills/gsd-path-forensics/SKILL.md +2 -1
- package/skills/gsd-path-forensics/references/operating-rules.md +214 -0
- package/skills/gsd-path-forensics/scripts/_common.py +472 -19
- package/skills/gsd-path-forensics/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-forensics/scripts/loop_run.py +20 -30
- package/skills/gsd-path-forensics/scripts/path_config.py +2 -2
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +82 -31
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +117 -22
- package/skills/gsd-path-forensics/scripts/review_panel.py +1 -1
- package/skills/gsd-path-forensics/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-inspect/SKILL.md +5 -4
- package/skills/gsd-path-inspect/references/codebase-mapper.md +2 -1
- package/skills/gsd-path-inspect/references/docs-auditor.md +2 -1
- package/skills/gsd-path-inspect/references/operating-rules.md +214 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/_common.py +472 -19
- package/skills/gsd-path-inspect/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-inspect/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-inspect/scripts/check_docs_audit.py +14 -3
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-inspect/scripts/detect_project.py +345 -15
- package/skills/gsd-path-inspect/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-inspect/scripts/integration.py +434 -24
- package/skills/gsd-path-inspect/scripts/isolation.py +1077 -28
- package/skills/gsd-path-inspect/scripts/loop_run.py +20 -30
- package/skills/gsd-path-inspect/scripts/members.py +704 -0
- package/skills/gsd-path-inspect/scripts/path_config.py +2 -2
- package/skills/gsd-path-inspect/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path-inspect/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-inspect/scripts/review_panel.py +1 -1
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path-inspect/scripts/state_promote.py +2 -0
- package/skills/gsd-path-inspect/scripts/workflow_run.py +103 -29
- package/skills/gsd-path-inspect/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-inspect/templates/codebase.md +1 -1
- package/skills/gsd-path-inspect/templates/docs-audit.md +1 -1
- package/skills/gsd-path-loop/SKILL.md +2 -1
- package/skills/gsd-path-loop/references/operating-rules.md +214 -0
- package/skills/gsd-path-loop/scripts/_common.py +472 -19
- package/skills/gsd-path-loop/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-loop/scripts/loop_run.py +20 -30
- package/skills/gsd-path-loop/scripts/path_config.py +2 -2
- package/skills/gsd-path-loop/scripts/review_panel.py +1 -1
- package/skills/gsd-path-loop/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-migrate/SKILL.md +2 -1
- package/skills/gsd-path-migrate/references/operating-rules.md +214 -0
- package/skills/gsd-path-migrate/scripts/_common.py +472 -19
- package/skills/gsd-path-migrate/scripts/core_hook_gate.py +10 -1
- package/skills/gsd-path-migrate/scripts/core_hook_settings.py +11 -4
- package/skills/gsd-path-migrate/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-migrate/scripts/loop_run.py +20 -30
- package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -2
- package/skills/gsd-path-migrate/scripts/path_config.py +2 -2
- package/skills/gsd-path-migrate/scripts/review_panel.py +1 -1
- package/skills/gsd-path-plan/SKILL.md +7 -1
- package/skills/gsd-path-plan/references/operating-rules.md +214 -0
- package/skills/gsd-path-plan/references/planner.md +8 -0
- package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/_common.py +472 -19
- package/skills/gsd-path-plan/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-plan/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-plan/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-plan/scripts/detect_project.py +345 -15
- package/skills/gsd-path-plan/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-plan/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-plan/scripts/integration.py +434 -24
- package/skills/gsd-path-plan/scripts/isolation.py +1077 -28
- package/skills/gsd-path-plan/scripts/loop_run.py +20 -30
- package/skills/gsd-path-plan/scripts/members.py +704 -0
- package/skills/gsd-path-plan/scripts/path_config.py +2 -2
- package/skills/gsd-path-plan/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-plan/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path-plan/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-plan/scripts/review_panel.py +1 -1
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path-plan/scripts/state_promote.py +2 -0
- package/skills/gsd-path-plan/scripts/workflow_run.py +103 -29
- package/skills/gsd-path-plan/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-plan/templates/task.md +1 -0
- package/skills/gsd-path-research/SKILL.md +2 -1
- package/skills/gsd-path-research/references/operating-rules.md +214 -0
- package/skills/gsd-path-research/scripts/_common.py +472 -19
- package/skills/gsd-path-research/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-research/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-research/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-research/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-research/scripts/detect_project.py +345 -15
- package/skills/gsd-path-research/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-research/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-research/scripts/integration.py +434 -24
- package/skills/gsd-path-research/scripts/isolation.py +1077 -28
- package/skills/gsd-path-research/scripts/loop_run.py +20 -30
- package/skills/gsd-path-research/scripts/members.py +704 -0
- package/skills/gsd-path-research/scripts/path_config.py +2 -2
- package/skills/gsd-path-research/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-research/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path-research/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-research/scripts/review_panel.py +1 -1
- package/skills/gsd-path-research/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path-research/scripts/state_promote.py +2 -0
- package/skills/gsd-path-research/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-roadmap/SKILL.md +2 -1
- package/skills/gsd-path-roadmap/references/operating-rules.md +214 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +472 -19
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-roadmap/scripts/detect_project.py +345 -15
- package/skills/gsd-path-roadmap/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-roadmap/scripts/integration.py +434 -24
- package/skills/gsd-path-roadmap/scripts/isolation.py +1077 -28
- package/skills/gsd-path-roadmap/scripts/loop_run.py +20 -30
- package/skills/gsd-path-roadmap/scripts/members.py +704 -0
- package/skills/gsd-path-roadmap/scripts/path_config.py +2 -2
- package/skills/gsd-path-roadmap/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-roadmap/scripts/review_panel.py +1 -1
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path-roadmap/scripts/state_promote.py +2 -0
- package/skills/gsd-path-roadmap/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-ship/SKILL.md +27 -10
- package/skills/gsd-path-ship/references/operating-rules.md +214 -0
- package/skills/gsd-path-ship/references/reviewer-wave.md +3 -1
- package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/_common.py +472 -19
- package/skills/gsd-path-ship/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-ship/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-ship/scripts/build_state.py +66 -23
- package/skills/gsd-path-ship/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-ship/scripts/detect_project.py +345 -15
- package/skills/gsd-path-ship/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-ship/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-ship/scripts/integration.py +434 -24
- package/skills/gsd-path-ship/scripts/isolation.py +1077 -28
- package/skills/gsd-path-ship/scripts/lean_verification.py +99 -19
- package/skills/gsd-path-ship/scripts/loop_run.py +20 -30
- package/skills/gsd-path-ship/scripts/members.py +704 -0
- package/skills/gsd-path-ship/scripts/path_config.py +2 -2
- package/skills/gsd-path-ship/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-ship/scripts/pipeline_state.py +176 -54
- package/skills/gsd-path-ship/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-ship/scripts/review_panel.py +1 -1
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +140 -31
- package/skills/gsd-path-ship/scripts/state_promote.py +2 -0
- package/skills/gsd-path-ship/scripts/workflow_run.py +103 -29
- package/skills/gsd-path-ship/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-ship/templates/final-review.md +5 -2
- package/skills/gsd-path-ship/templates/gap-review.md +2 -1
- package/skills/gsd-path-ship/templates/wave-review.md +3 -1
- package/skills/gsd-path-undo/SKILL.md +2 -1
- package/skills/gsd-path-undo/references/operating-rules.md +214 -0
- package/skills/gsd-path-undo/scripts/_common.py +472 -19
- package/skills/gsd-path-undo/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-undo/scripts/loop_run.py +20 -30
- package/skills/gsd-path-undo/scripts/path_config.py +2 -2
- package/skills/gsd-path-undo/scripts/pipeline_undo.py +117 -22
- package/skills/gsd-path-undo/scripts/review_panel.py +1 -1
- package/skills/gsd-path-undo/scripts/worktree_paths.py +18 -8
- package/skills/path/BUILD.md +7 -1
- package/skills/path/DECIDE.md +11 -4
- package/skills/path/DEFINE.md +8 -1
- package/skills/path/DOCS-AUDIT.md +6 -4
- package/skills/path/INSPECT.md +5 -4
- package/skills/path/PLAN.md +7 -1
- package/skills/path/RESEARCH.md +2 -1
- package/skills/path/ROADMAP.md +2 -1
- package/skills/path/SHIP.md +27 -10
- package/skills/path/SKILL.md +45 -1
- package/skills/path/references/build-native.md +47 -14
- package/skills/path/references/codebase-mapper.md +2 -1
- package/skills/path/references/docs-auditor.md +2 -1
- package/skills/path/references/operating-rules.md +214 -0
- package/skills/path/references/planner.md +8 -0
- package/skills/path/references/reviewer-wave.md +3 -1
- package/skills/path/scripts/_common.py +472 -19
- package/skills/path/scripts/archive_milestone.py +146 -73
- package/skills/path/scripts/bootstrap_repository.py +8 -5
- package/skills/path/scripts/build_recovery.py +4 -1
- package/skills/path/scripts/build_state.py +66 -23
- package/skills/path/scripts/check_docs_audit.py +14 -3
- package/skills/path/scripts/check_handoffs.py +116 -20
- package/skills/path/scripts/check_task_briefs.py +60 -8
- package/skills/path/scripts/check_update.py +2 -2
- package/skills/path/scripts/detect_project.py +345 -15
- package/skills/path/scripts/discussion_records.py +19 -13
- package/skills/path/scripts/discussion_validate.py +66 -21
- package/skills/path/scripts/dispatch_driver.py +111 -26
- package/skills/path/scripts/git_guard.py +116 -29
- package/skills/path/scripts/integration.py +434 -24
- package/skills/path/scripts/isolation.py +1077 -28
- package/skills/path/scripts/lean_verification.py +99 -19
- package/skills/path/scripts/loop_run.py +20 -30
- package/skills/path/scripts/members.py +704 -0
- package/skills/path/scripts/path_config.py +2 -2
- package/skills/path/scripts/pipeline_git.py +61 -22
- package/skills/path/scripts/pipeline_state.py +176 -54
- package/skills/path/scripts/promote_lookahead.py +1 -1
- package/skills/path/scripts/review_panel.py +1 -1
- package/skills/path/scripts/state_checkpoint.py +140 -31
- package/skills/path/scripts/state_promote.py +2 -0
- package/skills/path/scripts/status_runtime.py +36 -6
- package/skills/path/scripts/token_budget.py +12 -8
- package/skills/path/scripts/workflow_run.py +103 -29
- package/skills/path/scripts/worktree_paths.py +18 -8
- package/skills/path/templates/codebase.md +1 -1
- package/skills/path/templates/docs-audit.md +1 -1
- package/skills/path/templates/final-review.md +5 -2
- package/skills/path/templates/gap-review.md +2 -1
- package/skills/path/templates/task.md +1 -0
- package/skills/path/templates/wave-review.md +3 -1
|
@@ -12,23 +12,8 @@ import re
|
|
|
12
12
|
from pathlib import Path, PurePosixPath
|
|
13
13
|
from typing import Optional
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
from pipeline_git import (
|
|
17
|
-
bound_branch_name,
|
|
18
|
-
default_branch_name,
|
|
19
|
-
integrate_commit_body,
|
|
20
|
-
integrate_subject,
|
|
21
|
-
is_bound_branch,
|
|
22
|
-
milestone_id,
|
|
23
|
-
milestone_number,
|
|
24
|
-
ship_subject,
|
|
25
|
-
)
|
|
26
|
-
from pipeline_state import PipelineState
|
|
27
|
-
from isolation import publication_base
|
|
28
|
-
import _common
|
|
29
|
-
import worktree_paths
|
|
30
|
-
except ImportError: # pragma: no cover - package import used by tests
|
|
31
|
-
from scripts.pipeline_git import (
|
|
15
|
+
if __package__:
|
|
16
|
+
from .pipeline_git import (
|
|
32
17
|
bound_branch_name,
|
|
33
18
|
default_branch_name,
|
|
34
19
|
integrate_commit_body,
|
|
@@ -38,10 +23,41 @@ except ImportError: # pragma: no cover - package import used by tests
|
|
|
38
23
|
milestone_number,
|
|
39
24
|
ship_subject,
|
|
40
25
|
)
|
|
41
|
-
from
|
|
42
|
-
from
|
|
43
|
-
from
|
|
44
|
-
from
|
|
26
|
+
from .pipeline_state import PipelineState
|
|
27
|
+
from .isolation import publication_base
|
|
28
|
+
from . import _common
|
|
29
|
+
from . import worktree_paths
|
|
30
|
+
else:
|
|
31
|
+
try:
|
|
32
|
+
from pipeline_git import (
|
|
33
|
+
bound_branch_name,
|
|
34
|
+
default_branch_name,
|
|
35
|
+
integrate_commit_body,
|
|
36
|
+
integrate_subject,
|
|
37
|
+
is_bound_branch,
|
|
38
|
+
milestone_id,
|
|
39
|
+
milestone_number,
|
|
40
|
+
ship_subject,
|
|
41
|
+
)
|
|
42
|
+
from pipeline_state import PipelineState
|
|
43
|
+
from isolation import publication_base
|
|
44
|
+
import _common
|
|
45
|
+
import worktree_paths
|
|
46
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
47
|
+
from scripts.pipeline_git import (
|
|
48
|
+
bound_branch_name,
|
|
49
|
+
default_branch_name,
|
|
50
|
+
integrate_commit_body,
|
|
51
|
+
integrate_subject,
|
|
52
|
+
is_bound_branch,
|
|
53
|
+
milestone_id,
|
|
54
|
+
milestone_number,
|
|
55
|
+
ship_subject,
|
|
56
|
+
)
|
|
57
|
+
from scripts.pipeline_state import PipelineState
|
|
58
|
+
from scripts.isolation import publication_base
|
|
59
|
+
from scripts import _common
|
|
60
|
+
from scripts import worktree_paths
|
|
45
61
|
|
|
46
62
|
|
|
47
63
|
if __package__:
|
|
@@ -541,8 +557,8 @@ def ensure_integration_tag(
|
|
|
541
557
|
|
|
542
558
|
def github_repository(project: Path) -> str:
|
|
543
559
|
remote = archive_milestone.require_git_success(
|
|
544
|
-
run_git(project, "
|
|
545
|
-
"
|
|
560
|
+
run_git(project, "config", "--get", "remote.origin.url"),
|
|
561
|
+
"read configured origin URL",
|
|
546
562
|
)
|
|
547
563
|
patterns = (
|
|
548
564
|
r"https://github\.com/([^/]+/[^/]+?)(?:\.git)?$",
|
|
@@ -701,8 +717,9 @@ def update_pull_request_body(
|
|
|
701
717
|
archive_path: str,
|
|
702
718
|
ship_commit: str,
|
|
703
719
|
branch: str,
|
|
720
|
+
expected_body: Optional[str] = None,
|
|
704
721
|
) -> dict:
|
|
705
|
-
expected = pull_request_body(archive_path, ship_commit, branch)
|
|
722
|
+
expected = expected_body if expected_body is not None else pull_request_body(archive_path, ship_commit, branch)
|
|
706
723
|
current = pull.get("body")
|
|
707
724
|
if isinstance(current, str) and current.rstrip().endswith(PR_CREDIT_LINE):
|
|
708
725
|
return pull
|
|
@@ -1394,6 +1411,12 @@ def validate_integrated(
|
|
|
1394
1411
|
raise ArchiveError(f"published {ref} differs from local integration evidence")
|
|
1395
1412
|
if pull_request is not None:
|
|
1396
1413
|
result["pull_request"] = pull_request
|
|
1414
|
+
for row in shipped.get("members", []):
|
|
1415
|
+
proven = validate_member_integrated(project, row["name"], configured, row["reviewed_head"], refresh=refresh)
|
|
1416
|
+
if (proven["merge"], proven["tag"]) != (row["merge"], row["tag"]):
|
|
1417
|
+
raise ArchiveError(f"member {row['name']} integration differs from the ship commit")
|
|
1418
|
+
if "members" in shipped:
|
|
1419
|
+
result["members"] = shipped["members"]
|
|
1397
1420
|
return result
|
|
1398
1421
|
|
|
1399
1422
|
|
|
@@ -1457,3 +1480,390 @@ def require_published_integration(
|
|
|
1457
1480
|
raise ArchiveError(
|
|
1458
1481
|
f"published milestone tag {tag_name} does not point at the integration merge"
|
|
1459
1482
|
)
|
|
1483
|
+
|
|
1484
|
+
|
|
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, and tag."""
|
|
1487
|
+
try:
|
|
1488
|
+
import isolation
|
|
1489
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
1490
|
+
from scripts import isolation
|
|
1491
|
+
try:
|
|
1492
|
+
checkout, project = isolation.member_checkout(coordinator.resolve(), member)
|
|
1493
|
+
except isolation.IsolationError as error:
|
|
1494
|
+
raise ArchiveError(str(error)) from error
|
|
1495
|
+
archive_name = PurePosixPath(archive_path).name
|
|
1496
|
+
identifier = milestone_id(milestone_number(archive_name))
|
|
1497
|
+
bound = f"gsd-path/{project}-{identifier}"
|
|
1498
|
+
subject = f"integrate: {project} {identifier} — merge {bound} into main"
|
|
1499
|
+
return checkout, project, bound, subject, f"milestone/{project}-{archive_name}"
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
def _member_merge_body(archive_path: str, member: str, reviewed_head: str) -> str:
|
|
1503
|
+
return f"Archive: {archive_path}\nMember: {member}\nReviewed-HEAD: {reviewed_head}"
|
|
1504
|
+
|
|
1505
|
+
|
|
1506
|
+
def member_pull_request_tag_message(tag_name: str, pull_request: str, reviewed_head: str, landing: str) -> str:
|
|
1507
|
+
return (f"milestone {tag_name.removeprefix('milestone/')}\n\nMode: pull-request\n"
|
|
1508
|
+
f"Pull-Request: {pull_request}\nReviewed-HEAD: {reviewed_head}\nLanding: {landing}")
|
|
1509
|
+
|
|
1510
|
+
|
|
1511
|
+
def member_pull_request_tag_metadata(contents: str) -> dict[str, str]:
|
|
1512
|
+
fields: dict[str, str] = {}
|
|
1513
|
+
for line in contents.splitlines():
|
|
1514
|
+
match = re.fullmatch(r"(Mode|Pull-Request|Reviewed-HEAD|Landing): (.+)", line)
|
|
1515
|
+
if match:
|
|
1516
|
+
if match.group(1) in fields:
|
|
1517
|
+
raise ArchiveError("pull-request member tag repeats metadata")
|
|
1518
|
+
fields[match.group(1)] = match.group(2)
|
|
1519
|
+
if set(fields) != {"Mode", "Pull-Request", "Reviewed-HEAD", "Landing"}:
|
|
1520
|
+
raise ArchiveError("pull-request member tag is missing metadata")
|
|
1521
|
+
if fields["Mode"] != "pull-request":
|
|
1522
|
+
raise ArchiveError("pull-request member tag has the wrong mode")
|
|
1523
|
+
return fields
|
|
1524
|
+
|
|
1525
|
+
|
|
1526
|
+
def _find_member_merge(checkout: Path, subject: str, body: str, reviewed_head: str) -> Optional[str]:
|
|
1527
|
+
"""The member merge in origin/main's first-parent history, if published."""
|
|
1528
|
+
log = archive_milestone.require_git_success(
|
|
1529
|
+
run_git(checkout, "log", "--first-parent", "--format=%H%x00%s", "origin/main"),
|
|
1530
|
+
"inspect member origin/main first-parent history",
|
|
1531
|
+
)
|
|
1532
|
+
matches = [commit for commit, _, text in (line.partition("\x00") for line in log.splitlines())
|
|
1533
|
+
if text == subject]
|
|
1534
|
+
if len(matches) > 1:
|
|
1535
|
+
raise ArchiveError(f"multiple member merges named {subject!r} on origin/main")
|
|
1536
|
+
if not matches:
|
|
1537
|
+
return None
|
|
1538
|
+
_require_member_merge(checkout, matches[0], subject, body, reviewed_head)
|
|
1539
|
+
return matches[0]
|
|
1540
|
+
|
|
1541
|
+
|
|
1542
|
+
def _require_member_merge(checkout: Path, commit: str, subject: str, body: str, reviewed_head: str) -> str:
|
|
1543
|
+
"""Check a canonical member merge; return its first parent."""
|
|
1544
|
+
archive_milestone.require_canonical_commit_body(checkout, commit, subject, body, "member integration")
|
|
1545
|
+
parents = archive_milestone.require_git_success(
|
|
1546
|
+
run_git(checkout, "rev-list", "--parents", "-n", "1", commit), "inspect member merge parents"
|
|
1547
|
+
).split()
|
|
1548
|
+
if len(parents) != 3 or parents[2] != reviewed_head:
|
|
1549
|
+
raise ArchiveError("member merge second parent is not the member reviewed HEAD")
|
|
1550
|
+
return parents[1]
|
|
1551
|
+
|
|
1552
|
+
|
|
1553
|
+
def _create_member_merge(checkout: Path, project: str, archive_path: str, subject: str, body: str,
|
|
1554
|
+
reviewed_head: str, origin_main: str) -> str:
|
|
1555
|
+
identifier = milestone_id(milestone_number(PurePosixPath(archive_path).name))
|
|
1556
|
+
branch = f"gsd-path-integrate/{project}-{identifier}"
|
|
1557
|
+
try:
|
|
1558
|
+
worktree = worktree_paths.worktree_path(checkout, "integrate", f"{project}-{identifier}", pin=True)
|
|
1559
|
+
except (ValueError, OSError) as error:
|
|
1560
|
+
raise ArchiveError(str(error)) from error
|
|
1561
|
+
interrupted = registered_worktree(checkout, branch)
|
|
1562
|
+
if interrupted is not None and optional_ref(interrupted, "MERGE_HEAD") is not None:
|
|
1563
|
+
archive_milestone.require_git_success(run_git(interrupted, "merge", "--abort"),
|
|
1564
|
+
"abort interrupted member merge")
|
|
1565
|
+
remove_registered_worktree(checkout, branch, worktree)
|
|
1566
|
+
tip = optional_ref(checkout, f"refs/heads/{branch}")
|
|
1567
|
+
if tip is not None:
|
|
1568
|
+
base_tip = run_git(checkout, "merge-base", "--is-ancestor", tip, origin_main)
|
|
1569
|
+
if base_tip.returncode != 0:
|
|
1570
|
+
if base_tip.returncode != 1:
|
|
1571
|
+
archive_milestone.require_git_success(base_tip, "inspect interrupted member integration base")
|
|
1572
|
+
if _require_member_merge(checkout, tip, subject, body, reviewed_head) == origin_main:
|
|
1573
|
+
return tip
|
|
1574
|
+
delete_integration_branch(checkout, branch, tip)
|
|
1575
|
+
archive_milestone.require_git_success(
|
|
1576
|
+
run_git(checkout, "worktree", "add", "--quiet", "-b", branch, str(worktree), origin_main),
|
|
1577
|
+
"create member integration worktree",
|
|
1578
|
+
)
|
|
1579
|
+
merged = run_git(worktree, "merge", "--no-ff", "-m", subject, "-m", body, reviewed_head)
|
|
1580
|
+
if merged.returncode != 0:
|
|
1581
|
+
conflicts = run_git(worktree, "diff", "--name-only", "--diff-filter=U").stdout.split()
|
|
1582
|
+
if optional_ref(worktree, "MERGE_HEAD") is not None:
|
|
1583
|
+
run_git(worktree, "merge", "--abort")
|
|
1584
|
+
remove_registered_worktree(checkout, branch, worktree)
|
|
1585
|
+
delete_integration_branch(checkout, branch, origin_main)
|
|
1586
|
+
raise ArchiveError("member integration merge failed; Git aborted it without resolving files: "
|
|
1587
|
+
+ (", ".join(conflicts) or (merged.stderr or merged.stdout).strip()))
|
|
1588
|
+
merge = archive_milestone.require_git_success(run_git(worktree, "rev-parse", "HEAD"), "resolve member merge")
|
|
1589
|
+
if _require_member_merge(worktree, merge, subject, body, reviewed_head) != origin_main:
|
|
1590
|
+
raise ArchiveError("member merge first parent is not origin/main")
|
|
1591
|
+
remove_registered_worktree(checkout, branch, worktree)
|
|
1592
|
+
return merge
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
def _push_member_ref(checkout: Path, project: str, source: str, ref: str, sha: str, lease: Optional[str]) -> None:
|
|
1596
|
+
"""Authorize `ref` at `sha` for the member pre-push hook, then publish it."""
|
|
1597
|
+
try:
|
|
1598
|
+
import members
|
|
1599
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
1600
|
+
from scripts import members
|
|
1601
|
+
members.authorize_push(checkout, project, ref, sha)
|
|
1602
|
+
arguments = ["push"] + ([f"--force-with-lease={ref}:{lease}"] if lease is not None else [])
|
|
1603
|
+
archive_milestone.require_git_success(
|
|
1604
|
+
run_git(checkout, *arguments, "origin", f"{source}:{ref}"), f"publish member {ref}"
|
|
1605
|
+
)
|
|
1606
|
+
|
|
1607
|
+
|
|
1608
|
+
def _publish_member_bound(checkout: Path, project: str, member: str, bound: str, reviewed_head: str) -> None:
|
|
1609
|
+
bound_ref = f"refs/heads/{bound}"
|
|
1610
|
+
remote_bound = live_remote_ref(checkout, bound_ref)
|
|
1611
|
+
if remote_bound is None:
|
|
1612
|
+
_push_member_ref(checkout, project, reviewed_head, bound_ref, reviewed_head, "")
|
|
1613
|
+
elif remote_bound != reviewed_head:
|
|
1614
|
+
raise ArchiveError(f"member {member} origin/{bound} moved: {remote_bound}")
|
|
1615
|
+
|
|
1616
|
+
|
|
1617
|
+
def _require_member_close(checkout: Path, member: str, bound: str, reviewed_head: str) -> None:
|
|
1618
|
+
if optional_ref(checkout, f"refs/heads/{bound}") != reviewed_head:
|
|
1619
|
+
raise ArchiveError(f"member {member} {bound} is not at its reviewed HEAD {reviewed_head}")
|
|
1620
|
+
if default_branch_name(refresh_origin(checkout)["remote_default"]) != "main":
|
|
1621
|
+
raise ArchiveError(f"member {member} remote default must be main")
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
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 main, then tag."""
|
|
1626
|
+
checkout, project, bound, subject, tag_name = _member_names(coordinator, member, archive_path)
|
|
1627
|
+
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)
|
|
1630
|
+
if merge is None:
|
|
1631
|
+
origin_main = archive_milestone.require_git_success(
|
|
1632
|
+
run_git(checkout, "rev-parse", "origin/main"), "resolve member origin/main")
|
|
1633
|
+
ancestry = run_git(checkout, "merge-base", "--is-ancestor", reviewed_head, origin_main)
|
|
1634
|
+
if ancestry.returncode == 0:
|
|
1635
|
+
raise ArchiveError("member reviewed HEAD reached origin/main outside Path; user-approved recovery required")
|
|
1636
|
+
if ancestry.returncode != 1:
|
|
1637
|
+
archive_milestone.require_git_success(ancestry, "inspect member reviewed HEAD ancestry")
|
|
1638
|
+
merge = _create_member_merge(checkout, project, archive_path, subject, body, reviewed_head, origin_main)
|
|
1639
|
+
# Publish in order: bound branch, main, tag; a rerun publishes only what is missing.
|
|
1640
|
+
_publish_member_bound(checkout, project, member, bound, reviewed_head)
|
|
1641
|
+
if run_git(checkout, "merge-base", "--is-ancestor", merge, "origin/main").returncode != 0:
|
|
1642
|
+
_push_member_ref(checkout, project, merge, "refs/heads/main", merge, None)
|
|
1643
|
+
archive_milestone.require_git_success(
|
|
1644
|
+
run_git(checkout, "update-ref", "refs/remotes/origin/main", merge), "refresh member origin/main")
|
|
1645
|
+
tag_object, tag_published = ensure_integration_tag(checkout, tag_name, merge)
|
|
1646
|
+
if not tag_published:
|
|
1647
|
+
_push_member_ref(checkout, project, f"refs/tags/{tag_name}", f"refs/tags/{tag_name}", tag_object, None)
|
|
1648
|
+
archive_milestone.require_git_success(
|
|
1649
|
+
run_git(checkout, "update-ref", f"refs/remotes/origin/tags/{tag_name}", tag_object),
|
|
1650
|
+
"refresh member milestone-tag ref")
|
|
1651
|
+
identifier = milestone_id(milestone_number(PurePosixPath(archive_path).name))
|
|
1652
|
+
delete_integration_branch(checkout, f"gsd-path-integrate/{project}-{identifier}", merge)
|
|
1653
|
+
return validate_member_integrated(coordinator, member, archive_path, reviewed_head)
|
|
1654
|
+
|
|
1655
|
+
|
|
1656
|
+
def integrate_member_pull_request(coordinator: Path, member: str, archive_path: str, reviewed_head: str) -> dict:
|
|
1657
|
+
"""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)
|
|
1659
|
+
body = _member_merge_body(archive_path, member, reviewed_head)
|
|
1660
|
+
_require_member_close(checkout, member, bound, reviewed_head)
|
|
1661
|
+
require_github_authentication()
|
|
1662
|
+
repository = github_repository(checkout)
|
|
1663
|
+
pull = find_pull_request(repository, bound, reviewed_head)
|
|
1664
|
+
if pull is None or pull["merged_at"] is None:
|
|
1665
|
+
if pull is not None and pull["state"] != "open":
|
|
1666
|
+
raise ArchiveError(f"member {member} pull request was closed without merging")
|
|
1667
|
+
_publish_member_bound(checkout, project, member, bound, reviewed_head)
|
|
1668
|
+
if pull is None:
|
|
1669
|
+
pull = require_pull_request_shape(github_api_json(
|
|
1670
|
+
f"repos/{repository}/pulls", "--method", "POST", "-f", f"title={subject}",
|
|
1671
|
+
"-f", f"head={bound}", "-f", "base=main", "-f", f"body={body}\n\n---\n{PR_CREDIT_LINE}"))
|
|
1672
|
+
require_pull_request_identity(repository, pull, bound, reviewed_head)
|
|
1673
|
+
else:
|
|
1674
|
+
pull = update_pull_request_body(
|
|
1675
|
+
repository, pull, archive_path, reviewed_head, bound,
|
|
1676
|
+
f"{body}\n\n---\n{PR_CREDIT_LINE}",
|
|
1677
|
+
)
|
|
1678
|
+
require_pull_request_merge_provenance(repository, pull["number"])
|
|
1679
|
+
return {"status": "awaiting-merge", "mode": "pull-request", "member": member,
|
|
1680
|
+
"pull_request": pull["html_url"]}
|
|
1681
|
+
merge = pull["merge_commit_sha"]
|
|
1682
|
+
if pull["state"] != "closed" or not isinstance(merge, str):
|
|
1683
|
+
raise ArchiveError(f"member {member} pull request merge metadata is invalid")
|
|
1684
|
+
require_pull_request_merge_provenance(repository, pull["number"], merge)
|
|
1685
|
+
refresh_origin(checkout)
|
|
1686
|
+
require_pull_request_merge(checkout, merge, reviewed_head, "origin/main")
|
|
1687
|
+
pull = update_pull_request_body(
|
|
1688
|
+
repository, pull, archive_path, reviewed_head, bound,
|
|
1689
|
+
f"{body}\n\n---\n{PR_CREDIT_LINE}",
|
|
1690
|
+
)
|
|
1691
|
+
message = member_pull_request_tag_message(tag_name, pull["html_url"], reviewed_head, merge)
|
|
1692
|
+
tag_object, tag_published = ensure_integration_tag(checkout, tag_name, merge, message)
|
|
1693
|
+
if not tag_published:
|
|
1694
|
+
_push_member_ref(checkout, project, f"refs/tags/{tag_name}", f"refs/tags/{tag_name}", tag_object, None)
|
|
1695
|
+
archive_milestone.require_git_success(
|
|
1696
|
+
run_git(checkout, "update-ref", f"refs/remotes/origin/tags/{tag_name}", tag_object),
|
|
1697
|
+
"refresh member milestone-tag ref")
|
|
1698
|
+
return validate_member_integrated(coordinator, member, archive_path, reviewed_head)
|
|
1699
|
+
|
|
1700
|
+
|
|
1701
|
+
def validate_member_integrated(
|
|
1702
|
+
coordinator: Path, member: str, archive_path: str, reviewed_head: str, *, refresh: bool = True
|
|
1703
|
+
) -> dict:
|
|
1704
|
+
"""Prove the member merge is on origin/main and its published tag points at it."""
|
|
1705
|
+
checkout, _, bound, subject, tag_name = _member_names(coordinator, member, archive_path)
|
|
1706
|
+
tracking = f"refs/remotes/origin/tags/{tag_name}"
|
|
1707
|
+
if refresh:
|
|
1708
|
+
refresh_origin(checkout)
|
|
1709
|
+
published = live_remote_ref(checkout, f"refs/tags/{tag_name}")
|
|
1710
|
+
else:
|
|
1711
|
+
published = optional_ref(checkout, tracking)
|
|
1712
|
+
contents = ""
|
|
1713
|
+
if refresh and published is not None:
|
|
1714
|
+
archive_milestone.require_git_success(
|
|
1715
|
+
run_git(checkout, "update-ref", tracking, published), "refresh member milestone-tag ref")
|
|
1716
|
+
if published is not None:
|
|
1717
|
+
contents = run_git(checkout, "for-each-ref", "--format=%(contents)", tracking).stdout
|
|
1718
|
+
if "Mode: pull-request" in contents.splitlines():
|
|
1719
|
+
metadata = member_pull_request_tag_metadata(contents)
|
|
1720
|
+
if metadata["Reviewed-HEAD"] != reviewed_head:
|
|
1721
|
+
raise ArchiveError(f"member {member} tag {tag_name} names a different reviewed HEAD")
|
|
1722
|
+
merge = archive_milestone.require_git_success(
|
|
1723
|
+
run_git(checkout, "rev-parse", f"{tracking}^{{commit}}"), "resolve member tag target")
|
|
1724
|
+
if metadata["Landing"] != merge:
|
|
1725
|
+
raise ArchiveError(f"member {member} tag {tag_name} names a different landing")
|
|
1726
|
+
require_github_authentication()
|
|
1727
|
+
repository = github_repository(checkout)
|
|
1728
|
+
pull = find_pull_request(repository, bound, reviewed_head)
|
|
1729
|
+
if pull is None:
|
|
1730
|
+
raise ArchiveError(f"member {member} GitHub pull request is missing")
|
|
1731
|
+
if pull["html_url"] != metadata["Pull-Request"]:
|
|
1732
|
+
raise ArchiveError(f"member {member} tag {tag_name} names the wrong pull request")
|
|
1733
|
+
if (pull["state"] != "closed" or pull["merged_at"] is None
|
|
1734
|
+
or pull["merge_commit_sha"] != merge):
|
|
1735
|
+
raise ArchiveError(f"member {member} GitHub pull request is not merged at the tagged landing")
|
|
1736
|
+
require_pull_request_merge_provenance(repository, pull["number"], merge)
|
|
1737
|
+
require_pull_request_merge(checkout, merge, reviewed_head, "origin/main")
|
|
1738
|
+
require_annotated_tag(
|
|
1739
|
+
checkout, tracking, merge, f"member tag {tag_name}",
|
|
1740
|
+
member_pull_request_tag_message(tag_name, pull["html_url"], reviewed_head, merge),
|
|
1741
|
+
)
|
|
1742
|
+
else:
|
|
1743
|
+
merge = _find_member_merge(checkout, subject, _member_merge_body(archive_path, member, reviewed_head),
|
|
1744
|
+
reviewed_head)
|
|
1745
|
+
if merge is None:
|
|
1746
|
+
raise ArchiveError(f"member {member} has no {subject!r} merge on origin/main")
|
|
1747
|
+
if published is None:
|
|
1748
|
+
raise ArchiveError(f"member {member} tag {tag_name} is not published")
|
|
1749
|
+
require_annotated_tag(checkout, tracking, merge, f"member tag {tag_name}")
|
|
1750
|
+
return {"member": member, "merge": merge, "tag": tag_name, "tag_object": published}
|
|
1751
|
+
|
|
1752
|
+
|
|
1753
|
+
def retire_member(coordinator: Path, member: str, archive_path: str, reviewed_head: str,
|
|
1754
|
+
merge: str, tag: str) -> dict:
|
|
1755
|
+
"""After integration, delete a member's bound branch everywhere and clear its authorizations."""
|
|
1756
|
+
try:
|
|
1757
|
+
import members
|
|
1758
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
1759
|
+
from scripts import members
|
|
1760
|
+
checkout, project, bound, _, tag_name = _member_names(coordinator, member, archive_path)
|
|
1761
|
+
proven = validate_member_integrated(coordinator, member, archive_path, reviewed_head)
|
|
1762
|
+
if (proven["merge"], proven["tag"]) != (merge, tag):
|
|
1763
|
+
raise ArchiveError(f"member {member} integration differs from the ship commit")
|
|
1764
|
+
bound_ref = f"refs/heads/{bound}"
|
|
1765
|
+
local = optional_ref(checkout, bound_ref)
|
|
1766
|
+
if local is not None and local != reviewed_head:
|
|
1767
|
+
raise ArchiveError(f"member {member} {bound} moved: {local}")
|
|
1768
|
+
holder = registered_worktree(checkout, bound)
|
|
1769
|
+
if holder is not None:
|
|
1770
|
+
try:
|
|
1771
|
+
expected = worktree_paths.worktree_path(
|
|
1772
|
+
checkout, "bound", bound.removeprefix("gsd-path/")
|
|
1773
|
+
).resolve()
|
|
1774
|
+
except (ValueError, OSError) as error:
|
|
1775
|
+
raise ArchiveError(str(error)) from error
|
|
1776
|
+
if holder != expected:
|
|
1777
|
+
raise ArchiveError(f"member {member} bound checkout is outside Path's workspace: {holder}")
|
|
1778
|
+
status = archive_milestone.require_git_success(
|
|
1779
|
+
run_git(holder, "status", "--porcelain", "--untracked-files=all"),
|
|
1780
|
+
"inspect member bound checkout",
|
|
1781
|
+
)
|
|
1782
|
+
if status:
|
|
1783
|
+
raise ArchiveError(f"member {member} bound checkout is not clean: {holder}")
|
|
1784
|
+
remote = live_remote_ref(checkout, bound_ref)
|
|
1785
|
+
if remote is not None:
|
|
1786
|
+
if remote != reviewed_head:
|
|
1787
|
+
raise ArchiveError(f"member {member} origin/{bound} moved: {remote}")
|
|
1788
|
+
members.authorize_delete(checkout, project, bound_ref, reviewed_head)
|
|
1789
|
+
archive_milestone.require_git_success(
|
|
1790
|
+
run_git(checkout, "push", f"--force-with-lease={bound_ref}:{reviewed_head}", "origin", "--delete", bound),
|
|
1791
|
+
f"delete member origin/{bound}")
|
|
1792
|
+
if holder is not None:
|
|
1793
|
+
archive_milestone.require_git_success(run_git(checkout, "worktree", "remove", str(holder)),
|
|
1794
|
+
"remove member bound checkout")
|
|
1795
|
+
if local is not None:
|
|
1796
|
+
archive_milestone.require_git_success(run_git(checkout, "update-ref", "-d", bound_ref, reviewed_head),
|
|
1797
|
+
"delete member bound branch")
|
|
1798
|
+
run_git(checkout, "update-ref", "-d", f"refs/remotes/origin/{bound}")
|
|
1799
|
+
for kind, ref in (("push", bound_ref), ("push", "refs/heads/main"), ("push", f"refs/tags/{tag_name}"),
|
|
1800
|
+
("delete", bound_ref)):
|
|
1801
|
+
members.clear_authorization(checkout, project, kind, ref)
|
|
1802
|
+
return {"member": member, "retired": bound}
|
|
1803
|
+
|
|
1804
|
+
|
|
1805
|
+
def close_members(repo: Path) -> dict:
|
|
1806
|
+
"""Close each locked member in lock order before the coordinator ship commit; resumable."""
|
|
1807
|
+
try:
|
|
1808
|
+
import check_handoffs
|
|
1809
|
+
import members
|
|
1810
|
+
from pipeline_git import ship_commit_body
|
|
1811
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
1812
|
+
from scripts import check_handoffs, members
|
|
1813
|
+
from scripts.pipeline_git import ship_commit_body
|
|
1814
|
+
project = repo.resolve()
|
|
1815
|
+
state, _, _ = archive_milestone.strict_state(project)
|
|
1816
|
+
if (state.phase, state.status) != ("ship", "active") or state.archive is None:
|
|
1817
|
+
raise ArchiveError("close-members requires ship/active with a prepared archive")
|
|
1818
|
+
archive = project / state.archive
|
|
1819
|
+
final_text = (archive / "review" / "FINAL.md").read_text(encoding="utf-8")
|
|
1820
|
+
reviewed = check_handoffs._reviewed_head(final_text, "FINAL.md")
|
|
1821
|
+
lock = archive / "build" / "members.json"
|
|
1822
|
+
locked = json.loads(lock.read_text(encoding="utf-8"))["members"] if lock.is_file() else []
|
|
1823
|
+
try:
|
|
1824
|
+
heads = check_handoffs.member_reviewed_heads(final_text, "FINAL.md")
|
|
1825
|
+
except check_handoffs.HandoffError as error:
|
|
1826
|
+
raise ArchiveError(str(error)) from error
|
|
1827
|
+
if sorted(heads) != sorted(entry["name"] for entry in locked):
|
|
1828
|
+
raise ArchiveError("FINAL.md Member reviewed HEAD lines must name each locked member")
|
|
1829
|
+
common = Path(archive_milestone.require_git_success(
|
|
1830
|
+
run_git(project, "rev-parse", "--path-format=absolute", "--git-common-dir"), "resolve Git common dir"))
|
|
1831
|
+
archive_name = PurePosixPath(state.archive).name
|
|
1832
|
+
try:
|
|
1833
|
+
modes = {item["name"]: item["integration"] for item in members.read_members(project)} if locked else {}
|
|
1834
|
+
rows = {row["name"]: row for row in members.read_member_close(common, archive_name)}
|
|
1835
|
+
except members.MembersError as error:
|
|
1836
|
+
raise ArchiveError(str(error)) from error
|
|
1837
|
+
def save() -> None:
|
|
1838
|
+
members.write_member_close(common, archive_name, [rows[item["name"]] for item in locked if item["name"] in rows])
|
|
1839
|
+
|
|
1840
|
+
closed = []
|
|
1841
|
+
for entry in locked:
|
|
1842
|
+
name = entry["name"]
|
|
1843
|
+
mode = modes.get(name, "default")
|
|
1844
|
+
mode = state.integration if mode == "default" else mode
|
|
1845
|
+
row = rows.get(name) or {"name": name, "mode": mode, "reviewed_head": heads[name],
|
|
1846
|
+
"status": "pending", "merge": None, "tag": None}
|
|
1847
|
+
if row["reviewed_head"] != heads[name] or row["mode"] != mode:
|
|
1848
|
+
raise ArchiveError(f"member-close journal for {name} differs from FINAL.md or MEMBERS.md")
|
|
1849
|
+
if row["status"] == "integrated":
|
|
1850
|
+
result = validate_member_integrated(project, name, state.archive, heads[name])
|
|
1851
|
+
if (result["merge"], result["tag"]) != (row["merge"], row["tag"]):
|
|
1852
|
+
raise ArchiveError(f"member {name} integration differs from its member-close journal")
|
|
1853
|
+
closed.append(row)
|
|
1854
|
+
continue
|
|
1855
|
+
# Journal pending before the member moves; a later member waits for this one.
|
|
1856
|
+
rows[name] = row
|
|
1857
|
+
save()
|
|
1858
|
+
if mode == "direct":
|
|
1859
|
+
result = integrate_member(project, name, state.archive, heads[name])
|
|
1860
|
+
else:
|
|
1861
|
+
result = integrate_member_pull_request(project, name, state.archive, heads[name])
|
|
1862
|
+
if result.get("status") == "awaiting-merge":
|
|
1863
|
+
row["pull_request"] = result["pull_request"]
|
|
1864
|
+
save()
|
|
1865
|
+
return {"status": "awaiting-merge", "member": name, "pull_request": result["pull_request"]}
|
|
1866
|
+
row.update(status="integrated", merge=result["merge"], tag=result["tag"])
|
|
1867
|
+
save()
|
|
1868
|
+
closed.append(row)
|
|
1869
|
+
return {"status": "integrated", "members": closed, "body": ship_commit_body(state.archive, reviewed, closed)}
|