@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
|
@@ -100,6 +100,10 @@ Obtain a confident answer in every area before finishing:
|
|
|
100
100
|
give every named surface at least one success criterion observable
|
|
101
101
|
there. A criterion satisfied by a passing test alone does not cover a
|
|
102
102
|
surface.
|
|
103
|
+
9. **Edges and prohibitions** — once the success criteria are drafted, run
|
|
104
|
+
the [spec-reach probes](references/spec-probes.md): every applicable edge
|
|
105
|
+
and every kept prohibition gets the user's ruling in INTENT.md
|
|
106
|
+
`## Edge coverage` and `## Prohibitions`.
|
|
103
107
|
|
|
104
108
|
## Program mode
|
|
105
109
|
|
|
@@ -149,7 +153,10 @@ State ownership. There is no re-interview of charter or roadmap scope:
|
|
|
149
153
|
`Lane: milestone`, `Surfaces:` copied verbatim from the entry, and
|
|
150
154
|
`Review panel:` copied from CHARTER.md (default
|
|
151
155
|
the configured preference when CHARTER omits it). The confirmation may override the copied
|
|
152
|
-
panel value; do not invent `detected` or a named list.
|
|
156
|
+
panel value; do not invent `detected` or a named list. Run the
|
|
157
|
+
[spec-reach probes](references/spec-probes.md) over the derived criteria
|
|
158
|
+
and draft `## Edge coverage` and `## Prohibitions` with recommended
|
|
159
|
+
rulings. When brownfield,
|
|
153
160
|
fill `## Current state` from the map, record doc-vs-code rulings as in
|
|
154
161
|
Brownfield mode, and add protected existing behavior under Scope out.
|
|
155
162
|
Write Ground truth paths from the track root: `.project` normally, or
|
|
@@ -157,10 +164,16 @@ State ownership. There is no re-interview of charter or roadmap scope:
|
|
|
157
164
|
active `research/` paths.
|
|
158
165
|
3. Present one confirmation, not an interview: playback the derivation (and
|
|
159
166
|
brownfield ground truth when present), link the resolved absolute INTENT.md
|
|
160
|
-
path, and ask approve or adjust.
|
|
167
|
+
path, and ask approve or adjust. The confirmation lists each proposed edge
|
|
168
|
+
and prohibition ruling for the user to accept or change; it never rules on
|
|
169
|
+
them silently. A requested change that contradicts the
|
|
161
170
|
charter or the resolved roadmap entry is a scope change — this phase never
|
|
162
|
-
edits the roadmap; surface it to the user for a `$gsd-path-roadmap`
|
|
163
|
-
|
|
171
|
+
edits the roadmap; surface it to the user for a `$gsd-path-roadmap`
|
|
172
|
+
re-slice. Before intent approval, that re-slice keeps `define/active` and
|
|
173
|
+
can change the scope of this milestone's `active` entry; after its
|
|
174
|
+
approval, draft INTENT.md again from the changed entry. In `corrections`
|
|
175
|
+
mode the intent is already approved, so the re-slice waits for the next
|
|
176
|
+
milestone boundary.
|
|
164
177
|
4. On approval, finalize per the Output contract, setting `milestone` to the
|
|
165
178
|
resolved entry's slug. The router continues with milestone-scoped research
|
|
166
179
|
when the entry lists open questions, otherwise planning.
|
|
@@ -254,7 +267,15 @@ question. The rules change for every primary mode:
|
|
|
254
267
|
the brownfield map already established. Milestone mode skips this step.
|
|
255
268
|
5. Stop when coverage is complete or the user says `enough`. Record remaining
|
|
256
269
|
uncertainty under `## Open questions` with `RESEARCH` or `NEEDS-USER`.
|
|
257
|
-
|
|
270
|
+
`enough` does not skip the next step.
|
|
271
|
+
6. In standard mode, run the [spec-reach probes](references/spec-probes.md)
|
|
272
|
+
over the drafted success criteria: one question round for edges, then one
|
|
273
|
+
for kept prohibitions, each with a recommended ruling first. A ruling
|
|
274
|
+
observable where the user sees the work becomes or amends a success
|
|
275
|
+
criterion; write every ruling into `## Edge coverage` and
|
|
276
|
+
`## Prohibitions`. Program mode skips this step (each milestone runs it
|
|
277
|
+
in milestone mode); milestone mode runs it inside its confirmation.
|
|
278
|
+
7. Classify the proposed milestone lane before writing the approval draft.
|
|
258
279
|
Milestone mode keeps `Lane: milestone` and skips this classification.
|
|
259
280
|
Program mode writes CHARTER.md instead of INTENT.md. Otherwise choose
|
|
260
281
|
`quick` when scope fits at most two deliverable-sized tasks (project
|
|
@@ -275,6 +296,11 @@ question. The rules change for every primary mode:
|
|
|
275
296
|
## Output contract
|
|
276
297
|
|
|
277
298
|
At approval, retain the already reviewed `Lane:` value and its one-line reason.
|
|
299
|
+
Before every approval question on INTENT.md, run the bundled `python3
|
|
300
|
+
<absolute-bundled-script> intent --repo <absolute-root>` helper
|
|
301
|
+
(`scripts/check_handoffs.py`, plus `--project-dir .project/next` in
|
|
302
|
+
lookahead). It checks the success criteria, Surfaces, and both probe tables;
|
|
303
|
+
fix a failure in the draft before asking. It approves nothing.
|
|
278
304
|
Before presenting an approval draft without a CHARTER or explicit panel choice,
|
|
279
305
|
read `settings.review_panel.value` from the selected runtime's `path_config.py
|
|
280
306
|
show --repo <absolute-root>` (bundle for legacy projects; off for older selected
|
|
@@ -302,5 +328,8 @@ Use the executable phase handoff in AGENTS.md for the next action.
|
|
|
302
328
|
- In brownfield mode, state mapped facts for correction; never re-ask them.
|
|
303
329
|
- Treat solution-shaped problems as hypotheses and uncover the underlying pain.
|
|
304
330
|
- Preserve vetoes and corrections verbatim.
|
|
331
|
+
- Never rule on an edge's behavior or a prohibition yourself; recommend,
|
|
332
|
+
then record the user's ruling. An edge the user has not ruled on is a
|
|
333
|
+
required answer, including under pre-approval.
|
|
305
334
|
- Wrap up when every coverage-checklist area is answered or the user signals
|
|
306
335
|
done, and tag remaining gaps.
|
|
@@ -69,6 +69,19 @@ checks landed-task preservation and restored intent ownership, restores review
|
|
|
69
69
|
evidence only for unchanged contracts, and creates the normal journaled plan
|
|
70
70
|
checkpoint. A failed approval leaves recovery open.
|
|
71
71
|
|
|
72
|
+
A wave keeps its review evidence when INTENT.md, its own `## Wave N` section,
|
|
73
|
+
its task files, and every other non-wave PLAN.md section are byte-identical to
|
|
74
|
+
the recovery base. Two edits are exempt: the value of the `max_review_cycles`
|
|
75
|
+
and `wave_budget` lines in `## Config`, and all of `## Dependency notes`. In
|
|
76
|
+
`## Config` only those two values may change; any other edit there, a comment
|
|
77
|
+
edit included, drops the evidence of every wave, as does any other non-wave
|
|
78
|
+
edit. A `- review_panel:` line that is added, removed, or changed anywhere in
|
|
79
|
+
PLAN.md also drops the evidence of every wave. The approval result reports
|
|
80
|
+
this in `recovery_reviews`: `restored` lists the waves that kept their evidence, and
|
|
81
|
+
`review_again` lists each wave that lost it, with the reason. Review every
|
|
82
|
+
`review_again` wave again after build resumes; completion refuses a wave
|
|
83
|
+
without a passing review.
|
|
84
|
+
|
|
72
85
|
Return through `plan/done → build/active` with event `build started`. The dispatch
|
|
73
86
|
driver uses a separate record directory for this recovery base, while retaining
|
|
74
87
|
the milestone budget ledger. Resume only from the new approved task inventory.
|
|
@@ -38,8 +38,8 @@ bundles this file; spawned agents follow their role brief instead.
|
|
|
38
38
|
the lookahead planning track for the next milestone; it follows the same
|
|
39
39
|
marker rule, owns no task frontmatter, and never binds a branch.
|
|
40
40
|
- STATE.integration_default is the project closeout choice and
|
|
41
|
-
STATE.integration is the current milestone choice. Both are `direct
|
|
42
|
-
`pull-request`; STATE.integration_source is `default` or `milestone` and
|
|
41
|
+
STATE.integration is the current milestone choice. Both are `direct`,
|
|
42
|
+
`pull-request`, or `external-landing`; STATE.integration_source is `default` or `milestone` and
|
|
43
43
|
preserves explicit override provenance. Older v2 state defaults to `direct`,
|
|
44
44
|
and modes may be changed only through `pipeline_state.py
|
|
45
45
|
configure-integration` before build. A next milestone resets its current
|
|
@@ -82,7 +82,9 @@ bundles this file; spawned agents follow their role brief instead.
|
|
|
82
82
|
`isolation.py attest` may stand in for it: one `.project/`-only commit that
|
|
83
83
|
names the base, the attested HEAD, the declared files that changed, the
|
|
84
84
|
task Verify recorded as passing at that HEAD, and the ruling verbatim.
|
|
85
|
-
`recover` reports it as `attested`, never as a proven landing.
|
|
85
|
+
`recover` reports it as `attested`, never as a proven landing. Attestation
|
|
86
|
+
matches the declared files literally: work that is only below a declared
|
|
87
|
+
directory can land but cannot be attested.
|
|
86
88
|
- All pipeline work for a milestone lives on `gsd-path/M00N` (M001, M002,
|
|
87
89
|
…) bound in STATE.branch. The next milestone binds a new unused
|
|
88
90
|
`gsd-path/M00N` at the remote default after the previous ship integrates.
|
|
@@ -90,6 +92,12 @@ bundles this file; spawned agents follow their role brief instead.
|
|
|
90
92
|
branch — either Path's direct merge or a Path-owned PR merged by the user.
|
|
91
93
|
The bound branch never receives merges or back-merges and is never the
|
|
92
94
|
GitHub default.
|
|
95
|
+
- The default branch is the one that STATE `default_branch` records at
|
|
96
|
+
initialization; a STATE file without the field means `main`. `main` and
|
|
97
|
+
`origin/main` in these rules mean that branch. Never change the record and
|
|
98
|
+
never take the name from a later `origin/HEAD` lookup. For a member
|
|
99
|
+
repository, the branch is the one that its `.project/MEMBERS.md` section
|
|
100
|
+
records as `Default branch`; a section without the field means `main`.
|
|
93
101
|
- After initialization writes STATE.md, and before entering the first pipeline
|
|
94
102
|
phase in an existing Git repository, the router calls
|
|
95
103
|
`scripts/pipeline_git.py bind-initial` with the selected M00N and exact
|
|
@@ -20,6 +20,15 @@ references to bundled copies of those helpers. Other skill helpers, templates,
|
|
|
20
20
|
and role briefs remain in the installed skill bundle. Pass `-B` to Python helper
|
|
21
21
|
invocations so execution does not create bytecode in the runtime store.
|
|
22
22
|
|
|
23
|
+
The selected runtime is authoritative for each helper in its `manifest.json`;
|
|
24
|
+
this includes `review_findings.py` and `task_context.py`. A selected runtime
|
|
25
|
+
that was published before a helper became a runtime file does not list that
|
|
26
|
+
helper: run the bundled copy until the project runtime is upgraded.
|
|
27
|
+
`dispatch_driver.py` and `workflow_run.py` always run from the installed skill
|
|
28
|
+
bundle, because they read role briefs and templates from it; a skill update
|
|
29
|
+
replaces them, a runtime upgrade does not. The driver reloads the helper modules
|
|
30
|
+
that it imports from the selected runtime.
|
|
31
|
+
|
|
23
32
|
With no declaration, retain the legacy helper lookup. An existing
|
|
24
33
|
`.gsd-path/runtime/` requires the explicit `--runtime-migrate --project PATH`
|
|
25
34
|
operation before adopting external storage; review the resulting Git diff.
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Spec-reach probes
|
|
2
|
+
|
|
3
|
+
Define runs two probes over the success criteria before approval: the edge
|
|
4
|
+
probe (what each criterion must do at its edges) and the prohibition probe
|
|
5
|
+
(what each criterion must never silently become). Both leave a table in
|
|
6
|
+
INTENT.md. Plan, build review, and final review read those tables; the
|
|
7
|
+
`check_handoffs.py intent`, `plan`, and `final` gates enforce them.
|
|
8
|
+
|
|
9
|
+
## Why define owns this
|
|
10
|
+
|
|
11
|
+
A reviewer can only check what the intent states. An edge nobody wrote down
|
|
12
|
+
is never checked: final review covers exactly the INTENT success criteria,
|
|
13
|
+
and a reviewer passes any reasonable reading of an ambiguous one. Model
|
|
14
|
+
reviewers are also confidently wrong on exactly these gaps. On three tasks
|
|
15
|
+
whose correct behavior the spec left unstated, LLM verifier verdicts caught
|
|
16
|
+
the defect 0 times in 12 while a held-out test caught all three; in a larger
|
|
17
|
+
replication, stating the surfaced edge in the spec raised the verifier's
|
|
18
|
+
catch rate to 98% ([Verifier Reach Is Spec Reach](https://github.com/davesienkowski/verifier-reach-is-spec-reach)).
|
|
19
|
+
A smarter reviewer does not close that gap; a wider intent does. The user is
|
|
20
|
+
present at define, so define is where the edge gets its ruling.
|
|
21
|
+
|
|
22
|
+
## Edge probe
|
|
23
|
+
|
|
24
|
+
Walk every success criterion:
|
|
25
|
+
|
|
26
|
+
1. Name the data shapes the criterion handles, then raise only the
|
|
27
|
+
categories those shapes reach:
|
|
28
|
+
|
|
29
|
+
| Shape | Categories |
|
|
30
|
+
|-------|------------|
|
|
31
|
+
| numeric range, dates, money, counts | boundary, precision |
|
|
32
|
+
| list, set, rows, intervals | adjacency, empty, ordering |
|
|
33
|
+
| text, names, identifiers | empty, encoding |
|
|
34
|
+
| state that persists or repeats | idempotency, concurrency |
|
|
35
|
+
| files, network, processes | concurrency |
|
|
36
|
+
|
|
37
|
+
2. For each raised category, write one concrete candidate edge and the
|
|
38
|
+
ruling you recommend:
|
|
39
|
+
|
|
40
|
+
| Category | Probe question |
|
|
41
|
+
|----------|----------------|
|
|
42
|
+
| boundary | What happens exactly at each minimum, maximum, or threshold, and one step either side? |
|
|
43
|
+
| adjacency | When two items are equal or just touch, do they merge, collide, or stay separate? |
|
|
44
|
+
| empty | What is the result for empty, single-item, or missing input? |
|
|
45
|
+
| encoding | Whose length or equality applies: bytes, code points, grapheme clusters, normalized form, case? |
|
|
46
|
+
| ordering | When items compare equal, is output order specified and stable? |
|
|
47
|
+
| precision | Where can rounding, overflow, or truncation happen, and which rule applies? |
|
|
48
|
+
| idempotency | What happens when this runs twice on the same input? |
|
|
49
|
+
| concurrency | If interrupted or run in parallel, what is guaranteed? |
|
|
50
|
+
|
|
51
|
+
3. Ask the user in one small round, all criteria together, recommended
|
|
52
|
+
ruling first. Never rule on an edge's behavior yourself: the correct
|
|
53
|
+
answer is exactly what the criteria did not say, so a guess here is the
|
|
54
|
+
defect this probe exists to prevent. Under pre-approval, an edge whose
|
|
55
|
+
behavior the user has not stated is still a required answer: stop and
|
|
56
|
+
ask instead of ruling.
|
|
57
|
+
|
|
58
|
+
4. Record each ruling as one row:
|
|
59
|
+
- `criterion SCn` when the answer is observable where the user sees the
|
|
60
|
+
work. Add or amend that success criterion (define owns criteria), then
|
|
61
|
+
point the row at it. An existing criterion that already states the
|
|
62
|
+
edge may be named directly.
|
|
63
|
+
- `held-out` when the answer is a precise rule or a set of examples that
|
|
64
|
+
belongs in a test rather than a user-facing criterion (a normalization
|
|
65
|
+
form, a rounding rule, an inclusive bound on an internal API). Write the
|
|
66
|
+
ruling or the user's examples verbatim in Detail. The plan must schedule
|
|
67
|
+
a named test that pins it.
|
|
68
|
+
- `dismissed` when the edge cannot occur for this criterion. Detail says
|
|
69
|
+
why ("input is a closed enum"). "Not important" is not a reason.
|
|
70
|
+
|
|
71
|
+
Every criterion gets at least one row. A criterion with no data shape gets
|
|
72
|
+
one row with category `none`, dismissed with its reason.
|
|
73
|
+
|
|
74
|
+
## Prohibition probe
|
|
75
|
+
|
|
76
|
+
Walk every success criterion with one adversarial question: what could this
|
|
77
|
+
silently become that the user would not want, although nothing in INTENT.md
|
|
78
|
+
forbids it? Generate candidates privately, then keep only values, safety,
|
|
79
|
+
privacy, fairness, and transparency items: "must not email unsubscribed
|
|
80
|
+
users", "must not store raw card numbers in logs", "must not use guilt
|
|
81
|
+
wording". Drop routine engineering items (mutating input, throwing on empty
|
|
82
|
+
input, leaking handles); those are edges or ordinary review. Do not mint
|
|
83
|
+
generic security or compliance canon (injection, path traversal, data
|
|
84
|
+
retention law); record a real compliance need under Constraints instead.
|
|
85
|
+
Expect two or three kept items for work people touch, and often none for
|
|
86
|
+
internals.
|
|
87
|
+
|
|
88
|
+
Present the kept items with a recommended ruling and record each as a row:
|
|
89
|
+
|
|
90
|
+
- `criterion SCn` when a test or command can check it. State it as a
|
|
91
|
+
success criterion and point the row at it.
|
|
92
|
+
- `judgment` when it is real but not mechanically checkable. Detail says
|
|
93
|
+
what the final reviewer looks for.
|
|
94
|
+
- `dismissed` when the user rules it out. Detail records the reason.
|
|
95
|
+
|
|
96
|
+
Work that affects no person directly records a single row instead, and no
|
|
97
|
+
other: `| N1 | all | none | dismissed | <why no person is affected> |`.
|
|
98
|
+
|
|
99
|
+
## INTENT.md tables
|
|
100
|
+
|
|
101
|
+
```markdown
|
|
102
|
+
## Edge coverage
|
|
103
|
+
|
|
104
|
+
| Edge | Criterion | Category | Disposition | Detail |
|
|
105
|
+
|------|-----------|----------|-------------|--------|
|
|
106
|
+
| E1 | SC1 | boundary | criterion SC4 | |
|
|
107
|
+
| E2 | SC1 | empty | dismissed | the command always receives at least one file |
|
|
108
|
+
| E3 | SC2 | encoding | held-out | names compare by NFC code points, case-sensitive |
|
|
109
|
+
|
|
110
|
+
## Prohibitions
|
|
111
|
+
|
|
112
|
+
| Prohibition | Criterion | Must not | Disposition | Detail |
|
|
113
|
+
|-------------|-----------|----------|-------------|--------|
|
|
114
|
+
| N1 | SC2 | send a reminder to an unsubscribed user | criterion SC5 | |
|
|
115
|
+
| N2 | SC2 | use guilt or shame wording | judgment | reminder copy is neutral and states only the due item |
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Ids are contiguous from `E1` and `N1`. Detail is required for every row that
|
|
119
|
+
is not `criterion SCn`. The headings are exact: `## Edge coverage` and
|
|
120
|
+
`## Prohibitions`. An INTENT.md without them predates the probes and is not
|
|
121
|
+
rechecked; a near-miss heading fails the gate rather than switching it off.
|
|
122
|
+
|
|
123
|
+
## Downstream
|
|
124
|
+
|
|
125
|
+
- **Plan.** Every `held-out` edge gets one PLAN.md `## Held-out checks` row
|
|
126
|
+
naming the task that owns the edge's criterion and the test file (a
|
|
127
|
+
`path::test_name` node id is fine). That file is listed in the task's
|
|
128
|
+
`files`, the task's Verify runs it, and the task's Acceptance criteria
|
|
129
|
+
state the edge id and its ruling. A `criterion SCn` row needs nothing extra: SCn is
|
|
130
|
+
already covered like any criterion. A `judgment` prohibition belongs in
|
|
131
|
+
the owning task's Approach so the coder knows the line.
|
|
132
|
+
- **Review.** A row pins the reading of its criterion. A reviewer does not
|
|
133
|
+
pass a reading that contradicts a ruling in either table, and cites each
|
|
134
|
+
held-out edge and judgment prohibition id with its evidence.
|
|
135
|
+
- **Final review.** A criterion that carries a held-out edge or a judgment
|
|
136
|
+
prohibition is `met` only when its Check, Observed, or Reference cites
|
|
137
|
+
every such id with the evidence: the held-out test's recorded passing
|
|
138
|
+
output, or the observed behavior judged against Detail. When that
|
|
139
|
+
evidence is missing, did not run, or cannot be judged, record
|
|
140
|
+
`unverifiable` with `Finding: insufficient spec evidence: <id> ...`;
|
|
141
|
+
never `met` on inference. The trigger is the tag, not reviewer doubt:
|
|
142
|
+
criteria without a tag are graded as usual and never abstained.
|
|
143
|
+
|
|
144
|
+
## What the gates can and cannot prove
|
|
145
|
+
|
|
146
|
+
`check_handoffs.py intent` (before approval and again when define records
|
|
147
|
+
approval), `plan`, and `final` prove the tables are complete, every held-out
|
|
148
|
+
edge has a scheduled test, and every tagged `met` cites its tags. They cannot
|
|
149
|
+
prove who made a ruling or that a cited result says what the verdict claims.
|
|
150
|
+
Those stay with the define conversation and the reviewer's recorded evidence.
|
|
@@ -25,7 +25,8 @@ import signal
|
|
|
25
25
|
import subprocess
|
|
26
26
|
import time
|
|
27
27
|
from pathlib import Path
|
|
28
|
-
from typing import BinaryIO, Iterator, List, Optional, Sequence
|
|
28
|
+
from typing import BinaryIO, Iterable, Iterator, List, Optional, Sequence, Tuple
|
|
29
|
+
|
|
29
30
|
|
|
30
31
|
# Chosen by what imports, not os.name, so tests can emulate Windows on POSIX.
|
|
31
32
|
try:
|
|
@@ -65,6 +66,49 @@ VERIFY_RESULTS = ("pass", "fail")
|
|
|
65
66
|
VERIFY_LEDGER_SCHEMA = "gsd-path/verify-ledger/v2"
|
|
66
67
|
VERIFY_BLOCK_PATTERN = re.compile(r"```bash[ \t]*\n(?P<block>.*?)```", re.DOTALL)
|
|
67
68
|
|
|
69
|
+
REVIEW_BULLET_HEADING_PATTERN = re.compile(r"^#{2,3} ")
|
|
70
|
+
REVIEW_BULLET_FIELD_LABEL_PATTERN = re.compile(r"^- \*\*[^*]+\*\*:")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def review_bullet_field_label_pattern(field: str) -> re.Pattern[str]:
|
|
74
|
+
return re.compile(rf"^- \*\*{re.escape(field)}\*\*:[ \t]*(.*)$")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _review_bullet_field_value(lines: Sequence[str], index: int, inline: str) -> str:
|
|
78
|
+
if inline:
|
|
79
|
+
return inline
|
|
80
|
+
collected: list[str] = []
|
|
81
|
+
for line in lines[index + 1 :]:
|
|
82
|
+
if REVIEW_BULLET_FIELD_LABEL_PATTERN.match(line) or REVIEW_BULLET_HEADING_PATTERN.match(
|
|
83
|
+
line
|
|
84
|
+
):
|
|
85
|
+
break
|
|
86
|
+
if line and not line[0].isspace():
|
|
87
|
+
break
|
|
88
|
+
stripped = line.strip()
|
|
89
|
+
if stripped:
|
|
90
|
+
collected.append(stripped)
|
|
91
|
+
return "\n".join(collected)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def find_review_bullet_field_values(lines: Sequence[str], field: str) -> list[str]:
|
|
95
|
+
pattern = review_bullet_field_label_pattern(field)
|
|
96
|
+
values: list[str] = []
|
|
97
|
+
for index, line in enumerate(lines):
|
|
98
|
+
match = pattern.fullmatch(line)
|
|
99
|
+
if match is None:
|
|
100
|
+
continue
|
|
101
|
+
values.append(_review_bullet_field_value(lines, index, match.group(1)))
|
|
102
|
+
return values
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def parse_review_bullet_field(lines: Sequence[str], field: str) -> str:
|
|
106
|
+
"""Parse one `- **Field**:` review bullet, including indented continuations."""
|
|
107
|
+
values = find_review_bullet_field_values(lines, field)
|
|
108
|
+
if len(values) != 1:
|
|
109
|
+
raise ValueError(field)
|
|
110
|
+
return values[0]
|
|
111
|
+
|
|
68
112
|
|
|
69
113
|
# Sample paths the pipeline must commit. A product rule such as an unanchored
|
|
70
114
|
# `build/` would otherwise drop them silently from every commit.
|
|
@@ -118,6 +162,33 @@ def project_ignore_error(repo: Path) -> Optional[str]:
|
|
|
118
162
|
+ ". Anchor the product rule (for example `/build/`) in a task that owns it.")
|
|
119
163
|
|
|
120
164
|
|
|
165
|
+
_DEFAULT_BRANCH_RE = re.compile(
|
|
166
|
+
r"[^\s#\"'\\~^:?*\[\x00-\x1f\x7f-][^\s#\"'\\~^:?*\[\x00-\x1f\x7f]*"
|
|
167
|
+
)
|
|
168
|
+
_RESERVED_BRANCH_PREFIXES = ("gsd-path/", "gsd-path-integrate/", "gsd-path-task/")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def valid_default_branch(name: str) -> bool:
|
|
172
|
+
"""A git branch name that STATE frontmatter can hold and Path does not reserve."""
|
|
173
|
+
return (
|
|
174
|
+
bool(_DEFAULT_BRANCH_RE.fullmatch(name))
|
|
175
|
+
and name != "null"
|
|
176
|
+
and not name.startswith(_RESERVED_BRANCH_PREFIXES)
|
|
177
|
+
and not name.endswith(("/", ".", ".lock"))
|
|
178
|
+
and not any(part in name for part in ("..", "//", "/.", "@{"))
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def origin_default_branch(repo: Path) -> str:
|
|
183
|
+
"""The origin default branch that initialization records; an unset origin/HEAD means main."""
|
|
184
|
+
result = run_git(repo, "symbolic-ref", "--quiet", "refs/remotes/origin/HEAD")
|
|
185
|
+
prefix = "refs/remotes/origin/"
|
|
186
|
+
remote_ref = result.stdout.strip()
|
|
187
|
+
if result.returncode != 0 or not remote_ref.startswith(prefix):
|
|
188
|
+
return "main"
|
|
189
|
+
return remote_ref.removeprefix(prefix)
|
|
190
|
+
|
|
191
|
+
|
|
121
192
|
def section_body(text: str, heading: str) -> Optional[str]:
|
|
122
193
|
"""The text under `## <heading>` up to the next `## `, or None when absent."""
|
|
123
194
|
match = re.search(
|
|
@@ -126,6 +197,79 @@ def section_body(text: str, heading: str) -> Optional[str]:
|
|
|
126
197
|
return match.group("body") if match else None
|
|
127
198
|
|
|
128
199
|
|
|
200
|
+
def overlapping_paths(left: Sequence[str], right: Sequence[str]) -> List[str]:
|
|
201
|
+
"""Return the sorted paths that two task `files` lists both cover.
|
|
202
|
+
|
|
203
|
+
An entry covers itself and the paths below it. For a declared directory
|
|
204
|
+
and a path below it, the result names the lower path. The callers have no
|
|
205
|
+
task base, so this does not ask if an entry is a file: the task-brief
|
|
206
|
+
lint rejects a path below a file at the base.
|
|
207
|
+
"""
|
|
208
|
+
return sorted(
|
|
209
|
+
{
|
|
210
|
+
max(one, other, key=len)
|
|
211
|
+
for one in left
|
|
212
|
+
for other in right
|
|
213
|
+
if one == other or one.startswith(other + "/") or other.startswith(one + "/")
|
|
214
|
+
}
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# The `- ` of an indented sub-bullet line. A line that would then read as a
|
|
219
|
+
# numbered item (`- 2. text`) keeps its marker, so both readings of a source
|
|
220
|
+
# have the same criteria.
|
|
221
|
+
SUB_BULLET_MARKER = re.compile(r"(?m)^([ \t]+)-[ \t]+(?![ \t]*\d+\.\s)")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def without_sub_bullet_markers(source: str) -> str:
|
|
225
|
+
"""`source` with the `- ` marker of each indented sub-bullet line removed."""
|
|
226
|
+
return SUB_BULLET_MARKER.sub(r"\1", source)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class Criterion(str):
|
|
230
|
+
"""A criterion as its parser joins it, with the `- ` markers of its sub-bullet lines.
|
|
231
|
+
|
|
232
|
+
`bare` is the same criterion parsed from `without_sub_bullet_markers(source)`.
|
|
233
|
+
The joined text cannot tell a marker from a minus sign; the source lines can.
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
bare: str
|
|
237
|
+
|
|
238
|
+
def __new__(cls, text: str, bare: str) -> "Criterion":
|
|
239
|
+
criterion = super().__new__(cls, text)
|
|
240
|
+
criterion.bare = bare
|
|
241
|
+
return criterion
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def quotes_criterion(quote: str, criterion: str) -> bool:
|
|
245
|
+
"""Whether a review quote is `criterion`, for every reader.
|
|
246
|
+
|
|
247
|
+
Only whitespace is collapsed. The quote must equal the criterion with all
|
|
248
|
+
the markers of its sub-bullet lines or with none of them; every other
|
|
249
|
+
character, a hyphen inside a line too, is compared.
|
|
250
|
+
"""
|
|
251
|
+
forms = (criterion, getattr(criterion, "bare", criterion))
|
|
252
|
+
return " ".join(quote.split()) in {" ".join(form.split()) for form in forms}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def quoted_criterion(item: str, criteria: Sequence[str]) -> Optional[Tuple[int, int]]:
|
|
256
|
+
"""Index of the criterion quoted at the start of `item`, and where the quote ends.
|
|
257
|
+
|
|
258
|
+
A criterion can contain ` — ` itself, so each separator and the end of
|
|
259
|
+
`item` is a candidate, the longest quote first. A criterion as written
|
|
260
|
+
wins over any criterion read without its sub-bullet markers.
|
|
261
|
+
"""
|
|
262
|
+
ends = [match.start() for match in re.finditer("(?= —(?: |$))|$", item)][::-1]
|
|
263
|
+
for bare in (False, True):
|
|
264
|
+
for end in ends:
|
|
265
|
+
quote = " ".join(item[:end].split())
|
|
266
|
+
for index, criterion in enumerate(criteria):
|
|
267
|
+
form = getattr(criterion, "bare", criterion) if bare else criterion
|
|
268
|
+
if quote == " ".join(form.split()):
|
|
269
|
+
return index, end
|
|
270
|
+
return None
|
|
271
|
+
|
|
272
|
+
|
|
129
273
|
def task_verify_command(task_text: str) -> str:
|
|
130
274
|
"""The Verify shell text, excluding the closing fence's separator newline."""
|
|
131
275
|
body = section_body(task_text, "Verify")
|
|
@@ -133,6 +277,11 @@ def task_verify_command(task_text: str) -> str:
|
|
|
133
277
|
return block.group("block").removesuffix("\n") if block else ""
|
|
134
278
|
|
|
135
279
|
|
|
280
|
+
def fix_verify(commands: Iterable[str]) -> str:
|
|
281
|
+
"""The Verify of a fix task: each distinct source Verify in its own subshell, under `set -e`."""
|
|
282
|
+
return "set -e\n" + "\n".join(f"(\n{command}\n)" for command in dict.fromkeys(commands))
|
|
283
|
+
|
|
284
|
+
|
|
136
285
|
def latest_verify_entry(entries: list, command: str, commit: str,
|
|
137
286
|
repo: Optional[str] = None) -> Optional[dict]:
|
|
138
287
|
"""Rows are keyed by (command, repo, commit); a row without `repo` is a coordinator row."""
|
|
@@ -150,6 +299,11 @@ def verify_ledger_entries(path: Path) -> list:
|
|
|
150
299
|
return parse_verify_ledger(path.read_text(encoding="utf-8"))
|
|
151
300
|
|
|
152
301
|
|
|
302
|
+
def read_user_text(path: Path) -> str:
|
|
303
|
+
"""Read a user-edited text file with CRLF normalized to LF."""
|
|
304
|
+
return path.read_text(encoding="utf-8").replace("\r\n", "\n").replace("\r", "\n")
|
|
305
|
+
|
|
306
|
+
|
|
153
307
|
def parse_verify_ledger(text: str) -> list:
|
|
154
308
|
entries = []
|
|
155
309
|
for number, line in enumerate(text.splitlines(), start=1):
|
|
@@ -204,7 +358,43 @@ def run_command(
|
|
|
204
358
|
def run_git(
|
|
205
359
|
repo: Path, *arguments: str, input: Optional[str] = None
|
|
206
360
|
) -> subprocess.CompletedProcess[str]:
|
|
207
|
-
return _run_exact((
|
|
361
|
+
return _run_exact(git_command(repo, *arguments), input=input)
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def git_command(repo: Path, *arguments: str) -> tuple[str, ...]:
|
|
365
|
+
"""Git argv for repo, including WSL drvfs workarounds when needed."""
|
|
366
|
+
return ("git", "-C", str(repo), *git_drvfs_config_flags(repo), *arguments)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def git_drvfs_config_flags(repo: Path) -> tuple[str, ...]:
|
|
370
|
+
"""Disable index preload on 9p/drvfs mounts where git commit races on index.lock (#207)."""
|
|
371
|
+
if os.name != "posix":
|
|
372
|
+
return ()
|
|
373
|
+
try:
|
|
374
|
+
root = repo.resolve()
|
|
375
|
+
mounts = Path("/proc/mounts").read_text(encoding="utf-8", errors="replace").splitlines()
|
|
376
|
+
except OSError:
|
|
377
|
+
return ()
|
|
378
|
+
best = ""
|
|
379
|
+
fstype = ""
|
|
380
|
+
for line in mounts:
|
|
381
|
+
parts = line.split()
|
|
382
|
+
if len(parts) < 3:
|
|
383
|
+
continue
|
|
384
|
+
mount_point, mount_type = parts[1], parts[2]
|
|
385
|
+
if mount_type not in {"9p", "drvfs"}:
|
|
386
|
+
continue
|
|
387
|
+
if root == Path(mount_point) or root.is_relative_to(mount_point):
|
|
388
|
+
if len(mount_point) >= len(best):
|
|
389
|
+
best, fstype = mount_point, mount_type
|
|
390
|
+
if not fstype:
|
|
391
|
+
return ()
|
|
392
|
+
return ("-c", "core.preloadindex=false", "-c", "index.threads=1")
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def git_index_lock_retryable(stderr: str) -> bool:
|
|
396
|
+
text = stderr.casefold()
|
|
397
|
+
return "index.lock" in text and "file exists" in text
|
|
208
398
|
|
|
209
399
|
|
|
210
400
|
# OS files that are never pipeline artifacts when git ignores them.
|
|
@@ -227,6 +417,70 @@ def git_visible_entries(directory: Path) -> set:
|
|
|
227
417
|
return {path.split("/", 1)[0] for path in result.stdout.split("\0") if path}
|
|
228
418
|
|
|
229
419
|
|
|
420
|
+
RUNTIME_PIN_PATH = ".gsd-path/runtime.json"
|
|
421
|
+
RUNTIME_PIN_ALLOWANCE = (
|
|
422
|
+
"only runtime-pin commits (.gsd-path/runtime.json) may follow the reviewed HEAD"
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def reviewed_head_covers(repo: Path, reviewed: str, head: str) -> bool:
|
|
427
|
+
"""True when a review of `reviewed` still covers `head`.
|
|
428
|
+
|
|
429
|
+
That is the same commit, or `reviewed` is an ancestor of `head` and every
|
|
430
|
+
commit on the first-parent path in ``reviewed..head`` is a non-merge commit
|
|
431
|
+
whose only change is ``.gsd-path/runtime.json``. An empty commit does not
|
|
432
|
+
qualify. Any other path, any merge commit, an unresolvable SHA, or a
|
|
433
|
+
non-ancestor returns False.
|
|
434
|
+
"""
|
|
435
|
+
if not reviewed or not head:
|
|
436
|
+
return False
|
|
437
|
+
resolved_repo = Path(repo)
|
|
438
|
+
resolved: List[str] = []
|
|
439
|
+
for value in (reviewed, head):
|
|
440
|
+
result = run_git(
|
|
441
|
+
resolved_repo, "rev-parse", "--verify", "--quiet", f"{value}^{{commit}}"
|
|
442
|
+
)
|
|
443
|
+
if result.returncode != 0 or not result.stdout.strip():
|
|
444
|
+
return False
|
|
445
|
+
resolved.append(result.stdout.strip())
|
|
446
|
+
reviewed_sha, head_sha = resolved
|
|
447
|
+
if reviewed_sha == head_sha:
|
|
448
|
+
return True
|
|
449
|
+
if (
|
|
450
|
+
run_git(resolved_repo, "merge-base", "--is-ancestor", reviewed_sha, head_sha).returncode
|
|
451
|
+
!= 0
|
|
452
|
+
):
|
|
453
|
+
return False
|
|
454
|
+
listing = run_git(
|
|
455
|
+
resolved_repo,
|
|
456
|
+
"rev-list",
|
|
457
|
+
"--first-parent",
|
|
458
|
+
"--parents",
|
|
459
|
+
f"{reviewed_sha}..{head_sha}",
|
|
460
|
+
)
|
|
461
|
+
if listing.returncode != 0:
|
|
462
|
+
return False
|
|
463
|
+
for line in listing.stdout.splitlines():
|
|
464
|
+
fields = line.split()
|
|
465
|
+
if len(fields) != 2:
|
|
466
|
+
return False
|
|
467
|
+
commit_sha, parent_sha = fields[0], fields[1]
|
|
468
|
+
changed = run_git(
|
|
469
|
+
resolved_repo,
|
|
470
|
+
"diff",
|
|
471
|
+
"--name-only",
|
|
472
|
+
"--no-renames",
|
|
473
|
+
"--no-ext-diff",
|
|
474
|
+
parent_sha,
|
|
475
|
+
commit_sha,
|
|
476
|
+
)
|
|
477
|
+
if changed.returncode != 0:
|
|
478
|
+
return False
|
|
479
|
+
if {path for path in changed.stdout.splitlines() if path} != {RUNTIME_PIN_PATH}:
|
|
480
|
+
return False
|
|
481
|
+
return True
|
|
482
|
+
|
|
483
|
+
|
|
230
484
|
def atomic_replace(path: Path, temporary_path: Path, content: str) -> None:
|
|
231
485
|
temporary_path.parent.mkdir(parents=True, exist_ok=True)
|
|
232
486
|
descriptor = None
|