@danmoisan/drm-copilot-mcp 1.1.25 → 1.1.27

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 (185) hide show
  1. package/out/mcp-server.js +170 -52
  2. package/package.json +1 -1
  3. package/resources/claude-customizations/.claude/agents/atomic-executor.md +3 -3
  4. package/resources/claude-customizations/.claude/agents/atomic-planner.md +2 -2
  5. package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +7 -7
  6. package/resources/claude-customizations/.claude/agents/feature-review.md +6 -3
  7. package/resources/claude-customizations/.claude/agents/orchestrator.md +11 -7
  8. package/resources/claude-customizations/.claude/agents/parallel-orchestrator.md +7 -3
  9. package/resources/claude-customizations/.claude/agents/parallel-planner.md +6 -2
  10. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +7 -7
  11. package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +6 -6
  12. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
  13. package/resources/claude-customizations/.claude/hooks/enforce-epic-merge-gate.ps1 +16 -1
  14. package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +41 -0
  15. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate-cleanup.ps1 +481 -0
  16. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +3 -1
  17. package/resources/claude-customizations/.claude/hooks/enforce-parallel-cohort-barrier-helpers.ps1 +69 -3
  18. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill-helpers.ps1 +2 -2
  19. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.cleanup.ps1 +246 -0
  20. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.docs-only.ps1 +1 -0
  21. package/resources/claude-customizations/.claude/hooks/hook-cleanup-context.ps1 +343 -0
  22. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage-rules.ps1 +50 -16
  23. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorState.psm1 +6 -1
  24. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletion.psm1 +4 -2
  25. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateCompletionChecks.psm1 +51 -2
  26. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateReceipts.psm1 +5 -2
  27. package/resources/claude-customizations/.claude/lib/orchestrator-state/OrchestratorStateRemediationAccounting.psm1 +116 -4
  28. package/resources/claude-customizations/.claude/lib/parallel-admission/Invoke-ParallelAdmission.ps1 +626 -0
  29. package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmission.psm1 +508 -0
  30. package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmissionJson.psm1 +141 -0
  31. package/resources/claude-customizations/.claude/lib/parallel-admission/ParallelAdmissionPrecedence.psm1 +508 -0
  32. package/resources/claude-customizations/.claude/rules/csharp.md +8 -8
  33. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +28 -2
  34. package/resources/claude-customizations/.claude/rules/parallel-orchestration.md +17 -1
  35. package/resources/claude-customizations/.claude/rules/powershell.md +2 -2
  36. package/resources/claude-customizations/.claude/settings.json +1 -0
  37. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +9 -13
  38. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/SKILL.md +93 -14
  39. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup-worktrees.sh +23 -10
  40. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_lib.sh +54 -46
  41. package/resources/claude-customizations/.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_merge_lib.sh +234 -0
  42. package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +14 -15
  43. package/resources/claude-customizations/.claude/skills/epic-progress-report/SKILL.md +338 -0
  44. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Logic.ps1 +248 -0
  45. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Metrics.ps1 +232 -0
  46. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.Render.ps1 +196 -0
  47. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.State.ps1 +286 -0
  48. package/resources/claude-customizations/.claude/skills/epic-progress-report/scripts/Get-EpicProgressReport.ps1 +475 -0
  49. package/resources/claude-customizations/.claude/skills/execution-environment/SKILL.md +31 -16
  50. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +21 -9
  51. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +5 -5
  52. package/resources/claude-customizations/.claude/skills/invoke-powershell-engineer/SKILL.md +4 -4
  53. package/resources/claude-customizations/.claude/skills/invoke-python-engineer/SKILL.md +4 -4
  54. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +24 -2
  55. package/resources/claude-customizations/.claude/skills/parallel-add/SKILL.md +71 -7
  56. package/resources/claude-customizations/.claude/skills/parallel-orchestrate/SKILL.md +34 -2
  57. package/resources/claude-customizations/.claude/skills/parallel-plan/SKILL.md +82 -12
  58. package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +16 -17
  59. package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +15 -16
  60. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +1 -1
  61. package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +6 -6
  62. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
  63. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
  64. package/resources/claude-customizations/pack-manifests/core.json +15 -0
  65. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +8 -12
  66. package/resources/codex-and-agents-customizations/.agents/skills/atomic-planner/SKILL.md +1 -1
  67. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +13 -14
  68. package/resources/codex-and-agents-customizations/.agents/skills/execution-environment/SKILL.md +31 -16
  69. package/resources/codex-and-agents-customizations/.agents/skills/feature-review/SKILL.md +5 -1
  70. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +18 -5
  71. package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +4 -4
  72. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +4 -4
  73. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +4 -4
  74. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +24 -2
  75. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +16 -1
  76. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +3 -3
  77. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +2 -2
  78. package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +15 -16
  79. package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +14 -15
  80. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +2 -0
  81. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +13 -14
  82. package/resources/codex-and-agents-customizations/.agents-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +4 -4
  83. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c1.toml +3 -3
  84. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c2.toml +3 -3
  85. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3-elevated.toml +3 -3
  86. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c3.toml +3 -3
  87. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor-c4.toml +3 -3
  88. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +3 -3
  89. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c1.toml +2 -2
  90. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c2.toml +2 -2
  91. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3-elevated.toml +2 -2
  92. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c3.toml +2 -2
  93. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner-c4.toml +2 -2
  94. package/resources/codex-and-agents-customizations/.codex/agents/atomic-planner.toml +2 -2
  95. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-executor.toml +2 -2
  96. package/resources/codex-and-agents-customizations/.codex/agents/csharp-atomic-planning.toml +1 -1
  97. package/resources/codex-and-agents-customizations/.codex/agents/csharp-orchestrator.toml +1 -1
  98. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c1.toml +6 -6
  99. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c2.toml +6 -6
  100. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3-elevated.toml +6 -6
  101. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c3.toml +6 -6
  102. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer-c4.toml +6 -6
  103. package/resources/codex-and-agents-customizations/.codex/agents/csharp-typed-engineer.toml +6 -6
  104. package/resources/codex-and-agents-customizations/.codex/agents/feature-review.toml +2 -2
  105. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +2 -2
  106. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-planning.toml +1 -1
  107. package/resources/codex-and-agents-customizations/.codex/agents/powershell-di-unit-test-engineer.toml +1 -1
  108. package/resources/codex-and-agents-customizations/.codex/agents/powershell-orchestrator.toml +1 -1
  109. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c1.toml +7 -7
  110. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c2.toml +7 -7
  111. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3-elevated.toml +7 -7
  112. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c3.toml +7 -7
  113. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer-c4.toml +7 -7
  114. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +7 -7
  115. package/resources/codex-and-agents-customizations/.codex/agents/pytest-unit-test-coding.toml +1 -1
  116. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-executor.toml +2 -2
  117. package/resources/codex-and-agents-customizations/.codex/agents/python-atomic-planning.toml +1 -1
  118. package/resources/codex-and-agents-customizations/.codex/agents/python-execution-only-typed.toml +1 -1
  119. package/resources/codex-and-agents-customizations/.codex/agents/python-orchestrator.toml +1 -1
  120. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c1.toml +6 -6
  121. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c2.toml +6 -6
  122. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3-elevated.toml +6 -6
  123. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c3.toml +6 -6
  124. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer-c4.toml +6 -6
  125. package/resources/codex-and-agents-customizations/.codex/agents/python-typed-engineer.toml +6 -6
  126. package/resources/codex-and-agents-customizations/.codex/config.toml +1 -1
  127. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate-review.ps1 +699 -0
  128. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-epic-merge-gate.ps1 +29 -2
  129. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate-cleanup.ps1 +481 -0
  130. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +4 -2
  131. package/resources/codex-and-agents-customizations/.codex/hooks/hook-cleanup-context.ps1 +343 -0
  132. package/resources/codex-and-agents-customizations/.codex/hooks/validate-feature-review-coverage.ps1 +51 -4
  133. package/resources/codex-and-agents-customizations/.codex-variants/csharp-legacy/agents/csharp-typed-engineer.toml +6 -6
  134. package/resources/codex-and-agents-customizations/pack-manifests/core.json +3 -0
  135. package/resources/customizations/.github/agents/5.1-Beast-adjusted.agent.md +1 -1
  136. package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +20 -26
  137. package/resources/customizations/.github/agents/atomic_executor.agent.md +4 -4
  138. package/resources/customizations/.github/agents/atomic_planning.agent.md +4 -4
  139. package/resources/customizations/.github/agents/commentary-remediation.agent.md +1 -1
  140. package/resources/customizations/.github/agents/commit-steward.agent.md +1 -1
  141. package/resources/customizations/.github/agents/csharp-atomic-executor.agent.md +14 -14
  142. package/resources/customizations/.github/agents/csharp-atomic-planning.agent.md +2 -2
  143. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +4 -4
  144. package/resources/customizations/.github/agents/csharp-typed-engineer.agent.md +19 -22
  145. package/resources/customizations/.github/agents/epic-review.agent.md +1 -1
  146. package/resources/customizations/.github/agents/gpt-5-beast-mode.agent.md +3 -3
  147. package/resources/customizations/.github/agents/orchestrator.agent.md +8 -8
  148. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +12 -12
  149. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +4 -4
  150. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -6
  151. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +21 -24
  152. package/resources/customizations/.github/agents/pytest-unit-test-coding.agent.md +13 -17
  153. package/resources/customizations/.github/agents/python-atomic-executor.agent.md +11 -11
  154. package/resources/customizations/.github/agents/python-atomic-planning.agent.md +2 -2
  155. package/resources/customizations/.github/agents/python-execution-only-typed.agent.md +14 -17
  156. package/resources/customizations/.github/agents/python-orchestrator.agent.md +7 -7
  157. package/resources/customizations/.github/agents/python-typed-engineer.agent.md +17 -20
  158. package/resources/customizations/.github/agents/staged-review.agent.md +2 -2
  159. package/resources/customizations/.github/agents/tdd-green.agent.md +1 -1
  160. package/resources/customizations/.github/agents/tdd-red.agent.md +3 -3
  161. package/resources/customizations/.github/agents/tdd-refactor.agent.md +2 -2
  162. package/resources/customizations/.github/agents/typescript-engineer.agent.md +3 -3
  163. package/resources/customizations/.github/codex/execute-hard-lock.prompt.md +3 -3
  164. package/resources/customizations/.github/instructions/general-code-change.instructions.md +1 -1
  165. package/resources/customizations/.github/instructions/powershell-unit-test.instructions.md +1 -1
  166. package/resources/customizations/.github/instructions/python-code-change.instructions.md +5 -5
  167. package/resources/customizations/.github/instructions/python-suppressions.instructions.md +3 -3
  168. package/resources/customizations/.github/instructions/python-unit-test.instructions.md +1 -1
  169. package/resources/customizations/.github/prompts/execute-hard-lock.prompt.md +2 -2
  170. package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +2 -2
  171. package/resources/customizations/.github/prompts/remediate-comments.prompt.md +2 -2
  172. package/resources/customizations/.github/prompts/review-feature.prompt.md +1 -1
  173. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +8 -12
  174. package/resources/customizations/.github/skills/execution-environment/SKILL.md +31 -16
  175. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +15 -2
  176. package/resources/feature-templates/bug/plan.yyyy-MM-ddTHH-mm.md +4 -4
  177. package/resources/feature-templates/feature/plan.yyyy-MM-ddTHH-mm.md +3 -3
  178. package/resources/feature-templates/refactor/plan.yyyy-MM-ddTHH-mm.md +2 -2
  179. package/resources/powershell/PoshQC/PoshQC.ScanConfig.psm1 +3 -1
  180. package/resources/powershell/PoshQC/PoshQC.Testing.psm1 +14 -12
  181. package/resources/powershell/PoshQC/PoshQC.psm1 +3 -3
  182. package/resources/powershell/PoshQC/README.md +8 -8
  183. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +2 -2
  184. package/resources/templates/policy_audit/AGENTS.md +9 -10
  185. package/resources/templates/policy_audit/policy-audit.yyyy-MM-ddTHH-mm.md +19 -20
@@ -60,7 +60,7 @@ Enforced by `validate_parallel_orchestrator_state_text(text, *, require_complete
60
60
 
61
61
  9. **Blast-radius shape.** Each item's `blast_radius` must be an object carrying `paths`, `modules`, `shared_surfaces`, and `contracts` as lists of non-empty strings, a `source` in `{derived, declared, observed}`, and a non-empty `computed_at` string. `modules`, `shared_surfaces`, and `contracts` may be empty lists.
62
62
 
63
- 10. **Prohibited dependency edges.** No object anywhere in the checkpoint may carry a `depends_on` key, and no top-level `depends_on` key may be present. Ordering is expressed only as blast-radius overlap. Presence is an explicit rejection, not mere absence.
63
+ 10. **Prohibited dependency edges.** No object anywhere in the checkpoint may carry a `depends_on` key, and no top-level `depends_on` key may be present. Ordering is expressed only as blast-radius overlap and as the operator precedence input declared in `## Prepared-Item Admission (issue #1047)`, which is a separating input and not a dependency key. Presence is an explicit rejection, not mere absence.
64
64
 
65
65
  11. **Prohibited integration-branch fields.** The checkpoint must not carry `integration_branch` or `epic_merge_pr` at any level. Each parallel item opens its own pull request against `main`, so there is no integration branch and no final integration pull request.
66
66
 
@@ -256,6 +256,22 @@ F3 deliberately excludes the kickoff-prompt contract module `scripts/dev_tools/p
256
256
 
257
257
  F3's `require_ready_for_execution` gate is STRUCTURAL ONLY. It enforces the kickoff-PATH invariant (P9: `kickoff_prompt_path` must equal `artifacts/orchestration/parallel-kickoff-<parallel_slug>.md`) and does not parse or cross-check kickoff CONTENT. The deeper readiness-integrity machinery of the epic surface — git-integrity checks, launch-evidence binding, and kickoff-contract cross-checks — is left to F4, which may layer repository-aware checks behind an additional keyword without changing the schema. F3 likewise does not recompute the cohort coloring (planner invariant P5).
258
258
 
259
+ ## Prepared-Item Admission (issue #1047)
260
+
261
+ `/parallel-add` and `/parallel-plan` admit an item at the stage its pushed branch already reached. The decisions are made in code by the admission helper `.claude/lib/parallel-admission/Invoke-ParallelAdmission.ps1` (subcommands `guard-run`, `assess`, `order-cohorts`, `test-cohorts`, `record`, and `reuse-edges`). Every field below is tolerated and not validated: the Python validators and the TypeScript validator port accept it with zero errors, and no enum of `## Enum Ownership (F6/F7/F8 consume, never extend)` changes.
262
+
263
+ **Tolerated item fields.** A parallel-orchestrator checkpoint item may carry `preparation_status` (`none|research|spec|plan|cleared`, as reported by `assess`), `plan_hash` (the git blob hash of the plan at the item branch head), `base_commit` (the commit the staleness check compares `origin/main` against), and `band_source` (`caller|planner_checkpoint|assessment`). A parallel-planner checkpoint item may carry `plan_hash` and `base_commit`. The planner keeps its ready value `preparation_status: prepared` (invariant P7), which corresponds to the admission stage `cleared`; no second ready value is introduced.
264
+
265
+ **Precedence input.** Both checkpoints may carry a top-level `precedence[]` list of `{before, after, source, recorded_at}` entries, recorded from the operator's `--after` flags with `source` equal to `operator`. An entry states that the `after` item starts in a later cohort than the `before` item and only after the `before` item has merged. It is a separating input: each pair is passed to the cohort computation as an extra edge, and `order-cohorts` lifts an unstarted `after` item above its `before` item. It is not a `depends_on` key, and invariant 10, P3, and M7 are unchanged. Keys must resolve to items, an item is never paired with itself, and the entries must be acyclic. An entry is inactive once its `before` item has `merge_status` `merged` or `worktree_removed` or `state` `withdrawn`; a `blocked` predecessor still holds its dependent. A combined item inherits the union of its members' entries, and a pair inside one combination is dropped.
266
+
267
+ **Preparation receipt.** The preparation child writes `<feature folder>/evidence/other/preparation-receipt.<yyyy-MM-ddTHH-mm>.json` before its commit, carrying `schema_version`, `stage`, `research_path`, `spec_path`, `plan_path`, `plan_hash`, `base_commit`, `preflight_status`, and `recorded_at`. The lexicographically greatest receipt name wins, a newer receipt supersedes an older one, and no receipt is deleted. A receipt whose `plan_hash` differs from the current blob hash is downgraded to `plan`.
268
+
269
+ **Durable scheduling record.** `docs/features/parallel/<slug>/parallel-scheduling.json`, committed on `parallel/<slug>-plan`, carries `schema_version`, `parallel_slug`, `edges[]`, `tolerated_overlaps[]`, `precedence[]`, and `items[]` with `issue_num`, `branch_name`, `plan_path`, `plan_hash`, `base_commit`, `preparation_status`, and `complexity_band`. The planner writes it at seeding and `/parallel-add` rewrites it after each admission; the helper's `record` subcommand prints the content and performs no git write. A re-seed applies the reuse rule through the helper's `reuse-edges` subcommand, which reads the record by ref and reuses an item's recorded edges only when the recorded `plan_hash` equals the current blob hash. The run manifest stays read-only to the orchestrator.
270
+
271
+ **Admission output signal.** A helper rule rejection exits 1 with one stderr line `PARALLEL_ADMISSION_REJECTED: <code>: <detail>`, where the code is one of `run_closed`, `precedence_cycle`, `precedence_unknown_item`, and `precedence_violated`. This signal is separate from the mutation rejection signal of `## Mutation Rejection Signal (issue #1012)`: a `run_closed` rejection is emitted after a checkpoint exists and emits no mutation rejection signal, and that section's closed code set is unchanged.
272
+
273
+ **Known gap (Layer 2).** The Python orchestrator-state validator does not check precedence. Precedence is enforced by the helper's `test-cohorts` rejection and by Layer 1 of the cohort barrier hook (`Test-ParallelCohortBarrierClear` in `.claude/hooks/enforce-parallel-cohort-barrier-helpers.ps1`); the retrospective Layer 2 invariant reads `conflict_edges[]` only.
274
+
259
275
  ## Blast-Radius Contention Doctrine (issue #489)
260
276
 
261
277
  The conflict graph that seeds cohorts is only as good as the evidence that produces its edges. Two
@@ -15,7 +15,7 @@ This rule file summarizes the PowerShell-specific policies for this repository.
15
15
  1. **Formatting — Invoke-Formatter**: Format all PowerShell files via PoshQC. MCP command: `mcp__drm-copilot__run_poshqc_format`
16
16
  2. **Linting — PSScriptAnalyzer**: Run PoshQC analyzer with repo settings. MCP command: `mcp__drm-copilot__run_poshqc_analyze`. Optional autofix: `mcp__drm-copilot__run_poshqc_analyze_autofix`
17
17
  3. **Type checking**: Not applicable for PowerShell; skip to testing.
18
- 4. **Testing — Pester (v5.x)**: Run tests via MCP. MCP command: `mcp__drm-copilot__run_poshqc_test`. Use repo config at `scripts/powershell/PoshQC/settings/pester.runsettings.psd1`.
18
+ 4. **Testing — Pester (v6.x)**: Run tests via MCP. MCP command: `mcp__drm-copilot__run_poshqc_test`. Use repo config at `scripts/powershell/PoshQC/settings/pester.runsettings.psd1`.
19
19
 
20
20
  Run the toolchain in order: format → analyze → test. Restart from step 1 if any step fails or changes files. Use the MCP server functions; do not substitute VS Code task wrappers.
21
21
 
@@ -53,7 +53,7 @@ Introduce the smallest seam that enables reliable mocking. Apply these options i
53
53
 
54
54
  ## Testing Standards
55
55
 
56
- - Use **Pester** (v5.x) as the test framework.
56
+ - Use **Pester** (v6.x) as the test framework.
57
57
  - Organize tests to mirror code structure (e.g., `tests/scripts/dev-tools/ScriptName.Tests.ps1`).
58
58
  - Name test files `*.Tests.ps1`.
59
59
  - Use `Describe`/`Context`/`It` blocks; one behavior per `It`.
@@ -11,6 +11,7 @@
11
11
  "Bash(bash .claude/lib/bash/mutate-parallel-run.sh*)",
12
12
  "Bash(bash .claude/lib/bash/remove-parallel-item.sh*)",
13
13
  "Bash(bash .claude/lib/bash/validate-parallel-manifest.sh*)",
14
+ "Bash(pwsh -NoProfile -NonInteractive -File .claude/lib/parallel-admission/Invoke-ParallelAdmission.ps1*)",
14
15
  "Read",
15
16
  "Edit(docs/**)",
16
17
  "Edit(artifacts/**)",
@@ -32,7 +32,7 @@ Rules:
32
32
  - **Tool routes are not restated.** Tool routes, launchers, wrapper scripts, command-text restrictions, the batch budget, hook context, and CI shape are owned by the `execution-environment` skill. A plan references that skill instead of restating its content.
33
33
  - **Changed existing behavior names its asserting tests.** A task that changes the observable behavior of an existing function, script, fixture, or test stub (its signature, argv, output, exit status, or the files it writes) names every existing test that asserts that behavior and states each test's disposition: unchanged because it is unaffected, or updated, with the reason for the update. The tests are found by searching the test tree for the symbol, the file name, and the asserted output. A test that asserts the current behavior and is outside the task's file set fails once the change lands, and the plan's own acceptance condition then cannot be met. The planner's adversarial self-review includes this search for every task that changes existing behavior.
34
34
  - **Size guideline.** A plan stays within 40 canonical tasks and 400 physical lines. A plan that needs more carries a `## Size Justification` section with at least one line stating why the size is necessary (validator rule G13). Files produced by one generator run count as one generator task.
35
- - **Pre-Mortem.** Every plan carries a `## Pre-Mortem` section with one bullet beginning with each of the seven labels `Review checklists`, `Coverage`, `Mirror parity`, `File size`, `Test isolation`, `CI-only behavior`, and `Evidence`. Each bullet states how the plan prevents that failure class, or states "not applicable" followed by the reason. The `Test isolation` bullet names the isolation runs the plan performs: a seeded shuffled Jest run with the seed recorded, a reversed-order run of the new or changed Python test modules, and a seeded shuffled file-order run of the touched Pester suites with at least three seeds, each seed recorded. The `Mirror parity` bullet also covers the execution-environment brief: a plan that alters a fact the brief states (for example, it adds or renames a CI job, changes a required status context, a tool route, or a batch-budget rule) includes a task that updates the brief in the same change, in all six byte-identical copies, and the bullet names that task. Without that task the brief states a superseded fact once the change lands, which breaks the brief's own maintenance rule. The planner SubagentStop hook blocks a plan without a complete section.
35
+ - **Pre-Mortem.** Every plan carries a `## Pre-Mortem` section with one bullet beginning with each of the seven labels `Review checklists`, `Coverage`, `Mirror parity`, `File size`, `Test isolation`, `CI-only behavior`, and `Evidence`. Each bullet states how the plan prevents that failure class, or states "not applicable" followed by the reason. The `Test isolation` bullet names the parallel gates the plan uses: one `Invoke-PoshQCParallelTest` run, one `pytest -n auto` run, and Jest through its normal parallel workers, each re-run only after a fix. The `Mirror parity` bullet also covers the execution-environment brief: a plan that alters a fact the brief states (for example, it adds or renames a CI job, changes a required status context, a tool route, or a batch-budget rule) includes a task that updates the brief in the same change, in all six byte-identical copies, and the bullet names that task. Without that task the brief states a superseded fact once the change lands, which breaks the brief's own maintenance rule. The planner SubagentStop hook blocks a plan without a complete section.
36
36
  - **Final-phase self-review.** The final phase of every plan contains a self-review task that walks the policy-audit, code-review, and feature-audit templates under `docs/features/templates/policy_audit/` against the branch diff from the merge base and writes `<FEATURE>/evidence/qa-gates/self-review.<timestamp>.md` with one PASS, FAIL, or N/A row per checklist item. A FAIL row is fixed and the QA loop is re-run before the self-review is rewritten; the plan does not complete with a FAIL row.
37
37
 
38
38
  This section takes precedence over the literal-quoting rules of `## Wrap-Tolerant Assertion Authoring (Mandatory)`: those rules govern the asserted value of an acceptance condition, not context.
@@ -43,12 +43,11 @@ When orchestration selects short path, a minimal plan is still mandatory and mus
43
43
 
44
44
  1) Baseline capture block
45
45
  - policy reads in required order,
46
- - baseline toolchain/test state capture for each language that has explicit baseline command tasks in the plan.
46
+ - static baseline capture (format, lint, type check, and analyzer findings) for each language that has explicit baseline command tasks in the plan; no test suite runs in this block.
47
47
  - required artifacts:
48
48
  - a Phase 0 policy-read evidence artifact in the canonical evidence location defined by `evidence-and-timestamp-conventions`
49
49
  - one baseline artifact per baseline command step (no aggregate-only baseline artifact)
50
50
  - each baseline step artifact MUST include: `Timestamp:`, `Command:`, `EXIT_CODE:`, `Output Summary:`
51
- - baseline test-step artifacts for languages with mandatory coverage policy MUST include numeric coverage headline values in `Output Summary:` (baseline percent and, when applicable, targeted module/new-code percent).
52
51
 
53
52
  2) Delegated implementation block
54
53
  - explicit handoff task to the small-path implementation engineer,
@@ -59,7 +58,7 @@ When orchestration selects short path, a minimal plan is still mandatory and mus
59
58
  - rerun behavior when any step changes files or fails.
60
59
  - one final-QC artifact per QC command step (no aggregate-only final-QC artifact)
61
60
  - each final-QC step artifact MUST include: `Timestamp:`, `Command:`, `EXIT_CODE:`, `Output Summary:`
62
- - final-QC test commands for languages with mandatory coverage policy MUST run in coverage mode and record numeric post-change coverage values in `Output Summary:`.
61
+ - final-QC test commands for languages with mandatory coverage policy MUST run in coverage mode and record numeric coverage values from the single gate run in `Output Summary:`.
63
62
  - final-QC command tasks that are present in the approved plan MUST execute their stated commands; `SKIPPED` is invalid unless the task text itself explicitly authorizes a skip condition.
64
63
 
65
64
  4) Reduced audit block
@@ -96,7 +95,7 @@ Branching after Phase 0:
96
95
 
97
96
  Phase 0 must include tasks to read policy files in the order defined in `policy-compliance-order`.
98
97
 
99
- Phase 0 must also capture baseline toolchain results for the languages touched. Baseline artifact conventions (location + required fields) are defined in `evidence-and-timestamp-conventions`.
98
+ Phase 0 must also capture static baselines (format, lint, type check, and analyzer findings) for the languages touched, and no test suite runs in it. Baseline artifact conventions (location + required fields) are defined in `evidence-and-timestamp-conventions`.
100
99
 
101
100
  For short-path/minimal-audit plans, Phase 0 evidence is incomplete unless both artifacts exist:
102
101
  - `phase0-instructions-read.md` with at least: `Timestamp:`, `Policy Order:`, and explicit list of files read.
@@ -128,12 +127,9 @@ Plans name evidence artifacts `<name>.<timestamp>.md` and never fix a plan-time
128
127
 
129
128
  For any language in scope where repository policy requires coverage validation:
130
129
 
131
- - The approved plan MUST include explicit baseline and final-QC coverage capture tasks.
132
- - Baseline and final-QC artifacts MUST record numeric coverage values (not placeholders such as `UNVERIFIED`).
133
- - Where policy requires no-regression and new-code thresholds, the plan MUST include a delta/threshold verification task that reports:
134
- - baseline coverage,
135
- - post-change coverage,
136
- - new/changed-code coverage.
130
+ - The approved plan MUST include an explicit final-QC coverage capture task.
131
+ - The final-QC artifact MUST record numeric coverage values (not placeholders such as `UNVERIFIED`).
132
+ - The plan MUST include a threshold task that reports each changed file from the single gate run against the coverage thresholds. New behavior is tested. No earlier figure is compared.
137
133
  - If required coverage values are unavailable, the plan outcome MUST be remediation-required and MUST NOT be reported as PASS.
138
134
 
139
135
  ## Final QA Loop (Required for Code/Test Changes)
@@ -248,7 +244,7 @@ Rules:
248
244
  - **Observe a command's success-case output before asserting over that output (mandatory).** Run the command, or read a recorded run of it, and confirm the value you intend to assert is actually printed on a *successful* run. Do not infer it from the tool's documentation, from the plan, or from what the tool prints on failure. This requirement exists because the class of defect it prevents is not detectable by reading: an assertion on a line the tool prints only when it changed something is unsatisfiable on a clean run; a demand for a zero-diagnostic count from a tool that returns only an ok flag names a value with no source; a demand for separate line and branch percentages from a coverage run that prints one combined column reads two numbers that are never printed. G7 and G9 cover two decidable slices of this class. The remainder is covered by this requirement and by nothing else, so a reviewer cannot rely on the gate to catch it.
249
245
  - **Fix the evidence in the plan; never leave the executor to select it.** An executor free to choose the evidence it is judged against cannot fail. Do not write an acceptance condition that asks the executor to identify "the known-genuine pair", to choose a survivor list, or to pick any suitable instance. Name the instances, or state the mechanical derivation that produces them, so a third party re-running it obtains the same set. This is authoring guidance and not a rule: a validator rule that scanned acceptance text for selection vocabulary was proposed and rejected, because that vocabulary is ordinary plan prose used in roles carrying no selection semantics and a keyword scan over prose is not statically decidable for the property it would claim to detect. The judgment is the author's, and it is not automated. `.claude/rules/plan-acceptance-gates.md` records the rejection and the reason for it.
250
246
  - **Check that the task-ordering does not make the condition unsatisfiable.** No rule covers this. A gate that runs a test path containing deliberately-failing cases added by an earlier task, before the later task that makes them pass, cannot exit 0. A baseline captured after a write-mode formatter has already repaired pre-existing drift becomes either a blanket waiver or makes a later gate unsatisfiable. Read each acceptance condition against the state the plan will actually be in when its task runs.
251
- - **Name the runner and job for a Pester acceptance criterion.** In `.github/workflows/_poshqc.yml`, the Pester suites run with coverage on `windows-latest` in the shard jobs `poshqc / PowerShell QC shard 1`, `shard 2`, and `shard 3`; `poshqc / PowerShell QC` is the aggregating required check, which requires the static and shard jobs to succeed and merges shard coverage; and `poshqc / PowerShell hook suites (Linux)` runs only `tests/scripts/claude-hooks` and `tests/scripts/codex-hooks` on `ubuntu-latest`, with coverage disabled. The execution-environment brief's `## CI Shape and Required Checks` section is the authoritative description of this shape. A Pester acceptance criterion may cite `ubuntu-latest` only for suites under `tests/scripts/claude-hooks` or `tests/scripts/codex-hooks`, never for coverage, and must name the job it relies on (`poshqc / PowerShell QC` or `poshqc / PowerShell hook suites (Linux)`).
247
+ - **Name the runner and job for a Pester acceptance criterion.** In `.github/workflows/_poshqc.yml`, the Pester suites run with coverage on `windows-latest` in the shard jobs `poshqc / PowerShell QC shard 1`, `shard 2`, and `shard 3`; `poshqc / PowerShell QC` is the aggregating check, whose result `CI Required Gate` requires, and it requires the static and shard jobs to succeed and merges shard coverage; and `poshqc / PowerShell hook suites (Linux)` runs only `tests/scripts/claude-hooks` and `tests/scripts/codex-hooks` on `ubuntu-latest`, with coverage disabled. The execution-environment brief's `## CI Shape and Required Checks` section is the authoritative description of this shape. A Pester acceptance criterion may cite `ubuntu-latest` only for suites under `tests/scripts/claude-hooks` or `tests/scripts/codex-hooks`, never for coverage, and must name the job it relies on (`poshqc / PowerShell QC` or `poshqc / PowerShell hook suites (Linux)`).
252
248
  - **Do not assert a fixed-string search literal that contains a backslash.** Git for Windows grep 3.0 reads a doubled backslash in a `grep -F` pattern as one backslash, so the same check returns a different count locally than on a CI runner and cannot pass in both places. Assert a backslash-free substring of the literal, a named test, or a hash or byte-identity check instead.
253
249
 
254
250
  ## Plan-Path Continuity Contract (Mandatory)
@@ -275,7 +271,7 @@ When a plan is generated or validated from a feature folder, resolve selected mo
275
271
 
276
272
  ## Mode-Specific Mandatory Plan Gates
277
273
 
278
- - `minor-audit` plans MUST include baseline evidence tasks, targeted verification evidence tasks, and end-state evidence tasks.
274
+ - `minor-audit` plans MUST include static baseline evidence tasks, targeted verification evidence tasks read from the gate run, and end-state evidence tasks.
279
275
  - `minor-audit` plans MUST require `${feature-folder}/issue.md` to contain an explicit `## Acceptance Criteria` section; do not infer acceptance criteria from other `issue.md` sections.
280
276
  - `minor-audit` plans MUST NOT treat missing `spec.md` or `user-story.md` as automatic blockers.
281
277
  - `minor-audit` execution/validation/audit MUST fail closed when `spec.md` or `user-story.md` exists unexpectedly in the active folder, when the explicit `## Acceptance Criteria` section is missing from `issue.md`, when required Phase 0 artifacts are missing, or when checklist state contradicts evidence on disk.
@@ -162,7 +162,9 @@ The script emits pipe-delimited, `LC_ALL=C`-ordered records, one per line:
162
162
  2. **Editorial triage of the cherry-pick candidates.** Review each
163
163
  `COMMIT|...|UNIQUE|...` record — this is the LLM-judgment boundary. Confirm
164
164
  editorially that the unique commits are genuinely documentation/agent-memory
165
- content (for example paths under `docs/**`, `.claude/agent-memory/**`, or `**/*.md`).
165
+ content: paths under the repository-relative prefixes `docs/` and `.claude/agent-memory/`,
166
+ compared case-sensitively. A Markdown file outside those two prefixes (for example
167
+ `CLAUDE.md` or `.claude/rules/*.md`) is not consolidation content.
166
168
  The script only reports the deterministic facts (SHA, paths, author, date); deciding
167
169
  what counts as documentation is this skill's responsibility.
168
170
 
@@ -174,21 +176,42 @@ The script emits pipe-delimited, `LC_ALL=C`-ordered records, one per line:
174
176
  reuse it silently. Conflicts are aborted and surfaced as `CONFLICT` for editorial
175
177
  resolution, never auto-resolved.
176
178
 
179
+ **Cleanup context.** A cleanup run writes no orchestration checkpoint, so the enforcement
180
+ hooks recognize its operations by the cleanup context instead: the sanctioned-removal
181
+ manifest at the session root (`artifacts/orchestration/cleanup-worktrees-manifest.json` in
182
+ the main checkout where this skill runs, written whole with Write; see Sanctioned Removal
183
+ Manifest) and the consolidation worktree, identified by its branch
184
+ `documentationandmemories`. Any staging in that worktree beyond what the script performs
185
+ uses `git -C <consolidation root> add -- <path>` with paths under `docs/` or
186
+ `.claude/agent-memory/` only, and the commit uses
187
+ `git -C <consolidation root> commit -F <message file>` with no pathspec. The preimplementation
188
+ gate admits that commit only when every staged path is under those two prefixes.
189
+
177
190
  4. **Push and hand off PR creation to `Agent(pr-author)`.** Push the consolidation
178
- branch (`git push`). Refresh the PR-context bundle with
179
- `mcp__drm-copilot__collect_pr_context` using base branch `main` (producing
180
- `artifacts/pr_context.summary.txt` and `artifacts/pr_context.appendix.txt`).
181
- Validate the orchestrator-state checkpoint
182
- (`artifacts/orchestration/orchestrator-state.json`) with `--require-pr-creation-ready`
183
- and record the `pr_author_preflight` result; delegation is prohibited when that
184
- validation fails. Then delegate PR creation to `Agent(pr-author)` per
185
- `.claude/skills/pr-author/SKILL.md`, which owns the body-file and receipt contract;
186
- defer to that skill for it rather than restating any value from it here. For `<N>`,
191
+ branch (`git -C <consolidation root> push -u origin documentationandmemories`).
192
+ The push precedes `gh pr create`: the PR-author gate requires
193
+ `refs/remotes/origin/documentationandmemories` to resolve to the same commit as the
194
+ consolidation worktree `HEAD`, so push again after any later commit in that worktree. Refresh the
195
+ PR-context bundle with `mcp__drm-copilot__collect_pr_context` using base branch `main` and
196
+ `workspace_root` set to the consolidation worktree, so that
197
+ `artifacts/pr_context.summary.txt` and `artifacts/pr_context.appendix.txt` exist beneath the
198
+ consolidation worktree. A consolidation pull request has no orchestrator-state checkpoint and
199
+ is not validated against one; the hooks perform the cleanup readiness check instead.
200
+ `enforce-model-routing-receipt.ps1` admits the `Agent(pr-author)` delegation, and
201
+ `enforce-pr-author-skill.ps1` admits `gh pr create`, only while a fresh, valid manifest exists
202
+ at the session root; the PR-author gate also requires an explicit
203
+ `--head documentationandmemories --base main` and a branch diff limited to `docs/` and
204
+ `.claude/agent-memory/`, and its receipt checks still apply. Then delegate PR creation to
205
+ `Agent(pr-author)` per `.claude/skills/pr-author/SKILL.md`, which owns the body-file and
206
+ receipt contract; defer to that skill for it rather than restating any value from it here.
207
+ The delegation is non-isolated and passes the absolute body path beneath the consolidation
208
+ worktree, as that skill's coordinator-session topology requires. For `<N>`,
187
209
  use the GitHub issue number the run executes under when the run has one. When the run
188
210
  has none, `<N>` is an arbitrary run-scoped identifier chosen by the pr-author agent.
189
- The delegation prompt MUST carry a `branch: <name>` label naming the consolidation branch,
190
- and the canonical issue number line when the run has an issue number.
191
- `enforce-model-routing-receipt.ps1` identifies the item from those two lines and denies a
211
+ The delegation prompt MUST carry exactly one `branch: documentationandmemories` label, on
212
+ its own line, and no `--head` or `--branch` text, plus the canonical issue number line when
213
+ the run has an issue number.
214
+ `enforce-model-routing-receipt.ps1` identifies the item from those lines and denies a
192
215
  gated delegation it cannot identify. A cleanup run with no issue number is therefore
193
216
  identified by the branch label alone, which is why the label is required and the issue line
194
217
  is not.
@@ -397,6 +420,15 @@ specific files or commit SHAs, before step 9 acts on any finding.
397
420
  `git worktree remove <path>` command only when a record in it covers that exact path. A removal
398
421
  issued with no covering record is denied with each gate's existing, unchanged reason code.
399
422
 
423
+ The manifest lives at the session root: the main checkout where this skill runs. Both removal
424
+ gates, the PR-author gate, and the model-routing gate read the relative path
425
+ `artifacts/orchestration/cleanup-worktrees-manifest.json` against the hook process location,
426
+ which is the session root, so a manifest written in any other worktree is never read. Write the
427
+ whole document with Write; the preimplementation gate admits that Write without a checkpoint
428
+ only at the session-root spelling and only when the content carries the expected `tool` and
429
+ `schema_version`. An Edit of the manifest is not supported without a checkpoint, and neither is a
430
+ Codex `apply_patch` Update, Delete, or Move of it; rewrite the document with Write instead.
431
+
400
432
  The document is UTF-8 JSON with `snake_case` keys. It carries six top-level fields. Its `removals`
401
433
  and `preserved_files` entries are sibling arrays and are never nested: removal records are keyed by
402
434
  worktree path, preserve records by the pair of worktree path and file path, and the gates must
@@ -445,7 +477,8 @@ These records serve the consolidation consumer and are never read by either gate
445
477
  consumer reads that worktree's `MEMORY.md`.
446
478
  - `source_path` — string, required, repo-relative within that worktree. The file to preserve.
447
479
  - `change_class` — string, required, either `untracked` or `modified`, recording whether staging
448
- adds a new file or carries a working-tree modification.
480
+ adds a new file or carries a working-tree modification. How each class is staged is described
481
+ under `### Preserve staging outcomes`.
449
482
  - `disposition` — string, required, exactly `PRESERVE`.
450
483
  - `verdict` — string, required, a member of the step-5 verdict vocabulary listed above.
451
484
  - `target_path` — string, required, repo-relative on the consolidation branch. Required because the
@@ -499,6 +532,52 @@ These records serve the consolidation consumer and are never read by either gate
499
532
  }
500
533
  ```
501
534
 
535
+ ### Preserve staging outcomes
536
+
537
+ `cleanup-worktrees.sh --preserve` reports each record's outcome as
538
+ `ACTION|preserve-stage|<target_path>|<result>`. The result vocabulary is:
539
+
540
+ - `OK` — the record was written and staged.
541
+ - `FAILED` — a write or staging operation failed; the remaining records continue.
542
+ - `SKIPPED-INVALID` — a record field failed validation.
543
+ - `MISSING-SOURCE` — the source file does not exist in the named worktree.
544
+ - `HOST-TOKEN-BLOCKED` — the incoming bytes carry a host-identifying token; the whole pass stops
545
+ with exit 3 and nothing is staged.
546
+ - `IGNORED-TARGET` — the target path is ignored on the consolidation branch and is not forced.
547
+ - `MISSING-WORKTREE` — the consolidation worktree does not exist.
548
+ - `REFUSED-WOULD-REGRESS` — staging the record would lose a line that the consolidation branch
549
+ already carries, or the record repeats an earlier record's `target_path`.
550
+
551
+ Staging depends on whether `<consolidation-worktree>/<target_path>` is an existing regular file:
552
+
553
+ - Absent target, either `change_class`: the source is copied byte for byte.
554
+ - Existing target, `untracked` record: the source is copied only when every line of the target is
555
+ present in the source in the same order; otherwise the record is refused.
556
+ - Existing target, `modified` record with a merge base (the source path equals the target path, and
557
+ the file exists at the merge base of the worktree `HEAD` and the consolidation `HEAD`): the
558
+ worktree change must only add lines to the base (G1); a `git merge-file --union --object-id`
559
+ merge of the target, the base, and the source then runs; the merged result must keep every target
560
+ line (G2) and must add no line beyond the worktree's own additions (G3). The merged blob is
561
+ staged.
562
+ - Existing target, `modified` record without a merge base: treated as an existing `untracked`
563
+ target.
564
+ - A record whose `target_path` repeats an earlier record's in the same pass is refused; the first
565
+ record keeps the target.
566
+
567
+ A refused record still emits its `PRESERVE|` record, writes a `cleanup-worktrees: refused` line to
568
+ stderr naming the failed guard, appends no `MEMORY.md` index line, and makes the pass exit 1.
569
+
570
+ Residuals:
571
+
572
+ - A resurrected line textually identical to a line the worktree added cannot be distinguished by G3.
573
+ - The merge path requires Git 2.43 or later for `merge-file --object-id`; an older Git fails the
574
+ merge and the record is refused.
575
+ - The order in which `--union` places two insertions at the same position is not specified by Git;
576
+ G2 and G3 bound the result whatever the order.
577
+ - The planning phase writes unreferenced blob objects (`hash-object -w`, `merge-file --object-id`)
578
+ to the object store; it writes no index, ref, or working-tree file.
579
+ - A refused record is not consolidated; the operator consolidates it manually.
580
+
502
581
  ### Accepted residual
503
582
 
504
583
  Both gates read the command text of the `Bash` tool call they are given. A removal routed
@@ -30,19 +30,24 @@ source "$SCRIPT_DIR/cleanup_worktrees_actions_lib.sh"
30
30
  # shellcheck source=.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_detached_lib.sh
31
31
  # shellcheck disable=SC1091
32
32
  source "$SCRIPT_DIR/cleanup_worktrees_detached_lib.sh"
33
- # Neither of the three libraries below requires being sourced last, and neither claims to
34
- # be. Each library's source-time work is limited to function definitions plus constant and
33
+ # None of the four libraries below requires being sourced last, and none claims to be.
34
+ # Each library's source-time work is limited to function definitions plus constant and
35
35
  # environment-default variable assignments; none of them calls a function while being
36
36
  # sourced, and no function name is defined by more than one library, so no later source can
37
37
  # shadow an earlier definition. Bash resolves a function name when the call runs, not when
38
38
  # the caller is defined, so the cross-library calls resolve whatever the order: the
39
39
  # disposable-dirt classifier is called BY run_report and BY delete_candidate in the
40
40
  # libraries above, and the preserve library calls cleanup_wt_git from the enumeration
41
- # library and consolidation_worktree_path from the actions library. The order here is
42
- # therefore grouping for readability only.
41
+ # library, consolidation_worktree_path from the actions library, and the staging decision
42
+ # from the merge library. The order here is therefore grouping for readability only.
43
43
  # shellcheck source=.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_dirt_lib.sh
44
44
  # shellcheck disable=SC1091
45
45
  source "$SCRIPT_DIR/cleanup_worktrees_dirt_lib.sh"
46
+ # The merge library holds the preserve staging decision. It is sourced before the
47
+ # line-ending library so that library stays immediately before the preserve library.
48
+ # shellcheck source=.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_merge_lib.sh
49
+ # shellcheck disable=SC1091
50
+ source "$SCRIPT_DIR/cleanup_worktrees_preserve_merge_lib.sh"
46
51
  # The line-ending and index group is placed immediately before the preserve library, whose
47
52
  # read-only planning phase calls the functions it defines.
48
53
  # shellcheck source=.claude/skills/cleanup-merged-worktrees/scripts/cleanup_worktrees_preserve_eol_lib.sh
@@ -69,12 +74,20 @@ Commands:
69
74
  preserve | --preserve
70
75
  Stage the manifest's preserved_files[] findings onto the
71
76
  consolidation branch. Takes no operand; the manifest path comes
72
- from CLEANUP_WT_MANIFEST_PATH. Copies each named source file
73
- verbatim, carries its MEMORY.md index line across using the
74
- destination file's own re-derived line-ending convention, and
75
- refuses the whole pass when the incoming bytes carry a
76
- host-identifying token. Exit 0 clean, 1 something was skipped or
77
- an index was created, 3 host-token hard stop, 127 a required tool
77
+ from CLEANUP_WT_MANIFEST_PATH. Staging depends on the target and
78
+ change_class: a target absent from the consolidation branch is
79
+ copied byte for byte; an existing target is copied only when
80
+ none of its lines would be lost (untracked, or modified with no
81
+ merge base), or receives a three-way union merge of the
82
+ worktree's added lines (modified with a merge base). A record
83
+ that would lose a line, fails a merge guard, or repeats an
84
+ earlier target_path is refused with
85
+ ACTION|preserve-stage|<target>|REFUSED-WOULD-REGRESS. The
86
+ MEMORY.md index line is carried across using the destination
87
+ file's own re-derived line-ending convention, and the whole pass
88
+ is refused when the incoming bytes carry a host-identifying
89
+ token. Exit 0 clean, 1 something was skipped or refused or an
90
+ index was created, 3 host-token hard stop, 127 a required tool
78
91
  could not be resolved.
79
92
  --help | -h | help Print this help and exit 0.
80
93
 
@@ -12,12 +12,13 @@
12
12
  # cleanup_worktrees_actions_lib.sh, because it calls cleanup_wt_git (defined in the
13
13
  # enumeration library) and consolidation_worktree_path (defined in the actions library).
14
14
  #
15
- # Two-phase design. preserve_plan performs NO writes: it reads and validates the manifest
16
- # and every record, verifies each source file exists, runs the host-token pre-pass,
17
- # decides each index outcome, and emits the PRESERVE| and ACTION| records plus an internal
18
- # plan stream. preserve_commit_plan is the only function that writes and is entered only
19
- # when the pre-pass reported no match, which is what keeps the writing phase thin enough
20
- # to leave in the coverage denominator rather than exclude from measurement.
15
+ # Two-phase design. preserve_plan writes no index, ref, or working-tree file: it validates
16
+ # the manifest and every record, verifies each source exists, runs the host-token
17
+ # pre-pass, takes each staging and index decision, and emits the PRESERVE| and ACTION|
18
+ # records plus an internal plan stream. Its one write is the exception stated in
19
+ # cleanup_worktrees_preserve_merge_lib.sh: unreferenced blob objects created by
20
+ # `hash-object -w` and `merge-file --object-id`. preserve_commit_plan is the only writer of
21
+ # files and is entered only when the pre-pass reported no match.
21
22
  #
22
23
  # Seams. All git commands go through cleanup_wt_git (CLEANUP_WT_GIT_BIN); jq resolves
23
24
  # through preserve_resolve_jq (CLEANUP_WT_JQ_BIN); the manifest path comes from
@@ -174,9 +175,8 @@ preserve_validate_record() {
174
175
  # ACTION|preserve-stage|<target-path>|SKIPPED-INVALID; the first violation in the chain
175
176
  # below wins, because one bad field skips the record whatever the others say.
176
177
  #
177
- # change_class is validated for vocabulary but does NOT change the staging action: an
178
- # untracked and a modified record are both copied to target_path and staged, stated
179
- # here so no later change invents a divergent path for one of them.
178
+ # change_class is validated here for vocabulary only; its effect on staging belongs to
179
+ # preserve_merge_decide (absent target copied, existing target guarded or merged).
180
180
  #
181
181
  # host_token_scan is refused when it is absent, is not a JSON object, is missing either
182
182
  # member, or carries a result outside clean/tokens_present: a record whose scan metadata
@@ -236,16 +236,18 @@ preserve_validate_record() {
236
236
  }
237
237
 
238
238
  preserve_plan_one() {
239
- # Plan one manifest record. Performs no write of any kind.
239
+ # Plan one manifest record. Writes no index, ref, or working-tree file; the staging
240
+ # decision may write unreferenced blob objects (`hash-object -w`, `merge-file
241
+ # --object-id`), the exception cleanup_worktrees_preserve_merge_lib.sh states.
240
242
  #
241
- # The PRESERVE| report record is emitted for every record that survives validation,
242
- # before any per-record outcome is known, because that record identifies the finding at
243
- # its origin exactly as DIRTY|<worktree-path>|<status-line> does. The destination
244
- # outcome travels separately on the ACTION| result channel.
243
+ # The PRESERVE| record is emitted for every record that survives validation, before any
244
+ # per-record outcome is known, as DIRTY|<worktree-path>|<status-line> is; the outcome
245
+ # travels separately on the ACTION| result channel. The source file is read by plain
246
+ # file I/O at <worktree_path>/<source_path>, never through git (see the header).
245
247
  #
246
- # The source file is located by plain file I/O at <worktree_path>/<source_path> and is
247
- # never read through git: the repository-root .claude/agent-memory tree is gitignored,
248
- # so a git-mediated read would be refused or would need a force flag.
248
+ # The plan entry carries ten columns: target_path, source file, destination file, index
249
+ # action, index path, index line, terminator token, leading-terminator flag, staging
250
+ # mode (`copy` or `merge`), and the merged blob id (empty for `copy`).
249
251
  #
250
252
  # Args: $1 = the tab-separated record. Returns 0 when the record was planned and 1 when
251
253
  # it was skipped, refused, or reported.
@@ -294,6 +296,11 @@ preserve_plan_one() {
294
296
  printf 'cleanup-worktrees: destination is ignored, refusing to force: %s\n' "$tgt" >&2
295
297
  return 1
296
298
  fi
299
+ # The staging decision (merge library): a repeated target_path is refused, and a target
300
+ # that already exists is guarded or merged rather than overwritten. A refusal returns
301
+ # before the index decision, so a refused record produces no index record either.
302
+ preserve_merge_check_duplicate "$tgt" || return 1
303
+ preserve_merge_decide "${PRESERVE_FIELDS[2]}" "$wt" "$src" "$tgt" "$PRESERVE_CWT" || return 1
297
304
  # The index decision, including the line-ending re-derivation it rests on, lives in
298
305
  # the line-ending library. A memory_index_line of JSON null means the file is not a
299
306
  # memory entry: it is copied and staged, and no index is read, created, or appended to.
@@ -307,16 +314,19 @@ preserve_plan_one() {
307
314
  ipre=$PRESERVE_INDEX_PRE
308
315
  fi
309
316
  local entry
310
- printf -v entry '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s' \
311
- "$tgt" "$srcfile" "$PRESERVE_CWT/$tgt" "$iaction" "$ipath" "$iline" "$iterm" "$ipre"
317
+ printf -v entry '%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s' \
318
+ "$tgt" "$srcfile" "$PRESERVE_CWT/$tgt" "$iaction" "$ipath" "$iline" "$iterm" "$ipre" \
319
+ "$PRESERVE_STAGE_MODE" "$PRESERVE_STAGE_OID"
312
320
  PRESERVE_PLAN_STREAM+=("$entry")
313
321
  return 0
314
322
  }
315
323
 
316
324
  preserve_plan() {
317
- # Phase 1 of the two-phase driver: read, validate, decide, and report. It performs no
318
- # write of any kind, so a failure anywhere in it leaves the consolidation worktree
319
- # byte-unchanged and there is nothing to undo.
325
+ # Phase 1 of the two-phase driver: read, validate, decide, and report. It writes no
326
+ # index, ref, or working-tree file, so a failure anywhere in it leaves the consolidation
327
+ # worktree byte-unchanged and there is nothing to undo. The one exception is the staging
328
+ # decision's unreferenced blob objects (`hash-object -w`, `merge-file --object-id`),
329
+ # which nothing references unless phase 2 stages the merged result.
320
330
  #
321
331
  # Records are processed in LC_ALL=C order of the pair (worktree_path, source_path)
322
332
  # rather than in manifest array order, because the report stream's ordering is
@@ -324,10 +334,8 @@ preserve_plan() {
324
334
  # a contract. The sort is field-aware rather than whole-line, so a worktree_path that
325
335
  # is a prefix of another cannot reorder the pair.
326
336
  #
327
- # The decisions are accumulated on two globals rather than returned, because a record's
328
- # outcome has to survive the loop: PRESERVE_PLAN_STREAM carries one tab-separated entry
329
- # per record that reached the writing phase, and PRESERVE_EXIT accumulates the
330
- # per-record skip signal that becomes the pass's exit code.
337
+ # Outcomes survive the loop on globals: PRESERVE_PLAN_STREAM (one entry per planned
338
+ # record), PRESERVE_EXIT (the per-record skip signal), and PRESERVE_SEEN_TARGETS.
331
339
  #
332
340
  # Returns 0 when the pass may proceed to the writing phase, 1 when the manifest itself
333
341
  # was rejected, 3 on the host-token hard stop, and 127 when a required tool could not
@@ -337,6 +345,9 @@ preserve_plan() {
337
345
  PRESERVE_EXIT=0
338
346
  PRESERVE_BLOCKED=0
339
347
  PRESERVE_CWT=""
348
+ # Read by preserve_merge_check_duplicate in the merge library (SC2034 cannot see it).
349
+ # shellcheck disable=SC2034
350
+ PRESERVE_SEEN_TARGETS=""
340
351
  local manifest records sorted line rc=0
341
352
  manifest=${CLEANUP_WT_MANIFEST_PATH:-artifacts/orchestration/cleanup-worktrees-manifest.json}
342
353
  PRESERVE_CWT=$(consolidation_worktree_path) || return $?
@@ -353,31 +364,26 @@ preserve_plan() {
353
364
  [[ -n $line ]] || continue
354
365
  preserve_plan_one "$line" || PRESERVE_EXIT=1
355
366
  done <<<"$sorted"
356
- # The hard stop is evaluated only after every record has been examined, so the pass
357
- # reports every match rather than only the first, and the writing phase is not entered
358
- # for any record. Nothing has been written at this point, so there is nothing to undo.
367
+ # The hard stop is evaluated after every record is examined, so every match is reported
368
+ # and the writing phase is entered for no record; no file has been written to undo.
359
369
  ((PRESERVE_BLOCKED == 0)) || return 3
360
370
  return 0
361
371
  }
362
372
 
363
373
  preserve_commit_plan() {
364
- # Phase 2: the only function in this library that writes anything.
374
+ # Phase 2: the only function in this library that writes files. It executes each plan
375
+ # entry's decisions with exactly four kinds of operation: mkdir -p of the destination
376
+ # directory, the content write, the index append or creation, and the staging call. It
377
+ # takes no staging decision of its own, which keeps it thin enough to stay measured.
365
378
  #
366
- # It consumes the plan stream phase 1 built and performs exactly four kinds of
367
- # operation: mkdir -p of the destination directory, a verbatim byte copy of the source,
368
- # the destination index append or creation, and the staging call. Every decision was
369
- # already taken in phase 1, so this function contains no policy and is thin enough to
370
- # leave in the coverage denominator rather than exclude from measurement.
379
+ # The content write follows column 9. `merge` writes the merged blob phase 1 recorded
380
+ # (`cat-file blob <oid>`); `copy` or empty copies the source byte for byte. Neither
381
+ # applies a line-ending transformation to the preserved content; the line-ending
382
+ # obligation is scoped to the index append, and staging re-normalizes tracked text.
371
383
  #
372
- # The copy is verbatim. No line-ending transformation is applied to the preserved
373
- # file's own content: transforming it would alter content the editorial pass judged
374
- # worth keeping, and the staging call re-normalizes tracked text anyway. The
375
- # line-ending obligation is scoped to the index append, where the defect occurred.
376
- #
377
- # A non-zero result from any of the three operations emits
378
- # ACTION|preserve-stage|<target-path>|FAILED and contributes to exit 1; it never aborts
379
- # the remaining records, because each record's write is independent of the others.
380
- local entry tgt srcfile dstfile iaction ipath iline iterm ipre fail
384
+ # A failed operation emits ACTION|preserve-stage|<target-path>|FAILED and contributes to
385
+ # exit 1; it never aborts the remaining records, whose writes are independent.
386
+ local entry tgt srcfile dstfile iaction ipath iline iterm ipre mode oid fail
381
387
  ((${#PRESERVE_PLAN_STREAM[@]} > 0)) || return 0
382
388
  for entry in "${PRESERVE_PLAN_STREAM[@]}"; do
383
389
  preserve_split_tsv "$entry"
@@ -389,10 +395,12 @@ preserve_commit_plan() {
389
395
  iline=${PRESERVE_FIELDS[5]}
390
396
  iterm=${PRESERVE_FIELDS[6]}
391
397
  ipre=${PRESERVE_FIELDS[7]}
392
- fail=""
398
+ mode=${PRESERVE_FIELDS[8]-} oid=${PRESERVE_FIELDS[9]-} fail=""
393
399
  if ! mkdir -p -- "${dstfile%/*}" 2>/dev/null; then
394
400
  fail="cannot create the destination directory"
395
- elif ! cat -- "$srcfile" >"$dstfile" 2>/dev/null; then
401
+ elif [[ $mode == "merge" ]] && ! cleanup_wt_git -C "$PRESERVE_CWT" cat-file blob "$oid" >"$dstfile"; then
402
+ fail="reading the merged blob failed"
403
+ elif [[ $mode != "merge" ]] && ! cat -- "$srcfile" >"$dstfile" 2>/dev/null; then
396
404
  fail="the verbatim byte copy failed"
397
405
  elif [[ $iaction != "none" ]] &&
398
406
  ! preserve_render_index_append "$ipath" "$iline" "$iterm" "$ipre"; then