chore(tasks): model-router contract criteria 7-11, TODO hardening done + residuals, journal
This commit is contained in:
@@ -29,7 +29,7 @@ Q: legacy `Skill(effort-*)` / A: answered by the mod without loading the skill (
|
||||
3. `claude plugin test mods/model-router` passes; the suite covers: (a) `Skill(effort-low)` via `$.tool.call` is answered without `next` in the Skill tool's output shape (`success`, `commandName`) and the route shows `low` on main; (b) the `route` tool with `phase: "orchestrate"` sets `medium` on main and `/route show` prints it; (c) `/route clear` drops it; (d) `/route bogus` returns an error text naming the phases; (e) a `prompt.submit` text holding `ultrathink` sets `escalate` on main; (f) `/route model=sonnet` shows `claude-sonnet-5-5`, a full id passes through, a misspelt alias is refused; (g) `agent.spawn` of `Explore` without a model param reaches the bottom with `model === 'claude-sonnet-5-5'`, and with `model: 'opus'` given the param is untouched.
|
||||
CHECK: cd mods/model-router && out=$(claude plugin test . 2>&1); rc=$?; echo "$out" | tail -n 5; [ $rc -eq 0 ] && [ "$(grep -cE '^\s*test\(' hooks/register.test.ts)" -ge 7 ] && echo PLUGIN-TEST-OK
|
||||
EXPECT: PLUGIN-TEST-OK
|
||||
EVIDENCE: MET exit=0 marker-found :: (pass) a tabled agent steps at its table effort [19.75ms] 11 pass 0 fail Ran 11 tests across 1 file. [0.47s] PLUGIN-TEST-OK
|
||||
EVIDENCE: MET exit=0 marker-found :: (pass) a rule only scans the first 4096 chars of a prompt [27.16ms] 14 pass 0 fail Ran 14 tests across 1 file. [0.60s] PLUGIN-TEST-OK
|
||||
4. Hooks present, as `claude plugin validate` lists them: `session.start`, `command.run{command=route}`, `tool.call{tool=mcp__model-router__route}`, `tool.call{tool=Skill}`, `tool.call{tool=Agent}`, `skill.prompt`, `agent.spawn`, `turn.step`, `prompt.submit`, `turn.complete`, `ui.render{component=Spinner}`; every gating hook carries a fail-open `.catch` (validate prints no "gating hook without .catch").
|
||||
CHECK: cd mods/model-router && out=$(claude plugin validate . 2>&1) && for h in session.start 'command.run{command=route}' 'tool.call{tool=mcp__model-router__route}' 'tool.call{tool=Skill}' 'tool.call{tool=Agent}' skill.prompt agent.spawn turn.step prompt.submit turn.complete 'ui.render{component=Spinner}'; do echo "$out" | grep -qF -- "$h" || { echo "missing $h"; exit 1; }; done && ! echo "$out" | grep -q 'without .catch' && echo HOOKS-OK
|
||||
EXPECT: HOOKS-OK
|
||||
@@ -43,23 +43,23 @@ Q (r2): `scope: agents` / `/route agents` / A: dropped (challenge r1, no require
|
||||
Q (r2): agents table in wave 1 / A: built-ins only (Explore, Plan), matched for the engine provider; repo agents keep their frontmatter as the single writer until wave 2. [orchestrator — single source of truth]
|
||||
Q (r2): `/route off` / A: added as the session kill switch (every hook passes through). [orchestrator — robustness]
|
||||
|
||||
## HARDENING ROUND (security gate 2026-10-08, user go "oui durcis") — criteria 7-11
|
||||
Hardening round (security gate 2026-10-08, user go "oui durcis") — criteria 7-11, same ledger:
|
||||
7. `/route` is user-only: `command.run` answers `{ text: 'route: user-only command' }` without acting when `e.origin.kind !== 'composer'`; a test proves it (origin `{ kind: 'plugin', name: 'x' }` or the kit's non-composer origin → text contains `user-only`, state unchanged).
|
||||
CHECK: cd mods/model-router && grep -q "origin.kind" hooks/register.ts && grep -q "user-only" hooks/register.ts && grep -q "user-only" hooks/register.test.ts && echo ORIGIN-OK
|
||||
EXPECT: ORIGIN-OK
|
||||
EVIDENCE: pending
|
||||
EVIDENCE: MET exit=0 marker-found :: ORIGIN-OK
|
||||
8. An in-agent `route` call or skill table entry never changes that agent's MODEL: the `Loop` type has no `model` axis and no `explicitModel` flag, `turn.step` on an agent loop rewrites `effort` only, the route tool's description says "effort only; the model of a sub-agent is fixed at spawn". A test proves it: a route tool call carrying `agentId: 'a1'` with `phase: 'judge'` followed by a `turn.step` for `a1` leaves `model` as given and sets `effort` to `xhigh`.
|
||||
CHECK: cd mods/model-router && ! grep -qE "loop\.model|explicitModel|spawnModel" hooks/register.ts && grep -q "fixed at spawn" hooks/register.ts && echo NO-AGENT-MODEL-OK
|
||||
EXPECT: NO-AGENT-MODEL-OK
|
||||
EVIDENCE: pending
|
||||
EVIDENCE: MET exit=0 marker-found :: NO-AGENT-MODEL-OK
|
||||
9. Config hardening: a prompt rule pattern longer than 200 chars is dropped (logged); `re.test` runs on at most the first 4096 chars of the prompt; phase keys must match `^[a-z][a-z0-9_-]{0,31}$` (others dropped, logged); the override file is refused above 65536 bytes (logged, defaults kept); the route tool schema carries `additionalProperties: false`; the Skill hook checks `typeof e.skill === 'string'`.
|
||||
CHECK: cd mods/model-router && grep -q "additionalProperties: false" hooks/register.ts && grep -qE "\[a-z\]\[a-z0-9_-\]\{0,31\}" hooks/register.ts && grep -qE "4096|4_096" hooks/register.ts && grep -qE "65536|65_536|64 \* 1024" hooks/register.ts && grep -qE "200" hooks/register.ts && grep -q "typeof e.skill === 'string'" hooks/register.ts && echo CONFIG-HARDEN-OK
|
||||
EXPECT: CONFIG-HARDEN-OK
|
||||
EVIDENCE: pending
|
||||
EVIDENCE: MET exit=0 marker-found :: CONFIG-HARDEN-OK
|
||||
10. Visible fail-open: every `.catch` logs once per session per hook (`$.ui.log('model-router: <hook> failed (<kind>): routing skipped for this event')`, a `warned: Set<string>` in the state) before passing through or answering; the three silent config drops (non-object top level, wrong-typed table, non-array `prompt`) log a line.
|
||||
CHECK: cd mods/model-router && [ "$(grep -c '\.catch(' hooks/register.ts)" -ge 12 ] && grep -q "warned" hooks/register.ts && grep -q "routing skipped" hooks/register.ts && echo CATCH-LOG-OK
|
||||
EXPECT: CATCH-LOG-OK
|
||||
EVIDENCE: pending
|
||||
EVIDENCE: MET exit=0 marker-found :: CATCH-LOG-OK
|
||||
11. Post-`next` bookkeeping (loop tracking and logs after `await next(...)` in `agent.spawn` and the Skill hook) runs inside its own try/catch so a logging failure can never make the `.catch` re-run `next`. Judged by reading, with criteria 1-6 still MET (validate, tsc, tests ≥ 13, style, AC6 minus the removed model axis).
|
||||
|
||||
## FILE SCOPE
|
||||
|
||||
Reference in New Issue
Block a user