@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.20

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.
Files changed (264) hide show
  1. package/out/mcp-server.js +1149 -661
  2. package/package.json +3 -2
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +28 -6
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +20 -2
  5. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +17 -4
  6. package/resources/claude-customizations/.claude/agents/epic-planner.md +5 -4
  7. package/resources/claude-customizations/.claude/agents/feature-review.md +6 -4
  8. package/resources/claude-customizations/.claude/agents/orchestrator.md +9 -5
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +35 -13
  10. package/resources/claude-customizations/.claude/agents/parallel-planner.md +9 -6
  11. package/resources/claude-customizations/.claude/agents/pr-author.md +15 -2
  12. package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -1
  13. package/resources/claude-customizations/.claude/agents/task-researcher.md +8 -0
  14. package/resources/claude-customizations/.claude/hooks/check-powershell-test-purity.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  17. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
  18. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +78 -77
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
  25. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
  26. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
  27. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
  28. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
  29. package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
  30. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
  31. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
  32. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  33. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  34. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  35. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
  36. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
  37. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
  38. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
  39. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
  40. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  41. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
  42. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
  43. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
  44. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
  45. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
  46. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
  47. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
  48. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
  49. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
  50. package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
  51. package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  52. package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
  53. package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
  54. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
  55. package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
  56. package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
  57. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
  58. package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
  59. package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +497 -0
  60. package/resources/claude-customizations/.claude/hooks/premortem-section.ps1 +149 -0
  61. package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
  62. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
  63. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
  64. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +267 -113
  65. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
  66. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
  67. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
  68. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
  69. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
  70. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
  71. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +77 -33
  72. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
  73. package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
  74. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
  75. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +52 -36
  76. package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
  78. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
  79. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  80. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  81. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  82. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  83. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  84. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  85. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 +203 -0
  86. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  87. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
  88. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  89. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  90. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  91. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  92. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  93. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  94. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
  95. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  96. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +85 -10
  97. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  98. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  99. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  100. package/resources/claude-customizations/.claude/settings.json +3 -4
  101. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  102. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +52 -16
  103. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +7 -3
  104. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  105. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
  106. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
  107. package/resources/claude-customizations/.claude/skills/csharp-change-budget-router/SKILL.md +2 -2
  108. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +45 -9
  109. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +5 -3
  110. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  111. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +134 -0
  112. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  113. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
  114. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +36 -15
  115. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  116. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  117. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +108 -21
  118. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  119. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  120. package/resources/claude-customizations/.claude/skills/powershell-change-budget-router/SKILL.md +1 -1
  121. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
  122. package/resources/claude-customizations/.claude/skills/python-change-budget-router/SKILL.md +1 -1
  123. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
  124. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +16 -8
  125. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +6 -0
  126. package/resources/claude-customizations/pack-manifests/core.json +27 -0
  127. package/resources/codex-and-agents-customizations/.agents/skills/README.md +1 -0
  128. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  129. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  130. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +19 -7
  131. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +43 -4
  132. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +18 -6
  133. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  134. package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +2 -2
  135. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  136. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +16 -1
  137. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  138. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  139. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  140. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +134 -0
  141. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
  142. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  143. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  144. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +27 -8
  145. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
  146. package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -1
  147. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -1
  148. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  149. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  150. package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +6 -0
  151. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  152. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  153. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  154. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +21 -6
  155. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +21 -6
  156. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +21 -6
  157. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +21 -6
  158. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +21 -6
  159. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +21 -6
  160. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +15 -2
  161. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +15 -2
  162. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +15 -2
  163. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +15 -2
  164. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +15 -2
  165. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +15 -2
  166. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planning.toml +1 -1
  167. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  168. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +4 -1
  169. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  170. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +7 -4
  171. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +7 -4
  172. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +7 -4
  173. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +7 -4
  174. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +7 -4
  175. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +7 -4
  176. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  177. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  178. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c1.toml +6 -0
  179. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c2.toml +6 -0
  180. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3-elevated.toml +6 -0
  181. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c3.toml +6 -0
  182. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher-c4.toml +6 -0
  183. package/resources/codex-and-agents-customizations/.codex/agents/task-researcher.toml +6 -0
  184. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  185. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  186. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  187. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  188. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  189. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  190. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  191. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  192. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  193. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  194. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-command.ps1 +190 -0
  195. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +54 -43
  196. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  197. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  198. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  199. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  200. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  201. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  202. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  203. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  204. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  205. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  206. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  207. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  208. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  209. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  210. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  211. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  212. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  213. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  214. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  215. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  216. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  217. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  218. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +497 -0
  219. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  220. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  221. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  222. package/resources/codex-and-agents-customizations/.codex/prompts/feature-review-remediate.md +6 -4
  223. package/resources/codex-and-agents-customizations/pack-manifests/core.json +11 -0
  224. package/resources/customizations/.github/agents/atomic_executor.agent.md +21 -11
  225. package/resources/customizations/.github/agents/atomic_planning.agent.md +8 -3
  226. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +13 -9
  227. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  228. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +2 -2
  229. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +5 -5
  230. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -5
  231. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +13 -9
  232. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +2 -2
  233. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +2 -2
  234. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +3 -3
  235. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +13 -9
  236. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  237. package/resources/customizations/.github/agents/python-orchestrator.agent.md +2 -2
  238. package/resources/customizations/.github/agents/task-researcher.agent.md +6 -0
  239. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  240. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  241. package/resources/customizations/.github/prompts/generate-atomic-plan.prompt.md +16 -10
  242. package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +2 -2
  243. package/resources/customizations/.github/prompts/orchestrate-powershell-work.prompt.md +1 -1
  244. package/resources/customizations/.github/prompts/orchestrate-python-work.prompt.md +1 -1
  245. package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +2 -2
  246. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  247. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +43 -4
  248. package/resources/customizations/.github/skills/csharp-change-budget-router/SKILL.md +1 -1
  249. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  250. package/resources/customizations/.github/skills/execution-environment/SKILL.md +134 -0
  251. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  252. package/resources/customizations/.github/skills/powershell-change-budget-router/SKILL.md +1 -1
  253. package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +7 -3
  254. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +10 -0
  255. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +11 -0
  256. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +11 -0
  257. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  258. package/resources/powershell/PoshQC/PoshQC.CoverageMerge.psm1 +163 -0
  259. package/resources/powershell/PoshQC/PoshQC.Shard.psm1 +296 -0
  260. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +48 -32
  261. package/resources/powershell/PoshQC/PoshQC.psd1 +7 -1
  262. package/resources/powershell/PoshQC/PoshQC.psm1 +18 -5
  263. package/resources/powershell/PoshQC/README.md +21 -3
  264. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +5 -319
@@ -68,31 +68,23 @@ $script:PlanTaskPattern = [regex]::new(
68
68
  # on its own line, from producing spurious spans.
69
69
  $script:InlineCodeSpanPattern = [regex]::new('`([^`]+)`')
70
70
 
71
- # Top-level directories of this repository. A token starting with one of these is
72
- # accepted without needing a recognized extension, which admits ** globs naming a
73
- # subtree. artifacts/ is deliberately absent (issue #489): the process-artifact
74
- # tree is read by mandate rather than written by a work item, so admitting
75
- # artifacts/** as a subtree claim made unrelated items contend.
71
+ # Top-level directories of this repository. A wildcard token starting with one of
72
+ # these is accepted without its final component naming a file (issue #797), which
73
+ # admits ** globs naming a subtree. artifacts/ is deliberately absent (issue
74
+ # #489): the process-artifact tree is read by mandate rather than written by a
75
+ # work item, so admitting artifacts/** as a subtree claim made unrelated items
76
+ # contend.
76
77
  $script:KnownTopLevelSegment = @(
77
78
  'scripts/', 'tests/', 'docs/', 'config/', 'schemas/', 'packages/',
78
79
  'extensions/', '.claude/', '.codex/', '.github/', '.agents/'
79
80
  )
80
81
 
81
82
  # A file citation may carry a trailing line reference such as :90. The suffix is
82
- # stripped before the extension test so a line-anchored citation keeps the
83
- # acceptance its unanchored form has; the token itself is recorded verbatim.
83
+ # stripped before the file-shape test (issue #797) so a line-anchored citation
84
+ # keeps the acceptance its unanchored form has; the token itself is recorded
85
+ # verbatim.
84
86
  $script:LineSuffixPattern = [regex]::new(':\d+$')
85
87
 
86
- # Fallback acceptance rule: a token shaped <segment>/.../<name>.<ext> counts as a
87
- # repository path when its final component carries one of these extensions.
88
- $script:RecognizedPathExtension = [System.Collections.Generic.HashSet[string]]::new(
89
- [string[]] @(
90
- 'cfg', 'cs', 'csproj', 'ini', 'js', 'json', 'jsx', 'lock', 'md', 'ps1',
91
- 'psd1', 'psm1', 'py', 'sh', 'sln', 'toml', 'ts', 'tsx', 'txt', 'xml',
92
- 'yaml', 'yml'
93
- ),
94
- [StringComparer]::Ordinal)
95
-
96
88
  # Classification vocabulary for accepted path tokens. Concrete entries take part
97
89
  # in exact-match checks; glob entries cannot and are matched by pattern.
98
90
  $script:PathKindConcrete = 'concrete'
@@ -249,8 +241,11 @@ function Get-PathTokenKind {
249
241
  bare word such as a function name is a contract identifier, not a path.
250
242
  It must also be repository-relative: a leading separator marks an absolute
251
243
  path and a colon in the leading segment marks a URL scheme or a Windows
252
- drive. Acceptance then requires one of the two documented shape rules, a
253
- known top-level segment or a recognized final extension.
244
+ drive. A wildcard-free token is then accepted only when its final
245
+ component, after any :<line> suffix is stripped, names a file according
246
+ to Test-FileShapedComponent (a letter-led extension or a known file
247
+ name, issue #797); a wildcard token needs a known top-level segment or
248
+ such a final component.
254
249
 
255
250
  A token carrying any configured placeholder or interpolation marker is
256
251
  rejected wherever the marker sits, because it documents a shape rather
@@ -305,7 +300,7 @@ function Get-PathTokenKind {
305
300
  # disagree, and putting the cheaper marker scan first would only add work to
306
301
  # the common accepted case. It runs BEFORE the separator guard because a
307
302
  # marker-bearing token frequently does carry a separator and would otherwise
308
- # sail past that guard and reach the extension rule, which accepts it: the
303
+ # sail past that guard and reach the file-shape rule, which accepts it: the
309
304
  # dominant corpus shape is an angle-bracketed leading segment followed by a
310
305
  # real .md tail, and that is exactly the token this guard exists to reject.
311
306
  if (Test-PlaceholderMarker -Token $Token) {
@@ -320,26 +315,20 @@ function Get-PathTokenKind {
320
315
  return $null
321
316
  }
322
317
 
323
- # Read the final component's extension for the fallback acceptance rule; a
324
- # component with no dot (a directory name or **) has no extension. A trailing
325
- # line reference is stripped first so file.md:90 reads as md rather than as
326
- # the unrecognized extension md:90.
318
+ # Decide whether the final component names a file (issue #797): a letter-led
319
+ # extension or a known file name does; a directory name, a dot-directory, **,
320
+ # a digit-led tail, and a trailing dot do not. A trailing line reference is
321
+ # stripped first so file.md:90 reads as file.md rather than as md:90.
327
322
  $finalComponent = $script:LineSuffixPattern.Replace(
328
323
  $Token.Substring($Token.LastIndexOf('/') + 1), '')
329
- $extension = ''
330
- $dotIndex = $finalComponent.LastIndexOf('.')
331
- if ($dotIndex -ge 0) {
332
- $extension = $finalComponent.Substring($dotIndex + 1).ToLowerInvariant()
333
- }
334
-
335
- $hasExtension = $script:RecognizedPathExtension.Contains($extension)
324
+ $namesFile = Test-FileShapedComponent -Component $finalComponent
336
325
 
337
326
  # A wildcard-free token must name a file, not a directory (issue #489). A
338
327
  # directory-shaped token such as scripts/dev_tools is a location reference,
339
328
  # not a write claim, and admitting it made every item touching anything under
340
329
  # that directory contend at the path level.
341
330
  if ($Token.IndexOf('*') -lt 0) {
342
- if ($hasExtension) {
331
+ if ($namesFile) {
343
332
  return $script:PathKindConcrete
344
333
  }
345
334
  return $null
@@ -356,7 +345,7 @@ function Get-PathTokenKind {
356
345
  # A wildcard-bearing token must still satisfy one of the two documented shape
357
346
  # rules; failing both means the token is prose or a non-path expression that
358
347
  # merely contains a separator, so it is dropped.
359
- if (-not $hasKnownSegment -and -not $hasExtension) {
348
+ if (-not $hasKnownSegment -and -not $namesFile) {
360
349
  return $null
361
350
  }
362
351
 
@@ -469,6 +458,7 @@ Export-ModuleMember -Function `
469
458
  Get-InlineCodeToken, `
470
459
  Test-PlaceholderMarker, `
471
460
  Test-MultipleFeatureFolderSpan, `
461
+ Test-FileShapedComponent, `
472
462
  Get-PathTokenKind, `
473
463
  Get-PathFromLine, `
474
464
  Get-PlanPaths
@@ -44,11 +44,17 @@
44
44
  exactly the two members that matter most.
45
45
  - Substring search uses [System.StringComparison]::Ordinal so the result
46
46
  is culture-independent and matches Python's byte-wise 'in' operator.
47
- - Both predicates are total on every string, including the empty string, a
48
- token consisting only of a marker, and a bare bracket pair. Neither
49
- throws for any input, because the classifier that calls them runs over
50
- every inline-code span in a document and a throw would abort an entire
51
- derivation over one stray span.
47
+ - Every predicate is total on every string, including the empty string, a
48
+ token consisting only of a marker, a bare bracket pair, and a lone dot.
49
+ None throws for any input, because the classifier that calls them runs
50
+ over every inline-code span in a document and a throw would abort an
51
+ entire derivation over one stray span.
52
+ - Test-FileShapedComponent (issue #797) ports is_file_shaped_component.
53
+ Its extension pattern text is identical to FILE_EXTENSION_PATTERN_TEXT
54
+ and its known-name set equals KNOWN_FILE_NAMES; a Pester test reads both
55
+ from the Python source and pins them equal. A non-ASCII extension is
56
+ rejected before lower-casing (issue #903); ToLowerInvariant then matches
57
+ Python's str.lower on ASCII input, and known-name membership is ordinal.
52
58
  CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
53
59
  #>
54
60
 
@@ -84,6 +90,30 @@ $script:PlaceholderMarker = [string[]]@(
84
90
  $script:FeatureCorpusPrefix = 'docs/features/'
85
91
  $script:FeatureFolderSegmentIndex = 1
86
92
 
93
+ # File-shape extension pattern (issue #797). A final component names a file when
94
+ # the text after its last dot, lower-cased, is an ASCII letter followed by ASCII
95
+ # letters or digits. The pattern text is identical to FILE_EXTENSION_PATTERN_TEXT
96
+ # in the Python reference; \A and \z anchor the whole string without admitting a
97
+ # trailing newline, which matches Python's re.fullmatch. The regex is built with
98
+ # no options, so matching is case-sensitive and the explicit ASCII classes match
99
+ # exactly the inputs the Python pattern matches.
100
+ $script:FileExtensionPatternText = '[a-z][a-z0-9]*'
101
+ $script:FileExtensionPattern = [regex]::new('\A' + $script:FileExtensionPatternText + '\z')
102
+ $script:NonAsciiPattern = [regex]::new('[^\x00-\x7F]')
103
+
104
+ # Known extensionless file names and dotfiles (issue #797). Membership is exact
105
+ # and ordinal, matching Python's frozenset membership. The set is closed and is
106
+ # pinned equal to KNOWN_FILE_NAMES in the Python reference by a Pester parity
107
+ # test that reads the Python source, so edit both files together.
108
+ $script:KnownFileName = [System.Collections.Generic.HashSet[string]]::new(
109
+ [string[]] @(
110
+ 'Dockerfile', 'Makefile', 'LICENSE', 'CODEOWNERS', 'NOTICE', '.gitignore',
111
+ '.gitattributes', '.gitkeep', '.gitmodules', '.vscodeignore', '.npmignore',
112
+ '.npmrc', '.nvmrc', '.editorconfig', '.prettierrc', '.prettierignore',
113
+ '.eslintignore', '.shellcheckrc'
114
+ ),
115
+ [StringComparer]::Ordinal)
116
+
87
117
  function Test-PlaceholderMarker {
88
118
  <#
89
119
  .SYNOPSIS
@@ -184,6 +214,56 @@ function Test-MultipleFeatureFolderSpan {
184
214
  return $false
185
215
  }
186
216
 
217
+ function Test-FileShapedComponent {
218
+ <#
219
+ .SYNOPSIS
220
+ Report whether a final path component names a file (issue #797).
221
+
222
+ .DESCRIPTION
223
+ Port of is_file_shaped_component. The classifier formerly admitted a
224
+ wildcard-free token only when its extension appeared in a fixed
225
+ allowlist, so files a plan writes with an unlisted extension, dotfiles,
226
+ and extensionless names were dropped from the derived radius. This
227
+ predicate replaces the allowlist with a structural rule that still
228
+ rejects the directory-shaped tokens of issue #489: a component with no
229
+ dot, a dot-leading component outside the known-name set, a digit-led
230
+ tail, and a trailing dot all report false.
231
+
232
+ .PARAMETER Component
233
+ The final path component, after any :<line> suffix has been stripped.
234
+ The empty string is accepted and reports false.
235
+
236
+ .OUTPUTS
237
+ System.Boolean. True when the component is an ordinal member of the
238
+ known-name set, or when it has a non-empty stem before its last dot and
239
+ the lower-cased text after that dot fully matches the extension pattern;
240
+ otherwise false.
241
+ #>
242
+ [CmdletBinding()]
243
+ [OutputType([bool])]
244
+ param(
245
+ [Parameter(Mandatory = $true)]
246
+ [AllowEmptyString()]
247
+ [string] $Component
248
+ )
249
+
250
+ if ($script:KnownFileName.Contains($Component)) {
251
+ return $true
252
+ }
253
+
254
+ # LastIndexOf returns -1 when there is no dot and 0 when the dot leads the
255
+ # component; both leave no stem, so neither names a file by this rule.
256
+ $dotIndex = $Component.LastIndexOf('.')
257
+ if ($dotIndex -le 0) {
258
+ return $false
259
+ }
260
+
261
+ $extension = $Component.Substring($dotIndex + 1)
262
+ if ($script:NonAsciiPattern.IsMatch($extension)) { return $false }
263
+ return $script:FileExtensionPattern.IsMatch($extension.ToLowerInvariant())
264
+ }
265
+
187
266
  Export-ModuleMember -Function `
188
267
  Test-PlaceholderMarker, `
189
- Test-MultipleFeatureFolderSpan
268
+ Test-MultipleFeatureFolderSpan, `
269
+ Test-FileShapedComponent
@@ -5,7 +5,7 @@
5
5
 
6
6
  .DESCRIPTION
7
7
  Consumes the JSON array emitted by
8
- `gh pr checks --required --json bucket,name,state,link,workflow` and produces
8
+ `gh pr checks --json bucket,name,state,link,workflow` and produces
9
9
  the `ci_gate` object the orchestrator writes to its checkpoint. The object
10
10
  carries the five contract fields: head_sha, pr_pipeline_run_id,
11
11
  pr_pipeline_run_url, conclusion, and verified_at.
@@ -17,25 +17,33 @@
17
17
  the logic is deterministically testable.
18
18
 
19
19
  Conclusion derivation (per the documented bucket mapping):
20
- - Any required check with bucket 'fail' or 'cancel' -> conclusion 'failure'
21
- - Else any required check with bucket 'pending' -> conclusion 'pending'
20
+ - Any check with bucket 'fail' or 'cancel' -> conclusion 'failure'
21
+ - Else any check with bucket 'pending' -> conclusion 'pending'
22
22
  - Else (all checks 'pass' or 'skipping', or empty set) -> conclusion 'success'
23
- An empty required-check set is treated as 'success' (vacuously satisfied: no
24
- required check can fail or be in progress). An unrecognized bucket value is a
23
+ An empty check set is treated as 'success' (vacuously satisfied: no
24
+ check can fail or be in progress). An unrecognized bucket value is a
25
25
  fail-fast error so the parser never silently passes an enum it does not
26
26
  understand.
27
27
 
28
+ Epic-child guard (-RequireWorkflow): when -RequireWorkflow names a workflow
29
+ (S9 passes 'CI' when the checkpoint epic_mode is true), the empty-set rule
30
+ above does not apply. Failure and pending precedence is unchanged over every
31
+ observed check; the conclusion is 'success' only when at least one observed
32
+ check whose workflow property equals the name (case-sensitive) has bucket
33
+ 'pass', and 'pending' otherwise. An empty or null check set therefore yields
34
+ 'pending'. The default value ('') keeps the behavior described above.
35
+
28
36
  The verified_at timestamp is produced through an injectable clock delegate
29
37
  (-NowProvider) so tests can assert an exact deterministic value without
30
38
  reading the wall clock.
31
39
 
32
40
  .PARAMETER ChecksJson
33
- The JSON array string emitted by `gh pr checks --required --json ...`. Each
41
+ The JSON array string emitted by `gh pr checks --json ...`. Each
34
42
  element is expected to expose at least a `bucket` property. Accepts pipeline
35
43
  / stdin input so the orchestrator can pipe the `gh` output directly.
36
44
 
37
45
  .PARAMETER HeadSha
38
- The PR head SHA the required checks were observed against. Recorded verbatim
46
+ The PR head SHA the queried checks were observed against. Recorded verbatim
39
47
  as ci_gate.head_sha.
40
48
 
41
49
  .PARAMETER PrPipelineRunId
@@ -51,15 +59,26 @@
51
59
  ci_gate.verified_at. Defaults to a UTC Get-Date formatted as ISO-8601. Tests
52
60
  inject a fixed delegate to make verified_at deterministic.
53
61
 
62
+ .PARAMETER RequireWorkflow
63
+ Optional workflow name (default ''). When non-empty, the conclusion is
64
+ 'success' only after at least one check whose `workflow` property equals
65
+ this value (case-sensitive) has bucket 'pass'; an empty or null check set
66
+ yields 'pending'. S9 passes 'CI' for an epic-child PR. A whitespace-only
67
+ value is rejected with an error.
68
+
54
69
  .PARAMETER AsJson
55
70
  When set, emits the ci_gate object as a JSON string instead of a PowerShell
56
71
  object. The default (object) output is convenient for in-process callers; the
57
72
  JSON output is convenient for checkpoint serialization.
58
73
 
59
74
  .EXAMPLE
60
- gh pr checks --required --json bucket,name,state,link,workflow |
75
+ gh pr checks --json bucket,name,state,link,workflow |
61
76
  ./.claude/lib/ci-gate/Invoke-CiGateParser.ps1 -HeadSha $sha
62
77
 
78
+ .EXAMPLE
79
+ gh pr checks --json bucket,name,state,link,workflow |
80
+ ./.claude/lib/ci-gate/Invoke-CiGateParser.ps1 -HeadSha $sha -RequireWorkflow CI
81
+
63
82
  .OUTPUTS
64
83
  A PSCustomObject (or JSON string when -AsJson is set) with properties
65
84
  head_sha, pr_pipeline_run_id, pr_pipeline_run_url, conclusion, verified_at.
@@ -84,6 +103,9 @@ param(
84
103
  [ValidateNotNull()]
85
104
  [ScriptBlock]$NowProvider = { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') },
86
105
 
106
+ [Parameter(Mandatory = $false)]
107
+ [string]$RequireWorkflow = '',
108
+
87
109
  [Parameter(Mandatory = $false)]
88
110
  [switch]$AsJson
89
111
  )
@@ -96,7 +118,7 @@ begin {
96
118
  function Get-CiGateConclusion {
97
119
  <#
98
120
  .SYNOPSIS
99
- Pure derivation of ci_gate.conclusion from a parsed required-check set.
121
+ Pure derivation of ci_gate.conclusion from a parsed check set.
100
122
 
101
123
  .DESCRIPTION
102
124
  Implements the documented bucket precedence with no I/O and no external
@@ -109,9 +131,20 @@ begin {
109
131
  bucket value outside the known enum throws a fail-fast error naming the
110
132
  unrecognized value, preventing a silent pass on an unknown state.
111
133
 
134
+ With -RequireWorkflow set, one rule follows the pending rule: when no
135
+ check whose `workflow` property equals the name (case-sensitive) has
136
+ bucket 'pass', the result is 'pending'. An empty or null set then yields
137
+ 'pending' instead of 'success'.
138
+
112
139
  .PARAMETER Checks
113
- The parsed required-check collection (each element exposing a `bucket`
114
- property). May be $null or empty, which yields 'success'.
140
+ The parsed check collection (each element exposing a `bucket`
141
+ property). May be $null or empty, which yields 'success' unless
142
+ -RequireWorkflow is set.
143
+
144
+ .PARAMETER RequireWorkflow
145
+ Optional workflow name; '' (default) disables the guard. A
146
+ whitespace-only value throws. An element without a `workflow` property
147
+ never matches.
115
148
 
116
149
  .OUTPUTS
117
150
  One of the strings 'success', 'failure', or 'pending'.
@@ -124,12 +157,27 @@ begin {
124
157
  param(
125
158
  [Parameter(Mandatory = $false)]
126
159
  [AllowNull()]
127
- [object[]]$Checks
160
+ [object[]]$Checks,
161
+
162
+ [Parameter(Mandatory = $false)]
163
+ [string]$RequireWorkflow = ''
128
164
  )
129
165
 
130
- # Empty or null required-check set is vacuously satisfied: there is no check
131
- # that can fail or be in progress, so the gate concludes 'success'.
166
+ # A non-empty, whitespace-only workflow name is invalid input. Reject it
167
+ # rather than silently disabling the guard and restoring vacuous success.
168
+ if ($RequireWorkflow.Length -gt 0 -and [string]::IsNullOrWhiteSpace($RequireWorkflow)) {
169
+ throw "Invoke-CiGateParser: -RequireWorkflow must be '' or a workflow name; a whitespace-only value is not accepted."
170
+ }
171
+
172
+ $requireWorkflowGuard = $RequireWorkflow.Length -gt 0
173
+
174
+ # Empty or null check set. Without the guard it is vacuously satisfied (no
175
+ # check can fail or be in progress); with the guard no passing check from
176
+ # the named workflow was observed, so the gate stays 'pending'.
132
177
  if ($null -eq $Checks -or $Checks.Count -eq 0) {
178
+ if ($requireWorkflowGuard) {
179
+ return 'pending'
180
+ }
133
181
  return 'success'
134
182
  }
135
183
 
@@ -137,7 +185,10 @@ begin {
137
185
  # decision until after confirming nothing failed (failure outranks pending).
138
186
  $anyPending = $false
139
187
 
140
- # Inspect each required check once, classifying by its bucket value. Failure
188
+ # Track whether a passing check from the named workflow was observed.
189
+ $requiredWorkflowPassed = $false
190
+
191
+ # Inspect each check once, classifying by its bucket value. Failure
141
192
  # short-circuits immediately because it is the highest-precedence outcome.
142
193
  foreach ($check in $Checks) {
143
194
  # A check element must expose a bucket; absence is an undefined state and
@@ -152,10 +203,19 @@ begin {
152
203
  # change semantics because failure short-circuits via return; pending is
153
204
  # only recorded (not returned) so a later failure can still take priority.
154
205
  switch ($bucket) {
155
- 'fail' { return 'failure' } # a failed required check is terminal-non-success
156
- 'cancel' { return 'failure' } # a cancelled required check is treated conservatively as failure
206
+ 'fail' { return 'failure' } # a failed check is terminal-non-success
207
+ 'cancel' { return 'failure' } # a cancelled check is treated conservatively as failure
157
208
  'pending' { $anyPending = $true } # in-progress; defer until failures are ruled out
158
- 'pass' { } # passing check contributes to success; no state change
209
+ 'pass' {
210
+ # Under the guard, record a pass from the named workflow. The
211
+ # property test keeps an element without `workflow` from
212
+ # throwing under Set-StrictMode; such an element never matches.
213
+ if ($requireWorkflowGuard -and
214
+ ($check.PSObject.Properties.Name -contains 'workflow') -and
215
+ ([string]$check.workflow -ceq $RequireWorkflow)) {
216
+ $requiredWorkflowPassed = $true
217
+ }
218
+ }
159
219
  'skipping' { } # skipped check is non-blocking; neither failure nor pending
160
220
  default {
161
221
  # Unknown bucket: fail fast and name the value so the unsupported
@@ -171,6 +231,12 @@ begin {
171
231
  return 'pending'
172
232
  }
173
233
 
234
+ # With the guard, nothing failed or is pending, but success also requires
235
+ # at least one passing check from the named workflow.
236
+ if ($requireWorkflowGuard -and -not $requiredWorkflowPassed) {
237
+ return 'pending'
238
+ }
239
+
174
240
  return 'success'
175
241
  }
176
242
 
@@ -241,6 +307,8 @@ begin {
241
307
  See script-level parameter of the same name.
242
308
  .PARAMETER NowProvider
243
309
  See script-level parameter of the same name.
310
+ .PARAMETER RequireWorkflow
311
+ See script-level parameter of the same name.
244
312
  .PARAMETER AsJson
245
313
  See script-level parameter of the same name.
246
314
 
@@ -269,6 +337,9 @@ begin {
269
337
  [ValidateNotNull()]
270
338
  [ScriptBlock]$NowProvider = { (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ') },
271
339
 
340
+ [Parameter(Mandatory = $false)]
341
+ [string]$RequireWorkflow = '',
342
+
272
343
  [Parameter(Mandatory = $false)]
273
344
  [switch]$AsJson
274
345
  )
@@ -288,7 +359,7 @@ begin {
288
359
  # downstream Count/foreach logic uniform for the empty, single, and many cases.
289
360
  $checks = @($parsed)
290
361
 
291
- $conclusion = Get-CiGateConclusion -Checks $checks
362
+ $conclusion = Get-CiGateConclusion -Checks $checks -RequireWorkflow $RequireWorkflow
292
363
 
293
364
  # Resolve the timestamp through the injected delegate so tests can pin an
294
365
  # exact value; the default delegate reads UTC wall-clock time.
@@ -324,6 +395,7 @@ process {
324
395
  -PrPipelineRunId $PrPipelineRunId `
325
396
  -PrPipelineRunUrl $PrPipelineRunUrl `
326
397
  -NowProvider $NowProvider `
398
+ -RequireWorkflow $RequireWorkflow `
327
399
  -AsJson:$AsJson
328
400
  }
329
401
  }
@@ -0,0 +1,203 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Maps one pull-request snapshot to a merge-confirmation status (issue #952).
4
+
5
+ .DESCRIPTION
6
+ Consumes the JSON emitted by
7
+ `gh pr view <n> --json state,mergeCommit,mergedAt,autoMergeRequest`, or a GraphQL
8
+ `pullRequest` node that may also carry `isInMergeQueue`, and reports whether the pull
9
+ request has merged. Merge procedures call this parser while they poll after a merge or
10
+ merge-queue enqueue, and record a merge only after it reports `merged`.
11
+
12
+ Mapping (state values are case-sensitive):
13
+ - MERGED with a non-empty mergeCommit.oid -> merged
14
+ - MERGED with a null mergeCommit or an empty oid -> error
15
+ - CLOSED -> closed
16
+ - OPEN with isInMergeQueue true, or with a present
17
+ non-null autoMergeRequest -> pending
18
+ - OPEN otherwise -> not_queued
19
+ - a null snapshot, a missing state, or any other
20
+ state value -> error
21
+
22
+ Every error message starts with 'Invoke-MergeConfirmationParser: '.
23
+
24
+ This script does NOT invoke `gh`. The caller runs `gh` and passes the resulting JSON
25
+ through -PrJson or the pipeline, so the mapping is deterministically testable.
26
+
27
+ .PARAMETER PrJson
28
+ The pull-request snapshot JSON. Accepts pipeline input. Defaults to '' so the file can be
29
+ dot-sourced with no arguments.
30
+
31
+ .PARAMETER AsJson
32
+ When set, emits the result as compressed JSON instead of a PowerShell object.
33
+
34
+ .EXAMPLE
35
+ pwsh -NoProfile -File .claude/lib/ci-gate/Invoke-MergeConfirmationParser.ps1 -PrJson $snapshot -AsJson
36
+
37
+ .OUTPUTS
38
+ A PSCustomObject (or a JSON string when -AsJson is set) with the properties Status
39
+ (merged, pending, not_queued, or closed), MergeCommitSha, and MergedAt. MergeCommitSha and
40
+ MergedAt are empty unless Status is merged.
41
+ #>
42
+
43
+ [CmdletBinding()]
44
+ param(
45
+ [Parameter(ValueFromPipeline = $true)]
46
+ [AllowEmptyString()]
47
+ [string]$PrJson = '',
48
+
49
+ [Parameter(Mandatory = $false)]
50
+ [switch]$AsJson
51
+ )
52
+
53
+ # Function definitions live in begin so the script is a single explicit begin/process unit;
54
+ # the pipeline-bound -PrJson parameter forces a named process block.
55
+ begin {
56
+ function Get-MergeConfirmationPropertyValue {
57
+ <#
58
+ .SYNOPSIS
59
+ Return a named property value of a parsed object, or $null when it is absent.
60
+ .DESCRIPTION
61
+ Reads through PSObject.Properties so an absent property is not an error under
62
+ Set-StrictMode -Version Latest.
63
+ #>
64
+ [CmdletBinding()]
65
+ param(
66
+ [AllowNull()]
67
+ $InputObject,
68
+
69
+ [Parameter(Mandatory = $true)]
70
+ [string]$Name
71
+ )
72
+
73
+ if ($null -eq $InputObject) {
74
+ return $null
75
+ }
76
+ $property = $InputObject.PSObject.Properties[$Name]
77
+ if ($null -eq $property) {
78
+ return $null
79
+ }
80
+ return $property.Value
81
+ }
82
+
83
+ function ConvertTo-MergeConfirmationTimeText {
84
+ <#
85
+ .SYNOPSIS
86
+ Render a mergedAt value as text: strings verbatim, dates as UTC ISO-8601.
87
+ #>
88
+ [CmdletBinding()]
89
+ [OutputType([string])]
90
+ param(
91
+ [AllowNull()]
92
+ $Value
93
+ )
94
+
95
+ $format = 'yyyy-MM-ddTHH:mm:ssZ'
96
+ $culture = [System.Globalization.CultureInfo]::InvariantCulture
97
+ if ($null -eq $Value) {
98
+ return ''
99
+ }
100
+ if ($Value -is [datetimeoffset]) {
101
+ return $Value.UtcDateTime.ToString($format, $culture)
102
+ }
103
+ if ($Value -is [datetime]) {
104
+ # A Kind of Unspecified carries no offset; JSON timestamps from gh are UTC.
105
+ $utc = if ($Value.Kind -eq [System.DateTimeKind]::Unspecified) { [datetime]::SpecifyKind($Value, [System.DateTimeKind]::Utc) } else { $Value.ToUniversalTime() }
106
+ return $utc.ToString($format, $culture)
107
+ }
108
+ return [string]$Value
109
+ }
110
+
111
+ function Get-MergeConfirmationStatus {
112
+ <#
113
+ .SYNOPSIS
114
+ Map one parsed pull-request snapshot to the merge-confirmation result object.
115
+ .PARAMETER Snapshot
116
+ The parsed snapshot (gh pr view JSON or a GraphQL pullRequest node).
117
+ .OUTPUTS
118
+ PSCustomObject with Status, MergeCommitSha, and MergedAt.
119
+ #>
120
+ [CmdletBinding()]
121
+ [OutputType([pscustomobject])]
122
+ param(
123
+ [AllowNull()]
124
+ $Snapshot
125
+ )
126
+
127
+ if ($null -eq $Snapshot) {
128
+ throw 'Invoke-MergeConfirmationParser: the pull-request snapshot is null or empty.'
129
+ }
130
+ $state = Get-MergeConfirmationPropertyValue -InputObject $Snapshot -Name 'state'
131
+ if ($null -eq $state) {
132
+ throw 'Invoke-MergeConfirmationParser: the pull-request snapshot has no state field.'
133
+ }
134
+ $stateText = [string]$state
135
+
136
+ if ($stateText -ceq 'MERGED') {
137
+ $mergeCommit = Get-MergeConfirmationPropertyValue -InputObject $Snapshot -Name 'mergeCommit'
138
+ $oid = [string](Get-MergeConfirmationPropertyValue -InputObject $mergeCommit -Name 'oid')
139
+ if ([string]::IsNullOrWhiteSpace($oid)) {
140
+ throw 'Invoke-MergeConfirmationParser: state MERGED carries no merge commit oid.'
141
+ }
142
+ $mergedAt = ConvertTo-MergeConfirmationTimeText -Value (Get-MergeConfirmationPropertyValue -InputObject $Snapshot -Name 'mergedAt')
143
+ return [pscustomobject]@{ Status = 'merged'; MergeCommitSha = $oid; MergedAt = $mergedAt }
144
+ }
145
+ if ($stateText -ceq 'CLOSED') {
146
+ return [pscustomobject]@{ Status = 'closed'; MergeCommitSha = ''; MergedAt = '' }
147
+ }
148
+ if ($stateText -ceq 'OPEN') {
149
+ $inQueue = Get-MergeConfirmationPropertyValue -InputObject $Snapshot -Name 'isInMergeQueue'
150
+ $autoMerge = Get-MergeConfirmationPropertyValue -InputObject $Snapshot -Name 'autoMergeRequest'
151
+ $queued = ($inQueue -is [bool] -and $inQueue) -or ($null -ne $autoMerge)
152
+ $status = if ($queued) { 'pending' } else { 'not_queued' }
153
+ return [pscustomobject]@{ Status = $status; MergeCommitSha = ''; MergedAt = '' }
154
+ }
155
+
156
+ throw ("Invoke-MergeConfirmationParser: unrecognized pull-request state '{0}'." -f $stateText)
157
+ }
158
+
159
+ function Invoke-MergeConfirmationParser {
160
+ <#
161
+ .SYNOPSIS
162
+ Parse a pull-request snapshot JSON string and map it to a merge-confirmation status.
163
+ .PARAMETER PrJson
164
+ The snapshot JSON text.
165
+ .PARAMETER AsJson
166
+ Emit compressed JSON instead of the result object.
167
+ .OUTPUTS
168
+ PSCustomObject, or System.String when -AsJson is set.
169
+ #>
170
+ [CmdletBinding()]
171
+ param(
172
+ [AllowEmptyString()]
173
+ [AllowNull()]
174
+ [string]$PrJson,
175
+
176
+ [switch]$AsJson
177
+ )
178
+
179
+ $snapshot = $null
180
+ if (-not [string]::IsNullOrWhiteSpace($PrJson)) {
181
+ try {
182
+ $snapshot = $PrJson | ConvertFrom-Json -ErrorAction Stop
183
+ }
184
+ catch {
185
+ throw ('Invoke-MergeConfirmationParser: the pull-request snapshot is not valid JSON: ' + $_.Exception.Message)
186
+ }
187
+ }
188
+
189
+ $result = Get-MergeConfirmationStatus -Snapshot $snapshot
190
+ if ($AsJson) {
191
+ return ($result | ConvertTo-Json -Depth 3 -Compress)
192
+ }
193
+ return $result
194
+ }
195
+ }
196
+
197
+ # Entry point. The parser runs only when the file is executed directly; when dot-sourced,
198
+ # $MyInvocation.InvocationName is '.', so the guard leaves only the function definitions.
199
+ process {
200
+ if ($MyInvocation.InvocationName -ne '.') {
201
+ Invoke-MergeConfirmationParser -PrJson $PrJson -AsJson:$AsJson
202
+ }
203
+ }