@opengsd/gsd-path 1.3.1 → 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/AGENTS.md +2 -207
- package/CHANGELOG.md +331 -69
- package/DOCS.md +25 -9
- package/FULL.md +12 -5
- package/GUIDE.md +1 -1
- package/HOOKS.md +104 -14
- package/QUICK.md +5 -2
- package/README.md +131 -111
- package/RUNTIME.md +2 -2
- package/UPDATE.md +61 -11
- package/WORKFLOW.md +44 -7
- package/package.json +13 -9
- package/platforms/shared-agents/dispatch.md +13 -0
- package/scripts/_common.py +619 -18
- package/scripts/archive_milestone.py +160 -86
- package/scripts/bootstrap_repository.py +8 -5
- package/scripts/build_recovery.py +100 -32
- package/scripts/build_state.py +143 -24
- package/scripts/check_docs_audit.py +91 -7
- package/scripts/check_handoffs.py +662 -50
- package/scripts/check_task_briefs.py +338 -30
- package/scripts/check_update.py +2 -2
- package/scripts/core_hook_gate.py +10 -1
- package/scripts/core_hook_settings.py +11 -4
- package/scripts/detect_project.py +587 -32
- package/scripts/discussion_records.py +19 -13
- package/scripts/discussion_validate.py +152 -50
- package/scripts/dispatch_driver.py +501 -46
- package/scripts/git_guard.py +118 -31
- package/scripts/guard_hook.py +1567 -194
- package/scripts/install.mjs +77 -46
- package/scripts/install.py +611 -50
- package/scripts/integration.py +580 -25
- package/scripts/isolation.py +1438 -57
- package/scripts/lean_verification.py +99 -19
- package/scripts/loop_run.py +31 -29
- package/scripts/members.py +706 -0
- package/scripts/migrate_core.py +2 -2
- package/scripts/path_config.py +5 -5
- package/scripts/pipeline_diagnose.py +82 -31
- package/scripts/pipeline_git.py +124 -34
- package/scripts/pipeline_state.py +287 -87
- package/scripts/pipeline_undo.py +218 -30
- package/scripts/promote_lookahead.py +1 -1
- package/scripts/review_panel.py +1 -1
- package/scripts/runtime_store.py +55 -11
- package/scripts/skill-resources.json +37 -5
- package/scripts/state_checkpoint.py +135 -44
- package/scripts/state_promote.py +2 -0
- package/scripts/status_runtime.py +58 -10
- package/scripts/sync_skill_resources.py +8 -7
- package/scripts/task_context.py +12 -3
- package/scripts/token_budget.py +12 -8
- package/scripts/wizard.mjs +1 -0
- package/scripts/workflow_run.py +107 -30
- package/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path/BUILD.md +7 -1
- package/skills/gsd-path/DECIDE.md +11 -4
- package/skills/gsd-path/DEFINE.md +36 -4
- package/skills/gsd-path/DOCS-AUDIT.md +6 -4
- package/skills/gsd-path/INSPECT.md +5 -4
- package/skills/gsd-path/PLAN.md +18 -4
- package/skills/gsd-path/RESEARCH.md +13 -1
- package/skills/gsd-path/ROADMAP.md +2 -1
- package/skills/gsd-path/SHIP.md +33 -11
- package/skills/gsd-path/SKILL.md +45 -1
- package/skills/gsd-path/references/build-native.md +63 -15
- package/skills/gsd-path/references/codebase-mapper.md +2 -1
- package/skills/gsd-path/references/dispatch.md +13 -0
- package/skills/gsd-path/references/docs-auditor.md +7 -1
- package/skills/gsd-path/references/operating-rules.md +214 -0
- package/skills/gsd-path/references/planner.md +22 -4
- package/skills/gsd-path/references/reviewer-final.md +20 -2
- package/skills/gsd-path/references/reviewer-wave.md +18 -5
- 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 +619 -18
- package/skills/gsd-path/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path/scripts/bootstrap_repository.py +8 -5
- package/skills/gsd-path/scripts/build_recovery.py +100 -32
- package/skills/gsd-path/scripts/build_state.py +143 -24
- package/skills/gsd-path/scripts/check_docs_audit.py +91 -7
- package/skills/gsd-path/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path/scripts/check_update.py +2 -2
- package/skills/gsd-path/scripts/detect_project.py +587 -32
- package/skills/gsd-path/scripts/discussion_records.py +19 -13
- package/skills/gsd-path/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path/scripts/dispatch_driver.py +501 -46
- package/skills/gsd-path/scripts/git_guard.py +118 -31
- package/skills/gsd-path/scripts/integration.py +580 -25
- package/skills/gsd-path/scripts/isolation.py +1438 -57
- package/skills/gsd-path/scripts/lean_verification.py +99 -19
- package/skills/gsd-path/scripts/loop_run.py +31 -29
- package/skills/gsd-path/scripts/members.py +706 -0
- package/skills/gsd-path/scripts/path_config.py +5 -5
- package/skills/gsd-path/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path/scripts/review_panel.py +1 -1
- package/skills/gsd-path/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path/scripts/state_promote.py +2 -0
- package/skills/gsd-path/scripts/status_runtime.py +58 -10
- package/skills/gsd-path/scripts/task_context.py +12 -3
- package/skills/gsd-path/scripts/token_budget.py +12 -8
- package/skills/gsd-path/scripts/workflow_run.py +107 -30
- package/skills/gsd-path/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path/templates/codebase.md +1 -1
- package/skills/gsd-path/templates/docs-audit.md +6 -2
- package/skills/gsd-path/templates/final-review.md +16 -4
- package/skills/gsd-path/templates/gap-review.md +2 -1
- 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/task.md +1 -0
- package/skills/gsd-path/templates/wave-review.md +16 -4
- package/skills/gsd-path-build/SKILL.md +7 -1
- package/skills/gsd-path-build/references/build-native.md +63 -15
- package/skills/gsd-path-build/references/dispatch.md +13 -0
- package/skills/gsd-path-build/references/operating-rules.md +214 -0
- package/skills/gsd-path-build/references/reviewer-final.md +20 -2
- package/skills/gsd-path-build/references/reviewer-wave.md +18 -5
- 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 +619 -18
- package/skills/gsd-path-build/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path-build/scripts/build_recovery.py +100 -32
- package/skills/gsd-path-build/scripts/build_state.py +143 -24
- package/skills/gsd-path-build/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path-build/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path-build/scripts/detect_project.py +587 -32
- package/skills/gsd-path-build/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-build/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path-build/scripts/dispatch_driver.py +501 -46
- package/skills/gsd-path-build/scripts/integration.py +580 -25
- package/skills/gsd-path-build/scripts/isolation.py +1438 -57
- package/skills/gsd-path-build/scripts/loop_run.py +31 -29
- package/skills/gsd-path-build/scripts/members.py +706 -0
- package/skills/gsd-path-build/scripts/path_config.py +5 -5
- package/skills/gsd-path-build/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path-build/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path-build/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-build/scripts/review_panel.py +1 -1
- package/skills/gsd-path-build/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path-build/scripts/state_promote.py +2 -0
- package/skills/gsd-path-build/scripts/task_context.py +12 -3
- package/skills/gsd-path-build/scripts/token_budget.py +12 -8
- package/skills/gsd-path-build/scripts/workflow_run.py +107 -30
- package/skills/gsd-path-build/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-build/templates/task.md +1 -0
- package/skills/gsd-path-build/templates/wave-review.md +16 -4
- package/skills/gsd-path-decide/SKILL.md +11 -4
- package/skills/gsd-path-decide/references/dispatch.md +13 -0
- package/skills/gsd-path-decide/references/operating-rules.md +214 -0
- package/skills/gsd-path-decide/scripts/_common.py +619 -18
- package/skills/gsd-path-decide/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path-decide/scripts/build_recovery.py +100 -32
- package/skills/gsd-path-decide/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path-decide/scripts/detect_project.py +587 -32
- package/skills/gsd-path-decide/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-decide/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path-decide/scripts/integration.py +580 -25
- package/skills/gsd-path-decide/scripts/isolation.py +1438 -57
- package/skills/gsd-path-decide/scripts/loop_run.py +31 -29
- package/skills/gsd-path-decide/scripts/members.py +706 -0
- package/skills/gsd-path-decide/scripts/path_config.py +5 -5
- package/skills/gsd-path-decide/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path-decide/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path-decide/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-decide/scripts/review_panel.py +1 -1
- package/skills/gsd-path-decide/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path-decide/scripts/state_promote.py +2 -0
- package/skills/gsd-path-decide/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-define/SKILL.md +36 -4
- package/skills/gsd-path-define/references/operating-rules.md +214 -0
- package/skills/gsd-path-define/references/spec-probes.md +150 -0
- package/skills/gsd-path-define/scripts/_common.py +619 -18
- package/skills/gsd-path-define/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path-define/scripts/build_recovery.py +100 -32
- package/skills/gsd-path-define/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path-define/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path-define/scripts/detect_project.py +587 -32
- package/skills/gsd-path-define/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-define/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path-define/scripts/integration.py +580 -25
- package/skills/gsd-path-define/scripts/isolation.py +1438 -57
- package/skills/gsd-path-define/scripts/loop_run.py +31 -29
- package/skills/gsd-path-define/scripts/members.py +706 -0
- package/skills/gsd-path-define/scripts/path_config.py +5 -5
- package/skills/gsd-path-define/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path-define/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path-define/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-define/scripts/review_panel.py +1 -1
- package/skills/gsd-path-define/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path-define/scripts/state_promote.py +2 -0
- package/skills/gsd-path-define/scripts/worktree_paths.py +72 -10
- 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/SKILL.md +2 -1
- package/skills/gsd-path-discuss/references/operating-rules.md +214 -0
- package/skills/gsd-path-discuss/scripts/_common.py +619 -18
- package/skills/gsd-path-discuss/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-discuss/scripts/loop_run.py +31 -29
- package/skills/gsd-path-discuss/scripts/path_config.py +5 -5
- package/skills/gsd-path-discuss/scripts/review_panel.py +1 -1
- package/skills/gsd-path-discuss/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-docs-audit/SKILL.md +6 -4
- package/skills/gsd-path-docs-audit/references/dispatch.md +13 -0
- package/skills/gsd-path-docs-audit/references/docs-auditor.md +7 -1
- package/skills/gsd-path-docs-audit/references/operating-rules.md +214 -0
- package/skills/gsd-path-docs-audit/scripts/_common.py +619 -18
- package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +100 -32
- package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +91 -7
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path-docs-audit/scripts/detect_project.py +587 -32
- package/skills/gsd-path-docs-audit/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path-docs-audit/scripts/integration.py +580 -25
- package/skills/gsd-path-docs-audit/scripts/isolation.py +1438 -57
- package/skills/gsd-path-docs-audit/scripts/loop_run.py +31 -29
- package/skills/gsd-path-docs-audit/scripts/members.py +706 -0
- package/skills/gsd-path-docs-audit/scripts/path_config.py +5 -5
- package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-docs-audit/scripts/review_panel.py +1 -1
- package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path-docs-audit/scripts/state_promote.py +2 -0
- package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-docs-audit/templates/docs-audit.md +6 -2
- package/skills/gsd-path-forensics/SKILL.md +2 -1
- package/skills/gsd-path-forensics/references/operating-rules.md +214 -0
- package/skills/gsd-path-forensics/scripts/_common.py +619 -18
- package/skills/gsd-path-forensics/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-forensics/scripts/loop_run.py +31 -29
- package/skills/gsd-path-forensics/scripts/path_config.py +5 -5
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +82 -31
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +218 -30
- package/skills/gsd-path-forensics/scripts/review_panel.py +1 -1
- package/skills/gsd-path-forensics/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-inspect/SKILL.md +5 -4
- package/skills/gsd-path-inspect/references/codebase-mapper.md +2 -1
- package/skills/gsd-path-inspect/references/dispatch.md +13 -0
- package/skills/gsd-path-inspect/references/docs-auditor.md +7 -1
- package/skills/gsd-path-inspect/references/operating-rules.md +214 -0
- package/skills/gsd-path-inspect/scripts/_common.py +619 -18
- package/skills/gsd-path-inspect/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path-inspect/scripts/build_recovery.py +100 -32
- package/skills/gsd-path-inspect/scripts/check_docs_audit.py +91 -7
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path-inspect/scripts/detect_project.py +587 -32
- package/skills/gsd-path-inspect/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path-inspect/scripts/integration.py +580 -25
- package/skills/gsd-path-inspect/scripts/isolation.py +1438 -57
- package/skills/gsd-path-inspect/scripts/loop_run.py +31 -29
- package/skills/gsd-path-inspect/scripts/members.py +706 -0
- package/skills/gsd-path-inspect/scripts/path_config.py +5 -5
- package/skills/gsd-path-inspect/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path-inspect/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-inspect/scripts/review_panel.py +1 -1
- package/skills/gsd-path-inspect/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path-inspect/scripts/state_promote.py +2 -0
- package/skills/gsd-path-inspect/scripts/workflow_run.py +107 -30
- package/skills/gsd-path-inspect/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-inspect/templates/codebase.md +1 -1
- package/skills/gsd-path-inspect/templates/docs-audit.md +6 -2
- package/skills/gsd-path-inspect/templates/state.md +1 -1
- package/skills/gsd-path-loop/SKILL.md +2 -1
- package/skills/gsd-path-loop/references/operating-rules.md +214 -0
- package/skills/gsd-path-loop/scripts/_common.py +619 -18
- package/skills/gsd-path-loop/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-loop/scripts/loop_run.py +31 -29
- package/skills/gsd-path-loop/scripts/path_config.py +5 -5
- package/skills/gsd-path-loop/scripts/review_panel.py +1 -1
- package/skills/gsd-path-loop/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-migrate/SKILL.md +2 -1
- package/skills/gsd-path-migrate/references/operating-rules.md +214 -0
- package/skills/gsd-path-migrate/scripts/_common.py +619 -18
- package/skills/gsd-path-migrate/scripts/core_hook_gate.py +10 -1
- package/skills/gsd-path-migrate/scripts/core_hook_settings.py +11 -4
- package/skills/gsd-path-migrate/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-migrate/scripts/loop_run.py +31 -29
- package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -2
- package/skills/gsd-path-migrate/scripts/path_config.py +5 -5
- package/skills/gsd-path-migrate/scripts/review_panel.py +1 -1
- package/skills/gsd-path-plan/SKILL.md +18 -4
- package/skills/gsd-path-plan/references/dispatch.md +13 -0
- package/skills/gsd-path-plan/references/operating-rules.md +214 -0
- package/skills/gsd-path-plan/references/planner.md +22 -4
- package/skills/gsd-path-plan/references/spec-probes.md +150 -0
- package/skills/gsd-path-plan/scripts/_common.py +619 -18
- package/skills/gsd-path-plan/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path-plan/scripts/build_recovery.py +100 -32
- package/skills/gsd-path-plan/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path-plan/scripts/detect_project.py +587 -32
- package/skills/gsd-path-plan/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-plan/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path-plan/scripts/integration.py +580 -25
- package/skills/gsd-path-plan/scripts/isolation.py +1438 -57
- package/skills/gsd-path-plan/scripts/loop_run.py +31 -29
- package/skills/gsd-path-plan/scripts/members.py +706 -0
- package/skills/gsd-path-plan/scripts/path_config.py +5 -5
- package/skills/gsd-path-plan/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path-plan/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path-plan/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-plan/scripts/review_panel.py +1 -1
- package/skills/gsd-path-plan/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path-plan/scripts/state_promote.py +2 -0
- package/skills/gsd-path-plan/scripts/workflow_run.py +107 -30
- package/skills/gsd-path-plan/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-plan/templates/patch-findings.md +3 -3
- package/skills/gsd-path-plan/templates/plan.md +17 -2
- package/skills/gsd-path-plan/templates/task.md +1 -0
- package/skills/gsd-path-research/SKILL.md +13 -1
- package/skills/gsd-path-research/references/dispatch.md +13 -0
- package/skills/gsd-path-research/references/operating-rules.md +214 -0
- package/skills/gsd-path-research/scripts/_common.py +619 -18
- package/skills/gsd-path-research/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path-research/scripts/build_recovery.py +100 -32
- package/skills/gsd-path-research/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path-research/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path-research/scripts/detect_project.py +587 -32
- package/skills/gsd-path-research/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-research/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path-research/scripts/integration.py +580 -25
- package/skills/gsd-path-research/scripts/isolation.py +1438 -57
- package/skills/gsd-path-research/scripts/loop_run.py +31 -29
- package/skills/gsd-path-research/scripts/members.py +706 -0
- package/skills/gsd-path-research/scripts/path_config.py +5 -5
- package/skills/gsd-path-research/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path-research/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path-research/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-research/scripts/review_panel.py +1 -1
- package/skills/gsd-path-research/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path-research/scripts/state_promote.py +2 -0
- package/skills/gsd-path-research/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-research/templates/research-handoff.md +14 -3
- package/skills/gsd-path-roadmap/SKILL.md +2 -1
- package/skills/gsd-path-roadmap/references/dispatch.md +13 -0
- package/skills/gsd-path-roadmap/references/operating-rules.md +214 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +619 -18
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +100 -32
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path-roadmap/scripts/detect_project.py +587 -32
- package/skills/gsd-path-roadmap/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path-roadmap/scripts/integration.py +580 -25
- package/skills/gsd-path-roadmap/scripts/isolation.py +1438 -57
- package/skills/gsd-path-roadmap/scripts/loop_run.py +31 -29
- package/skills/gsd-path-roadmap/scripts/members.py +706 -0
- package/skills/gsd-path-roadmap/scripts/path_config.py +5 -5
- package/skills/gsd-path-roadmap/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-roadmap/scripts/review_panel.py +1 -1
- package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path-roadmap/scripts/state_promote.py +2 -0
- package/skills/gsd-path-roadmap/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-ship/SKILL.md +33 -11
- package/skills/gsd-path-ship/references/dispatch.md +13 -0
- package/skills/gsd-path-ship/references/operating-rules.md +214 -0
- package/skills/gsd-path-ship/references/reviewer-final.md +20 -2
- package/skills/gsd-path-ship/references/reviewer-wave.md +18 -5
- 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 +619 -18
- package/skills/gsd-path-ship/scripts/archive_milestone.py +160 -86
- package/skills/gsd-path-ship/scripts/build_recovery.py +100 -32
- package/skills/gsd-path-ship/scripts/build_state.py +143 -24
- package/skills/gsd-path-ship/scripts/check_handoffs.py +662 -50
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +338 -30
- package/skills/gsd-path-ship/scripts/detect_project.py +587 -32
- package/skills/gsd-path-ship/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-ship/scripts/discussion_validate.py +152 -50
- package/skills/gsd-path-ship/scripts/integration.py +580 -25
- package/skills/gsd-path-ship/scripts/isolation.py +1438 -57
- package/skills/gsd-path-ship/scripts/lean_verification.py +99 -19
- package/skills/gsd-path-ship/scripts/loop_run.py +31 -29
- package/skills/gsd-path-ship/scripts/members.py +706 -0
- package/skills/gsd-path-ship/scripts/path_config.py +5 -5
- package/skills/gsd-path-ship/scripts/pipeline_git.py +124 -34
- package/skills/gsd-path-ship/scripts/pipeline_state.py +287 -87
- package/skills/gsd-path-ship/scripts/promote_lookahead.py +1 -1
- package/skills/gsd-path-ship/scripts/review_panel.py +1 -1
- package/skills/gsd-path-ship/scripts/state_checkpoint.py +135 -44
- package/skills/gsd-path-ship/scripts/state_promote.py +2 -0
- package/skills/gsd-path-ship/scripts/workflow_run.py +107 -30
- package/skills/gsd-path-ship/scripts/worktree_paths.py +72 -10
- package/skills/gsd-path-ship/templates/final-review.md +16 -4
- package/skills/gsd-path-ship/templates/gap-review.md +2 -1
- package/skills/gsd-path-ship/templates/patch-findings.md +3 -3
- package/skills/gsd-path-ship/templates/wave-review.md +16 -4
- package/skills/gsd-path-undo/SKILL.md +2 -1
- package/skills/gsd-path-undo/references/operating-rules.md +214 -0
- package/skills/gsd-path-undo/scripts/_common.py +619 -18
- package/skills/gsd-path-undo/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-undo/scripts/loop_run.py +31 -29
- package/skills/gsd-path-undo/scripts/path_config.py +5 -5
- package/skills/gsd-path-undo/scripts/pipeline_undo.py +218 -30
- package/skills/gsd-path-undo/scripts/review_panel.py +1 -1
- package/skills/gsd-path-undo/scripts/worktree_paths.py +72 -10
- package/skills/path/BUILD.md +7 -1
- package/skills/path/DECIDE.md +11 -4
- package/skills/path/DEFINE.md +36 -4
- package/skills/path/DOCS-AUDIT.md +6 -4
- package/skills/path/INSPECT.md +5 -4
- package/skills/path/PLAN.md +18 -4
- package/skills/path/RESEARCH.md +13 -1
- package/skills/path/ROADMAP.md +2 -1
- package/skills/path/SHIP.md +33 -11
- package/skills/path/SKILL.md +45 -1
- package/skills/path/references/build-native.md +63 -15
- package/skills/path/references/codebase-mapper.md +2 -1
- package/skills/path/references/dispatch.md +13 -0
- package/skills/path/references/docs-auditor.md +7 -1
- package/skills/path/references/operating-rules.md +214 -0
- package/skills/path/references/planner.md +22 -4
- package/skills/path/references/reviewer-final.md +20 -2
- package/skills/path/references/reviewer-wave.md +18 -5
- package/skills/path/references/reviewer.md +3 -1
- package/skills/path/references/spec-probes.md +150 -0
- package/skills/path/scripts/_common.py +619 -18
- package/skills/path/scripts/archive_milestone.py +160 -86
- package/skills/path/scripts/bootstrap_repository.py +8 -5
- package/skills/path/scripts/build_recovery.py +100 -32
- package/skills/path/scripts/build_state.py +143 -24
- package/skills/path/scripts/check_docs_audit.py +91 -7
- package/skills/path/scripts/check_handoffs.py +662 -50
- package/skills/path/scripts/check_task_briefs.py +338 -30
- package/skills/path/scripts/check_update.py +2 -2
- package/skills/path/scripts/detect_project.py +587 -32
- package/skills/path/scripts/discussion_records.py +19 -13
- package/skills/path/scripts/discussion_validate.py +152 -50
- package/skills/path/scripts/dispatch_driver.py +501 -46
- package/skills/path/scripts/git_guard.py +118 -31
- package/skills/path/scripts/integration.py +580 -25
- package/skills/path/scripts/isolation.py +1438 -57
- package/skills/path/scripts/lean_verification.py +99 -19
- package/skills/path/scripts/loop_run.py +31 -29
- package/skills/path/scripts/members.py +706 -0
- package/skills/path/scripts/path_config.py +5 -5
- package/skills/path/scripts/pipeline_git.py +124 -34
- package/skills/path/scripts/pipeline_state.py +287 -87
- package/skills/path/scripts/promote_lookahead.py +1 -1
- package/skills/path/scripts/review_panel.py +1 -1
- package/skills/path/scripts/state_checkpoint.py +135 -44
- package/skills/path/scripts/state_promote.py +2 -0
- package/skills/path/scripts/status_runtime.py +58 -10
- package/skills/path/scripts/task_context.py +12 -3
- package/skills/path/scripts/token_budget.py +12 -8
- package/skills/path/scripts/workflow_run.py +107 -30
- package/skills/path/scripts/worktree_paths.py +72 -10
- package/skills/path/templates/codebase.md +1 -1
- package/skills/path/templates/docs-audit.md +6 -2
- package/skills/path/templates/final-review.md +16 -4
- package/skills/path/templates/gap-review.md +2 -1
- 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/task.md +1 -0
- package/skills/path/templates/wave-review.md +16 -4
- 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
|
@@ -14,28 +14,41 @@ from datetime import datetime, timezone
|
|
|
14
14
|
from pathlib import Path, PurePosixPath
|
|
15
15
|
from typing import Dict, List, Mapping, Optional, Sequence, Tuple
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
from check_task_briefs import _frontmatter, _sections
|
|
19
|
-
from isolation import (
|
|
17
|
+
if __package__:
|
|
18
|
+
from .check_task_briefs import _frontmatter, _sections
|
|
19
|
+
from .isolation import (
|
|
20
20
|
VERIFY_LEDGER_PATH,
|
|
21
21
|
IsolationError,
|
|
22
22
|
recover as recover_isolation,
|
|
23
23
|
verify_landed_task_files,
|
|
24
24
|
)
|
|
25
|
-
from pipeline_git import task_commit_subject
|
|
26
|
-
from pipeline_state import PipelineStateError, load_state
|
|
27
|
-
import _common
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
from .pipeline_git import task_commit_subject
|
|
26
|
+
from .pipeline_state import PipelineStateError, load_state
|
|
27
|
+
from . import _common, check_handoffs
|
|
28
|
+
else:
|
|
29
|
+
try:
|
|
30
|
+
from check_task_briefs import _frontmatter, _sections
|
|
31
|
+
from isolation import (
|
|
32
|
+
VERIFY_LEDGER_PATH,
|
|
33
|
+
IsolationError,
|
|
34
|
+
recover as recover_isolation,
|
|
35
|
+
verify_landed_task_files,
|
|
36
|
+
)
|
|
37
|
+
from pipeline_git import task_commit_subject
|
|
38
|
+
from pipeline_state import PipelineStateError, load_state
|
|
39
|
+
import _common
|
|
40
|
+
import check_handoffs
|
|
41
|
+
except ImportError: # pragma: no cover - package imports used by tests
|
|
42
|
+
from scripts.check_task_briefs import _frontmatter, _sections
|
|
43
|
+
from scripts.isolation import (
|
|
44
|
+
VERIFY_LEDGER_PATH,
|
|
45
|
+
IsolationError,
|
|
46
|
+
recover as recover_isolation,
|
|
47
|
+
verify_landed_task_files,
|
|
48
|
+
)
|
|
49
|
+
from scripts.pipeline_git import task_commit_subject
|
|
50
|
+
from scripts.pipeline_state import PipelineStateError, load_state
|
|
51
|
+
from scripts import _common, check_handoffs
|
|
39
52
|
|
|
40
53
|
|
|
41
54
|
DEFAULT_PROJECT_DIR = ".project"
|
|
@@ -45,6 +58,10 @@ FULL_SHA_RE = re.compile(r"^[0-9a-f]{40}$")
|
|
|
45
58
|
# recover verdicts that count as landed, mapped to their evidence classification.
|
|
46
59
|
LANDED_VERDICTS = {"recovered": "proven-landed", "attested": "attested"}
|
|
47
60
|
WAVE_HEADING_RE = re.compile(r"(?m)^## Wave (?P<wave>\d+)\b.*$")
|
|
61
|
+
REVIEW_VERDICT_RE = re.compile(
|
|
62
|
+
r"(?m)^Wave verdict:\s*(?P<verdict>pass|blocked)\s*(?:<!--.*?-->)?\s*$"
|
|
63
|
+
)
|
|
64
|
+
REVIEW_FINDINGS_HEADING = "Review findings"
|
|
48
65
|
VERIFY_HEAVY_RE = re.compile(r"(?m)^Heavy:\s*(?P<value>yes|no)\s*(?:<!--.*-->)?\s*$")
|
|
49
66
|
VALID_TASK_STATUSES = {"pending", "in-progress", "done", "failed", "blocked"}
|
|
50
67
|
VERIFY_RESULTS = _common.VERIFY_RESULTS
|
|
@@ -75,6 +92,8 @@ class Task:
|
|
|
75
92
|
task_branch: Optional[str]
|
|
76
93
|
task_file: str
|
|
77
94
|
verify_heavy: bool
|
|
95
|
+
repo: str = ""
|
|
96
|
+
review_findings: bool = False
|
|
78
97
|
|
|
79
98
|
|
|
80
99
|
@dataclass(frozen=True)
|
|
@@ -227,6 +246,8 @@ def _parse_task(path: Path, relative_path: str, task_file: Optional[str] = None)
|
|
|
227
246
|
task_branch=_nullable_string(fields, "task_branch", label),
|
|
228
247
|
task_file=task_file or relative_path,
|
|
229
248
|
verify_heavy=_verify_heavy(text, label),
|
|
249
|
+
repo=str(fields.get("repo") or ""),
|
|
250
|
+
review_findings=REVIEW_FINDINGS_HEADING in _sections(text),
|
|
230
251
|
)
|
|
231
252
|
|
|
232
253
|
|
|
@@ -284,6 +305,8 @@ def _load_tasks(
|
|
|
284
305
|
raise BuildStateError("missing-input", f"task directory is missing or unsafe: {tasks_dir}")
|
|
285
306
|
task_paths: Dict[str, Path] = {}
|
|
286
307
|
for path in sorted(tasks_dir.iterdir()):
|
|
308
|
+
if _common.is_ignored_junk(path):
|
|
309
|
+
continue
|
|
287
310
|
match = TASK_FILE_RE.fullmatch(path.name)
|
|
288
311
|
if match is None or not path.is_file() or path.is_symlink():
|
|
289
312
|
raise BuildStateError(
|
|
@@ -464,9 +487,41 @@ def _validate_ready_metadata(project: Project) -> None:
|
|
|
464
487
|
|
|
465
488
|
|
|
466
489
|
def _overlap(left: Task, right: Task) -> List[str]:
|
|
490
|
+
if left.repo != right.repo:
|
|
491
|
+
return []
|
|
467
492
|
return sorted(set(left.files) & set(right.files))
|
|
468
493
|
|
|
469
494
|
|
|
495
|
+
def _blocked_wave_reviews(project: Project) -> Dict[int, int]:
|
|
496
|
+
"""Return waves whose highest canonical review cycle has a blocked lens."""
|
|
497
|
+
|
|
498
|
+
review_dir = project.tasks_dir.parent / "review"
|
|
499
|
+
if review_dir.is_symlink() or not review_dir.is_dir():
|
|
500
|
+
return {}
|
|
501
|
+
|
|
502
|
+
latest: Dict[int, int] = {}
|
|
503
|
+
verdicts: Dict[Tuple[int, int], List[str]] = {}
|
|
504
|
+
for path in sorted(review_dir.iterdir()):
|
|
505
|
+
named = check_handoffs.WAVE_REVIEW_NAME.fullmatch(path.name)
|
|
506
|
+
if named is None or path.is_symlink() or not path.is_file():
|
|
507
|
+
continue
|
|
508
|
+
wave, cycle = int(named.group("wave")), int(named.group("cycle"))
|
|
509
|
+
latest[wave] = max(latest.get(wave, 0), cycle)
|
|
510
|
+
try:
|
|
511
|
+
text = path.read_text(encoding="utf-8")
|
|
512
|
+
except (OSError, UnicodeDecodeError):
|
|
513
|
+
continue
|
|
514
|
+
verdict = REVIEW_VERDICT_RE.search(text)
|
|
515
|
+
if verdict is not None:
|
|
516
|
+
verdicts.setdefault((wave, cycle), []).append(verdict.group("verdict"))
|
|
517
|
+
|
|
518
|
+
return {
|
|
519
|
+
wave: cycle
|
|
520
|
+
for wave, cycle in latest.items()
|
|
521
|
+
if "blocked" in verdicts.get((wave, cycle), [])
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
|
|
470
525
|
def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
|
|
471
526
|
allow_done: bool = False) -> Dict[str, object]:
|
|
472
527
|
"""Return task readiness, optionally accepting build/done for completion checks."""
|
|
@@ -476,6 +531,39 @@ def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
|
|
|
476
531
|
by_id = {task.task_id: task for task in project.tasks}
|
|
477
532
|
unfinished_waves = sorted({task.wave for task in project.tasks if task.status != "done"})
|
|
478
533
|
current_wave = unfinished_waves[0] if unfinished_waves else None
|
|
534
|
+
blocked_reviews = _blocked_wave_reviews(project)
|
|
535
|
+
blocked_wave = min(
|
|
536
|
+
(
|
|
537
|
+
wave
|
|
538
|
+
for wave in blocked_reviews
|
|
539
|
+
if (current_wave is None or wave < current_wave)
|
|
540
|
+
and any(task.wave == wave for task in project.tasks)
|
|
541
|
+
and all(task.status == "done" for task in project.tasks if task.wave == wave)
|
|
542
|
+
),
|
|
543
|
+
default=None,
|
|
544
|
+
)
|
|
545
|
+
repair_for_wave: Optional[int] = None
|
|
546
|
+
blocked_review: Optional[Dict[str, object]] = None
|
|
547
|
+
if blocked_wave is not None:
|
|
548
|
+
source_ids = {task.task_id for task in project.tasks if task.wave == blocked_wave}
|
|
549
|
+
repairs = [
|
|
550
|
+
task
|
|
551
|
+
for task in project.tasks
|
|
552
|
+
if task.status != "done"
|
|
553
|
+
and task.wave > blocked_wave
|
|
554
|
+
and task.review_findings
|
|
555
|
+
and source_ids.intersection(task.deps)
|
|
556
|
+
]
|
|
557
|
+
if repairs:
|
|
558
|
+
repair_for_wave = blocked_wave
|
|
559
|
+
current_wave = min(task.wave for task in repairs)
|
|
560
|
+
else:
|
|
561
|
+
current_wave = blocked_wave
|
|
562
|
+
blocked_review = {
|
|
563
|
+
"wave": blocked_wave,
|
|
564
|
+
"cycle": blocked_reviews[blocked_wave],
|
|
565
|
+
"waiting_waves": unfinished_waves,
|
|
566
|
+
}
|
|
479
567
|
active = [task for task in project.tasks if task.status == "in-progress"]
|
|
480
568
|
if current_wave is not None and any(task.wave != current_wave for task in active):
|
|
481
569
|
task = next(task for task in active if task.wave != current_wave)
|
|
@@ -518,7 +606,7 @@ def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
|
|
|
518
606
|
)
|
|
519
607
|
|
|
520
608
|
wave_active = any(task.wave == current_wave for task in active)
|
|
521
|
-
if current_wave is not None and not selectable and not wave_active:
|
|
609
|
+
if current_wave is not None and blocked_review is None and not selectable and not wave_active:
|
|
522
610
|
stalled = [
|
|
523
611
|
{
|
|
524
612
|
"id": task.task_id,
|
|
@@ -538,11 +626,17 @@ def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
|
|
|
538
626
|
{"wave": current_wave, "tasks": stalled},
|
|
539
627
|
)
|
|
540
628
|
|
|
629
|
+
typed: Dict[str, object] = {}
|
|
630
|
+
if repair_for_wave is not None:
|
|
631
|
+
typed["repair_for_wave"] = repair_for_wave
|
|
632
|
+
if blocked_review is not None:
|
|
633
|
+
typed["blocked_review"] = blocked_review
|
|
541
634
|
return {
|
|
542
635
|
"command": "ready",
|
|
543
636
|
"branch": project.branch,
|
|
544
637
|
"head": project.head,
|
|
545
638
|
"current_wave": current_wave,
|
|
639
|
+
**typed,
|
|
546
640
|
"ready": [
|
|
547
641
|
{
|
|
548
642
|
"id": task.task_id,
|
|
@@ -553,6 +647,7 @@ def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
|
|
|
553
647
|
"files": list(task.files),
|
|
554
648
|
"task_file": task.task_file,
|
|
555
649
|
"verify_heavy": task.verify_heavy,
|
|
650
|
+
**({"repo": task.repo} if task.repo else {}),
|
|
556
651
|
}
|
|
557
652
|
for task in selectable
|
|
558
653
|
],
|
|
@@ -726,6 +821,22 @@ def _ledger_path(repo: Path) -> Path:
|
|
|
726
821
|
return path
|
|
727
822
|
|
|
728
823
|
|
|
824
|
+
def _member_checkout(repo: Path, member: Optional[str]) -> Path:
|
|
825
|
+
if member is None:
|
|
826
|
+
return repo
|
|
827
|
+
try:
|
|
828
|
+
import members
|
|
829
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
830
|
+
from scripts import members
|
|
831
|
+
try:
|
|
832
|
+
listed = {item["name"]: Path(item["checkout"]) for item in members.read_members(repo)}
|
|
833
|
+
except members.MembersError as error:
|
|
834
|
+
raise BuildStateError("invalid-member", str(error)) from error
|
|
835
|
+
if member not in listed:
|
|
836
|
+
raise BuildStateError("invalid-member", f"{member} is not in MEMBERS.md")
|
|
837
|
+
return listed[member]
|
|
838
|
+
|
|
839
|
+
|
|
729
840
|
def _ledger_key(repo: Path, command: str, commit: str) -> Tuple[str, str]:
|
|
730
841
|
if not command.strip():
|
|
731
842
|
raise BuildStateError("invalid-command", "--command must not be empty")
|
|
@@ -742,13 +853,20 @@ def _ledger_entries(path: Path) -> List[Dict[str, object]]:
|
|
|
742
853
|
|
|
743
854
|
|
|
744
855
|
def verify_record(repo: str, command: str, commit: str, result: str,
|
|
745
|
-
execution: Optional[Dict[str, object]] = None
|
|
746
|
-
|
|
856
|
+
execution: Optional[Dict[str, object]] = None,
|
|
857
|
+
member: Optional[str] = None) -> Dict[str, object]:
|
|
858
|
+
"""Append one verify run (command, commit, result, timestamp) to the ledger.
|
|
859
|
+
|
|
860
|
+
A member run names its member and a commit in that member's repository.
|
|
861
|
+
"""
|
|
747
862
|
|
|
748
863
|
repository = _repo_root(repo)
|
|
749
864
|
if result not in VERIFY_RESULTS:
|
|
750
865
|
raise BuildStateError("invalid-result", "--result must be pass or fail")
|
|
751
|
-
normalized, commit = _ledger_key(repository, command, commit)
|
|
866
|
+
normalized, commit = _ledger_key(_member_checkout(repository, member), command, commit)
|
|
867
|
+
ignore_error = _common.project_ignore_error(repository)
|
|
868
|
+
if ignore_error:
|
|
869
|
+
raise BuildStateError("ignored-ledger", ignore_error)
|
|
752
870
|
path = _ledger_path(repository)
|
|
753
871
|
_ledger_entries(path)
|
|
754
872
|
entry = {
|
|
@@ -757,6 +875,7 @@ def verify_record(repo: str, command: str, commit: str, result: str,
|
|
|
757
875
|
"commit": commit,
|
|
758
876
|
"result": result,
|
|
759
877
|
"recorded_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
|
|
878
|
+
**({"repo": member} if member else {}),
|
|
760
879
|
}
|
|
761
880
|
if execution is not None:
|
|
762
881
|
if (not isinstance(execution.get("stdout"), str)
|
|
@@ -771,12 +890,12 @@ def verify_record(repo: str, command: str, commit: str, result: str,
|
|
|
771
890
|
return {"command": "verify-record", "ledger": VERIFY_LEDGER_PATH, "entry": entry}
|
|
772
891
|
|
|
773
892
|
|
|
774
|
-
def verify_lookup(repo: str, command: str, commit: str) -> Dict[str, object]:
|
|
893
|
+
def verify_lookup(repo: str, command: str, commit: str, member: Optional[str] = None) -> Dict[str, object]:
|
|
775
894
|
"""Return the latest recorded run of this command at this commit, or a miss."""
|
|
776
895
|
|
|
777
896
|
repository = _repo_root(repo)
|
|
778
|
-
normalized, commit = _ledger_key(repository, command, commit)
|
|
779
|
-
entry = _common.latest_verify_entry(_ledger_entries(_ledger_path(repository)), normalized, commit)
|
|
897
|
+
normalized, commit = _ledger_key(_member_checkout(repository, member), command, commit)
|
|
898
|
+
entry = _common.latest_verify_entry(_ledger_entries(_ledger_path(repository)), normalized, commit, member)
|
|
780
899
|
return {
|
|
781
900
|
"command": "verify-lookup",
|
|
782
901
|
"ledger": VERIFY_LEDGER_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.
|
|
@@ -26,6 +30,10 @@ gate derives the inventory from tracked Markdown files, excluding `.git`,
|
|
|
26
30
|
`*/skills/path` bundles,
|
|
27
31
|
the audit itself, and — unless the
|
|
28
32
|
audit declares `Alignment mode: yes` — everything else under `.project/`.
|
|
33
|
+
|
|
34
|
+
`Repo root` names the primary repository. When the audit is gated inside a
|
|
35
|
+
disposable sidecar, pass that sidecar as `--repo` and the primary as
|
|
36
|
+
`--primary`; the header then stays valid after the sidecar is retired.
|
|
29
37
|
"""
|
|
30
38
|
|
|
31
39
|
import argparse
|
|
@@ -92,6 +100,12 @@ def _table_rows(body: str, width: int, label: str) -> List[List[str]]:
|
|
|
92
100
|
return rows
|
|
93
101
|
|
|
94
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
|
+
|
|
95
109
|
def _not_placeholder(value: str, label: str) -> None:
|
|
96
110
|
normalized = value.strip().strip("`\"").strip()
|
|
97
111
|
if (
|
|
@@ -243,12 +257,70 @@ def _check_carried(
|
|
|
243
257
|
raise AuditError(f"{label}: doc or evidence changed since the prior audit: {', '.join(moved)}")
|
|
244
258
|
|
|
245
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
|
+
|
|
246
317
|
def validate(
|
|
247
318
|
repo: Path,
|
|
248
319
|
audit_relative: str,
|
|
249
320
|
inventory: Optional[Sequence[str]],
|
|
250
321
|
prior_text: Optional[str] = None,
|
|
251
322
|
changed: Optional[Sequence[str]] = None,
|
|
323
|
+
primary: Optional[Path] = None,
|
|
252
324
|
) -> Dict[str, object]:
|
|
253
325
|
audit = repo / audit_relative
|
|
254
326
|
if not audit.is_file() or audit.is_symlink():
|
|
@@ -263,8 +335,8 @@ def validate(
|
|
|
263
335
|
raise AuditError(f"header must contain exactly one {key} line")
|
|
264
336
|
header[key] = values[0]
|
|
265
337
|
declared_root = Path(header["Repo root"])
|
|
266
|
-
if not declared_root.is_absolute() or declared_root.resolve() != repo.resolve():
|
|
267
|
-
raise AuditError("Repo root does not match --repo")
|
|
338
|
+
if not declared_root.is_absolute() or declared_root.resolve() != (primary or repo).resolve():
|
|
339
|
+
raise AuditError("Repo root does not match --primary (default --repo)")
|
|
268
340
|
try:
|
|
269
341
|
date.fromisoformat(header["Audited"])
|
|
270
342
|
except ValueError as error:
|
|
@@ -349,7 +421,9 @@ def validate(
|
|
|
349
421
|
|
|
350
422
|
queue_body = sections["Remediation queue"]
|
|
351
423
|
non_verified = sum(tallies[verdict] for verdict in VERDICTS if verdict != "verified")
|
|
352
|
-
queue =
|
|
424
|
+
queue = _remediation_queue_rows(
|
|
425
|
+
queue_body, "Remediation queue", required=bool(non_verified)
|
|
426
|
+
)
|
|
353
427
|
expected_queue = sorted(
|
|
354
428
|
(doc, claim["claim"], claim["verdict"])
|
|
355
429
|
for doc, claims in docs.items()
|
|
@@ -366,9 +440,13 @@ def validate(
|
|
|
366
440
|
_not_placeholder(action, f"Remediation queue #{number}: action")
|
|
367
441
|
if len(queue) != non_verified:
|
|
368
442
|
raise AuditError(f"Remediation queue has {len(queue)} rows for {non_verified} non-verified claims")
|
|
369
|
-
numbers = [row[0] for row in queue]
|
|
370
|
-
if
|
|
371
|
-
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)
|
|
372
450
|
actual_queue = sorted((doc, claim, verdict) for _, doc, claim, verdict, _, _ in queue)
|
|
373
451
|
if actual_queue != expected_queue:
|
|
374
452
|
raise AuditError(
|
|
@@ -392,6 +470,11 @@ def parser() -> argparse.ArgumentParser:
|
|
|
392
470
|
argument_parser.add_argument("--repo", type=Path, required=True)
|
|
393
471
|
argument_parser.add_argument("--audit", default=DEFAULT_AUDIT, help="relative audit path (default: %(default)s)")
|
|
394
472
|
argument_parser.add_argument("--inventory", help="file with one inventoried POSIX path per line, or - for stdin")
|
|
473
|
+
argument_parser.add_argument(
|
|
474
|
+
"--primary",
|
|
475
|
+
type=Path,
|
|
476
|
+
help="primary repository the Repo root header must name (default: --repo)",
|
|
477
|
+
)
|
|
395
478
|
argument_parser.add_argument(
|
|
396
479
|
"--prior-audit",
|
|
397
480
|
type=Path,
|
|
@@ -454,7 +537,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
|
454
537
|
changed = _path_lines(arguments.changed) if arguments.changed is not None else None
|
|
455
538
|
try:
|
|
456
539
|
result = validate(
|
|
457
|
-
arguments.repo.resolve(), arguments.audit, inventory, prior_text, changed
|
|
540
|
+
arguments.repo.resolve(), arguments.audit, inventory, prior_text, changed,
|
|
541
|
+
arguments.primary,
|
|
458
542
|
)
|
|
459
543
|
except AuditError as error:
|
|
460
544
|
print(f"docs audit validation failed: {error}", file=sys.stderr)
|