@danmoisan/drm-copilot-mcp 1.0.8 → 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/package.json +1 -1
- package/resources/claude-customizations/.claude/lib/model-routing/ModelRouting.psm1 +209 -0
- package/resources/claude-customizations/.claude/skills/epic-orchestrate/SKILL.md +3 -3
- package/resources/claude-customizations/.claude/skills/orchestrate/SKILL.md +6 -4
- package/resources/claude-customizations/pack-manifests/core.json +2 -1
package/package.json
CHANGED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
<#
|
|
2
|
+
.SYNOPSIS
|
|
3
|
+
Model-routing reference formulas for the orchestrator, ported from the Python references.
|
|
4
|
+
|
|
5
|
+
.DESCRIPTION
|
|
6
|
+
Provides the destination-runtime PowerShell ports of the two self-contained,
|
|
7
|
+
pure model-routing formulas that the `orchestrate` skill instructs the
|
|
8
|
+
orchestrator to run:
|
|
9
|
+
|
|
10
|
+
- Get-ComplexityFloor port of scripts/dev_tools/compute_complexity_floor.py
|
|
11
|
+
- Resolve-DelegationModel port of scripts/dev_tools/resolve_delegation_model.py
|
|
12
|
+
|
|
13
|
+
Both functions are pure and deterministic: they read no file at runtime and
|
|
14
|
+
encode only the fixed band ordering, the base complexity-to-model table, the
|
|
15
|
+
preferred overlay, and the disabled-mode clamp as module-scope constants.
|
|
16
|
+
Those literals are pinned to config/orchestration-routing.json (model_policy /
|
|
17
|
+
model_budget) by a static config-parity Pester test, and the Python modules
|
|
18
|
+
remain the validator's authoritative reference. This module is one half of a
|
|
19
|
+
two-language mirror; it never imports validator logic.
|
|
20
|
+
#>
|
|
21
|
+
|
|
22
|
+
Set-StrictMode -Version Latest
|
|
23
|
+
|
|
24
|
+
# The fixed complexity-band vocabulary, ordered from lowest to highest rigor.
|
|
25
|
+
# The array order defines "higher" and "lower" band comparisons used by the
|
|
26
|
+
# floor computation, mirroring BAND_ORDER in compute_complexity_floor.py.
|
|
27
|
+
$script:BAND_ORDER = @('C1', 'C2', 'C3', 'C4')
|
|
28
|
+
|
|
29
|
+
# The lowest band, returned when no floor signal is present (LOWEST_BAND).
|
|
30
|
+
$script:LOWEST_BAND = 'C1'
|
|
31
|
+
|
|
32
|
+
# Every present floor signal contributes this uniform candidate band, per the
|
|
33
|
+
# model_policy.complexity contract (each [floor] signal contributes C3).
|
|
34
|
+
$script:FLOOR_CANDIDATE_BAND = 'C3'
|
|
35
|
+
|
|
36
|
+
# Floors never exceed this ceiling; C4 is judgment-only and never floor-forced,
|
|
37
|
+
# so the computed floor is clamped to at most C3 (FLOOR_CEILING_BAND).
|
|
38
|
+
$script:FLOOR_CEILING_BAND = 'C3'
|
|
39
|
+
|
|
40
|
+
# The three session-level fable policies (model_budget.fable_policy).
|
|
41
|
+
$script:DISABLED_POLICY = 'disabled'
|
|
42
|
+
$script:PREFERRED_POLICY = 'preferred'
|
|
43
|
+
|
|
44
|
+
# The model tier removed from consideration under the disabled policy, the tier
|
|
45
|
+
# a disabled-mode fable cell clamps down to, and the recorded clamp reason.
|
|
46
|
+
$script:FABLE_MODEL = 'fable'
|
|
47
|
+
$script:DISABLED_CLAMP_MODEL = 'opus'
|
|
48
|
+
$script:DISABLED_CLAMP_REASON = 'fable_disabled'
|
|
49
|
+
|
|
50
|
+
# The base complexity-to-model table applied uniformly across delegated agents
|
|
51
|
+
# (BASE_COMPLEXITY_TO_MODEL). Pinned to model_policy.complexity_to_model.
|
|
52
|
+
$script:BASE_COMPLEXITY_TO_MODEL = @{
|
|
53
|
+
C1 = 'haiku'
|
|
54
|
+
C2 = 'sonnet'
|
|
55
|
+
C3 = 'opus'
|
|
56
|
+
C4 = 'fable'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# The agents whose C3 cell the preferred overlay redirects to fable. No other
|
|
60
|
+
# agent and no other band is affected (PREFERRED_OVERLAY_AGENTS).
|
|
61
|
+
$script:PREFERRED_OVERLAY_AGENTS = @(
|
|
62
|
+
'atomic-planner',
|
|
63
|
+
'prd-feature',
|
|
64
|
+
'feature-review',
|
|
65
|
+
'task-researcher'
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# The single band and target model the preferred overlay applies.
|
|
69
|
+
$script:PREFERRED_OVERLAY_BAND = 'C3'
|
|
70
|
+
$script:PREFERRED_OVERLAY_MODEL = 'fable'
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
function Get-ComplexityFloor {
|
|
74
|
+
<#
|
|
75
|
+
.SYNOPSIS
|
|
76
|
+
Compute the deterministic complexity-band floor from present floor signals.
|
|
77
|
+
|
|
78
|
+
.DESCRIPTION
|
|
79
|
+
Faithful PowerShell port of compute_complexity_floor
|
|
80
|
+
(scripts/dev_tools/compute_complexity_floor.py). Returns the deterministic
|
|
81
|
+
lower-bound complexity band implied by the set of present floor signals:
|
|
82
|
+
each present floor signal contributes a candidate band of C3, the floor is
|
|
83
|
+
the maximum triggered candidate band, and the floor never exceeds C3
|
|
84
|
+
(C4 is never floor-forced). With no floor signal present the floor is the
|
|
85
|
+
lowest band C1. The function is pure: it reads no file and does not mutate
|
|
86
|
+
its input, and the result is independent of input ordering.
|
|
87
|
+
|
|
88
|
+
.PARAMETER SignalsPresent
|
|
89
|
+
The names of the present signals flagged [floor] in the
|
|
90
|
+
model_policy.complexity catalog. Every element is treated as a triggered
|
|
91
|
+
floor signal contributing the candidate band C3. An empty collection means
|
|
92
|
+
no floor signal is present.
|
|
93
|
+
|
|
94
|
+
.OUTPUTS
|
|
95
|
+
System.String. The floor band: C1 when no floor signal is present,
|
|
96
|
+
otherwise the maximum triggered candidate band clamped to at most C3.
|
|
97
|
+
C4 is never returned.
|
|
98
|
+
#>
|
|
99
|
+
[CmdletBinding()]
|
|
100
|
+
[OutputType([string])]
|
|
101
|
+
param(
|
|
102
|
+
[Parameter(Mandatory = $true)]
|
|
103
|
+
[AllowEmptyCollection()]
|
|
104
|
+
[string[]] $SignalsPresent
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# With no present floor signal there is no candidate band to raise the floor
|
|
108
|
+
# above the lowest band, so the floor is C1 (mirrors the empty-input guard).
|
|
109
|
+
if (-not $SignalsPresent -or $SignalsPresent.Count -eq 0) {
|
|
110
|
+
return $script:LOWEST_BAND
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# Each present floor signal contributes the uniform candidate band; the floor
|
|
114
|
+
# is the maximum triggered candidate rank across all of them. Because every
|
|
115
|
+
# signal contributes the same candidate band, the max equals that rank.
|
|
116
|
+
$candidateRank = $script:BAND_ORDER.IndexOf($script:FLOOR_CANDIDATE_BAND)
|
|
117
|
+
$highestRank = $candidateRank
|
|
118
|
+
|
|
119
|
+
# Clamp with the ceiling rank so the floor can never exceed C3; this is what
|
|
120
|
+
# keeps C4 from ever being floor-forced regardless of how many signals exist.
|
|
121
|
+
$ceilingRank = $script:BAND_ORDER.IndexOf($script:FLOOR_CEILING_BAND)
|
|
122
|
+
$floorRank = [Math]::Min($highestRank, $ceilingRank)
|
|
123
|
+
return $script:BAND_ORDER[$floorRank]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function Resolve-DelegationModel {
|
|
127
|
+
<#
|
|
128
|
+
.SYNOPSIS
|
|
129
|
+
Resolve the delegation model tier for an agent, band, and fable policy.
|
|
130
|
+
|
|
131
|
+
.DESCRIPTION
|
|
132
|
+
Faithful PowerShell port of resolve_delegation_model
|
|
133
|
+
(scripts/dev_tools/resolve_delegation_model.py). Applies the model_policy
|
|
134
|
+
selection formula to a single delegation: it computes the pre-clamp
|
|
135
|
+
table_model (the base complexity_to_model table plus any preferred overlay)
|
|
136
|
+
and the post-clamp model, recording the clamp provenance. The preferred
|
|
137
|
+
overlay redirects only the C3 cell to fable and only for the four overlay
|
|
138
|
+
agents; atomic-executor and pr-author C3 cells stay opus under every
|
|
139
|
+
policy. Under the disabled policy a fable table cell clamps to opus with
|
|
140
|
+
clamped_from = fable and clamp_reason = fable_disabled. The function is
|
|
141
|
+
pure: it reads no file and mutates no input.
|
|
142
|
+
|
|
143
|
+
.PARAMETER Agent
|
|
144
|
+
The target delegate agent name (for example atomic-planner). Only
|
|
145
|
+
participates in preferred-overlay eligibility.
|
|
146
|
+
|
|
147
|
+
.PARAMETER Band
|
|
148
|
+
The assessed complexity band, one of C1..C4. Used as the key into the
|
|
149
|
+
base complexity_to_model table. A band outside the table is the PowerShell
|
|
150
|
+
analog of the Python KeyError and causes a terminating error (throw).
|
|
151
|
+
|
|
152
|
+
.PARAMETER FablePolicy
|
|
153
|
+
The session fable policy, one of disabled, available, or preferred.
|
|
154
|
+
|
|
155
|
+
.OUTPUTS
|
|
156
|
+
System.Collections.Hashtable. A hashtable with keys table_model (the
|
|
157
|
+
pre-clamp table lookup, including any overlay), model (the post-clamp
|
|
158
|
+
result), clamped_from (fable when a clamp occurred, else $null), and
|
|
159
|
+
clamp_reason (fable_disabled when a clamp occurred, else $null).
|
|
160
|
+
#>
|
|
161
|
+
[CmdletBinding()]
|
|
162
|
+
[OutputType([hashtable])]
|
|
163
|
+
param(
|
|
164
|
+
[Parameter(Mandatory = $true)]
|
|
165
|
+
[string] $Agent,
|
|
166
|
+
[Parameter(Mandatory = $true)]
|
|
167
|
+
[string] $Band,
|
|
168
|
+
[Parameter(Mandatory = $true)]
|
|
169
|
+
[string] $FablePolicy
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# The preferred overlay redirects only the C3 cell to fable, and only for the
|
|
173
|
+
# overlay agents; every other case reads the base table unchanged. The three
|
|
174
|
+
# conditions (policy, agent membership, band) must all hold for the overlay.
|
|
175
|
+
if ($FablePolicy -eq $script:PREFERRED_POLICY -and
|
|
176
|
+
$script:PREFERRED_OVERLAY_AGENTS -contains $Agent -and
|
|
177
|
+
$Band -eq $script:PREFERRED_OVERLAY_BAND) {
|
|
178
|
+
$tableModel = $script:PREFERRED_OVERLAY_MODEL
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
# A band outside the base table is the PowerShell analog of the Python
|
|
182
|
+
# KeyError: fail fast rather than return a silently wrong value.
|
|
183
|
+
if (-not $script:BASE_COMPLEXITY_TO_MODEL.ContainsKey($Band)) {
|
|
184
|
+
throw "Unknown complexity band '$Band'; expected one of $($script:BAND_ORDER -join ', ')."
|
|
185
|
+
}
|
|
186
|
+
$tableModel = $script:BASE_COMPLEXITY_TO_MODEL[$Band]
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# Under the disabled policy, fable is removed from consideration: a fable
|
|
190
|
+
# table cell clamps down to opus and records the clamp provenance.
|
|
191
|
+
if ($FablePolicy -eq $script:DISABLED_POLICY -and $tableModel -eq $script:FABLE_MODEL) {
|
|
192
|
+
return @{
|
|
193
|
+
table_model = $tableModel
|
|
194
|
+
model = $script:DISABLED_CLAMP_MODEL
|
|
195
|
+
clamped_from = $script:FABLE_MODEL
|
|
196
|
+
clamp_reason = $script:DISABLED_CLAMP_REASON
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# No clamp applies: the resolved model is the table model verbatim.
|
|
201
|
+
return @{
|
|
202
|
+
table_model = $tableModel
|
|
203
|
+
model = $tableModel
|
|
204
|
+
clamped_from = $null
|
|
205
|
+
clamp_reason = $null
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
Export-ModuleMember -Function Get-ComplexityFloor, Resolve-DelegationModel
|
|
@@ -115,9 +115,9 @@ The child's own `orchestrator` reads this line and applies the two-axis model-se
|
|
|
115
115
|
documented in `.claude/skills/orchestrate/SKILL.md` (`## Model Selection`): it assesses a
|
|
116
116
|
judgment-based `complexity_band`, records `complexity_assessments[]` and `model_routing_receipts[]`,
|
|
117
117
|
and resolves each delegation's model tier under the given `fable_policy`. The two canonical, tested
|
|
118
|
-
reference implementations are
|
|
119
|
-
(`
|
|
120
|
-
(`
|
|
118
|
+
reference implementations are `.claude/lib/model-routing/ModelRouting.psm1`
|
|
119
|
+
(`Get-ComplexityFloor`) and `.claude/lib/model-routing/ModelRouting.psm1`
|
|
120
|
+
(`Resolve-DelegationModel`). Default `fable_policy` is `disabled` when the marker is absent.
|
|
121
121
|
|
|
122
122
|
`route` is never an input to model selection; `route` remains file-count driven and governs only
|
|
123
123
|
agents, skills, and MCP tools. A skill whose frontmatter `context` field holds the value `fork`
|
|
@@ -29,9 +29,9 @@ On every invocation, the main session must:
|
|
|
29
29
|
Because model selection is required once delegation occurs (see `## Model Selection`), a resuming orchestrator must repair a missing model choice deterministically before delegating at a delegating `next_step`. When the resumed `next_step` is a delegating step:
|
|
30
30
|
|
|
31
31
|
a. **Preflight the checkpoint.** Run the orchestrator-state validator with `--require-model-routing` (via `mcp__drm-copilot__validate_orchestration_artifacts` or the local CLI) against `artifacts/orchestration/orchestrator-state.json` before the first delegation. Record the result in a `model_routing_preflight` block `{ status ("pass"|"fail"), checked_at (ISO-8601 UTC), validator_command, output_summary }`.
|
|
32
|
-
b. **Recompute the floor.** For the upcoming phase, recompute the complexity floor with `
|
|
32
|
+
b. **Recompute the floor.** For the upcoming phase, recompute the complexity floor with `Get-ComplexityFloor -SignalsPresent <names>` (`.claude/lib/model-routing/ModelRouting.psm1`); do not reimplement the formula.
|
|
33
33
|
c. **Record the assessment.** Write a `complexity_assessments[]` entry `{ phase, band, floor, signals_present[], rationale, assessed_at }` with `floor` equal to the recomputed value and `band >= floor`.
|
|
34
|
-
d. **Resolve and record the receipt.** Resolve the model with `
|
|
34
|
+
d. **Resolve and record the receipt.** Resolve the model with `Resolve-DelegationModel -Agent <agent> -Band <complexity_band> -FablePolicy <fable_policy>` (`.claude/lib/model-routing/ModelRouting.psm1`) and write a `model_routing_receipts[]` entry `{ agent, phase, complexity_band, fable_policy, table_model, clamped_from | null, model }`.
|
|
35
35
|
e. **Persist and delegate.** Persist the checkpoint, then delegate with `model` equal to the receipt's `model`.
|
|
36
36
|
|
|
37
37
|
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`.
|
|
@@ -83,8 +83,10 @@ Model selection is a second axis, strictly separate from `route`. `route` (`smal
|
|
|
83
83
|
|
|
84
84
|
The two canonical, tested reference implementations express the formulas the orchestrator applies by judgment:
|
|
85
85
|
|
|
86
|
-
-
|
|
87
|
-
-
|
|
86
|
+
- `.claude/lib/model-routing/ModelRouting.psm1` (`Get-ComplexityFloor`) — the deterministic complexity-floor formula. Each present `[floor]` signal contributes a candidate band of `C3`; the floor is the maximum triggered candidate band; the floor never exceeds `C3`. C4 is never floor-forced; it is reached only by judgment.
|
|
87
|
+
- `.claude/lib/model-routing/ModelRouting.psm1` (`Resolve-DelegationModel`) — the delegation-model selection formula (base `complexity_to_model` table, the `preferred` overlay, and the `disabled` clamp).
|
|
88
|
+
|
|
89
|
+
The runnable reference the destination runtime applies is the `.claude`-resident PowerShell module above; the repository validator remains the Python authority (`scripts/dev_tools/compute_complexity_floor.py` and `scripts/dev_tools/resolve_delegation_model.py`), pinned to the same `config/orchestration-routing.json` truth table by a static config-parity test.
|
|
88
90
|
|
|
89
91
|
End-to-end procedure:
|
|
90
92
|
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
".claude/skills/review-staged/SKILL.md",
|
|
70
70
|
".claude/skills/skill-canonical-location-audit/SKILL.md",
|
|
71
71
|
".claude/skills/translate-copilot-to-claude/SKILL.md",
|
|
72
|
-
".claude/skills/update-status/SKILL.md"
|
|
72
|
+
".claude/skills/update-status/SKILL.md",
|
|
73
|
+
".claude/lib/model-routing/ModelRouting.psm1"
|
|
73
74
|
]
|
|
74
75
|
}
|