@opengsd/gsd-path 1.5.0 → 1.6.1
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 +1 -1
- package/CHANGELOG.md +169 -0
- package/DOCS.md +4 -3
- package/FULL.md +68 -7
- package/HOOKS.md +81 -4
- package/MIGRATE.md +12 -0
- package/QUICK.md +4 -2
- package/README.md +69 -16
- package/RUNTIME.md +23 -5
- package/UPDATE.md +35 -0
- package/WORKFLOW.md +66 -7
- package/package.json +1 -1
- package/scripts/_common.py +150 -1
- package/scripts/build_recovery.py +62 -30
- package/scripts/build_state.py +195 -5
- package/scripts/check_handoffs.py +92 -30
- package/scripts/check_task_briefs.py +27 -19
- package/scripts/detect_project.py +17 -2
- package/scripts/discussion_records.py +9 -1
- package/scripts/discussion_validate.py +7 -6
- package/scripts/dispatch_driver.py +174 -36
- package/scripts/git_guard.py +42 -7
- package/scripts/guard_hook.py +1216 -47
- package/scripts/install.mjs +76 -3
- package/scripts/install.py +182 -37
- package/scripts/integration.py +115 -68
- package/scripts/isolation.py +192 -23
- package/scripts/members.py +68 -25
- package/scripts/migrate_core.py +2 -0
- package/scripts/model_policy.py +13 -2
- package/scripts/pipeline_diagnose.py +17 -2
- package/scripts/pipeline_git.py +87 -35
- package/scripts/pipeline_state.py +31 -7
- package/scripts/pipeline_undo.py +17 -8
- 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 +1 -0
- package/scripts/state_checkpoint.py +30 -9
- package/scripts/state_promote.py +92 -5
- package/scripts/task_context.py +2 -0
- package/scripts/workflow_run.py +9 -2
- package/skills/gsd-path/DEFINE.md +6 -2
- package/skills/gsd-path/PLAN.md +6 -2
- package/skills/gsd-path/ROADMAP.md +16 -6
- package/skills/gsd-path/SHIP.md +38 -11
- package/skills/gsd-path/SKILL.md +52 -24
- package/skills/gsd-path/references/build-native.md +96 -9
- package/skills/gsd-path/references/build-recovery.md +13 -0
- package/skills/gsd-path/references/coder.md +7 -3
- package/skills/gsd-path/references/model-policy.md +3 -1
- package/skills/gsd-path/references/operating-rules.md +14 -5
- package/skills/gsd-path/references/planner.md +13 -1
- package/skills/gsd-path/references/reviewer-final.md +5 -5
- package/skills/gsd-path/references/reviewer-wave.md +46 -13
- package/skills/gsd-path/references/runtime-selection.md +10 -0
- package/skills/gsd-path/scripts/_common.py +150 -1
- package/skills/gsd-path/scripts/build_recovery.py +62 -30
- package/skills/gsd-path/scripts/build_state.py +195 -5
- package/skills/gsd-path/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path/scripts/detect_project.py +17 -2
- package/skills/gsd-path/scripts/discussion_records.py +9 -1
- package/skills/gsd-path/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path/scripts/dispatch_driver.py +174 -36
- package/skills/gsd-path/scripts/git_guard.py +42 -7
- package/skills/gsd-path/scripts/integration.py +115 -68
- package/skills/gsd-path/scripts/isolation.py +192 -23
- package/skills/gsd-path/scripts/members.py +68 -25
- package/skills/gsd-path/scripts/model_policy.py +13 -2
- package/skills/gsd-path/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path/scripts/state_promote.py +92 -5
- package/skills/gsd-path/scripts/task_context.py +2 -0
- package/skills/gsd-path/scripts/workflow_run.py +9 -2
- package/skills/gsd-path/templates/final-review.md +5 -5
- package/skills/gsd-path/templates/state.md +1 -0
- package/skills/gsd-path/templates/task.md +1 -0
- package/skills/gsd-path/templates/wave-review.md +18 -9
- package/skills/gsd-path-build/references/build-native.md +96 -9
- 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/model-policy.md +3 -1
- package/skills/gsd-path-build/references/operating-rules.md +14 -5
- package/skills/gsd-path-build/references/reviewer-final.md +5 -5
- package/skills/gsd-path-build/references/reviewer-wave.md +46 -13
- package/skills/gsd-path-build/references/runtime-selection.md +10 -0
- package/skills/gsd-path-build/scripts/_common.py +150 -1
- package/skills/gsd-path-build/scripts/build_recovery.py +62 -30
- package/skills/gsd-path-build/scripts/build_state.py +195 -5
- package/skills/gsd-path-build/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path-build/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path-build/scripts/detect_project.py +17 -2
- package/skills/gsd-path-build/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-build/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path-build/scripts/dispatch_driver.py +174 -36
- package/skills/gsd-path-build/scripts/integration.py +115 -68
- package/skills/gsd-path-build/scripts/isolation.py +192 -23
- package/skills/gsd-path-build/scripts/members.py +68 -25
- package/skills/gsd-path-build/scripts/model_policy.py +13 -2
- package/skills/gsd-path-build/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path-build/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path-build/scripts/state_promote.py +92 -5
- package/skills/gsd-path-build/scripts/task_context.py +2 -0
- package/skills/gsd-path-build/scripts/workflow_run.py +9 -2
- package/skills/gsd-path-build/templates/task.md +1 -0
- package/skills/gsd-path-build/templates/wave-review.md +18 -9
- package/skills/gsd-path-decide/references/model-policy.md +3 -1
- package/skills/gsd-path-decide/references/operating-rules.md +14 -5
- package/skills/gsd-path-decide/references/runtime-selection.md +10 -0
- package/skills/gsd-path-decide/scripts/_common.py +150 -1
- package/skills/gsd-path-decide/scripts/build_recovery.py +62 -30
- package/skills/gsd-path-decide/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path-decide/scripts/detect_project.py +17 -2
- package/skills/gsd-path-decide/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-decide/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path-decide/scripts/integration.py +115 -68
- package/skills/gsd-path-decide/scripts/isolation.py +192 -23
- package/skills/gsd-path-decide/scripts/members.py +68 -25
- package/skills/gsd-path-decide/scripts/model_policy.py +13 -2
- package/skills/gsd-path-decide/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path-decide/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path-decide/scripts/state_promote.py +92 -5
- package/skills/gsd-path-define/SKILL.md +6 -2
- package/skills/gsd-path-define/references/build-recovery.md +13 -0
- package/skills/gsd-path-define/references/operating-rules.md +14 -5
- package/skills/gsd-path-define/references/runtime-selection.md +10 -0
- package/skills/gsd-path-define/scripts/_common.py +150 -1
- package/skills/gsd-path-define/scripts/build_recovery.py +62 -30
- package/skills/gsd-path-define/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path-define/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path-define/scripts/detect_project.py +17 -2
- package/skills/gsd-path-define/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-define/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path-define/scripts/integration.py +115 -68
- package/skills/gsd-path-define/scripts/isolation.py +192 -23
- package/skills/gsd-path-define/scripts/members.py +68 -25
- package/skills/gsd-path-define/scripts/model_policy.py +13 -2
- package/skills/gsd-path-define/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path-define/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path-define/scripts/state_promote.py +92 -5
- package/skills/gsd-path-define/templates/state.md +1 -0
- package/skills/gsd-path-discuss/references/operating-rules.md +14 -5
- package/skills/gsd-path-discuss/references/runtime-selection.md +10 -0
- package/skills/gsd-path-discuss/scripts/_common.py +150 -1
- package/skills/gsd-path-discuss/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-discuss/scripts/model_policy.py +13 -2
- package/skills/gsd-path-docs-audit/references/model-policy.md +3 -1
- package/skills/gsd-path-docs-audit/references/operating-rules.md +14 -5
- package/skills/gsd-path-docs-audit/references/runtime-selection.md +10 -0
- package/skills/gsd-path-docs-audit/scripts/_common.py +150 -1
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +62 -30
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path-docs-audit/scripts/detect_project.py +17 -2
- package/skills/gsd-path-docs-audit/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path-docs-audit/scripts/integration.py +115 -68
- package/skills/gsd-path-docs-audit/scripts/isolation.py +192 -23
- package/skills/gsd-path-docs-audit/scripts/members.py +68 -25
- package/skills/gsd-path-docs-audit/scripts/model_policy.py +13 -2
- package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path-docs-audit/scripts/state_promote.py +92 -5
- package/skills/gsd-path-forensics/references/operating-rules.md +14 -5
- package/skills/gsd-path-forensics/references/runtime-selection.md +10 -0
- package/skills/gsd-path-forensics/scripts/_common.py +150 -1
- package/skills/gsd-path-forensics/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-forensics/scripts/model_policy.py +13 -2
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +17 -2
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +17 -8
- package/skills/gsd-path-inspect/references/model-policy.md +3 -1
- package/skills/gsd-path-inspect/references/operating-rules.md +14 -5
- package/skills/gsd-path-inspect/references/runtime-selection.md +10 -0
- package/skills/gsd-path-inspect/scripts/_common.py +150 -1
- package/skills/gsd-path-inspect/scripts/build_recovery.py +62 -30
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path-inspect/scripts/detect_project.py +17 -2
- package/skills/gsd-path-inspect/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path-inspect/scripts/integration.py +115 -68
- package/skills/gsd-path-inspect/scripts/isolation.py +192 -23
- package/skills/gsd-path-inspect/scripts/members.py +68 -25
- package/skills/gsd-path-inspect/scripts/model_policy.py +13 -2
- package/skills/gsd-path-inspect/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path-inspect/scripts/state_promote.py +92 -5
- package/skills/gsd-path-inspect/scripts/workflow_run.py +9 -2
- package/skills/gsd-path-inspect/templates/state.md +1 -0
- package/skills/gsd-path-loop/references/operating-rules.md +14 -5
- package/skills/gsd-path-loop/references/runtime-selection.md +10 -0
- package/skills/gsd-path-loop/scripts/_common.py +150 -1
- package/skills/gsd-path-loop/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-loop/scripts/model_policy.py +13 -2
- package/skills/gsd-path-migrate/SKILL.md +8 -0
- package/skills/gsd-path-migrate/references/operating-rules.md +14 -5
- package/skills/gsd-path-migrate/references/runtime-selection.md +10 -0
- package/skills/gsd-path-migrate/scripts/_common.py +150 -1
- package/skills/gsd-path-migrate/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -0
- package/skills/gsd-path-migrate/scripts/model_policy.py +13 -2
- package/skills/gsd-path-plan/SKILL.md +6 -2
- package/skills/gsd-path-plan/references/build-recovery.md +13 -0
- package/skills/gsd-path-plan/references/model-policy.md +3 -1
- package/skills/gsd-path-plan/references/operating-rules.md +14 -5
- package/skills/gsd-path-plan/references/planner.md +13 -1
- package/skills/gsd-path-plan/references/runtime-selection.md +10 -0
- package/skills/gsd-path-plan/scripts/_common.py +150 -1
- package/skills/gsd-path-plan/scripts/build_recovery.py +62 -30
- package/skills/gsd-path-plan/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path-plan/scripts/detect_project.py +17 -2
- package/skills/gsd-path-plan/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-plan/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path-plan/scripts/integration.py +115 -68
- package/skills/gsd-path-plan/scripts/isolation.py +192 -23
- package/skills/gsd-path-plan/scripts/members.py +68 -25
- package/skills/gsd-path-plan/scripts/model_policy.py +13 -2
- package/skills/gsd-path-plan/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path-plan/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path-plan/scripts/state_promote.py +92 -5
- package/skills/gsd-path-plan/scripts/workflow_run.py +9 -2
- package/skills/gsd-path-plan/templates/task.md +1 -0
- package/skills/gsd-path-research/references/model-policy.md +3 -1
- package/skills/gsd-path-research/references/operating-rules.md +14 -5
- package/skills/gsd-path-research/references/runtime-selection.md +10 -0
- package/skills/gsd-path-research/scripts/_common.py +150 -1
- package/skills/gsd-path-research/scripts/build_recovery.py +62 -30
- package/skills/gsd-path-research/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path-research/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path-research/scripts/detect_project.py +17 -2
- package/skills/gsd-path-research/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-research/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path-research/scripts/integration.py +115 -68
- package/skills/gsd-path-research/scripts/isolation.py +192 -23
- package/skills/gsd-path-research/scripts/members.py +68 -25
- package/skills/gsd-path-research/scripts/model_policy.py +13 -2
- package/skills/gsd-path-research/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path-research/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path-research/scripts/state_promote.py +92 -5
- package/skills/gsd-path-roadmap/SKILL.md +16 -6
- package/skills/gsd-path-roadmap/references/model-policy.md +3 -1
- package/skills/gsd-path-roadmap/references/operating-rules.md +14 -5
- package/skills/gsd-path-roadmap/references/runtime-selection.md +10 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +150 -1
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +62 -30
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path-roadmap/scripts/detect_project.py +17 -2
- package/skills/gsd-path-roadmap/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path-roadmap/scripts/integration.py +115 -68
- package/skills/gsd-path-roadmap/scripts/isolation.py +192 -23
- package/skills/gsd-path-roadmap/scripts/members.py +68 -25
- package/skills/gsd-path-roadmap/scripts/model_policy.py +13 -2
- package/skills/gsd-path-roadmap/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path-roadmap/scripts/state_promote.py +92 -5
- package/skills/gsd-path-ship/SKILL.md +38 -11
- package/skills/gsd-path-ship/references/model-policy.md +3 -1
- package/skills/gsd-path-ship/references/operating-rules.md +14 -5
- package/skills/gsd-path-ship/references/reviewer-final.md +5 -5
- package/skills/gsd-path-ship/references/reviewer-wave.md +46 -13
- package/skills/gsd-path-ship/references/runtime-selection.md +10 -0
- package/skills/gsd-path-ship/scripts/_common.py +150 -1
- package/skills/gsd-path-ship/scripts/build_recovery.py +62 -30
- package/skills/gsd-path-ship/scripts/build_state.py +195 -5
- package/skills/gsd-path-ship/scripts/check_handoffs.py +92 -30
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +27 -19
- package/skills/gsd-path-ship/scripts/detect_project.py +17 -2
- package/skills/gsd-path-ship/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-ship/scripts/discussion_validate.py +7 -6
- package/skills/gsd-path-ship/scripts/integration.py +115 -68
- package/skills/gsd-path-ship/scripts/isolation.py +192 -23
- package/skills/gsd-path-ship/scripts/members.py +68 -25
- package/skills/gsd-path-ship/scripts/model_policy.py +13 -2
- package/skills/gsd-path-ship/scripts/pipeline_git.py +87 -35
- package/skills/gsd-path-ship/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/gsd-path-ship/scripts/state_promote.py +92 -5
- package/skills/gsd-path-ship/scripts/workflow_run.py +9 -2
- package/skills/gsd-path-ship/templates/final-review.md +5 -5
- package/skills/gsd-path-ship/templates/wave-review.md +18 -9
- package/skills/gsd-path-undo/references/operating-rules.md +14 -5
- package/skills/gsd-path-undo/references/runtime-selection.md +10 -0
- package/skills/gsd-path-undo/scripts/_common.py +150 -1
- package/skills/gsd-path-undo/scripts/discussion_records.py +9 -1
- package/skills/gsd-path-undo/scripts/model_policy.py +13 -2
- package/skills/gsd-path-undo/scripts/pipeline_undo.py +17 -8
- package/skills/path/DEFINE.md +6 -2
- package/skills/path/PLAN.md +6 -2
- package/skills/path/ROADMAP.md +16 -6
- package/skills/path/SHIP.md +38 -11
- package/skills/path/SKILL.md +52 -24
- package/skills/path/references/build-native.md +96 -9
- package/skills/path/references/build-recovery.md +13 -0
- package/skills/path/references/coder.md +7 -3
- package/skills/path/references/model-policy.md +3 -1
- package/skills/path/references/operating-rules.md +14 -5
- package/skills/path/references/planner.md +13 -1
- package/skills/path/references/reviewer-final.md +5 -5
- package/skills/path/references/reviewer-wave.md +46 -13
- package/skills/path/references/runtime-selection.md +10 -0
- package/skills/path/scripts/_common.py +150 -1
- package/skills/path/scripts/build_recovery.py +62 -30
- package/skills/path/scripts/build_state.py +195 -5
- package/skills/path/scripts/check_handoffs.py +92 -30
- package/skills/path/scripts/check_task_briefs.py +27 -19
- package/skills/path/scripts/detect_project.py +17 -2
- package/skills/path/scripts/discussion_records.py +9 -1
- package/skills/path/scripts/discussion_validate.py +7 -6
- package/skills/path/scripts/dispatch_driver.py +174 -36
- package/skills/path/scripts/git_guard.py +42 -7
- package/skills/path/scripts/integration.py +115 -68
- package/skills/path/scripts/isolation.py +192 -23
- package/skills/path/scripts/members.py +68 -25
- package/skills/path/scripts/model_policy.py +13 -2
- package/skills/path/scripts/pipeline_git.py +87 -35
- package/skills/path/scripts/pipeline_state.py +31 -7
- 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 +30 -9
- package/skills/path/scripts/state_promote.py +92 -5
- package/skills/path/scripts/task_context.py +2 -0
- package/skills/path/scripts/workflow_run.py +9 -2
- package/skills/path/templates/final-review.md +5 -5
- package/skills/path/templates/state.md +1 -0
- package/skills/path/templates/task.md +1 -0
- package/skills/path/templates/wave-review.md +18 -9
|
@@ -161,7 +161,8 @@ def _reject_placeholder(value: str, label: str) -> None:
|
|
|
161
161
|
if placeholder is not None:
|
|
162
162
|
raise HandoffError(
|
|
163
163
|
f"{label} still contains the placeholder {placeholder.group(0)}; "
|
|
164
|
-
"replace it with the real value"
|
|
164
|
+
"replace it with the real value. A value cannot be only a placeholder; "
|
|
165
|
+
"literal text such as an HTML tag must be inside a sentence, in backticks"
|
|
165
166
|
)
|
|
166
167
|
|
|
167
168
|
|
|
@@ -667,22 +668,30 @@ def _strip_comments(text: str) -> str:
|
|
|
667
668
|
return COMMENT_PATTERN.sub("", text)
|
|
668
669
|
|
|
669
670
|
|
|
670
|
-
def
|
|
671
|
-
|
|
672
|
-
|
|
671
|
+
def _numbered_entries(section: str, *, continuations: bool = False) -> List[Tuple[int, str]]:
|
|
672
|
+
"""Each numbered item in document order; a repeated number stays a separate entry."""
|
|
673
|
+
entries: List[Tuple[int, str]] = []
|
|
674
|
+
open_item = False
|
|
673
675
|
for line in _strip_comments(section).splitlines():
|
|
674
676
|
match = NUMBERED_ITEM_PATTERN.fullmatch(line.strip())
|
|
675
677
|
if not match:
|
|
676
|
-
if continuations and
|
|
677
|
-
|
|
678
|
+
if continuations and open_item and line[:1].isspace() and line.strip():
|
|
679
|
+
number, text = entries[-1]
|
|
680
|
+
entries[-1] = (number, text + " " + line.strip())
|
|
678
681
|
elif line.strip():
|
|
679
|
-
|
|
682
|
+
open_item = False
|
|
680
683
|
continue
|
|
681
|
-
|
|
684
|
+
entries.append((int(match.group(1)), match.group(2).strip()))
|
|
685
|
+
open_item = True
|
|
686
|
+
return entries
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def _numbered_items(section: str, *, continuations: bool = False) -> Dict[int, str]:
|
|
690
|
+
items: Dict[int, str] = {}
|
|
691
|
+
for number, text in _numbered_entries(section, continuations=continuations):
|
|
682
692
|
if number in items:
|
|
683
693
|
raise HandoffError(f"repeated numbered item {number}")
|
|
684
|
-
items[number] =
|
|
685
|
-
current = number
|
|
694
|
+
items[number] = text
|
|
686
695
|
return items
|
|
687
696
|
|
|
688
697
|
|
|
@@ -693,7 +702,11 @@ def _success_criteria(intent: str) -> Dict[str, str]:
|
|
|
693
702
|
expected = list(range(1, max(items) + 1))
|
|
694
703
|
if sorted(items) != expected:
|
|
695
704
|
raise HandoffError("INTENT.md success criteria must be contiguous from 1")
|
|
696
|
-
|
|
705
|
+
# Issue #392: a review can quote a criterion without the markers of its sub-bullet lines.
|
|
706
|
+
bare = _numbered_items(
|
|
707
|
+
_section(_common.without_sub_bullet_markers(intent), "Success criteria"), continuations=True
|
|
708
|
+
)
|
|
709
|
+
return {f"SC{number}": _common.Criterion(text, bare[number]) for number, text in items.items()}
|
|
697
710
|
|
|
698
711
|
|
|
699
712
|
def _split_top_level_commas(value: str) -> List[str]:
|
|
@@ -1297,18 +1310,22 @@ def _normalize_ws(value: str) -> str:
|
|
|
1297
1310
|
|
|
1298
1311
|
|
|
1299
1312
|
def task_review_observation(item: str, task_text: str) -> str:
|
|
1300
|
-
"""Separate
|
|
1313
|
+
"""Separate a quoted acceptance criterion from its observed evidence."""
|
|
1301
1314
|
item = _normalize_ws(item)
|
|
1302
1315
|
section = _common.section_body(task_text, "Acceptance criteria")
|
|
1303
1316
|
if section is not None:
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1317
|
+
# Issue #374: frozen landed bytes may repeat a criterion number. Only the
|
|
1318
|
+
# criterion text matters here, so each repeated entry is still split
|
|
1319
|
+
# from its evidence and the evidence gets the same checks.
|
|
1320
|
+
criteria = [criterion for _number, criterion in _numbered_entries(section, continuations=True)]
|
|
1321
|
+
quoted = _common.quoted_criterion(item, criteria)
|
|
1322
|
+
if quoted is not None:
|
|
1323
|
+
return item[quoted[1] + len(" — ") :]
|
|
1324
|
+
# Issue #392: a quote can leave out the `- ` markers of the criterion's
|
|
1325
|
+
# sub-bullet lines, so read the criteria again without them.
|
|
1326
|
+
bare = _common.without_sub_bullet_markers(task_text)
|
|
1327
|
+
return item if bare == task_text else task_review_observation(item, bare)
|
|
1328
|
+
|
|
1312
1329
|
|
|
1313
1330
|
|
|
1314
1331
|
def _verify_command(task_text: str) -> str:
|
|
@@ -1528,11 +1545,30 @@ def _validate_task_graph(
|
|
|
1528
1545
|
if _task_wave(tasks[dependency], dependency) > wave:
|
|
1529
1546
|
raise HandoffError(f"{task_id} depends on later-wave {dependency}")
|
|
1530
1547
|
|
|
1548
|
+
# `supersedes` retires the regression Verify of an earlier task: the same overlap rule selects both.
|
|
1549
|
+
for task_id, text in tasks.items():
|
|
1550
|
+
superseded = _strict_frontmatter(text, task_id).get("supersedes", [])
|
|
1551
|
+
if (
|
|
1552
|
+
not isinstance(superseded, list)
|
|
1553
|
+
or any(not re.fullmatch(r"T\d{3}", item) for item in superseded)
|
|
1554
|
+
or len(superseded) != len(set(superseded))
|
|
1555
|
+
):
|
|
1556
|
+
raise HandoffError(f"{task_id} supersedes must be a list of different task ids, for example [T001]")
|
|
1557
|
+
for earlier in superseded:
|
|
1558
|
+
if earlier not in tasks:
|
|
1559
|
+
raise HandoffError(f"{task_id} supersedes unknown task {earlier}")
|
|
1560
|
+
if task_waves[earlier] >= task_waves[task_id]:
|
|
1561
|
+
raise HandoffError(f"{task_id} supersedes {earlier}, which is not in an earlier wave")
|
|
1562
|
+
if task_repos[earlier] != task_repos[task_id]:
|
|
1563
|
+
raise HandoffError(f"{task_id} supersedes {earlier}, which is in another repo")
|
|
1564
|
+
if not _common.overlapping_paths(task_files[earlier], task_files[task_id]):
|
|
1565
|
+
raise HandoffError(f"{task_id} supersedes {earlier}, which shares no declared file with it")
|
|
1566
|
+
|
|
1531
1567
|
for index, left in enumerate(task_ids):
|
|
1532
1568
|
for right in task_ids[index + 1 :]:
|
|
1533
1569
|
if task_waves[left] != task_waves[right] or task_repos[left] != task_repos[right]:
|
|
1534
1570
|
continue
|
|
1535
|
-
overlap =
|
|
1571
|
+
overlap = _common.overlapping_paths(task_files[left], task_files[right])
|
|
1536
1572
|
if overlap:
|
|
1537
1573
|
if (
|
|
1538
1574
|
_task_scalar(tasks[left], left, "status") == "done"
|
|
@@ -1746,7 +1782,24 @@ def validate_roadmap(
|
|
|
1746
1782
|
) -> Dict[str, object]:
|
|
1747
1783
|
"""Validate roadmap structure and dependency ordering."""
|
|
1748
1784
|
|
|
1749
|
-
|
|
1785
|
+
# A milestone-boundary re-slice keeps its entering inspect or define state.
|
|
1786
|
+
_require_state_one_of(
|
|
1787
|
+
root,
|
|
1788
|
+
{("roadmap", "active"), ("inspect", "active"), ("define", "active")},
|
|
1789
|
+
project_dir,
|
|
1790
|
+
)
|
|
1791
|
+
if _require_pipeline(root, project_dir)["phase"] == "define":
|
|
1792
|
+
if __package__:
|
|
1793
|
+
from . import build_recovery
|
|
1794
|
+
else:
|
|
1795
|
+
import build_recovery
|
|
1796
|
+
try:
|
|
1797
|
+
# Define entered again after intent approval is not a boundary.
|
|
1798
|
+
reopened = (build_recovery.context(root.resolve()) or {}).get("active")
|
|
1799
|
+
except PipelineStateError as error:
|
|
1800
|
+
raise HandoffError(str(error)) from error
|
|
1801
|
+
if reopened:
|
|
1802
|
+
raise HandoffError("roadmap re-slice is not legal after intent approval")
|
|
1750
1803
|
relative = f"{project_dir}/ROADMAP.md"
|
|
1751
1804
|
text = _read(root, relative)
|
|
1752
1805
|
if re.search(r"(?m)^## Wave |^\|\s*Task\s*\||^Files:\s*", text):
|
|
@@ -2045,25 +2098,28 @@ def validate_wave_evidence(
|
|
|
2045
2098
|
if enforce_plan_tasks:
|
|
2046
2099
|
if reviewed_tasks != expected_tasks:
|
|
2047
2100
|
raise HandoffError(
|
|
2048
|
-
f"{name} must review exactly {', '.join(expected_tasks)} in
|
|
2101
|
+
f"{name} must review exactly {', '.join(expected_tasks)} in task id order"
|
|
2049
2102
|
)
|
|
2050
2103
|
else:
|
|
2051
2104
|
unknown = [task_id for task_id in reviewed_tasks if task_id not in expected_tasks]
|
|
2052
2105
|
if unknown:
|
|
2053
2106
|
raise HandoffError(
|
|
2054
|
-
f"{name} must review exactly {', '.join(expected_tasks)} in
|
|
2107
|
+
f"{name} must review exactly {', '.join(expected_tasks)} in task id order"
|
|
2055
2108
|
)
|
|
2056
2109
|
plan_order = [task_id for task_id in expected_tasks if task_id in reviewed_tasks]
|
|
2057
2110
|
if reviewed_tasks != plan_order:
|
|
2058
2111
|
raise HandoffError(
|
|
2059
|
-
f"{name} must review exactly {', '.join(expected_tasks)} in
|
|
2112
|
+
f"{name} must review exactly {', '.join(expected_tasks)} in task id order"
|
|
2060
2113
|
)
|
|
2061
2114
|
task_verdicts = []
|
|
2062
2115
|
for heading_index, heading in task_headings:
|
|
2063
2116
|
task_id = heading.group("task")
|
|
2064
2117
|
expected_title = _normalize_ws(_task_scalar(tasks[task_id], task_id, "title"))
|
|
2065
2118
|
if _normalize_ws(heading.group("title")) != expected_title:
|
|
2066
|
-
raise HandoffError(
|
|
2119
|
+
raise HandoffError(
|
|
2120
|
+
f"{name} title for {task_id} differs from task file; "
|
|
2121
|
+
f"expected the exact `title:` value: {expected_title}"
|
|
2122
|
+
)
|
|
2067
2123
|
end = next(
|
|
2068
2124
|
(
|
|
2069
2125
|
index
|
|
@@ -2080,7 +2136,10 @@ def validate_wave_evidence(
|
|
|
2080
2136
|
if line.strip().startswith(f"- {marker} ")
|
|
2081
2137
|
]
|
|
2082
2138
|
if not evidence:
|
|
2083
|
-
raise HandoffError(
|
|
2139
|
+
raise HandoffError(
|
|
2140
|
+
f"{name} task {task_id} lacks {verdict} evidence; "
|
|
2141
|
+
f"add a line that starts with `- {marker} ` under its heading"
|
|
2142
|
+
)
|
|
2084
2143
|
for item in evidence:
|
|
2085
2144
|
item = task_review_observation(item, tasks[task_id])
|
|
2086
2145
|
_non_placeholder(item, f"{name} task {task_id} {verdict} evidence")
|
|
@@ -2132,7 +2191,7 @@ def validate_wave_evidence(
|
|
|
2132
2191
|
sc_id in criteria
|
|
2133
2192
|
and (
|
|
2134
2193
|
not separator
|
|
2135
|
-
or
|
|
2194
|
+
or not _common.quotes_criterion(heading_text, criteria[sc_id])
|
|
2136
2195
|
)
|
|
2137
2196
|
):
|
|
2138
2197
|
raise HandoffError(f"{name} {sc_id} heading text differs from INTENT.md")
|
|
@@ -2150,7 +2209,10 @@ def validate_wave_evidence(
|
|
|
2150
2209
|
if line.strip().startswith(f"- {marker} ")
|
|
2151
2210
|
]
|
|
2152
2211
|
if not evidence:
|
|
2153
|
-
raise HandoffError(
|
|
2212
|
+
raise HandoffError(
|
|
2213
|
+
f"{name} {sc_id} lacks {verdict} evidence; "
|
|
2214
|
+
f"add a line that starts with `- {marker} ` under its heading"
|
|
2215
|
+
)
|
|
2154
2216
|
for item in evidence:
|
|
2155
2217
|
_non_placeholder(item, f"{name} {sc_id} {verdict} evidence")
|
|
2156
2218
|
verdicts[sc_id] = verdict
|
|
@@ -2230,7 +2292,7 @@ def validate_final(
|
|
|
2230
2292
|
verdicts: Dict[str, str] = {}
|
|
2231
2293
|
for index, heading in enumerate(blocks):
|
|
2232
2294
|
sc_id = heading.group("id")
|
|
2233
|
-
if
|
|
2295
|
+
if not _common.quotes_criterion(heading.group("title"), criteria[sc_id]):
|
|
2234
2296
|
raise HandoffError(f"FINAL.md {sc_id} heading text differs from INTENT.md")
|
|
2235
2297
|
end = blocks[index + 1].start() if index + 1 < len(blocks) else len(text)
|
|
2236
2298
|
block = text[heading.end() : end]
|
|
@@ -100,13 +100,15 @@ def _supplied_contract(repo: Path, task: Path, token: str) -> bool:
|
|
|
100
100
|
return path.is_file() and path.resolve() == repo.resolve() / token
|
|
101
101
|
|
|
102
102
|
|
|
103
|
+
def _pipeline_workspace_path(repo: Path, task: Path, token: str) -> bool:
|
|
104
|
+
track = _project_track(task, repo)
|
|
105
|
+
return track is not None and token.startswith(
|
|
106
|
+
tuple(f"{track}/{name}/" for name in PIPELINE_WORKSPACE_DIRS))
|
|
107
|
+
|
|
108
|
+
|
|
103
109
|
def _pipeline_workspace_file(repo: Path, task: Path, token: str) -> bool:
|
|
104
110
|
"""Pipeline-owned paths may exist only in the working tree before checkpoint."""
|
|
105
|
-
|
|
106
|
-
if track is None:
|
|
107
|
-
return False
|
|
108
|
-
prefixes = tuple(f"{track}/{name}/" for name in PIPELINE_WORKSPACE_DIRS)
|
|
109
|
-
if not any(token.startswith(prefix) for prefix in prefixes):
|
|
111
|
+
if not _pipeline_workspace_path(repo, task, token):
|
|
110
112
|
return False
|
|
111
113
|
path = repo / token
|
|
112
114
|
return path.is_file() and path.resolve() == repo.resolve() / token
|
|
@@ -277,7 +279,7 @@ def _verify_tokens(
|
|
|
277
279
|
|
|
278
280
|
|
|
279
281
|
def _member_bases(root: Path):
|
|
280
|
-
"""Resolve a member name to (checkout,
|
|
282
|
+
"""Resolve a member name to (checkout, base SHA), reading MEMBERS.md once."""
|
|
281
283
|
cache: Dict[str, Optional[Tuple[Path, str]]] = {}
|
|
282
284
|
|
|
283
285
|
def locate(name: str) -> Optional[Tuple[Path, str]]:
|
|
@@ -290,21 +292,22 @@ def _member_bases(root: Path):
|
|
|
290
292
|
except ImportError: # pragma: no cover - package import used by tests
|
|
291
293
|
from scripts import members
|
|
292
294
|
try:
|
|
293
|
-
listed = {member["name"]:
|
|
294
|
-
|
|
295
|
-
if checkout is None:
|
|
295
|
+
listed = {member["name"]: member for member in members.read_members(root)}
|
|
296
|
+
if name not in listed:
|
|
296
297
|
cache[name] = None
|
|
297
298
|
else:
|
|
299
|
+
checkout = Path(listed[name]["checkout"])
|
|
298
300
|
# A build lock selects the bound tip, including during Plan recovery;
|
|
299
|
-
# without the lock, use
|
|
301
|
+
# without the lock, use the member's recorded default branch on origin
|
|
302
|
+
# before the first build start.
|
|
300
303
|
lock = root / members.LOCK_PATH
|
|
301
304
|
locked = [entry for entry in (json.loads(lock.read_text(encoding="utf-8"))["members"]
|
|
302
305
|
if lock.is_file() else []) if entry["name"] == name]
|
|
303
306
|
if locked:
|
|
304
307
|
cache[name] = (checkout, _resolve_base(checkout, f"refs/heads/{locked[0]['branch']}"))
|
|
305
308
|
else:
|
|
306
|
-
members.
|
|
307
|
-
|
|
309
|
+
cache[name] = (checkout, members.require_origin_branch(
|
|
310
|
+
checkout, members.default_branch(listed[name])))
|
|
308
311
|
except (members.MembersError, BriefError) as error:
|
|
309
312
|
raise BriefError(f"member {name} is unavailable: {error}") from error
|
|
310
313
|
return cache[name]
|
|
@@ -328,8 +331,8 @@ def _lint_task(
|
|
|
328
331
|
checked = 0
|
|
329
332
|
if fields is None:
|
|
330
333
|
return task_id, [error or "invalid frontmatter"], None, checked
|
|
331
|
-
# A member task's paths resolve in the member at its origin
|
|
332
|
-
# member bound branch starts; `repo:` absent means the coordinator.
|
|
334
|
+
# A member task's paths resolve in the member at its default branch on origin,
|
|
335
|
+
# where the member bound branch starts; `repo:` absent means the coordinator.
|
|
333
336
|
git_root = repo
|
|
334
337
|
member = fields.get("repo")
|
|
335
338
|
if member is not None:
|
|
@@ -384,7 +387,9 @@ def _lint_task(
|
|
|
384
387
|
)
|
|
385
388
|
break
|
|
386
389
|
|
|
387
|
-
|
|
390
|
+
# A landed brief is history that nobody can edit: only an unlanded brief must name present paths.
|
|
391
|
+
landed = task_id in (landed_bases or {})
|
|
392
|
+
for name in () if landed else PROSE_SECTIONS:
|
|
388
393
|
body = sections.get(name)
|
|
389
394
|
if body is None or not _clean(body):
|
|
390
395
|
continue
|
|
@@ -406,7 +411,7 @@ def _lint_task(
|
|
|
406
411
|
block = VERIFY_BLOCK_PATTERN.search(verify_body)
|
|
407
412
|
if block is None or not block.group("block").strip():
|
|
408
413
|
problems.append("## Verify must contain a non-empty fenced bash block")
|
|
409
|
-
|
|
414
|
+
elif not landed:
|
|
410
415
|
for token in _verify_tokens(
|
|
411
416
|
block.group("block"),
|
|
412
417
|
repo=repo,
|
|
@@ -443,9 +448,10 @@ def validate_task_briefs(
|
|
|
443
448
|
|
|
444
449
|
Plain lint uses the layer base without overrides. Plan approval and dispatch
|
|
445
450
|
supply transitive dependency files for unlanded tasks and resolved historical
|
|
446
|
-
bases for landed tasks
|
|
451
|
+
bases for landed tasks. A landed brief is immutable: its `files` scope is
|
|
452
|
+
checked at that base, and the paths its prose and Verify name are not checked.
|
|
447
453
|
The caller validates the task graph and landed metadata before supplying
|
|
448
|
-
these mappings
|
|
454
|
+
these mappings.
|
|
449
455
|
"""
|
|
450
456
|
|
|
451
457
|
resolved_base = _resolve_base(root, base)
|
|
@@ -476,6 +482,9 @@ def validate_task_briefs(
|
|
|
476
482
|
for shape, task_ids in contracts.items():
|
|
477
483
|
if len(task_ids) == 1:
|
|
478
484
|
task_id = next(iter(task_ids))
|
|
485
|
+
# A landed brief is history: a later repair may reword the bullet in its partner.
|
|
486
|
+
if task_id in (landed_bases or {}):
|
|
487
|
+
continue
|
|
479
488
|
problems.append(
|
|
480
489
|
f"{task_id}: interface contract is not shared by any other task: {shape}"
|
|
481
490
|
)
|
|
@@ -587,7 +596,6 @@ def plan_validation_bases(
|
|
|
587
596
|
)
|
|
588
597
|
except IsolationError as error:
|
|
589
598
|
raise BriefError(f"{task_id} landed task has invalid historical base: {error}") from error
|
|
590
|
-
dependency_files[task_id] = set()
|
|
591
599
|
except check_handoffs.HandoffError as error:
|
|
592
600
|
raise BriefError(str(error)) from error
|
|
593
601
|
return tasks, dependency_files, landed_bases
|
|
@@ -1291,10 +1291,17 @@ def project_slug(root: Path) -> str:
|
|
|
1291
1291
|
return slug or "project"
|
|
1292
1292
|
|
|
1293
1293
|
|
|
1294
|
-
def filled_state_template(
|
|
1294
|
+
def filled_state_template(
|
|
1295
|
+
template: str, slug: str, phase: str, default_branch: str = "main"
|
|
1296
|
+
) -> str:
|
|
1295
1297
|
if "<slug>" not in template:
|
|
1296
1298
|
raise DetectError("state template missing slug placeholder")
|
|
1297
1299
|
text = template.replace("<slug>", slug)
|
|
1300
|
+
text, count = re.subn(
|
|
1301
|
+
r"(?m)^default_branch: main\b", lambda _: f"default_branch: {default_branch}", text, count=1
|
|
1302
|
+
)
|
|
1303
|
+
if count != 1:
|
|
1304
|
+
raise DetectError("state template missing default_branch: main line")
|
|
1298
1305
|
text, count = re.subn(r"(?m)^phase: define\b", f"phase: {phase}", text, count=1)
|
|
1299
1306
|
if count != 1:
|
|
1300
1307
|
raise DetectError("state template missing phase: define line")
|
|
@@ -2030,6 +2037,14 @@ def initialize(
|
|
|
2030
2037
|
)
|
|
2031
2038
|
if git.returncode != 0 or git.stdout.strip() != "true":
|
|
2032
2039
|
return {**payload, "route": "setup-repository", "wrote_state": False}
|
|
2040
|
+
default_branch = _common.origin_default_branch(root)
|
|
2041
|
+
if not _common.valid_default_branch(default_branch):
|
|
2042
|
+
return {**payload, "wrote_state": False, "error": (
|
|
2043
|
+
f"origin default branch {default_branch!r} cannot be recorded in STATE.md: "
|
|
2044
|
+
"it is a GSD Path reserved name or it has a character that STATE frontmatter "
|
|
2045
|
+
"cannot hold. Rename the default branch, then run "
|
|
2046
|
+
"`git remote set-head origin --auto`."
|
|
2047
|
+
)}
|
|
2033
2048
|
expected = "inspect" if payload["verdict"] == "brownfield" else "define"
|
|
2034
2049
|
if phase is None:
|
|
2035
2050
|
phase = expected
|
|
@@ -2043,7 +2058,7 @@ def initialize(
|
|
|
2043
2058
|
return payload
|
|
2044
2059
|
write_state_anchored(
|
|
2045
2060
|
root,
|
|
2046
|
-
filled_state_template(raw, project_slug(root), phase),
|
|
2061
|
+
filled_state_template(raw, project_slug(root), phase, default_branch),
|
|
2047
2062
|
root_status,
|
|
2048
2063
|
project_status,
|
|
2049
2064
|
)
|
|
@@ -7,6 +7,7 @@ import json
|
|
|
7
7
|
import os
|
|
8
8
|
import shutil
|
|
9
9
|
import sys
|
|
10
|
+
import traceback
|
|
10
11
|
|
|
11
12
|
# Runtime helpers must not modify their immutable installation.
|
|
12
13
|
sys.dont_write_bytecode = True
|
|
@@ -33,7 +34,7 @@ def _load_pipeline_modules():
|
|
|
33
34
|
try:
|
|
34
35
|
from scripts import archive_milestone, pipeline_state, _common
|
|
35
36
|
return _common, archive_milestone, pipeline_state
|
|
36
|
-
except
|
|
37
|
+
except ImportError as error: # a foreign `scripts` package
|
|
37
38
|
if error.name not in {
|
|
38
39
|
"scripts",
|
|
39
40
|
"scripts.archive_milestone",
|
|
@@ -41,6 +42,13 @@ def _load_pipeline_modules():
|
|
|
41
42
|
"scripts._common",
|
|
42
43
|
}:
|
|
43
44
|
raise
|
|
45
|
+
# A plain ImportError raised while a `scripts` module loaded is not a
|
|
46
|
+
# foreign package. A missing module falls back, as before.
|
|
47
|
+
if not isinstance(error, ModuleNotFoundError) and any(
|
|
48
|
+
frame.f_globals.get("__name__", "").startswith("scripts.")
|
|
49
|
+
for frame, _ in traceback.walk_tb(error.__traceback__)
|
|
50
|
+
):
|
|
51
|
+
raise
|
|
44
52
|
shared_scripts = Path(__file__).resolve().parents[2] / "gsd-path" / "scripts"
|
|
45
53
|
sys.path.insert(0, str(shared_scripts))
|
|
46
54
|
import archive_milestone
|
|
@@ -507,7 +507,8 @@ def archived_intent_criteria(archive: Path) -> dict[int, str]:
|
|
|
507
507
|
criteria = check_handoffs._success_criteria(text)
|
|
508
508
|
except check_handoffs.HandoffError as error:
|
|
509
509
|
raise ArchiveError(str(error)) from error
|
|
510
|
-
|
|
510
|
+
# Each value keeps the form without sub-bullet markers that quotes_criterion reads.
|
|
511
|
+
return {int(key[2:]): value for key, value in criteria.items()}
|
|
511
512
|
|
|
512
513
|
|
|
513
514
|
def parse_final_review(archive: Path) -> tuple:
|
|
@@ -563,7 +564,7 @@ def _final_review(archive: Path) -> tuple:
|
|
|
563
564
|
if len(headings) != len(intent_criteria):
|
|
564
565
|
raise ArchiveError("FINAL.md success criteria do not exactly cover INTENT.md")
|
|
565
566
|
for _, number, criterion in headings:
|
|
566
|
-
if
|
|
567
|
+
if not _common.quotes_criterion(criterion, intent_criteria[number]):
|
|
567
568
|
raise ArchiveError(
|
|
568
569
|
f"FINAL.md SC{number} heading text differs from archived INTENT.md"
|
|
569
570
|
)
|
|
@@ -951,10 +952,10 @@ def validate_wave_review(
|
|
|
951
952
|
(index, match.group(1), " ".join(match.group(2).split()), match.group(3))
|
|
952
953
|
)
|
|
953
954
|
actual = [(sc_id, text) for _, sc_id, text, _ in criterion_headings]
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
955
|
+
if len(actual) != len(expected_criteria) or any(
|
|
956
|
+
sc_id != expected_id or not _common.quotes_criterion(text, expected)
|
|
957
|
+
for (sc_id, text), (expected_id, expected) in zip(actual, expected_criteria)
|
|
958
|
+
):
|
|
958
959
|
raise ArchiveError(
|
|
959
960
|
f"{path.name} Intent coverage does not match its owned success criteria"
|
|
960
961
|
)
|