@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
|
@@ -12,10 +12,13 @@ sys.dont_write_bytecode = True
|
|
|
12
12
|
from pathlib import Path, PurePosixPath
|
|
13
13
|
from typing import Dict, List, Optional, Sequence, Set, Tuple
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
import _common
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
if __package__:
|
|
16
|
+
from . import _common
|
|
17
|
+
else:
|
|
18
|
+
try:
|
|
19
|
+
import _common
|
|
20
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
21
|
+
from scripts import _common
|
|
19
22
|
|
|
20
23
|
|
|
21
24
|
DEFAULT_TASKS_DIR = ".project/tasks"
|
|
@@ -50,6 +53,9 @@ COMMENT_PATTERN = re.compile(r"<!--.*?-->", re.DOTALL)
|
|
|
50
53
|
BACKTICK_PATTERN = re.compile(r"`([^`\n]+)`")
|
|
51
54
|
PATH_TOKEN_PATTERN = re.compile(r"[A-Za-z0-9._~/-]+")
|
|
52
55
|
VERIFY_BLOCK_PATTERN = re.compile(r"```bash[ \t]*\n(?P<block>.*?)```", re.DOTALL)
|
|
56
|
+
TEXT_TOOL_COMMANDS = frozenset({"awk", "egrep", "fgrep", "gawk", "grep", "gsed", "rg", "sed"})
|
|
57
|
+
REGEX_PATTERN_CHARS = frozenset("|*+?[]()^$.\\")
|
|
58
|
+
PIPELINE_WORKSPACE_DIRS = ("review", "discuss", "build", "plan")
|
|
53
59
|
|
|
54
60
|
|
|
55
61
|
class BriefError(RuntimeError):
|
|
@@ -70,10 +76,20 @@ def _base_exists(repo: Path, base: str, path: str) -> bool:
|
|
|
70
76
|
return _run_git(repo, "cat-file", "-e", f"{base}:{path}").returncode == 0
|
|
71
77
|
|
|
72
78
|
|
|
79
|
+
def _project_track(task: Path, repo: Path) -> Optional[str]:
|
|
80
|
+
try:
|
|
81
|
+
track = task.parent.parent.relative_to(repo).as_posix()
|
|
82
|
+
except ValueError:
|
|
83
|
+
return None
|
|
84
|
+
if track not in {".project", ".project/next"}:
|
|
85
|
+
return None
|
|
86
|
+
return track
|
|
87
|
+
|
|
88
|
+
|
|
73
89
|
def _supplied_contract(repo: Path, task: Path, token: str) -> bool:
|
|
74
90
|
"""Plan approval checkpoints these supplied inputs after the brief gate."""
|
|
75
|
-
track = task
|
|
76
|
-
if track
|
|
91
|
+
track = _project_track(task, repo)
|
|
92
|
+
if track is None:
|
|
77
93
|
return False
|
|
78
94
|
if token not in {
|
|
79
95
|
f"{track}/intent/INTENT.md",
|
|
@@ -84,6 +100,18 @@ def _supplied_contract(repo: Path, task: Path, token: str) -> bool:
|
|
|
84
100
|
return path.is_file() and path.resolve() == repo.resolve() / token
|
|
85
101
|
|
|
86
102
|
|
|
103
|
+
def _pipeline_workspace_file(repo: Path, task: Path, token: str) -> bool:
|
|
104
|
+
"""Pipeline-owned paths may exist only in the working tree before checkpoint."""
|
|
105
|
+
track = _project_track(task, repo)
|
|
106
|
+
if track is None:
|
|
107
|
+
return False
|
|
108
|
+
prefixes = tuple(f"{track}/{name}/" for name in PIPELINE_WORKSPACE_DIRS)
|
|
109
|
+
if not any(token.startswith(prefix) for prefix in prefixes):
|
|
110
|
+
return False
|
|
111
|
+
path = repo / token
|
|
112
|
+
return path.is_file() and path.resolve() == repo.resolve() / token
|
|
113
|
+
|
|
114
|
+
|
|
87
115
|
_strip_yaml_comment = _common.strip_yaml_comment
|
|
88
116
|
_unquote = _common.unquote
|
|
89
117
|
|
|
@@ -148,7 +176,7 @@ def _clean(body: str) -> str:
|
|
|
148
176
|
return COMMENT_PATTERN.sub("", body).strip()
|
|
149
177
|
|
|
150
178
|
|
|
151
|
-
def
|
|
179
|
+
def _slash_token_shape(token: str) -> bool:
|
|
152
180
|
return (
|
|
153
181
|
"/" in token
|
|
154
182
|
and PATH_TOKEN_PATTERN.fullmatch(token) is not None
|
|
@@ -159,34 +187,136 @@ def _looks_like_path(token: str) -> bool:
|
|
|
159
187
|
)
|
|
160
188
|
|
|
161
189
|
|
|
190
|
+
def _last_segment_has_extension(token: str) -> bool:
|
|
191
|
+
name = PurePosixPath(token).name
|
|
192
|
+
return "." in name and not name.startswith(".")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _looks_like_path(
|
|
196
|
+
token: str,
|
|
197
|
+
*,
|
|
198
|
+
repo: Path,
|
|
199
|
+
git_root: Path,
|
|
200
|
+
base: str,
|
|
201
|
+
task: Path,
|
|
202
|
+
) -> bool:
|
|
203
|
+
if not _slash_token_shape(token):
|
|
204
|
+
return False
|
|
205
|
+
if _last_segment_has_extension(token):
|
|
206
|
+
return True
|
|
207
|
+
if _base_exists(git_root, base, token):
|
|
208
|
+
return True
|
|
209
|
+
if _pipeline_workspace_file(repo, task, token):
|
|
210
|
+
return True
|
|
211
|
+
return False
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _looks_like_regex_pattern(token: str) -> bool:
|
|
215
|
+
return any(character in token for character in REGEX_PATTERN_CHARS)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _text_tool_command(command: str) -> bool:
|
|
219
|
+
return Path(command).name.casefold() in TEXT_TOOL_COMMANDS
|
|
220
|
+
|
|
221
|
+
|
|
162
222
|
def _normalize(path: str) -> str:
|
|
163
223
|
return str(PurePosixPath(path))
|
|
164
224
|
|
|
165
225
|
|
|
166
|
-
def _prose_tokens(
|
|
226
|
+
def _prose_tokens(
|
|
227
|
+
body: str,
|
|
228
|
+
*,
|
|
229
|
+
repo: Path,
|
|
230
|
+
git_root: Path,
|
|
231
|
+
base: str,
|
|
232
|
+
task: Path,
|
|
233
|
+
) -> List[str]:
|
|
167
234
|
cleaned = COMMENT_PATTERN.sub("", body)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if _looks_like_path(
|
|
172
|
-
|
|
235
|
+
tokens: List[str] = []
|
|
236
|
+
for match in BACKTICK_PATTERN.finditer(cleaned):
|
|
237
|
+
candidate = match.group(1).strip()
|
|
238
|
+
if _looks_like_path(
|
|
239
|
+
candidate, repo=repo, git_root=git_root, base=base, task=task
|
|
240
|
+
):
|
|
241
|
+
tokens.append(_normalize(candidate))
|
|
242
|
+
return tokens
|
|
173
243
|
|
|
174
244
|
|
|
175
|
-
def _verify_tokens(
|
|
176
|
-
|
|
245
|
+
def _verify_tokens(
|
|
246
|
+
block: str,
|
|
247
|
+
*,
|
|
248
|
+
repo: Path,
|
|
249
|
+
git_root: Path,
|
|
250
|
+
base: str,
|
|
251
|
+
task: Path,
|
|
252
|
+
) -> List[str]:
|
|
253
|
+
tokens: List[str] = []
|
|
177
254
|
for line in block.splitlines():
|
|
178
255
|
parts = line.split()
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
256
|
+
if len(parts) < 2:
|
|
257
|
+
continue
|
|
258
|
+
command = parts[0]
|
|
259
|
+
extended_grep = False
|
|
260
|
+
index = 1
|
|
261
|
+
while index < len(parts) and parts[index].startswith("-") and "=" not in parts[index]:
|
|
262
|
+
flag = parts[index]
|
|
263
|
+
if flag in {"-E", "-G"} or flag.startswith("-E"):
|
|
264
|
+
extended_grep = True
|
|
265
|
+
index += 1
|
|
266
|
+
text_tool = _text_tool_command(command)
|
|
267
|
+
for raw in parts[index:]:
|
|
268
|
+
quoted = raw.startswith(("'", '"'))
|
|
269
|
+
token = raw.strip("\"'")
|
|
270
|
+
if text_tool and quoted:
|
|
271
|
+
continue
|
|
272
|
+
if text_tool and command.casefold() == "grep" and extended_grep and _looks_like_regex_pattern(token):
|
|
273
|
+
continue
|
|
274
|
+
if _looks_like_path(token, repo=repo, git_root=git_root, base=base, task=task):
|
|
182
275
|
tokens.append(_normalize(token))
|
|
183
276
|
return tokens
|
|
184
277
|
|
|
185
278
|
|
|
279
|
+
def _member_bases(root: Path):
|
|
280
|
+
"""Resolve a member name to (checkout, origin/main SHA), reading MEMBERS.md once."""
|
|
281
|
+
cache: Dict[str, Optional[Tuple[Path, str]]] = {}
|
|
282
|
+
|
|
283
|
+
def locate(name: str) -> Optional[Tuple[Path, str]]:
|
|
284
|
+
if name not in cache:
|
|
285
|
+
if __package__:
|
|
286
|
+
from . import members
|
|
287
|
+
else:
|
|
288
|
+
try:
|
|
289
|
+
import members
|
|
290
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
291
|
+
from scripts import members
|
|
292
|
+
try:
|
|
293
|
+
listed = {member["name"]: Path(member["checkout"]) for member in members.read_members(root)}
|
|
294
|
+
checkout = listed.get(name)
|
|
295
|
+
if checkout is None:
|
|
296
|
+
cache[name] = None
|
|
297
|
+
else:
|
|
298
|
+
# A build lock selects the bound tip, including during Plan recovery;
|
|
299
|
+
# without the lock, use origin/main before the first build start.
|
|
300
|
+
lock = root / members.LOCK_PATH
|
|
301
|
+
locked = [entry for entry in (json.loads(lock.read_text(encoding="utf-8"))["members"]
|
|
302
|
+
if lock.is_file() else []) if entry["name"] == name]
|
|
303
|
+
if locked:
|
|
304
|
+
cache[name] = (checkout, _resolve_base(checkout, f"refs/heads/{locked[0]['branch']}"))
|
|
305
|
+
else:
|
|
306
|
+
members.require_origin_main(checkout)
|
|
307
|
+
cache[name] = (checkout, _resolve_base(checkout, "refs/remotes/origin/main"))
|
|
308
|
+
except (members.MembersError, BriefError) as error:
|
|
309
|
+
raise BriefError(f"member {name} is unavailable: {error}") from error
|
|
310
|
+
return cache[name]
|
|
311
|
+
|
|
312
|
+
return locate
|
|
313
|
+
|
|
314
|
+
|
|
186
315
|
def _lint_task(
|
|
187
316
|
repo: Path, base: str, path: Path,
|
|
188
317
|
dependency_files: Optional[Dict[str, Set[str]]] = None,
|
|
189
318
|
landed_bases: Optional[Dict[str, str]] = None,
|
|
319
|
+
member_base=None,
|
|
190
320
|
) -> Tuple[str, List[str], Optional[str], int]:
|
|
191
321
|
text = path.read_text(encoding="utf-8")
|
|
192
322
|
fields, error = _frontmatter(text)
|
|
@@ -198,6 +328,15 @@ def _lint_task(
|
|
|
198
328
|
checked = 0
|
|
199
329
|
if fields is None:
|
|
200
330
|
return task_id, [error or "invalid frontmatter"], None, checked
|
|
331
|
+
# A member task's paths resolve in the member at its origin/main, where the
|
|
332
|
+
# member bound branch starts; `repo:` absent means the coordinator.
|
|
333
|
+
git_root = repo
|
|
334
|
+
member = fields.get("repo")
|
|
335
|
+
if member is not None:
|
|
336
|
+
located = member_base(member) if isinstance(member, str) and member and member_base else None
|
|
337
|
+
if located is None:
|
|
338
|
+
return task_id, [f"repo: names no member in MEMBERS.md: {member}"], None, checked
|
|
339
|
+
git_root, base = located[0], (landed_bases or {}).get(task_id, located[1])
|
|
201
340
|
|
|
202
341
|
for field in REQUIRED_FIELDS:
|
|
203
342
|
if field not in fields:
|
|
@@ -237,7 +376,7 @@ def _lint_task(
|
|
|
237
376
|
for parent in candidate.parents:
|
|
238
377
|
if str(parent) == ".":
|
|
239
378
|
break
|
|
240
|
-
kind = _run_git(
|
|
379
|
+
kind = _run_git(git_root, "cat-file", "-t", f"{base}:{parent}")
|
|
241
380
|
if kind.returncode == 0 and kind.stdout.strip() != "tree":
|
|
242
381
|
problems.append(
|
|
243
382
|
f"files entry {normalized} has non-directory ancestor "
|
|
@@ -249,13 +388,16 @@ def _lint_task(
|
|
|
249
388
|
body = sections.get(name)
|
|
250
389
|
if body is None or not _clean(body):
|
|
251
390
|
continue
|
|
252
|
-
for token in _prose_tokens(
|
|
391
|
+
for token in _prose_tokens(
|
|
392
|
+
body, repo=repo, git_root=git_root, base=base, task=path
|
|
393
|
+
):
|
|
253
394
|
checked += 1
|
|
254
395
|
if (
|
|
255
396
|
token not in declared
|
|
256
397
|
and token not in supplied
|
|
257
398
|
and not _supplied_contract(repo, path, token)
|
|
258
|
-
and not
|
|
399
|
+
and not _pipeline_workspace_file(repo, path, token)
|
|
400
|
+
and not _base_exists(git_root, base, token)
|
|
259
401
|
):
|
|
260
402
|
problems.append(f"## {name} names a path missing at the layer base: {token}")
|
|
261
403
|
|
|
@@ -265,9 +407,20 @@ def _lint_task(
|
|
|
265
407
|
if block is None or not block.group("block").strip():
|
|
266
408
|
problems.append("## Verify must contain a non-empty fenced bash block")
|
|
267
409
|
else:
|
|
268
|
-
for token in _verify_tokens(
|
|
410
|
+
for token in _verify_tokens(
|
|
411
|
+
block.group("block"),
|
|
412
|
+
repo=repo,
|
|
413
|
+
git_root=git_root,
|
|
414
|
+
base=base,
|
|
415
|
+
task=path,
|
|
416
|
+
):
|
|
269
417
|
checked += 1
|
|
270
|
-
if
|
|
418
|
+
if (
|
|
419
|
+
token not in declared
|
|
420
|
+
and token not in supplied
|
|
421
|
+
and not _pipeline_workspace_file(repo, path, token)
|
|
422
|
+
and not _base_exists(git_root, base, token)
|
|
423
|
+
):
|
|
271
424
|
problems.append(f"## Verify names a path missing at the layer base: {token}")
|
|
272
425
|
|
|
273
426
|
contract = None
|
|
@@ -284,12 +437,13 @@ def validate_task_briefs(
|
|
|
284
437
|
root: Path, base: str, tasks_dir: str = DEFAULT_TASKS_DIR,
|
|
285
438
|
*, dependency_files: Optional[Dict[str, Set[str]]] = None,
|
|
286
439
|
landed_bases: Optional[Dict[str, str]] = None,
|
|
440
|
+
member_base=None,
|
|
287
441
|
) -> Dict[str, object]:
|
|
288
442
|
"""Lint task briefs using the caller's validation bases.
|
|
289
443
|
|
|
290
|
-
|
|
291
|
-
transitive dependency files for
|
|
292
|
-
for landed tasks, whose immutable briefs must survive later file changes.
|
|
444
|
+
Plain lint uses the layer base without overrides. Plan approval and dispatch
|
|
445
|
+
supply transitive dependency files for unlanded tasks and resolved historical
|
|
446
|
+
bases for landed tasks, whose immutable briefs must survive later file changes.
|
|
293
447
|
The caller validates the task graph and landed metadata before supplying
|
|
294
448
|
these mappings; landed tasks receive no dependency-file allowance.
|
|
295
449
|
"""
|
|
@@ -305,9 +459,11 @@ def validate_task_briefs(
|
|
|
305
459
|
task_files = sorted(tasks_path.glob("*.md"))
|
|
306
460
|
if not task_files:
|
|
307
461
|
raise BriefError(f"no task briefs found in {tasks_dir}")
|
|
462
|
+
if member_base is None:
|
|
463
|
+
member_base = _member_bases(root)
|
|
308
464
|
for path in task_files:
|
|
309
465
|
task_id, task_problems, contract, task_checked = _lint_task(
|
|
310
|
-
root, resolved_base, path, dependency_files, landed_bases
|
|
466
|
+
root, resolved_base, path, dependency_files, landed_bases, member_base
|
|
311
467
|
)
|
|
312
468
|
problems.extend(f"{task_id}: {problem}" for problem in task_problems)
|
|
313
469
|
if contract is not None:
|
|
@@ -329,6 +485,136 @@ def validate_task_briefs(
|
|
|
329
485
|
return {"base": resolved_base, "checked": checked, "tasks": len(task_files)}
|
|
330
486
|
|
|
331
487
|
|
|
488
|
+
def _resolve_landed_task_base(
|
|
489
|
+
task_id: str,
|
|
490
|
+
recorded_base: str,
|
|
491
|
+
recorded_member_base: object,
|
|
492
|
+
coordinator_repo: Path,
|
|
493
|
+
member_repo: Optional[Path],
|
|
494
|
+
) -> str:
|
|
495
|
+
"""Resolve a landed task's historical base in the repository it names."""
|
|
496
|
+
if __package__:
|
|
497
|
+
from .isolation import IsolationError, require_commit, require_full_sha
|
|
498
|
+
else:
|
|
499
|
+
try:
|
|
500
|
+
from isolation import IsolationError, require_commit, require_full_sha
|
|
501
|
+
except ModuleNotFoundError as error:
|
|
502
|
+
if error.name != "isolation":
|
|
503
|
+
raise
|
|
504
|
+
from scripts.isolation import IsolationError, require_commit, require_full_sha
|
|
505
|
+
|
|
506
|
+
recorded_full = require_full_sha(recorded_base)
|
|
507
|
+
if member_repo is None:
|
|
508
|
+
try:
|
|
509
|
+
return require_commit(coordinator_repo, recorded_full)
|
|
510
|
+
except IsolationError as error:
|
|
511
|
+
raise BriefError(
|
|
512
|
+
f"{task_id} landed task has invalid historical base: {error}"
|
|
513
|
+
) from error
|
|
514
|
+
|
|
515
|
+
legacy_member_base = (
|
|
516
|
+
recorded_member_base is None
|
|
517
|
+
or (isinstance(recorded_member_base, str)
|
|
518
|
+
and recorded_member_base.strip().casefold() in {"", "null", "~"})
|
|
519
|
+
or (isinstance(recorded_member_base, list) and not recorded_member_base)
|
|
520
|
+
)
|
|
521
|
+
try:
|
|
522
|
+
if not legacy_member_base:
|
|
523
|
+
if not isinstance(recorded_member_base, str):
|
|
524
|
+
raise IsolationError(
|
|
525
|
+
f"member_base must be a full 40-character SHA: {recorded_member_base}"
|
|
526
|
+
)
|
|
527
|
+
member_full = require_full_sha(recorded_member_base)
|
|
528
|
+
return require_commit(member_repo, member_full)
|
|
529
|
+
try:
|
|
530
|
+
return require_commit(coordinator_repo, recorded_full)
|
|
531
|
+
except IsolationError:
|
|
532
|
+
return require_commit(member_repo, recorded_full)
|
|
533
|
+
except IsolationError as error:
|
|
534
|
+
raise BriefError(
|
|
535
|
+
f"{task_id} landed task has invalid historical base: {error}"
|
|
536
|
+
) from error
|
|
537
|
+
|
|
538
|
+
def plan_validation_bases(
|
|
539
|
+
root: Path, project_dir: str, member_base=None, *, initial: bool = True,
|
|
540
|
+
) -> Tuple[Dict[str, str], Dict[str, Set[str]], Dict[str, str]]:
|
|
541
|
+
"""Share dependency files and landed historical bases across approval and dispatch.
|
|
542
|
+
|
|
543
|
+
Approval requires unlanded tasks to start clean. Dispatch may run beside
|
|
544
|
+
in-flight tasks, while retaining the same graph and path checks.
|
|
545
|
+
"""
|
|
546
|
+
if __package__:
|
|
547
|
+
from . import check_handoffs
|
|
548
|
+
from .isolation import IsolationError
|
|
549
|
+
else:
|
|
550
|
+
try:
|
|
551
|
+
import check_handoffs
|
|
552
|
+
from isolation import IsolationError
|
|
553
|
+
except ModuleNotFoundError as error:
|
|
554
|
+
if error.name not in {"check_handoffs", "isolation"}:
|
|
555
|
+
raise
|
|
556
|
+
from scripts import check_handoffs
|
|
557
|
+
from scripts.isolation import IsolationError
|
|
558
|
+
if member_base is None:
|
|
559
|
+
member_base = _member_bases(root)
|
|
560
|
+
try:
|
|
561
|
+
tasks, dependency_files = check_handoffs.plan_brief_inputs(
|
|
562
|
+
root, project_dir, initial=initial
|
|
563
|
+
)
|
|
564
|
+
landed_bases: Dict[str, str] = {}
|
|
565
|
+
for task_id, text in tasks.items():
|
|
566
|
+
if check_handoffs._task_scalar(text, task_id, "status") != "done":
|
|
567
|
+
continue
|
|
568
|
+
agent = check_handoffs._task_scalar(text, task_id, "agent")
|
|
569
|
+
if agent in {"", "null"}:
|
|
570
|
+
raise BriefError(f"{task_id} landed task has no recorded agent")
|
|
571
|
+
fields = check_handoffs._strict_frontmatter(text, task_id)
|
|
572
|
+
member = fields.get("repo")
|
|
573
|
+
base_repo = root
|
|
574
|
+
if member is not None:
|
|
575
|
+
located = member_base(member) if isinstance(member, str) and member else None
|
|
576
|
+
if located is None:
|
|
577
|
+
raise BriefError(f"repo: names no member in MEMBERS.md: {member}")
|
|
578
|
+
base_repo = located[0]
|
|
579
|
+
recorded_base = check_handoffs._task_scalar(text, task_id, "base")
|
|
580
|
+
try:
|
|
581
|
+
landed_bases[task_id] = _resolve_landed_task_base(
|
|
582
|
+
task_id,
|
|
583
|
+
recorded_base,
|
|
584
|
+
fields.get("member_base") if member is not None else None,
|
|
585
|
+
root,
|
|
586
|
+
base_repo if member is not None else None,
|
|
587
|
+
)
|
|
588
|
+
except IsolationError as error:
|
|
589
|
+
raise BriefError(f"{task_id} landed task has invalid historical base: {error}") from error
|
|
590
|
+
dependency_files[task_id] = set()
|
|
591
|
+
except check_handoffs.HandoffError as error:
|
|
592
|
+
raise BriefError(str(error)) from error
|
|
593
|
+
return tasks, dependency_files, landed_bases
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def validate_plan_task_briefs(
|
|
597
|
+
root: Path,
|
|
598
|
+
base: str,
|
|
599
|
+
project_dir: str = ".project",
|
|
600
|
+
tasks_dir: Optional[str] = None,
|
|
601
|
+
) -> Dict[str, object]:
|
|
602
|
+
"""Dispatch lint with dependency files and landed historical bases."""
|
|
603
|
+
member_base = _member_bases(root)
|
|
604
|
+
_, dependency_files, landed_bases = plan_validation_bases(
|
|
605
|
+
root, project_dir, member_base, initial=False
|
|
606
|
+
)
|
|
607
|
+
resolved_tasks_dir = tasks_dir or f"{project_dir}/tasks"
|
|
608
|
+
return validate_task_briefs(
|
|
609
|
+
root,
|
|
610
|
+
base,
|
|
611
|
+
resolved_tasks_dir,
|
|
612
|
+
dependency_files=dependency_files,
|
|
613
|
+
landed_bases=landed_bases,
|
|
614
|
+
member_base=member_base,
|
|
615
|
+
)
|
|
616
|
+
|
|
617
|
+
|
|
332
618
|
def _tasks_dir(value: str) -> str:
|
|
333
619
|
"""Argparse type for --tasks-dir: a relative POSIX-style path."""
|
|
334
620
|
|
|
@@ -359,15 +645,37 @@ def parser() -> argparse.ArgumentParser:
|
|
|
359
645
|
help="relative POSIX-style directory under --repo holding the task files "
|
|
360
646
|
"(default: .project/tasks)",
|
|
361
647
|
)
|
|
648
|
+
argument_parser.add_argument(
|
|
649
|
+
"--project-dir",
|
|
650
|
+
default=None,
|
|
651
|
+
help="apply plan dependency files and landed-task bases for this track "
|
|
652
|
+
"(inferred from --tasks-dir when its sibling plan/PLAN.md exists)",
|
|
653
|
+
)
|
|
362
654
|
return argument_parser
|
|
363
655
|
|
|
364
656
|
|
|
365
657
|
def main(argv: Optional[Sequence[str]] = None) -> int:
|
|
366
658
|
arguments = parser().parse_args(argv)
|
|
367
659
|
try:
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
660
|
+
root = arguments.repo.resolve()
|
|
661
|
+
project_dir = arguments.project_dir
|
|
662
|
+
if project_dir is None:
|
|
663
|
+
tasks_path = PurePosixPath(arguments.tasks_dir)
|
|
664
|
+
if tasks_path.name == "tasks" and len(tasks_path.parts) > 1:
|
|
665
|
+
track = tasks_path.parent.as_posix()
|
|
666
|
+
if (root / track / "plan/PLAN.md").is_file():
|
|
667
|
+
project_dir = track
|
|
668
|
+
if project_dir is not None:
|
|
669
|
+
result = validate_plan_task_briefs(
|
|
670
|
+
root,
|
|
671
|
+
arguments.base,
|
|
672
|
+
project_dir,
|
|
673
|
+
None if arguments.tasks_dir == DEFAULT_TASKS_DIR else arguments.tasks_dir,
|
|
674
|
+
)
|
|
675
|
+
else:
|
|
676
|
+
result = validate_task_briefs(
|
|
677
|
+
root, arguments.base, arguments.tasks_dir
|
|
678
|
+
)
|
|
371
679
|
except BriefError as error:
|
|
372
680
|
print(f"task brief validation failed: {error}", file=sys.stderr)
|
|
373
681
|
return 1
|