@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
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env bash
2
+ # parallel-mutation-ops.sh: sourceable bash port of the add, close, and requeue
3
+ # functions of the parallel mutation engine (issue #890), so a workspace that
4
+ # received the Claude customization payload can decide an admission, gate a run
5
+ # close, and build the add, close, and requeue mutations[] records without an
6
+ # interpreter. It sources parallel-mutation.sh, which carries the removal
7
+ # functions and the shared message builders.
8
+ #
9
+ # Parity reference (the repository authority; this file reproduces its
10
+ # observable outputs and rejection messages byte for byte):
11
+ # decide_admission scripts/dev_tools/parallel_mutation_protocol.py
12
+ # decide_close scripts/dev_tools/parallel_mutation_protocol.py
13
+ # build_add_entry scripts/dev_tools/_parallel_mutation_entries.py
14
+ # build_close_entry scripts/dev_tools/_parallel_mutation_entries.py
15
+ # build_requeue_entry scripts/dev_tools/_parallel_mutation_entries.py
16
+ # ItemRecord, MutationEntry scripts/dev_tools/_parallel_mutation_models.py
17
+ # rejection messages scripts/dev_tools/_parallel_mutation_errors.py
18
+ #
19
+ # Declared divergences (also listed in both parity lanes):
20
+ # 1. A duplicate key in `close --items` is a usage error (exit 2) raised by
21
+ # the command line; the Python mapping would keep the last record.
22
+ # 2. `at` is a caller-supplied string instead of a `datetime`.
23
+ # 3. Option abbreviations are rejected.
24
+ # 4. `admit` has no rejection branch, in either implementation.
25
+ #
26
+ # Contract. Every public function is pure: no file I/O, no network access, no
27
+ # clock read, and no external utility beyond `sort`. Each sets PM_RESULT to one
28
+ # compact JSON object and returns 0 on success, or sets PM_ERROR to the exact
29
+ # reference message and returns 1 on a rule rejection. Integer arguments are
30
+ # expected in canonical decimal form; the entry point mutate-parallel-run.sh
31
+ # validates them before calling here. Entry validation order follows
32
+ # MutationEntry: the stamped recolor_generation first, then item_key.
33
+ #
34
+ # shellcheck disable=SC2034
35
+ # SC2034 is disabled file-wide because PM_RESULT and PM_ERROR are written here
36
+ # and read by the entry point mutate-parallel-run.sh and by the bats suites,
37
+ # which shellcheck analyses separately.
38
+
39
+ # Resolve this file's own directory so its dependencies source regardless of
40
+ # the caller's working directory.
41
+ PMOPS_LIB_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
42
+ # shellcheck source=.claude/lib/bash/parallel-mutation.sh
43
+ # shellcheck disable=SC1091
44
+ source "$PMOPS_LIB_DIR/parallel-mutation.sh"
45
+
46
+ pm_decide_admission() {
47
+ # Decide whether a candidate joins the current cohort or is deferred.
48
+ #
49
+ # Args: $1 = candidate key, $2 = space-separated `a:b` conflict edges over
50
+ # all items, $3 = space-separated in-flight keys, $4 = space-separated
51
+ # current-cohort member keys. The candidate is deferred when any edge joins
52
+ # it to a key in the union of $3 and $4, in either endpoint position.
53
+ # Returns 0 with PM_RESULT holding the decision; admission never rejects.
54
+ local candidate="$1" edges="$2" edge first second outcome="admit_current_cohort"
55
+ local recompute=false
56
+ PM_RESULT=""
57
+ PM_ERROR=""
58
+ pcoh_split_words "$3 $4"
59
+ local blocking=" ${PCOH_WORDS[*]} "
60
+ pcoh_split_words "$edges"
61
+ local -a edge_list=("${PCOH_WORDS[@]}")
62
+ # Scan every edge for one joining the candidate to a blocking key; the
63
+ # first such edge settles the decision.
64
+ for edge in "${edge_list[@]}"; do
65
+ first="${edge%%:*}"
66
+ second="${edge#*:}"
67
+ if { [[ $first == "$candidate" ]] && [[ $blocking == *" $second "* ]]; } ||
68
+ { [[ $second == "$candidate" ]] && [[ $blocking == *" $first "* ]]; }; then
69
+ outcome="defer_and_recolor"
70
+ recompute=true
71
+ break
72
+ fi
73
+ done
74
+ PM_RESULT="{\"candidate\":$candidate,\"outcome\":\"$outcome\",\"triggers_recompute\":$recompute}"
75
+ return 0
76
+ }
77
+
78
+ pm_build_add_entry() {
79
+ # Build the single mutations[] record for an accepted add.
80
+ #
81
+ # Args: $1 = item key, $2 = `true` for a deferred add (stamps g + 1),
82
+ # otherwise `false` (stamps g), $3 = the generation before the add, $4 =
83
+ # the `at` timestamp string. Returns 0 with PM_RESULT holding the record in
84
+ # F3 field order, 1 with PM_ERROR holding the reference contract message.
85
+ local item_key="$1" deferred="$2" generation="$3" at="$4" stamped
86
+ PM_RESULT=""
87
+ PM_ERROR=""
88
+ stamped="$generation"
89
+ if [[ $deferred == true ]]; then
90
+ stamped=$((generation + 1))
91
+ fi
92
+ if ((stamped < 0)); then
93
+ pm_fail "$(pm_entry_contract_message recolor_generation "$stamped" add)"
94
+ return 1
95
+ fi
96
+ if ((item_key <= 0)); then
97
+ pm_fail "$(pm_entry_contract_message item_key "$item_key" add)"
98
+ return 1
99
+ fi
100
+ PM_RESULT="{\"op\":\"add\",\"item_key\":$item_key,\"at\":$(pm_json_string "$at"),\"prior_state\":null,\"new_state\":\"scheduled\",\"disposition\":null,\"recolor_generation\":$stamped}"
101
+ return 0
102
+ }
103
+
104
+ pm_in_flight_list() {
105
+ # Echo a key list as Python renders a list of ints, ascending.
106
+ #
107
+ # Args: $1 = space-separated keys, at least one.
108
+ local ordered key rendered=""
109
+ pcoh_split_words "$1"
110
+ ordered=$(printf '%s\n' "${PCOH_WORDS[@]}" | LC_ALL=C sort -n)
111
+ while IFS= read -r key; do
112
+ [[ -n $key ]] || continue
113
+ [[ -z $rendered ]] || rendered="$rendered, "
114
+ rendered="$rendered$key"
115
+ done <<<"$ordered"
116
+ printf '[%s]' "$rendered"
117
+ }
118
+
119
+ pm_decide_close() {
120
+ # Gate a run close on no item being in flight.
121
+ #
122
+ # Args: $1 = space-separated `<key>:<state>` tokens, one per tracked item.
123
+ # Each token is validated in supplied order as the reference ItemRecord
124
+ # constructor validates it: a non-positive key names nothing the run
125
+ # tracks, and an out-of-enum state is rejected with the item-state members.
126
+ # Returns 0 with PM_RESULT `{"close_permitted":true}`, or 1 with PM_ERROR
127
+ # naming every in-flight key ascending.
128
+ local token key state in_flight=""
129
+ PM_RESULT=""
130
+ PM_ERROR=""
131
+ pcoh_split_words "$1"
132
+ local -a tokens=("${PCOH_WORDS[@]}")
133
+ for token in "${tokens[@]}"; do
134
+ key="${token%%:*}"
135
+ state="${token#*:}"
136
+ if ((key <= 0)); then
137
+ pm_fail "$(pm_unknown_item_message "$key")"
138
+ return 1
139
+ fi
140
+ if ! pc_enum_members_contains "$PC_VALID_ITEM_STATES" "$state"; then
141
+ pm_fail "$PM_REJECTION_PREFIX item $key state $(pc_repr str "$state") is not one of $PC_VALID_ITEM_STATES."
142
+ return 1
143
+ fi
144
+ if [[ $state == "$PM_PINNED_STATE" ]]; then
145
+ in_flight="$in_flight $key"
146
+ fi
147
+ done
148
+ if [[ -n $in_flight ]]; then
149
+ pm_fail "$PM_REJECTION_PREFIX close requires no item in flight; still in flight: $(pm_in_flight_list "$in_flight")."
150
+ return 1
151
+ fi
152
+ PM_RESULT='{"close_permitted":true}'
153
+ return 0
154
+ }
155
+
156
+ pm_build_close_entry() {
157
+ # Build the single run-scoped mutations[] record for an accepted close.
158
+ #
159
+ # Args: $1 = the generation, stamped unchanged, $2 = the `at` timestamp
160
+ # string. Returns 0 with PM_RESULT holding the record, 1 with PM_ERROR when
161
+ # the generation is negative.
162
+ local generation="$1" at="$2"
163
+ PM_RESULT=""
164
+ PM_ERROR=""
165
+ if ((generation < 0)); then
166
+ pm_fail "$(pm_entry_contract_message recolor_generation "$generation" close)"
167
+ return 1
168
+ fi
169
+ PM_RESULT="{\"op\":\"close\",\"item_key\":null,\"at\":$(pm_json_string "$at"),\"prior_state\":null,\"new_state\":null,\"disposition\":null,\"recolor_generation\":$generation}"
170
+ return 0
171
+ }
172
+
173
+ pm_build_requeue_entry() {
174
+ # Build the single mutations[] record for a drift-induced requeue.
175
+ #
176
+ # Args: $1 = item key, $2 = the generation before the requeue (stamps
177
+ # g + 1), $3 = the `at` timestamp string. Returns 0 with PM_RESULT holding
178
+ # the record, 1 with PM_ERROR holding the reference contract message.
179
+ local item_key="$1" generation="$2" at="$3" stamped
180
+ PM_RESULT=""
181
+ PM_ERROR=""
182
+ stamped=$((generation + 1))
183
+ if ((stamped < 0)); then
184
+ pm_fail "$(pm_entry_contract_message recolor_generation "$stamped" requeue)"
185
+ return 1
186
+ fi
187
+ if ((item_key <= 0)); then
188
+ pm_fail "$(pm_entry_contract_message item_key "$item_key" requeue)"
189
+ return 1
190
+ fi
191
+ PM_RESULT="{\"op\":\"requeue\",\"item_key\":$item_key,\"at\":$(pm_json_string "$at"),\"prior_state\":\"$PM_PINNED_STATE\",\"new_state\":\"blocked\",\"disposition\":null,\"recolor_generation\":$stamped}"
192
+ return 0
193
+ }
@@ -0,0 +1,322 @@
1
+ #!/usr/bin/env bash
2
+ # parallel-mutation.sh: sourceable bash port of the three removal functions of
3
+ # the parallel mutation engine, so a workspace that received the Claude
4
+ # customization payload can decide a removal, recolor the unstarted subgraph,
5
+ # and build the removal's mutations[] record without an interpreter.
6
+ #
7
+ # Parity reference (the repository authority; this file reproduces its
8
+ # observable outputs and rejection messages byte for byte):
9
+ # decide_removal scripts/dev_tools/parallel_mutation_protocol.py
10
+ # recolor_unstarted scripts/dev_tools/parallel_mutation_protocol.py
11
+ # build_remove_entry scripts/dev_tools/_parallel_mutation_entries.py
12
+ # MutationEntry scripts/dev_tools/_parallel_mutation_models.py
13
+ # rejection messages scripts/dev_tools/_parallel_mutation_errors.py
14
+ #
15
+ # Declared divergences (also listed in both parity lanes):
16
+ # 1. A `--removal-disposition` value outside `detach abandon` is a usage error
17
+ # (exit 2) raised by the command line before the library runs. A direct
18
+ # library call and the Python `UnknownEnumMemberError` both reject it with
19
+ # the same message, which lists `detach, abandon`
20
+ # (spec D5; aligned under #890).
21
+ # 2. `at` is a caller-supplied string instead of a `datetime`.
22
+ # 3. Option abbreviations are rejected.
23
+ #
24
+ # Contract. Every public function is pure: no file I/O, no network access, no
25
+ # clock read, and no external utility beyond the ones parallel-cohorts.sh
26
+ # already uses. Each sets PM_RESULT to one compact JSON object and returns 0 on
27
+ # success, or sets PM_ERROR to the exact reference message and returns 1 on a
28
+ # rule rejection. Integer arguments are expected in canonical decimal form; the
29
+ # entry point remove-parallel-item.sh validates them before calling here.
30
+ #
31
+ # shellcheck disable=SC2034
32
+ # SC2034 is disabled file-wide because PM_RESULT and PM_ERROR are written here
33
+ # and read by the entry point remove-parallel-item.sh and by the bats suites,
34
+ # which shellcheck analyses separately.
35
+
36
+ # Resolve this file's own directory so its dependencies source regardless of
37
+ # the caller's working directory.
38
+ PM_LIB_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
39
+ # shellcheck source=.claude/lib/bash/parallel-cohorts.sh
40
+ # shellcheck disable=SC1091
41
+ source "$PM_LIB_DIR/parallel-cohorts.sh"
42
+
43
+ # The compact JSON object produced by the most recent success.
44
+ PM_RESULT=""
45
+
46
+ # The rejection message set by the most recent non-zero return.
47
+ PM_ERROR=""
48
+
49
+ # Literal prefix shared by every engine rejection message.
50
+ PM_REJECTION_PREFIX="Parallel mutation rejected:"
51
+
52
+ # Item states whose items have not started (vertices of the recolored subgraph).
53
+ PM_UNSTARTED_STATES="proposed admitted prepared scheduled"
54
+
55
+ # The single item state that pins an item against recoloring.
56
+ PM_PINNED_STATE="in_flight"
57
+
58
+ # The two accepted dispositions of an in-flight removal, in canonical order.
59
+ PM_VALID_DISPOSITIONS="detach abandon"
60
+
61
+ pm_fail() {
62
+ # Record a rejection message and return 1.
63
+ #
64
+ # Args: $1 = the complete, literal rejection message.
65
+ PM_ERROR="$1"
66
+ return 1
67
+ }
68
+
69
+ pm_json_string() {
70
+ # Echo a JSON string literal for a raw value.
71
+ #
72
+ # Args: $1 = the raw value. Escapes the backslash first so a later escape's
73
+ # own backslash is not doubled, then the quote and the three control
74
+ # characters a caller-supplied token could carry.
75
+ local body="$1"
76
+ body=${body//\\/\\\\}
77
+ body=${body//\"/\\\"}
78
+ body=${body//$'\n'/\\n}
79
+ body=${body//$'\r'/\\r}
80
+ body=${body//$'\t'/\\t}
81
+ printf '"%s"' "$body"
82
+ }
83
+
84
+ pm_json_nullable_string() {
85
+ # Echo a JSON string literal, or null for an empty value.
86
+ #
87
+ # Args: $1 = the raw value; empty means absent (Python None).
88
+ if [[ -z ${1-} ]]; then
89
+ printf 'null'
90
+ else
91
+ pm_json_string "$1"
92
+ fi
93
+ }
94
+
95
+ pm_unknown_item_message() {
96
+ # Echo the UnknownItemError message for one item key.
97
+ #
98
+ # Args: $1 = the offending integer key, rendered as Python repr renders it.
99
+ printf '%s item key %s does not resolve to a tracked items[].issue_num.' \
100
+ "$PM_REJECTION_PREFIX" "$1"
101
+ }
102
+
103
+ pm_entry_contract_message() {
104
+ # Echo the MutationEntryContractError message for one op.
105
+ #
106
+ # Args: $1 = field name, $2 = the already-rendered repr of the value, $3 =
107
+ # the op name, optional and defaulting to `remove` so every removal call
108
+ # site passes two arguments unchanged.
109
+ printf "Parallel mutation entry rejected: %s %s violates the F3 mutations[] contract for op '%s'." \
110
+ "$1" "$2" "${3:-remove}"
111
+ }
112
+
113
+ pm_decide_removal() {
114
+ # Decide the outcome of removing one item, per the FR2 behavior table.
115
+ #
116
+ # Args: $1 = item key, $2 = the item's state, or empty when the key resolves
117
+ # to no tracked item, $3 = disposition, or empty when none was supplied.
118
+ # Returns 0 with PM_RESULT holding the decision, 1 with PM_ERROR holding the
119
+ # reference rejection message.
120
+ local item_key="$1" state="${2-}" disposition="${3-}" prior
121
+ PM_RESULT=""
122
+ PM_ERROR=""
123
+ # An absent record and a non-positive key both name nothing the run tracks;
124
+ # the reference rejects the latter while constructing the item record.
125
+ if [[ -z $state ]] || ((item_key <= 0)); then
126
+ pm_fail "$(pm_unknown_item_message "$item_key")"
127
+ return 1
128
+ fi
129
+ if ! pc_enum_members_contains "$PC_VALID_ITEM_STATES" "$state"; then
130
+ pm_fail "$PM_REJECTION_PREFIX item $item_key state $(pc_repr str "$state") is not one of $PC_VALID_ITEM_STATES."
131
+ return 1
132
+ fi
133
+
134
+ # Routing table, one branch per FR2 row. An unstarted item is a vertex of
135
+ # the recolored subgraph, so its removal recomputes and records no
136
+ # disposition even when one was supplied.
137
+ if pc_contains_word "$PM_UNSTARTED_STATES" "$state"; then
138
+ PM_RESULT="{\"item_key\":$item_key,\"prior_state\":$(pm_json_string "$state"),\"new_state\":\"withdrawn\",\"disposition\":null,\"triggers_recompute\":true}"
139
+ return 0
140
+ fi
141
+ if [[ $state == "$PM_PINNED_STATE" ]]; then
142
+ if [[ -z $disposition ]]; then
143
+ pm_fail "$PM_REJECTION_PREFIX removal of in-flight item $item_key requires an explicit disposition, one of detach, abandon; no default is inferred."
144
+ return 1
145
+ fi
146
+ if ! pc_contains_word "$PM_VALID_DISPOSITIONS" "$disposition"; then
147
+ pm_fail "$PM_REJECTION_PREFIX item $item_key disposition $(pc_repr str "$disposition") is not one of ${PM_VALID_DISPOSITIONS// /, }."
148
+ return 1
149
+ fi
150
+ prior="$PM_PINNED_STATE"
151
+ PM_RESULT="{\"item_key\":$item_key,\"prior_state\":\"$prior\",\"new_state\":\"withdrawn\",\"disposition\":\"$disposition\",\"triggers_recompute\":false}"
152
+ return 0
153
+ fi
154
+ if [[ $state == merged ]]; then
155
+ pm_fail "$PM_REJECTION_PREFIX item $item_key is already merged into main and cannot be removed."
156
+ return 1
157
+ fi
158
+ # Only withdrawn and blocked remain; neither is a live removal target.
159
+ pm_fail "$(pm_unknown_item_message "$item_key")"
160
+ return 1
161
+ }
162
+
163
+ pm_smallest_overlap() {
164
+ # Echo the smallest key present in both key lists, or nothing.
165
+ #
166
+ # Args: $1 = space-separated unstarted keys, $2 = space-separated pinned keys.
167
+ local key smallest=""
168
+ pcoh_split_words "$1"
169
+ local -a unstarted_keys=("${PCOH_WORDS[@]}")
170
+ pcoh_split_words "$2"
171
+ local pinned=" ${PCOH_WORDS[*]} "
172
+ for key in "${unstarted_keys[@]}"; do
173
+ [[ $pinned == *" $key "* ]] || continue
174
+ if [[ -z $smallest ]] || ((key < smallest)); then
175
+ smallest="$key"
176
+ fi
177
+ done
178
+ printf '%s' "$smallest"
179
+ }
180
+
181
+ pm_render_assignments() {
182
+ # Echo the cohort_assignments object for a cohort list and an offset.
183
+ #
184
+ # Args: $1 = compact JSON array of arrays from pcoh_compute_cohorts, $2 = the
185
+ # uniform offset. List position is the local color index; keys are emitted
186
+ # in ascending numeric order, matching the reference rendering.
187
+ local cohorts="$1" offset="$2" group key index=0 lines="" ordered rendered=""
188
+ # Strip the outer brackets and the first and last inner bracket, then turn
189
+ # each `],[` boundary into a space so every group is one comma-joined word.
190
+ cohorts=${cohorts#[}
191
+ cohorts=${cohorts%]}
192
+ cohorts=${cohorts#[}
193
+ cohorts=${cohorts%]}
194
+ pcoh_split_words "${cohorts//'],['/ }"
195
+ local -a groups=("${PCOH_WORDS[@]}")
196
+ for group in "${groups[@]}"; do
197
+ pcoh_split_words "${group//,/ }"
198
+ for key in "${PCOH_WORDS[@]}"; do
199
+ lines="$lines$key $((offset + index))"$'\n'
200
+ done
201
+ index=$((index + 1))
202
+ done
203
+ if [[ -n $lines ]]; then
204
+ ordered=$(printf '%s' "$lines" | LC_ALL=C sort -k1,1n)
205
+ while IFS=' ' read -r key index; do
206
+ [[ -n $key ]] || continue
207
+ [[ -z $rendered ]] || rendered="$rendered,"
208
+ rendered="$rendered\"$key\":$index"
209
+ done <<<"$ordered"
210
+ fi
211
+ printf '{%s}' "$rendered"
212
+ }
213
+
214
+ pm_recolor_unstarted() {
215
+ # Recolor the unstarted subgraph, leaving every pinned item untouched.
216
+ #
217
+ # Args: $1 = space-separated unstarted keys, $2 = space-separated `a:b`
218
+ # conflict edges over all items, $3 = space-separated pinned keys, $4 = the
219
+ # generation before this recolor, $5 = current_cohort, $6 =
220
+ # highest_pinned_cohort. Returns 0 with PM_RESULT holding the assignments
221
+ # and the next generation, 1 with PM_ERROR holding the reference message.
222
+ local unstarted="$1" edges="$2" pinned="$3" generation="$4"
223
+ local current_cohort="$5" highest_pinned="$6"
224
+ local overlap edge first second crosses=0 induced="" offset
225
+ PM_RESULT=""
226
+ PM_ERROR=""
227
+
228
+ # Check order is part of the contract: overlap, then the negative base,
229
+ # then the pinned barrier, then the induced coloring.
230
+ overlap=$(pm_smallest_overlap "$unstarted" "$pinned")
231
+ if [[ -n $overlap ]]; then
232
+ pm_fail "$(pm_unknown_item_message "$overlap")"
233
+ return 1
234
+ fi
235
+ if ((current_cohort < 0)); then
236
+ pm_fail "current_cohort must be >= 0 per F3 invariant 12; received $current_cohort."
237
+ return 1
238
+ fi
239
+
240
+ pcoh_split_words "$unstarted"
241
+ local unstarted_set=" ${PCOH_WORDS[*]} "
242
+ pcoh_split_words "$pinned"
243
+ local pinned_set=" ${PCOH_WORDS[*]} "
244
+ pcoh_split_words "$edges"
245
+ local -a edge_list=("${PCOH_WORDS[@]}")
246
+ # Decide the pinned barrier before the induced restriction discards the
247
+ # unstarted-to-pinned edges, then keep only edges with both ends unstarted.
248
+ for edge in "${edge_list[@]}"; do
249
+ first="${edge%%:*}"
250
+ second="${edge#*:}"
251
+ if [[ $unstarted_set == *" $first "* && $pinned_set == *" $second "* ]] ||
252
+ [[ $unstarted_set == *" $second "* && $pinned_set == *" $first "* ]]; then
253
+ crosses=1
254
+ fi
255
+ if [[ $unstarted_set == *" $first "* && $unstarted_set == *" $second "* ]]; then
256
+ induced="$induced $edge"
257
+ fi
258
+ done
259
+
260
+ if ! pcoh_compute_cohorts "$unstarted" "$induced"; then
261
+ pm_fail "$PCOH_ERROR"
262
+ return 1
263
+ fi
264
+
265
+ # A uniform shift keeps the local-to-absolute map injective.
266
+ if ((crosses == 1)); then
267
+ offset=$((highest_pinned + 1))
268
+ else
269
+ offset="$current_cohort"
270
+ fi
271
+ PM_RESULT="{\"cohort_assignments\":$(pm_render_assignments "$PCOH_RESULT" "$offset"),\"generation\":$((generation + 1))}"
272
+ return 0
273
+ }
274
+
275
+ pm_build_remove_entry() {
276
+ # Build the single mutations[] record for an accepted removal.
277
+ #
278
+ # Args: $1 = item key, $2 = prior state, $3 = disposition, or empty for
279
+ # none, $4 = `true` when the removal recomputes, otherwise `false`, $5 = the
280
+ # generation before the removal, $6 = the `at` timestamp string. Returns 0
281
+ # with PM_RESULT holding the record in F3 field order, 1 with PM_ERROR
282
+ # holding the reference contract message.
283
+ local item_key="$1" prior_state="$2" disposition="${3-}" recompute="$4"
284
+ local generation="$5" at="$6" stamped
285
+ PM_RESULT=""
286
+ PM_ERROR=""
287
+ # The stamped generation is computed first; the record validation then
288
+ # runs in field order, so the first violation names the earliest field.
289
+ stamped="$generation"
290
+ if [[ $recompute == true ]]; then
291
+ stamped=$((generation + 1))
292
+ fi
293
+ if ((stamped < 0)); then
294
+ pm_fail "$(pm_entry_contract_message recolor_generation "$stamped")"
295
+ return 1
296
+ fi
297
+ if ((item_key <= 0)); then
298
+ pm_fail "$(pm_entry_contract_message item_key "$item_key")"
299
+ return 1
300
+ fi
301
+ if ! pc_enum_members_contains "$PC_VALID_ITEM_STATES" "$prior_state"; then
302
+ pm_fail "$PM_REJECTION_PREFIX item $item_key state $(pc_repr str "$prior_state") is not one of $PC_VALID_ITEM_STATES."
303
+ return 1
304
+ fi
305
+ # An in-flight removal must record a valid disposition; every other
306
+ # removal must record none.
307
+ if [[ $prior_state == "$PM_PINNED_STATE" ]]; then
308
+ if [[ -z $disposition ]]; then
309
+ pm_fail "$(pm_entry_contract_message disposition None)"
310
+ return 1
311
+ fi
312
+ if ! pc_contains_word "$PM_VALID_DISPOSITIONS" "$disposition"; then
313
+ pm_fail "$(pm_entry_contract_message disposition "$(pc_repr str "$disposition")")"
314
+ return 1
315
+ fi
316
+ elif [[ -n $disposition ]]; then
317
+ pm_fail "$(pm_entry_contract_message disposition "$(pc_repr str "$disposition")")"
318
+ return 1
319
+ fi
320
+ PM_RESULT="{\"op\":\"remove\",\"item_key\":$item_key,\"at\":$(pm_json_string "$at"),\"prior_state\":$(pm_json_string "$prior_state"),\"new_state\":\"withdrawn\",\"disposition\":$(pm_json_nullable_string "$disposition"),\"recolor_generation\":$stamped}"
321
+ return 0
322
+ }
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env bash
2
+ # remove-parallel-item.sh: destination-portable command-line entry point for
3
+ # the parallel-remove skill's decision, recolor, and audit-record steps. It
4
+ # exists so a workspace that received the Claude customization payload can run
5
+ # those steps with nothing but bash -- no interpreter, no repository checkout.
6
+ #
7
+ # Usage:
8
+ # bash .claude/lib/bash/remove-parallel-item.sh decide --item <key> \
9
+ # [--state <state>] [--removal-disposition detach|abandon]
10
+ # bash .claude/lib/bash/remove-parallel-item.sh recolor --unstarted "<k> ..." \
11
+ # [--edges "<a>:<b> ..."] --pinned "<k> ..." --generation <g> \
12
+ # --current-cohort <c> --highest-pinned-cohort <h>
13
+ # bash .claude/lib/bash/remove-parallel-item.sh entry --item <key> \
14
+ # --prior-state <state> [--removal-disposition detach|abandon] \
15
+ # --recompute true|false --generation <g> [--at <timestamp>]
16
+ #
17
+ # `decide` without `--state` means the key resolves to no tracked item.
18
+ # `--unstarted`, `--edges`, and `--pinned` accept an empty string. Integer
19
+ # tokens match `-?(0|[1-9][0-9]*)`. `--at` defaults to the current UTC time as
20
+ # yyyy-MM-ddTHH-mm; the clock is read only when `--at` is omitted.
21
+ #
22
+ # Exit-code contract:
23
+ # exit 0 success; one compact JSON object on stdout
24
+ # exit 1 rule rejection; the reference implementation's exact message as
25
+ # one stderr line
26
+ # exit 2 usage error; one stderr line beginning
27
+ # "PARALLEL_MUTATION_ERROR: usage error:"
28
+ # Argument validation runs first, then rule rejection, then output.
29
+ #
30
+ # Parity reference: scripts/dev_tools/parallel_mutation_protocol.py
31
+ # (decide_removal, recolor_unstarted) and
32
+ # scripts/dev_tools/_parallel_mutation_entries.py (build_remove_entry).
33
+ #
34
+ # Declared divergences (also listed in both parity lanes):
35
+ # 1. A `--removal-disposition` value outside `detach abandon` is a usage error
36
+ # (exit 2) raised by the command line before the library runs. A direct
37
+ # library call and the Python `UnknownEnumMemberError` both reject it with
38
+ # the same message, which lists `detach, abandon`
39
+ # (spec D5; aligned under #890).
40
+ # 2. `at` is a caller-supplied string instead of a `datetime`.
41
+ # 3. Option abbreviations are rejected.
42
+ set -euo pipefail
43
+
44
+ # Resolve this script's own directory so the libraries source regardless of
45
+ # cwd. The option-parsing helpers (pmcli_*) are shared with
46
+ # mutate-parallel-run.sh through parallel-mutation-cli.sh (issue #890).
47
+ RPI_SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
48
+ # shellcheck source=.claude/lib/bash/parallel-mutation.sh
49
+ # shellcheck disable=SC1091
50
+ source "$RPI_SCRIPT_DIR/parallel-mutation.sh"
51
+ # shellcheck source=.claude/lib/bash/parallel-mutation-cli.sh
52
+ # shellcheck disable=SC1091
53
+ source "$RPI_SCRIPT_DIR/parallel-mutation-cli.sh"
54
+
55
+ pc_enforce_c_locale
56
+
57
+ rpi_disposition() {
58
+ # Validate the optional disposition; store it, or empty when absent.
59
+ PMCLI_VALUE="${PMCLI_OPTS["removal-disposition"]-}"
60
+ if [[ -n ${PMCLI_OPTS["removal-disposition"]+set} ]] && ! pc_contains_word "detach abandon" "$PMCLI_VALUE"; then
61
+ pmcli_usage_error "--removal-disposition must be one of detach, abandon; found: $PMCLI_VALUE"
62
+ fi
63
+ }
64
+
65
+ rpi_decide() {
66
+ # Run the decide subcommand.
67
+ local item state="" disposition rc=0
68
+ pmcli_parse_options "item state removal-disposition" "$@"
69
+ pmcli_require item
70
+ pmcli_integer "${PMCLI_OPTS[item]}" "--item"
71
+ item="$PMCLI_VALUE"
72
+ if [[ -n ${PMCLI_OPTS[state]+set} ]]; then
73
+ state="${PMCLI_OPTS[state]}"
74
+ pc_enum_members_contains "$PC_VALID_ITEM_STATES" "$state" ||
75
+ pmcli_usage_error "--state must be one of $PC_VALID_ITEM_STATES; found: $state"
76
+ fi
77
+ rpi_disposition
78
+ disposition="$PMCLI_VALUE"
79
+ pm_decide_removal "$item" "$state" "$disposition" || rc=$?
80
+ pmcli_emit "$rc"
81
+ }
82
+
83
+ rpi_recolor() {
84
+ # Run the recolor subcommand.
85
+ local unstarted edges pinned generation current highest rc=0
86
+ pmcli_parse_options "unstarted edges pinned generation current-cohort highest-pinned-cohort" "$@"
87
+ pmcli_require unstarted pinned generation current-cohort highest-pinned-cohort
88
+ pmcli_integer_list "${PMCLI_OPTS[unstarted]}" "--unstarted"
89
+ unstarted="$PMCLI_VALUE"
90
+ pmcli_edge_list "${PMCLI_OPTS[edges]-}"
91
+ edges="$PMCLI_VALUE"
92
+ pmcli_integer_list "${PMCLI_OPTS[pinned]}" "--pinned"
93
+ pinned="$PMCLI_VALUE"
94
+ pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
95
+ generation="$PMCLI_VALUE"
96
+ pmcli_integer "${PMCLI_OPTS["current-cohort"]}" "--current-cohort"
97
+ current="$PMCLI_VALUE"
98
+ pmcli_integer "${PMCLI_OPTS["highest-pinned-cohort"]}" "--highest-pinned-cohort"
99
+ highest="$PMCLI_VALUE"
100
+ pm_recolor_unstarted "$unstarted" "$edges" "$pinned" "$generation" "$current" "$highest" || rc=$?
101
+ pmcli_emit "$rc"
102
+ }
103
+
104
+ rpi_entry() {
105
+ # Run the entry subcommand.
106
+ local item prior disposition recompute generation at rc=0
107
+ pmcli_parse_options "item prior-state removal-disposition recompute generation at" "$@"
108
+ pmcli_require item prior-state recompute generation
109
+ pmcli_integer "${PMCLI_OPTS[item]}" "--item"
110
+ item="$PMCLI_VALUE"
111
+ prior="${PMCLI_OPTS["prior-state"]}"
112
+ [[ $prior =~ ^[A-Za-z_]+$ ]] || pmcli_usage_error "--prior-state must match [A-Za-z_]+; found: $prior"
113
+ rpi_disposition
114
+ disposition="$PMCLI_VALUE"
115
+ recompute="${PMCLI_OPTS[recompute]}"
116
+ [[ $recompute == true || $recompute == false ]] ||
117
+ pmcli_usage_error "--recompute must be true or false; found: $recompute"
118
+ pmcli_integer "${PMCLI_OPTS[generation]}" "--generation"
119
+ generation="$PMCLI_VALUE"
120
+ # The clock is read only when --at is omitted.
121
+ pmcli_timestamp
122
+ at="$PMCLI_VALUE"
123
+ pm_build_remove_entry "$item" "$prior" "$disposition" "$recompute" "$generation" "$at" || rc=$?
124
+ pmcli_emit "$rc"
125
+ }
126
+
127
+ rpi_main() {
128
+ # Dispatch on the subcommand.
129
+ (($# > 0)) || pmcli_usage_error "missing subcommand; expected one of decide, recolor, entry"
130
+ local subcommand="$1"
131
+ shift
132
+ case "$subcommand" in
133
+ decide) rpi_decide "$@" ;;
134
+ recolor) rpi_recolor "$@" ;;
135
+ entry) rpi_entry "$@" ;;
136
+ *) pmcli_usage_error "unknown subcommand: $subcommand" ;;
137
+ esac
138
+ }
139
+
140
+ # Guard so the file can be sourced without executing main. main's return code
141
+ # is captured and re-exited explicitly as the final statement.
142
+ if [[ ${BASH_SOURCE[0]} == "${0}" ]]; then
143
+ rpi_rc=0
144
+ rpi_main "$@" || rpi_rc=$?
145
+ exit "$rpi_rc"
146
+ fi