feat(model-router): wave 2-B — orchestrators declare phases, shifters and pins removed, frontmatter = off-state floor

The 15 Skill(effort-*) citers now call mcp__model-router__route per phase
(orchestrate at a dispatch span, reflect/plan for the skill's own level,
apply at the bookkeeping tail, escalate at the verify-secure caps); built-in
judgment dispatches carry an explicit effort= param. lib/effort-shift.md is
the route doctrine, lib/model-gate.md the mod rule (route answer = witness,
/route on as remedy). Deleted: skills/effort-*, lib/effort-pins.txt/.sh,
lib/model-check.sh, their tests, the installers' re-apply blocks. The mod
drops its Skill(effort-*) bridge. The tracked model:/effort: frontmatter
stays as the off-state floor, census-locked equal to the rows
(lib/tests/effort-routing.test.sh rewritten, 140 checks; analyzer → xhigh).

Contract .claude/tasks/contracts/2026-10-10-model-router-w2b-1045.md, plan
r4 § W2-B: GATE 0 MET, verifier ECARTS(7) then CONFORME 10/10, security
PASS, full make test green (design-tool-gate env red only).
This commit is contained in:
bchanot
2026-10-10 11:24:54 +02:00
parent 65dff0e768
commit 1f2d33b7a6
43 changed files with 318 additions and 811 deletions
-26
View File
@@ -59,24 +59,6 @@ const spawnInput = (model?: string) => ({
...(model === undefined ? {} : { model }),
})
test('Skill(effort-low) is answered without next, route shows low', async (
$, on) => {
let reached = false
on('tool.call', { tool: 'Skill' }, () => {
reached = true
return { result: { success: true, commandName: 'bottom' } }
})
await boot($, on)
const out = await $.tool.call({ tool: 'Skill', skill: 'effort-low' })
expect(out).toMatchObject({
result: { success: true, commandName: 'effort-low' },
})
expect(reached).toBe(false)
const line = mainLine(await route($, 'show'))
expect(line).toContain('skill effort-low')
expect(line).toContain('effort low')
})
test('route tool with phase orchestrate sets medium on main', async (
$, on) => {
await boot($, on)
@@ -987,14 +969,6 @@ test('run slot: route(clear) clears the turn and names the run', async (
expect(JSON.stringify(out)).toContain('run reflect still holds')
})
test('Skill(effort-low) bridge is not sticky', async ($, on) => {
await bootRun($, on)
await loadSkill($, 'effort-low')
expect(mainLine(await route($, 'show'))).toContain('effort low')
await endTurn($)
expect(mainLine(await route($, 'show'))).toContain('session defaults')
})
test('route answer names the id even with a floor in force', async (
$, on) => {
await bootRun($, on)
+2 -33
View File
@@ -48,8 +48,8 @@ type State = {
rules: Rule[]
source: string // 'defaults' or the override path
userMain: Routed | null // /route by the user, sticky until /route clear
turnMain: Routed | null // model route tool, skill table row, Skill(effort-*)
// bridge, prompt default rule, derived orchestrate; dropped at turn end
turnMain: Routed | null // model route tool, skill table row, prompt
// default rule, derived orchestrate; dropped at turn end
runMain: Routed | null // best-tier skill row: spans the turns of a run;
// only a skill, /route clear|off or a user /model write or drop it
turnFloor: Routed | null // user-explicit level for this turn (prompt rule):
@@ -95,7 +95,6 @@ type Decision = { effort: Effort; by: EffortBy }
const LEVELS: readonly Level[] = ['low', 'medium', 'high', 'xhigh', 'max']
const MODEL_ID = /^claude-[a-z0-9.-]+$/
const TOOL = 'mcp__model-router__route'
const EFFORT_SKILL = /^effort-(low|medium|high|xhigh|max)$/
const BEST = 'best' // the tier whose skill rows hold for a whole run
// Origins that are a person typing: only these arm a typed slash.
const TYPED_ORIGINS: ReadonlySet<string> = new Set([
@@ -1378,34 +1377,6 @@ async function handleRouteTool($: Api, st: State, e: RouteInput) {
// ---- skills ----------------------------------------------------------
const skillResult = (skill: string, line: string) => ({
result: { success: true, commandName: skill, status: 'inline' as const },
context: [line],
})
/** Answers Skill(effort-<l>) in place: one writer, the skill never loads. */
function effortBridge(st: State, agentId: string | undefined, skill: string,
level: Level) {
if (agentId === undefined) {
const route = { ...st.turnMain?.route, effort: level }
st.turnMain = { phase: skill, route, source: 'skill' }
const note = mainNote(st, level)
return skillResult(skill, note
? `model-router: ${skill} recorded, but ${note}; the ${skill} skill ` +
'text was not loaded.'
: `model-router: effort → ${level} for this loop from the next ` +
`request on; the ${skill} skill text was not loaded.`)
}
const loop = loopOf(st, agentId)
if (loop.explicitEffort) {
return skillResult(skill, 'model-router: this agent was dispatched with ' +
'an explicit effort; the shift does not apply.')
}
loop.effort = level
return skillResult(skill, `model-router: effort → ${level} for this ` +
`loop from the next request on; the ${skill} skill text was not loaded.`)
}
/** A skill's table row: its phase and that phase's route, if both exist. */
function skillRow(st: State, skill: string): Picked | undefined {
const phase = hasKey(st.cfg.skills, skill) ? st.cfg.skills[skill] : undefined
@@ -1793,8 +1764,6 @@ function registerSkills(on: On, st: State): void {
on('tool.call', { tool: 'Skill' }, async ($, e, next) => {
const skill = typeof e.skill === 'string' ? e.skill : undefined
if (st.off || skill === undefined) return next(e)
const level = EFFORT_SKILL.exec(skill)?.[1]
if (isLevel(level)) return effortBridge(st, e.agentId, skill, level)
st.skillCalls += 1
try {
safely(st, $, 'Skill', () => onSkillLoad(st, skill, e.agentId))