@opengsd/gsd-path 1.4.0 → 1.5.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 +325 -344
- package/DOCS.md +12 -2
- package/FULL.md +3 -2
- package/HOOKS.md +95 -8
- package/QUICK.md +1 -1
- package/README.md +78 -110
- package/RUNTIME.md +1 -1
- package/UPDATE.md +4 -3
- package/WORKFLOW.md +22 -1
- package/package.json +5 -2
- package/platforms/shared-agents/dispatch.md +13 -0
- package/scripts/_common.py +149 -1
- package/scripts/archive_milestone.py +14 -13
- package/scripts/build_recovery.py +96 -31
- package/scripts/build_state.py +79 -3
- package/scripts/check_docs_audit.py +77 -4
- package/scripts/check_handoffs.py +546 -30
- package/scripts/check_task_briefs.py +279 -23
- package/scripts/detect_project.py +243 -18
- package/scripts/discussion_validate.py +86 -29
- package/scripts/dispatch_driver.py +392 -22
- package/scripts/git_guard.py +3 -3
- package/scripts/guard_hook.py +1416 -165
- package/scripts/install.mjs +23 -8
- package/scripts/install.py +49 -9
- package/scripts/integration.py +146 -1
- package/scripts/isolation.py +392 -60
- package/scripts/loop_run.py +16 -4
- package/scripts/members.py +11 -9
- package/scripts/path_config.py +3 -3
- package/scripts/pipeline_git.py +63 -12
- package/scripts/pipeline_state.py +111 -33
- package/scripts/pipeline_undo.py +101 -8
- package/scripts/runtime_store.py +20 -2
- package/scripts/skill-resources.json +9 -2
- package/scripts/state_checkpoint.py +9 -27
- package/scripts/status_runtime.py +22 -4
- package/scripts/task_context.py +12 -3
- package/scripts/wizard.mjs +1 -0
- package/scripts/workflow_run.py +5 -2
- package/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path/DEFINE.md +28 -3
- package/skills/gsd-path/PLAN.md +11 -3
- package/skills/gsd-path/RESEARCH.md +11 -0
- package/skills/gsd-path/SHIP.md +6 -1
- package/skills/gsd-path/references/build-native.md +16 -1
- package/skills/gsd-path/references/dispatch.md +13 -0
- package/skills/gsd-path/references/docs-auditor.md +5 -0
- package/skills/gsd-path/references/planner.md +14 -4
- package/skills/gsd-path/references/reviewer-final.md +20 -2
- package/skills/gsd-path/references/reviewer-wave.md +15 -4
- package/skills/gsd-path/references/reviewer.md +3 -1
- package/skills/gsd-path/references/spec-probes.md +150 -0
- package/skills/gsd-path/scripts/_common.py +149 -1
- package/skills/gsd-path/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path/scripts/build_recovery.py +96 -31
- package/skills/gsd-path/scripts/build_state.py +79 -3
- package/skills/gsd-path/scripts/check_docs_audit.py +77 -4
- package/skills/gsd-path/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path/scripts/detect_project.py +243 -18
- package/skills/gsd-path/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path/scripts/dispatch_driver.py +392 -22
- package/skills/gsd-path/scripts/git_guard.py +3 -3
- package/skills/gsd-path/scripts/integration.py +146 -1
- package/skills/gsd-path/scripts/isolation.py +392 -60
- package/skills/gsd-path/scripts/loop_run.py +16 -4
- package/skills/gsd-path/scripts/members.py +11 -9
- package/skills/gsd-path/scripts/path_config.py +3 -3
- package/skills/gsd-path/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path/scripts/state_checkpoint.py +9 -27
- package/skills/gsd-path/scripts/status_runtime.py +22 -4
- package/skills/gsd-path/scripts/task_context.py +12 -3
- package/skills/gsd-path/scripts/workflow_run.py +5 -2
- 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 +1 -1
- package/skills/gsd-path/templates/wave-review.md +16 -6
- package/skills/gsd-path-build/references/build-native.md +16 -1
- package/skills/gsd-path-build/references/dispatch.md +13 -0
- package/skills/gsd-path-build/references/reviewer-final.md +20 -2
- package/skills/gsd-path-build/references/reviewer-wave.md +15 -4
- package/skills/gsd-path-build/references/reviewer.md +3 -1
- package/skills/gsd-path-build/references/spec-probes.md +150 -0
- package/skills/gsd-path-build/scripts/_common.py +149 -1
- package/skills/gsd-path-build/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-build/scripts/build_recovery.py +96 -31
- package/skills/gsd-path-build/scripts/build_state.py +79 -3
- package/skills/gsd-path-build/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path-build/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path-build/scripts/detect_project.py +243 -18
- package/skills/gsd-path-build/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path-build/scripts/dispatch_driver.py +392 -22
- package/skills/gsd-path-build/scripts/integration.py +146 -1
- package/skills/gsd-path-build/scripts/isolation.py +392 -60
- package/skills/gsd-path-build/scripts/loop_run.py +16 -4
- package/skills/gsd-path-build/scripts/members.py +11 -9
- package/skills/gsd-path-build/scripts/path_config.py +3 -3
- package/skills/gsd-path-build/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path-build/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path-build/scripts/state_checkpoint.py +9 -27
- package/skills/gsd-path-build/scripts/task_context.py +12 -3
- package/skills/gsd-path-build/scripts/workflow_run.py +5 -2
- package/skills/gsd-path-build/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-build/templates/wave-review.md +16 -6
- package/skills/gsd-path-decide/references/dispatch.md +13 -0
- package/skills/gsd-path-decide/scripts/_common.py +149 -1
- package/skills/gsd-path-decide/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-decide/scripts/build_recovery.py +96 -31
- package/skills/gsd-path-decide/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path-decide/scripts/detect_project.py +243 -18
- package/skills/gsd-path-decide/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path-decide/scripts/integration.py +146 -1
- package/skills/gsd-path-decide/scripts/isolation.py +392 -60
- package/skills/gsd-path-decide/scripts/loop_run.py +16 -4
- package/skills/gsd-path-decide/scripts/members.py +11 -9
- package/skills/gsd-path-decide/scripts/path_config.py +3 -3
- package/skills/gsd-path-decide/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path-decide/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +9 -27
- package/skills/gsd-path-decide/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-define/SKILL.md +28 -3
- package/skills/gsd-path-define/references/spec-probes.md +150 -0
- package/skills/gsd-path-define/scripts/_common.py +149 -1
- package/skills/gsd-path-define/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-define/scripts/build_recovery.py +96 -31
- package/skills/gsd-path-define/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path-define/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path-define/scripts/detect_project.py +243 -18
- package/skills/gsd-path-define/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path-define/scripts/integration.py +146 -1
- package/skills/gsd-path-define/scripts/isolation.py +392 -60
- package/skills/gsd-path-define/scripts/loop_run.py +16 -4
- package/skills/gsd-path-define/scripts/members.py +11 -9
- package/skills/gsd-path-define/scripts/path_config.py +3 -3
- package/skills/gsd-path-define/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path-define/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path-define/scripts/state_checkpoint.py +9 -27
- 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 +1 -1
- package/skills/gsd-path-discuss/scripts/_common.py +149 -1
- package/skills/gsd-path-discuss/scripts/loop_run.py +16 -4
- 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/scripts/_common.py +149 -1
- package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +96 -31
- package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +77 -4
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path-docs-audit/scripts/detect_project.py +243 -18
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path-docs-audit/scripts/integration.py +146 -1
- package/skills/gsd-path-docs-audit/scripts/isolation.py +392 -60
- package/skills/gsd-path-docs-audit/scripts/loop_run.py +16 -4
- package/skills/gsd-path-docs-audit/scripts/members.py +11 -9
- package/skills/gsd-path-docs-audit/scripts/path_config.py +3 -3
- package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +9 -27
- 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/scripts/_common.py +149 -1
- package/skills/gsd-path-forensics/scripts/loop_run.py +16 -4
- package/skills/gsd-path-forensics/scripts/path_config.py +3 -3
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +101 -8
- 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/scripts/_common.py +149 -1
- package/skills/gsd-path-inspect/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-inspect/scripts/build_recovery.py +96 -31
- package/skills/gsd-path-inspect/scripts/check_docs_audit.py +77 -4
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path-inspect/scripts/detect_project.py +243 -18
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path-inspect/scripts/integration.py +146 -1
- package/skills/gsd-path-inspect/scripts/isolation.py +392 -60
- package/skills/gsd-path-inspect/scripts/loop_run.py +16 -4
- package/skills/gsd-path-inspect/scripts/members.py +11 -9
- package/skills/gsd-path-inspect/scripts/path_config.py +3 -3
- package/skills/gsd-path-inspect/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +9 -27
- package/skills/gsd-path-inspect/scripts/workflow_run.py +5 -2
- 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 +1 -1
- package/skills/gsd-path-loop/scripts/_common.py +149 -1
- package/skills/gsd-path-loop/scripts/loop_run.py +16 -4
- 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/scripts/_common.py +149 -1
- package/skills/gsd-path-migrate/scripts/loop_run.py +16 -4
- package/skills/gsd-path-migrate/scripts/path_config.py +3 -3
- package/skills/gsd-path-plan/SKILL.md +11 -3
- package/skills/gsd-path-plan/references/dispatch.md +13 -0
- package/skills/gsd-path-plan/references/planner.md +14 -4
- package/skills/gsd-path-plan/references/spec-probes.md +150 -0
- package/skills/gsd-path-plan/scripts/_common.py +149 -1
- package/skills/gsd-path-plan/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-plan/scripts/build_recovery.py +96 -31
- package/skills/gsd-path-plan/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path-plan/scripts/detect_project.py +243 -18
- package/skills/gsd-path-plan/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path-plan/scripts/integration.py +146 -1
- package/skills/gsd-path-plan/scripts/isolation.py +392 -60
- package/skills/gsd-path-plan/scripts/loop_run.py +16 -4
- package/skills/gsd-path-plan/scripts/members.py +11 -9
- package/skills/gsd-path-plan/scripts/path_config.py +3 -3
- package/skills/gsd-path-plan/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path-plan/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +9 -27
- package/skills/gsd-path-plan/scripts/workflow_run.py +5 -2
- 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-research/SKILL.md +11 -0
- package/skills/gsd-path-research/references/dispatch.md +13 -0
- package/skills/gsd-path-research/scripts/_common.py +149 -1
- package/skills/gsd-path-research/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-research/scripts/build_recovery.py +96 -31
- package/skills/gsd-path-research/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path-research/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path-research/scripts/detect_project.py +243 -18
- package/skills/gsd-path-research/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path-research/scripts/integration.py +146 -1
- package/skills/gsd-path-research/scripts/isolation.py +392 -60
- package/skills/gsd-path-research/scripts/loop_run.py +16 -4
- package/skills/gsd-path-research/scripts/members.py +11 -9
- package/skills/gsd-path-research/scripts/path_config.py +3 -3
- package/skills/gsd-path-research/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path-research/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path-research/scripts/state_checkpoint.py +9 -27
- 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/references/dispatch.md +13 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +149 -1
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +96 -31
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path-roadmap/scripts/detect_project.py +243 -18
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path-roadmap/scripts/integration.py +146 -1
- package/skills/gsd-path-roadmap/scripts/isolation.py +392 -60
- package/skills/gsd-path-roadmap/scripts/loop_run.py +16 -4
- package/skills/gsd-path-roadmap/scripts/members.py +11 -9
- package/skills/gsd-path-roadmap/scripts/path_config.py +3 -3
- package/skills/gsd-path-roadmap/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +9 -27
- package/skills/gsd-path-roadmap/scripts/worktree_paths.py +54 -2
- package/skills/gsd-path-ship/SKILL.md +6 -1
- package/skills/gsd-path-ship/references/dispatch.md +13 -0
- package/skills/gsd-path-ship/references/reviewer-final.md +20 -2
- package/skills/gsd-path-ship/references/reviewer-wave.md +15 -4
- package/skills/gsd-path-ship/references/reviewer.md +3 -1
- package/skills/gsd-path-ship/references/spec-probes.md +150 -0
- package/skills/gsd-path-ship/scripts/_common.py +149 -1
- package/skills/gsd-path-ship/scripts/archive_milestone.py +14 -13
- package/skills/gsd-path-ship/scripts/build_recovery.py +96 -31
- package/skills/gsd-path-ship/scripts/build_state.py +79 -3
- package/skills/gsd-path-ship/scripts/check_handoffs.py +546 -30
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +279 -23
- package/skills/gsd-path-ship/scripts/detect_project.py +243 -18
- package/skills/gsd-path-ship/scripts/discussion_validate.py +86 -29
- package/skills/gsd-path-ship/scripts/integration.py +146 -1
- package/skills/gsd-path-ship/scripts/isolation.py +392 -60
- package/skills/gsd-path-ship/scripts/loop_run.py +16 -4
- package/skills/gsd-path-ship/scripts/members.py +11 -9
- package/skills/gsd-path-ship/scripts/path_config.py +3 -3
- package/skills/gsd-path-ship/scripts/pipeline_git.py +63 -12
- package/skills/gsd-path-ship/scripts/pipeline_state.py +111 -33
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +9 -27
- package/skills/gsd-path-ship/scripts/workflow_run.py +5 -2
- 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 +16 -6
- package/skills/gsd-path-undo/scripts/_common.py +149 -1
- package/skills/gsd-path-undo/scripts/loop_run.py +16 -4
- package/skills/gsd-path-undo/scripts/path_config.py +3 -3
- package/skills/gsd-path-undo/scripts/pipeline_undo.py +101 -8
- package/skills/gsd-path-undo/scripts/worktree_paths.py +54 -2
- package/skills/path/DEFINE.md +28 -3
- package/skills/path/PLAN.md +11 -3
- package/skills/path/RESEARCH.md +11 -0
- package/skills/path/SHIP.md +6 -1
- package/skills/path/references/build-native.md +16 -1
- package/skills/path/references/dispatch.md +13 -0
- package/skills/path/references/docs-auditor.md +5 -0
- package/skills/path/references/planner.md +14 -4
- package/skills/path/references/reviewer-final.md +20 -2
- package/skills/path/references/reviewer-wave.md +15 -4
- package/skills/path/references/reviewer.md +3 -1
- package/skills/path/references/spec-probes.md +150 -0
- package/skills/path/scripts/_common.py +149 -1
- package/skills/path/scripts/archive_milestone.py +14 -13
- package/skills/path/scripts/build_recovery.py +96 -31
- package/skills/path/scripts/build_state.py +79 -3
- package/skills/path/scripts/check_docs_audit.py +77 -4
- package/skills/path/scripts/check_handoffs.py +546 -30
- package/skills/path/scripts/check_task_briefs.py +279 -23
- package/skills/path/scripts/detect_project.py +243 -18
- package/skills/path/scripts/discussion_validate.py +86 -29
- package/skills/path/scripts/dispatch_driver.py +392 -22
- package/skills/path/scripts/git_guard.py +3 -3
- package/skills/path/scripts/integration.py +146 -1
- package/skills/path/scripts/isolation.py +392 -60
- package/skills/path/scripts/loop_run.py +16 -4
- package/skills/path/scripts/members.py +11 -9
- package/skills/path/scripts/path_config.py +3 -3
- package/skills/path/scripts/pipeline_git.py +63 -12
- package/skills/path/scripts/pipeline_state.py +111 -33
- package/skills/path/scripts/state_checkpoint.py +9 -27
- package/skills/path/scripts/status_runtime.py +22 -4
- package/skills/path/scripts/task_context.py +12 -3
- package/skills/path/scripts/workflow_run.py +5 -2
- 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 +1 -1
- package/skills/path/templates/wave-review.md +16 -6
- 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
|
@@ -53,6 +53,9 @@ COMMENT_PATTERN = re.compile(r"<!--.*?-->", re.DOTALL)
|
|
|
53
53
|
BACKTICK_PATTERN = re.compile(r"`([^`\n]+)`")
|
|
54
54
|
PATH_TOKEN_PATTERN = re.compile(r"[A-Za-z0-9._~/-]+")
|
|
55
55
|
VERIFY_BLOCK_PATTERN = re.compile(r"```bash[ \t]*\n(?P<block>.*?)```", re.DOTALL)
|
|
56
|
+
TEXT_TOOL_COMMANDS = frozenset({"awk", "egrep", "fgrep", "gawk", "grep", "gsed", "rg", "sed"})
|
|
57
|
+
REGEX_PATTERN_CHARS = frozenset("|*+?[]()^$.\\")
|
|
58
|
+
PIPELINE_WORKSPACE_DIRS = ("review", "discuss", "build", "plan")
|
|
56
59
|
|
|
57
60
|
|
|
58
61
|
class BriefError(RuntimeError):
|
|
@@ -73,10 +76,20 @@ def _base_exists(repo: Path, base: str, path: str) -> bool:
|
|
|
73
76
|
return _run_git(repo, "cat-file", "-e", f"{base}:{path}").returncode == 0
|
|
74
77
|
|
|
75
78
|
|
|
79
|
+
def _project_track(task: Path, repo: Path) -> Optional[str]:
|
|
80
|
+
try:
|
|
81
|
+
track = task.parent.parent.relative_to(repo).as_posix()
|
|
82
|
+
except ValueError:
|
|
83
|
+
return None
|
|
84
|
+
if track not in {".project", ".project/next"}:
|
|
85
|
+
return None
|
|
86
|
+
return track
|
|
87
|
+
|
|
88
|
+
|
|
76
89
|
def _supplied_contract(repo: Path, task: Path, token: str) -> bool:
|
|
77
90
|
"""Plan approval checkpoints these supplied inputs after the brief gate."""
|
|
78
|
-
track = task
|
|
79
|
-
if track
|
|
91
|
+
track = _project_track(task, repo)
|
|
92
|
+
if track is None:
|
|
80
93
|
return False
|
|
81
94
|
if token not in {
|
|
82
95
|
f"{track}/intent/INTENT.md",
|
|
@@ -87,6 +100,18 @@ def _supplied_contract(repo: Path, task: Path, token: str) -> bool:
|
|
|
87
100
|
return path.is_file() and path.resolve() == repo.resolve() / token
|
|
88
101
|
|
|
89
102
|
|
|
103
|
+
def _pipeline_workspace_file(repo: Path, task: Path, token: str) -> bool:
|
|
104
|
+
"""Pipeline-owned paths may exist only in the working tree before checkpoint."""
|
|
105
|
+
track = _project_track(task, repo)
|
|
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):
|
|
110
|
+
return False
|
|
111
|
+
path = repo / token
|
|
112
|
+
return path.is_file() and path.resolve() == repo.resolve() / token
|
|
113
|
+
|
|
114
|
+
|
|
90
115
|
_strip_yaml_comment = _common.strip_yaml_comment
|
|
91
116
|
_unquote = _common.unquote
|
|
92
117
|
|
|
@@ -151,7 +176,7 @@ def _clean(body: str) -> str:
|
|
|
151
176
|
return COMMENT_PATTERN.sub("", body).strip()
|
|
152
177
|
|
|
153
178
|
|
|
154
|
-
def
|
|
179
|
+
def _slash_token_shape(token: str) -> bool:
|
|
155
180
|
return (
|
|
156
181
|
"/" in token
|
|
157
182
|
and PATH_TOKEN_PATTERN.fullmatch(token) is not None
|
|
@@ -162,26 +187,91 @@ def _looks_like_path(token: str) -> bool:
|
|
|
162
187
|
)
|
|
163
188
|
|
|
164
189
|
|
|
190
|
+
def _last_segment_has_extension(token: str) -> bool:
|
|
191
|
+
name = PurePosixPath(token).name
|
|
192
|
+
return "." in name and not name.startswith(".")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _looks_like_path(
|
|
196
|
+
token: str,
|
|
197
|
+
*,
|
|
198
|
+
repo: Path,
|
|
199
|
+
git_root: Path,
|
|
200
|
+
base: str,
|
|
201
|
+
task: Path,
|
|
202
|
+
) -> bool:
|
|
203
|
+
if not _slash_token_shape(token):
|
|
204
|
+
return False
|
|
205
|
+
if _last_segment_has_extension(token):
|
|
206
|
+
return True
|
|
207
|
+
if _base_exists(git_root, base, token):
|
|
208
|
+
return True
|
|
209
|
+
if _pipeline_workspace_file(repo, task, token):
|
|
210
|
+
return True
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _looks_like_regex_pattern(token: str) -> bool:
|
|
215
|
+
return any(character in token for character in REGEX_PATTERN_CHARS)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _text_tool_command(command: str) -> bool:
|
|
219
|
+
return Path(command).name.casefold() in TEXT_TOOL_COMMANDS
|
|
220
|
+
|
|
221
|
+
|
|
165
222
|
def _normalize(path: str) -> str:
|
|
166
223
|
return str(PurePosixPath(path))
|
|
167
224
|
|
|
168
225
|
|
|
169
|
-
def _prose_tokens(
|
|
226
|
+
def _prose_tokens(
|
|
227
|
+
body: str,
|
|
228
|
+
*,
|
|
229
|
+
repo: Path,
|
|
230
|
+
git_root: Path,
|
|
231
|
+
base: str,
|
|
232
|
+
task: Path,
|
|
233
|
+
) -> List[str]:
|
|
170
234
|
cleaned = COMMENT_PATTERN.sub("", body)
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if _looks_like_path(
|
|
175
|
-
|
|
235
|
+
tokens: List[str] = []
|
|
236
|
+
for match in BACKTICK_PATTERN.finditer(cleaned):
|
|
237
|
+
candidate = match.group(1).strip()
|
|
238
|
+
if _looks_like_path(
|
|
239
|
+
candidate, repo=repo, git_root=git_root, base=base, task=task
|
|
240
|
+
):
|
|
241
|
+
tokens.append(_normalize(candidate))
|
|
242
|
+
return tokens
|
|
176
243
|
|
|
177
244
|
|
|
178
|
-
def _verify_tokens(
|
|
179
|
-
|
|
245
|
+
def _verify_tokens(
|
|
246
|
+
block: str,
|
|
247
|
+
*,
|
|
248
|
+
repo: Path,
|
|
249
|
+
git_root: Path,
|
|
250
|
+
base: str,
|
|
251
|
+
task: Path,
|
|
252
|
+
) -> List[str]:
|
|
253
|
+
tokens: List[str] = []
|
|
180
254
|
for line in block.splitlines():
|
|
181
255
|
parts = line.split()
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
256
|
+
if len(parts) < 2:
|
|
257
|
+
continue
|
|
258
|
+
command = parts[0]
|
|
259
|
+
extended_grep = False
|
|
260
|
+
index = 1
|
|
261
|
+
while index < len(parts) and parts[index].startswith("-") and "=" not in parts[index]:
|
|
262
|
+
flag = parts[index]
|
|
263
|
+
if flag in {"-E", "-G"} or flag.startswith("-E"):
|
|
264
|
+
extended_grep = True
|
|
265
|
+
index += 1
|
|
266
|
+
text_tool = _text_tool_command(command)
|
|
267
|
+
for raw in parts[index:]:
|
|
268
|
+
quoted = raw.startswith(("'", '"'))
|
|
269
|
+
token = raw.strip("\"'")
|
|
270
|
+
if text_tool and quoted:
|
|
271
|
+
continue
|
|
272
|
+
if text_tool and command.casefold() == "grep" and extended_grep and _looks_like_regex_pattern(token):
|
|
273
|
+
continue
|
|
274
|
+
if _looks_like_path(token, repo=repo, git_root=git_root, base=base, task=task):
|
|
185
275
|
tokens.append(_normalize(token))
|
|
186
276
|
return tokens
|
|
187
277
|
|
|
@@ -298,12 +388,15 @@ def _lint_task(
|
|
|
298
388
|
body = sections.get(name)
|
|
299
389
|
if body is None or not _clean(body):
|
|
300
390
|
continue
|
|
301
|
-
for token in _prose_tokens(
|
|
391
|
+
for token in _prose_tokens(
|
|
392
|
+
body, repo=repo, git_root=git_root, base=base, task=path
|
|
393
|
+
):
|
|
302
394
|
checked += 1
|
|
303
395
|
if (
|
|
304
396
|
token not in declared
|
|
305
397
|
and token not in supplied
|
|
306
398
|
and not _supplied_contract(repo, path, token)
|
|
399
|
+
and not _pipeline_workspace_file(repo, path, token)
|
|
307
400
|
and not _base_exists(git_root, base, token)
|
|
308
401
|
):
|
|
309
402
|
problems.append(f"## {name} names a path missing at the layer base: {token}")
|
|
@@ -314,9 +407,20 @@ def _lint_task(
|
|
|
314
407
|
if block is None or not block.group("block").strip():
|
|
315
408
|
problems.append("## Verify must contain a non-empty fenced bash block")
|
|
316
409
|
else:
|
|
317
|
-
for token in _verify_tokens(
|
|
410
|
+
for token in _verify_tokens(
|
|
411
|
+
block.group("block"),
|
|
412
|
+
repo=repo,
|
|
413
|
+
git_root=git_root,
|
|
414
|
+
base=base,
|
|
415
|
+
task=path,
|
|
416
|
+
):
|
|
318
417
|
checked += 1
|
|
319
|
-
if
|
|
418
|
+
if (
|
|
419
|
+
token not in declared
|
|
420
|
+
and token not in supplied
|
|
421
|
+
and not _pipeline_workspace_file(repo, path, token)
|
|
422
|
+
and not _base_exists(git_root, base, token)
|
|
423
|
+
):
|
|
320
424
|
problems.append(f"## Verify names a path missing at the layer base: {token}")
|
|
321
425
|
|
|
322
426
|
contract = None
|
|
@@ -337,9 +441,9 @@ def validate_task_briefs(
|
|
|
337
441
|
) -> Dict[str, object]:
|
|
338
442
|
"""Lint task briefs using the caller's validation bases.
|
|
339
443
|
|
|
340
|
-
|
|
341
|
-
transitive dependency files for
|
|
342
|
-
for landed tasks, whose immutable briefs must survive later file changes.
|
|
444
|
+
Plain lint uses the layer base without overrides. Plan approval and dispatch
|
|
445
|
+
supply transitive dependency files for unlanded tasks and resolved historical
|
|
446
|
+
bases for landed tasks, whose immutable briefs must survive later file changes.
|
|
343
447
|
The caller validates the task graph and landed metadata before supplying
|
|
344
448
|
these mappings; landed tasks receive no dependency-file allowance.
|
|
345
449
|
"""
|
|
@@ -381,6 +485,136 @@ def validate_task_briefs(
|
|
|
381
485
|
return {"base": resolved_base, "checked": checked, "tasks": len(task_files)}
|
|
382
486
|
|
|
383
487
|
|
|
488
|
+
def _resolve_landed_task_base(
|
|
489
|
+
task_id: str,
|
|
490
|
+
recorded_base: str,
|
|
491
|
+
recorded_member_base: object,
|
|
492
|
+
coordinator_repo: Path,
|
|
493
|
+
member_repo: Optional[Path],
|
|
494
|
+
) -> str:
|
|
495
|
+
"""Resolve a landed task's historical base in the repository it names."""
|
|
496
|
+
if __package__:
|
|
497
|
+
from .isolation import IsolationError, require_commit, require_full_sha
|
|
498
|
+
else:
|
|
499
|
+
try:
|
|
500
|
+
from isolation import IsolationError, require_commit, require_full_sha
|
|
501
|
+
except ModuleNotFoundError as error:
|
|
502
|
+
if error.name != "isolation":
|
|
503
|
+
raise
|
|
504
|
+
from scripts.isolation import IsolationError, require_commit, require_full_sha
|
|
505
|
+
|
|
506
|
+
recorded_full = require_full_sha(recorded_base)
|
|
507
|
+
if member_repo is None:
|
|
508
|
+
try:
|
|
509
|
+
return require_commit(coordinator_repo, recorded_full)
|
|
510
|
+
except IsolationError as error:
|
|
511
|
+
raise BriefError(
|
|
512
|
+
f"{task_id} landed task has invalid historical base: {error}"
|
|
513
|
+
) from error
|
|
514
|
+
|
|
515
|
+
legacy_member_base = (
|
|
516
|
+
recorded_member_base is None
|
|
517
|
+
or (isinstance(recorded_member_base, str)
|
|
518
|
+
and recorded_member_base.strip().casefold() in {"", "null", "~"})
|
|
519
|
+
or (isinstance(recorded_member_base, list) and not recorded_member_base)
|
|
520
|
+
)
|
|
521
|
+
try:
|
|
522
|
+
if not legacy_member_base:
|
|
523
|
+
if not isinstance(recorded_member_base, str):
|
|
524
|
+
raise IsolationError(
|
|
525
|
+
f"member_base must be a full 40-character SHA: {recorded_member_base}"
|
|
526
|
+
)
|
|
527
|
+
member_full = require_full_sha(recorded_member_base)
|
|
528
|
+
return require_commit(member_repo, member_full)
|
|
529
|
+
try:
|
|
530
|
+
return require_commit(coordinator_repo, recorded_full)
|
|
531
|
+
except IsolationError:
|
|
532
|
+
return require_commit(member_repo, recorded_full)
|
|
533
|
+
except IsolationError as error:
|
|
534
|
+
raise BriefError(
|
|
535
|
+
f"{task_id} landed task has invalid historical base: {error}"
|
|
536
|
+
) from error
|
|
537
|
+
|
|
538
|
+
def plan_validation_bases(
|
|
539
|
+
root: Path, project_dir: str, member_base=None, *, initial: bool = True,
|
|
540
|
+
) -> Tuple[Dict[str, str], Dict[str, Set[str]], Dict[str, str]]:
|
|
541
|
+
"""Share dependency files and landed historical bases across approval and dispatch.
|
|
542
|
+
|
|
543
|
+
Approval requires unlanded tasks to start clean. Dispatch may run beside
|
|
544
|
+
in-flight tasks, while retaining the same graph and path checks.
|
|
545
|
+
"""
|
|
546
|
+
if __package__:
|
|
547
|
+
from . import check_handoffs
|
|
548
|
+
from .isolation import IsolationError
|
|
549
|
+
else:
|
|
550
|
+
try:
|
|
551
|
+
import check_handoffs
|
|
552
|
+
from isolation import IsolationError
|
|
553
|
+
except ModuleNotFoundError as error:
|
|
554
|
+
if error.name not in {"check_handoffs", "isolation"}:
|
|
555
|
+
raise
|
|
556
|
+
from scripts import check_handoffs
|
|
557
|
+
from scripts.isolation import IsolationError
|
|
558
|
+
if member_base is None:
|
|
559
|
+
member_base = _member_bases(root)
|
|
560
|
+
try:
|
|
561
|
+
tasks, dependency_files = check_handoffs.plan_brief_inputs(
|
|
562
|
+
root, project_dir, initial=initial
|
|
563
|
+
)
|
|
564
|
+
landed_bases: Dict[str, str] = {}
|
|
565
|
+
for task_id, text in tasks.items():
|
|
566
|
+
if check_handoffs._task_scalar(text, task_id, "status") != "done":
|
|
567
|
+
continue
|
|
568
|
+
agent = check_handoffs._task_scalar(text, task_id, "agent")
|
|
569
|
+
if agent in {"", "null"}:
|
|
570
|
+
raise BriefError(f"{task_id} landed task has no recorded agent")
|
|
571
|
+
fields = check_handoffs._strict_frontmatter(text, task_id)
|
|
572
|
+
member = fields.get("repo")
|
|
573
|
+
base_repo = root
|
|
574
|
+
if member is not None:
|
|
575
|
+
located = member_base(member) if isinstance(member, str) and member else None
|
|
576
|
+
if located is None:
|
|
577
|
+
raise BriefError(f"repo: names no member in MEMBERS.md: {member}")
|
|
578
|
+
base_repo = located[0]
|
|
579
|
+
recorded_base = check_handoffs._task_scalar(text, task_id, "base")
|
|
580
|
+
try:
|
|
581
|
+
landed_bases[task_id] = _resolve_landed_task_base(
|
|
582
|
+
task_id,
|
|
583
|
+
recorded_base,
|
|
584
|
+
fields.get("member_base") if member is not None else None,
|
|
585
|
+
root,
|
|
586
|
+
base_repo if member is not None else None,
|
|
587
|
+
)
|
|
588
|
+
except IsolationError as error:
|
|
589
|
+
raise BriefError(f"{task_id} landed task has invalid historical base: {error}") from error
|
|
590
|
+
dependency_files[task_id] = set()
|
|
591
|
+
except check_handoffs.HandoffError as error:
|
|
592
|
+
raise BriefError(str(error)) from error
|
|
593
|
+
return tasks, dependency_files, landed_bases
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def validate_plan_task_briefs(
|
|
597
|
+
root: Path,
|
|
598
|
+
base: str,
|
|
599
|
+
project_dir: str = ".project",
|
|
600
|
+
tasks_dir: Optional[str] = None,
|
|
601
|
+
) -> Dict[str, object]:
|
|
602
|
+
"""Dispatch lint with dependency files and landed historical bases."""
|
|
603
|
+
member_base = _member_bases(root)
|
|
604
|
+
_, dependency_files, landed_bases = plan_validation_bases(
|
|
605
|
+
root, project_dir, member_base, initial=False
|
|
606
|
+
)
|
|
607
|
+
resolved_tasks_dir = tasks_dir or f"{project_dir}/tasks"
|
|
608
|
+
return validate_task_briefs(
|
|
609
|
+
root,
|
|
610
|
+
base,
|
|
611
|
+
resolved_tasks_dir,
|
|
612
|
+
dependency_files=dependency_files,
|
|
613
|
+
landed_bases=landed_bases,
|
|
614
|
+
member_base=member_base,
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
|
|
384
618
|
def _tasks_dir(value: str) -> str:
|
|
385
619
|
"""Argparse type for --tasks-dir: a relative POSIX-style path."""
|
|
386
620
|
|
|
@@ -411,15 +645,37 @@ def parser() -> argparse.ArgumentParser:
|
|
|
411
645
|
help="relative POSIX-style directory under --repo holding the task files "
|
|
412
646
|
"(default: .project/tasks)",
|
|
413
647
|
)
|
|
648
|
+
argument_parser.add_argument(
|
|
649
|
+
"--project-dir",
|
|
650
|
+
default=None,
|
|
651
|
+
help="apply plan dependency files and landed-task bases for this track "
|
|
652
|
+
"(inferred from --tasks-dir when its sibling plan/PLAN.md exists)",
|
|
653
|
+
)
|
|
414
654
|
return argument_parser
|
|
415
655
|
|
|
416
656
|
|
|
417
657
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
418
658
|
arguments = parser().parse_args(argv)
|
|
419
659
|
try:
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
660
|
+
root = arguments.repo.resolve()
|
|
661
|
+
project_dir = arguments.project_dir
|
|
662
|
+
if project_dir is None:
|
|
663
|
+
tasks_path = PurePosixPath(arguments.tasks_dir)
|
|
664
|
+
if tasks_path.name == "tasks" and len(tasks_path.parts) > 1:
|
|
665
|
+
track = tasks_path.parent.as_posix()
|
|
666
|
+
if (root / track / "plan/PLAN.md").is_file():
|
|
667
|
+
project_dir = track
|
|
668
|
+
if project_dir is not None:
|
|
669
|
+
result = validate_plan_task_briefs(
|
|
670
|
+
root,
|
|
671
|
+
arguments.base,
|
|
672
|
+
project_dir,
|
|
673
|
+
None if arguments.tasks_dir == DEFAULT_TASKS_DIR else arguments.tasks_dir,
|
|
674
|
+
)
|
|
675
|
+
else:
|
|
676
|
+
result = validate_task_briefs(
|
|
677
|
+
root, arguments.base, arguments.tasks_dir
|
|
678
|
+
)
|
|
423
679
|
except BriefError as error:
|
|
424
680
|
print(f"task brief validation failed: {error}", file=sys.stderr)
|
|
425
681
|
return 1
|