RUL-026 — delegated work defaults to Sonnet unless a tier is named

SUPERSEDED 2026-10-24 by RUL-042. Delegated work now runs through Hermes on the DeepSeek provider with deepseek-v4-pro. Do not dispatch Sonnet. This page is kept as history.

Ruling

"I wANT IT Automated and the default is sonnet unless you specify" (verbatim, 2026-08-06).

Every agent dispatched to do work runs on Sonnet by default. A stronger tier is a decision, and a decision has to be stated at the call site. What this forbids is not the opus tier — it is the silent tier, where nobody chose and the model was inherited from whatever session happened to author the call.

This is the automation half of the standing split recorded in project memory build-workflow-opus-orchestrates-sonnet-builds: Opus does design/analysis/decisions/review, Sonnet builds. That preference existed and was still violated, because one of the two dispatch paths had no mechanism.

Why it needed a mechanism

The Agent path already honoured it: every build-shaped definition in .claude/agents/ declares model: sonnet (builder, wirer, refactorer, test-writer, auditor), lint-fixer declares haiku, and only reviewer declares opus.

The Workflow path did not. An agent() call that omits model inherits the main-loop model — that is the tool's documented default — so a workflow authored during an Opus session runs every one of its agents on Opus.

Measured 2026-08-05, session 260: two workflow scripts were written with no model on any agent() call. Result: 58 agents, 100% claude-opus-5[1m], 10.4M subagent tokens, for fan-out work (reproduce this flake, write this test, grep this corpus) that is Sonnet-shaped. Nothing warned, because omitting the field is the normal-looking thing to do.

The judgment stages in those same workflows — reading a coredump backtrace, adversarially refuting six flake diagnoses (four died), deciding a stale binary rather than a wrong fix — were correctly Opus. The rule is not "spend less"; it is "choose, and say what you chose."

Enforcement

How it works

PreToolUse on Workflow. Blocks (exit 2) any script with an agent() call that names no model, listing the offending call labels and the remedy. Naming opus passes — the guard forbids the silent inherit, not the tier.

Deliberate allows, so the guard cannot strand real work. A resumeFromRunId replay passes untouched, because it replays cached (prompt, opts) pairs and changing opts busts the cache. A saved workflow invoked by name carries no inline script to grade. Malformed or unreadable input fails open — a hook that errors must not become a hook that blocks all work.

Call-site scanning is paren-balanced rather than regex-lazy: an options object routinely nests braces and parens, and agent\(.*?\) would stop at the first inner close paren and misread a correctly-specified call as unspecified.

It also scans code, not prose. A workflow script talks about agents constantly — in its meta description, in the prompts it sends, in comments — and a scanner that cannot tell the two apart reports calls that do not exist. The hook's own first live invocation proved it: the probe blocked for 2 offenders and one of them was the probe's own detail: 'unreachable agent() call with no model' string. So a character-level mask marks every '/"/backtick string, // line comment and /* */ block comment as non-code before matching. Template literals are handled rather than blanket-masked: ${...} interpolations are code again, so a real unspecified call nested inside one is still caught.

That is the third instance of the same defect class in session 260 — the kimi agent-level gate flagged a work-journal entry that described a violation, and the requirement-id gate re-fired on a comment naming the id it had just rejected. A gate that cannot distinguish an artifact from a description of that artifact will eventually flag its own documentation.

Covered by tests/scripts/test_workflow_default_sonnet.py (39 tests), whose negative corpus is as large as the positive one, and which also pins the .claude/agents/ frontmatter so the Agent path cannot regress to inheriting either.

Choosing the tier

Tier Use for
sonnet (default) build and fan-out: reproduce, extract, write-the-test, apply-the-codemod, grep-the-corpus, mechanical fixes to a settled brief
opus judgment: diagnose, adversarially verify, judge a panel, synthesize, review a diff
haiku shallow high-volume extraction/classification/summarization — never judgment (project memory haiku-4-5-capabilities)

Related: RUL-014 (a ruling must resolve to a mechanism), RUL-023 (a hook that is not registered is not a delivery).