@danmoisan/drm-copilot-mcp 1.0.7 → 1.0.9
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 +82 -8
- package/package.json +1 -1
- package/resources/claude-customizations/.claude/agents/atomic-executor.md +1 -0
- package/resources/claude-customizations/.claude/agents/atomic-planner.md +1 -0
- package/resources/claude-customizations/.claude/agents/csharp-typed-engineer.md +1 -0
- package/resources/claude-customizations/.claude/agents/epic-orchestrator.md +1 -0
- package/resources/claude-customizations/.claude/agents/epic-review.md +1 -0
- package/resources/claude-customizations/.claude/agents/feature-review.md +1 -0
- package/resources/claude-customizations/.claude/agents/orchestrator.md +13 -0
- package/resources/claude-customizations/.claude/agents/powershell-typed-engineer.md +1 -0
- package/resources/claude-customizations/.claude/agents/prd-feature.md +1 -0
- package/resources/claude-customizations/.claude/agents/python-typed-engineer.md +1 -0
- package/resources/claude-customizations/.claude/agents/staged-review.md +1 -0
- package/resources/claude-customizations/.claude/agents/status-updater.md +1 -0
- package/resources/claude-customizations/.claude/agents/typescript-engineer.md +1 -0
- package/resources/claude-customizations/.claude/hooks/enforce-model-routing-receipt.ps1 +182 -0
- package/resources/claude-customizations/.claude/hooks/validate-orchestrator-output.ps1 +9 -1
- package/resources/claude-customizations/.claude/lib/model-routing/ModelRouting.psm1 +209 -0
- package/resources/claude-customizations/.claude/rules/orchestrator-state.md +17 -0
- package/resources/claude-customizations/.claude/settings.json +4 -0
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +3 -3
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +22 -3
- package/resources/claude-customizations/pack-manifests/core.json +3 -1
- package/resources/codex-and-agents-customizations/.agents/skills/feature-promotion-lifecycle/SKILL.md +8 -2
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrate/SKILL.md +18 -0
- package/resources/codex-and-agents-customizations/.agents/skills/orchestrator-workflow/SKILL.md +13 -0
- package/resources/codex-and-agents-customizations/.codex/agents/orchestrator.toml +15 -14
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-consistency.ps1 +144 -28
- package/resources/codex-and-agents-customizations/.codex/hooks/enforce-completion-helpers.ps1 +163 -0
package/out/mcp-server.js
CHANGED
|
@@ -16208,6 +16208,10 @@ var REPO_AUTOMATION_TOOL_DEFINITIONS = [
|
|
|
16208
16208
|
require_complete: {
|
|
16209
16209
|
type: "boolean",
|
|
16210
16210
|
description: "When true and artifact_type is 'orchestrator-state', require all phases to be complete."
|
|
16211
|
+
},
|
|
16212
|
+
require_model_routing: {
|
|
16213
|
+
type: "boolean",
|
|
16214
|
+
description: "When true and artifact_type is 'orchestrator-state', require a model_routing_receipts entry per delegated agent once a delegation is recorded. The TypeScript side performs the existence check only; the Python validator is authoritative for full per-receipt correctness."
|
|
16211
16215
|
}
|
|
16212
16216
|
},
|
|
16213
16217
|
required: ["artifact_type", "artifact_path"],
|
|
@@ -16597,6 +16601,7 @@ function resolveValidateOrchestrationArtifactsToolInput(rawInput, fallbackWorksp
|
|
|
16597
16601
|
);
|
|
16598
16602
|
}
|
|
16599
16603
|
const requireComplete = args["require_complete"];
|
|
16604
|
+
const requireModelRouting = args["require_model_routing"];
|
|
16600
16605
|
return {
|
|
16601
16606
|
workspaceRoot: normalizeWorkspaceRoot(
|
|
16602
16607
|
args["workspace_root"],
|
|
@@ -16604,7 +16609,8 @@ function resolveValidateOrchestrationArtifactsToolInput(rawInput, fallbackWorksp
|
|
|
16604
16609
|
),
|
|
16605
16610
|
artifactType,
|
|
16606
16611
|
artifactPath: normalizeRequiredText(args["artifact_path"], "artifact_path"),
|
|
16607
|
-
...requireComplete === true ? { requireComplete: true } : {}
|
|
16612
|
+
...requireComplete === true ? { requireComplete: true } : {},
|
|
16613
|
+
...requireModelRouting === true ? { requireModelRouting: true } : {}
|
|
16608
16614
|
};
|
|
16609
16615
|
}
|
|
16610
16616
|
|
|
@@ -22519,6 +22525,14 @@ var STEP_STATUS_KEYS = [
|
|
|
22519
22525
|
"step9_status",
|
|
22520
22526
|
"step10_status"
|
|
22521
22527
|
];
|
|
22528
|
+
var DELEGATING_AGENTS = /* @__PURE__ */ new Set([
|
|
22529
|
+
"atomic-planner",
|
|
22530
|
+
"atomic-executor",
|
|
22531
|
+
"feature-review",
|
|
22532
|
+
"task-researcher",
|
|
22533
|
+
"prd-feature",
|
|
22534
|
+
"pr-author"
|
|
22535
|
+
]);
|
|
22522
22536
|
function isObject7(value) {
|
|
22523
22537
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22524
22538
|
}
|
|
@@ -22582,6 +22596,53 @@ function resolveRoutingMatrix(options) {
|
|
|
22582
22596
|
}
|
|
22583
22597
|
return void 0;
|
|
22584
22598
|
}
|
|
22599
|
+
function delegatedAgents(stateMap) {
|
|
22600
|
+
const agents = /* @__PURE__ */ new Set();
|
|
22601
|
+
const receipts = stateMap["delegation_receipts"];
|
|
22602
|
+
if (Array.isArray(receipts)) {
|
|
22603
|
+
receipts.forEach((receipt) => {
|
|
22604
|
+
if (!isObject7(receipt)) {
|
|
22605
|
+
return;
|
|
22606
|
+
}
|
|
22607
|
+
const agentName = receipt["agent_name"];
|
|
22608
|
+
if (typeof agentName === "string" && agentName.trim() !== "") {
|
|
22609
|
+
agents.add(agentName);
|
|
22610
|
+
}
|
|
22611
|
+
});
|
|
22612
|
+
}
|
|
22613
|
+
const nextStep = stateMap["next_step"];
|
|
22614
|
+
if (typeof nextStep === "string" && DELEGATING_AGENTS.has(nextStep)) {
|
|
22615
|
+
agents.add(nextStep);
|
|
22616
|
+
}
|
|
22617
|
+
return agents;
|
|
22618
|
+
}
|
|
22619
|
+
function validateModelRoutingExistence(stateMap) {
|
|
22620
|
+
const errors = [];
|
|
22621
|
+
const delegated = delegatedAgents(stateMap);
|
|
22622
|
+
if (delegated.size === 0) {
|
|
22623
|
+
return errors;
|
|
22624
|
+
}
|
|
22625
|
+
const receiptAgents2 = /* @__PURE__ */ new Set();
|
|
22626
|
+
const receipts = stateMap["model_routing_receipts"];
|
|
22627
|
+
if (Array.isArray(receipts)) {
|
|
22628
|
+
receipts.forEach((receipt) => {
|
|
22629
|
+
if (!isObject7(receipt)) {
|
|
22630
|
+
return;
|
|
22631
|
+
}
|
|
22632
|
+
const agent = receipt["agent"];
|
|
22633
|
+
if (typeof agent === "string" && agent.trim() !== "") {
|
|
22634
|
+
receiptAgents2.add(agent);
|
|
22635
|
+
}
|
|
22636
|
+
});
|
|
22637
|
+
}
|
|
22638
|
+
const missing = [...delegated].filter((a) => !receiptAgents2.has(a)).sort();
|
|
22639
|
+
for (const agent of missing) {
|
|
22640
|
+
errors.push(
|
|
22641
|
+
`Checkpoint model_routing_receipts is missing a receipt for delegated agent: ${agent}.`
|
|
22642
|
+
);
|
|
22643
|
+
}
|
|
22644
|
+
return errors;
|
|
22645
|
+
}
|
|
22585
22646
|
function validateOrchestratorStateText(text, options = {}) {
|
|
22586
22647
|
const errors = [];
|
|
22587
22648
|
let state;
|
|
@@ -22655,6 +22716,9 @@ function validateOrchestratorStateText(text, options = {}) {
|
|
|
22655
22716
|
})
|
|
22656
22717
|
);
|
|
22657
22718
|
}
|
|
22719
|
+
if (options.requireModelRouting === true) {
|
|
22720
|
+
errors.push(...validateModelRoutingExistence(stateMap));
|
|
22721
|
+
}
|
|
22658
22722
|
return errors;
|
|
22659
22723
|
}
|
|
22660
22724
|
|
|
@@ -23029,6 +23093,7 @@ function validateArtifact(input) {
|
|
|
23029
23093
|
case "orchestrator-state": {
|
|
23030
23094
|
const options = {
|
|
23031
23095
|
...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete },
|
|
23096
|
+
...input.requireModelRouting === void 0 ? {} : { requireModelRouting: input.requireModelRouting },
|
|
23032
23097
|
...input.fs === void 0 ? {} : { fs: input.fs },
|
|
23033
23098
|
...input.root === void 0 ? {} : { root: input.root },
|
|
23034
23099
|
...input.routingMatrix === void 0 ? {} : { routingMatrix: input.routingMatrix }
|
|
@@ -23056,6 +23121,7 @@ function validateOrchestrationServiceCall(input) {
|
|
|
23056
23121
|
artifactType: input.artifactType,
|
|
23057
23122
|
text,
|
|
23058
23123
|
...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete },
|
|
23124
|
+
...input.requireModelRouting === void 0 ? {} : { requireModelRouting: input.requireModelRouting },
|
|
23059
23125
|
fs: input.fileSystem,
|
|
23060
23126
|
root: input.workspaceRoot
|
|
23061
23127
|
});
|
|
@@ -23072,6 +23138,18 @@ ${errors.join("\n")}`
|
|
|
23072
23138
|
};
|
|
23073
23139
|
}
|
|
23074
23140
|
|
|
23141
|
+
// ../../extensions/drm-copilot/src/lib/validate/build-validate-orchestration-service-call-input.ts
|
|
23142
|
+
function buildValidateOrchestrationServiceCallInput(fileSystem, input) {
|
|
23143
|
+
return {
|
|
23144
|
+
fileSystem,
|
|
23145
|
+
workspaceRoot: input.workspaceRoot,
|
|
23146
|
+
artifactType: input.artifactType,
|
|
23147
|
+
artifactPath: input.artifactPath,
|
|
23148
|
+
...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete },
|
|
23149
|
+
...input.requireModelRouting === void 0 ? {} : { requireModelRouting: input.requireModelRouting }
|
|
23150
|
+
};
|
|
23151
|
+
}
|
|
23152
|
+
|
|
23075
23153
|
// ../../extensions/drm-copilot/src/lib/new-potential-bug-entry.ts
|
|
23076
23154
|
var fs5 = __toESM(require("node:fs"));
|
|
23077
23155
|
var nodePath4 = __toESM(require("node:path"));
|
|
@@ -27713,13 +27791,9 @@ var DefaultRepoAutomationService = class {
|
|
|
27713
27791
|
});
|
|
27714
27792
|
}
|
|
27715
27793
|
async validateOrchestrationArtifacts(input) {
|
|
27716
|
-
return validateOrchestrationServiceCall(
|
|
27717
|
-
|
|
27718
|
-
|
|
27719
|
-
artifactType: input.artifactType,
|
|
27720
|
-
artifactPath: input.artifactPath,
|
|
27721
|
-
...input.requireComplete === void 0 ? {} : { requireComplete: input.requireComplete }
|
|
27722
|
-
});
|
|
27794
|
+
return validateOrchestrationServiceCall(
|
|
27795
|
+
buildValidateOrchestrationServiceCallInput(this.fileSystem, input)
|
|
27796
|
+
);
|
|
27723
27797
|
}
|
|
27724
27798
|
async executeScript(options) {
|
|
27725
27799
|
const execution = await executeBundledScriptFromExtensionRoot(this.output, {
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: csharp-typed-engineer
|
|
3
|
+
model: sonnet
|
|
3
4
|
description: Project-scoped worker that implements and verifies C# changes within typed repository boundaries. Applies the CSharpier -> .NET Analyzers -> Nullable Analysis -> xUnit toolchain, the 1-3 production-file small-path budget, and zero-regression quality gates.
|
|
4
5
|
tools:
|
|
5
6
|
- Read
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: epic-orchestrator
|
|
3
|
+
model: opus
|
|
3
4
|
description: Deterministic epic-scale orchestrator that schedules a dependency graph of child features across parallel, isolated git worktrees, fans results back into a shared integration branch, and drives the final integration-to-main PR. Distinct from orchestrator; only this agent is authorized to delegate to Agent(orchestrator).
|
|
4
5
|
tools:
|
|
5
6
|
- "Agent(orchestrator)"
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: orchestrator
|
|
3
|
+
model: opus
|
|
3
4
|
description: Deterministic repository orchestrator that estimates change budget, selects small or large workflow path, delegates to specialist subagents, persists checkpoint state, and enforces completion gates proactively.
|
|
4
5
|
tools:
|
|
5
6
|
- "Agent(atomic-planner,atomic-executor,feature-review,task-researcher,prd-feature,staged-review,epic-review,status-updater,pr-author,commit-message,human-exception-runbook,python-typed-engineer,powershell-typed-engineer,csharp-typed-engineer,typescript-engineer)"
|
|
@@ -56,6 +57,18 @@ On every invocation:
|
|
|
56
57
|
4. If a valid checkpoint exists with a matching objective, resume from the recorded `next_step`.
|
|
57
58
|
5. If no checkpoint exists or the objective is new, begin from change-budget estimation.
|
|
58
59
|
|
|
60
|
+
### Model-choice reconciliation on resume
|
|
61
|
+
|
|
62
|
+
When the resumed `next_step` is a delegating step, repair a missing model choice deterministically before delegating (this mirrors `## Checkpoint Handling` in `.claude/skills/orchestrate/SKILL.md`):
|
|
63
|
+
|
|
64
|
+
a. Run the orchestrator-state validator with `--require-model-routing` before the first delegation and record a `model_routing_preflight` block `{ status ("pass"|"fail"), checked_at, validator_command, output_summary }`.
|
|
65
|
+
b. Recompute the upcoming phase's floor with `compute_complexity_floor(signals_present)` (no reimplementation).
|
|
66
|
+
c. Record a `complexity_assessments[]` entry `{ phase, band, floor, signals_present[], rationale, assessed_at }` with `floor` equal to the recomputed value and `band >= floor`.
|
|
67
|
+
d. Resolve the model with `resolve_delegation_model(agent, complexity_band, fable_policy)` and record a `model_routing_receipts[]` entry `{ agent, phase, complexity_band, fable_policy, table_model, clamped_from | null, model }`.
|
|
68
|
+
e. Persist the checkpoint, then delegate with `model` equal to the receipt's `model`.
|
|
69
|
+
|
|
70
|
+
The orchestrator MUST NOT delegate at a delegating `next_step` while `model_routing_preflight` status is `fail`; it repairs the missing choice (steps b-e) and re-preflights until the status is `pass`.
|
|
71
|
+
|
|
59
72
|
## Change Budget Routing
|
|
60
73
|
|
|
61
74
|
The first action is always to estimate the change budget by identifying likely affected production files and tests:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: powershell-typed-engineer
|
|
3
|
+
model: sonnet
|
|
3
4
|
description: Project-scoped worker that implements and verifies PowerShell changes within typed repository boundaries. Applies PoshQC format -> PSScriptAnalyzer -> Pester toolchain, the 1-2 production-file direct-mode budget, the 3-production + 3-test per-batch cap, and zero-regression quality gates.
|
|
4
5
|
tools:
|
|
5
6
|
- Read
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: python-typed-engineer
|
|
3
|
+
model: sonnet
|
|
3
4
|
description: Project-scoped worker that implements and verifies Python changes within typed repository boundaries. Applies the Black -> Ruff -> Pyright -> Pytest toolchain, the 3-production + 3-test per-batch budget, and zero-regression quality gates.
|
|
4
5
|
tools:
|
|
5
6
|
- Read
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Pre-tool-use hook that blocks a delegation to a gated subagent when the
|
|
4
|
+
orchestrator checkpoint records no model-routing receipt for that agent.
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
Invoked by the Claude Code PreToolUse hook on the Agent (Task) tool. Reads
|
|
8
|
+
tool input JSON from the CLAUDE_TOOL_INPUT environment variable and the
|
|
9
|
+
orchestrator checkpoint from artifacts/orchestration/orchestrator-state.json.
|
|
10
|
+
|
|
11
|
+
The hook enforces presence only: it cannot read the delegate's chosen
|
|
12
|
+
`model` (no `model` field is exposed in the tool input), so it verifies that
|
|
13
|
+
a `model_routing_receipts[]` entry already exists for the target
|
|
14
|
+
`subagent_type`. Correctness of the recorded model stays with the
|
|
15
|
+
authoritative Python validator.
|
|
16
|
+
|
|
17
|
+
Gated subagent types are the Agent-tool delegates that participate in model
|
|
18
|
+
selection: atomic-planner, atomic-executor, feature-review, task-researcher,
|
|
19
|
+
prd-feature, pr-author. The `orchestrator` type is deliberately excluded: it
|
|
20
|
+
is the calling agent, not a subagent delegated via the Agent tool, so it is
|
|
21
|
+
never a receipt-gated `subagent_type`.
|
|
22
|
+
|
|
23
|
+
Allow-through (graceful allow) applies to a non-delegating `subagent_type`,
|
|
24
|
+
empty or absent tool input, and malformed tool-input JSON.
|
|
25
|
+
|
|
26
|
+
The checkpoint read goes through Get-ModelRoutingCheckpoint so tests can
|
|
27
|
+
inject a synthetic checkpoint without touching disk.
|
|
28
|
+
|
|
29
|
+
.NOTES
|
|
30
|
+
Compatible with PowerShell 7+. Read-only presence-gating deterrent.
|
|
31
|
+
#>
|
|
32
|
+
[CmdletBinding()]
|
|
33
|
+
param()
|
|
34
|
+
|
|
35
|
+
function Get-ModelRoutingCheckpoint {
|
|
36
|
+
<#
|
|
37
|
+
.SYNOPSIS
|
|
38
|
+
Returns the parsed orchestrator checkpoint object, or $null when the
|
|
39
|
+
file is missing or not valid JSON. Tests mock this seam.
|
|
40
|
+
#>
|
|
41
|
+
[CmdletBinding()]
|
|
42
|
+
[OutputType([object])]
|
|
43
|
+
param(
|
|
44
|
+
[string] $CheckpointPath = 'artifacts/orchestration/orchestrator-state.json'
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
if (-not (Test-Path -LiteralPath $CheckpointPath -PathType Leaf)) {
|
|
48
|
+
return $null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
$raw = Get-Content -LiteralPath $CheckpointPath -Raw -ErrorAction Stop
|
|
53
|
+
return $raw | ConvertFrom-Json -ErrorAction Stop
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return $null
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function Get-ModelRoutingGatedAgent {
|
|
61
|
+
<#
|
|
62
|
+
.SYNOPSIS
|
|
63
|
+
Returns the set of subagent types that are receipt-gated: the Agent-tool
|
|
64
|
+
delegates that participate in model selection. `orchestrator` is
|
|
65
|
+
excluded because it is the caller, not a delegated subagent.
|
|
66
|
+
#>
|
|
67
|
+
[CmdletBinding()]
|
|
68
|
+
[OutputType([string[]])]
|
|
69
|
+
param()
|
|
70
|
+
|
|
71
|
+
return [string[]] @(
|
|
72
|
+
'atomic-planner',
|
|
73
|
+
'atomic-executor',
|
|
74
|
+
'feature-review',
|
|
75
|
+
'task-researcher',
|
|
76
|
+
'prd-feature',
|
|
77
|
+
'pr-author'
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function Test-ModelRoutingReceiptPresent {
|
|
82
|
+
<#
|
|
83
|
+
.SYNOPSIS
|
|
84
|
+
Returns $true when the checkpoint carries a model_routing_receipts entry
|
|
85
|
+
whose agent equals the target subagent type.
|
|
86
|
+
#>
|
|
87
|
+
[CmdletBinding()]
|
|
88
|
+
[OutputType([bool])]
|
|
89
|
+
param(
|
|
90
|
+
[Parameter(Mandatory)]
|
|
91
|
+
[AllowNull()]
|
|
92
|
+
$Checkpoint,
|
|
93
|
+
|
|
94
|
+
[Parameter(Mandatory)]
|
|
95
|
+
[string] $Subagent
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
if ($null -eq $Checkpoint) {
|
|
99
|
+
return $false
|
|
100
|
+
}
|
|
101
|
+
if ($Checkpoint.PSObject.Properties.Name -notcontains 'model_routing_receipts') {
|
|
102
|
+
return $false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# Scan every receipt for one whose agent matches the delegated subagent type.
|
|
106
|
+
foreach ($receipt in @($Checkpoint.model_routing_receipts)) {
|
|
107
|
+
if ($null -eq $receipt) {
|
|
108
|
+
continue
|
|
109
|
+
}
|
|
110
|
+
if ($receipt.PSObject.Properties.Name -contains 'agent' -and
|
|
111
|
+
[string]$receipt.agent -eq $Subagent) {
|
|
112
|
+
return $true
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return $false
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function Invoke-ModelRoutingReceiptDecision {
|
|
119
|
+
<#
|
|
120
|
+
.SYNOPSIS
|
|
121
|
+
Parses CLAUDE_TOOL_INPUT and returns an allow-or-block decision object.
|
|
122
|
+
#>
|
|
123
|
+
[CmdletBinding()]
|
|
124
|
+
[OutputType([System.Collections.Specialized.OrderedDictionary])]
|
|
125
|
+
param(
|
|
126
|
+
[string] $ToolInputRaw
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
$allow = [ordered]@{ hookSpecificOutput = [ordered]@{ hookEventName = 'PreToolUse'; permissionDecision = 'allow' } }
|
|
130
|
+
|
|
131
|
+
# Empty or absent tool input is not a delegation this hook can gate.
|
|
132
|
+
if (-not $ToolInputRaw) {
|
|
133
|
+
return $allow
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# Malformed tool-input JSON is allowed through gracefully; this hook is a
|
|
137
|
+
# deterrent, not the authoritative validator.
|
|
138
|
+
try {
|
|
139
|
+
$toolInput = $ToolInputRaw | ConvertFrom-Json -ErrorAction Stop
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return $allow
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
$subagent = [string]$toolInput.subagent_type
|
|
146
|
+
|
|
147
|
+
# Only the gated Agent-tool delegates are receipt-checked; any other
|
|
148
|
+
# subagent_type (including orchestrator) passes through.
|
|
149
|
+
if (-not $subagent -or ((Get-ModelRoutingGatedAgent) -notcontains $subagent)) {
|
|
150
|
+
return $allow
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
$checkpoint = Get-ModelRoutingCheckpoint
|
|
154
|
+
if (Test-ModelRoutingReceiptPresent -Checkpoint $checkpoint -Subagent $subagent) {
|
|
155
|
+
return $allow
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return [ordered]@{
|
|
159
|
+
hookSpecificOutput = [ordered]@{
|
|
160
|
+
hookEventName = 'PreToolUse'
|
|
161
|
+
permissionDecision = 'deny'
|
|
162
|
+
permissionDecisionReason = "MODEL_ROUTING_RECEIPT_BLOCKED: cannot delegate to '$subagent' before a model_routing_receipts entry for it is recorded in the orchestrator checkpoint. Perform Model Selection (record the complexity assessment and routing receipt) before delegating."
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
# Guard allows dot-sourcing in tests without executing the entrypoint.
|
|
168
|
+
if ($MyInvocation.InvocationName -eq '.') {
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
$decision = Invoke-ModelRoutingReceiptDecision -ToolInputRaw $env:CLAUDE_TOOL_INPUT
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
Write-Error $_
|
|
177
|
+
exit 1
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
$decision | ConvertTo-Json -Compress -Depth 5 | Write-Output
|
|
181
|
+
|
|
182
|
+
exit 0
|
|
@@ -180,7 +180,7 @@ function Invoke-RoutingContractValidation {
|
|
|
180
180
|
[scriptblock] $Invoker = {
|
|
181
181
|
param($Path, $Type)
|
|
182
182
|
$output = & python -m scripts.dev_tools.validate_orchestration_artifacts `
|
|
183
|
-
$Type $Path --require-complete 2>&1
|
|
183
|
+
$Type $Path --require-complete --require-model-routing 2>&1
|
|
184
184
|
[pscustomobject]@{
|
|
185
185
|
ExitCode = $LASTEXITCODE
|
|
186
186
|
Output = ($output | Out-String)
|
|
@@ -293,6 +293,14 @@ function Invoke-OrchestratorOutputValidation {
|
|
|
293
293
|
}
|
|
294
294
|
$routingResult = Invoke-RoutingContractValidation @routingArgs
|
|
295
295
|
if ($routingResult.HasErrors) {
|
|
296
|
+
# One subprocess call now covers both --require-complete and
|
|
297
|
+
# --require-model-routing. Surface a model-routing gate failure under its
|
|
298
|
+
# own block reason (its errors name model_routing_receipts or
|
|
299
|
+
# complexity_assessments); otherwise fall back to the routing-contract
|
|
300
|
+
# block reason for a generic completion/routing failure.
|
|
301
|
+
if ($routingResult.ErrorText -match 'model_routing_receipts|complexity_assessments') {
|
|
302
|
+
return @{ Ok = $false; Message = "MODEL_ROUTING_BLOCKED: $($routingResult.ErrorText)" }
|
|
303
|
+
}
|
|
296
304
|
return @{ Ok = $false; Message = "ROUTING_CONTRACT_BLOCKED: $($routingResult.ErrorText)" }
|
|
297
305
|
}
|
|
298
306
|
|