diff --git a/.claude/agents/trinity.md b/.claude/agents/trinity.md index d6a4c99456..7502e45064 100644 --- a/.claude/agents/trinity.md +++ b/.claude/agents/trinity.md @@ -1,5 +1,5 @@ --- -model: opus +model: sonnet description: Trinity Agent (Queen) - Orchestrates multi-agent coordination, executes AEL v2.0 color: "#ef4444" orchestrator: true diff --git a/.claude/skills/model-routing/SKILL.md b/.claude/skills/model-routing/SKILL.md index d05f475df1..ed082e760e 100644 --- a/.claude/skills/model-routing/SKILL.md +++ b/.claude/skills/model-routing/SKILL.md @@ -1,11 +1,11 @@ --- id: model-routing name: model-routing -description: Which Claude model does which job -- the orchestrator runs on Opus 5.5, every sub-task it delegates runs on Sonnet 5.5, and a sub-task escalates to Opus only after two failed attempts or on the owner's word. Load before spawning any subagent, writing a workflow, or picking a model in TRI CLAW or the Queen. +description: Which Claude model does which job -- every agent runs on Sonnet 5.5 by default, the orchestrator included, and any job escalates to Opus 5.5 only after two failed attempts or on the owner's word. Load before spawning any subagent, writing a workflow, or picking a model in TRI CLAW or the Queen. argument-hint: "[role: orchestrate|plan|review-final|implement|port|search|test|docs|fix]" --- -# /model-routing -- Opus orchestrates, Sonnet does the sub-tasks +# /model-routing -- Sonnet by default for every agent, Opus only on evidence > **Only t27: everything is written in `.t27`.** (Owner hard rule 2026-10-05; budget #7371; restated in every skill per #7588.) @@ -16,7 +16,7 @@ argument-hint: "[role: orchestrate|plan|review-final|implement|port|search|test| > - What gen cannot express yet is a compiler defect. File it on #5980 with a minimal `.t27` repro instead of writing the code by hand. > - This rule overrides any step below that says to write a script or code in another language. The full text is in `AGENTS.md`, "Only t27". -Owner directive 2026-10-18: orchestration through Opus 5.5, sub-tasks through Sonnet 5.5, because it is cheaper; the rule is part of TRI CLAW so development stays economical. +Owner directive 2026-10-19 (translated): "set Sonnet as the default model for all agents -- it is cheaper with the same result" (#10921). It replaces the 2026-10-18 rule (orchestration on Opus 5.5, sub-tasks on Sonnet 5.5, #10493). The rule is part of TRI CLAW so development stays economical. **The law is `specs/claw/model_route.t27`.** This skill is how to follow it in a Claude Code session. If the two disagree, the spec wins; fix the skill. @@ -24,24 +24,24 @@ Owner directive 2026-10-18: orchestration through Opus 5.5, sub-tasks through So | Role | Kind | Model | |---|---|---| -| orchestrate, plan, review-final | orchestration | **Opus 5.5** (`opus`, `claude-opus-5-5`) | +| orchestrate, plan, review-final | orchestration | **Sonnet 5.5** (`sonnet`, `claude-sonnet-5-5`) | | implement, port, search, test, docs, fix | sub-task | **Sonnet 5.5** (`sonnet`, `claude-sonnet-5-5`) | -- A sub-task goes to Opus only on evidence: **2 failed Sonnet attempts on the same task** (`ESCALATE_AFTER`), or the owner says so. Nothing else raises a tier. -- Orchestration is never routed down. Splitting the work, deciding what is true, and saying merge or stop stay on Opus. +- Any job goes to Opus 5.5 (`opus`, `claude-opus-5-5`) only on evidence: **2 failed Sonnet attempts on the same task** (`ESCALATE_AFTER`), or the owner says so. Nothing else raises a tier. +- Opus without that evidence is waste for every role (`is_waste`), the orchestrator included. - Why: epic #10066 measured Sonnet 5.5 at least as good as Opus 5.5 on ten bounded spec-to-RTL tasks (9 vs 8 direct Verilog, 7 vs 7 in .t27) at about 40% of the cost. ## How it is wired in Claude Code - `.claude/settings.json` sets `CLAUDE_CODE_SUBAGENT_MODEL=claude-sonnet-5-5`, so any subagent with no model of its own runs on Sonnet. -- Every sub-task agent in `.claude/agents/` carries `model: sonnet`; the orchestrator `trinity.md` (Queen) carries `model: opus`. -- The main session is the orchestrator: pick **Opus 5.5** for it in the app's model menu. +- Every agent in `.claude/agents/` carries `model: sonnet`, the orchestrator `trinity.md` (Queen) included. +- The main session is the orchestrator: pick **Sonnet 5.5** for it in the app's model menu; switch it to Opus only for a job that failed twice on Sonnet or on the owner's word. ## When you are the orchestrator -1. Plan on Opus: split the task into sub-tasks with a clear input, output and done-check each. +1. Plan (on Sonnet): split the task into sub-tasks with a clear input, output and done-check each. 2. Spawn each sub-task with the Agent tool. Leave `model` unset (the default is Sonnet) or pass `model: "sonnet"`. Give the subagent everything it needs in the prompt: file paths, the issue, the acceptance check. A sub-task prompt that needs Opus to understand is a planning failure, not a reason to escalate. -3. Verify the result yourself (that is review-final, on Opus): read the diff or the output, run the check. +3. Verify the result yourself (that is review-final): read the diff or the output, run the check. 4. If the same sub-task fails twice on Sonnet, re-spawn it once with `model: "opus"`, and say in the result why it escalated. 5. In a Workflow script, set `model: 'sonnet'` on sub-task `agent()` calls and keep the orchestration logic in the script itself. @@ -49,7 +49,7 @@ Parallelism: at most 3 subagents at once on this 8 GB Mac (swap eats the disk); ## Audit -`is_waste(role, tier, failed_attempts, owner_flag)` in the spec answers whether a run spent Opus on a sub-task without evidence. Use it when TRI CLAW or the Queen records which model ran a job. +`is_waste(role, tier, failed_attempts, owner_flag)` in the spec answers whether a run spent Opus on any role without evidence. Use it when TRI CLAW or the Queen records which model ran a job. ## Not covered yet diff --git a/.trinity/seals/claw_ClawModelRoute.json b/.trinity/seals/claw_ClawModelRoute.json index ffcf8e6f47..ce4c6a6ab4 100644 --- a/.trinity/seals/claw_ClawModelRoute.json +++ b/.trinity/seals/claw_ClawModelRoute.json @@ -1,17 +1,17 @@ { - "built_by": "t27c-bootstrap@0.5.2+9b556b7c3", + "built_by": "t27c-bootstrap@0.5.2+ea35d9b72", "closure": [], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:bc9e7460cb2d53c2b552cc6ea1db3a87a41fea981237fca28e2e92a353d94092", - "gen_hash_rust": "sha256:d31ea1c2bfe1e7403ed63aeef7e8774461e9b122eb328ac75949f2f191ae578f", - "gen_hash_verilog": "sha256:0d8da221bea4f2da090aa8f840641d6d84e83dba26a4ed0df112cb263dd1c18c", - "gen_hash_zig": "sha256:f4fde0bf222b2cc78dd99301ea3c9f798548930830e3b85575fc504a6ce47c05", + "gen_hash_c": "sha256:dd581b1b0902f88cc5985caf5b9a6fffcb96d5e5bb136d361c39d3c907d80f94", + "gen_hash_rust": "sha256:0c02f37c6a0859250c13dcfd61998c5929c1d71c72f8fd46a726a557f99b1e67", + "gen_hash_verilog": "sha256:76412baed107fc229715114f3be64f0a6e1d7496eb563d569b32ded5ab9bf57d", + "gen_hash_zig": "sha256:711d11929e6ad2ad59beab7785ba672f5c7812eff2cc1a0aaa1e7e5ddc133ac6", "module": "ClawModelRoute", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-18T15:12:43Z", + "sealed_at": "2026-10-19T07:01:31Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:f27cfca11fe116628decf5d0a935fc5a72db9a761ff06a85df6301f003bef24e", + "spec_hash": "sha256:4dd19b984389e06576034616ba920eb25171abc0b31fc9bde8cf728d248bd915", "spec_path": "specs/claw/model_route.t27", "tests": { "failed": 0, @@ -22,7 +22,7 @@ "vacuous": 0 }, "toolchain": { - "test_runner": "sha256:1982ae948e24d00c5d221ccec12dfce1eb01204b53e37b0f857f93b16d64629d", + "test_runner": "sha256:8512b80da533b79289110fca4a65f3336abd7a059ca4b72ea595d5e33ff8ad10", "zig": "0.16.0" } } \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 3f2aef9f97..e5a27ce7c0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -122,11 +122,10 @@ This triggers automatic branch creation for the next phase. Load these skills when their functionality matches the task. -### Model routing (owner directive 2026-10-18) +### Model routing (owner directive 2026-10-19, replacing 2026-10-18) -The orchestrating session runs on **Opus 5.5**; every sub-task it delegates (implement, port, search, -test, docs, fix) runs on **Sonnet 5.5**, and escalates to Opus only after 2 failed attempts or on the -owner's word. The law is `specs/claw/model_route.t27`; how to follow it is the `model-routing` skill. +Every agent runs on **Sonnet 5.5** by default, the orchestrating session included; any job escalates to +**Opus 5.5** only after 2 failed attempts on the same task or on the owner's word. The law is `specs/claw/model_route.t27`; how to follow it is the `model-routing` skill. `.claude/settings.json` sets `CLAUDE_CODE_SUBAGENT_MODEL` and each agent in `.claude/agents/` names its model. diff --git a/specs/claw/model_route.t27 b/specs/claw/model_route.t27 index e16931918f..70148dfd96 100644 --- a/specs/claw/model_route.t27 +++ b/specs/claw/model_route.t27 @@ -1,29 +1,33 @@ // SPDX-License-Identifier: Apache-2.0 ; specs/claw/model_route.t27 -- which Claude model does which job in TRI CLAW and in every agent -; session of this project: the orchestrator thinks on Opus 5.5, its sub-tasks run on Sonnet 5.5. -; Owner directive 2026-10-18: "orchestration through Opus 5.5, sub-tasks through Sonnet 5.5, it is -; cheaper; add this rule to TRI CLAW so development stays economical." +; session of this project: every role starts on Sonnet 5.5, the orchestrator included; Opus 5.5 is +; reached only on evidence (failed attempts) or on the owner's word. +; Owner directive 2026-10-19 (translated): "set Sonnet as the default model for all agents -- it is +; cheaper with the same result" (#10921). It replaces the directive of 2026-10-18 (#10493): +; "orchestration through Opus 5.5, sub-tasks through Sonnet 5.5", which kept the orchestration roles +; on Opus as "not a false economy"; the owner has since judged the result the same on Sonnet. ; -- ; WHY. Epic #10066 measured one-shot spec-to-RTL work on the same ten tasks: Sonnet 5.5 passed 9 of ; 10 direct-Verilog tasks against Opus 5.5's 8, and 7 of 10 .t27 tasks against Opus's 7, at about 40% ; of Opus's cost (~$0.41 vs ~$0.99 and ~$0.77 vs ~$1.53 per task, list-price estimates). N = 10, one ; shot: the robust part is that Sonnet is not worse on bounded tasks and is much cheaper. What Opus -; is kept for is the part no sub-task sees: splitting the work, deciding what is true, and saying -; "merge" or "stop". +; was kept for until 2026-10-19 is the part no sub-task sees -- splitting the work, deciding what is +; true, saying "merge" or "stop" -- and that is now the owner's call per task, through owner_flag. ; -- ; THE RULE. ; - A role is either ORCHESTRATION (orchestrate, plan, final review) or a SUB-TASK (implement, -; port, search, test, docs, fix). base_tier() maps the first to Opus and the second to Sonnet. -; - A sub-task escalates to Opus only on evidence: ESCALATE_AFTER failed Sonnet attempts on the same +; port, search, test, docs, fix). The distinction stays for records and reviews, but base_tier() +; maps every role to Sonnet. +; - Any role escalates to Opus only on evidence: ESCALATE_AFTER failed Sonnet attempts on the same ; task, or the owner's explicit flag. route() is that rule; nothing else may raise a tier. -; - An orchestration role is never routed down. A cheaper orchestrator is a false economy: its -; mistakes are paid for by every sub-task it spawns. +; - Opus without that evidence is waste, for an orchestration role as much as for a sub-task. ; - The rule names models by the aliases Claude Code's Agent tool and agent frontmatter accept ; ("opus", "sonnet") and by the full API ids, so a host that is not Claude Code (TRI CLAW's own ; chat agent, the Queen) reads the same table. ; -- ; WHERE IT BINDS. Claude Code: `.claude/settings.json` sets CLAUDE_CODE_SUBAGENT_MODEL to the Sonnet -; id, the sub-task agents in `.claude/agents/` carry `model: sonnet`, and the skill +; id, every agent in `.claude/agents/` (the orchestrator `trinity.md` too) carries `model: sonnet`, +; the main session picks Sonnet 5.5 in the app's model menu, and the skill ; `.claude/skills/model-routing/SKILL.md` tells the orchestrator how to spawn and when to escalate. ; TRI CLAW's app and the Queen read this table when they pick a model (follow-up in their repos). ; ASCII only (L3). phi^2 + 1/phi^2 = 3 | TRINITY @@ -68,15 +72,14 @@ pub fn is_orchestration(role : u32) -> bool { return role <= R_REVIEW_FINAL; } -/// The tier a role starts at. An unknown role is a sub-task: the cheap tier is the default, and an -/// orchestrator that wants Opus names its role. +/// The tier a role starts at: Sonnet for every role, known or not (owner 2026-10-19). pub fn base_tier(role : u32) -> u32 { - if (is_orchestration(role)) { return TIER_OPUS; } + _ = role; return TIER_SONNET; } -/// The tier a job runs at: the base tier, raised to Opus for a sub-task only after ESCALATE_AFTER -/// failed attempts or on the owner's flag. Never lowered. +/// The tier a job runs at: the base tier, raised to Opus for any role only after ESCALATE_AFTER +/// failed attempts on the same task or on the owner's flag. pub fn route(role : u32, failed_attempts : u32, owner_flag : bool) -> u32 { if (base_tier(role) == TIER_OPUS) { return TIER_OPUS; } if (owner_flag) { return TIER_OPUS; } @@ -84,16 +87,18 @@ pub fn route(role : u32, failed_attempts : u32, owner_flag : bool) -> u32 { return TIER_SONNET; } -/// Is a sub-task running on Opus without the evidence route() asks for? The audit question for a -/// session log or a TRI CLAW run record. +/// Is a job running on Opus without the evidence route() asks for? The audit question for a session +/// log or a TRI CLAW run record, for every role. pub fn is_waste(role : u32, tier : u32, failed_attempts : u32, owner_flag : bool) -> bool { return tier == TIER_OPUS and route(role, failed_attempts, owner_flag) == TIER_SONNET; } -test orchestration_roles_run_on_opus { - assert base_tier(R_ORCHESTRATE) == TIER_OPUS; - assert base_tier(R_PLAN) == TIER_OPUS; - assert base_tier(R_REVIEW_FINAL) == TIER_OPUS; +test orchestration_roles_start_on_sonnet { + assert base_tier(R_ORCHESTRATE) == TIER_SONNET; + assert base_tier(R_PLAN) == TIER_SONNET; + assert base_tier(R_REVIEW_FINAL) == TIER_SONNET; + assert is_orchestration(R_REVIEW_FINAL); + assert is_orchestration(R_IMPLEMENT) == false; } test sub_tasks_run_on_sonnet { @@ -118,10 +123,11 @@ test a_sub_task_escalates_only_on_evidence { assert route(R_SEARCH, 0, true) == TIER_OPUS; } -test orchestration_is_never_routed_down { - assert route(R_ORCHESTRATE, 0, false) == TIER_OPUS; - assert route(R_PLAN, 0, false) == TIER_OPUS; - assert route(R_REVIEW_FINAL, 0, false) == TIER_OPUS; +test orchestration_escalates_only_on_evidence_too { + assert route(R_ORCHESTRATE, 0, false) == TIER_SONNET; + assert route(R_PLAN, 1, false) == TIER_SONNET; + assert route(R_REVIEW_FINAL, 2, false) == TIER_OPUS; + assert route(R_ORCHESTRATE, 0, true) == TIER_OPUS; } test negative_control_opus_on_a_fresh_sub_task_is_waste { @@ -129,7 +135,9 @@ test negative_control_opus_on_a_fresh_sub_task_is_waste { assert is_waste(R_PORT, TIER_OPUS, 2, false) == false; assert is_waste(R_PORT, TIER_OPUS, 0, true) == false; assert is_waste(R_PORT, TIER_SONNET, 0, false) == false; - assert is_waste(R_PLAN, TIER_OPUS, 0, false) == false; + assert is_waste(R_PLAN, TIER_OPUS, 0, false) == true; + assert is_waste(R_ORCHESTRATE, TIER_OPUS, 0, true) == false; + assert is_waste(R_REVIEW_FINAL, TIER_OPUS, 2, false) == false; } invariant every_role_routes_to_a_known_tier {