@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.
- package/out/mcp-server.js +98 -21
- package/package.json +21 -5
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/MEMORY.md +15 -3
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_branch_base_check_unmerged_pr_deps.md +16 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_every_change_through_lifecycle.md +15 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_policy_compliance_not_optional.md +18 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_potential_to_issue_creates_github_issue.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_remediation_plan_em_dash_required.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_small_bug_uses_minor_audit.md +13 -0
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_test_files_count_against_500_cap.md +13 -0
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +7 -7
- package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +4 -5
- package/resources/claude-customizations/.claude/agents/feature-review.md +7 -3
- package/resources/claude-customizations/.claude/agents/orchestrator.md +22 -3
- package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -1
- package/resources/claude-customizations/.claude/agents/pr-author.md +78 -0
- package/resources/claude-customizations/.claude/agents/task-researcher.md +10 -6
- package/resources/claude-customizations/.claude/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
- package/resources/claude-customizations/.claude/hooks/enforce-completion-consistency.ps1 +300 -0
- package/resources/claude-customizations/.claude/hooks/enforce-evidence-locations.ps1 +40 -14
- package/resources/claude-customizations/.claude/hooks/enforce-feature-folder-order.ps1 +148 -0
- package/resources/claude-customizations/.claude/hooks/enforce-orchestration-preimplementation-gate.ps1 +210 -0
- package/resources/claude-customizations/.claude/hooks/enforce-pr-author-skill.ps1 +333 -0
- package/resources/claude-customizations/.claude/hooks/enforce-prd-feature-before-planner.ps1 +216 -0
- package/resources/claude-customizations/.claude/hooks/enforce-promotion-mcp-only.ps1 +84 -15
- package/resources/claude-customizations/.claude/hooks/validate-executor-output.ps1 +1 -1
- package/resources/claude-customizations/.claude/hooks/validate-feature-review-coverage.ps1 +75 -5
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +93 -0
- package/resources/claude-customizations/.claude/hooks/validate-pr-author-output.ps1 +136 -0
- package/resources/claude-customizations/.claude/hooks/validate-task-researcher-output.ps1 +90 -6
- package/resources/claude-customizations/.claude/rules/architecture-boundaries.md +46 -0
- package/resources/claude-customizations/.claude/rules/benchmark-baselines.md +35 -0
- package/resources/claude-customizations/.claude/rules/ci-workflows.md +36 -0
- package/resources/claude-customizations/.claude/rules/csharp.md +62 -16
- package/resources/claude-customizations/.claude/rules/general-code-change.md +12 -3
- package/resources/claude-customizations/.claude/rules/general-unit-test.md +47 -2
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +39 -0
- package/resources/claude-customizations/.claude/rules/powershell.md +5 -5
- package/resources/claude-customizations/.claude/rules/python.md +4 -3
- package/resources/claude-customizations/.claude/rules/quality-tiers.md +51 -0
- package/resources/claude-customizations/.claude/rules/typescript.md +37 -8
- package/resources/claude-customizations/.claude/settings.json +59 -12
- package/resources/claude-customizations/.claude/skills/atomic-plan-contract/SKILL.md +2 -2
- package/resources/claude-customizations/.claude/skills/csharp-qa-gate/SKILL.md +25 -10
- package/resources/claude-customizations/.claude/skills/evidence-and-timestamp-conventions/SKILL.md +0 -1
- package/resources/claude-customizations/.claude/skills/execute-hard-lock/SKILL.md +6 -6
- package/resources/claude-customizations/.claude/skills/feature-promotion-lifecycle/SKILL.md +8 -8
- package/resources/claude-customizations/.claude/skills/feature-review-workflow/SKILL.md +17 -6
- package/resources/claude-customizations/.claude/skills/human-exception-runbook/SKILL.md +52 -0
- package/resources/claude-customizations/.claude/skills/human-exception-runbook/example.runbook.md +36 -0
- package/resources/claude-customizations/.claude/skills/invoke-csharp-engineer/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +172 -5
- package/resources/claude-customizations/.claude/skills/policy-audit-template-usage/SKILL.md +3 -3
- package/resources/claude-customizations/.claude/skills/powershell-qa-gate/SKILL.md +4 -4
- package/resources/claude-customizations/.claude/skills/pr-base-branch-merge-base/SKILL.md +3 -3
- package/resources/claude-customizations/.claude/skills/python-qa-gate/SKILL.md +1 -1
- package/resources/claude-customizations/.claude/skills/remediation-handoff-atomic-planner/SKILL.md +90 -17
- package/resources/claude-customizations/.claude/skills/research-issue/SKILL.md +5 -3
- package/resources/claude-customizations/.claude-variants/csharp-legacy/agents/csharp-typed-engineer.md +69 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/rules/csharp.md +96 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/csharp-qa-gate/SKILL.md +77 -0
- package/resources/claude-customizations/.claude-variants/csharp-legacy/skills/invoke-csharp-engineer/SKILL.md +64 -0
- package/resources/claude-customizations/pack-manifests/core.json +65 -0
- package/resources/claude-customizations/pack-manifests/csharp-legacy.json +11 -0
- package/resources/claude-customizations/pack-manifests/csharp-modern.json +12 -0
- package/resources/claude-customizations/pack-manifests/powershell.json +14 -0
- package/resources/claude-customizations/pack-manifests/python.json +14 -0
- package/resources/claude-customizations/pack-manifests/typescript.json +9 -0
- package/resources/claude-dir-customizations/.mcp.json +3 -3
- package/resources/codex-and-agents-customizations/.agents/README.md +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/acceptance-criteria-tracking/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/architecture-boundaries/SKILL.md +52 -0
- package/resources/codex-and-agents-customizations/.agents/skills/atomic-plan-contract/SKILL.md +16 -8
- package/resources/codex-and-agents-customizations/.agents/skills/benchmark-baselines/SKILL.md +44 -0
- package/resources/codex-and-agents-customizations/.agents/skills/ci-workflows/SKILL.md +45 -0
- package/resources/codex-and-agents-customizations/.agents/skills/commit-message/SKILL.md +3 -11
- package/resources/codex-and-agents-customizations/.agents/skills/csharp/SKILL.md +1 -5
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-change-budget-router/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-orchestration-state-machine/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/csharp-qa-gate/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/evidence-and-timestamp-conventions/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/execute-hard-lock/SKILL.md +8 -17
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +41 -25
- package/resources/codex-and-agents-customizations/.agents/skills/feature-review-workflow/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/fill-feature-docs/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/general-code-change/SKILL.md +86 -0
- package/resources/codex-and-agents-customizations/.agents/skills/general-unit-test/SKILL.md +111 -0
- package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/SKILL.md +57 -0
- package/resources/codex-and-agents-customizations/.agents/skills/human-exception-runbook/example.runbook.md +36 -0
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-csharp-engineer/SKILL.md +0 -9
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-powershell-engineer/SKILL.md +0 -9
- package/resources/codex-and-agents-customizations/.agents/skills/invoke-python-engineer/SKILL.md +0 -9
- package/resources/codex-and-agents-customizations/.agents/skills/make-skill-template/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +206 -18
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-state/SKILL.md +48 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +94 -10
- package/resources/codex-and-agents-customizations/.agents/skills/policy-audit-template-usage/SKILL.md +3 -8
- package/resources/codex-and-agents-customizations/.agents/skills/policy-compliance-order/SKILL.md +0 -10
- package/resources/codex-and-agents-customizations/.agents/skills/powershell/SKILL.md +4 -8
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-change-budget-router/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-orchestration-state-machine/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/powershell-qa-gate/SKILL.md +3 -9
- package/resources/codex-and-agents-customizations/.agents/skills/pr-author/SKILL.md +1 -9
- package/resources/codex-and-agents-customizations/.agents/skills/pr-base-branch-merge-base/SKILL.md +4 -9
- package/resources/codex-and-agents-customizations/.agents/skills/pr-context-artifacts/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/python/SKILL.md +1 -5
- package/resources/codex-and-agents-customizations/.agents/skills/python-change-budget-router/SKILL.md +1 -6
- package/resources/codex-and-agents-customizations/.agents/skills/python-qa-gate/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/python-suppressions/SKILL.md +2 -6
- package/resources/codex-and-agents-customizations/.agents/skills/quality-tiers/SKILL.md +57 -0
- package/resources/codex-and-agents-customizations/.agents/skills/remediation-handoff-atomic-planner/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/SKILL.md +96 -72
- package/resources/codex-and-agents-customizations/.agents/skills/repo-automation-adapter/agents/openai.yaml +1 -1
- package/resources/codex-and-agents-customizations/.agents/skills/research-issue/SKILL.md +0 -10
- package/resources/codex-and-agents-customizations/.agents/skills/review-epic/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/review-feature/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/review-staged/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/self-explanatory-code-commenting/SKILL.md +2 -6
- package/resources/codex-and-agents-customizations/.agents/skills/skill-canonical-location-audit/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.agents/skills/tonality/SKILL.md +86 -0
- package/resources/codex-and-agents-customizations/.agents/skills/translate-claude-to-codex/SKILL.md +297 -0
- package/resources/codex-and-agents-customizations/.agents/skills/translate-copilot-to-claude/SKILL.md +0 -22
- package/resources/codex-and-agents-customizations/.agents/skills/typescript/SKILL.md +1 -5
- package/resources/codex-and-agents-customizations/.agents/skills/typescript-suppressions/SKILL.md +2 -6
- package/resources/codex-and-agents-customizations/.agents/skills/update-status/SKILL.md +0 -5
- package/resources/codex-and-agents-customizations/.codex/agents/atomic-executor.toml +5 -5
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +91 -63
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-atomic-executor.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/agents/powershell-typed-engineer.toml +1 -1
- package/resources/codex-and-agents-customizations/.codex/config.toml +111 -137
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-checkpoint-monotonic.ps1 +303 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +300 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-orchestration-preimplementation-gate.ps1 +148 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-pr-author-skill.ps1 +336 -0
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-promotion-mcp-only.ps1 +84 -18
- package/resources/codex-and-agents-customizations/.codex/prompts/orchestrate-work.md +4 -3
- package/resources/codex-and-agents-customizations/.codex/scripts/post-codex-worktree-session.ps1 +5 -0
- package/resources/codex-and-agents-customizations/.github/workflows/_validate-orchestrator-state.yml +68 -0
- package/resources/codex-and-agents-customizations/.github/workflows/validate-orchestrator-state.yml +15 -0
- package/resources/config/orchestration-routing.json +76 -0
- package/resources/customizations/.github/agents/Powershell DI Unit Test Engineer.agent.md +1 -1
- package/resources/customizations/.github/agents/atomic_executor.agent.md +1 -1
- package/resources/customizations/.github/agents/atomic_planning.agent.md +10 -10
- package/resources/customizations/.github/agents/csharp-orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/feature-review.agent.md +2 -2
- package/resources/customizations/.github/agents/orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/powershell-atomic-executor.agent.md +4 -4
- package/resources/customizations/.github/agents/powershell-atomic-planning.agent.md +10 -10
- package/resources/customizations/.github/agents/powershell-orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/powershell-typed-engineer.agent.md +2 -2
- package/resources/customizations/.github/agents/pr-author.agent.md +25 -0
- package/resources/customizations/.github/agents/python-orchestrator.agent.md +6 -2
- package/resources/customizations/.github/agents/staged-review.agent.md +1 -1
- package/resources/customizations/.github/agents/task-researcher.agent.md +4 -4
- package/resources/customizations/.github/instructions/powershell-code-change.instructions.md +6 -6
- package/resources/customizations/.github/prompts/fillout-prd-feature.prompt.md +1 -1
- package/resources/customizations/.github/prompts/generate-commit-message-repo.prompt.md +1 -1
- package/resources/customizations/.github/prompts/orchestrate-csharp-work.prompt.md +5 -3
- package/resources/customizations/.github/prompts/orchestrate-work.prompt.md +5 -3
- package/resources/customizations/.github/prompts/research-issue.prompt.md +4 -3
- package/resources/customizations/.github/skills/atomic-plan-contract/SKILL.md +14 -1
- package/resources/customizations/.github/skills/feature-promotion-lifecycle/SKILL.md +11 -7
- package/resources/customizations/.github/skills/feature-review-workflow/SKILL.md +10 -1
- package/resources/customizations/.github/skills/pr-base-branch-merge-base/SKILL.md +2 -2
- package/resources/customizations/.github/skills/remediation-handoff-atomic-planner/SKILL.md +5 -0
- package/resources/powershell/PoshQC/settings/pester.runsettings.psd1 +7 -0
- package/resources/scripts/dev_tools/_orchestrator_state_human_interaction.py +127 -0
- package/resources/scripts/dev_tools/_orchestrator_state_routing.py +216 -0
- package/resources/scripts/dev_tools/push_down_claude_customizations.py +276 -61
- package/resources/scripts/dev_tools/push_down_claude_filesystem.py +472 -0
- package/resources/scripts/dev_tools/push_down_claude_pack_selection.py +401 -0
- package/resources/scripts/dev_tools/validate_orchestration_artifacts.py +103 -411
- package/resources/scripts/dev_tools/validate_orchestration_review_artifacts.py +107 -0
- package/resources/scripts/dev_tools/validate_orchestrator_state.py +505 -0
- package/resources/scripts/dev_tools/validate_policy_audit_artifact.py +472 -0
- package/resources/templates/push_down_claude_customizations.py +162 -117
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_repo_root_is_source_of_truth.md +0 -11
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/feedback_vsce_verify_package_location.md +0 -19
- package/resources/claude-customizations/.claude/agent-memory/orchestrator/project_extension_location.md +0 -11
- package/resources/claude-customizations/.claude/agent-memory/prd-feature/MEMORY.md +0 -1
- package/resources/claude-customizations/.claude/agent-memory/prd-feature/project_push_down_pattern.md +0 -13
- package/resources/claude-customizations/.claude/agent-memory/task-researcher/MEMORY.md +0 -3
- 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/
|
|
24
|
-
artifacts/
|
|
25
|
-
|
|
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
|
-
|
|
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)
|