@opengsd/gsd-path 1.0.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/AGENTS.md +397 -0
- package/DOCS.md +341 -0
- package/FULL.md +475 -0
- package/GUIDE.md +23 -0
- package/HOOKS.md +273 -0
- package/LICENSE +21 -0
- package/MIGRATE.md +100 -0
- package/QUICK.md +146 -0
- package/README.md +280 -0
- package/RUNTIME.md +229 -0
- package/UPDATE.md +196 -0
- package/WORKFLOW.md +683 -0
- package/package.json +80 -0
- package/platforms/claude/dispatch.md +63 -0
- package/platforms/copilot/dispatch.md +61 -0
- package/platforms/cursor/agent.md +17 -0
- package/platforms/cursor/dispatch.md +63 -0
- package/platforms/grok/dispatch.md +61 -0
- package/platforms/kimi/dispatch.md +64 -0
- package/platforms/kiro/dispatch.md +58 -0
- package/platforms/opencode/dispatch.md +62 -0
- package/platforms/qwen/dispatch.md +60 -0
- package/platforms/shared-agents/dispatch.md +131 -0
- package/plugin.json +14 -0
- package/scripts/_common.py +180 -0
- package/scripts/archive_milestone.py +2265 -0
- package/scripts/bootstrap_repository.py +710 -0
- package/scripts/build_state.py +854 -0
- package/scripts/check_docs_audit.py +463 -0
- package/scripts/check_handoffs.py +1855 -0
- package/scripts/check_task_briefs.py +376 -0
- package/scripts/check_update.py +93 -0
- package/scripts/core_hook_gate.py +34 -0
- package/scripts/core_hook_settings.py +119 -0
- package/scripts/detect_project.py +1517 -0
- package/scripts/discussion_records.py +619 -0
- package/scripts/discussion_validate.py +1321 -0
- package/scripts/dispatch_driver.py +1812 -0
- package/scripts/git_guard.py +759 -0
- package/scripts/guard_hook.py +2124 -0
- package/scripts/install.mjs +3239 -0
- package/scripts/install.py +3008 -0
- package/scripts/integration.py +1433 -0
- package/scripts/isolation.py +3538 -0
- package/scripts/lean_verification.py +256 -0
- package/scripts/loop_run.py +837 -0
- package/scripts/migrate_core.py +169 -0
- package/scripts/pipeline_diagnose.py +715 -0
- package/scripts/pipeline_git.py +940 -0
- package/scripts/pipeline_state.py +2291 -0
- package/scripts/pipeline_undo.py +1039 -0
- package/scripts/promote_lookahead.py +415 -0
- package/scripts/review_findings.py +714 -0
- package/scripts/review_panel.py +553 -0
- package/scripts/skill-resources.json +340 -0
- package/scripts/state_checkpoint.py +1039 -0
- package/scripts/state_promote.py +743 -0
- package/scripts/status_runtime.py +122 -0
- package/scripts/sync_skill_resources.py +259 -0
- package/scripts/token_budget.py +146 -0
- package/scripts/wizard.mjs +179 -0
- package/scripts/workflow_run.py +117 -0
- package/skills/gsd-path/BUILD.md +700 -0
- package/skills/gsd-path/DECIDE.md +115 -0
- package/skills/gsd-path/DEFINE.md +297 -0
- package/skills/gsd-path/DOCS-AUDIT.md +190 -0
- package/skills/gsd-path/INSPECT.md +167 -0
- package/skills/gsd-path/PLAN.md +411 -0
- package/skills/gsd-path/RESEARCH.md +159 -0
- package/skills/gsd-path/ROADMAP.md +215 -0
- package/skills/gsd-path/SHIP.md +412 -0
- package/skills/gsd-path/SKILL.md +471 -0
- package/skills/gsd-path/agents/openai.yaml +6 -0
- package/skills/gsd-path/references/codebase-mapper.md +52 -0
- package/skills/gsd-path/references/coder.md +90 -0
- package/skills/gsd-path/references/decider.md +36 -0
- package/skills/gsd-path/references/dispatch.md +131 -0
- package/skills/gsd-path/references/docs-auditor.md +60 -0
- package/skills/gsd-path/references/planner.md +104 -0
- package/skills/gsd-path/references/researcher.md +42 -0
- package/skills/gsd-path/references/reviewer.md +188 -0
- package/skills/gsd-path/references/roadmapper.md +48 -0
- package/skills/gsd-path/scripts/_common.py +180 -0
- package/skills/gsd-path/scripts/archive_milestone.py +2265 -0
- package/skills/gsd-path/scripts/bootstrap_repository.py +710 -0
- package/skills/gsd-path/scripts/build_state.py +854 -0
- package/skills/gsd-path/scripts/check_docs_audit.py +463 -0
- package/skills/gsd-path/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path/scripts/check_update.py +93 -0
- package/skills/gsd-path/scripts/detect_project.py +1517 -0
- package/skills/gsd-path/scripts/discussion_records.py +619 -0
- package/skills/gsd-path/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path/scripts/dispatch_driver.py +1812 -0
- package/skills/gsd-path/scripts/integration.py +1433 -0
- package/skills/gsd-path/scripts/isolation.py +3538 -0
- package/skills/gsd-path/scripts/lean_verification.py +256 -0
- package/skills/gsd-path/scripts/loop_run.py +837 -0
- package/skills/gsd-path/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path/scripts/promote_lookahead.py +415 -0
- package/skills/gsd-path/scripts/review_findings.py +714 -0
- package/skills/gsd-path/scripts/review_panel.py +553 -0
- package/skills/gsd-path/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path/scripts/state_promote.py +743 -0
- package/skills/gsd-path/scripts/token_budget.py +146 -0
- package/skills/gsd-path/scripts/workflow_run.py +117 -0
- package/skills/gsd-path/templates/answers.md +29 -0
- package/skills/gsd-path/templates/archive-manifest.md +37 -0
- package/skills/gsd-path/templates/charter.md +50 -0
- package/skills/gsd-path/templates/codebase.md +46 -0
- package/skills/gsd-path/templates/dialogue.md +21 -0
- package/skills/gsd-path/templates/docs-audit.md +69 -0
- package/skills/gsd-path/templates/evidence.md +24 -0
- package/skills/gsd-path/templates/final-review.md +27 -0
- package/skills/gsd-path/templates/gap-review.md +20 -0
- package/skills/gsd-path/templates/intent.md +98 -0
- package/skills/gsd-path/templates/loop.md +59 -0
- package/skills/gsd-path/templates/patch-findings.md +16 -0
- package/skills/gsd-path/templates/plan-panel.md +25 -0
- package/skills/gsd-path/templates/plan.md +89 -0
- package/skills/gsd-path/templates/repository.md +16 -0
- package/skills/gsd-path/templates/research-handoff.md +25 -0
- package/skills/gsd-path/templates/roadmap.md +38 -0
- package/skills/gsd-path/templates/skeptic.md +36 -0
- package/skills/gsd-path/templates/state.md +27 -0
- package/skills/gsd-path/templates/synthesis.md +36 -0
- package/skills/gsd-path/templates/task.md +74 -0
- package/skills/gsd-path/templates/wave-panel.md +25 -0
- package/skills/gsd-path/templates/wave-review.md +64 -0
- package/skills/gsd-path-build/SKILL.md +700 -0
- package/skills/gsd-path-build/agents/openai.yaml +6 -0
- package/skills/gsd-path-build/references/coder.md +90 -0
- package/skills/gsd-path-build/references/dispatch.md +131 -0
- package/skills/gsd-path-build/references/reviewer.md +188 -0
- package/skills/gsd-path-build/scripts/_common.py +180 -0
- package/skills/gsd-path-build/scripts/archive_milestone.py +2265 -0
- package/skills/gsd-path-build/scripts/build_state.py +854 -0
- package/skills/gsd-path-build/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path-build/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path-build/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-build/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path-build/scripts/dispatch_driver.py +1812 -0
- package/skills/gsd-path-build/scripts/integration.py +1433 -0
- package/skills/gsd-path-build/scripts/isolation.py +3538 -0
- package/skills/gsd-path-build/scripts/loop_run.py +837 -0
- package/skills/gsd-path-build/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path-build/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path-build/scripts/review_findings.py +714 -0
- package/skills/gsd-path-build/scripts/review_panel.py +553 -0
- package/skills/gsd-path-build/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path-build/scripts/state_promote.py +743 -0
- package/skills/gsd-path-build/scripts/token_budget.py +146 -0
- package/skills/gsd-path-build/scripts/workflow_run.py +117 -0
- package/skills/gsd-path-build/templates/skeptic.md +36 -0
- package/skills/gsd-path-build/templates/task.md +74 -0
- package/skills/gsd-path-build/templates/wave-panel.md +25 -0
- package/skills/gsd-path-build/templates/wave-review.md +64 -0
- package/skills/gsd-path-decide/SKILL.md +115 -0
- package/skills/gsd-path-decide/agents/openai.yaml +6 -0
- package/skills/gsd-path-decide/references/decider.md +36 -0
- package/skills/gsd-path-decide/references/dispatch.md +131 -0
- package/skills/gsd-path-decide/scripts/_common.py +180 -0
- package/skills/gsd-path-decide/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path-decide/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-decide/scripts/isolation.py +3538 -0
- package/skills/gsd-path-decide/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path-decide/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path-decide/scripts/state_promote.py +743 -0
- package/skills/gsd-path-decide/templates/synthesis.md +36 -0
- package/skills/gsd-path-define/SKILL.md +297 -0
- package/skills/gsd-path-define/agents/openai.yaml +6 -0
- package/skills/gsd-path-define/scripts/_common.py +180 -0
- package/skills/gsd-path-define/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path-define/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path-define/scripts/detect_project.py +1517 -0
- package/skills/gsd-path-define/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-define/scripts/isolation.py +3538 -0
- package/skills/gsd-path-define/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path-define/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path-define/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path-define/scripts/state_promote.py +743 -0
- package/skills/gsd-path-define/templates/charter.md +50 -0
- package/skills/gsd-path-define/templates/intent.md +98 -0
- package/skills/gsd-path-define/templates/state.md +27 -0
- package/skills/gsd-path-discuss/SKILL.md +162 -0
- package/skills/gsd-path-discuss/agents/openai.yaml +6 -0
- package/skills/gsd-path-discuss/scripts/_common.py +180 -0
- package/skills/gsd-path-discuss/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-discuss/templates/answers.md +29 -0
- package/skills/gsd-path-discuss/templates/dialogue.md +21 -0
- package/skills/gsd-path-docs-audit/SKILL.md +190 -0
- package/skills/gsd-path-docs-audit/agents/openai.yaml +6 -0
- package/skills/gsd-path-docs-audit/references/dispatch.md +131 -0
- package/skills/gsd-path-docs-audit/references/docs-auditor.md +60 -0
- package/skills/gsd-path-docs-audit/scripts/_common.py +180 -0
- package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +463 -0
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path-docs-audit/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-docs-audit/scripts/isolation.py +3538 -0
- package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path-docs-audit/scripts/state_promote.py +743 -0
- package/skills/gsd-path-docs-audit/templates/docs-audit.md +69 -0
- package/skills/gsd-path-forensics/SKILL.md +39 -0
- package/skills/gsd-path-forensics/agents/openai.yaml +6 -0
- package/skills/gsd-path-forensics/scripts/_common.py +180 -0
- package/skills/gsd-path-forensics/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +715 -0
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +1039 -0
- package/skills/gsd-path-inspect/SKILL.md +167 -0
- package/skills/gsd-path-inspect/agents/openai.yaml +6 -0
- package/skills/gsd-path-inspect/references/codebase-mapper.md +52 -0
- package/skills/gsd-path-inspect/references/dispatch.md +131 -0
- package/skills/gsd-path-inspect/references/docs-auditor.md +60 -0
- package/skills/gsd-path-inspect/scripts/_common.py +180 -0
- package/skills/gsd-path-inspect/scripts/check_docs_audit.py +463 -0
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path-inspect/scripts/detect_project.py +1517 -0
- package/skills/gsd-path-inspect/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-inspect/scripts/isolation.py +3538 -0
- package/skills/gsd-path-inspect/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path-inspect/scripts/state_promote.py +743 -0
- package/skills/gsd-path-inspect/templates/codebase.md +46 -0
- package/skills/gsd-path-inspect/templates/docs-audit.md +69 -0
- package/skills/gsd-path-inspect/templates/state.md +27 -0
- package/skills/gsd-path-loop/SKILL.md +98 -0
- package/skills/gsd-path-loop/agents/openai.yaml +6 -0
- package/skills/gsd-path-loop/examples/ci-repair.LOOP.md +61 -0
- package/skills/gsd-path-loop/scripts/_common.py +180 -0
- package/skills/gsd-path-loop/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-loop/scripts/loop_run.py +837 -0
- package/skills/gsd-path-loop/templates/loop.md +59 -0
- package/skills/gsd-path-migrate/SKILL.md +134 -0
- package/skills/gsd-path-migrate/agents/openai.yaml +6 -0
- package/skills/gsd-path-migrate/scripts/core_hook_gate.py +34 -0
- package/skills/gsd-path-migrate/scripts/core_hook_settings.py +119 -0
- package/skills/gsd-path-migrate/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-migrate/scripts/migrate_core.py +169 -0
- package/skills/gsd-path-plan/SKILL.md +411 -0
- package/skills/gsd-path-plan/agents/openai.yaml +6 -0
- package/skills/gsd-path-plan/references/dispatch.md +131 -0
- package/skills/gsd-path-plan/references/planner.md +104 -0
- package/skills/gsd-path-plan/scripts/_common.py +180 -0
- package/skills/gsd-path-plan/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path-plan/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-plan/scripts/isolation.py +3538 -0
- package/skills/gsd-path-plan/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path-plan/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path-plan/scripts/review_panel.py +553 -0
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path-plan/scripts/state_promote.py +743 -0
- package/skills/gsd-path-plan/scripts/workflow_run.py +117 -0
- package/skills/gsd-path-plan/templates/patch-findings.md +16 -0
- package/skills/gsd-path-plan/templates/plan-panel.md +25 -0
- package/skills/gsd-path-plan/templates/plan.md +89 -0
- package/skills/gsd-path-plan/templates/task.md +74 -0
- package/skills/gsd-path-research/SKILL.md +159 -0
- package/skills/gsd-path-research/agents/openai.yaml +6 -0
- package/skills/gsd-path-research/references/dispatch.md +131 -0
- package/skills/gsd-path-research/references/researcher.md +42 -0
- package/skills/gsd-path-research/scripts/_common.py +180 -0
- package/skills/gsd-path-research/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path-research/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path-research/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-research/scripts/isolation.py +3538 -0
- package/skills/gsd-path-research/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path-research/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path-research/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path-research/scripts/state_promote.py +743 -0
- package/skills/gsd-path-research/templates/evidence.md +24 -0
- package/skills/gsd-path-research/templates/research-handoff.md +25 -0
- package/skills/gsd-path-roadmap/SKILL.md +215 -0
- package/skills/gsd-path-roadmap/agents/openai.yaml +6 -0
- package/skills/gsd-path-roadmap/references/dispatch.md +131 -0
- package/skills/gsd-path-roadmap/references/roadmapper.md +48 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +180 -0
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +2265 -0
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path-roadmap/scripts/detect_project.py +1517 -0
- package/skills/gsd-path-roadmap/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path-roadmap/scripts/integration.py +1433 -0
- package/skills/gsd-path-roadmap/scripts/isolation.py +3538 -0
- package/skills/gsd-path-roadmap/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +415 -0
- package/skills/gsd-path-roadmap/scripts/review_panel.py +553 -0
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path-roadmap/scripts/state_promote.py +743 -0
- package/skills/gsd-path-roadmap/templates/roadmap.md +38 -0
- package/skills/gsd-path-ship/SKILL.md +412 -0
- package/skills/gsd-path-ship/agents/openai.yaml +6 -0
- package/skills/gsd-path-ship/references/dispatch.md +131 -0
- package/skills/gsd-path-ship/references/reviewer.md +188 -0
- package/skills/gsd-path-ship/scripts/_common.py +180 -0
- package/skills/gsd-path-ship/scripts/archive_milestone.py +2265 -0
- package/skills/gsd-path-ship/scripts/build_state.py +854 -0
- package/skills/gsd-path-ship/scripts/check_handoffs.py +1855 -0
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +376 -0
- package/skills/gsd-path-ship/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-ship/scripts/discussion_validate.py +1321 -0
- package/skills/gsd-path-ship/scripts/integration.py +1433 -0
- package/skills/gsd-path-ship/scripts/isolation.py +3538 -0
- package/skills/gsd-path-ship/scripts/lean_verification.py +256 -0
- package/skills/gsd-path-ship/scripts/pipeline_git.py +940 -0
- package/skills/gsd-path-ship/scripts/pipeline_state.py +2291 -0
- package/skills/gsd-path-ship/scripts/review_panel.py +553 -0
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +1039 -0
- package/skills/gsd-path-ship/scripts/state_promote.py +743 -0
- package/skills/gsd-path-ship/scripts/workflow_run.py +117 -0
- package/skills/gsd-path-ship/templates/archive-manifest.md +37 -0
- package/skills/gsd-path-ship/templates/final-review.md +27 -0
- package/skills/gsd-path-ship/templates/gap-review.md +20 -0
- package/skills/gsd-path-ship/templates/patch-findings.md +16 -0
- package/skills/gsd-path-ship/templates/wave-review.md +64 -0
- package/skills/gsd-path-undo/SKILL.md +45 -0
- package/skills/gsd-path-undo/agents/openai.yaml +6 -0
- package/skills/gsd-path-undo/scripts/_common.py +180 -0
- package/skills/gsd-path-undo/scripts/discussion_records.py +619 -0
- package/skills/gsd-path-undo/scripts/pipeline_undo.py +1039 -0
- package/skills/path/BUILD.md +700 -0
- package/skills/path/DECIDE.md +115 -0
- package/skills/path/DEFINE.md +297 -0
- package/skills/path/DOCS-AUDIT.md +190 -0
- package/skills/path/INSPECT.md +167 -0
- package/skills/path/PLAN.md +411 -0
- package/skills/path/RESEARCH.md +159 -0
- package/skills/path/ROADMAP.md +215 -0
- package/skills/path/SHIP.md +412 -0
- package/skills/path/SKILL.md +471 -0
- package/skills/path/agents/openai.yaml +6 -0
- package/skills/path/references/codebase-mapper.md +52 -0
- package/skills/path/references/coder.md +90 -0
- package/skills/path/references/decider.md +36 -0
- package/skills/path/references/dispatch.md +131 -0
- package/skills/path/references/docs-auditor.md +60 -0
- package/skills/path/references/planner.md +104 -0
- package/skills/path/references/researcher.md +42 -0
- package/skills/path/references/reviewer.md +188 -0
- package/skills/path/references/roadmapper.md +48 -0
- package/skills/path/scripts/_common.py +180 -0
- package/skills/path/scripts/archive_milestone.py +2265 -0
- package/skills/path/scripts/bootstrap_repository.py +710 -0
- package/skills/path/scripts/build_state.py +854 -0
- package/skills/path/scripts/check_docs_audit.py +463 -0
- package/skills/path/scripts/check_handoffs.py +1855 -0
- package/skills/path/scripts/check_task_briefs.py +376 -0
- package/skills/path/scripts/check_update.py +93 -0
- package/skills/path/scripts/detect_project.py +1517 -0
- package/skills/path/scripts/discussion_records.py +619 -0
- package/skills/path/scripts/discussion_validate.py +1321 -0
- package/skills/path/scripts/dispatch_driver.py +1812 -0
- package/skills/path/scripts/integration.py +1433 -0
- package/skills/path/scripts/isolation.py +3538 -0
- package/skills/path/scripts/lean_verification.py +256 -0
- package/skills/path/scripts/loop_run.py +837 -0
- package/skills/path/scripts/pipeline_git.py +940 -0
- package/skills/path/scripts/pipeline_state.py +2291 -0
- package/skills/path/scripts/promote_lookahead.py +415 -0
- package/skills/path/scripts/review_findings.py +714 -0
- package/skills/path/scripts/review_panel.py +553 -0
- package/skills/path/scripts/state_checkpoint.py +1039 -0
- package/skills/path/scripts/state_promote.py +743 -0
- package/skills/path/scripts/token_budget.py +146 -0
- package/skills/path/scripts/workflow_run.py +117 -0
- package/skills/path/templates/answers.md +29 -0
- package/skills/path/templates/archive-manifest.md +37 -0
- package/skills/path/templates/charter.md +50 -0
- package/skills/path/templates/codebase.md +46 -0
- package/skills/path/templates/dialogue.md +21 -0
- package/skills/path/templates/docs-audit.md +69 -0
- package/skills/path/templates/evidence.md +24 -0
- package/skills/path/templates/final-review.md +27 -0
- package/skills/path/templates/gap-review.md +20 -0
- package/skills/path/templates/intent.md +98 -0
- package/skills/path/templates/loop.md +59 -0
- package/skills/path/templates/patch-findings.md +16 -0
- package/skills/path/templates/plan-panel.md +25 -0
- package/skills/path/templates/plan.md +89 -0
- package/skills/path/templates/repository.md +16 -0
- package/skills/path/templates/research-handoff.md +25 -0
- package/skills/path/templates/roadmap.md +38 -0
- package/skills/path/templates/skeptic.md +36 -0
- package/skills/path/templates/state.md +27 -0
- package/skills/path/templates/synthesis.md +36 -0
- package/skills/path/templates/task.md +74 -0
- package/skills/path/templates/wave-panel.md +25 -0
- package/skills/path/templates/wave-review.md +64 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Run canonical workflow helpers with fixed arguments and fail-stop receipts."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from scripts import _common
|
|
12
|
+
except ImportError: # bundled copy inside a skill's scripts directory
|
|
13
|
+
import _common
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class StepFailed(RuntimeError):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def run_workflow(repo: Path, action: str, project_dir: str, expected_head: str = None,
|
|
21
|
+
task_id: str = None, round_size: int = None) -> dict:
|
|
22
|
+
steps = []
|
|
23
|
+
scripts = Path(__file__).resolve().parent
|
|
24
|
+
|
|
25
|
+
def step(script, *arguments):
|
|
26
|
+
command = [sys.executable, "-B", str(scripts / script), *arguments]
|
|
27
|
+
completed = subprocess.run(command, cwd=repo, capture_output=True, text=True)
|
|
28
|
+
receipt = {"script": script, "command": command, "exit_code": completed.returncode,
|
|
29
|
+
"stdout": completed.stdout, "stderr": completed.stderr}
|
|
30
|
+
steps.append(receipt)
|
|
31
|
+
if completed.stderr:
|
|
32
|
+
sys.stderr.write(completed.stderr)
|
|
33
|
+
if completed.returncode:
|
|
34
|
+
raise StepFailed(script)
|
|
35
|
+
try:
|
|
36
|
+
receipt["result"] = json.loads(completed.stdout)
|
|
37
|
+
except json.JSONDecodeError as error:
|
|
38
|
+
raise StepFailed(f"{script} did not return JSON") from error
|
|
39
|
+
return receipt["result"]
|
|
40
|
+
|
|
41
|
+
common = ["--repo", str(repo), "--project-dir", project_dir]
|
|
42
|
+
try:
|
|
43
|
+
if action == "route":
|
|
44
|
+
step("pipeline_state.py", "route", *common)
|
|
45
|
+
elif action == "lint-round":
|
|
46
|
+
head = subprocess.run(["git", "rev-parse", "--verify", "HEAD"],
|
|
47
|
+
cwd=repo, capture_output=True, text=True, check=True).stdout.strip()
|
|
48
|
+
step("check_task_briefs.py", "--repo", str(repo), "--base", head,
|
|
49
|
+
"--tasks-dir", f"{project_dir}/tasks")
|
|
50
|
+
step("check_handoffs.py", "plan", *common)
|
|
51
|
+
elif action == "prepare-task":
|
|
52
|
+
task = step("isolation.py", "isolate-task", "--repo", str(repo),
|
|
53
|
+
"--base", expected_head, "--task-id", task_id,
|
|
54
|
+
"--round-size", str(round_size))
|
|
55
|
+
if task["mode"] == "serial":
|
|
56
|
+
step("isolation.py", "isolate-verify", "--repo", str(repo),
|
|
57
|
+
"--base", expected_head, "--name", f"task-{task_id.lower()}-verify")
|
|
58
|
+
elif action == "build-evidence":
|
|
59
|
+
proof = step("build_state.py", "verify-landed", *common, "--head", expected_head)
|
|
60
|
+
# The landing proof has one canonical home; lean final-review reuse allows only this path.
|
|
61
|
+
evidence = repo / project_dir / "build" / "evidence.json"
|
|
62
|
+
_common.atomic_write(evidence, json.dumps(proof, indent=2, sort_keys=True) + "\n")
|
|
63
|
+
steps.append({"script": "workflow_run.py", "evidence": str(evidence)})
|
|
64
|
+
elif action == "prepare-final":
|
|
65
|
+
if project_dir != ".project":
|
|
66
|
+
raise StepFailed("final review requires the active milestone")
|
|
67
|
+
pending = step("discussion_records.py", "pending", "--repo", str(repo))
|
|
68
|
+
if pending["pending"]:
|
|
69
|
+
raise StepFailed("pending discussion requires its owner disposition")
|
|
70
|
+
step("build_state.py", "verify-landed", *common, "--head", expected_head)
|
|
71
|
+
step("lean_verification.py", "--repo", str(repo), "--expected-head", expected_head)
|
|
72
|
+
else:
|
|
73
|
+
pending = step("discussion_records.py", "pending", "--repo", str(repo))
|
|
74
|
+
if pending["pending"]:
|
|
75
|
+
raise StepFailed("pending discussion requires its owner disposition")
|
|
76
|
+
step("check_handoffs.py", "plan", *common)
|
|
77
|
+
head = subprocess.run(["git", "rev-parse", "--verify", "HEAD"],
|
|
78
|
+
cwd=repo, capture_output=True, text=True, check=True).stdout.strip()
|
|
79
|
+
step("check_task_briefs.py", "--repo", str(repo), "--base", head,
|
|
80
|
+
"--tasks-dir", f"{project_dir}/tasks")
|
|
81
|
+
panel = ["validate-plan", "--plan", str(repo / project_dir / "plan/PLAN.md"),
|
|
82
|
+
"--intent", str(repo / project_dir / "intent/INTENT.md")]
|
|
83
|
+
charter = repo / ".project/CHARTER.md"
|
|
84
|
+
if charter.exists():
|
|
85
|
+
panel += ["--charter", str(charter)]
|
|
86
|
+
step("review_panel.py", *panel)
|
|
87
|
+
if action == "approve-plan":
|
|
88
|
+
step("pipeline_state.py", "approve", *common, "--kind", "plan",
|
|
89
|
+
"--expected-head", expected_head)
|
|
90
|
+
except (StepFailed, OSError, subprocess.CalledProcessError) as error:
|
|
91
|
+
return {"status": "blocked", "reason": str(error), "steps": steps,
|
|
92
|
+
"next": "$gsd-path-forensics"}
|
|
93
|
+
return {"status": "complete", "steps": steps}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def main(argv=None):
|
|
97
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
98
|
+
parser.add_argument("action", choices=("route", "gate-plan", "approve-plan", "build-evidence", "lint-round", "prepare-task", "prepare-final"))
|
|
99
|
+
parser.add_argument("--repo", type=Path, required=True)
|
|
100
|
+
parser.add_argument("--project-dir", choices=(".project", ".project/next"), default=".project")
|
|
101
|
+
parser.add_argument("--expected-head")
|
|
102
|
+
parser.add_argument("--task-id")
|
|
103
|
+
parser.add_argument("--round-size", type=int)
|
|
104
|
+
arguments = parser.parse_args(argv)
|
|
105
|
+
if arguments.action in {"approve-plan", "build-evidence", "prepare-task", "prepare-final"} and not arguments.expected_head:
|
|
106
|
+
parser.error(f"{arguments.action} requires --expected-head")
|
|
107
|
+
if arguments.action == "prepare-task" and (not arguments.task_id or arguments.round_size is None):
|
|
108
|
+
parser.error("prepare-task requires --task-id and --round-size")
|
|
109
|
+
result = run_workflow(arguments.repo.resolve(), arguments.action,
|
|
110
|
+
arguments.project_dir, arguments.expected_head,
|
|
111
|
+
arguments.task_id, arguments.round_size)
|
|
112
|
+
print(json.dumps(result, sort_keys=True))
|
|
113
|
+
return 0 if result["status"] == "complete" else 1
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Archive — <NNN>-<milestone-slug>
|
|
2
|
+
|
|
3
|
+
<!-- Written at ship time by $gsd-path-ship final. The archive's table of
|
|
4
|
+
contents: a future audit must be able to reconstruct the milestone
|
|
5
|
+
from this file. Append-only once written. The validator requires
|
|
6
|
+
"Final verdict:" to be exactly the string shown below. -->
|
|
7
|
+
|
|
8
|
+
Milestone: <name>
|
|
9
|
+
Shipped: <date>
|
|
10
|
+
Final verdict: all criteria met; project verify passed
|
|
11
|
+
Waves: <n> Tasks: <n> done / <n> total Review cycles used: <per wave, e.g. 1/2/1>
|
|
12
|
+
Carried forward: <none, or "N DOCS-AUDIT ruling(s)">
|
|
13
|
+
|
|
14
|
+
## Success criteria at ship
|
|
15
|
+
|
|
16
|
+
<!-- Evidence is copied verbatim from FINAL.md; escape a literal pipe as
|
|
17
|
+
\| so the row keeps its three cells. -->
|
|
18
|
+
|
|
19
|
+
| Criterion | Verdict | Evidence |
|
|
20
|
+
|-----------|---------|----------|
|
|
21
|
+
| <criterion copied from FINAL.md> | met | <FINAL Reference; else Check; else Observed> |
|
|
22
|
+
|
|
23
|
+
## Contents
|
|
24
|
+
|
|
25
|
+
<!-- Every file moved into this archive, one per line, relative paths. -->
|
|
26
|
+
- intent/INTENT.md
|
|
27
|
+
- research/...
|
|
28
|
+
- plan/PLAN.md
|
|
29
|
+
- tasks/...
|
|
30
|
+
- review/...
|
|
31
|
+
|
|
32
|
+
## Notes
|
|
33
|
+
|
|
34
|
+
<!-- Anything the next milestone should know: escalations that shaped the
|
|
35
|
+
build, accepted drift, known debt. Write at least one substantive bullet;
|
|
36
|
+
use "none" if clean. -->
|
|
37
|
+
- <note>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Final Review — <project name>
|
|
2
|
+
|
|
3
|
+
<!-- Authorship and evidence reuse follow the ship phase contract. Each INTENT.md success
|
|
4
|
+
criterion appears exactly once and carries checked evidence. A criterion
|
|
5
|
+
listed in PLAN.md's Surface contract also names its Surface, and its
|
|
6
|
+
Check is the walkthrough performed — never internal test output. Embedded
|
|
7
|
+
angle brackets are valid in concrete Check and Observed values. -->
|
|
8
|
+
|
|
9
|
+
Reviewed HEAD: <full SHA>
|
|
10
|
+
Overall verdict: <pass | blocked>
|
|
11
|
+
|
|
12
|
+
<!-- pass only when every criterion is met; blocked when any criterion is
|
|
13
|
+
not-met or unverifiable. -->
|
|
14
|
+
|
|
15
|
+
## Success criteria
|
|
16
|
+
|
|
17
|
+
### SC1 — <criterion copied verbatim from INTENT.md>
|
|
18
|
+
|
|
19
|
+
- **Verdict**: <met | not-met | unverifiable>
|
|
20
|
+
- **Surface**: <the surface PLAN.md's Surface contract lists this criterion under; omit the whole field for any other criterion>
|
|
21
|
+
- **Check**: `<command actually run, or "none">`
|
|
22
|
+
- **Observed**: <relevant output or observed behavior>
|
|
23
|
+
- **Reference**: <file:line, artifact path, or "none">
|
|
24
|
+
- **Finding**: <none, or what was found instead>
|
|
25
|
+
- **Fix direction**: <none, or concrete direction for a patch task>
|
|
26
|
+
|
|
27
|
+
<!-- Repeat once per success criterion, preserving INTENT.md order. -->
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Gap Review — <N>: <risk identity>
|
|
2
|
+
|
|
3
|
+
<!-- Written by one final gap reviewer. This file covers exactly one
|
|
4
|
+
cross-wave integration risk named in the review brief. -->
|
|
5
|
+
|
|
6
|
+
Reviewed HEAD: <full SHA>
|
|
7
|
+
Gap verdict: <pass | blocked>
|
|
8
|
+
Risk: <same risk identity as the heading>
|
|
9
|
+
Waves checked: <wave numbers>
|
|
10
|
+
|
|
11
|
+
## Checked evidence
|
|
12
|
+
|
|
13
|
+
- **Check**: `<command actually run, or "none">`
|
|
14
|
+
- **Observed**: <relevant output or observed behavior>
|
|
15
|
+
- **Reference**: <file:line, artifact path, or "none">
|
|
16
|
+
|
|
17
|
+
## Finding
|
|
18
|
+
|
|
19
|
+
- **Found**: <evidence-backed result>
|
|
20
|
+
- **Fix direction**: <none when pass; concrete direction when blocked>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Patch Findings
|
|
2
|
+
|
|
3
|
+
<!-- Written by final review when it blocks and consumed by patch planning. -->
|
|
4
|
+
|
|
5
|
+
Reviewed HEAD: <full SHA>
|
|
6
|
+
State: ship/blocked
|
|
7
|
+
|
|
8
|
+
## Findings
|
|
9
|
+
|
|
10
|
+
<!-- Copy only evidenced, selected findings. Keep IDs contiguous and ordered. -->
|
|
11
|
+
### P001 — <short finding title>
|
|
12
|
+
|
|
13
|
+
- **Source**: `.project/review/FINAL.md`
|
|
14
|
+
- **Locator**: `<SC number or gap risk row>`
|
|
15
|
+
- **Evidence**: <observed result copied from the source review>
|
|
16
|
+
- **Fix direction**: <concrete direction for the patch task>
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Review — wave <N>, cycle <C>
|
|
2
|
+
|
|
3
|
+
<!-- Written by the reviewer role in wave mode. File: wave-N.cycleC.md —
|
|
4
|
+
deep reviews use wave-N.cycleC.contract.md and
|
|
5
|
+
wave-N.cycleC.adversarial.md instead. Keep validated fields on their
|
|
6
|
+
own lines: Cycle holds only the number
|
|
7
|
+
(C > 1 means re-review after fixes; see previous cycle file). -->
|
|
8
|
+
|
|
9
|
+
<!-- For quick single-full-wave final scope, include these two metadata lines
|
|
10
|
+
with actual values, otherwise omit them:
|
|
11
|
+
Reviewed HEAD: <full review commit SHA>
|
|
12
|
+
Review scope: final
|
|
13
|
+
In each surface SC block also include Surface, Check, and Observed fields
|
|
14
|
+
using the final-review format. Record the actual walkthrough evidence once. -->
|
|
15
|
+
|
|
16
|
+
Wave verdict: <pass | blocked — blocked if any task fails>
|
|
17
|
+
Cycle: <C>
|
|
18
|
+
Depth: <full | deep | verify-only — verify-only is orchestrator-written from
|
|
19
|
+
its isolated Verify evidence; no independent reviewer ran>
|
|
20
|
+
Lens: <contract | adversarial — deep reviews only; empty or omitted
|
|
21
|
+
otherwise>
|
|
22
|
+
Tasks reviewed: <count>
|
|
23
|
+
|
|
24
|
+
## T001 — <title>: <pass | fail>
|
|
25
|
+
|
|
26
|
+
<!-- Per task criterion: copy the full acceptance/interface criterion verbatim
|
|
27
|
+
before the em dash; join wrapped lines with spaces. An AC id or summary
|
|
28
|
+
alone cannot be matched by review_findings.py. -->
|
|
29
|
+
- ✅ <criterion> — <evidence: verify output / file:line checked>
|
|
30
|
+
- ❌ <criterion> — found: <what exists instead, file:line>
|
|
31
|
+
fix: <concrete direction a coder can execute without re-investigating>
|
|
32
|
+
|
|
33
|
+
Warnings (non-blocking):
|
|
34
|
+
- <weakened test / unfailable verify / none>
|
|
35
|
+
|
|
36
|
+
Contract violations (blocking):
|
|
37
|
+
- ❌ <task changed path outside its files list> — <evidence and fix direction>
|
|
38
|
+
|
|
39
|
+
<!-- Repeat per task. -->
|
|
40
|
+
|
|
41
|
+
## Intent coverage
|
|
42
|
+
|
|
43
|
+
<!-- One heading per INTENT.md success criterion owned by a task in this
|
|
44
|
+
wave, ending `: pass` or `: fail`. `scripts/check_handoffs.py wave
|
|
45
|
+
--review <this file>` gates the SC id set and verdicts. Omit this
|
|
46
|
+
section when no task in the wave owns an SC. -->
|
|
47
|
+
|
|
48
|
+
### SC1 — <criterion>: <pass | fail>
|
|
49
|
+
- ✅ <evidence: verify output / file:line checked>
|
|
50
|
+
- ❌ <criterion> — found: <what exists instead, file:line>
|
|
51
|
+
fix: <concrete direction a coder can execute without re-investigating>
|
|
52
|
+
|
|
53
|
+
## Fixed since last cycle
|
|
54
|
+
|
|
55
|
+
<!-- C > 1 only: previously failed criteria now confirmed fixed (re-checked,
|
|
56
|
+
not assumed), and any regressions the fixes introduced. -->
|
|
57
|
+
- <criterion> — confirmed fixed / regressed: <evidence>
|
|
58
|
+
|
|
59
|
+
## Summary for orchestrator
|
|
60
|
+
|
|
61
|
+
- blocked findings: <failed criteria grouped by disjoint file scope; the
|
|
62
|
+
orchestrator applies the configured skeptic or fix-task path>
|
|
63
|
+
- repeat offenders: <criteria failing across cycles — possible plan defect>
|
|
64
|
+
- warnings worth a human eye: <...or none>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gsd-path-undo
|
|
3
|
+
description: Use only when the user explicitly invokes $gsd-path-undo. Preview and apply helper-owned undo of an unpublished checkpoint, last task landing, uncommitted ship archive, or untracked lookahead track. Never invent git reset, revert, or force-push.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GSD Path Undo
|
|
7
|
+
|
|
8
|
+
One invocation = one preview, then apply only after explicit user confirmation.
|
|
9
|
+
The bundled [pipeline_undo.py](scripts/pipeline_undo.py) is the only undo
|
|
10
|
+
authority; do not reimplement git reset in model reasoning.
|
|
11
|
+
|
|
12
|
+
This skill never advances a phase gate, never force-pushes, and never rewrites
|
|
13
|
+
a commit that is an ancestor of `origin/main`.
|
|
14
|
+
|
|
15
|
+
## Process
|
|
16
|
+
|
|
17
|
+
1. **Preview.** Run `python3 <absolute-bundled-pipeline_undo.py> preview --repo
|
|
18
|
+
<absolute-root>`. Treat the JSON as the only classification. On helper
|
|
19
|
+
error, present **Outcome** with the error, **Review** linking STATE.md when
|
|
20
|
+
it exists, and **Next** naming `$gsd-path-forensics` when the pipeline looks
|
|
21
|
+
stuck.
|
|
22
|
+
2. **Blocked.** When `target.kind` is `null`, report every `target.blocked`
|
|
23
|
+
reason. **Next** is the retry named in those reasons, or `$gsd-path-forensics`.
|
|
24
|
+
Stop. Do not apply.
|
|
25
|
+
3. **Confirm.** Present **Outcome** as the `target.effects` list, **Review**
|
|
26
|
+
linking STATE.md, and **Next** asking whether to apply that exact kind
|
|
27
|
+
(recommended only when `apply` is non-null) or stop. Do not apply until the
|
|
28
|
+
user confirms.
|
|
29
|
+
4. **Apply.** After confirmation, run `python3 <absolute-bundled-pipeline_undo.py>
|
|
30
|
+
apply --repo <absolute-root> --kind <target.kind> --expected-head
|
|
31
|
+
<target.head>` using the preview values verbatim. A mismatch means HEAD
|
|
32
|
+
moved: rerun preview, do not invent a SHA.
|
|
33
|
+
5. **Report.** **Outcome** is `status: applied` plus the new STATE phase/status.
|
|
34
|
+
**Review** links STATE.md. **Next** names `$gsd-path status` to inspect
|
|
35
|
+
without advancing.
|
|
36
|
+
|
|
37
|
+
## Rules
|
|
38
|
+
|
|
39
|
+
- Never run `git reset`, `git revert`, `git push --force`, or `git checkout`
|
|
40
|
+
except through this helper.
|
|
41
|
+
- Never undo a ship commit, integration merge, abandon commit, or promotion
|
|
42
|
+
commit. Those are blocked findings, not apply targets.
|
|
43
|
+
- A published HEAD (reachable from `origin/<STATE.branch>`) is blocked.
|
|
44
|
+
- A validated pending undo journal is returned as the resumable preview target.
|
|
45
|
+
Other pending helper journals are blocked and must be resumed by their owner.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# gsd-path project runtime
|
|
3
|
+
"""Helpers shared by the gsd-path scripts.
|
|
4
|
+
|
|
5
|
+
Every consuming script binds the names it used before (for example
|
|
6
|
+
``run_git = _common.run_git``) so callers and tests that patch the name on the
|
|
7
|
+
consuming module keep working.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import subprocess
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
PIPELINE_MARKER = "gsd-path/v2"
|
|
20
|
+
BOUND_BRANCH_RE = re.compile(r"^gsd-path/M(\d{3,})$")
|
|
21
|
+
|
|
22
|
+
FIELD_PATTERN = re.compile(r"^(?P<key>[a-z_]+):\s*(?P<value>.*)$")
|
|
23
|
+
INLINE_LIST_PATTERN = re.compile(r"^\[(?P<body>.*)\]$")
|
|
24
|
+
LIST_ITEM_PATTERN = re.compile(r"^\s*-\s+(?P<value>.*)$")
|
|
25
|
+
VERIFY_LEDGER_PATH = ".project/build/verify-ledger.jsonl"
|
|
26
|
+
VERIFY_RESULTS = ("pass", "fail")
|
|
27
|
+
VERIFY_LEDGER_SCHEMA = "gsd-path/verify-ledger/v2"
|
|
28
|
+
VERIFY_BLOCK_PATTERN = re.compile(r"```bash[ \t]*\n(?P<block>.*?)```", re.DOTALL)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def section_body(text: str, heading: str) -> Optional[str]:
|
|
32
|
+
"""The text under `## <heading>` up to the next `## `, or None when absent."""
|
|
33
|
+
match = re.search(
|
|
34
|
+
rf"(?ms)^## {re.escape(heading)}\s*\n(?P<body>.*?)(?=^## |\Z)", text
|
|
35
|
+
)
|
|
36
|
+
return match.group("body") if match else None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def task_verify_command(task_text: str) -> str:
|
|
40
|
+
"""The Verify shell text, excluding the closing fence's separator newline."""
|
|
41
|
+
body = section_body(task_text, "Verify")
|
|
42
|
+
block = VERIFY_BLOCK_PATTERN.search(body) if body is not None else None
|
|
43
|
+
return block.group("block").removesuffix("\n") if block else ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def latest_verify_entry(entries: list, command: str, commit: str) -> Optional[dict]:
|
|
47
|
+
for entry in reversed(entries):
|
|
48
|
+
if (entry.get("schema") == VERIFY_LEDGER_SCHEMA
|
|
49
|
+
and entry["command"] == command and entry["commit"] == commit):
|
|
50
|
+
return entry
|
|
51
|
+
return None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def verify_ledger_entries(path: Path) -> list:
|
|
55
|
+
"""Parsed verify-ledger rows; raises ValueError on a malformed line."""
|
|
56
|
+
if not path.exists():
|
|
57
|
+
return []
|
|
58
|
+
return parse_verify_ledger(path.read_text(encoding="utf-8"))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def parse_verify_ledger(text: str) -> list:
|
|
62
|
+
entries = []
|
|
63
|
+
for number, line in enumerate(text.splitlines(), start=1):
|
|
64
|
+
if not line.strip():
|
|
65
|
+
continue
|
|
66
|
+
try:
|
|
67
|
+
entry = json.loads(line)
|
|
68
|
+
except json.JSONDecodeError as error:
|
|
69
|
+
raise ValueError(f"{VERIFY_LEDGER_PATH} line {number} is not JSON: {error}") from error
|
|
70
|
+
if (
|
|
71
|
+
not isinstance(entry, dict)
|
|
72
|
+
or not isinstance(entry.get("command"), str)
|
|
73
|
+
or not isinstance(entry.get("commit"), str)
|
|
74
|
+
or entry.get("result") not in VERIFY_RESULTS
|
|
75
|
+
or not isinstance(entry.get("recorded_at"), str)
|
|
76
|
+
):
|
|
77
|
+
raise ValueError(f"{VERIFY_LEDGER_PATH} line {number} has invalid fields")
|
|
78
|
+
entries.append(entry)
|
|
79
|
+
return entries
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def run_command(
|
|
83
|
+
*arguments: str, cwd: Optional[Path] = None
|
|
84
|
+
) -> subprocess.CompletedProcess[str]:
|
|
85
|
+
return subprocess.run(
|
|
86
|
+
arguments,
|
|
87
|
+
cwd=cwd,
|
|
88
|
+
text=True,
|
|
89
|
+
capture_output=True,
|
|
90
|
+
check=False,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def run_git(
|
|
95
|
+
repo: Path, *arguments: str, input: Optional[str] = None
|
|
96
|
+
) -> subprocess.CompletedProcess[str]:
|
|
97
|
+
return subprocess.run(
|
|
98
|
+
("git", "-C", str(repo), *arguments),
|
|
99
|
+
input=input,
|
|
100
|
+
text=True,
|
|
101
|
+
capture_output=True,
|
|
102
|
+
check=False,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def atomic_replace(path: Path, temporary_path: Path, content: str) -> None:
|
|
107
|
+
temporary_path.parent.mkdir(parents=True, exist_ok=True)
|
|
108
|
+
descriptor = None
|
|
109
|
+
try:
|
|
110
|
+
if temporary_path.exists() or temporary_path.is_symlink():
|
|
111
|
+
temporary_path.unlink()
|
|
112
|
+
descriptor = os.open(
|
|
113
|
+
temporary_path,
|
|
114
|
+
os.O_WRONLY | os.O_CREAT | os.O_EXCL,
|
|
115
|
+
0o666,
|
|
116
|
+
)
|
|
117
|
+
handle = os.fdopen(descriptor, "w", encoding="utf-8")
|
|
118
|
+
descriptor = None
|
|
119
|
+
with handle:
|
|
120
|
+
handle.write(content)
|
|
121
|
+
os.replace(temporary_path, path)
|
|
122
|
+
finally:
|
|
123
|
+
if descriptor is not None:
|
|
124
|
+
os.close(descriptor)
|
|
125
|
+
if temporary_path.exists() or temporary_path.is_symlink():
|
|
126
|
+
temporary_path.unlink()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def atomic_write(path: Path, content: str) -> None:
|
|
130
|
+
atomic_replace(path, path.parent / f".{path.name}.gsd-path-tmp", content)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def strip_yaml_comment(value: str) -> str:
|
|
134
|
+
quote = None
|
|
135
|
+
previous_significant = None
|
|
136
|
+
inline_list = value.lstrip().startswith("[")
|
|
137
|
+
index = 0
|
|
138
|
+
while index < len(value):
|
|
139
|
+
character = value[index]
|
|
140
|
+
if quote == '"':
|
|
141
|
+
if character == "\\" and index + 1 < len(value):
|
|
142
|
+
index += 2
|
|
143
|
+
continue
|
|
144
|
+
if character == quote:
|
|
145
|
+
quote = None
|
|
146
|
+
elif quote == "'":
|
|
147
|
+
if (
|
|
148
|
+
character == quote
|
|
149
|
+
and index + 1 < len(value)
|
|
150
|
+
and value[index + 1] == quote
|
|
151
|
+
):
|
|
152
|
+
index += 2
|
|
153
|
+
continue
|
|
154
|
+
if character == quote:
|
|
155
|
+
quote = None
|
|
156
|
+
else:
|
|
157
|
+
if character in {"'", '"'} and (
|
|
158
|
+
previous_significant is None
|
|
159
|
+
or (inline_list and previous_significant in {"[", ","})
|
|
160
|
+
):
|
|
161
|
+
quote = character
|
|
162
|
+
elif character == "#" and (
|
|
163
|
+
index == 0 or value[index - 1].isspace()
|
|
164
|
+
):
|
|
165
|
+
return value[:index].rstrip()
|
|
166
|
+
if quote is None and not character.isspace():
|
|
167
|
+
previous_significant = character
|
|
168
|
+
index += 1
|
|
169
|
+
return value.strip()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def unquote(value: str) -> str:
|
|
173
|
+
cleaned = strip_yaml_comment(value).strip()
|
|
174
|
+
if (
|
|
175
|
+
len(cleaned) >= 2
|
|
176
|
+
and cleaned[0] == cleaned[-1]
|
|
177
|
+
and cleaned[0] in {"'", '"'}
|
|
178
|
+
):
|
|
179
|
+
return cleaned[1:-1]
|
|
180
|
+
return cleaned
|