@opengsd/gsd-path 1.3.0 → 1.4.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 +294 -0
- package/DOCS.md +19 -10
- package/FULL.md +9 -3
- package/GUIDE.md +1 -1
- package/HOOKS.md +9 -6
- package/QUICK.md +4 -1
- package/README.md +63 -11
- package/RUNTIME.md +1 -1
- package/UPDATE.md +61 -12
- package/WORKFLOW.md +22 -6
- package/package.json +9 -8
- package/scripts/_common.py +472 -19
- package/scripts/archive_milestone.py +146 -73
- package/scripts/bootstrap_repository.py +8 -5
- package/scripts/build_recovery.py +4 -1
- package/scripts/build_state.py +66 -23
- package/scripts/check_docs_audit.py +14 -3
- package/scripts/check_handoffs.py +116 -20
- package/scripts/check_task_briefs.py +60 -8
- 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 +345 -15
- package/scripts/discussion_records.py +19 -13
- package/scripts/discussion_validate.py +66 -21
- package/scripts/dispatch_driver.py +111 -26
- package/scripts/git_guard.py +116 -29
- package/scripts/guard_hook.py +151 -29
- package/scripts/install.mjs +55 -39
- package/scripts/install.py +564 -41
- package/scripts/integration.py +434 -24
- package/scripts/isolation.py +1077 -28
- package/scripts/lean_verification.py +99 -19
- package/scripts/loop_run.py +20 -30
- package/scripts/members.py +704 -0
- package/scripts/migrate_core.py +2 -2
- package/scripts/path_config.py +2 -2
- package/scripts/pipeline_diagnose.py +82 -31
- package/scripts/pipeline_git.py +61 -22
- package/scripts/pipeline_state.py +176 -54
- package/scripts/pipeline_undo.py +117 -22
- package/scripts/promote_lookahead.py +1 -1
- package/scripts/review_panel.py +1 -1
- package/scripts/runtime_store.py +49 -11
- package/scripts/skill-resources.json +28 -3
- package/scripts/state_checkpoint.py +140 -31
- package/scripts/state_promote.py +2 -0
- package/scripts/status_runtime.py +36 -6
- package/scripts/sync_skill_resources.py +8 -7
- package/scripts/token_budget.py +12 -8
- package/scripts/workflow_run.py +103 -29
- package/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path/BUILD.md +7 -1
- package/skills/gsd-path/DECIDE.md +11 -4
- package/skills/gsd-path/DEFINE.md +8 -1
- package/skills/gsd-path/DOCS-AUDIT.md +6 -4
- package/skills/gsd-path/INSPECT.md +5 -4
- package/skills/gsd-path/PLAN.md +7 -1
- package/skills/gsd-path/RESEARCH.md +2 -1
- package/skills/gsd-path/ROADMAP.md +2 -1
- package/skills/gsd-path/SHIP.md +27 -10
- package/skills/gsd-path/SKILL.md +45 -1
- package/skills/gsd-path/references/build-native.md +47 -14
- package/skills/gsd-path/references/codebase-mapper.md +2 -1
- package/skills/gsd-path/references/docs-auditor.md +2 -1
- package/skills/gsd-path/references/operating-rules.md +214 -0
- package/skills/gsd-path/references/planner.md +8 -0
- package/skills/gsd-path/references/reviewer-wave.md +3 -1
- 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/scripts/_common.py +472 -19
- package/skills/gsd-path/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path/scripts/bootstrap_repository.py +8 -5
- package/skills/gsd-path/scripts/build_recovery.py +4 -1
- package/skills/gsd-path/scripts/build_state.py +66 -23
- package/skills/gsd-path/scripts/check_docs_audit.py +14 -3
- package/skills/gsd-path/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path/scripts/check_update.py +2 -2
- package/skills/gsd-path/scripts/detect_project.py +345 -15
- package/skills/gsd-path/scripts/discussion_records.py +19 -13
- package/skills/gsd-path/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path/scripts/dispatch_driver.py +111 -26
- package/skills/gsd-path/scripts/git_guard.py +116 -29
- package/skills/gsd-path/scripts/integration.py +434 -24
- package/skills/gsd-path/scripts/isolation.py +1077 -28
- package/skills/gsd-path/scripts/lean_verification.py +99 -19
- package/skills/gsd-path/scripts/loop_run.py +20 -30
- package/skills/gsd-path/scripts/members.py +704 -0
- package/skills/gsd-path/scripts/path_config.py +2 -2
- package/skills/gsd-path/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path/scripts/state_promote.py +2 -0
- package/skills/gsd-path/scripts/status_runtime.py +36 -6
- package/skills/gsd-path/scripts/token_budget.py +12 -8
- package/skills/gsd-path/scripts/workflow_run.py +103 -29
- package/skills/gsd-path/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path/templates/codebase.md +1 -1
- package/skills/gsd-path/templates/docs-audit.md +1 -1
- package/skills/gsd-path/templates/final-review.md +5 -2
- package/skills/gsd-path/templates/gap-review.md +2 -1
- package/skills/gsd-path/templates/task.md +1 -0
- package/skills/gsd-path/templates/wave-review.md +3 -1
- package/skills/gsd-path-build/SKILL.md +7 -1
- package/skills/gsd-path-build/references/build-native.md +47 -14
- package/skills/gsd-path-build/references/operating-rules.md +214 -0
- package/skills/gsd-path-build/references/reviewer-wave.md +3 -1
- 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-build/scripts/_common.py +472 -19
- package/skills/gsd-path-build/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-build/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-build/scripts/build_state.py +66 -23
- package/skills/gsd-path-build/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-build/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-build/scripts/detect_project.py +345 -15
- package/skills/gsd-path-build/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-build/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-build/scripts/dispatch_driver.py +111 -26
- package/skills/gsd-path-build/scripts/integration.py +434 -24
- package/skills/gsd-path-build/scripts/isolation.py +1077 -28
- package/skills/gsd-path-build/scripts/loop_run.py +20 -30
- package/skills/gsd-path-build/scripts/members.py +704 -0
- package/skills/gsd-path-build/scripts/path_config.py +2 -2
- package/skills/gsd-path-build/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-build/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path-build/scripts/state_promote.py +2 -0
- package/skills/gsd-path-build/scripts/token_budget.py +12 -8
- package/skills/gsd-path-build/scripts/workflow_run.py +103 -29
- package/skills/gsd-path-build/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-build/templates/task.md +1 -0
- package/skills/gsd-path-build/templates/wave-review.md +3 -1
- package/skills/gsd-path-decide/SKILL.md +11 -4
- package/skills/gsd-path-decide/references/operating-rules.md +214 -0
- package/skills/gsd-path-decide/scripts/_common.py +472 -19
- package/skills/gsd-path-decide/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-decide/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-decide/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-decide/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-decide/scripts/detect_project.py +345 -15
- package/skills/gsd-path-decide/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-decide/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-decide/scripts/integration.py +434 -24
- package/skills/gsd-path-decide/scripts/isolation.py +1077 -28
- package/skills/gsd-path-decide/scripts/loop_run.py +20 -30
- package/skills/gsd-path-decide/scripts/members.py +704 -0
- package/skills/gsd-path-decide/scripts/path_config.py +2 -2
- package/skills/gsd-path-decide/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-decide/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path-decide/scripts/state_promote.py +2 -0
- package/skills/gsd-path-decide/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-define/SKILL.md +8 -1
- package/skills/gsd-path-define/references/operating-rules.md +214 -0
- package/skills/gsd-path-define/scripts/_common.py +472 -19
- package/skills/gsd-path-define/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-define/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-define/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-define/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-define/scripts/detect_project.py +345 -15
- package/skills/gsd-path-define/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-define/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-define/scripts/integration.py +434 -24
- package/skills/gsd-path-define/scripts/isolation.py +1077 -28
- package/skills/gsd-path-define/scripts/loop_run.py +20 -30
- package/skills/gsd-path-define/scripts/members.py +704 -0
- package/skills/gsd-path-define/scripts/path_config.py +2 -2
- package/skills/gsd-path-define/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-define/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path-define/scripts/state_promote.py +2 -0
- package/skills/gsd-path-define/scripts/worktree_paths.py +18 -8
- 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 +472 -19
- package/skills/gsd-path-discuss/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-discuss/scripts/loop_run.py +20 -30
- package/skills/gsd-path-discuss/scripts/path_config.py +2 -2
- package/skills/gsd-path-discuss/scripts/review_panel.py +1 -1
- package/skills/gsd-path-discuss/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-docs-audit/SKILL.md +6 -4
- package/skills/gsd-path-docs-audit/references/docs-auditor.md +2 -1
- package/skills/gsd-path-docs-audit/references/operating-rules.md +214 -0
- package/skills/gsd-path-docs-audit/scripts/_common.py +472 -19
- package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-docs-audit/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +14 -3
- package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-docs-audit/scripts/detect_project.py +345 -15
- package/skills/gsd-path-docs-audit/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-docs-audit/scripts/integration.py +434 -24
- package/skills/gsd-path-docs-audit/scripts/isolation.py +1077 -28
- package/skills/gsd-path-docs-audit/scripts/loop_run.py +20 -30
- package/skills/gsd-path-docs-audit/scripts/members.py +704 -0
- package/skills/gsd-path-docs-audit/scripts/path_config.py +2 -2
- package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path-docs-audit/scripts/state_promote.py +2 -0
- package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-docs-audit/templates/docs-audit.md +1 -1
- 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 +472 -19
- package/skills/gsd-path-forensics/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-forensics/scripts/loop_run.py +20 -30
- package/skills/gsd-path-forensics/scripts/path_config.py +2 -2
- package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +82 -31
- package/skills/gsd-path-forensics/scripts/pipeline_undo.py +117 -22
- package/skills/gsd-path-forensics/scripts/review_panel.py +1 -1
- package/skills/gsd-path-forensics/scripts/worktree_paths.py +18 -8
- 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/docs-auditor.md +2 -1
- package/skills/gsd-path-inspect/references/operating-rules.md +214 -0
- package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-inspect/scripts/_common.py +472 -19
- package/skills/gsd-path-inspect/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-inspect/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-inspect/scripts/check_docs_audit.py +14 -3
- package/skills/gsd-path-inspect/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-inspect/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-inspect/scripts/detect_project.py +345 -15
- package/skills/gsd-path-inspect/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-inspect/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-inspect/scripts/integration.py +434 -24
- package/skills/gsd-path-inspect/scripts/isolation.py +1077 -28
- package/skills/gsd-path-inspect/scripts/loop_run.py +20 -30
- package/skills/gsd-path-inspect/scripts/members.py +704 -0
- package/skills/gsd-path-inspect/scripts/path_config.py +2 -2
- package/skills/gsd-path-inspect/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-inspect/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path-inspect/scripts/state_promote.py +2 -0
- package/skills/gsd-path-inspect/scripts/workflow_run.py +103 -29
- package/skills/gsd-path-inspect/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-inspect/templates/codebase.md +1 -1
- package/skills/gsd-path-inspect/templates/docs-audit.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 +472 -19
- package/skills/gsd-path-loop/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-loop/scripts/loop_run.py +20 -30
- package/skills/gsd-path-loop/scripts/path_config.py +2 -2
- package/skills/gsd-path-loop/scripts/review_panel.py +1 -1
- package/skills/gsd-path-loop/scripts/worktree_paths.py +18 -8
- 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 +472 -19
- 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 +20 -30
- package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -2
- package/skills/gsd-path-migrate/scripts/path_config.py +2 -2
- package/skills/gsd-path-migrate/scripts/review_panel.py +1 -1
- package/skills/gsd-path-plan/SKILL.md +7 -1
- package/skills/gsd-path-plan/references/operating-rules.md +214 -0
- package/skills/gsd-path-plan/references/planner.md +8 -0
- package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-plan/scripts/_common.py +472 -19
- package/skills/gsd-path-plan/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-plan/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-plan/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-plan/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-plan/scripts/detect_project.py +345 -15
- package/skills/gsd-path-plan/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-plan/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-plan/scripts/integration.py +434 -24
- package/skills/gsd-path-plan/scripts/isolation.py +1077 -28
- package/skills/gsd-path-plan/scripts/loop_run.py +20 -30
- package/skills/gsd-path-plan/scripts/members.py +704 -0
- package/skills/gsd-path-plan/scripts/path_config.py +2 -2
- package/skills/gsd-path-plan/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-plan/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path-plan/scripts/state_promote.py +2 -0
- package/skills/gsd-path-plan/scripts/workflow_run.py +103 -29
- package/skills/gsd-path-plan/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-plan/templates/task.md +1 -0
- package/skills/gsd-path-research/SKILL.md +2 -1
- package/skills/gsd-path-research/references/operating-rules.md +214 -0
- package/skills/gsd-path-research/scripts/_common.py +472 -19
- package/skills/gsd-path-research/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-research/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-research/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-research/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-research/scripts/detect_project.py +345 -15
- package/skills/gsd-path-research/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-research/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-research/scripts/integration.py +434 -24
- package/skills/gsd-path-research/scripts/isolation.py +1077 -28
- package/skills/gsd-path-research/scripts/loop_run.py +20 -30
- package/skills/gsd-path-research/scripts/members.py +704 -0
- package/skills/gsd-path-research/scripts/path_config.py +2 -2
- package/skills/gsd-path-research/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-research/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path-research/scripts/state_promote.py +2 -0
- package/skills/gsd-path-research/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-roadmap/SKILL.md +2 -1
- package/skills/gsd-path-roadmap/references/operating-rules.md +214 -0
- package/skills/gsd-path-roadmap/scripts/_common.py +472 -19
- package/skills/gsd-path-roadmap/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-roadmap/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-roadmap/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-roadmap/scripts/detect_project.py +345 -15
- package/skills/gsd-path-roadmap/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-roadmap/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-roadmap/scripts/integration.py +434 -24
- package/skills/gsd-path-roadmap/scripts/isolation.py +1077 -28
- package/skills/gsd-path-roadmap/scripts/loop_run.py +20 -30
- package/skills/gsd-path-roadmap/scripts/members.py +704 -0
- package/skills/gsd-path-roadmap/scripts/path_config.py +2 -2
- package/skills/gsd-path-roadmap/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-roadmap/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path-roadmap/scripts/state_promote.py +2 -0
- package/skills/gsd-path-roadmap/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-ship/SKILL.md +27 -10
- package/skills/gsd-path-ship/references/operating-rules.md +214 -0
- package/skills/gsd-path-ship/references/reviewer-wave.md +3 -1
- package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
- package/skills/gsd-path-ship/scripts/_common.py +472 -19
- package/skills/gsd-path-ship/scripts/archive_milestone.py +146 -73
- package/skills/gsd-path-ship/scripts/build_recovery.py +4 -1
- package/skills/gsd-path-ship/scripts/build_state.py +66 -23
- package/skills/gsd-path-ship/scripts/check_handoffs.py +116 -20
- package/skills/gsd-path-ship/scripts/check_task_briefs.py +60 -8
- package/skills/gsd-path-ship/scripts/detect_project.py +345 -15
- package/skills/gsd-path-ship/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-ship/scripts/discussion_validate.py +66 -21
- package/skills/gsd-path-ship/scripts/integration.py +434 -24
- package/skills/gsd-path-ship/scripts/isolation.py +1077 -28
- package/skills/gsd-path-ship/scripts/lean_verification.py +99 -19
- package/skills/gsd-path-ship/scripts/loop_run.py +20 -30
- package/skills/gsd-path-ship/scripts/members.py +704 -0
- package/skills/gsd-path-ship/scripts/path_config.py +2 -2
- package/skills/gsd-path-ship/scripts/pipeline_git.py +61 -22
- package/skills/gsd-path-ship/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/gsd-path-ship/scripts/state_promote.py +2 -0
- package/skills/gsd-path-ship/scripts/workflow_run.py +103 -29
- package/skills/gsd-path-ship/scripts/worktree_paths.py +18 -8
- package/skills/gsd-path-ship/templates/final-review.md +5 -2
- package/skills/gsd-path-ship/templates/gap-review.md +2 -1
- package/skills/gsd-path-ship/templates/wave-review.md +3 -1
- 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 +472 -19
- package/skills/gsd-path-undo/scripts/discussion_records.py +19 -13
- package/skills/gsd-path-undo/scripts/loop_run.py +20 -30
- package/skills/gsd-path-undo/scripts/path_config.py +2 -2
- package/skills/gsd-path-undo/scripts/pipeline_undo.py +117 -22
- package/skills/gsd-path-undo/scripts/review_panel.py +1 -1
- package/skills/gsd-path-undo/scripts/worktree_paths.py +18 -8
- package/skills/path/BUILD.md +7 -1
- package/skills/path/DECIDE.md +11 -4
- package/skills/path/DEFINE.md +8 -1
- package/skills/path/DOCS-AUDIT.md +6 -4
- package/skills/path/INSPECT.md +5 -4
- package/skills/path/PLAN.md +7 -1
- package/skills/path/RESEARCH.md +2 -1
- package/skills/path/ROADMAP.md +2 -1
- package/skills/path/SHIP.md +27 -10
- package/skills/path/SKILL.md +45 -1
- package/skills/path/references/build-native.md +47 -14
- package/skills/path/references/codebase-mapper.md +2 -1
- package/skills/path/references/docs-auditor.md +2 -1
- package/skills/path/references/operating-rules.md +214 -0
- package/skills/path/references/planner.md +8 -0
- package/skills/path/references/reviewer-wave.md +3 -1
- package/skills/path/scripts/_common.py +472 -19
- package/skills/path/scripts/archive_milestone.py +146 -73
- package/skills/path/scripts/bootstrap_repository.py +8 -5
- package/skills/path/scripts/build_recovery.py +4 -1
- package/skills/path/scripts/build_state.py +66 -23
- package/skills/path/scripts/check_docs_audit.py +14 -3
- package/skills/path/scripts/check_handoffs.py +116 -20
- package/skills/path/scripts/check_task_briefs.py +60 -8
- package/skills/path/scripts/check_update.py +2 -2
- package/skills/path/scripts/detect_project.py +345 -15
- package/skills/path/scripts/discussion_records.py +19 -13
- package/skills/path/scripts/discussion_validate.py +66 -21
- package/skills/path/scripts/dispatch_driver.py +111 -26
- package/skills/path/scripts/git_guard.py +116 -29
- package/skills/path/scripts/integration.py +434 -24
- package/skills/path/scripts/isolation.py +1077 -28
- package/skills/path/scripts/lean_verification.py +99 -19
- package/skills/path/scripts/loop_run.py +20 -30
- package/skills/path/scripts/members.py +704 -0
- package/skills/path/scripts/path_config.py +2 -2
- package/skills/path/scripts/pipeline_git.py +61 -22
- package/skills/path/scripts/pipeline_state.py +176 -54
- 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 +140 -31
- package/skills/path/scripts/state_promote.py +2 -0
- package/skills/path/scripts/status_runtime.py +36 -6
- package/skills/path/scripts/token_budget.py +12 -8
- package/skills/path/scripts/workflow_run.py +103 -29
- package/skills/path/scripts/worktree_paths.py +18 -8
- package/skills/path/templates/codebase.md +1 -1
- package/skills/path/templates/docs-audit.md +1 -1
- package/skills/path/templates/final-review.md +5 -2
- package/skills/path/templates/gap-review.md +2 -1
- package/skills/path/templates/task.md +1 -0
- package/skills/path/templates/wave-review.md +3 -1
|
@@ -13,12 +13,16 @@ sys.dont_write_bytecode = True
|
|
|
13
13
|
from pathlib import Path, PurePosixPath
|
|
14
14
|
from typing import Dict, List, Optional, Sequence, Set, Tuple
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
from pipeline_state import PipelineStateError, load_state
|
|
18
|
-
import _common
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
if __package__:
|
|
17
|
+
from .pipeline_state import PipelineStateError, load_state
|
|
18
|
+
from . import _common
|
|
19
|
+
else:
|
|
20
|
+
try:
|
|
21
|
+
from pipeline_state import PipelineStateError, load_state
|
|
22
|
+
import _common
|
|
23
|
+
except ImportError: # pragma: no cover - package imports used by tests
|
|
24
|
+
from scripts.pipeline_state import PipelineStateError, load_state
|
|
25
|
+
from scripts import _common
|
|
22
26
|
|
|
23
27
|
|
|
24
28
|
PIPELINE = "gsd-path/v2"
|
|
@@ -377,7 +381,10 @@ def validate_research(
|
|
|
377
381
|
) -> Dict[str, object]:
|
|
378
382
|
"""Validate the research-to-synthesis hand-off and return its summary."""
|
|
379
383
|
|
|
380
|
-
|
|
384
|
+
# Research runs this before closing; decide runs it after its entry transition.
|
|
385
|
+
_require_state_one_of(
|
|
386
|
+
root, {("research", "active"), ("decide", "active")}, project_dir
|
|
387
|
+
)
|
|
381
388
|
return validate_research_artifacts(root, project_dir)
|
|
382
389
|
|
|
383
390
|
|
|
@@ -548,6 +555,45 @@ def _reviewed_head(text: str, source: str) -> str:
|
|
|
548
555
|
return value
|
|
549
556
|
|
|
550
557
|
|
|
558
|
+
MEMBER_HEAD_PATTERN = re.compile(r"(?m)^Member reviewed HEAD:(.*)$")
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def member_reviewed_heads(text: str, source: str) -> Dict[str, str]:
|
|
562
|
+
"""`Member reviewed HEAD: <member> <sha>` lines, one per member of a multi-repo milestone."""
|
|
563
|
+
heads: Dict[str, str] = {}
|
|
564
|
+
# Only the header binds heads; command output quoted in later sections cannot add one.
|
|
565
|
+
for value in MEMBER_HEAD_PATTERN.findall(_strip_comments(text).split("\n## ", 1)[0]):
|
|
566
|
+
name, _, sha = value.strip().partition(" ")
|
|
567
|
+
if not name or not SHA_PATTERN.fullmatch(sha.strip()):
|
|
568
|
+
raise HandoffError(
|
|
569
|
+
f"{source} Member reviewed HEAD must be '<member> <full commit SHA>', not {value.strip() or 'empty'}"
|
|
570
|
+
)
|
|
571
|
+
if name in heads:
|
|
572
|
+
raise HandoffError(f"{source} repeats Member reviewed HEAD for {name}")
|
|
573
|
+
heads[name] = sha.strip()
|
|
574
|
+
return heads
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def _member_tips(root: Path) -> Dict[str, str]:
|
|
578
|
+
"""Each locked member's bound branch tip; empty when the milestone locks no member."""
|
|
579
|
+
try:
|
|
580
|
+
import isolation
|
|
581
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
582
|
+
from scripts import isolation
|
|
583
|
+
lock = root / ".project/build/members.json"
|
|
584
|
+
if not lock.is_file():
|
|
585
|
+
return {}
|
|
586
|
+
tips: Dict[str, str] = {}
|
|
587
|
+
try:
|
|
588
|
+
for item in json.loads(lock.read_text(encoding="utf-8"))["members"]:
|
|
589
|
+
checkout, _, entry = isolation._member_context(root, item["name"])
|
|
590
|
+
tips[entry["name"]] = isolation.git_output(
|
|
591
|
+
checkout, "rev-parse", "--verify", f"refs/heads/{entry['branch']}^{{commit}}")
|
|
592
|
+
except isolation.IsolationError as error:
|
|
593
|
+
raise HandoffError(str(error)) from error
|
|
594
|
+
return tips
|
|
595
|
+
|
|
596
|
+
|
|
551
597
|
def _strip_comments(text: str) -> str:
|
|
552
598
|
return COMMENT_PATTERN.sub("", text)
|
|
553
599
|
|
|
@@ -750,6 +796,8 @@ def _task_texts(root: Path, project_dir: str) -> Dict[str, str]:
|
|
|
750
796
|
raise HandoffError(f"missing {project_dir}/tasks")
|
|
751
797
|
tasks: Dict[str, str] = {}
|
|
752
798
|
for path in sorted(tasks_dir.iterdir()):
|
|
799
|
+
if _common.is_ignored_junk(path):
|
|
800
|
+
continue
|
|
753
801
|
named = TASK_FILE_NAME.fullmatch(path.name)
|
|
754
802
|
if named is None or not path.is_file() or path.is_symlink():
|
|
755
803
|
raise HandoffError(f"non-canonical task artifact: {path.name}")
|
|
@@ -1008,7 +1056,10 @@ def _validate_task_graph(
|
|
|
1008
1056
|
graph: Dict[str, List[str]] = {}
|
|
1009
1057
|
task_waves: Dict[str, int] = {}
|
|
1010
1058
|
task_files: Dict[str, List[str]] = {}
|
|
1059
|
+
# `repo:` names the member a task changes; absent means the coordinator.
|
|
1060
|
+
task_repos: Dict[str, str] = {}
|
|
1011
1061
|
for task_id, text in tasks.items():
|
|
1062
|
+
task_repos[task_id] = str(_strict_frontmatter(text, task_id).get("repo") or "")
|
|
1012
1063
|
# Patch mode reopens planning beside landed tasks; only new tasks start clean.
|
|
1013
1064
|
landed = _task_scalar(text, task_id, "status") == "done"
|
|
1014
1065
|
_require_task_structure(task_id, text, initial=initial and not landed)
|
|
@@ -1028,7 +1079,7 @@ def _validate_task_graph(
|
|
|
1028
1079
|
|
|
1029
1080
|
for index, left in enumerate(task_ids):
|
|
1030
1081
|
for right in task_ids[index + 1 :]:
|
|
1031
|
-
if task_waves[left] != task_waves[right]:
|
|
1082
|
+
if task_waves[left] != task_waves[right] or task_repos[left] != task_repos[right]:
|
|
1032
1083
|
continue
|
|
1033
1084
|
overlap = sorted(set(task_files[left]) & set(task_files[right]))
|
|
1034
1085
|
if overlap:
|
|
@@ -1039,6 +1090,7 @@ def _validate_task_graph(
|
|
|
1039
1090
|
visiting: Set[str] = set()
|
|
1040
1091
|
visited: Set[str] = set()
|
|
1041
1092
|
dependency_files: Dict[str, Set[str]] = {}
|
|
1093
|
+
dependency_pairs: Dict[str, Set[Tuple[str, str]]] = {}
|
|
1042
1094
|
|
|
1043
1095
|
def visit(task_id: str) -> None:
|
|
1044
1096
|
if task_id in visiting:
|
|
@@ -1046,11 +1098,15 @@ def _validate_task_graph(
|
|
|
1046
1098
|
if task_id in visited:
|
|
1047
1099
|
return
|
|
1048
1100
|
visiting.add(task_id)
|
|
1049
|
-
|
|
1101
|
+
dependency_pairs[task_id] = set()
|
|
1050
1102
|
for dependency in graph[task_id]:
|
|
1051
1103
|
visit(dependency)
|
|
1052
|
-
|
|
1053
|
-
|
|
1104
|
+
dependency_pairs[task_id].update((task_repos[dependency], name) for name in task_files[dependency])
|
|
1105
|
+
dependency_pairs[task_id].update(dependency_pairs[dependency])
|
|
1106
|
+
# A dependency's files are supplied only within the same repo.
|
|
1107
|
+
dependency_files[task_id] = {
|
|
1108
|
+
name for repo, name in dependency_pairs[task_id] if repo == task_repos[task_id]
|
|
1109
|
+
}
|
|
1054
1110
|
visiting.remove(task_id)
|
|
1055
1111
|
visited.add(task_id)
|
|
1056
1112
|
|
|
@@ -1212,7 +1268,7 @@ def _head_file(root: Path, relative: str) -> Optional[str]:
|
|
|
1212
1268
|
["git", "show", f"HEAD:{relative}"],
|
|
1213
1269
|
cwd=root,
|
|
1214
1270
|
capture_output=True,
|
|
1215
|
-
|
|
1271
|
+
encoding="utf-8", errors="replace",
|
|
1216
1272
|
check=False,
|
|
1217
1273
|
)
|
|
1218
1274
|
return result.stdout if result.returncode == 0 else None
|
|
@@ -1313,6 +1369,21 @@ def validate_roadmap(
|
|
|
1313
1369
|
return {"phase": "roadmap", "milestones": ids, "dependencies": dependencies}
|
|
1314
1370
|
|
|
1315
1371
|
|
|
1372
|
+
def _integrated_members(root: Path, state: Dict[str, str]) -> Set[str]:
|
|
1373
|
+
"""Members this milestone already closed; a new task for one would need a second merge."""
|
|
1374
|
+
match = re.fullmatch(r"gsd-path/M(\d{3,})", state.get("branch") or "")
|
|
1375
|
+
common = subprocess.run(["git", "-C", str(root), "rev-parse", "--path-format=absolute", "--git-common-dir"],
|
|
1376
|
+
capture_output=True, text=True, check=False)
|
|
1377
|
+
if match is None or common.returncode != 0:
|
|
1378
|
+
return set()
|
|
1379
|
+
if __package__:
|
|
1380
|
+
from . import members
|
|
1381
|
+
else:
|
|
1382
|
+
import members
|
|
1383
|
+
rows = members.milestone_member_close(Path(common.stdout.strip()), int(match.group(1)))
|
|
1384
|
+
return {row["name"] for row in rows if row.get("status") == "integrated"}
|
|
1385
|
+
|
|
1386
|
+
|
|
1316
1387
|
def validate_plan(
|
|
1317
1388
|
root: Path, project_dir: str = DEFAULT_PROJECT_DIR
|
|
1318
1389
|
) -> Dict[str, object]:
|
|
@@ -1332,6 +1403,14 @@ def validate_plan(
|
|
|
1332
1403
|
# Build readiness owns task lifecycle validation; coverage must survive landings.
|
|
1333
1404
|
state = _require_pipeline(root, project_dir)
|
|
1334
1405
|
_validate_task_graph(wave_depths, tasks, initial=state["phase"] == "plan")
|
|
1406
|
+
integrated = _integrated_members(root, state)
|
|
1407
|
+
for task_id, text in tasks.items():
|
|
1408
|
+
repo = str(_strict_frontmatter(text, task_id).get("repo") or "")
|
|
1409
|
+
if repo in integrated and _task_scalar(text, task_id, "status") != "done":
|
|
1410
|
+
raise HandoffError(
|
|
1411
|
+
f"{task_id} changes member {repo}, which is already integrated in this milestone; "
|
|
1412
|
+
"plan that change in a new milestone"
|
|
1413
|
+
)
|
|
1335
1414
|
assigned: Dict[str, Set[str]] = {task_id: set() for task_id in tasks}
|
|
1336
1415
|
covered = set()
|
|
1337
1416
|
for criterion, task_id, acceptance in rows:
|
|
@@ -1591,6 +1670,19 @@ def validate_wave_evidence(
|
|
|
1591
1670
|
}
|
|
1592
1671
|
|
|
1593
1672
|
|
|
1673
|
+
def criterion_surfaces(root: Path, project_dir: str = DEFAULT_PROJECT_DIR) -> Dict[str, str]:
|
|
1674
|
+
"""Map each surface-owned INTENT SC to the surface PLAN.md's contract lists it under."""
|
|
1675
|
+
|
|
1676
|
+
surface_of: Dict[str, str] = {}
|
|
1677
|
+
surfaces = _surfaces(_read(root, _intent_path(project_dir)), "INTENT.md")
|
|
1678
|
+
if surfaces:
|
|
1679
|
+
plan = _read(root, f"{project_dir}/plan/PLAN.md")
|
|
1680
|
+
for surface, (_task, owned) in _surface_contract(plan, surfaces).items():
|
|
1681
|
+
for criterion in owned:
|
|
1682
|
+
surface_of[criterion] = surface
|
|
1683
|
+
return surface_of
|
|
1684
|
+
|
|
1685
|
+
|
|
1594
1686
|
def validate_final(
|
|
1595
1687
|
root: Path, project_dir: str = DEFAULT_PROJECT_DIR, *, final_text: Optional[str] = None
|
|
1596
1688
|
) -> Dict[str, object]:
|
|
@@ -1599,20 +1691,14 @@ def validate_final(
|
|
|
1599
1691
|
_require_state(root, "ship", "active", project_dir)
|
|
1600
1692
|
intent = _read(root, _intent_path(project_dir))
|
|
1601
1693
|
criteria = _success_criteria(intent)
|
|
1602
|
-
surface_of
|
|
1603
|
-
surfaces = _surfaces(intent, "INTENT.md")
|
|
1604
|
-
if surfaces:
|
|
1605
|
-
plan = _read(root, f"{project_dir}/plan/PLAN.md")
|
|
1606
|
-
for surface, (_task, owned) in _surface_contract(plan, surfaces).items():
|
|
1607
|
-
for criterion in owned:
|
|
1608
|
-
surface_of[criterion] = surface
|
|
1694
|
+
surface_of = criterion_surfaces(root, project_dir)
|
|
1609
1695
|
relative = f"{project_dir}/review/FINAL.md"
|
|
1610
1696
|
text = _read(root, relative) if final_text is None else final_text
|
|
1611
1697
|
reviewed_head = _reviewed_head(text, relative)
|
|
1612
1698
|
head_result = subprocess.run(
|
|
1613
1699
|
("git", "-C", str(root), "rev-parse", "--verify", "HEAD"),
|
|
1614
1700
|
capture_output=True,
|
|
1615
|
-
|
|
1701
|
+
encoding="utf-8", errors="replace",
|
|
1616
1702
|
check=False,
|
|
1617
1703
|
)
|
|
1618
1704
|
if head_result.returncode != 0:
|
|
@@ -1623,6 +1709,14 @@ def validate_final(
|
|
|
1623
1709
|
f"FINAL.md Reviewed HEAD {reviewed_head} is not the current HEAD "
|
|
1624
1710
|
f"{current_head}; re-run the final review on the current commit"
|
|
1625
1711
|
)
|
|
1712
|
+
member_heads = member_reviewed_heads(text, relative)
|
|
1713
|
+
tips = _member_tips(root)
|
|
1714
|
+
if member_heads != tips:
|
|
1715
|
+
expected = ", ".join(f"{name} {tip}" for name, tip in tips.items()) or "none"
|
|
1716
|
+
raise HandoffError(
|
|
1717
|
+
f"FINAL.md Member reviewed HEAD lines must name each locked member head ({expected}); "
|
|
1718
|
+
"re-run the final review on the current member heads"
|
|
1719
|
+
)
|
|
1626
1720
|
overall = _line_value(text, "Overall verdict:")
|
|
1627
1721
|
if overall not in {"pass", "blocked"}:
|
|
1628
1722
|
raise HandoffError("FINAL.md Overall verdict is invalid")
|
|
@@ -1697,6 +1791,8 @@ def validate_final(
|
|
|
1697
1791
|
raise HandoffError(f"{gap_relative} heading does not match its file number")
|
|
1698
1792
|
if _reviewed_head(gap, gap_relative) != reviewed_head:
|
|
1699
1793
|
raise HandoffError(f"{gap_relative} Reviewed HEAD differs from FINAL.md")
|
|
1794
|
+
if member_reviewed_heads(gap, gap_relative) != member_heads:
|
|
1795
|
+
raise HandoffError(f"{gap_relative} Member reviewed HEAD lines differ from FINAL.md")
|
|
1700
1796
|
verdict = _line_value(gap, "Gap verdict:")
|
|
1701
1797
|
if verdict not in {"pass", "blocked"}:
|
|
1702
1798
|
raise HandoffError(f"{gap_relative} Gap verdict is invalid")
|
|
@@ -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"
|
|
@@ -183,10 +186,47 @@ def _verify_tokens(block: str) -> List[str]:
|
|
|
183
186
|
return tokens
|
|
184
187
|
|
|
185
188
|
|
|
189
|
+
def _member_bases(root: Path):
|
|
190
|
+
"""Resolve a member name to (checkout, origin/main SHA), reading MEMBERS.md once."""
|
|
191
|
+
cache: Dict[str, Optional[Tuple[Path, str]]] = {}
|
|
192
|
+
|
|
193
|
+
def locate(name: str) -> Optional[Tuple[Path, str]]:
|
|
194
|
+
if name not in cache:
|
|
195
|
+
if __package__:
|
|
196
|
+
from . import members
|
|
197
|
+
else:
|
|
198
|
+
try:
|
|
199
|
+
import members
|
|
200
|
+
except ImportError: # pragma: no cover - package import used by tests
|
|
201
|
+
from scripts import members
|
|
202
|
+
try:
|
|
203
|
+
listed = {member["name"]: Path(member["checkout"]) for member in members.read_members(root)}
|
|
204
|
+
checkout = listed.get(name)
|
|
205
|
+
if checkout is None:
|
|
206
|
+
cache[name] = None
|
|
207
|
+
else:
|
|
208
|
+
# A build lock selects the bound tip, including during Plan recovery;
|
|
209
|
+
# without the lock, use origin/main before the first build start.
|
|
210
|
+
lock = root / members.LOCK_PATH
|
|
211
|
+
locked = [entry for entry in (json.loads(lock.read_text(encoding="utf-8"))["members"]
|
|
212
|
+
if lock.is_file() else []) if entry["name"] == name]
|
|
213
|
+
if locked:
|
|
214
|
+
cache[name] = (checkout, _resolve_base(checkout, f"refs/heads/{locked[0]['branch']}"))
|
|
215
|
+
else:
|
|
216
|
+
members.require_origin_main(checkout)
|
|
217
|
+
cache[name] = (checkout, _resolve_base(checkout, "refs/remotes/origin/main"))
|
|
218
|
+
except (members.MembersError, BriefError) as error:
|
|
219
|
+
raise BriefError(f"member {name} is unavailable: {error}") from error
|
|
220
|
+
return cache[name]
|
|
221
|
+
|
|
222
|
+
return locate
|
|
223
|
+
|
|
224
|
+
|
|
186
225
|
def _lint_task(
|
|
187
226
|
repo: Path, base: str, path: Path,
|
|
188
227
|
dependency_files: Optional[Dict[str, Set[str]]] = None,
|
|
189
228
|
landed_bases: Optional[Dict[str, str]] = None,
|
|
229
|
+
member_base=None,
|
|
190
230
|
) -> Tuple[str, List[str], Optional[str], int]:
|
|
191
231
|
text = path.read_text(encoding="utf-8")
|
|
192
232
|
fields, error = _frontmatter(text)
|
|
@@ -198,6 +238,15 @@ def _lint_task(
|
|
|
198
238
|
checked = 0
|
|
199
239
|
if fields is None:
|
|
200
240
|
return task_id, [error or "invalid frontmatter"], None, checked
|
|
241
|
+
# A member task's paths resolve in the member at its origin/main, where the
|
|
242
|
+
# member bound branch starts; `repo:` absent means the coordinator.
|
|
243
|
+
git_root = repo
|
|
244
|
+
member = fields.get("repo")
|
|
245
|
+
if member is not None:
|
|
246
|
+
located = member_base(member) if isinstance(member, str) and member and member_base else None
|
|
247
|
+
if located is None:
|
|
248
|
+
return task_id, [f"repo: names no member in MEMBERS.md: {member}"], None, checked
|
|
249
|
+
git_root, base = located[0], (landed_bases or {}).get(task_id, located[1])
|
|
201
250
|
|
|
202
251
|
for field in REQUIRED_FIELDS:
|
|
203
252
|
if field not in fields:
|
|
@@ -237,7 +286,7 @@ def _lint_task(
|
|
|
237
286
|
for parent in candidate.parents:
|
|
238
287
|
if str(parent) == ".":
|
|
239
288
|
break
|
|
240
|
-
kind = _run_git(
|
|
289
|
+
kind = _run_git(git_root, "cat-file", "-t", f"{base}:{parent}")
|
|
241
290
|
if kind.returncode == 0 and kind.stdout.strip() != "tree":
|
|
242
291
|
problems.append(
|
|
243
292
|
f"files entry {normalized} has non-directory ancestor "
|
|
@@ -255,7 +304,7 @@ def _lint_task(
|
|
|
255
304
|
token not in declared
|
|
256
305
|
and token not in supplied
|
|
257
306
|
and not _supplied_contract(repo, path, token)
|
|
258
|
-
and not _base_exists(
|
|
307
|
+
and not _base_exists(git_root, base, token)
|
|
259
308
|
):
|
|
260
309
|
problems.append(f"## {name} names a path missing at the layer base: {token}")
|
|
261
310
|
|
|
@@ -267,7 +316,7 @@ def _lint_task(
|
|
|
267
316
|
else:
|
|
268
317
|
for token in _verify_tokens(block.group("block")):
|
|
269
318
|
checked += 1
|
|
270
|
-
if token not in declared and token not in supplied and not _base_exists(
|
|
319
|
+
if token not in declared and token not in supplied and not _base_exists(git_root, base, token):
|
|
271
320
|
problems.append(f"## Verify names a path missing at the layer base: {token}")
|
|
272
321
|
|
|
273
322
|
contract = None
|
|
@@ -284,6 +333,7 @@ def validate_task_briefs(
|
|
|
284
333
|
root: Path, base: str, tasks_dir: str = DEFAULT_TASKS_DIR,
|
|
285
334
|
*, dependency_files: Optional[Dict[str, Set[str]]] = None,
|
|
286
335
|
landed_bases: Optional[Dict[str, str]] = None,
|
|
336
|
+
member_base=None,
|
|
287
337
|
) -> Dict[str, object]:
|
|
288
338
|
"""Lint task briefs using the caller's validation bases.
|
|
289
339
|
|
|
@@ -305,9 +355,11 @@ def validate_task_briefs(
|
|
|
305
355
|
task_files = sorted(tasks_path.glob("*.md"))
|
|
306
356
|
if not task_files:
|
|
307
357
|
raise BriefError(f"no task briefs found in {tasks_dir}")
|
|
358
|
+
if member_base is None:
|
|
359
|
+
member_base = _member_bases(root)
|
|
308
360
|
for path in task_files:
|
|
309
361
|
task_id, task_problems, contract, task_checked = _lint_task(
|
|
310
|
-
root, resolved_base, path, dependency_files, landed_bases
|
|
362
|
+
root, resolved_base, path, dependency_files, landed_bases, member_base
|
|
311
363
|
)
|
|
312
364
|
problems.extend(f"{task_id}: {problem}" for problem in task_problems)
|
|
313
365
|
if contract is not None:
|