@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
|
@@ -426,10 +426,10 @@ def frontmatter_value(content: str, key: str) -> Optional[str]:
|
|
|
426
426
|
for line in lines[1:]:
|
|
427
427
|
if line == "---":
|
|
428
428
|
break
|
|
429
|
-
match = re.match(rf"^{re.escape(key)}
|
|
429
|
+
match = re.match(rf"^{re.escape(key)}:(.*)$", line)
|
|
430
430
|
if match:
|
|
431
|
-
value = match.group(1)
|
|
432
|
-
return value
|
|
431
|
+
value = _common.strip_yaml_comment(match.group(1))
|
|
432
|
+
return value.strip().strip('"').strip("'")
|
|
433
433
|
return None
|
|
434
434
|
|
|
435
435
|
|
|
@@ -586,14 +586,11 @@ def resolved_archive_target(
|
|
|
586
586
|
expected_number = milestone_number(branch)
|
|
587
587
|
configured = parsed_state.archive
|
|
588
588
|
entries = archive_sequence_entries(project)
|
|
589
|
-
missing_prior = [
|
|
590
|
-
number for number in range(1, expected_number) if number not in entries
|
|
591
|
-
]
|
|
592
589
|
later = sorted(number for number in entries if number > expected_number)
|
|
593
|
-
if
|
|
590
|
+
if later:
|
|
594
591
|
raise ArchiveError(
|
|
595
592
|
f"archive sequence does not match bound branch {branch}: "
|
|
596
|
-
f"
|
|
593
|
+
f"later={later}"
|
|
597
594
|
)
|
|
598
595
|
|
|
599
596
|
if configured is not None:
|
|
@@ -1065,9 +1062,10 @@ def prepare_locked(project: Path, active_root: Path, slug: str) -> dict:
|
|
|
1065
1062
|
final = active_root / "review" / "FINAL.md"
|
|
1066
1063
|
reviewed = re.search(r"(?m)^Reviewed HEAD:\s*(\S+)", final.read_text(encoding="utf-8")) if final.is_file() else None
|
|
1067
1064
|
head = require_git_success(run_git(project, "rev-parse", "HEAD"), "resolve HEAD").strip()
|
|
1068
|
-
if reviewed and reviewed.group(1)
|
|
1065
|
+
if reviewed and not _common.reviewed_head_covers(project, reviewed.group(1), head):
|
|
1069
1066
|
raise ArchiveError(
|
|
1070
1067
|
f"FINAL.md Reviewed HEAD {reviewed.group(1)} does not match current HEAD {head}; "
|
|
1068
|
+
f"{_common.RUNTIME_PIN_ALLOWANCE}; "
|
|
1071
1069
|
"re-run the final review on the current commit before archiving"
|
|
1072
1070
|
)
|
|
1073
1071
|
target = resolved_archive_target(project, slug, parsed_state)
|
|
@@ -1680,6 +1678,7 @@ def abandon_locked(
|
|
|
1680
1678
|
|
|
1681
1679
|
|
|
1682
1680
|
run_git = _common.run_git
|
|
1681
|
+
reviewed_head_covers = _common.reviewed_head_covers
|
|
1683
1682
|
run_command = _common.run_command
|
|
1684
1683
|
|
|
1685
1684
|
|
|
@@ -1899,9 +1898,10 @@ def preflight(repo: Path) -> dict:
|
|
|
1899
1898
|
require_stageable_carry_forward(project, archive)
|
|
1900
1899
|
require_clean_older_archives(project, configured)
|
|
1901
1900
|
head = require_git_success(run_git(project, "rev-parse", "HEAD"), "resolve preflight HEAD")
|
|
1902
|
-
if reviewed_head
|
|
1901
|
+
if not reviewed_head_covers(project, reviewed_head, head):
|
|
1903
1902
|
raise ArchiveError(
|
|
1904
|
-
f"FINAL.md Reviewed HEAD {reviewed_head} does not match current HEAD {head}"
|
|
1903
|
+
f"FINAL.md Reviewed HEAD {reviewed_head} does not match current HEAD {head}; "
|
|
1904
|
+
f"{_common.RUNTIME_PIN_ALLOWANCE}"
|
|
1905
1905
|
)
|
|
1906
1906
|
return {
|
|
1907
1907
|
"archive": configured,
|
|
@@ -2093,9 +2093,10 @@ def validate(repo: Path, *, historical: bool = False) -> dict:
|
|
|
2093
2093
|
|
|
2094
2094
|
members = member_ship_rows(project, archive, ship_commit)
|
|
2095
2095
|
ship_parent = require_git_success(run_git(project, "rev-parse", f"{ship_commit}^"), "resolve ship parent")
|
|
2096
|
-
if reviewed_head
|
|
2096
|
+
if not reviewed_head_covers(project, reviewed_head, ship_parent):
|
|
2097
2097
|
raise ArchiveError(
|
|
2098
|
-
f"FINAL.md Reviewed HEAD {reviewed_head} does not match ship parent {ship_parent}"
|
|
2098
|
+
f"FINAL.md Reviewed HEAD {reviewed_head} does not match ship parent {ship_parent}; "
|
|
2099
|
+
f"{_common.RUNTIME_PIN_ALLOWANCE}"
|
|
2099
2100
|
)
|
|
2100
2101
|
require_canonical_commit_body(
|
|
2101
2102
|
project,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# gsd-path project runtime
|
|
2
|
-
"""Guard
|
|
2
|
+
"""Guard return to Define or Plan using a committed recovery base."""
|
|
3
3
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
@@ -17,7 +17,9 @@ EVENTS = {
|
|
|
17
17
|
"define": "build intent corrections requested",
|
|
18
18
|
"plan": "build plan repair requested",
|
|
19
19
|
}
|
|
20
|
+
PLAN_INTENT_EVENT = "plan intent corrections requested"
|
|
20
21
|
MARKER = "build recovery: "
|
|
22
|
+
RECOVERY_KEYS = frozenset({"base", "branch", "kind", "source"})
|
|
21
23
|
|
|
22
24
|
|
|
23
25
|
def runtime():
|
|
@@ -36,9 +38,42 @@ def checkpoint_runtime():
|
|
|
36
38
|
return state_checkpoint
|
|
37
39
|
|
|
38
40
|
|
|
41
|
+
def _parse_recovery_record(value: object) -> dict:
|
|
42
|
+
if (
|
|
43
|
+
not isinstance(value, dict)
|
|
44
|
+
or not {"base", "branch", "kind"}.issubset(value)
|
|
45
|
+
or not set(value).issubset(RECOVERY_KEYS)
|
|
46
|
+
or not isinstance(value["base"], str)
|
|
47
|
+
or not re.fullmatch(r"[0-9a-f]{40}", value["base"])
|
|
48
|
+
):
|
|
49
|
+
raise runtime().PipelineStateError("invalid build recovery record")
|
|
50
|
+
source = value.get("source", "build")
|
|
51
|
+
if source not in {"build", "plan"}:
|
|
52
|
+
raise runtime().PipelineStateError("invalid build recovery record")
|
|
53
|
+
if source == "plan":
|
|
54
|
+
if value["kind"] != "define":
|
|
55
|
+
raise runtime().PipelineStateError("invalid build recovery record")
|
|
56
|
+
elif value["kind"] not in EVENTS:
|
|
57
|
+
raise runtime().PipelineStateError("invalid build recovery record")
|
|
58
|
+
return {**value, "source": source}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _recovery_base_proves_milestone(before, current, source: str) -> bool:
|
|
62
|
+
if before.branch != current.branch or before.milestone != current.milestone:
|
|
63
|
+
return False
|
|
64
|
+
if source == "plan":
|
|
65
|
+
return (before.phase, before.status) in {("plan", "active"), ("plan", "blocked")}
|
|
66
|
+
return (before.phase, before.status) == ("build", "blocked")
|
|
67
|
+
|
|
68
|
+
|
|
39
69
|
def context(repo: Path, text: str | None = None) -> dict | None:
|
|
40
70
|
state = runtime()
|
|
41
|
-
|
|
71
|
+
try:
|
|
72
|
+
current, loaded, _ = state.load_state(repo)
|
|
73
|
+
except state.PipelineStateError as error:
|
|
74
|
+
if "STATE.md must be a real file" not in str(error):
|
|
75
|
+
raise
|
|
76
|
+
return None
|
|
42
77
|
text = loaded if text is None else text
|
|
43
78
|
recovery = None
|
|
44
79
|
for line in text.splitlines():
|
|
@@ -47,26 +82,24 @@ def context(repo: Path, text: str | None = None) -> dict | None:
|
|
|
47
82
|
event = line.split(" — ", 2)[-1]
|
|
48
83
|
if event.startswith(MARKER):
|
|
49
84
|
try:
|
|
50
|
-
value = json.loads(event[len(MARKER):])
|
|
85
|
+
value = _parse_recovery_record(json.loads(event[len(MARKER):]))
|
|
51
86
|
except ValueError as error:
|
|
52
87
|
raise state.PipelineStateError("invalid build recovery record") from error
|
|
53
|
-
if (not isinstance(value, dict) or set(value) != {"base", "branch", "kind"}
|
|
54
|
-
or value["kind"] not in EVENTS
|
|
55
|
-
or not isinstance(value["base"], str)
|
|
56
|
-
or not re.fullmatch(r"[0-9a-f]{40}", value["base"])):
|
|
57
|
-
raise state.PipelineStateError("invalid build recovery record")
|
|
58
88
|
recovery = {**value, "active": True} if value["branch"] == current.branch else None
|
|
59
|
-
elif
|
|
60
|
-
recovery
|
|
89
|
+
elif recovery and recovery.get("active"):
|
|
90
|
+
source = recovery.get("source", "build")
|
|
91
|
+
if source == "build" and event == "build started":
|
|
92
|
+
recovery["active"] = False
|
|
93
|
+
elif source == "plan" and event == "plan approved":
|
|
94
|
+
recovery["active"] = False
|
|
61
95
|
if recovery:
|
|
62
96
|
if not state._is_ancestor(repo, recovery["base"], "HEAD"):
|
|
63
97
|
raise state.PipelineStateError("build recovery base is not an ancestor of HEAD")
|
|
64
|
-
|
|
65
|
-
if
|
|
98
|
+
base_text = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/STATE.md")
|
|
99
|
+
if base_text is None:
|
|
66
100
|
raise state.PipelineStateError("build recovery base has no STATE.md")
|
|
67
|
-
before = state._state_from_text(
|
|
68
|
-
if (
|
|
69
|
-
or before.branch != current.branch or before.milestone != current.milestone):
|
|
101
|
+
before = state._state_from_text(base_text)
|
|
102
|
+
if not _recovery_base_proves_milestone(before, current, recovery.get("source", "build")):
|
|
70
103
|
raise state.PipelineStateError("build recovery base does not prove this blocked milestone")
|
|
71
104
|
return recovery
|
|
72
105
|
|
|
@@ -108,18 +141,20 @@ def settled_tasks(repo: Path) -> dict[str, tuple[str, dict]]:
|
|
|
108
141
|
return tasks
|
|
109
142
|
|
|
110
143
|
|
|
111
|
-
def
|
|
144
|
+
def _begin_common(repo: Path, before, event: str) -> str:
|
|
112
145
|
state = runtime()
|
|
113
|
-
if (before.phase, before.status) != ("build", "blocked") or after.phase not in EVENTS:
|
|
114
|
-
raise state.PipelineStateError("build recovery requires build/blocked")
|
|
115
|
-
if event != EVENTS[after.phase]:
|
|
116
|
-
raise state.PipelineStateError(f"state transition requires event: {EVENTS[after.phase]}")
|
|
117
146
|
if before.archive is not None or before.branch != state._current_branch(repo):
|
|
118
147
|
raise state.PipelineStateError("build recovery requires the active bound branch")
|
|
119
148
|
if state._worktree_changes(repo):
|
|
120
149
|
raise state.PipelineStateError("checkpoint the blocked build before recovery")
|
|
121
150
|
if any(state.transaction_journals(repo).values()):
|
|
122
151
|
raise state.PipelineStateError("finish pending transactions before build recovery")
|
|
152
|
+
state._read_real_file(repo / ".project/intent/INTENT.md", "INTENT.md")
|
|
153
|
+
return state._run_git(repo, "rev-parse", "HEAD").stdout.strip()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def _settle_build_dispatch(repo: Path) -> None:
|
|
157
|
+
state = runtime()
|
|
123
158
|
records = records_root(repo)
|
|
124
159
|
for directory in {path.parent.parent for path in records.rglob("attempt-*/state.json")}:
|
|
125
160
|
latest = max(directory.glob("attempt-*/state.json"), key=lambda path: int(path.parent.name.split("-")[1]))
|
|
@@ -128,19 +163,49 @@ def begin(repo: Path, before, after, event: str) -> dict:
|
|
|
128
163
|
or (record.get("worktree") and record.get("branch") and not record.get("cleanup_complete"))
|
|
129
164
|
or record.get("outcome") not in {"landed", "collected", "blocked", "resolved", "redispatched"}):
|
|
130
165
|
raise state.PipelineStateError(f"settle dispatch records before recovery: {latest}")
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _verify_done_tasks(repo: Path, tasks: dict[str, tuple[str, dict]], base: str) -> None:
|
|
169
|
+
state = runtime()
|
|
135
170
|
done = [repo / ".project/tasks" / name for name, (_, fields) in tasks.items()
|
|
136
171
|
if fields["status"] == "done"]
|
|
137
|
-
if done:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
172
|
+
if not done:
|
|
173
|
+
return
|
|
174
|
+
if __package__:
|
|
175
|
+
from scripts.isolation import verify_landed_task_files
|
|
176
|
+
else:
|
|
177
|
+
from isolation import verify_landed_task_files
|
|
178
|
+
verify_landed_task_files(repo, done, ".project/tasks", base)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def begin(repo: Path, before, after, event: str) -> dict:
|
|
182
|
+
state = runtime()
|
|
183
|
+
if (before.phase, before.status) == ("build", "blocked") and after.phase in EVENTS:
|
|
184
|
+
if event != EVENTS[after.phase]:
|
|
185
|
+
raise state.PipelineStateError(f"state transition requires event: {EVENTS[after.phase]}")
|
|
186
|
+
_settle_build_dispatch(repo)
|
|
187
|
+
tasks = settled_tasks(repo)
|
|
188
|
+
state._read_real_file(repo / ".project/plan/PLAN.md", "PLAN.md")
|
|
189
|
+
base = _begin_common(repo, before, event)
|
|
190
|
+
_verify_done_tasks(repo, tasks, base)
|
|
191
|
+
return {"kind": after.phase, "base": base, "branch": before.branch, "source": "build"}
|
|
192
|
+
if (
|
|
193
|
+
before.phase == "plan"
|
|
194
|
+
and before.status in {"active", "blocked"}
|
|
195
|
+
and (after.phase, after.status) == ("define", "active")
|
|
196
|
+
):
|
|
197
|
+
if event != PLAN_INTENT_EVENT:
|
|
198
|
+
raise state.PipelineStateError(f"state transition requires event: {PLAN_INTENT_EVENT}")
|
|
199
|
+
state._read_real_file(repo / ".project/plan/PLAN.md", "PLAN.md")
|
|
200
|
+
tasks_dir = repo / ".project/tasks"
|
|
201
|
+
tasks = settled_tasks(repo) if tasks_dir.is_dir() and any(
|
|
202
|
+
path.is_file() and not state._common.is_ignored_junk(path)
|
|
203
|
+
for path in tasks_dir.iterdir()
|
|
204
|
+
) else {}
|
|
205
|
+
base = _begin_common(repo, before, event)
|
|
206
|
+
_verify_done_tasks(repo, tasks, base)
|
|
207
|
+
return {"kind": "define", "base": base, "branch": before.branch, "source": "plan"}
|
|
208
|
+
raise state.PipelineStateError("build recovery requires build/blocked")
|
|
144
209
|
|
|
145
210
|
|
|
146
211
|
def records_root(repo: Path) -> Path:
|
|
@@ -8,6 +8,10 @@ with testable claims where every claim row carries a valid type, a valid
|
|
|
8
8
|
verdict, and real evidence, a `## Descriptive docs` list for claimless docs,
|
|
9
9
|
the two sets disjoint and together equal to the frozen inventory, and a
|
|
10
10
|
remediation queue with one classified row per non-verified claim.
|
|
11
|
+
Queue numbers are unique, positive and strictly increasing, not necessarily
|
|
12
|
+
contiguous. With `--prior-audit`, persisting rows keep their number and new
|
|
13
|
+
rows use a number above every prior queue number and User rulings Queue #.
|
|
14
|
+
Numbers stay bound to their original (doc, claim); never reuse a retired one.
|
|
11
15
|
`## User rulings` is fixed-format durable memory. Pass the pre-rewrite audit
|
|
12
16
|
with `--prior-audit FILE`; every prior row, including its Planned value, must
|
|
13
17
|
remain an exact ordered prefix.
|
|
@@ -96,6 +100,12 @@ def _table_rows(body: str, width: int, label: str) -> List[List[str]]:
|
|
|
96
100
|
return rows
|
|
97
101
|
|
|
98
102
|
|
|
103
|
+
def _remediation_queue_rows(body: str, label: str, required: bool = False) -> List[List[str]]:
|
|
104
|
+
if not required and "|" not in body:
|
|
105
|
+
return []
|
|
106
|
+
return _table_rows(body, 6, label)
|
|
107
|
+
|
|
108
|
+
|
|
99
109
|
def _not_placeholder(value: str, label: str) -> None:
|
|
100
110
|
normalized = value.strip().strip("`\"").strip()
|
|
101
111
|
if (
|
|
@@ -247,6 +257,63 @@ def _check_carried(
|
|
|
247
257
|
raise AuditError(f"{label}: doc or evidence changed since the prior audit: {', '.join(moved)}")
|
|
248
258
|
|
|
249
259
|
|
|
260
|
+
def _queue_number(number: str, label: str) -> int:
|
|
261
|
+
if not re.fullmatch(r"[1-9][0-9]*", number):
|
|
262
|
+
raise AuditError(f"{label}: queue numbers must be positive integers")
|
|
263
|
+
return int(number)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _check_queue_number_history(
|
|
267
|
+
queue: Sequence[Sequence[str]],
|
|
268
|
+
prior_sections: Dict[str, str],
|
|
269
|
+
) -> None:
|
|
270
|
+
if "Remediation queue" not in prior_sections:
|
|
271
|
+
raise AuditError("prior audit is missing section: ## Remediation queue")
|
|
272
|
+
if "User rulings" not in prior_sections:
|
|
273
|
+
raise AuditError("prior audit is missing section: ## User rulings")
|
|
274
|
+
|
|
275
|
+
prior_queue = _remediation_queue_rows(
|
|
276
|
+
prior_sections["Remediation queue"], "Prior remediation queue"
|
|
277
|
+
)
|
|
278
|
+
prior_items_by_number: Dict[int, tuple] = {}
|
|
279
|
+
prior_numbers_by_item: Dict[tuple, int] = {}
|
|
280
|
+
for number, doc, claim, *_ in prior_queue:
|
|
281
|
+
numeric_number = _queue_number(number, "Prior remediation queue")
|
|
282
|
+
identity = (doc, claim)
|
|
283
|
+
prior_items_by_number[numeric_number] = identity
|
|
284
|
+
prior_numbers_by_item[identity] = numeric_number
|
|
285
|
+
|
|
286
|
+
prior_rulings = _user_rulings(
|
|
287
|
+
prior_sections["User rulings"], "Prior user rulings"
|
|
288
|
+
)
|
|
289
|
+
prior_ruling_numbers = {int(row[0]) for row in prior_rulings}
|
|
290
|
+
prior_max = max(
|
|
291
|
+
[*prior_items_by_number, *prior_ruling_numbers],
|
|
292
|
+
default=0,
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
for number, doc, claim, *_ in queue:
|
|
296
|
+
numeric_number = _queue_number(number, "Remediation queue")
|
|
297
|
+
identity = (doc, claim)
|
|
298
|
+
prior_number = prior_numbers_by_item.get(identity)
|
|
299
|
+
if numeric_number in prior_ruling_numbers and prior_items_by_number.get(numeric_number) != identity:
|
|
300
|
+
raise AuditError(
|
|
301
|
+
f"Remediation queue #{number}: Queue # {number} is already retired by a prior "
|
|
302
|
+
"User rulings row for a different item; give new items a number above every "
|
|
303
|
+
"prior queue number and User rulings Queue #"
|
|
304
|
+
)
|
|
305
|
+
if prior_number is not None and numeric_number != prior_number:
|
|
306
|
+
raise AuditError(
|
|
307
|
+
f"Remediation queue #{number}: persisting item must keep its prior queue "
|
|
308
|
+
f"number #{prior_number}"
|
|
309
|
+
)
|
|
310
|
+
if prior_number is None and numeric_number <= prior_max:
|
|
311
|
+
raise AuditError(
|
|
312
|
+
f"Remediation queue #{number}: new items must use a number above every prior "
|
|
313
|
+
f"queue number and User rulings Queue # (maximum {prior_max})"
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
|
|
250
317
|
def validate(
|
|
251
318
|
repo: Path,
|
|
252
319
|
audit_relative: str,
|
|
@@ -354,7 +421,9 @@ def validate(
|
|
|
354
421
|
|
|
355
422
|
queue_body = sections["Remediation queue"]
|
|
356
423
|
non_verified = sum(tallies[verdict] for verdict in VERDICTS if verdict != "verified")
|
|
357
|
-
queue =
|
|
424
|
+
queue = _remediation_queue_rows(
|
|
425
|
+
queue_body, "Remediation queue", required=bool(non_verified)
|
|
426
|
+
)
|
|
358
427
|
expected_queue = sorted(
|
|
359
428
|
(doc, claim["claim"], claim["verdict"])
|
|
360
429
|
for doc, claims in docs.items()
|
|
@@ -371,9 +440,13 @@ def validate(
|
|
|
371
440
|
_not_placeholder(action, f"Remediation queue #{number}: action")
|
|
372
441
|
if len(queue) != non_verified:
|
|
373
442
|
raise AuditError(f"Remediation queue has {len(queue)} rows for {non_verified} non-verified claims")
|
|
374
|
-
numbers = [row[0] for row in queue]
|
|
375
|
-
if
|
|
376
|
-
raise AuditError(
|
|
443
|
+
numbers = [_queue_number(row[0], "Remediation queue") for row in queue]
|
|
444
|
+
if any(later <= earlier for earlier, later in zip(numbers, numbers[1:])):
|
|
445
|
+
raise AuditError(
|
|
446
|
+
"Remediation queue numbers must be unique and strictly increasing"
|
|
447
|
+
)
|
|
448
|
+
if prior_sections is not None:
|
|
449
|
+
_check_queue_number_history(queue, prior_sections)
|
|
377
450
|
actual_queue = sorted((doc, claim, verdict) for _, doc, claim, verdict, _, _ in queue)
|
|
378
451
|
if actual_queue != expected_queue:
|
|
379
452
|
raise AuditError(
|