@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,463 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Gate a GSD Path docs audit (.project/research/DOCS-AUDIT.md) against its contract.
|
|
3
|
+
|
|
4
|
+
Checks the rules the docs-audit contract states in prose: the header lines,
|
|
5
|
+
a Summary table whose counts match the body, one `## Doc:` section per doc
|
|
6
|
+
with testable claims where every claim row carries a valid type, a valid
|
|
7
|
+
verdict, and real evidence, a `## Descriptive docs` list for claimless docs,
|
|
8
|
+
the two sets disjoint and together equal to the frozen inventory, and a
|
|
9
|
+
remediation queue with one classified row per non-verified claim.
|
|
10
|
+
`## User rulings` is fixed-format durable memory. Pass the pre-rewrite audit
|
|
11
|
+
with `--prior-audit FILE`; every prior row, including its Planned value, must
|
|
12
|
+
remain an exact ordered prefix.
|
|
13
|
+
|
|
14
|
+
A re-audit may carry a prior `verified` row forward verbatim with its Evidence
|
|
15
|
+
prefixed `unchanged: `. Pass the paths that differ from the prior audit's
|
|
16
|
+
`Audited HEAD` with `--changed FILE` (one path per line); a carried row must
|
|
17
|
+
repeat a prior verified non-command row whose doc and evidence paths are all
|
|
18
|
+
outside that set. Without both `--prior-audit` and `--changed`, carried rows
|
|
19
|
+
are rejected.
|
|
20
|
+
|
|
21
|
+
The frozen inventory travels in the dispatch brief; pass it with
|
|
22
|
+
`--inventory FILE` (one POSIX path per line, `-` for stdin). Without it the
|
|
23
|
+
gate derives the inventory from tracked Markdown files, excluding `.git`,
|
|
24
|
+
`node_modules`, `.project/archive/**`, installed `*/skills/gsd-path*` bundles,
|
|
25
|
+
the audit itself, and — unless the
|
|
26
|
+
audit declares `Alignment mode: yes` — everything else under `.project/`.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
import argparse
|
|
30
|
+
import json
|
|
31
|
+
import re
|
|
32
|
+
import subprocess
|
|
33
|
+
import sys
|
|
34
|
+
from datetime import date
|
|
35
|
+
from pathlib import Path
|
|
36
|
+
from typing import Dict, List, Optional, Sequence
|
|
37
|
+
|
|
38
|
+
DEFAULT_AUDIT = ".project/research/DOCS-AUDIT.md"
|
|
39
|
+
TYPES = ("command", "feature", "structure", "status", "config", "integration")
|
|
40
|
+
VERDICTS = ("verified", "stale", "aspirational", "unverifiable")
|
|
41
|
+
CLASSES = ("fix-doc", "fix-code", "NEEDS-USER")
|
|
42
|
+
RULINGS = ("fix-code", "fix-doc", "accept-drift")
|
|
43
|
+
SUMMARY_ROWS = VERDICTS + ("descriptive docs (no testable claims)",)
|
|
44
|
+
|
|
45
|
+
HEADER_PATTERN = re.compile(r"^(Repo root|Audited|Audited HEAD|Alignment mode): (.+)$", re.M)
|
|
46
|
+
HEAD_PATTERN = re.compile(r"[0-9a-f]{40}|none")
|
|
47
|
+
CARRIED = "unchanged: " # Evidence prefix of a row carried forward from the prior audit
|
|
48
|
+
SECTION_PATTERN = re.compile(r"^## (.+?)\s*$", re.M)
|
|
49
|
+
PLACEHOLDER_PATTERN = re.compile(r"^<[^>]*>$") # a cell left as the template placeholder
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class AuditError(RuntimeError):
|
|
53
|
+
"""Raised when the audit is absent or violates its contract."""
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _cells(line: str) -> Optional[List[str]]:
|
|
57
|
+
if not line.startswith("|"):
|
|
58
|
+
return None
|
|
59
|
+
cells = [cell.replace("\\|", "|").strip() for cell in re.split(r"(?<!\\)\|", line.strip().strip("|"))]
|
|
60
|
+
if all(re.fullmatch(r":?-+:?", cell) for cell in cells):
|
|
61
|
+
return None # separator row
|
|
62
|
+
return cells
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _sections(text: str) -> Dict[str, str]:
|
|
66
|
+
sections: Dict[str, str] = {}
|
|
67
|
+
matches = list(SECTION_PATTERN.finditer(text))
|
|
68
|
+
for index, match in enumerate(matches):
|
|
69
|
+
end = matches[index + 1].start() if index + 1 < len(matches) else len(text)
|
|
70
|
+
title = match.group(1)
|
|
71
|
+
if title in sections:
|
|
72
|
+
raise AuditError(f"duplicate section: ## {title}")
|
|
73
|
+
sections[title] = text[match.end() : end]
|
|
74
|
+
return sections
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _table_rows(body: str, width: int, label: str) -> List[List[str]]:
|
|
78
|
+
rows = []
|
|
79
|
+
for line in body.splitlines():
|
|
80
|
+
cells = _cells(line)
|
|
81
|
+
if cells is None:
|
|
82
|
+
continue
|
|
83
|
+
if len(cells) != width:
|
|
84
|
+
raise AuditError(f"{label}: expected {width} columns, found {len(cells)}: {line.strip()}")
|
|
85
|
+
rows.append(cells)
|
|
86
|
+
if not rows:
|
|
87
|
+
raise AuditError(f"{label}: missing table")
|
|
88
|
+
header, *rows = rows
|
|
89
|
+
return rows
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _not_placeholder(value: str, label: str) -> None:
|
|
93
|
+
normalized = value.strip().strip("`\"").strip()
|
|
94
|
+
if (
|
|
95
|
+
not normalized
|
|
96
|
+
or normalized.casefold() == "none"
|
|
97
|
+
or PLACEHOLDER_PATTERN.match(normalized)
|
|
98
|
+
):
|
|
99
|
+
raise AuditError(f"{label}: placeholder or empty value: {value!r}")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _installed_skill(parts: Sequence[str]) -> bool:
|
|
103
|
+
"""True for files inside an installed GSD Path skill bundle (<host>/skills/gsd-path*/…)."""
|
|
104
|
+
return any(
|
|
105
|
+
parts[index] == "skills" and parts[index + 1].startswith("gsd-path")
|
|
106
|
+
for index in range(len(parts) - 2)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def derive_inventory(repo: Path, audit_relative: str, alignment: bool) -> List[str]:
|
|
111
|
+
listed = subprocess.run(
|
|
112
|
+
[
|
|
113
|
+
"git",
|
|
114
|
+
"ls-files",
|
|
115
|
+
"-z",
|
|
116
|
+
"--cached",
|
|
117
|
+
"--others",
|
|
118
|
+
"--exclude-standard",
|
|
119
|
+
"--",
|
|
120
|
+
"*.md",
|
|
121
|
+
"**/*.md",
|
|
122
|
+
],
|
|
123
|
+
cwd=repo,
|
|
124
|
+
capture_output=True,
|
|
125
|
+
check=False,
|
|
126
|
+
)
|
|
127
|
+
if listed.returncode != 0:
|
|
128
|
+
raise AuditError("cannot derive inventory: git ls-files failed; pass --inventory")
|
|
129
|
+
paths = set()
|
|
130
|
+
for raw in listed.stdout.decode("utf-8").split("\0"):
|
|
131
|
+
if not raw or raw == audit_relative:
|
|
132
|
+
continue
|
|
133
|
+
parts = raw.split("/")
|
|
134
|
+
if parts[0] in (".git", "node_modules") or "node_modules" in parts:
|
|
135
|
+
continue
|
|
136
|
+
if _installed_skill(parts):
|
|
137
|
+
continue
|
|
138
|
+
if raw.startswith(".project/archive/"):
|
|
139
|
+
continue
|
|
140
|
+
if raw.startswith(".project/") and not alignment:
|
|
141
|
+
continue
|
|
142
|
+
paths.add(raw)
|
|
143
|
+
return sorted(paths)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _user_rulings(body: str, label: str) -> List[List[str]]:
|
|
147
|
+
rows = _table_rows(body, 4, label)
|
|
148
|
+
seen = set()
|
|
149
|
+
for number, ruling, words, planned in rows:
|
|
150
|
+
if not number.isdigit() or int(number) < 1:
|
|
151
|
+
raise AuditError(f"{label}: Queue # must be a positive integer: {number}")
|
|
152
|
+
if number in seen:
|
|
153
|
+
raise AuditError(f"{label}: repeated Queue #: {number}")
|
|
154
|
+
seen.add(number)
|
|
155
|
+
if ruling not in RULINGS:
|
|
156
|
+
raise AuditError(f"{label}: invalid ruling: {ruling}")
|
|
157
|
+
_not_placeholder(words, f"{label} #{number} User's words")
|
|
158
|
+
if ruling == "accept-drift":
|
|
159
|
+
if planned != "n/a (accept-drift)":
|
|
160
|
+
raise AuditError(
|
|
161
|
+
f"{label} #{number}: accept-drift must be planned n/a (accept-drift)"
|
|
162
|
+
)
|
|
163
|
+
elif planned != "no" and not re.fullmatch(r"T\d{3}", planned):
|
|
164
|
+
raise AuditError(
|
|
165
|
+
f"{label} #{number}: Planned must be no or a T### task id"
|
|
166
|
+
)
|
|
167
|
+
return rows
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _doc_claims(sections: Dict[str, str]) -> Dict[str, List[Dict[str, str]]]:
|
|
171
|
+
docs: Dict[str, List[Dict[str, str]]] = {}
|
|
172
|
+
for title, body in sections.items():
|
|
173
|
+
if not title.startswith("Doc: "):
|
|
174
|
+
continue
|
|
175
|
+
path = title[len("Doc: ") :].strip().strip("`")
|
|
176
|
+
_not_placeholder(path, "Doc section path")
|
|
177
|
+
if path in docs:
|
|
178
|
+
raise AuditError(f"duplicate normalized Doc section: {path}")
|
|
179
|
+
claims = []
|
|
180
|
+
claim_rows = _table_rows(body, 4, f"Doc: {path}")
|
|
181
|
+
if not claim_rows:
|
|
182
|
+
raise AuditError(f"Doc: {path}: requires at least one claim row")
|
|
183
|
+
for claim, kind, verdict, evidence in claim_rows:
|
|
184
|
+
if kind not in TYPES:
|
|
185
|
+
raise AuditError(f"Doc: {path}: invalid claim type: {kind}")
|
|
186
|
+
if verdict not in VERDICTS:
|
|
187
|
+
raise AuditError(f"Doc: {path}: invalid verdict: {verdict}")
|
|
188
|
+
_not_placeholder(claim, f"Doc: {path}: claim")
|
|
189
|
+
_not_placeholder(evidence, f"Doc: {path}: evidence for {claim}")
|
|
190
|
+
claims.append({"claim": claim, "type": kind, "verdict": verdict, "evidence": evidence})
|
|
191
|
+
docs[path] = claims
|
|
192
|
+
return docs
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _evidence_names_path(evidence: str, path: str) -> bool:
|
|
196
|
+
parts = path.split("/")
|
|
197
|
+
return path in evidence or any(
|
|
198
|
+
"/".join(parts[:end]) + "/" in evidence
|
|
199
|
+
for end in range(1, len(parts))
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _check_carried(
|
|
204
|
+
docs: Dict[str, List[Dict[str, str]]],
|
|
205
|
+
prior_sections: Optional[Dict[str, str]],
|
|
206
|
+
changed: Optional[Sequence[str]],
|
|
207
|
+
) -> None:
|
|
208
|
+
carried = [
|
|
209
|
+
(path, claim)
|
|
210
|
+
for path, claims in docs.items()
|
|
211
|
+
for claim in claims
|
|
212
|
+
if claim["evidence"].startswith(CARRIED)
|
|
213
|
+
]
|
|
214
|
+
if not carried:
|
|
215
|
+
return
|
|
216
|
+
if changed is None:
|
|
217
|
+
raise AuditError("carried (unchanged:) rows require --prior-audit and --changed")
|
|
218
|
+
prior = {
|
|
219
|
+
(path, claim["claim"], claim["type"]): claim["evidence"].removeprefix(CARRIED)
|
|
220
|
+
for path, claims in _doc_claims(prior_sections or {}).items()
|
|
221
|
+
for claim in claims
|
|
222
|
+
if claim["verdict"] == "verified"
|
|
223
|
+
}
|
|
224
|
+
for path, claim in carried:
|
|
225
|
+
label = f"Doc: {path}: carried row {claim['claim']}"
|
|
226
|
+
evidence = claim["evidence"].removeprefix(CARRIED)
|
|
227
|
+
if claim["verdict"] != "verified" or claim["type"] == "command":
|
|
228
|
+
raise AuditError(f"{label}: only verified non-command claims carry forward")
|
|
229
|
+
if prior.get((path, claim["claim"], claim["type"])) != evidence:
|
|
230
|
+
raise AuditError(f"{label}: does not repeat a prior verified row")
|
|
231
|
+
# Evidence is free text, so a substring match errs toward re-verifying.
|
|
232
|
+
moved = sorted(
|
|
233
|
+
changed_path
|
|
234
|
+
for changed_path in changed
|
|
235
|
+
if changed_path == path
|
|
236
|
+
or _evidence_names_path(evidence, changed_path)
|
|
237
|
+
)
|
|
238
|
+
if moved:
|
|
239
|
+
raise AuditError(f"{label}: doc or evidence changed since the prior audit: {', '.join(moved)}")
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def validate(
|
|
243
|
+
repo: Path,
|
|
244
|
+
audit_relative: str,
|
|
245
|
+
inventory: Optional[Sequence[str]],
|
|
246
|
+
prior_text: Optional[str] = None,
|
|
247
|
+
changed: Optional[Sequence[str]] = None,
|
|
248
|
+
) -> Dict[str, object]:
|
|
249
|
+
audit = repo / audit_relative
|
|
250
|
+
if not audit.is_file() or audit.is_symlink():
|
|
251
|
+
raise AuditError(f"missing real audit file: {audit_relative}")
|
|
252
|
+
text = audit.read_text(encoding="utf-8")
|
|
253
|
+
|
|
254
|
+
header_rows = HEADER_PATTERN.findall(text)
|
|
255
|
+
header = {}
|
|
256
|
+
for key in ("Repo root", "Audited", "Audited HEAD", "Alignment mode"):
|
|
257
|
+
values = [value for found_key, value in header_rows if found_key == key]
|
|
258
|
+
if len(values) != 1:
|
|
259
|
+
raise AuditError(f"header must contain exactly one {key} line")
|
|
260
|
+
header[key] = values[0]
|
|
261
|
+
declared_root = Path(header["Repo root"])
|
|
262
|
+
if not declared_root.is_absolute() or declared_root.resolve() != repo.resolve():
|
|
263
|
+
raise AuditError("Repo root does not match --repo")
|
|
264
|
+
try:
|
|
265
|
+
date.fromisoformat(header["Audited"])
|
|
266
|
+
except ValueError as error:
|
|
267
|
+
raise AuditError("Audited must be an ISO date") from error
|
|
268
|
+
if not HEAD_PATTERN.fullmatch(header["Audited HEAD"]):
|
|
269
|
+
raise AuditError("Audited HEAD must be a 40-hex commit or none")
|
|
270
|
+
alignment_value = header["Alignment mode"].split("—")[0].strip().lower()
|
|
271
|
+
if alignment_value not in ("yes", "no"):
|
|
272
|
+
raise AuditError(f"Alignment mode must be yes or no, found: {header['Alignment mode']}")
|
|
273
|
+
alignment = alignment_value == "yes"
|
|
274
|
+
|
|
275
|
+
sections = _sections(text)
|
|
276
|
+
for required in ("Summary", "Descriptive docs", "User rulings", "Remediation queue"):
|
|
277
|
+
if required not in sections:
|
|
278
|
+
raise AuditError(f"missing section: ## {required}")
|
|
279
|
+
|
|
280
|
+
summary = {}
|
|
281
|
+
for verdict, count in _table_rows(sections["Summary"], 2, "Summary"):
|
|
282
|
+
if verdict not in SUMMARY_ROWS:
|
|
283
|
+
raise AuditError(f"Summary: unknown row: {verdict}")
|
|
284
|
+
if not count.isdigit():
|
|
285
|
+
raise AuditError(f"Summary: count for {verdict} is not an integer: {count!r}")
|
|
286
|
+
summary[verdict] = int(count)
|
|
287
|
+
missing = [row for row in SUMMARY_ROWS if row not in summary]
|
|
288
|
+
if missing:
|
|
289
|
+
raise AuditError(f"Summary: missing rows: {', '.join(missing)}")
|
|
290
|
+
|
|
291
|
+
docs = _doc_claims(sections)
|
|
292
|
+
tallies = {verdict: 0 for verdict in VERDICTS}
|
|
293
|
+
for claims in docs.values():
|
|
294
|
+
for claim in claims:
|
|
295
|
+
tallies[claim["verdict"]] += 1
|
|
296
|
+
|
|
297
|
+
descriptive = []
|
|
298
|
+
for line in sections["Descriptive docs"].splitlines():
|
|
299
|
+
line = line.strip()
|
|
300
|
+
if line.startswith("- "):
|
|
301
|
+
path = line[2:].strip().strip("`")
|
|
302
|
+
_not_placeholder(path, "Descriptive docs entry")
|
|
303
|
+
if path in descriptive:
|
|
304
|
+
raise AuditError(f"Descriptive docs: listed twice: {path}")
|
|
305
|
+
descriptive.append(path)
|
|
306
|
+
|
|
307
|
+
overlap = sorted(set(docs) & set(descriptive))
|
|
308
|
+
if overlap:
|
|
309
|
+
raise AuditError(f"docs listed both as claim sections and descriptive: {', '.join(overlap)}")
|
|
310
|
+
|
|
311
|
+
expected = sorted(inventory) if inventory is not None else derive_inventory(repo, audit_relative, alignment)
|
|
312
|
+
covered = sorted(set(docs) | set(descriptive))
|
|
313
|
+
if covered != expected:
|
|
314
|
+
unaudited = sorted(set(expected) - set(covered))
|
|
315
|
+
extra = sorted(set(covered) - set(expected))
|
|
316
|
+
detail = []
|
|
317
|
+
if unaudited:
|
|
318
|
+
detail.append(f"not audited: {', '.join(unaudited)}")
|
|
319
|
+
if extra:
|
|
320
|
+
detail.append(f"outside inventory: {', '.join(extra)}")
|
|
321
|
+
raise AuditError("audit coverage does not equal the frozen inventory; " + "; ".join(detail))
|
|
322
|
+
|
|
323
|
+
for verdict in VERDICTS:
|
|
324
|
+
if summary[verdict] != tallies[verdict]:
|
|
325
|
+
raise AuditError(f"Summary: {verdict} count {summary[verdict]} does not match {tallies[verdict]} claim rows")
|
|
326
|
+
if summary["descriptive docs (no testable claims)"] != len(descriptive):
|
|
327
|
+
raise AuditError(
|
|
328
|
+
f"Summary: descriptive count {summary['descriptive docs (no testable claims)']} "
|
|
329
|
+
f"does not match {len(descriptive)} listed docs"
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
rulings = _user_rulings(sections["User rulings"], "User rulings")
|
|
333
|
+
prior_sections = _sections(prior_text) if prior_text is not None else None
|
|
334
|
+
if prior_sections is not None:
|
|
335
|
+
if "User rulings" not in prior_sections:
|
|
336
|
+
raise AuditError("prior audit is missing section: ## User rulings")
|
|
337
|
+
prior_rulings = _user_rulings(
|
|
338
|
+
prior_sections["User rulings"], "Prior user rulings"
|
|
339
|
+
)
|
|
340
|
+
if rulings[: len(prior_rulings)] != prior_rulings:
|
|
341
|
+
raise AuditError(
|
|
342
|
+
"User rulings must preserve every prior row and Planned value in order"
|
|
343
|
+
)
|
|
344
|
+
_check_carried(docs, prior_sections, changed)
|
|
345
|
+
|
|
346
|
+
queue_body = sections["Remediation queue"]
|
|
347
|
+
non_verified = sum(tallies[verdict] for verdict in VERDICTS if verdict != "verified")
|
|
348
|
+
queue = _table_rows(queue_body, 6, "Remediation queue") if non_verified or "|" in queue_body else []
|
|
349
|
+
expected_queue = sorted(
|
|
350
|
+
(doc, claim["claim"], claim["verdict"])
|
|
351
|
+
for doc, claims in docs.items()
|
|
352
|
+
for claim in claims
|
|
353
|
+
if claim["verdict"] != "verified"
|
|
354
|
+
)
|
|
355
|
+
for number, doc, claim, verdict, klass, action in queue:
|
|
356
|
+
if verdict not in VERDICTS or verdict == "verified":
|
|
357
|
+
raise AuditError(f"Remediation queue #{number}: verdict must be non-verified, found: {verdict}")
|
|
358
|
+
if klass not in CLASSES:
|
|
359
|
+
raise AuditError(f"Remediation queue #{number}: invalid class: {klass}")
|
|
360
|
+
if doc not in docs:
|
|
361
|
+
raise AuditError(f"Remediation queue #{number}: unknown doc: {doc}")
|
|
362
|
+
_not_placeholder(action, f"Remediation queue #{number}: action")
|
|
363
|
+
if len(queue) != non_verified:
|
|
364
|
+
raise AuditError(f"Remediation queue has {len(queue)} rows for {non_verified} non-verified claims")
|
|
365
|
+
numbers = [row[0] for row in queue]
|
|
366
|
+
if numbers != [str(number) for number in range(1, len(queue) + 1)]:
|
|
367
|
+
raise AuditError("Remediation queue numbers must be contiguous and ordered from 1")
|
|
368
|
+
actual_queue = sorted((doc, claim, verdict) for _, doc, claim, verdict, _, _ in queue)
|
|
369
|
+
if actual_queue != expected_queue:
|
|
370
|
+
raise AuditError(
|
|
371
|
+
"Remediation queue must bind each non-verified doc, claim, and verdict exactly once"
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
return {
|
|
375
|
+
"audit": audit_relative,
|
|
376
|
+
"alignment": alignment,
|
|
377
|
+
"docs": sorted(docs),
|
|
378
|
+
"descriptive": descriptive,
|
|
379
|
+
"claims": sum(len(claims) for claims in docs.values()),
|
|
380
|
+
"verdicts": tallies,
|
|
381
|
+
"queue": len(queue),
|
|
382
|
+
"rulings": len(rulings),
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def parser() -> argparse.ArgumentParser:
|
|
387
|
+
argument_parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
388
|
+
argument_parser.add_argument("--repo", type=Path, required=True)
|
|
389
|
+
argument_parser.add_argument("--audit", default=DEFAULT_AUDIT, help="relative audit path (default: %(default)s)")
|
|
390
|
+
argument_parser.add_argument("--inventory", help="file with one inventoried POSIX path per line, or - for stdin")
|
|
391
|
+
argument_parser.add_argument(
|
|
392
|
+
"--prior-audit",
|
|
393
|
+
type=Path,
|
|
394
|
+
help="pre-rewrite audit whose User rulings must carry forward",
|
|
395
|
+
)
|
|
396
|
+
argument_parser.add_argument(
|
|
397
|
+
"--changed",
|
|
398
|
+
help="file with one path changed since the prior audit's Audited HEAD per line; permits unchanged: rows",
|
|
399
|
+
)
|
|
400
|
+
argument_parser.add_argument(
|
|
401
|
+
"--emit-inventory",
|
|
402
|
+
action="store_true",
|
|
403
|
+
help="print the frozen tracked and untracked Markdown inventory, one path per line",
|
|
404
|
+
)
|
|
405
|
+
argument_parser.add_argument(
|
|
406
|
+
"--alignment",
|
|
407
|
+
action="store_true",
|
|
408
|
+
help="include active .project Markdown when emitting an alignment inventory",
|
|
409
|
+
)
|
|
410
|
+
return argument_parser
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def _path_lines(name: str) -> List[str]:
|
|
414
|
+
source = sys.stdin if name == "-" else open(name, encoding="utf-8")
|
|
415
|
+
with source:
|
|
416
|
+
return [line.strip() for line in source if line.strip()]
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
420
|
+
arguments = parser().parse_args(argv)
|
|
421
|
+
if arguments.changed is not None and arguments.prior_audit is None:
|
|
422
|
+
print("docs audit validation failed: --changed requires --prior-audit", file=sys.stderr)
|
|
423
|
+
return 1
|
|
424
|
+
if arguments.emit_inventory:
|
|
425
|
+
if arguments.inventory is not None or arguments.prior_audit is not None or arguments.changed is not None:
|
|
426
|
+
print(
|
|
427
|
+
"docs audit validation failed: --emit-inventory cannot use validation inputs",
|
|
428
|
+
file=sys.stderr,
|
|
429
|
+
)
|
|
430
|
+
return 1
|
|
431
|
+
try:
|
|
432
|
+
inventory = derive_inventory(
|
|
433
|
+
arguments.repo.resolve(), arguments.audit, arguments.alignment
|
|
434
|
+
)
|
|
435
|
+
except AuditError as error:
|
|
436
|
+
print(f"docs audit validation failed: {error}", file=sys.stderr)
|
|
437
|
+
return 1
|
|
438
|
+
print("\n".join(inventory))
|
|
439
|
+
return 0
|
|
440
|
+
inventory = _path_lines(arguments.inventory) if arguments.inventory is not None else None
|
|
441
|
+
prior_text = None
|
|
442
|
+
if arguments.prior_audit is not None:
|
|
443
|
+
if arguments.prior_audit.is_symlink() or not arguments.prior_audit.is_file():
|
|
444
|
+
print(
|
|
445
|
+
"docs audit validation failed: --prior-audit must be a real file",
|
|
446
|
+
file=sys.stderr,
|
|
447
|
+
)
|
|
448
|
+
return 1
|
|
449
|
+
prior_text = arguments.prior_audit.read_text(encoding="utf-8")
|
|
450
|
+
changed = _path_lines(arguments.changed) if arguments.changed is not None else None
|
|
451
|
+
try:
|
|
452
|
+
result = validate(
|
|
453
|
+
arguments.repo.resolve(), arguments.audit, inventory, prior_text, changed
|
|
454
|
+
)
|
|
455
|
+
except AuditError as error:
|
|
456
|
+
print(f"docs audit validation failed: {error}", file=sys.stderr)
|
|
457
|
+
return 1
|
|
458
|
+
print(json.dumps(result, sort_keys=True))
|
|
459
|
+
return 0
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
if __name__ == "__main__":
|
|
463
|
+
raise SystemExit(main())
|