@danmoisan/drm-copilot-mcp 1.1.18 → 1.1.19

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 (206) hide show
  1. package/out/mcp-server.js +989 -662
  2. package/package.json +3 -2
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +11 -0
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +10 -0
  5. package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +12 -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 +5 -4
  9. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +31 -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/hooks/check-powershell-test-purity.ps1 +7 -1
  14. package/resources/claude-customizations/.claude/hooks/check-python-test-purity.ps1 +7 -1
  15. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  16. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +111 -46
  17. package/resources/claude-customizations/.claude/hooks/enforce-completion-helpers.ps1 +105 -0
  18. package/resources/claude-customizations/.claude/hooks/enforce-discovery-artifact-gate.ps1 +8 -1
  19. package/resources/claude-customizations/.claude/hooks/enforce-epic-invocation-origin.ps1 +7 -1
  20. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-resolution.ps1 +116 -59
  21. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +55 -23
  22. package/resources/claude-customizations/.claude/hooks/enforce-epic-wave-barrier.ps1 +74 -75
  23. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate-resolution.ps1 +127 -35
  24. package/resources/claude-customizations/.claude/hooks/enforce-epic-worktree-removal-gate.ps1 +61 -57
  25. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +7 -1
  26. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +129 -39
  27. package/resources/claude-customizations/.claude/hooks/enforce-mermaid-validation.ps1 +7 -1
  28. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +9 -3
  29. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +39 -51
  30. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  31. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  32. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  33. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +17 -12
  34. package/resources/claude-customizations/.claude/hooks/enforce-parallel-abandon-gate.ps1 +9 -3
  35. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier.ps1 +65 -50
  36. package/resources/claude-customizations/.claude/hooks/enforce-parallel-drift-gate.ps1 +49 -42
  37. package/resources/claude-customizations/.claude/hooks/enforce-parallel-worktree-removal-gate.ps1 +54 -72
  38. package/resources/claude-customizations/.claude/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  39. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-command-allowlist.ps1 +279 -0
  40. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +129 -75
  41. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.artifact-root.ps1 +233 -0
  42. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +224 -0
  43. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +69 -24
  44. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner-helpers.ps1 +29 -18
  45. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +10 -4
  46. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +14 -5
  47. package/resources/claude-customizations/.claude/hooks/enforce-python-batch-budget.ps1 +8 -2
  48. package/resources/claude-customizations/.claude/hooks/feature-folder-resolution.ps1 +373 -0
  49. package/resources/claude-customizations/.claude/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  50. package/resources/claude-customizations/.claude/hooks/hook-command-heredoc.ps1 +119 -0
  51. package/resources/claude-customizations/.claude/hooks/hook-command-invocation-operands.ps1 +198 -0
  52. package/resources/claude-customizations/.claude/hooks/hook-command-invocation.ps1 +254 -255
  53. package/resources/claude-customizations/.claude/hooks/hook-command-payload-powershell.ps1 +201 -0
  54. package/resources/claude-customizations/.claude/hooks/hook-command-payload.ps1 +484 -0
  55. package/resources/claude-customizations/.claude/hooks/hook-command-scanner.ps1 +27 -110
  56. package/resources/claude-customizations/.claude/hooks/hook-dependency-guard.ps1 +114 -0
  57. package/resources/claude-customizations/.claude/hooks/hook-docs-only-diff.ps1 +493 -0
  58. package/resources/claude-customizations/.claude/hooks/subagentstop-envelope.ps1 +192 -0
  59. package/resources/claude-customizations/.claude/hooks/validate-bash.ps1 +9 -3
  60. package/resources/claude-customizations/.claude/hooks/validate-discovery-artifact-gate.ps1 +40 -42
  61. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +66 -38
  62. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-metrics.ps1 +180 -0
  63. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +195 -0
  64. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +141 -395
  65. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-resolution.ps1 +311 -0
  66. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output-routing.ps1 +170 -0
  67. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +115 -190
  68. package/resources/claude-customizations/.claude/hooks/validate-planner-output.ps1 +64 -32
  69. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +39 -44
  70. package/resources/claude-customizations/.claude/hooks/validate-prd-feature-output.ps1 +43 -14
  71. package/resources/claude-customizations/.claude/hooks/validate-required-artifact-output.ps1 +66 -36
  72. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +31 -34
  73. package/resources/claude-customizations/.claude/lib/bash/mutate-parallel-run.sh +191 -0
  74. package/resources/claude-customizations/.claude/lib/bash/parallel-cohorts.sh +13 -3
  75. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-cli.sh +152 -0
  76. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation-ops.sh +193 -0
  77. package/resources/claude-customizations/.claude/lib/bash/parallel-mutation.sh +322 -0
  78. package/resources/claude-customizations/.claude/lib/bash/remove-parallel-item.sh +146 -0
  79. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusExtraction.psm1 +23 -33
  80. package/resources/claude-customizations/.claude/lib/blast-radius/BlastRadiusTokenShape.psm1 +86 -6
  81. package/resources/claude-customizations/.claude/lib/ci-gate/Invoke-CiGateParser.ps1 +91 -19
  82. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.psm1 +346 -0
  83. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateIssueAdoption.psm1 +24 -3
  84. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeItemResolution.psm1 +79 -2
  85. package/resources/claude-customizations/.claude/lib/worktree-resolution/WorktreeRunResolution.psm1 +3 -3
  86. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +5 -5
  87. package/resources/claude-customizations/.claude/rules/csharp.md +2 -2
  88. package/resources/claude-customizations/.claude/rules/general-code-change.md +1 -1
  89. package/resources/claude-customizations/.claude/rules/general-unit-test.md +3 -2
  90. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +37 -4
  91. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +50 -5
  92. package/resources/claude-customizations/.claude/rules/plan-acceptance-gates.md +47 -8
  93. package/resources/claude-customizations/.claude/rules/quality-tiers.md +11 -7
  94. package/resources/claude-customizations/.claude/rules/shell.md +10 -8
  95. package/resources/claude-customizations/.claude/rules/typescript.md +1 -1
  96. package/resources/claude-customizations/.claude/settings.json +3 -4
  97. package/resources/claude-customizations/.claude/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  98. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +5 -1
  99. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh +1 -1
  100. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_enumerate_lib.sh +2 -0
  101. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_scan_helper.sh +2 -0
  102. package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +33 -6
  103. package/resources/claude-customizations/.claude/skills/epic-plan/SKILL.md +1 -1
  104. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  105. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +1 -1
  106. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +4 -3
  107. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +14 -6
  108. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +52 -14
  109. package/resources/claude-customizations/.claude/skills/parallel-close/SKILL.md +22 -9
  110. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +79 -16
  111. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +63 -7
  112. package/resources/claude-customizations/.claude/skills/parallel-remove/SKILL.md +35 -12
  113. package/resources/claude-customizations/.claude/skills/pr-author/SKILL.md +3 -1
  114. package/resources/claude-customizations/.claude/skills/quota-throttling/SKILL.md +1 -1
  115. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +2 -2
  116. package/resources/claude-customizations/pack-manifests/core.json +23 -0
  117. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  118. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +5 -5
  119. package/resources/codex-and-agents-customizations/.agents/skills/atomic-executor/SKILL.md +11 -0
  120. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +4 -0
  121. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +10 -0
  122. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +2 -2
  123. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +2 -2
  124. package/resources/codex-and-agents-customizations/.agents/skills/epic-orchestrate/SKILL.md +8 -0
  125. package/resources/codex-and-agents-customizations/.agents/skills/epic-plan/SKILL.md +17 -1
  126. package/resources/codex-and-agents-customizations/.agents/skills/epic-run/SKILL.md +2 -1
  127. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  128. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +13 -5
  129. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +1 -1
  130. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +3 -2
  131. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +15 -6
  132. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +1 -1
  133. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +11 -7
  134. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +1 -1
  135. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp/SKILL.md +2 -2
  136. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +2 -2
  137. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +11 -0
  138. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +11 -0
  139. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +11 -0
  140. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +11 -0
  141. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +11 -0
  142. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +11 -0
  143. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +10 -0
  144. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +10 -0
  145. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +10 -0
  146. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +10 -0
  147. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +10 -0
  148. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +10 -0
  149. package/resources/codex-and-agents-customizations/.codex/agents/epic-orchestrator.toml +2 -1
  150. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +6 -6
  151. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c1.toml +2 -2
  152. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c2.toml +2 -2
  153. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3-elevated.toml +2 -2
  154. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c3.toml +2 -2
  155. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator-c4.toml +2 -2
  156. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +2 -2
  157. package/resources/codex-and-agents-customizations/.codex/codex-web-setup.sh +318 -273
  158. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  159. package/resources/codex-and-agents-customizations/.codex/hooks/check-powershell-test-purity.ps1 +7 -1
  160. package/resources/codex-and-agents-customizations/.codex/hooks/check-python-test-purity.ps1 +7 -1
  161. package/resources/codex-and-agents-customizations/.codex/hooks/codex-epic-child-launch-attestation.ps1 +1 -1
  162. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +7 -1
  163. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-codex-model-routing.ps1 +8 -2
  164. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +97 -44
  165. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +105 -0
  166. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-child-worktree-binding.ps1 +7 -1
  167. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +37 -5
  168. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-planning-only.ps1 +6 -0
  169. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-root-invocation.ps1 +7 -1
  170. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-wave-barrier.ps1 +7 -1
  171. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-worktree-removal-gate.ps1 +54 -41
  172. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-evidence-locations.ps1 +7 -1
  173. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-epic-scope.ps1 +35 -14
  174. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-helpers.ps1 +31 -58
  175. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-modes.ps1 +63 -47
  176. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-targets.ps1 +433 -0
  177. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +24 -11
  178. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-powershell-batch-budget.ps1 +8 -2
  179. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +13 -4
  180. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-python-batch-budget.ps1 +8 -2
  181. package/resources/codex-and-agents-customizations/.codex/hooks/feature-folder-resolution.ps1 +373 -0
  182. package/resources/codex-and-agents-customizations/.codex/hooks/feature-review-coverage-thresholds.ps1 +85 -0
  183. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-heredoc.ps1 +119 -0
  184. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation-operands.ps1 +198 -0
  185. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-invocation.ps1 +254 -255
  186. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload-powershell.ps1 +201 -0
  187. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-payload.ps1 +484 -0
  188. package/resources/codex-and-agents-customizations/.codex/hooks/hook-command-scanner.ps1 +27 -110
  189. package/resources/codex-and-agents-customizations/.codex/hooks/hook-dependency-guard.ps1 +114 -0
  190. package/resources/codex-and-agents-customizations/.codex/hooks/hook-docs-only-diff.ps1 +493 -0
  191. package/resources/codex-and-agents-customizations/.codex/hooks/validate-bash.ps1 +8 -2
  192. package/resources/codex-and-agents-customizations/.codex/hooks/validate-codex-subagent-routing.ps1 +7 -1
  193. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +24 -8
  194. package/resources/codex-and-agents-customizations/pack-manifests/core.json +9 -0
  195. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +2 -2
  196. package/resources/customizations/.github/instructions/csharp-code-change.instructions.md +4 -4
  197. package/resources/customizations/.github/instructions/csharp-unit-test.instructions.md +2 -2
  198. package/resources/customizations/.github/skills/acceptance-criteria-tracking/SKILL.md +1 -1
  199. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +4 -0
  200. package/resources/customizations/.github/skills/evidence-and-timestamp-conventions/SKILL.md +10 -1
  201. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +1 -1
  202. package/resources/powershell/PoshQC/PoshQC.Coverage.psm1 +314 -0
  203. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +25 -28
  204. package/resources/powershell/PoshQC/PoshQC.psm1 +1 -0
  205. package/resources/powershell/PoshQC/README.md +8 -2
  206. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -319
@@ -0,0 +1,346 @@
1
+ <#
2
+ .SYNOPSIS
3
+ PowerShell port of the epic wave-barrier Layer 2 ordering check (issue #840).
4
+
5
+ .DESCRIPTION
6
+ Ports validate_wave_barrier_ordering (scripts/dev_tools/_epic_orchestrator_state_wave_barrier.py)
7
+ as reached through validate_epic_orchestrator_state_text: feature extraction, the union
8
+ index with lifecycle-prefix normalization, the start guard, and the status and timing
9
+ checks with status-before-timing precedence. For in-parity inputs the violation strings
10
+ are byte-identical to the Python authority.
11
+
12
+ The checkpoint is parsed with System.Text.Json (JsonDocument), not ConvertFrom-Json:
13
+ ConvertFrom-Json coerces ISO-8601 strings to DateTime and reads property names
14
+ case-insensitively, and either behavior would break parity. Properties are read through
15
+ an ordinal, last-definition-wins enumeration, which matches Python json semantics for a
16
+ duplicate key.
17
+
18
+ An input this port does not evaluate fails closed with an error whose message starts
19
+ with EPIC_WAVE_BARRIER_UNEVALUABLE:. The declared divergence classes are listed in
20
+ tests/scripts/claude-lib/orchestrator-state/OrchestratorStateEpicWaveBarrier.Parity.Tests.ps1.
21
+
22
+ .NOTES
23
+ PowerShell 7+. Reads no file and starts no process: the caller supplies checkpoint text
24
+ it has already read. Mirrored byte-identically into the bundled extension payload.
25
+ CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
26
+ #>
27
+
28
+ Set-StrictMode -Version Latest
29
+ $ErrorActionPreference = 'Stop'
30
+
31
+ # Lifecycle prefixes a folder hint may carry, tested in this order; the first match is stripped.
32
+ $script:LifecyclePrefixes = @('docs/features/active/', 'docs/features/completed/', 'active/', 'completed/')
33
+
34
+ # The merge statuses that durably confirm a dependency edge, and the literal not-started status.
35
+ $script:MergedStatuses = @('merged', 'worktree_removed')
36
+ $script:NotStartedStatus = 'not_started'
37
+
38
+ # A JSON number token that Python reads as an int; any other number token is not compared.
39
+ $script:IntegerTokenPattern = '^-?(0|[1-9][0-9]*)$'
40
+
41
+ # The fail-closed token and the two violation formats of the Python authority.
42
+ $script:UnevaluableToken = 'EPIC_WAVE_BARRIER_UNEVALUABLE:'
43
+ $script:StatusViolationFormat = 'EPIC_WAVE_BARRIER_VIOLATION: {0} is treated as started while dependency {1} is not merged'
44
+ $script:TimingViolationFormat = 'EPIC_WAVE_BARRIER_VIOLATION: {0} worktree_created_at precedes dependency {1} merge_confirmed_at'
45
+
46
+ # Private: the last property of an object element whose name equals Name (ordinal), so a
47
+ # duplicate key resolves the way Python json does. Found is $false when there is none.
48
+ function Get-EpicWaveBarrierProperty {
49
+ [CmdletBinding()]
50
+ [OutputType([pscustomobject])]
51
+ param(
52
+ [Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Element,
53
+ [Parameter(Mandatory = $true)] [string] $Name
54
+ )
55
+
56
+ $found = $false
57
+ $value = $null
58
+ foreach ($property in $Element.EnumerateObject()) {
59
+ if ([string]::Equals($property.Name, $Name, [System.StringComparison]::Ordinal)) {
60
+ $found = $true
61
+ $value = $property.Value
62
+ }
63
+ }
64
+ return [pscustomobject]@{ Found = $found; Value = $value }
65
+ }
66
+
67
+ # Private: the string value of a named property, or $null when it is absent or not a string.
68
+ function Get-EpicWaveBarrierString {
69
+ [CmdletBinding()]
70
+ [OutputType([string])]
71
+ param(
72
+ [Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Element,
73
+ [Parameter(Mandatory = $true)] [string] $Name
74
+ )
75
+
76
+ $property = Get-EpicWaveBarrierProperty -Element $Element -Name $Name
77
+ if ($property.Found -and $property.Value.ValueKind -eq [System.Text.Json.JsonValueKind]::String) {
78
+ return $property.Value.GetString()
79
+ }
80
+ return $null
81
+ }
82
+
83
+ # Private: strip the first matching lifecycle prefix (ordinal), as _normalize_folder_hint does.
84
+ function ConvertTo-EpicWaveBarrierFolderHint {
85
+ [CmdletBinding()]
86
+ [OutputType([string])]
87
+ param(
88
+ [Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Value
89
+ )
90
+
91
+ foreach ($prefix in $script:LifecyclePrefixes) {
92
+ if ($Value.StartsWith($prefix, [System.StringComparison]::Ordinal)) {
93
+ return $Value.Substring($prefix.Length)
94
+ }
95
+ }
96
+ return $Value
97
+ }
98
+
99
+ # Private: the canonical decimal text of an integer token; BigInteger keeps values beyond
100
+ # 64 bits exact and renders -0 as 0, as Python str(int) does.
101
+ function ConvertTo-EpicWaveBarrierIntegerText {
102
+ [CmdletBinding()]
103
+ [OutputType([string])]
104
+ param(
105
+ [Parameter(Mandatory = $true)] [string] $RawToken
106
+ )
107
+
108
+ $invariant = [System.Globalization.CultureInfo]::InvariantCulture
109
+ return [System.Numerics.BigInteger]::Parse($RawToken, $invariant).ToString($invariant)
110
+ }
111
+
112
+ # Private: the union-index key of a JSON number. Python indexes an int by value; a
113
+ # non-integer token is outside parity, so it fails closed rather than being compared.
114
+ function Get-EpicWaveBarrierNumberKey {
115
+ [CmdletBinding()]
116
+ [OutputType([string])]
117
+ param(
118
+ [Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Element,
119
+ [Parameter(Mandatory = $true)] [string] $Role,
120
+ [Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Folder
121
+ )
122
+
123
+ $raw = $Element.GetRawText()
124
+ if ($raw -cnotmatch $script:IntegerTokenPattern) {
125
+ throw ("{0} feature '{1}' has a non-integer numeric {2} token '{3}', which this port does not compare" -f
126
+ $script:UnevaluableToken, $Folder, $Role, $raw)
127
+ }
128
+ return 'n:' + (ConvertTo-EpicWaveBarrierIntegerText -RawToken $raw)
129
+ }
130
+
131
+ # Private: render a resolved dependency reference as Python str() renders the raw value.
132
+ # Only strings, booleans, and integer numbers resolve, so no other kind reaches this point.
133
+ function Format-EpicWaveBarrierReference {
134
+ [CmdletBinding()]
135
+ [OutputType([string])]
136
+ param(
137
+ [Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Element
138
+ )
139
+
140
+ $kind = $Element.ValueKind
141
+ if ($kind -eq [System.Text.Json.JsonValueKind]::String) { return $Element.GetString() }
142
+ if ($kind -eq [System.Text.Json.JsonValueKind]::True) { return 'True' }
143
+ if ($kind -eq [System.Text.Json.JsonValueKind]::False) { return 'False' }
144
+ return (ConvertTo-EpicWaveBarrierIntegerText -RawToken $Element.GetRawText())
145
+ }
146
+
147
+ # Private: the start guard of feature_has_started. A string worktree_created_at (empty
148
+ # included) starts a feature; otherwise only the exact string not_started leaves it unstarted.
149
+ function Test-EpicWaveBarrierFeatureStarted {
150
+ [CmdletBinding()]
151
+ [OutputType([bool])]
152
+ param(
153
+ [Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Feature
154
+ )
155
+
156
+ if ($null -ne (Get-EpicWaveBarrierString -Element $Feature -Name 'worktree_created_at')) { return $true }
157
+ $status = Get-EpicWaveBarrierProperty -Element $Feature -Name 'merge_status'
158
+ if (-not $status.Found) { return $true }
159
+ $isNotStarted = ($status.Value.ValueKind -eq [System.Text.Json.JsonValueKind]::String) -and
160
+ [string]::Equals($status.Value.GetString(), $script:NotStartedStatus, [System.StringComparison]::Ordinal)
161
+ return (-not $isNotStarted)
162
+ }
163
+
164
+ # Private: whether a dependency's merge_status is a string equal (ordinal) to a merged status.
165
+ # An absent or non-string status arrives as $null, which the [string] binding turns into an
166
+ # empty string, and an empty string is not a merged status.
167
+ function Test-EpicWaveBarrierMerged {
168
+ [CmdletBinding()]
169
+ [OutputType([bool])]
170
+ param(
171
+ [Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Status
172
+ )
173
+
174
+ foreach ($merged in $script:MergedStatuses) {
175
+ if ([string]::Equals($Status, $merged, [System.StringComparison]::Ordinal)) { return $true }
176
+ }
177
+ return $false
178
+ }
179
+
180
+ # Private: the union-index key of a feature's issue_num, or $null when it is absent, null,
181
+ # or a string (a string issue_num is never matched by a non-string reference).
182
+ function Get-EpicWaveBarrierIssueKey {
183
+ [CmdletBinding()]
184
+ [OutputType([string])]
185
+ param(
186
+ [Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Feature,
187
+ [Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Folder
188
+ )
189
+
190
+ $issue = Get-EpicWaveBarrierProperty -Element $Feature -Name 'issue_num'
191
+ if (-not $issue.Found) { return $null }
192
+ $kind = $issue.Value.ValueKind
193
+ # Routing table: an array or object cannot be hashed by the authority (fail closed);
194
+ # booleans key as 1 and 0 because Python treats True == 1; numbers key by value.
195
+ if ($kind -eq [System.Text.Json.JsonValueKind]::Array -or $kind -eq [System.Text.Json.JsonValueKind]::Object) {
196
+ throw ("{0} feature '{1}' has an issue_num that is an array or object, which the Python authority cannot index" -f
197
+ $script:UnevaluableToken, $Folder)
198
+ }
199
+ if ($kind -eq [System.Text.Json.JsonValueKind]::True) { return 'n:1' }
200
+ if ($kind -eq [System.Text.Json.JsonValueKind]::False) { return 'n:0' }
201
+ if ($kind -eq [System.Text.Json.JsonValueKind]::Number) {
202
+ return (Get-EpicWaveBarrierNumberKey -Element $issue.Value -Role 'issue_num' -Folder $Folder)
203
+ }
204
+ return $null
205
+ }
206
+
207
+ # Private: the object entries of a list-valued features property, in order; empty when the
208
+ # root is not an object or features is absent or not a list.
209
+ function Get-EpicWaveBarrierFeatureEntry {
210
+ [CmdletBinding()]
211
+ [OutputType([System.Text.Json.JsonElement[]], [object[]])]
212
+ param(
213
+ [Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Root
214
+ )
215
+
216
+ $entries = [System.Collections.Generic.List[System.Text.Json.JsonElement]]::new()
217
+ if ($Root.ValueKind -ne [System.Text.Json.JsonValueKind]::Object) { return , $entries.ToArray() }
218
+ $features = Get-EpicWaveBarrierProperty -Element $Root -Name 'features'
219
+ if (-not $features.Found -or $features.Value.ValueKind -ne [System.Text.Json.JsonValueKind]::Array) {
220
+ return , $entries.ToArray()
221
+ }
222
+ # Keep only object-shaped entries, as _extract_features does.
223
+ foreach ($item in $features.Value.EnumerateArray()) {
224
+ if ($item.ValueKind -eq [System.Text.Json.JsonValueKind]::Object) { $entries.Add($item) }
225
+ }
226
+ return , $entries.ToArray()
227
+ }
228
+
229
+ # Private: resolve one depends_on element to a feature folder through the union index, or
230
+ # $null. A string consults the folder hints only; a boolean or number consults issue_num only.
231
+ function Resolve-EpicWaveBarrierReference {
232
+ [CmdletBinding()]
233
+ [OutputType([string])]
234
+ param(
235
+ [Parameter(Mandatory = $true)] [System.Text.Json.JsonElement] $Dependency,
236
+ [Parameter(Mandatory = $true)] [System.Collections.Generic.Dictionary[string, string]] $ByHint,
237
+ [Parameter(Mandatory = $true)] [System.Collections.Generic.Dictionary[string, string]] $ByIssue,
238
+ [Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $Folder
239
+ )
240
+
241
+ $kind = $Dependency.ValueKind
242
+ $key = $null
243
+ $map = $ByIssue
244
+ if ($kind -eq [System.Text.Json.JsonValueKind]::String) {
245
+ $key = ConvertTo-EpicWaveBarrierFolderHint -Value $Dependency.GetString()
246
+ $map = $ByHint
247
+ }
248
+ elseif ($kind -eq [System.Text.Json.JsonValueKind]::True) { $key = 'n:1' }
249
+ elseif ($kind -eq [System.Text.Json.JsonValueKind]::False) { $key = 'n:0' }
250
+ elseif ($kind -eq [System.Text.Json.JsonValueKind]::Number) {
251
+ $key = Get-EpicWaveBarrierNumberKey -Element $Dependency -Role 'depends_on reference' -Folder $Folder
252
+ }
253
+ if ($null -eq $key -or -not $map.ContainsKey($key)) { return $null }
254
+ return $map[$key]
255
+ }
256
+
257
+ # Private: the violation lines for the object entries, in feature and depends_on order.
258
+ function Get-EpicWaveBarrierViolation {
259
+ [CmdletBinding()]
260
+ [OutputType([string])]
261
+ param(
262
+ [Parameter(Mandatory = $true)] [AllowEmptyCollection()] [System.Text.Json.JsonElement[]] $Entry
263
+ )
264
+
265
+ $ordinal = [System.StringComparer]::Ordinal
266
+ $byFolder = [System.Collections.Generic.Dictionary[string, System.Text.Json.JsonElement]]::new($ordinal)
267
+ $byHint = [System.Collections.Generic.Dictionary[string, string]]::new($ordinal)
268
+ $byIssue = [System.Collections.Generic.Dictionary[string, string]]::new($ordinal)
269
+
270
+ # Build the lookups: every string folder by name, and every non-empty string folder by
271
+ # its normalized hint and its issue_num. A later duplicate replaces an earlier one.
272
+ foreach ($feature in $Entry) {
273
+ $folder = Get-EpicWaveBarrierString -Element $feature -Name 'feature_folder'
274
+ if ($null -eq $folder) { continue }
275
+ $byFolder[$folder] = $feature
276
+ if ($folder.Length -eq 0) { continue }
277
+ $byHint[(ConvertTo-EpicWaveBarrierFolderHint -Value $folder)] = $folder
278
+ $issueKey = Get-EpicWaveBarrierIssueKey -Feature $feature -Folder $folder
279
+ if ($null -ne $issueKey) { $byIssue[$issueKey] = $folder }
280
+ }
281
+
282
+ # Check every started feature with a list depends_on; each edge yields at most one line,
283
+ # and the status violation takes precedence over the timing violation.
284
+ foreach ($feature in $Entry) {
285
+ $folder = Get-EpicWaveBarrierString -Element $feature -Name 'feature_folder'
286
+ $dependsOn = Get-EpicWaveBarrierProperty -Element $feature -Name 'depends_on'
287
+ if ($null -eq $folder -or -not $dependsOn.Found -or $dependsOn.Value.ValueKind -ne [System.Text.Json.JsonValueKind]::Array) { continue }
288
+ if (-not (Test-EpicWaveBarrierFeatureStarted -Feature $feature)) { continue }
289
+ $created = Get-EpicWaveBarrierString -Element $feature -Name 'worktree_created_at'
290
+ foreach ($dependency in $dependsOn.Value.EnumerateArray()) {
291
+ $resolved = Resolve-EpicWaveBarrierReference -Dependency $dependency -ByHint $byHint -ByIssue $byIssue -Folder $folder
292
+ if ($null -eq $resolved -or -not $byFolder.ContainsKey($resolved)) { continue }
293
+ $target = $byFolder[$resolved]
294
+ $reference = Format-EpicWaveBarrierReference -Element $dependency
295
+ if (-not (Test-EpicWaveBarrierMerged -Status (Get-EpicWaveBarrierString -Element $target -Name 'merge_status'))) {
296
+ $script:StatusViolationFormat -f $folder, $reference
297
+ continue
298
+ }
299
+ $confirmed = Get-EpicWaveBarrierString -Element $target -Name 'merge_confirmed_at'
300
+ if ($null -ne $confirmed -and $null -ne $created -and [string]::CompareOrdinal($confirmed, $created) -gt 0) {
301
+ $script:TimingViolationFormat -f $folder, $reference
302
+ }
303
+ }
304
+ }
305
+ }
306
+
307
+ function Get-OrchestratorStateEpicWaveBarrierError {
308
+ <#
309
+ .SYNOPSIS
310
+ Return the EPIC_WAVE_BARRIER_VIOLATION lines for an epic checkpoint's text.
311
+ .DESCRIPTION
312
+ Parses the text with System.Text.Json default options and applies the Layer 2
313
+ ordering check. Returns an empty array when there is no violation, when the root is
314
+ not an object, or when features is absent or not a list. Throws an error whose
315
+ message starts with EPIC_WAVE_BARRIER_UNEVALUABLE: for text System.Text.Json does
316
+ not parse (including NaN or Infinity literals and nesting deeper than 64 levels), an
317
+ array or object issue_num, and a non-integer number token that the check consults.
318
+ .PARAMETER CheckpointText
319
+ The raw epic checkpoint JSON text.
320
+ .OUTPUTS
321
+ System.String[]
322
+ #>
323
+ [CmdletBinding()]
324
+ [OutputType([string[]], [object[]])]
325
+ param(
326
+ [Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $CheckpointText
327
+ )
328
+
329
+ $document = $null
330
+ try {
331
+ $document = [System.Text.Json.JsonDocument]::Parse($CheckpointText, [System.Text.Json.JsonDocumentOptions]::new())
332
+ }
333
+ catch {
334
+ throw ('{0} the checkpoint is not parseable by System.Text.Json with default options (a non-standard literal such as NaN or Infinity, or nesting deeper than 64 levels): {1}' -f
335
+ $script:UnevaluableToken, $_.Exception.Message)
336
+ }
337
+ try {
338
+ $entries = Get-EpicWaveBarrierFeatureEntry -Root $document.RootElement
339
+ return , [string[]] @(Get-EpicWaveBarrierViolation -Entry $entries)
340
+ }
341
+ finally {
342
+ $document.Dispose()
343
+ }
344
+ }
345
+
346
+ Export-ModuleMember -Function Get-OrchestratorStateEpicWaveBarrierError
@@ -43,6 +43,8 @@ $script:WAIVABLE_TOOLS = [string[]]@($script:POTENTIAL_TO_ISSUE_TOOL, $script:FE
43
43
  $script:PROMOTION_ENTRY_TOOLS = [string[]]@($script:FEATURE_PROMOTION_ENTRY_TOOL, $script:BUG_PROMOTION_ENTRY_TOOL)
44
44
  $script:POTENTIAL_RECORD_PREFIX = 'docs/features/potential/'
45
45
  $script:POTENTIAL_RECORD_SUFFIX = '.md'
46
+ # Origins whose promotion-entry waiver may omit the potential_record key (rule 9).
47
+ $script:RECORD_OPTIONAL_ORIGINS = [string[]]@('transferred', 'filed_before_orchestration')
46
48
  $script:ISSUE_NUM_PATTERN = '\A[1-9][0-9]*\z'
47
49
 
48
50
  $script:ERROR_NOT_OBJECT = 'Checkpoint issue_adoption must be an object when present.'
@@ -264,6 +266,13 @@ function Get-AdoptionPotentialRecordError {
264
266
  The well-formed waived tool list, in checkpoint order.
265
267
  .PARAMETER PotentialRecord
266
268
  The deserialized potential_record value. May be $null.
269
+ .PARAMETER Origin
270
+ The deserialized origin value. May be $null. When the potential_record
271
+ key is absent and the origin is transferred or filed_before_orchestration,
272
+ nothing is reported.
273
+ .PARAMETER RecordPresent
274
+ Whether the potential_record key is present. A present record, including
275
+ $null, is always validated.
267
276
  .OUTPUTS
268
277
  System.String[] - zero or more error strings.
269
278
  #>
@@ -275,9 +284,20 @@ function Get-AdoptionPotentialRecordError {
275
284
 
276
285
  [Parameter(Mandatory = $true)]
277
286
  [AllowNull()]
278
- [object] $PotentialRecord
287
+ [object] $PotentialRecord,
288
+
289
+ [Parameter()]
290
+ [AllowNull()]
291
+ [object] $Origin,
292
+
293
+ [Parameter(Mandatory = $true)]
294
+ [bool] $RecordPresent
279
295
  )
280
296
 
297
+ if (-not $RecordPresent -and ($Origin -is [string]) -and ($script:RECORD_OPTIONAL_ORIGINS -ccontains [string]$Origin)) {
298
+ return [string[]]@()
299
+ }
300
+
281
301
  $recordIsValid = $false
282
302
  if ($PotentialRecord -is [string]) {
283
303
  $record = [string]$PotentialRecord
@@ -356,8 +376,9 @@ function Get-OrchestratorStateIssueAdoptionResult {
356
376
  }
357
377
 
358
378
  $errors.AddRange([string[]]@(Get-AdoptionWaivedToolError -WaivedTool $waived.Value -RouteId $RouteId -RequiredMcpTool $RequiredMcpTool -SuccessfulTool $SuccessfulTool))
359
- $potentialRecord = (Get-CheckpointObjectMember -Owner $adoption -Name 'potential_record').Value
360
- $errors.AddRange([string[]]@(Get-AdoptionPotentialRecordError -WaivedTool $waived.Value -PotentialRecord $potentialRecord))
379
+ $recordMember = Get-CheckpointObjectMember -Owner $adoption -Name 'potential_record'
380
+ $origin = (Get-CheckpointObjectMember -Owner $adoption -Name 'origin').Value
381
+ $errors.AddRange([string[]]@(Get-AdoptionPotentialRecordError -WaivedTool $waived.Value -PotentialRecord $recordMember.Value -Origin $origin -RecordPresent $recordMember.Present))
361
382
 
362
383
  if ($errors.Count -gt 0) {
363
384
  return @{ Errors = $errors.ToArray(); WaivedTools = [string[]]@() }
@@ -21,7 +21,8 @@
21
21
  PowerShell 7+. Re-uses its siblings for enumeration, the marker test, the ascent,
22
22
  the branch reader, normalisation, the join, the result constructor, and both
23
23
  reason-code accessors, re-implementing none. Neither reason-code literal appears
24
- here. Its only filesystem read is the checkpoint-text seam. Mirrored byte-identically.
24
+ here. Its only filesystem read is still the checkpoint-text seam, including for the
25
+ item-by-pull-request resolver added for issue #850. Mirrored byte-identically.
25
26
  CONVENTION: this module fails fast at module scope and imports its siblings with -ErrorAction Stop.
26
27
  #>
27
28
 
@@ -395,6 +396,81 @@ function Resolve-WorktreeItemTarget {
395
396
  return (Resolve-WorktreeItemTargetByIssue -Session $session -Issue $issues[0])
396
397
  }
397
398
 
399
+ # Private: whether checkpoint text records a pull request number in pr_gate.pr_number or
400
+ # in a standalone_merge_authorizations entry whose pr_number is a positive JSON integer.
401
+ function Test-WorktreeItemCheckpointRecordsPr {
402
+ [CmdletBinding()]
403
+ [OutputType([bool])]
404
+ param(
405
+ [Parameter(Mandatory = $true)] [AllowNull()] [AllowEmptyString()] [string] $Text,
406
+ [Parameter(Mandatory = $true)] [long] $PrNumber
407
+ )
408
+
409
+ if ([string]::IsNullOrWhiteSpace($Text)) { return $false }
410
+ $payload = $null
411
+ try { $payload = $Text | ConvertFrom-Json } catch { return $false }
412
+ if ($null -eq $payload -or $payload -isnot [pscustomobject]) { return $false }
413
+ # Names are collected per property, because strict mode throws on .Name of an empty object.
414
+ $names = @($payload.PSObject.Properties | ForEach-Object { $_.Name })
415
+ if ($names -contains 'pr_gate' -and $payload.pr_gate -is [pscustomobject] -and @($payload.pr_gate.PSObject.Properties | ForEach-Object { $_.Name }) -contains 'pr_number') {
416
+ $recorded = [long] 0
417
+ if ([long]::TryParse([string] $payload.pr_gate.pr_number, [ref] $recorded) -and $recorded -eq $PrNumber) { return $true }
418
+ }
419
+ if ($names -notcontains 'standalone_merge_authorizations') { return $false }
420
+ foreach ($entry in @($payload.standalone_merge_authorizations)) {
421
+ if ($entry -isnot [pscustomobject] -or @($entry.PSObject.Properties | ForEach-Object { $_.Name }) -notcontains 'pr_number') { continue }
422
+ $value = $entry.pr_number
423
+ # Only a JSON integer counts; a string, fraction, zero, or negative never matches.
424
+ if (($value -is [int] -or $value -is [long]) -and $value -gt 0 -and [long] $value -eq $PrNumber) { return $true }
425
+ }
426
+ return $false
427
+ }
428
+
429
+ function Resolve-WorktreeItemTargetByPrNumber {
430
+ <#
431
+ .SYNOPSIS
432
+ Resolve the live worktree whose orchestrator checkpoint records a pull request number (issue #850).
433
+ .DESCRIPTION
434
+ Keeps each live root whose checkpoint records the number in pr_gate.pr_number or
435
+ in a standalone_merge_authorizations entry. Zero matches resolve NoTarget, one
436
+ resolves, and several are Ambiguous with no tie-break. A number of zero or less
437
+ is NoTarget. Reads only through the checkpoint-text seam.
438
+ .PARAMETER PrNumber
439
+ The pull request number the command names.
440
+ .PARAMETER SessionRoot
441
+ The calling process's path, used only to find the repository and label a result.
442
+ Defaults to the current location when blank.
443
+ #>
444
+ [CmdletBinding()]
445
+ [OutputType([pscustomobject])]
446
+ param(
447
+ [Parameter(Mandatory = $true)] [long] $PrNumber,
448
+ [Parameter(Mandatory = $true)] [AllowEmptyString()] [string] $SessionRoot
449
+ )
450
+
451
+ $session = if ([string]::IsNullOrWhiteSpace($SessionRoot)) { (Get-Location).ProviderPath } else { $SessionRoot }
452
+ $noTarget = "pull request {0} is recorded in pr_gate.pr_number or a standalone_merge_authorizations entry of no live worktree's orchestrator checkpoint" -f $PrNumber
453
+ if ($PrNumber -le 0) {
454
+ return (New-WorktreeResolutionTargetResult -Status 'NoTarget' -SessionRoot $session -Detail $noTarget)
455
+ }
456
+ # Assigned before it is wrapped, for the reason recorded in the branch resolver.
457
+ $liveRoots = Get-WorktreeItemLiveRoot -SessionRoot $session
458
+ $matched = [System.Collections.Generic.List[string]]::new()
459
+ foreach ($root in @($liveRoots)) {
460
+ $text = Get-WorktreeItemCheckpointText -Path (Get-WorktreeItemCheckpointPath -WorktreeRoot $root)
461
+ if (Test-WorktreeItemCheckpointRecordsPr -Text $text -PrNumber $PrNumber) { $matched.Add($root) }
462
+ }
463
+ if ($matched.Count -eq 0) {
464
+ return (New-WorktreeResolutionTargetResult -Status 'NoTarget' -SessionRoot $session -Detail $noTarget)
465
+ }
466
+ if ($matched.Count -eq 1) {
467
+ return (ConvertTo-WorktreeItemResolvedResult -WorktreeRoot $matched[0] -SessionRoot $session -Detail (
468
+ "pull request {0} resolves to the live worktree whose orchestrator checkpoint records it" -f $PrNumber))
469
+ }
470
+ return (New-WorktreeResolutionTargetResult -Status 'Ambiguous' -SessionRoot $session -Candidate $matched.ToArray() -Detail (
471
+ "pull request {0} is recorded in the orchestrator checkpoints of {1} live worktrees; move the stale checkpoint to artifacts/orchestration/handoff/ so that one worktree records it" -f $PrNumber, $matched.Count))
472
+ }
473
+
398
474
  Export-ModuleMember -Function `
399
475
  Get-WorktreeItemCheckpointRelativePath, `
400
476
  Get-WorktreeItemCheckpointPath, `
@@ -404,4 +480,5 @@ Export-ModuleMember -Function `
404
480
  Get-WorktreeItemCheckpointIssue, `
405
481
  Get-WorktreeItemLiveRoot, `
406
482
  ConvertTo-WorktreeItemResolvedResult, `
407
- Resolve-WorktreeItemTarget
483
+ Resolve-WorktreeItemTarget, `
484
+ Resolve-WorktreeItemTargetByPrNumber
@@ -340,7 +340,7 @@ function Resolve-WorktreeParallelTarget {
340
340
  }
341
341
 
342
342
  # Private: compare two worktree paths, insensitive to separator style and trailing
343
- # slashes, and case-insensitive only for drive-letter paths.
343
+ # slashes, and case-insensitive for drive-letter and UNC (//) paths.
344
344
  function Test-WorktreeRunPathEqual {
345
345
  [CmdletBinding()]
346
346
  [OutputType([bool])]
@@ -352,8 +352,8 @@ function Test-WorktreeRunPathEqual {
352
352
  if ([string]::IsNullOrWhiteSpace($Left) -or [string]::IsNullOrWhiteSpace($Right)) { return $false }
353
353
  $a = ($Left.Trim() -replace '\\', '/').TrimEnd('/')
354
354
  $b = ($Right.Trim() -replace '\\', '/').TrimEnd('/')
355
- $isDrivePath = ($a -match '^[A-Za-z]:') -or ($b -match '^[A-Za-z]:')
356
- $comparison = if ($isDrivePath) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
355
+ $isCaseInsensitivePath = ($a -match '^([A-Za-z]:|//)') -or ($b -match '^([A-Za-z]:|//)')
356
+ $comparison = if ($isCaseInsensitivePath) { [System.StringComparison]::OrdinalIgnoreCase } else { [System.StringComparison]::Ordinal }
357
357
  return [string]::Equals($a, $b, $comparison)
358
358
  }
359
359
 
@@ -2,7 +2,7 @@
2
2
  paths:
3
3
  - "**/*.ts"
4
4
  - "**/*.cs"
5
- description: Architecture boundary enforcement rules for the No-COM architecture.
5
+ description: Architecture boundary enforcement rules for a host-neutral layered architecture.
6
6
  ---
7
7
 
8
8
  # Architecture Boundaries
@@ -14,7 +14,7 @@ Architecture boundary enforcement is a uniform gate across all tiers (T1–T4).
14
14
  - **TypeScript:** `dependency-cruiser`. Configuration file pattern: `.dependency-cruiser.cjs`.
15
15
  - **.NET (when the backend exists):** `NetArchTest.Rules`. Test project naming pattern: `*.ArchitectureTests`.
16
16
 
17
- ## No-COM Architecture Rules (enforceable assertions)
17
+ ## Host-Neutral Architecture Rules (enforceable assertions)
18
18
 
19
19
  Production code in this repository must satisfy each of the following assertions. Each assertion is enforced by `dependency-cruiser` (TypeScript) or `NetArchTest.Rules` (.NET) where applicable; legacy import utilities, when added, must satisfy the same assertions.
20
20
 
@@ -37,9 +37,9 @@ Production code in this repository must satisfy each of the following assertions
37
37
 
38
38
  ## Layer Boundary Assertions (.NET, applies once the backend exists)
39
39
 
40
- - `TaskMaster.Domain` must have zero references to Outlook PIA, VSTO, or Office.js types.
41
- - `TaskMaster.Application` may depend on `TaskMaster.Domain` only.
42
- - Adapter projects may depend on `TaskMaster.Domain` and `TaskMaster.Application`; domain may not depend on adapters.
40
+ - `<Product>.Domain` must have zero references to Outlook PIA, VSTO, or Office.js types.
41
+ - `<Product>.Application` may depend on `<Product>.Domain` only.
42
+ - Adapter projects may depend on `<Product>.Domain` and `<Product>.Application`; domain may not depend on adapters.
43
43
 
44
44
  ## Enforcement Outcome
45
45
 
@@ -2,12 +2,12 @@
2
2
  paths:
3
3
  - "**/*.cs"
4
4
  - "**/*.csproj"
5
- description: C#-specific toolchain and coding standards (No-COM, xUnit).
5
+ description: C#-specific toolchain and coding standards (xUnit).
6
6
  ---
7
7
 
8
8
  # C# Code Standards
9
9
 
10
- This rule file summarizes the C#-specific policies for this repository. It targets the No-COM .NET foundation: xUnit, NSubstitute, FluentAssertions, `dotnet build`, the analyzer stack, `TimeProvider`, and uniform coverage thresholds.
10
+ This rule file summarizes the C#-specific policies for this repository. It targets a host-neutral .NET foundation: xUnit, NSubstitute, FluentAssertions, `dotnet build`, the analyzer stack, `TimeProvider`, and uniform coverage thresholds.
11
11
 
12
12
  ## Toolchain
13
13
 
@@ -26,7 +26,7 @@ Apply these priorities in order when designing or changing code:
26
26
 
27
27
  ## Module Rigor Tiers
28
28
 
29
- Module rigor tiers (T1–T4) and the uniform-versus-tier-dependent gate matrix are defined in `.claude/rules/quality-tiers.md`. Every project must be classified in `quality-tiers.yml` at repo root.
29
+ Module rigor tiers (T1–T4) and the uniform-versus-tier-dependent gate matrix are defined in `.claude/rules/quality-tiers.md` and apply only when `quality-tiers.yml` exists at the repository root; in that case every project must be classified in it. A repository without `quality-tiers.yml` has not adopted tiers, and the tier requirements do not apply.
30
30
 
31
31
  ## Mandatory Toolchain Loop
32
32
 
@@ -20,10 +20,11 @@ Every unit test must satisfy all five of these properties:
20
20
 
21
21
  ## Coverage Requirements
22
22
 
23
+ - Threshold precedence: when the repository's root `CLAUDE.md` states line or branch coverage thresholds, those thresholds govern. The 85% line and 75% branch figures are defaults that apply only when the root `CLAUDE.md` states none. This precedence applies to every restatement of these figures in other rule files, agents, and skills. Each metric falls back independently: when the root `CLAUDE.md` states only a line threshold or only a branch threshold, the stated threshold governs that metric and the default governs the other.
23
24
  - **Line coverage must remain >= 85% across all tiers (T1–T4).**
24
25
  - **Branch coverage must remain >= 75% across all tiers (T1–T4) for languages whose coverage tooling measures branch coverage.** PowerShell (Pester) and bash (kcov) are the exceptions: neither tool measures branch coverage in any output format, so only the line threshold applies to them and there is no branch-coverage gate. This is a threshold exemption only; PowerShell and bash production files remain in the coverage denominator under the Coverage Exclusion Policy below.
25
26
  - Code changes or refactors must not reduce coverage for the lines that were changed.
26
- - Tier-specific lower coverage thresholds are not used in this repository. See `.claude/rules/quality-tiers.md` for the full tier system.
27
+ - Tier-specific lower coverage thresholds are not used. See `.claude/rules/quality-tiers.md` for the full tier system, which applies only when `quality-tiers.yml` exists at the repository root.
27
28
  - Coverage is a supporting metric, not the sole quality gate. Untested critical behavior is not acceptable even if the overall percentage looks good.
28
29
  - Configure coverage tooling to exclude test files (e.g., `tests/`) so metrics reflect application code, not tests.
29
30
  - Type-only / interface-only modules with no executable behavior may be omitted from coverage measurement. Examples: Python `Protocol`-only modules consumed only under `TYPE_CHECKING`, TypeScript interface/type-only files, and C# interface-only files. Such modules legitimately report 0% executable coverage and may be excluded from measurement. This is a clarification only; it does not lower any coverage threshold.
@@ -86,7 +87,7 @@ Colocation — placing test files alongside production source files in `src/` or
86
87
 
87
88
  ## Test Categories
88
89
 
89
- The following test categories apply across the repository, with tier-dependent obligations per `.claude/rules/quality-tiers.md`:
90
+ The following test categories apply across the repository, with tier-dependent obligations per `.claude/rules/quality-tiers.md`; those tier-dependent obligations apply only when `quality-tiers.yml` exists at the repository root:
90
91
 
91
92
  - **Unit tests** — required for all tiers (T1–T4). Cover single units of behavior in isolation.
92
93
  - **Property-based tests** — required for T1 and T2 modules: at least one property test per pure function. Use `fast-check` (TypeScript) or `hypothesis` (Python) where applicable.