@danmoisan/drm-copilot-mcp 0.0.1 → 0.0.6

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 (183) hide show
  1. package/out/mcp-server.js +98 -21
  2. package/package.json +21 -5
  3. package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +15 -3
  4. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_branch_base_check_unmerged_pr_deps.md +16 -0
  5. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_every_change_through_lifecycle.md +15 -0
  6. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_policy_compliance_not_optional.md +18 -0
  7. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_potential_to_issue_creates_github_issue.md +13 -0
  8. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_remediation_plan_em_dash_required.md +13 -0
  9. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_small_bug_uses_minor_audit.md +13 -0
  10. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +13 -0
  11. package/resources/claude-customizations/.claude/agents/atomic-executor.md +7 -7
  12. package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +4 -5
  13. package/resources/claude-customizations/.claude/agents/feature-review.md +7 -3
  14. package/resources/claude-customizations/.claude/agents/orchestrator.md +22 -3
  15. package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
  16. package/resources/claude-customizations/.claude/agents/pr-author.md +78 -0
  17. package/resources/claude-customizations/.claude/agents/task-researcher.md +10 -6
  18. package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
  19. package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +300 -0
  20. package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +40 -14
  21. package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +148 -0
  22. package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +210 -0
  23. package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +333 -0
  24. package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +216 -0
  25. package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +84 -15
  26. package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +1 -1
  27. package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +75 -5
  28. package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +93 -0
  29. package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +136 -0
  30. package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +90 -6
  31. package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +46 -0
  32. package/resources/claude-customizations/.claude/rules/benchmark-baselines.md +35 -0
  33. package/resources/claude-customizations/.claude/rules/ci-workflows.md +36 -0
  34. package/resources/claude-customizations/.claude/rules/csharp.md +62 -16
  35. package/resources/claude-customizations/.claude/rules/general-code-change.md +12 -3
  36. package/resources/claude-customizations/.claude/rules/general-unit-test.md +47 -2
  37. package/resources/claude-customizations/.claude/rules/orchestrator-state.md +39 -0
  38. package/resources/claude-customizations/.claude/rules/powershell.md +5 -5
  39. package/resources/claude-customizations/.claude/rules/python.md +4 -3
  40. package/resources/claude-customizations/.claude/rules/quality-tiers.md +51 -0
  41. package/resources/claude-customizations/.claude/rules/typescript.md +37 -8
  42. package/resources/claude-customizations/.claude/settings.json +59 -12
  43. package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +2 -2
  44. package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +25 -10
  45. package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +0 -1
  46. package/resources/claude-customizations/.claude/skills/execute-hard-lock/SKILL.md +6 -6
  47. package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +8 -8
  48. package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +17 -6
  49. package/resources/claude-customizations/.claude/skills/human-exception-runbook/SKILL.md +52 -0
  50. package/resources/claude-customizations/.claude/skills/human-exception-runbook/example.runbook.md +36 -0
  51. package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +4 -4
  52. package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +172 -5
  53. package/resources/claude-customizations/.claude/skills/policy-audit-template-usage/SKILL.md +3 -3
  54. package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +4 -4
  55. package/resources/claude-customizations/.claude/skills/pr-base-branch-merge-base/SKILL.md +3 -3
  56. package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +1 -1
  57. package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +90 -17
  58. package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +5 -3
  59. package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +69 -0
  60. package/resources/claude-customizations/.claude-variants/csharp-legacy/rules/csharp.md +96 -0
  61. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +77 -0
  62. package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +64 -0
  63. package/resources/claude-customizations/pack-manifests/core.json +65 -0
  64. package/resources/claude-customizations/pack-manifests/csharp-legacy.json +11 -0
  65. package/resources/claude-customizations/pack-manifests/csharp-modern.json +12 -0
  66. package/resources/claude-customizations/pack-manifests/powershell.json +14 -0
  67. package/resources/claude-customizations/pack-manifests/python.json +14 -0
  68. package/resources/claude-customizations/pack-manifests/typescript.json +9 -0
  69. package/resources/claude-dir-customizations/.mcp.json +3 -3
  70. package/resources/codex-and-agents-customizations/.agents/README.md +1 -1
  71. package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +0 -5
  72. package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +52 -0
  73. package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +16 -8
  74. package/resources/codex-and-agents-customizations/.agents/skills/benchmark-baselines/SKILL.md +44 -0
  75. package/resources/codex-and-agents-customizations/.agents/skills/ci-workflows/SKILL.md +45 -0
  76. package/resources/codex-and-agents-customizations/.agents/skills/commit-message/SKILL.md +3 -11
  77. package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +1 -5
  78. package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +1 -6
  79. package/resources/codex-and-agents-customizations/.agents/skills/csharp-orchestration-state-machine/SKILL.md +0 -5
  80. package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +0 -5
  81. package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +0 -5
  82. package/resources/codex-and-agents-customizations/.agents/skills/execute-hard-lock/SKILL.md +8 -17
  83. package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +41 -25
  84. package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +1 -6
  85. package/resources/codex-and-agents-customizations/.agents/skills/fill-feature-docs/SKILL.md +0 -5
  86. package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +86 -0
  87. package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +111 -0
  88. package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/SKILL.md +57 -0
  89. package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/example.runbook.md +36 -0
  90. package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +0 -9
  91. package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +0 -9
  92. package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +0 -9
  93. package/resources/codex-and-agents-customizations/.agents/skills/make-skill-template/SKILL.md +0 -5
  94. package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +206 -18
  95. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +48 -0
  96. package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +94 -10
  97. package/resources/codex-and-agents-customizations/.agents/skills/policy-audit-template-usage/SKILL.md +3 -8
  98. package/resources/codex-and-agents-customizations/.agents/skills/policy-compliance-order/SKILL.md +0 -10
  99. package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +4 -8
  100. package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -6
  101. package/resources/codex-and-agents-customizations/.agents/skills/powershell-orchestration-state-machine/SKILL.md +0 -5
  102. package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +3 -9
  103. package/resources/codex-and-agents-customizations/.agents/skills/pr-author/SKILL.md +1 -9
  104. package/resources/codex-and-agents-customizations/.agents/skills/pr-base-branch-merge-base/SKILL.md +4 -9
  105. package/resources/codex-and-agents-customizations/.agents/skills/pr-context-artifacts/SKILL.md +0 -5
  106. package/resources/codex-and-agents-customizations/.agents/skills/python/SKILL.md +1 -5
  107. package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -6
  108. package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +0 -5
  109. package/resources/codex-and-agents-customizations/.agents/skills/python-suppressions/SKILL.md +2 -6
  110. package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +57 -0
  111. package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +0 -5
  112. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +96 -72
  113. package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/agents/openai.yaml +1 -1
  114. package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +0 -10
  115. package/resources/codex-and-agents-customizations/.agents/skills/review-epic/SKILL.md +0 -5
  116. package/resources/codex-and-agents-customizations/.agents/skills/review-feature/SKILL.md +0 -5
  117. package/resources/codex-and-agents-customizations/.agents/skills/review-staged/SKILL.md +0 -5
  118. package/resources/codex-and-agents-customizations/.agents/skills/self-explanatory-code-commenting/SKILL.md +2 -6
  119. package/resources/codex-and-agents-customizations/.agents/skills/skill-canonical-location-audit/SKILL.md +0 -5
  120. package/resources/codex-and-agents-customizations/.agents/skills/tonality/SKILL.md +86 -0
  121. package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +297 -0
  122. package/resources/codex-and-agents-customizations/.agents/skills/translate-copilot-to-claude/SKILL.md +0 -22
  123. package/resources/codex-and-agents-customizations/.agents/skills/typescript/SKILL.md +1 -5
  124. package/resources/codex-and-agents-customizations/.agents/skills/typescript-suppressions/SKILL.md +2 -6
  125. package/resources/codex-and-agents-customizations/.agents/skills/update-status/SKILL.md +0 -5
  126. package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +5 -5
  127. package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +91 -63
  128. package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +1 -1
  129. package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
  130. package/resources/codex-and-agents-customizations/.codex/config.toml +111 -137
  131. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
  132. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +300 -0
  133. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +148 -0
  134. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +336 -0
  135. package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +84 -18
  136. package/resources/codex-and-agents-customizations/.codex/prompts/orchestrate-work.md +4 -3
  137. package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +5 -0
  138. package/resources/codex-and-agents-customizations/.github/workflows/_validate-orchestrator-state.yml +68 -0
  139. package/resources/codex-and-agents-customizations/.github/workflows/validate-orchestrator-state.yml +15 -0
  140. package/resources/config/orchestration-routing.json +76 -0
  141. package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +1 -1
  142. package/resources/customizations/.github/agents/atomic_executor.agent.md +1 -1
  143. package/resources/customizations/.github/agents/atomic_planning.agent.md +10 -10
  144. package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +6 -2
  145. package/resources/customizations/.github/agents/feature-review.agent.md +2 -2
  146. package/resources/customizations/.github/agents/orchestrator.agent.md +6 -2
  147. package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +4 -4
  148. package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +10 -10
  149. package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -2
  150. package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +2 -2
  151. package/resources/customizations/.github/agents/pr-author.agent.md +25 -0
  152. package/resources/customizations/.github/agents/python-orchestrator.agent.md +6 -2
  153. package/resources/customizations/.github/agents/staged-review.agent.md +1 -1
  154. package/resources/customizations/.github/agents/task-researcher.agent.md +4 -4
  155. package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +6 -6
  156. package/resources/customizations/.github/prompts/fillout-prd-feature.prompt.md +1 -1
  157. package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +1 -1
  158. package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +5 -3
  159. package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +5 -3
  160. package/resources/customizations/.github/prompts/research-issue.prompt.md +4 -3
  161. package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +14 -1
  162. package/resources/customizations/.github/skills/feature-promotion-lifecycle/SKILL.md +11 -7
  163. package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +10 -1
  164. package/resources/customizations/.github/skills/pr-base-branch-merge-base/SKILL.md +2 -2
  165. package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +5 -0
  166. package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +7 -0
  167. package/resources/scripts/dev_tools/_orchestrator_state_human_interaction.py +127 -0
  168. package/resources/scripts/dev_tools/_orchestrator_state_routing.py +216 -0
  169. package/resources/scripts/dev_tools/push_down_claude_customizations.py +276 -61
  170. package/resources/scripts/dev_tools/push_down_claude_filesystem.py +472 -0
  171. package/resources/scripts/dev_tools/push_down_claude_pack_selection.py +401 -0
  172. package/resources/scripts/dev_tools/validate_orchestration_artifacts.py +103 -411
  173. package/resources/scripts/dev_tools/validate_orchestration_review_artifacts.py +107 -0
  174. package/resources/scripts/dev_tools/validate_orchestrator_state.py +505 -0
  175. package/resources/scripts/dev_tools/validate_policy_audit_artifact.py +472 -0
  176. package/resources/templates/push_down_claude_customizations.py +162 -117
  177. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_repo_root_is_source_of_truth.md +0 -11
  178. package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_vsce_verify_package_location.md +0 -19
  179. package/resources/claude-customizations/.claude/agent-memory/orchestrator/project_extension_location.md +0 -11
  180. package/resources/claude-customizations/.claude/agent-memory/prd-feature/MEMORY.md +0 -1
  181. package/resources/claude-customizations/.claude/agent-memory/prd-feature/project_push_down_pattern.md +0 -13
  182. package/resources/claude-customizations/.claude/agent-memory/task-researcher/MEMORY.md +0 -3
  183. package/resources/claude-customizations/.claude/agent-memory/task-researcher/project_push_down_claude_dir.md +0 -11
@@ -0,0 +1,303 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Pre-tool-use hook that blocks Write operations on the orchestrator checkpoint
4
+ file when completed_steps appear out of declared canonical order.
5
+
6
+ .DESCRIPTION
7
+ Invoked by the Claude Code PreToolUse hook on Write or Edit operations. The
8
+ hook activates only when the target file_path is
9
+ artifacts/orchestration/orchestrator-state.json.
10
+
11
+ For Write tool calls, the content field is parsed as JSON and its
12
+ completed_steps array is validated against the canonical orchestrator
13
+ workflow step prefixes:
14
+
15
+ S0_startup_checks
16
+ S1_change_budget_estimation
17
+ S2_research
18
+ S3_promotion
19
+ S4_atomic_planning
20
+ S5_atomic_execution
21
+ S6_pre_review_commit
22
+ S7_feature_review
23
+ S8_create_pr
24
+ S9_remediation_loop
25
+ S10_post_pr
26
+ S12_complete
27
+
28
+ Entries that do not match any canonical prefix are treated as informational
29
+ and ignored. If any pair (i, j) with i < j has a higher canonical index at
30
+ position i than at position j, the script blocks with a reason listing the
31
+ offending pair. A non-empty rollback_history array suppresses this check
32
+ because rollbacks legitimately reorder steps.
33
+
34
+ Edit tool calls supply only old_string/new_string (a partial patch) and
35
+ cannot be reliably validated without the full target file content, so they
36
+ are allowed by this hook. The next Write call will catch a regression.
37
+
38
+ .NOTES
39
+ Compatible with PowerShell 7+. Read-only validation gate.
40
+ #>
41
+ [CmdletBinding()]
42
+ param()
43
+
44
+ $script:CanonicalStepPrefixes = @(
45
+ 'S0_startup_checks',
46
+ 'S1_change_budget_estimation',
47
+ 'S2_research',
48
+ 'S3_promotion',
49
+ 'S4_atomic_planning',
50
+ 'S5_atomic_execution',
51
+ 'S6_pre_review_commit',
52
+ 'S7_feature_review',
53
+ 'S8_create_pr',
54
+ 'S9_remediation_loop',
55
+ 'S10_post_pr',
56
+ 'S12_complete'
57
+ )
58
+
59
+ function ConvertFrom-CheckpointJson {
60
+ <#
61
+ .SYNOPSIS
62
+ Wrapper around ConvertFrom-Json for the checkpoint content. Mockable.
63
+ #>
64
+ [CmdletBinding()]
65
+ param(
66
+ [Parameter(Mandatory)]
67
+ [string] $Json
68
+ )
69
+
70
+ return $Json | ConvertFrom-Json -ErrorAction Stop
71
+ }
72
+
73
+ function Get-CanonicalStepIndex {
74
+ <#
75
+ .SYNOPSIS
76
+ Returns the canonical index for a completed_steps entry, or -1 when the
77
+ entry does not match any canonical prefix.
78
+ #>
79
+ [CmdletBinding()]
80
+ [OutputType([int])]
81
+ param(
82
+ [Parameter(Mandatory)]
83
+ [AllowEmptyString()]
84
+ [string] $StepEntry
85
+ )
86
+
87
+ for ($i = 0; $i -lt $script:CanonicalStepPrefixes.Count; $i++) {
88
+ $prefix = $script:CanonicalStepPrefixes[$i]
89
+ if ($StepEntry -eq $prefix -or $StepEntry.StartsWith("$prefix" + '_') -or $StepEntry.StartsWith("$prefix.") -or $StepEntry.StartsWith("$prefix-")) {
90
+ return $i
91
+ }
92
+ # The S3_promotion family of variants (S3_promotion_potential, S3_promotion_issue,
93
+ # S3_promotion_folder, etc.) is matched by the StartsWith branches above.
94
+ }
95
+
96
+ return -1
97
+ }
98
+
99
+ function Get-OutOfOrderPair {
100
+ <#
101
+ .SYNOPSIS
102
+ Returns the first pair (entry-at-i, entry-at-j, indices) where i<j but the
103
+ canonical index at i is greater than the canonical index at j. Non-canonical
104
+ entries (index -1) are skipped. Returns $null when in order.
105
+ #>
106
+ [CmdletBinding()]
107
+ param(
108
+ [Parameter(Mandatory)]
109
+ [AllowEmptyCollection()]
110
+ [string[]] $CompletedSteps
111
+ )
112
+
113
+ $indexed = @()
114
+ for ($i = 0; $i -lt $CompletedSteps.Count; $i++) {
115
+ $idx = Get-CanonicalStepIndex -StepEntry $CompletedSteps[$i]
116
+ if ($idx -ge 0) {
117
+ $indexed += [pscustomobject]@{ Position = $i; CanonicalIndex = $idx; Entry = $CompletedSteps[$i] }
118
+ }
119
+ }
120
+
121
+ for ($a = 0; $a -lt $indexed.Count; $a++) {
122
+ for ($b = $a + 1; $b -lt $indexed.Count; $b++) {
123
+ if ($indexed[$a].CanonicalIndex -gt $indexed[$b].CanonicalIndex) {
124
+ return [pscustomobject]@{
125
+ EarlierEntry = $indexed[$a].Entry
126
+ EarlierPos = $indexed[$a].Position
127
+ LaterEntry = $indexed[$b].Entry
128
+ LaterPos = $indexed[$b].Position
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ return $null
135
+ }
136
+
137
+ function Test-StepHasPrefix {
138
+ [CmdletBinding()]
139
+ [OutputType([bool])]
140
+ param(
141
+ [Parameter(Mandatory)]
142
+ [string] $StepEntry,
143
+
144
+ [Parameter(Mandatory)]
145
+ [string] $Prefix
146
+ )
147
+
148
+ return $StepEntry -eq $Prefix -or $StepEntry.StartsWith("$Prefix" + '_') -or $StepEntry.StartsWith("$Prefix.") -or $StepEntry.StartsWith("$Prefix-")
149
+ }
150
+
151
+ function Get-MissingPrerequisiteForAdvancedStep {
152
+ [CmdletBinding()]
153
+ param(
154
+ [Parameter(Mandatory)]
155
+ [AllowEmptyCollection()]
156
+ [string[]] $CompletedSteps
157
+ )
158
+
159
+ $hasPromotion = $false
160
+ $hasPlanning = $false
161
+ foreach ($step in $CompletedSteps) {
162
+ if (Test-StepHasPrefix -StepEntry $step -Prefix 'S3_promotion') {
163
+ $hasPromotion = $true
164
+ }
165
+ if (Test-StepHasPrefix -StepEntry $step -Prefix 'S4_atomic_planning') {
166
+ $hasPlanning = $true
167
+ }
168
+ }
169
+
170
+ foreach ($step in $CompletedSteps) {
171
+ $index = Get-CanonicalStepIndex -StepEntry $step
172
+ if ($index -ge 5 -and (-not $hasPromotion -or -not $hasPlanning)) {
173
+ return [pscustomobject]@{
174
+ Step = $step
175
+ MissingPromotion = -not $hasPromotion
176
+ MissingPlanning = -not $hasPlanning
177
+ }
178
+ }
179
+ }
180
+
181
+ return $null
182
+ }
183
+
184
+ function Test-IsCheckpointPath {
185
+ [CmdletBinding()]
186
+ [OutputType([bool])]
187
+ param(
188
+ [Parameter(Mandatory)]
189
+ [string] $NormalizedPath
190
+ )
191
+
192
+ return $NormalizedPath -match '(^|/)artifacts/orchestration/orchestrator-state\.json$'
193
+ }
194
+
195
+ function Invoke-CheckpointMonotonicDecision {
196
+ <#
197
+ .SYNOPSIS
198
+ Parses CLAUDE_TOOL_INPUT and returns an allow-or-block decision.
199
+ #>
200
+ [CmdletBinding()]
201
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
202
+ param(
203
+ [string] $ToolInputRaw
204
+ )
205
+
206
+ if (-not $ToolInputRaw) {
207
+ return [ordered]@{ decision = 'allow' }
208
+ }
209
+
210
+ try {
211
+ $toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
212
+ }
213
+ catch {
214
+ throw "enforce-checkpoint-monotonic hook received malformed JSON in CLAUDE_TOOL_INPUT: $_"
215
+ }
216
+
217
+ $filePath = $toolInput.file_path
218
+ if (-not $filePath) {
219
+ return [ordered]@{ decision = 'allow' }
220
+ }
221
+
222
+ $normalized = $filePath -replace '\\', '/'
223
+ if (-not (Test-IsCheckpointPath -NormalizedPath $normalized)) {
224
+ return [ordered]@{ decision = 'allow' }
225
+ }
226
+
227
+ # Write tool: validate the content payload. Edit tool: partial new_string is
228
+ # not reliable without the full target file content, so allow.
229
+ $content = $toolInput.content
230
+ if (-not $content) {
231
+ return [ordered]@{ decision = 'allow' }
232
+ }
233
+
234
+ try {
235
+ $payload = ConvertFrom-CheckpointJson -Json $content
236
+ }
237
+ catch {
238
+ # The content itself is not valid JSON. Let downstream tools surface the
239
+ # error rather than blocking with a misleading reason here.
240
+ return [ordered]@{ decision = 'allow' }
241
+ }
242
+
243
+ if (-not $payload.PSObject.Properties.Name -contains 'completed_steps') {
244
+ return [ordered]@{ decision = 'allow' }
245
+ }
246
+
247
+ $steps = @()
248
+ if ($payload.completed_steps) {
249
+ foreach ($s in $payload.completed_steps) {
250
+ $steps += [string]$s
251
+ }
252
+ }
253
+
254
+ $rollbackHistory = $null
255
+ if ($payload.PSObject.Properties.Name -contains 'rollback_history') {
256
+ $rollbackHistory = $payload.rollback_history
257
+ }
258
+ if ($rollbackHistory -and @($rollbackHistory).Count -gt 0) {
259
+ return [ordered]@{ decision = 'allow' }
260
+ }
261
+
262
+ $pair = if ($steps.Count -ge 2) { Get-OutOfOrderPair -CompletedSteps $steps } else { $null }
263
+ if ($null -ne $pair) {
264
+ return [ordered]@{
265
+ decision = 'block'
266
+ reason = "CHECKPOINT_ORDER_BLOCKED: completed_steps lists '$($pair.EarlierEntry)' at position $($pair.EarlierPos) before '$($pair.LaterEntry)' at position $($pair.LaterPos), but the canonical orchestrator workflow requires the later step to follow the earlier one. Reorder completed_steps or, if a rollback occurred, record it in rollback_history."
267
+ }
268
+ }
269
+
270
+ $missingPrerequisite = Get-MissingPrerequisiteForAdvancedStep -CompletedSteps $steps
271
+ if ($null -ne $missingPrerequisite) {
272
+ $missing = @()
273
+ if ($missingPrerequisite.MissingPromotion) {
274
+ $missing += 'S3_promotion'
275
+ }
276
+ if ($missingPrerequisite.MissingPlanning) {
277
+ $missing += 'S4_atomic_planning'
278
+ }
279
+ return [ordered]@{
280
+ decision = 'block'
281
+ reason = "CHECKPOINT_ORDER_BLOCKED: completed_steps lists '$($missingPrerequisite.Step)' before required prerequisite step(s): $($missing -join ', '). Record promotion and planning completion before implementation, review, PR, CI, or DONE steps."
282
+ }
283
+ }
284
+
285
+ return [ordered]@{ decision = 'allow' }
286
+ }
287
+
288
+ # Guard allows dot-sourcing in tests without executing the entrypoint.
289
+ if ($MyInvocation.InvocationName -eq '.') {
290
+ return
291
+ }
292
+
293
+ try {
294
+ $decision = Invoke-CheckpointMonotonicDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
295
+ }
296
+ catch {
297
+ Write-Error $_
298
+ exit 1
299
+ }
300
+
301
+ $decision | ConvertTo-Json -Compress | Write-Output
302
+
303
+ exit 0
@@ -0,0 +1,300 @@
1
+ <#
2
+ .SYNOPSIS
3
+ Pre-tool-use hook that blocks Write operations on the orchestrator checkpoint
4
+ file when the checkpoint asserts completion without verifiable completion
5
+ evidence.
6
+
7
+ .DESCRIPTION
8
+ Invoked by the Claude Code PreToolUse hook on Write or Edit operations. The
9
+ hook activates only when the target file_path normalizes to
10
+ artifacts/orchestration/orchestrator-state.json.
11
+
12
+ A written checkpoint asserts completion when any of the following hold:
13
+
14
+ next_step == "complete"
15
+ completed_steps contains "S12_complete"
16
+ step8_status, step9_status, or step10_status == "completed"
17
+
18
+ When completion is asserted, the write is blocked unless ALL of the following
19
+ completion evidence is present:
20
+
21
+ - a non-empty issue-num (top-level, falling back to variables.issue-num);
22
+ - a non-empty feature-folder (top-level, falling back to
23
+ variables.feature-folder);
24
+ - a ci_gate object with conclusion == "success" and a non-empty head_sha.
25
+
26
+ The block reason names the specific missing evidence so the caller can
27
+ remediate. When completion is not asserted, the write is allowed
28
+ (backward compatibility).
29
+
30
+ Edit tool calls supply only old_string/new_string (a partial patch) and
31
+ cannot be reliably validated without the full target file content, so they
32
+ are allowed by this hook, matching enforce-checkpoint-monotonic.ps1. For
33
+ Write calls whose content is not valid JSON, the hook allows the operation
34
+ and defers to downstream tools to surface the error.
35
+
36
+ .NOTES
37
+ Compatible with PowerShell 7+. Read-only validation gate.
38
+ #>
39
+ [CmdletBinding()]
40
+ param()
41
+
42
+ function ConvertFrom-CheckpointJson {
43
+ <#
44
+ .SYNOPSIS
45
+ Wrapper around ConvertFrom-Json for the checkpoint content. Mockable.
46
+ #>
47
+ [CmdletBinding()]
48
+ param(
49
+ [Parameter(Mandatory)]
50
+ [string] $Json
51
+ )
52
+
53
+ return $Json | ConvertFrom-Json -ErrorAction Stop
54
+ }
55
+
56
+ function Test-IsCheckpointPath {
57
+ [CmdletBinding()]
58
+ [OutputType([bool])]
59
+ param(
60
+ [Parameter(Mandatory)]
61
+ [string] $NormalizedPath
62
+ )
63
+
64
+ return $NormalizedPath -match '(^|/)artifacts/orchestration/orchestrator-state\.json$'
65
+ }
66
+
67
+ function Get-CheckpointStringValue {
68
+ <#
69
+ .SYNOPSIS
70
+ Returns a trimmed string for a payload property, or an empty string when
71
+ the property is absent or its value is not a non-empty string.
72
+ #>
73
+ [CmdletBinding()]
74
+ [OutputType([string])]
75
+ param(
76
+ [Parameter(Mandatory)]
77
+ [AllowNull()]
78
+ $Payload,
79
+
80
+ [Parameter(Mandatory)]
81
+ [string] $Name
82
+ )
83
+
84
+ if ($null -eq $Payload) {
85
+ return ''
86
+ }
87
+ if (-not ($Payload.PSObject.Properties.Name -contains $Name)) {
88
+ return ''
89
+ }
90
+
91
+ $value = $Payload.$Name
92
+ if ($null -eq $value) {
93
+ return ''
94
+ }
95
+
96
+ return ([string]$value).Trim()
97
+ }
98
+
99
+ function Test-CompletionAsserted {
100
+ <#
101
+ .SYNOPSIS
102
+ Returns $true when the checkpoint payload asserts completion via
103
+ next_step, completed_steps, or step8/9/10 status fields.
104
+ #>
105
+ [CmdletBinding()]
106
+ [OutputType([bool])]
107
+ param(
108
+ [Parameter(Mandatory)]
109
+ [AllowNull()]
110
+ $Payload
111
+ )
112
+
113
+ if ($null -eq $Payload) {
114
+ return $false
115
+ }
116
+
117
+ $nextStep = Get-CheckpointStringValue -Payload $Payload -Name 'next_step'
118
+ if ($nextStep -eq 'complete') {
119
+ return $true
120
+ }
121
+
122
+ if ($Payload.PSObject.Properties.Name -contains 'completed_steps' -and $Payload.completed_steps) {
123
+ foreach ($step in $Payload.completed_steps) {
124
+ if (([string]$step) -eq 'S12_complete') {
125
+ return $true
126
+ }
127
+ }
128
+ }
129
+
130
+ foreach ($statusField in @('step8_status', 'step9_status', 'step10_status')) {
131
+ $status = Get-CheckpointStringValue -Payload $Payload -Name $statusField
132
+ if ($status -eq 'completed') {
133
+ return $true
134
+ }
135
+ }
136
+
137
+ return $false
138
+ }
139
+
140
+ function Get-MissingCompletionEvidence {
141
+ <#
142
+ .SYNOPSIS
143
+ Returns the list of missing completion-evidence descriptions for a
144
+ completion-asserting checkpoint. An empty list means all evidence is
145
+ present.
146
+ #>
147
+ [CmdletBinding()]
148
+ [OutputType([string[]])]
149
+ param(
150
+ [Parameter(Mandatory)]
151
+ [AllowNull()]
152
+ $Payload
153
+ )
154
+
155
+ $missing = @()
156
+
157
+ $issueNum = Get-CheckpointStringValue -Payload $Payload -Name 'issue-num'
158
+ if (-not $issueNum -and $null -ne $Payload -and ($Payload.PSObject.Properties.Name -contains 'variables')) {
159
+ $issueNum = Get-CheckpointStringValue -Payload $Payload.variables -Name 'issue-num'
160
+ }
161
+ if (-not $issueNum) {
162
+ $missing += 'issue-num'
163
+ }
164
+
165
+ $featureFolder = Get-CheckpointStringValue -Payload $Payload -Name 'feature-folder'
166
+ if (-not $featureFolder -and $null -ne $Payload -and ($Payload.PSObject.Properties.Name -contains 'variables')) {
167
+ $featureFolder = Get-CheckpointStringValue -Payload $Payload.variables -Name 'feature-folder'
168
+ }
169
+ if (-not $featureFolder) {
170
+ $missing += 'feature-folder'
171
+ }
172
+
173
+ $ciGate = $null
174
+ if ($null -ne $Payload -and ($Payload.PSObject.Properties.Name -contains 'ci_gate')) {
175
+ $ciGate = $Payload.ci_gate
176
+ }
177
+ if ($null -eq $ciGate -or $ciGate -isnot [System.Management.Automation.PSCustomObject]) {
178
+ $missing += 'ci_gate (object with conclusion == "success" and non-empty head_sha)'
179
+ }
180
+ else {
181
+ $conclusion = Get-CheckpointStringValue -Payload $ciGate -Name 'conclusion'
182
+ if ($conclusion -ne 'success') {
183
+ $missing += 'ci_gate.conclusion == "success"'
184
+ }
185
+ $headSha = Get-CheckpointStringValue -Payload $ciGate -Name 'head_sha'
186
+ if (-not $headSha) {
187
+ $missing += 'ci_gate.head_sha'
188
+ }
189
+ }
190
+
191
+ if ($issueNum -eq '232') {
192
+ $prGate = $null
193
+ if ($null -ne $Payload -and ($Payload.PSObject.Properties.Name -contains 'pr_gate')) {
194
+ $prGate = $Payload.pr_gate
195
+ }
196
+ if ($null -eq $prGate -or $prGate -isnot [System.Management.Automation.PSCustomObject]) {
197
+ $missing += 'pr_gate (object with pr_number, pr_url, head_branch, and head_sha)'
198
+ }
199
+ else {
200
+ foreach ($field in @('pr_number', 'pr_url', 'head_branch', 'head_sha')) {
201
+ if (-not (Get-CheckpointStringValue -Payload $prGate -Name $field)) {
202
+ $missing += "pr_gate.$field"
203
+ }
204
+ }
205
+ $prHeadSha = Get-CheckpointStringValue -Payload $prGate -Name 'head_sha'
206
+ $ciHeadSha = Get-CheckpointStringValue -Payload $ciGate -Name 'head_sha'
207
+ if ($prHeadSha -and $ciHeadSha -and $prHeadSha -ne $ciHeadSha) {
208
+ $missing += 'ci_gate.head_sha matching pr_gate.head_sha'
209
+ }
210
+ }
211
+ }
212
+
213
+ return [string[]]$missing
214
+ }
215
+
216
+ function Invoke-CompletionConsistencyDecision {
217
+ <#
218
+ .SYNOPSIS
219
+ Parses CLAUDE_TOOL_INPUT and returns an allow-or-block decision based on
220
+ completion-evidence consistency.
221
+ #>
222
+ [CmdletBinding()]
223
+ [OutputType([System.Collections.Specialized.OrderedDictionary])]
224
+ param(
225
+ [string] $ToolInputRaw
226
+ )
227
+
228
+ if (-not $ToolInputRaw) {
229
+ return [ordered]@{ decision = 'allow' }
230
+ }
231
+
232
+ try {
233
+ $toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
234
+ }
235
+ catch {
236
+ throw "enforce-completion-consistency hook received malformed JSON in CLAUDE_TOOL_INPUT: $_"
237
+ }
238
+
239
+ $filePath = $toolInput.file_path
240
+ if (-not $filePath) {
241
+ return [ordered]@{ decision = 'allow' }
242
+ }
243
+
244
+ $normalized = $filePath -replace '\\', '/'
245
+ if (-not (Test-IsCheckpointPath -NormalizedPath $normalized)) {
246
+ return [ordered]@{ decision = 'allow' }
247
+ }
248
+
249
+ # Write tool: validate the content payload. Edit tool: partial new_string is
250
+ # not reliable without the full target file content, so allow.
251
+ $content = $toolInput.content
252
+ if (-not $content) {
253
+ return [ordered]@{ decision = 'allow' }
254
+ }
255
+
256
+ try {
257
+ $payload = ConvertFrom-CheckpointJson -Json $content
258
+ }
259
+ catch {
260
+ # The content itself is not valid JSON. Let downstream tools surface the
261
+ # error rather than blocking with a misleading reason here.
262
+ return [ordered]@{ decision = 'allow' }
263
+ }
264
+
265
+ if (-not (Test-CompletionAsserted -Payload $payload)) {
266
+ return [ordered]@{ decision = 'allow' }
267
+ }
268
+
269
+ $missing = Get-MissingCompletionEvidence -Payload $payload
270
+ if ($missing.Count -eq 0) {
271
+ return [ordered]@{ decision = 'allow' }
272
+ }
273
+
274
+ $issueContext = ''
275
+ if ((Get-CheckpointStringValue -Payload $payload -Name 'issue-num') -eq '232') {
276
+ $issueContext = ' Issue #232 requires pr_gate evidence and current-head ci_gate evidence.'
277
+ }
278
+
279
+ return [ordered]@{
280
+ decision = 'block'
281
+ reason = "COMPLETION_CONSISTENCY_BLOCKED: the checkpoint asserts completion but is missing required completion evidence: $($missing -join ', ').$issueContext A completion-asserting checkpoint must include a non-empty issue-num, a non-empty feature-folder, and a ci_gate object with conclusion == 'success' and a non-empty head_sha. Supply the missing evidence or remove the completion assertion."
282
+ }
283
+ }
284
+
285
+ # Guard allows dot-sourcing in tests without executing the entrypoint.
286
+ if ($MyInvocation.InvocationName -eq '.') {
287
+ return
288
+ }
289
+
290
+ try {
291
+ $decision = Invoke-CompletionConsistencyDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
292
+ }
293
+ catch {
294
+ Write-Error $_
295
+ exit 1
296
+ }
297
+
298
+ $decision | ConvertTo-Json -Compress | Write-Output
299
+
300
+ exit 0
@@ -17,12 +17,15 @@
17
17
  - artifacts/evidence/
18
18
  - artifacts/regression-testing/
19
19
  - artifacts/post-change/
20
+ - artifacts/research/
20
21
 
21
22
  All other paths pass through, including canonical evidence paths of the form
22
23
  <FEATURE>/evidence/<kind>/ and permitted artifacts/ sub-paths such as
23
- artifacts/orchestration/, artifacts/research/, artifacts/pr_context,
24
- artifacts/reviews/, artifacts/status/, artifacts/python/, artifacts/pester/,
25
- and artifacts/csharp/.
24
+ artifacts/orchestration/, artifacts/pr_context, artifacts/reviews/,
25
+ artifacts/status/, artifacts/python/, artifacts/pester/, and
26
+ artifacts/csharp/. Research output is no longer an artifacts/ sub-path; it
27
+ is written to the tracked roots docs/features/<feature>/research/
28
+ (feature-associated) or docs/research/ (one-off).
26
29
 
27
30
  If the file_path resolves to a forbidden prefix, the script writes a JSON response
28
31
  to stdout with 'decision': 'block' and exits with code 0 so Claude Code surfaces
@@ -61,7 +64,8 @@ function Test-EvidenceLocationForbidden {
61
64
  'artifacts/coverage/',
62
65
  'artifacts/evidence/',
63
66
  'artifacts/regression-testing/',
64
- 'artifacts/post-change/'
67
+ 'artifacts/post-change/',
68
+ 'artifacts/research/'
65
69
  )
66
70
 
67
71
  # Match the prefix either at the start of the string or after any directory separator,
@@ -133,18 +137,40 @@ function Invoke-EvidenceLocationDecision {
133
137
  return [ordered]@{ decision = 'allow' }
134
138
  }
135
139
 
140
+ function Invoke-EvidenceLocationEntryPoint {
141
+ <#
142
+ .SYNOPSIS
143
+ Runs the evidence-location decision and returns the process exit code.
144
+ .DESCRIPTION
145
+ Wraps the dispatch logic that the hook entry point performs so it can be
146
+ exercised by unit tests. On success it writes the compact JSON decision to
147
+ the output stream and returns 0. On a hard failure (malformed JSON) it
148
+ writes the error record and returns 1. This function does not call exit;
149
+ the thin entry-point wiring converts the returned code into a process exit.
150
+ .PARAMETER ToolInputRaw
151
+ The raw JSON string from $env:CLAUDE_TOOL_INPUT.
152
+ #>
153
+ [CmdletBinding()]
154
+ [OutputType([int])]
155
+ param(
156
+ [string] $ToolInputRaw = $env:CLAUDE_TOOL_INPUT
157
+ )
158
+
159
+ try {
160
+ $decision = Invoke-EvidenceLocationDecision -ToolInputRaw $ToolInputRaw
161
+ } catch {
162
+ Write-Error $_
163
+ return 1
164
+ }
165
+
166
+ $decision | ConvertTo-Json -Compress | Write-Output
167
+
168
+ return 0
169
+ }
170
+
136
171
  # Guard allows dot-sourcing in tests without executing the entrypoint.
137
172
  if ($MyInvocation.InvocationName -eq '.') {
138
173
  return
139
174
  }
140
175
 
141
- try {
142
- $decision = Invoke-EvidenceLocationDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
143
- } catch {
144
- Write-Error $_
145
- exit 1
146
- }
147
-
148
- $decision | ConvertTo-Json -Compress | Write-Output
149
-
150
- exit 0
176
+ exit (Invoke-EvidenceLocationEntryPoint)