@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
|
@@ -8,20 +8,29 @@ canonical review files, and every skeptic file for this wave. It groups
|
|
|
8
8
|
blocking findings by canonical criterion locator (`t001_ac2`, `sc3`,
|
|
9
9
|
`t001_files`, `t001_interface_contract`), collapses true duplicate
|
|
10
10
|
observations, selects the groups that need a skeptic, carries refutations
|
|
11
|
-
forward across cycles, batches fix-task groups by disjoint file
|
|
12
|
-
structural blockers out of skeptic accounting, and reports
|
|
13
|
-
cap is reached. It never decides the wave verdict.
|
|
11
|
+
forward across cycles, batches fix-task groups by repo and disjoint file
|
|
12
|
+
scope, keeps structural blockers out of skeptic accounting, and reports
|
|
13
|
+
whether the cycle cap is reached. It never decides the wave verdict.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
18
|
import argparse
|
|
19
|
+
import functools
|
|
19
20
|
import json
|
|
20
21
|
import re
|
|
21
22
|
import sys
|
|
23
|
+
|
|
24
|
+
# Runtime helpers must not modify their immutable installation.
|
|
25
|
+
sys.dont_write_bytecode = True
|
|
22
26
|
from pathlib import Path
|
|
23
27
|
from typing import Dict, List, Optional, Sequence
|
|
24
28
|
|
|
29
|
+
try:
|
|
30
|
+
from . import _common
|
|
31
|
+
except ImportError: # installed standalone helper
|
|
32
|
+
import _common
|
|
33
|
+
|
|
25
34
|
DEFAULT_PROJECT_DIR = ".project"
|
|
26
35
|
DEFAULT_MAX_REVIEW_CYCLES = 3
|
|
27
36
|
CONFIG_KEYS = ("max_review_cycles", "wave_budget", "review_panel", "finding_skeptics")
|
|
@@ -143,15 +152,25 @@ def _frontmatter_list(frontmatter: str, key: str) -> List[str]:
|
|
|
143
152
|
|
|
144
153
|
|
|
145
154
|
def _numbered_items(section: Optional[str]) -> List[str]:
|
|
146
|
-
|
|
155
|
+
"""Numbered criteria; each also carries its text without sub-bullet markers (issue #392)."""
|
|
147
156
|
if section is None:
|
|
148
|
-
return
|
|
157
|
+
return []
|
|
158
|
+
bare = _numbered_texts(_common.without_sub_bullet_markers(section))
|
|
159
|
+
return [_common.Criterion(text, other) for text, other in zip(_numbered_texts(section), bare)]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _numbered_texts(section: str) -> List[str]:
|
|
163
|
+
items: List[str] = []
|
|
164
|
+
open_item = False
|
|
149
165
|
for line in _strip_comments(section).splitlines():
|
|
150
166
|
match = NUMBERED_ITEM.match(line.strip())
|
|
151
167
|
if match:
|
|
152
168
|
items.append(_normalize(match.group(2)))
|
|
153
|
-
|
|
169
|
+
open_item = True
|
|
170
|
+
elif open_item and line[:1].isspace() and line.strip():
|
|
154
171
|
items[-1] = _normalize(items[-1] + " " + line.strip())
|
|
172
|
+
elif line.strip():
|
|
173
|
+
open_item = False
|
|
155
174
|
return items
|
|
156
175
|
|
|
157
176
|
|
|
@@ -183,9 +202,11 @@ def load_wave_tasks(project: Path, wave: int) -> Dict[str, dict]:
|
|
|
183
202
|
if not task_wave or int(task_wave.group(1)) != wave:
|
|
184
203
|
continue
|
|
185
204
|
body = parts[2]
|
|
205
|
+
member = re.search(r"(?m)^repo:[ \t]*(?P<name>[^\s#]+)", frontmatter)
|
|
186
206
|
tasks[named.group("id")] = {
|
|
187
207
|
"id": named.group("id"),
|
|
188
208
|
"path": str(path),
|
|
209
|
+
"repo": member.group("name").strip("\"'") if member else None, # None is the coordinator
|
|
189
210
|
"files": _frontmatter_list(frontmatter, "files"),
|
|
190
211
|
"acceptance": _numbered_items(_section(body, "Acceptance criteria")),
|
|
191
212
|
"interface": [
|
|
@@ -270,17 +291,20 @@ def parse_lens(text: str, lens: str, tasks: Dict[str, dict]) -> dict:
|
|
|
270
291
|
continue
|
|
271
292
|
zones[zone].append(line)
|
|
272
293
|
for observation in _fail_items(zones["criteria"]):
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
elif criterion in task["interface"]:
|
|
278
|
-
locator = f"{task_id.lower()}_interface_contract"
|
|
279
|
-
if locator is None:
|
|
294
|
+
# Issue #392: match as the wave gate does, then report the task's own text.
|
|
295
|
+
criteria = task["acceptance"] + task["interface"]
|
|
296
|
+
quoted = _common.quoted_criterion(observation, criteria)
|
|
297
|
+
if quoted is None:
|
|
280
298
|
problems.append(
|
|
281
|
-
f"{lens} lens {task_id} finding names no task criterion:
|
|
299
|
+
f"{lens} lens {task_id} finding names no task criterion: "
|
|
300
|
+
f"{_criterion_of(observation)}"
|
|
282
301
|
)
|
|
283
302
|
continue
|
|
303
|
+
criterion = criteria[quoted[0]]
|
|
304
|
+
if quoted[0] < len(task["acceptance"]):
|
|
305
|
+
locator = f"{task_id.lower()}_ac{quoted[0] + 1}"
|
|
306
|
+
else:
|
|
307
|
+
locator = f"{task_id.lower()}_interface_contract"
|
|
284
308
|
findings.append(
|
|
285
309
|
{"locator": locator, "criterion": criterion, "task": task_id,
|
|
286
310
|
"lens": lens, "observation": observation}
|
|
@@ -433,21 +457,27 @@ def _earlier_lens_locators(project: Path, wave: int, cycle: int, depth: str, tas
|
|
|
433
457
|
|
|
434
458
|
|
|
435
459
|
def _fix_batches(groups: Sequence[dict], tasks: Dict[str, dict]) -> List[dict]:
|
|
436
|
-
"""One batch per disjoint file scope: groups whose task files overlap share a batch.
|
|
460
|
+
"""One batch per repo and disjoint file scope: groups whose task files overlap in one repo share a batch.
|
|
461
|
+
|
|
462
|
+
A member batch names its `repo`; a coordinator batch has no such key.
|
|
463
|
+
"""
|
|
437
464
|
|
|
438
465
|
batches: List[dict] = []
|
|
439
466
|
for group in groups:
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
467
|
+
owners = [tasks.get(task_id, {}) for task_id in group["tasks"]]
|
|
468
|
+
for repo in sorted({task.get("repo") or "" for task in owners}) or [""]:
|
|
469
|
+
files = sorted({path for task in owners if (task.get("repo") or "") == repo
|
|
470
|
+
for path in task.get("files", [])})
|
|
471
|
+
merged = {"locators": [group["locator"]], "files": files, **({"repo": repo} if repo else {})}
|
|
472
|
+
keep: List[dict] = []
|
|
473
|
+
for batch in batches:
|
|
474
|
+
if batch.get("repo") == merged.get("repo") and _common.overlapping_paths(batch["files"], files):
|
|
475
|
+
merged["locators"] = batch["locators"] + merged["locators"]
|
|
476
|
+
merged["files"] = sorted(set(batch["files"]) | set(merged["files"]))
|
|
477
|
+
else:
|
|
478
|
+
keep.append(batch)
|
|
479
|
+
keep.append(merged)
|
|
480
|
+
batches = keep
|
|
451
481
|
return batches
|
|
452
482
|
|
|
453
483
|
|
|
@@ -604,7 +634,7 @@ def repair_evidence(repo: Path, project_dir: str, wave: int, cycle: int, task_id
|
|
|
604
634
|
)["tasks"][0]
|
|
605
635
|
if proof["verdict"] != "recovered":
|
|
606
636
|
raise ReviewFindingsError("repair requires a proven landing")
|
|
607
|
-
base
|
|
637
|
+
base = proof["base"] # in the coordinator, where the source review is
|
|
608
638
|
findings = _section(text, "Review findings") or ""
|
|
609
639
|
headings = list(re.finditer(r"(?m)^### ([a-z0-9_]+)\s*$", findings))
|
|
610
640
|
locators = [heading.group(1) for heading in headings]
|
|
@@ -613,10 +643,28 @@ def repair_evidence(repo: Path, project_dir: str, wave: int, cycle: int, task_id
|
|
|
613
643
|
source = compute(repo, project_dir, wave, cycle)
|
|
614
644
|
if source["structural_blockers"]:
|
|
615
645
|
raise ReviewFindingsError("source findings have structural blockers")
|
|
646
|
+
member = fields.get("repo") # a finding in two repos has one batch per repo with the same locators
|
|
616
647
|
batches = [batch for batch in source["fix_batches"]
|
|
617
|
-
if set(batch["locators"]) == set(locators)]
|
|
648
|
+
if batch.get("repo") == member and set(batch["locators"]) == set(locators)]
|
|
618
649
|
if len(batches) != 1:
|
|
619
650
|
raise ReviewFindingsError("repair must carry one eligible source finding batch")
|
|
651
|
+
# A member task's product is in the member repository: its commits, its bound branch, its Verify rows.
|
|
652
|
+
where, tip = repo, [] # the coordinator product is judged in the working tree
|
|
653
|
+
if member:
|
|
654
|
+
where, _, lock = isolation._member_context(repo, member)
|
|
655
|
+
tip = [f"refs/heads/{lock['branch']}"]
|
|
656
|
+
|
|
657
|
+
def entry(landing: dict, path: Path, paths: List[str]) -> dict:
|
|
658
|
+
"""A proven landing as the receipt shows it; a member landing adds its commits in the member."""
|
|
659
|
+
item = {"task": landing["task_id"], "base": landing["base"], "commit": landing["commit"], "files": paths}
|
|
660
|
+
if member:
|
|
661
|
+
item.update(member_base=isolation.task_frontmatter(_read(path, "task"))[0]["member_base"],
|
|
662
|
+
member_commit=landing["landing"])
|
|
663
|
+
return item
|
|
664
|
+
|
|
665
|
+
def at(item: dict, point: str = "commit") -> str:
|
|
666
|
+
return item.get(f"member_{point}", item[point])
|
|
667
|
+
|
|
620
668
|
groups = [group for group in source["groups"] if group["locator"] in locators]
|
|
621
669
|
for index, heading in enumerate(headings):
|
|
622
670
|
end = headings[index + 1].start() if index + 1 < len(headings) else len(findings)
|
|
@@ -627,22 +675,101 @@ def repair_evidence(repo: Path, project_dir: str, wave: int, cycle: int, task_id
|
|
|
627
675
|
raise ReviewFindingsError("repair does not carry the exact criterion and observations")
|
|
628
676
|
frontmatter = text.split("---", 2)[1]
|
|
629
677
|
files = _frontmatter_list(frontmatter, "files")
|
|
630
|
-
|
|
678
|
+
sources = load_wave_tasks(project, wave)
|
|
679
|
+
originals = sorted({task for group in groups for task in group["tasks"] if sources[task]["repo"] == member})
|
|
680
|
+
# A repair names the files it needs. They can be fewer than the batch files, and a plan repair
|
|
681
|
+
# can add a fix site that the source tasks did not declare.
|
|
631
682
|
if (task_id in originals or not set(originals) <= set(_frontmatter_list(frontmatter, "deps"))
|
|
632
|
-
or
|
|
683
|
+
or not files):
|
|
633
684
|
raise ReviewFindingsError("repair dependencies/files do not match its source batch")
|
|
634
|
-
|
|
685
|
+
repair = {"task_file": repair_path.relative_to(repo).as_posix(), **entry(proof, repair_path, files),
|
|
686
|
+
**({"member": member} if member else {})}
|
|
687
|
+
|
|
688
|
+
def differ(start: str, end: List[str], paths: List[str]) -> bool:
|
|
689
|
+
return bool(isolation.run_git(where, "diff", "--quiet", start, *end, "--", *paths).returncode)
|
|
690
|
+
|
|
691
|
+
def before(start: str, end: str) -> bool:
|
|
692
|
+
return not isolation.run_git(where, "merge-base", "--is-ancestor", start, end).returncode
|
|
693
|
+
|
|
694
|
+
@functools.lru_cache(maxsize=None)
|
|
695
|
+
def linked() -> tuple:
|
|
696
|
+
"""Landed repairs of the same findings that `deps` join to this repair, in landing order.
|
|
697
|
+
|
|
698
|
+
A linked task is done, is in the same repo, carries a finding of this repair in its
|
|
699
|
+
`## Review findings`, and depends on this repair or on a linked task, or the reverse.
|
|
700
|
+
"""
|
|
701
|
+
tasks, carried = {task_id: (repair_path, fields)}, {}
|
|
702
|
+
for path in (project / "tasks").glob("T*-*.md"):
|
|
703
|
+
other = _read(path, "task")
|
|
704
|
+
other_fields = isolation.task_frontmatter(other)[0] or {}
|
|
705
|
+
tasks[str(other_fields.get("id"))] = (path, other_fields)
|
|
706
|
+
blocks = re.split(r"(?m)^### ([a-z0-9_]+)\s*$", _section(other, "Review findings") or "")
|
|
707
|
+
carried[str(other_fields.get("id"))] = {
|
|
708
|
+
(locator, _normalize(line)) for locator, block in zip(blocks[1::2], blocks[2::2])
|
|
709
|
+
for line in re.findall(r"(?m)^Criterion:(.*)$", block)}
|
|
710
|
+
|
|
711
|
+
def traced(finding: tuple, seen: frozenset = frozenset()) -> set:
|
|
712
|
+
"""The finding as it started: its locator and criterion in the review of the source tasks.
|
|
713
|
+
|
|
714
|
+
A finding on an acceptance criterion of a fix task (`fix-tasks` copies the carried criteria
|
|
715
|
+
there) is the finding with the same criterion that this fix task carries. Two findings with
|
|
716
|
+
the same criterion text and other locators stay two findings.
|
|
717
|
+
"""
|
|
718
|
+
owner = re.fullmatch(r"t(\d{3})_ac\d+", finding[0])
|
|
719
|
+
sources = () if not owner else {
|
|
720
|
+
item for item in carried.get(f"T{owner.group(1)}", ()) if item[1] == finding[1]} - seen
|
|
721
|
+
return {origin for item in sources for origin in traced(item, seen | {finding})} or {finding}
|
|
722
|
+
|
|
723
|
+
def origins(other: str) -> set:
|
|
724
|
+
return {origin for finding in carried.get(other, ()) for origin in traced(finding)}
|
|
725
|
+
|
|
726
|
+
repairs = {other: task for other, task in tasks.items() if other == task_id or (
|
|
727
|
+
task[1].get("status") == "done" and task[1].get("repo") == member
|
|
728
|
+
and origins(task_id) & origins(other))}
|
|
729
|
+
joined, more = set(), {task_id}
|
|
730
|
+
while more:
|
|
731
|
+
joined |= more
|
|
732
|
+
more = {other for other, (_, other_fields) in repairs.items() if other not in joined and (
|
|
733
|
+
joined & set(other_fields.get("deps") or [])
|
|
734
|
+
or any(other in (repairs[item][1].get("deps") or []) for item in joined))}
|
|
735
|
+
others = sorted(joined - {task_id})
|
|
736
|
+
proofs = isolation.verify_landed_task_files(
|
|
737
|
+
repo, [repairs[other][0] for other in others], f"{project_dir}/tasks", head)["tasks"] if others else []
|
|
738
|
+
links = []
|
|
739
|
+
for landing in proofs:
|
|
740
|
+
path, other_fields = repairs[landing["task_id"]]
|
|
741
|
+
if landing["verdict"] == "recovered":
|
|
742
|
+
links.append({"task_file": path.relative_to(repo).as_posix(),
|
|
743
|
+
**entry(landing, path, list(other_fields.get("files") or []))})
|
|
744
|
+
return tuple(sorted(links, key=lambda link: int(isolation.git_output(where, "rev-list", "--count", at(link)))))
|
|
745
|
+
|
|
746
|
+
def linked_changes(start: str, end: List[str], paths: List[str]) -> Optional[List[dict]]:
|
|
747
|
+
"""The linked repairs whose landings are every change to `paths` from `start` to `end`, else None."""
|
|
748
|
+
if not differ(start, end, paths):
|
|
749
|
+
return []
|
|
750
|
+
links = [link for link in linked()
|
|
751
|
+
if before(start, f"{at(link)}^") and (not end or before(at(link), end[0]))]
|
|
752
|
+
for link in links:
|
|
753
|
+
if differ(start, [f"{at(link)}^"], paths):
|
|
754
|
+
return None
|
|
755
|
+
start = at(link)
|
|
756
|
+
return None if differ(start, end, paths) else links
|
|
757
|
+
|
|
635
758
|
original_paths = [Path(sources[task]["path"]) for task in originals]
|
|
636
759
|
original_proofs = isolation.verify_landed_task_files(
|
|
637
760
|
repo, original_paths, f"{project_dir}/tasks", head
|
|
638
761
|
)["tasks"]
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
762
|
+
landed, earlier = [], set()
|
|
763
|
+
for landing in original_proofs:
|
|
764
|
+
links = None
|
|
765
|
+
if landing["verdict"] == "recovered":
|
|
766
|
+
original = entry(landing, Path(sources[landing["task_id"]]["path"]), sources[landing["task_id"]]["files"])
|
|
767
|
+
if before(at(original), at(repair, "base")):
|
|
768
|
+
links = linked_changes(at(original), [at(repair, "base")], original["files"])
|
|
769
|
+
if links is None:
|
|
645
770
|
raise ReviewFindingsError("repair base contains unlinked changes to original product")
|
|
771
|
+
landed.append(original)
|
|
772
|
+
earlier.update(link["task"] for link in links)
|
|
646
773
|
source_paths = list(review_paths(project, wave, cycle, source["depth"]).values())
|
|
647
774
|
source_paths.extend(Path(group["skeptic"]["path"]) for group in groups if group["skeptic"])
|
|
648
775
|
for path in set(source_paths):
|
|
@@ -650,19 +777,28 @@ def repair_evidence(repo: Path, project_dir: str, wave: int, cycle: int, task_id
|
|
|
650
777
|
at_base = isolation.run_git(repo, "show", f"{base}:{relative}")
|
|
651
778
|
if at_base.returncode or at_base.stdout != _read(path, "source review"):
|
|
652
779
|
raise ReviewFindingsError("source review must be unchanged and precede the repair")
|
|
653
|
-
|
|
780
|
+
# A later linked repair supersedes this one: the last one is the product at the tip, and its
|
|
781
|
+
# landing and its Verify are the evidence. The reviewer applies every patch of the chain, so the
|
|
782
|
+
# files of every later linked repair must also change only through the landings of the chain.
|
|
783
|
+
later = linked_changes(at(repair), tip, files)
|
|
784
|
+
chain_files = sorted({*files, *(path for link in later or [] for path in link["files"])})
|
|
785
|
+
last = later[-1] if later else repair
|
|
786
|
+
verification = build_state.verify_lookup(
|
|
787
|
+
str(repo), _common.task_verify_command(_read(repo / last["task_file"], "repair task")), at(last), member)
|
|
654
788
|
if not verification["reuse"]:
|
|
655
789
|
raise ReviewFindingsError("repair has no reusable passing Verify at its landing")
|
|
656
|
-
if
|
|
790
|
+
if later is None or (later and linked_changes(at(repair), tip, chain_files) is None):
|
|
657
791
|
raise ReviewFindingsError("product changed after the repair landing")
|
|
792
|
+
# A parallel landing can have a parent that is newer than the repair base. The reviewer builds the
|
|
793
|
+
# product from the base, so no other landing between the two can change a file of the chain.
|
|
794
|
+
if differ(at(repair, "base"), [f"{at(repair)}^"], chain_files):
|
|
795
|
+
raise ReviewFindingsError("product changed between the repair base and the repair landing")
|
|
658
796
|
return {
|
|
659
797
|
"status": "ok", "command": "repair-evidence", "head": head,
|
|
660
798
|
"source": {"wave": wave, "cycle": cycle}, "groups": groups,
|
|
661
|
-
"originals":
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
"repair": {"task": task_id, "task_file": repair_path.relative_to(repo).as_posix(),
|
|
665
|
-
"base": base, "commit": commit, "files": files},
|
|
799
|
+
"originals": landed, "repair": repair,
|
|
800
|
+
**({"earlier_repairs": [link for link in linked() if link["task"] in earlier]} if earlier else {}),
|
|
801
|
+
**({"later_repairs": later, "chain_files": chain_files} if later else {}),
|
|
666
802
|
"verification": verification,
|
|
667
803
|
"verdict": "evidence-ready; reviewer judges the original unchanged criteria",
|
|
668
804
|
}
|
|
@@ -252,7 +252,12 @@ def activate_milestone(text: str, milestone: str) -> str:
|
|
|
252
252
|
return "".join(lines)
|
|
253
253
|
|
|
254
254
|
|
|
255
|
-
def
|
|
255
|
+
def _entry_identity(block: dict) -> tuple:
|
|
256
|
+
"""Heading, Status, Depends on, Archive and Integrated of one entry."""
|
|
257
|
+
return block["id"], block["title"], {name: value for name, (_, value) in block["fields"].items()}
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def reslice(before: str, candidate: str, phase: str, selected: str, amend_active: bool = False) -> str:
|
|
256
261
|
old_blocks = roadmap_blocks(before)
|
|
257
262
|
before_lines = before.splitlines(keepends=True)
|
|
258
263
|
candidate_lines = candidate.splitlines(keepends=True)
|
|
@@ -264,6 +269,9 @@ def reslice(before: str, candidate: str, phase: str, selected: str) -> str:
|
|
|
264
269
|
new = milestone_block(candidate, slug)
|
|
265
270
|
if status == "shipped":
|
|
266
271
|
changed = roadmap_contract(before, slug) != roadmap_contract(candidate, slug)
|
|
272
|
+
elif status == "active" and amend_active:
|
|
273
|
+
# Intent is not approved: the scope may change, the identity may not.
|
|
274
|
+
changed = _entry_identity(block) != _entry_identity(new)
|
|
267
275
|
else:
|
|
268
276
|
changed = before_lines[block["start"]:block["end"]] != candidate_lines[new["start"]:new["end"]]
|
|
269
277
|
if changed:
|
|
@@ -18,14 +18,14 @@ from pathlib import Path, PurePosixPath
|
|
|
18
18
|
from typing import Mapping, Optional, Sequence
|
|
19
19
|
|
|
20
20
|
if __package__:
|
|
21
|
-
from .isolation import IsolationError, checkpoint as isolation_checkpoint,
|
|
21
|
+
from .isolation import IsolationError, checkpoint as isolation_checkpoint, PROJECT_ENTRIES
|
|
22
22
|
else:
|
|
23
23
|
try:
|
|
24
|
-
from isolation import IsolationError, checkpoint as isolation_checkpoint,
|
|
24
|
+
from isolation import IsolationError, checkpoint as isolation_checkpoint, PROJECT_ENTRIES
|
|
25
25
|
except ModuleNotFoundError as error: # pragma: no cover - package imports used by tests
|
|
26
26
|
if error.name != "isolation":
|
|
27
27
|
raise
|
|
28
|
-
from scripts.isolation import IsolationError, checkpoint as isolation_checkpoint,
|
|
28
|
+
from scripts.isolation import IsolationError, checkpoint as isolation_checkpoint, PROJECT_ENTRIES
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
if __package__:
|
|
@@ -164,9 +164,16 @@ def _approval_details(
|
|
|
164
164
|
raise PipelineStateError(f"unsupported approval kind: {kind}")
|
|
165
165
|
|
|
166
166
|
|
|
167
|
-
def _reslice_roadmap(
|
|
167
|
+
def _reslice_roadmap(
|
|
168
|
+
repo: Path, before: str, candidate: str, state: PipelineState, state_text: str, selected: str,
|
|
169
|
+
) -> str:
|
|
170
|
+
# Define may amend its active entry until intent approval. A return to
|
|
171
|
+
# define after that approval always leaves an active recovery record.
|
|
172
|
+
amend_active = state.phase == "define" and not (
|
|
173
|
+
pipeline_state._build_recovery().context(repo, state_text) or {}
|
|
174
|
+
).get("active")
|
|
168
175
|
try:
|
|
169
|
-
return roadmap.reslice(before, candidate, state.phase, selected)
|
|
176
|
+
return roadmap.reslice(before, candidate, state.phase, selected, amend_active)
|
|
170
177
|
except roadmap.RoadmapError as error:
|
|
171
178
|
raise PipelineStateError(str(error)) from error
|
|
172
179
|
|
|
@@ -223,6 +230,10 @@ def _approval_journal(repo: Path, journal: Mapping[str, object]) -> dict[str, ob
|
|
|
223
230
|
expected_keys.add("baseline_before")
|
|
224
231
|
if not isinstance(journal.get("baseline_before"), str):
|
|
225
232
|
raise PipelineStateError("checkpoint journal has an invalid re-slice baseline")
|
|
233
|
+
if "recovery_reviews" in journal:
|
|
234
|
+
expected_keys.add("recovery_reviews")
|
|
235
|
+
if journal.get("kind") != "plan" or not isinstance(journal["recovery_reviews"], dict):
|
|
236
|
+
raise PipelineStateError("checkpoint journal has an invalid recovery review report")
|
|
226
237
|
if set(journal) != expected_keys:
|
|
227
238
|
raise PipelineStateError("checkpoint journal has invalid fields")
|
|
228
239
|
if journal.get("schema") != CHECKPOINT_SCHEMA or journal.get("repo") != str(repo):
|
|
@@ -307,7 +318,8 @@ def _approval_journal(repo: Path, journal: Mapping[str, object]) -> dict[str, ob
|
|
|
307
318
|
if journal["roadmap_path"] != ".project/ROADMAP.md":
|
|
308
319
|
raise PipelineStateError("checkpoint journal has an invalid ROADMAP path")
|
|
309
320
|
expected_roadmap = _reslice_roadmap(
|
|
310
|
-
str(journal["baseline_before"]), str(journal["roadmap_before"]), before,
|
|
321
|
+
repo, str(journal["baseline_before"]), str(journal["roadmap_before"]), before,
|
|
322
|
+
str(journal["state_before"]), str(selected),
|
|
311
323
|
) if journal["kind"] == "roadmap-reslice" else _activate_roadmap_milestone(
|
|
312
324
|
str(journal["roadmap_before"]),
|
|
313
325
|
str(selected),
|
|
@@ -423,7 +435,7 @@ def _resume_checkpoint_locked(
|
|
|
423
435
|
raise PipelineStateError("approval checkpoint returned the wrong commit")
|
|
424
436
|
_unlink_checkpoint_journal(journal_path)
|
|
425
437
|
state = pipeline_state._state_from_text(str(transaction["state_after"]))
|
|
426
|
-
|
|
438
|
+
approved: dict[str, object] = {
|
|
427
439
|
"schema": CHECKPOINT_SCHEMA,
|
|
428
440
|
"status": "approved",
|
|
429
441
|
"kind": transaction["kind"],
|
|
@@ -432,6 +444,9 @@ def _resume_checkpoint_locked(
|
|
|
432
444
|
"paths": committed.get("paths"),
|
|
433
445
|
"state": state.json(),
|
|
434
446
|
}
|
|
447
|
+
if "recovery_reviews" in transaction:
|
|
448
|
+
approved["recovery_reviews"] = transaction["recovery_reviews"]
|
|
449
|
+
return approved
|
|
435
450
|
|
|
436
451
|
|
|
437
452
|
def _validate_plan_briefs(repo: Path, kind: str, project_dir: str) -> None:
|
|
@@ -444,14 +459,14 @@ def _validate_plan_briefs(repo: Path, kind: str, project_dir: str) -> None:
|
|
|
444
459
|
# Pre-Git approval defers base-dependent checks to build.
|
|
445
460
|
return
|
|
446
461
|
if __package__:
|
|
447
|
-
from .check_task_briefs import BriefError, _member_bases, validate_task_briefs
|
|
462
|
+
from .check_task_briefs import BriefError, _member_bases, plan_validation_bases, validate_task_briefs
|
|
448
463
|
else:
|
|
449
464
|
try:
|
|
450
|
-
from check_task_briefs import BriefError, _member_bases, validate_task_briefs
|
|
465
|
+
from check_task_briefs import BriefError, _member_bases, plan_validation_bases, validate_task_briefs
|
|
451
466
|
except ModuleNotFoundError as error: # pragma: no cover - package imports
|
|
452
467
|
if error.name != "check_task_briefs":
|
|
453
468
|
raise
|
|
454
|
-
from scripts.check_task_briefs import BriefError, _member_bases, validate_task_briefs
|
|
469
|
+
from scripts.check_task_briefs import BriefError, _member_bases, plan_validation_bases, validate_task_briefs
|
|
455
470
|
if __package__:
|
|
456
471
|
from . import check_handoffs
|
|
457
472
|
else:
|
|
@@ -462,28 +477,10 @@ def _validate_plan_briefs(repo: Path, kind: str, project_dir: str) -> None:
|
|
|
462
477
|
raise
|
|
463
478
|
from scripts import check_handoffs
|
|
464
479
|
try:
|
|
465
|
-
tasks, dependency_files = check_handoffs.plan_brief_inputs(repo, project_dir)
|
|
466
|
-
landed_bases = {}
|
|
467
480
|
member_base = _member_bases(repo)
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
agent = check_handoffs._task_scalar(text, task_id, "agent")
|
|
472
|
-
if agent in {"", "null"}:
|
|
473
|
-
raise BriefError(f"{task_id} landed task has no recorded agent")
|
|
474
|
-
member = check_handoffs._strict_frontmatter(text, task_id).get("repo")
|
|
475
|
-
base_repo = repo
|
|
476
|
-
if member is not None:
|
|
477
|
-
located = member_base(member) if isinstance(member, str) and member else None
|
|
478
|
-
if located is None:
|
|
479
|
-
raise BriefError(f"repo: names no member in MEMBERS.md: {member}")
|
|
480
|
-
base_repo = located[0]
|
|
481
|
-
recorded_base = check_handoffs._task_scalar(text, task_id, "base")
|
|
482
|
-
try:
|
|
483
|
-
landed_bases[task_id] = require_commit(base_repo, require_full_sha(recorded_base))
|
|
484
|
-
except IsolationError as error:
|
|
485
|
-
raise BriefError(f"{task_id} landed task has invalid historical base: {error}") from error
|
|
486
|
-
dependency_files[task_id] = set()
|
|
481
|
+
tasks, dependency_files, landed_bases = plan_validation_bases(
|
|
482
|
+
repo, project_dir, member_base, initial=True
|
|
483
|
+
)
|
|
487
484
|
validate_task_briefs(
|
|
488
485
|
repo, head.stdout.strip(), f"{project_dir}/tasks",
|
|
489
486
|
dependency_files=dependency_files, landed_bases=landed_bases,
|
|
@@ -581,7 +578,10 @@ def checkpoint_approval(
|
|
|
581
578
|
f"{expected_head}^{{commit}}",
|
|
582
579
|
).stdout.strip()
|
|
583
580
|
if resolved_head != expected_head:
|
|
584
|
-
raise PipelineStateError(
|
|
581
|
+
raise PipelineStateError(
|
|
582
|
+
"expected-head must be an exact full commit SHA; "
|
|
583
|
+
"resolve a ref or a short SHA with git rev-parse first"
|
|
584
|
+
)
|
|
585
585
|
if pipeline_state._run_git(resolved, "rev-parse", "HEAD").stdout.strip() != expected_head:
|
|
586
586
|
raise PipelineStateError("approval must start at expected-head")
|
|
587
587
|
active_state, _, _ = pipeline_state.load_state(resolved)
|
|
@@ -596,8 +596,9 @@ def checkpoint_approval(
|
|
|
596
596
|
state, state_before, state_path = pipeline_state.load_state(resolved, project_dir)
|
|
597
597
|
changes, event, subject, body = _approval_details(kind, state, selected_milestone)
|
|
598
598
|
_validate_plan_briefs(resolved, kind, project_dir)
|
|
599
|
+
recovery_reviews = None
|
|
599
600
|
if kind == "plan" and project_dir == ".project":
|
|
600
|
-
pipeline_state._build_recovery().restore_unchanged_reviews(resolved)
|
|
601
|
+
recovery_reviews = pipeline_state._build_recovery().restore_unchanged_reviews(resolved)
|
|
601
602
|
expected = {
|
|
602
603
|
"phase": state.phase,
|
|
603
604
|
"status": state.status,
|
|
@@ -629,7 +630,7 @@ def checkpoint_approval(
|
|
|
629
630
|
if kind == "roadmap-reslice":
|
|
630
631
|
baseline_before = pipeline_state._read_real_file(project / "ROADMAP.before-reslice.md", "re-slice baseline")
|
|
631
632
|
roadmap_after = _reslice_roadmap(
|
|
632
|
-
baseline_before, roadmap_before, state, selected_milestone,
|
|
633
|
+
resolved, baseline_before, roadmap_before, state, state_before, selected_milestone,
|
|
633
634
|
) if kind == "roadmap-reslice" else _activate_roadmap_milestone(
|
|
634
635
|
roadmap_before,
|
|
635
636
|
selected_milestone,
|
|
@@ -659,6 +660,8 @@ def checkpoint_approval(
|
|
|
659
660
|
}
|
|
660
661
|
if baseline_before is not None:
|
|
661
662
|
journal["baseline_before"] = baseline_before
|
|
663
|
+
if recovery_reviews is not None:
|
|
664
|
+
journal["recovery_reviews"] = recovery_reviews
|
|
662
665
|
pipeline_state._write_json(journal_path, journal)
|
|
663
666
|
return _resume_checkpoint_locked(resolved, project, journal_path, journal)
|
|
664
667
|
|
|
@@ -740,8 +743,8 @@ def defer_approval(
|
|
|
740
743
|
if kind == "roadmap-reslice":
|
|
741
744
|
baseline = project / "ROADMAP.before-reslice.md"
|
|
742
745
|
roadmap_after = _reslice_roadmap(
|
|
743
|
-
pipeline_state._read_real_file(baseline, "re-slice baseline"),
|
|
744
|
-
roadmap_before, state, selected_milestone,
|
|
746
|
+
resolved, pipeline_state._read_real_file(baseline, "re-slice baseline"),
|
|
747
|
+
roadmap_before, state, state_before, selected_milestone,
|
|
745
748
|
)
|
|
746
749
|
else:
|
|
747
750
|
roadmap_after = _activate_roadmap_milestone(roadmap_before, selected_milestone)
|
|
@@ -377,17 +377,22 @@ def _prepare_promotion(
|
|
|
377
377
|
head = pipeline_state._run_git(repo, "rev-parse", "HEAD").stdout.strip()
|
|
378
378
|
if head != base:
|
|
379
379
|
raise PipelineStateError(f"promotion must start at base SHA: {head} != {base}")
|
|
380
|
-
|
|
380
|
+
active_state, active_text, _ = pipeline_state.load_state(repo)
|
|
381
|
+
remote_default = f"origin/{active_state.default_branch}"
|
|
382
|
+
remote = pipeline_state._run_git(
|
|
383
|
+
repo, "rev-parse", "--verify", f"{remote_default}^{{commit}}"
|
|
384
|
+
).stdout.strip()
|
|
381
385
|
if remote != base:
|
|
382
386
|
raise PipelineStateError(
|
|
383
|
-
f"base SHA is not current
|
|
387
|
+
f"base SHA is not current {remote_default}: {base} != {remote}"
|
|
384
388
|
)
|
|
385
389
|
if not pipeline_state._is_ancestor(repo, landing, base):
|
|
386
|
-
raise PipelineStateError(
|
|
390
|
+
raise PipelineStateError(
|
|
391
|
+
f"landing is not an ancestor of the current {active_state.default_branch} base"
|
|
392
|
+
)
|
|
387
393
|
if pipeline_state._run_git(repo, "status", "--porcelain", "--untracked-files=all").stdout:
|
|
388
394
|
raise PipelineStateError("promotion must start from a clean worktree")
|
|
389
395
|
|
|
390
|
-
active_state, active_text, _ = pipeline_state.load_state(repo)
|
|
391
396
|
if active_state.phase != "shipped" or active_state.status != "done":
|
|
392
397
|
raise PipelineStateError("promotion requires active STATE shipped/done")
|
|
393
398
|
if active_state.milestone is None:
|
|
@@ -456,6 +461,12 @@ def _prepare_promotion(
|
|
|
456
461
|
raise PipelineStateError(
|
|
457
462
|
"lookahead integration_default does not match the active project"
|
|
458
463
|
)
|
|
464
|
+
if next_state.default_branch != active_state.default_branch:
|
|
465
|
+
raise PipelineStateError(
|
|
466
|
+
"lookahead default_branch does not match the active project: copy "
|
|
467
|
+
f"`default_branch: {active_state.default_branch}` from .project/STATE.md "
|
|
468
|
+
"into .project/next/STATE.md"
|
|
469
|
+
)
|
|
459
470
|
if next_state.milestone != milestone:
|
|
460
471
|
raise PipelineStateError(
|
|
461
472
|
f"requested milestone {milestone} != next STATE milestone {next_state.milestone}"
|
|
@@ -6,6 +6,7 @@ import hashlib
|
|
|
6
6
|
import errno
|
|
7
7
|
import json
|
|
8
8
|
import os
|
|
9
|
+
import stat
|
|
9
10
|
import subprocess
|
|
10
11
|
import sys
|
|
11
12
|
import time
|
|
@@ -151,7 +152,17 @@ def validate_runtime(data: dict) -> Path:
|
|
|
151
152
|
path = root / name
|
|
152
153
|
if path.is_symlink() or hashlib.sha256(path.read_bytes()).hexdigest() != expected:
|
|
153
154
|
raise ValueError(f"runtime file changed: {name}")
|
|
154
|
-
|
|
155
|
+
runtime_entries = set()
|
|
156
|
+
for path in root.iterdir():
|
|
157
|
+
if path.name == "__pycache__":
|
|
158
|
+
if not stat.S_ISDIR(path.lstat().st_mode):
|
|
159
|
+
raise ValueError("unexpected runtime files")
|
|
160
|
+
for cached in path.iterdir():
|
|
161
|
+
if cached.suffix != ".pyc" or not stat.S_ISREG(cached.lstat().st_mode):
|
|
162
|
+
raise ValueError("unexpected runtime files")
|
|
163
|
+
continue
|
|
164
|
+
runtime_entries.add(path.name)
|
|
165
|
+
if runtime_entries != set(files) | {"manifest.json"}:
|
|
155
166
|
raise ValueError("unexpected runtime files")
|
|
156
167
|
except (OSError, ValueError, TypeError) as error:
|
|
157
168
|
raise ValueError(hint + f" ({error})") from error
|
|
@@ -167,8 +178,13 @@ def run_guard(repo: Path, name: str) -> None:
|
|
|
167
178
|
root = resolve_runtime(repo)
|
|
168
179
|
sys.dont_write_bytecode = True
|
|
169
180
|
sys.path.insert(0, str(root))
|
|
170
|
-
|
|
171
|
-
|
|
181
|
+
previous_pycache_prefix = sys.pycache_prefix
|
|
182
|
+
sys.pycache_prefix = str(root / "manifest.json")
|
|
183
|
+
try:
|
|
184
|
+
code = compile((root / name).read_bytes(), str(root / name), "exec")
|
|
185
|
+
exec(code, {"__name__": "__main__", "__file__": str(repo / ".gsd-path" / name)})
|
|
186
|
+
finally:
|
|
187
|
+
sys.pycache_prefix = previous_pycache_prefix
|
|
172
188
|
|
|
173
189
|
|
|
174
190
|
def launch(repo: Path) -> int:
|
|
@@ -179,7 +195,9 @@ def launch(repo: Path) -> int:
|
|
|
179
195
|
except (OSError, ValueError) as error:
|
|
180
196
|
print(str(error), file=sys.stderr)
|
|
181
197
|
return 2
|
|
182
|
-
return subprocess.call([sys.executable, "-B",
|
|
198
|
+
return subprocess.call([sys.executable, "-B", "-X",
|
|
199
|
+
f"pycache_prefix={runtime.parent / 'manifest.json'}",
|
|
200
|
+
str(runtime), "status", "--repo", str(repo)],
|
|
183
201
|
env={**os.environ, "GIT_OPTIONAL_LOCKS": "0"})
|
|
184
202
|
runtime = parent / "runtime" / "pipeline_state.py"
|
|
185
203
|
lock = repo / ".gsd-path-install-lock"
|