Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 20 additions & 16 deletions .claude/skills/model-routing/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
id: model-routing
name: model-routing
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.
description: Which model does which job -- a sub-task starts in a T27 OS pane on the free key tier, the orchestrator runs on Sonnet 5.5, Sonnet is the first escalation of a sub-task, and Opus 5.5 is reached only after failed attempts or on the owner's word. Load before spawning any subagent or pane, 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 -- Sonnet by default for every agent, Opus only on evidence
# /model-routing -- a pane for sub-tasks, Sonnet to orchestrate, Opus only on evidence

<!-- only-t27-rule -->
> **Only t27: everything is written in `.t27`.** (Owner hard rule 2026-10-05; budget #7371; restated in every skill per #7588.)
Expand All @@ -16,40 +16,44 @@ 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-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.
Owner directive 2026-10-20 (#11317, summarised): run lanes through T27 OS instead of paid subagents -- cheaper, and the owner watches every lane live in the window. It adds the pane tier below Sonnet for sub-tasks. It builds on the directive of 2026-10-19 (translated): "set Sonnet as the default model for all agents -- it is cheaper with the same result" (#10921), which keeps the orchestrator on Sonnet 5.5; that one replaced the 2026-10-18 rule (orchestration on Opus 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.

## The rule

| Role | Kind | Model |
|---|---|---|
| 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`) |
| Role | Kind | Starts at | Escalates to |
|---|---|---|---|
| implement, port, search, test, docs, fix | sub-task | **a T27 OS pane** on the free key tier (`TIER_PANE`, `pane`) | Sonnet 5.5 after 2 failures, Opus 5.5 after 4 |
| orchestrate, plan, review-final | orchestration | **Sonnet 5.5** (`TIER_SONNET`, `sonnet`, `claude-sonnet-5-5`) | Opus 5.5 after 2 failures |

- 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.
- Escalation is one step per `ESCALATE_AFTER` (2) failed attempts on the same task: pane, then Sonnet, then Opus (`opus`, `claude-opus-5-5`). The owner's word is Opus at any time (`owner_flag`). Nothing else raises a tier.
- Any tier stronger than `route` names is waste (`is_waste`): Opus on a fresh job for every role, and Sonnet on a fresh sub-task that a pane could have run.
- 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; a pane costs no tokens, and the gate decides what lands, not the model that wrote the diff.

## 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.
- `.claude/settings.json` sets `CLAUDE_CODE_SUBAGENT_MODEL=claude-sonnet-5-5`, so a subagent spawned on escalation, with no model of its own, runs on Sonnet.
- 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.

## How a sub-task runs as a pane

The pane tier is a place, not a Claude model: a Claude Code pane in the T27 OS window, started in the lane's worktree through `T27OS_CTL`, on the free key tier (glm-4.5-flash, then an OpenRouter `:free` model). The skill `t27os-orchestra` is the procedure: the brief as a file, spawn, wait, read the report, verify the diff, land with the gates, kill the pane. Which `T27OS_CTL` verbs exist today and which are pending (#11316) is stated there.

## When you are the orchestrator

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.
2. Start each sub-task as a pane (`t27os-orchestra`). Give it everything it needs in its brief file: paths, the issue, the boundary, the acceptance check. A brief that needs a stronger model to understand is a planning failure, not a reason to escalate.
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.
4. If the same sub-task fails twice in a pane, re-run it once as a Sonnet subagent (the Agent tool, `model` unset or `"sonnet"`); if it fails twice there too, once more with `model: "opus"`. Say in the result why it escalated.
5. In a Workflow script, set `model: 'sonnet'` on `agent()` calls for escalated sub-tasks and keep the orchestration logic in the script itself.

Parallelism: at most 3 subagents at once on this 8 GB Mac (swap eats the disk); more on the Railway lab.
Parallelism: about 3 working lanes (panes or subagents together) at once on this 8 GB Mac (swap eats the disk); more on the Railway lab.

## Audit

`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.
`is_waste(role, tier, failed_attempts, owner_flag)` in the spec answers whether a run spent a stronger tier than `route` names. Use it when TRI CLAW or the Queen records which model ran a job.

## Not covered yet

Expand Down
67 changes: 67 additions & 0 deletions .claude/skills/t27os-orchestra/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
id: t27os-orchestra
name: t27os-orchestra
description: Run a lane as a T27 OS pane on the free key tier instead of a paid subagent -- spawn a Claude Code pane in the lane worktree through T27OS_CTL, give it the brief as a file, wait, read the report, verify the diff, land through the gates. Load before starting any sub-task lane (implement, port, search, test, docs, fix) and whenever model_route.t27 names TIER_PANE.
argument-hint: "[issue number | pane id | list|spawn|kill|read|write]"
---

# /t27os-orchestra -- lanes run as panes in the T27 OS window

<!-- only-t27-rule -->
> **Only t27: everything is written in `.t27`.** (Owner hard rule 2026-10-05; budget #7371; restated in every skill per #7588.)
> - New logic starts as a `.t27` spec with `test` / `invariant` blocks. It reaches Rust, C, Zig, JS or Python only through `t27c gen-*`, and generated files are never hand-edited (L2).
> - Hand-written code in any other language is denied by the hooks and by CI: `.rs .py .ts .js .sh .zig .c .go .v .yml .toml`, Dockerfile, Makefile; the full list is in `specs/policy/own_language.t27`.
> - Existing foreign code is debt that only shrinks. Delete it, or replace it with a spec and its generated output.
> - Thin hand-written glue is capped at 40 added lines per file and 80 per PR. No label lifts the cap. Never split a PR to slip under it.
> - 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-20 (#11317): run lanes through T27 OS instead of paid subagents. The law is `specs/claw/model_route.t27` (`TIER_PANE`, the base tier of every sub-task role); the `model-routing` skill says which tier a job gets, this skill says how a pane runs it. If they disagree with the spec, the spec wins.

## Why

- **Cheaper.** A pane runs Claude Code on a free key tier (glm-4.5-flash, then an OpenRouter `:free` model, then paid last); a subagent bills Sonnet tokens.
- **Visible.** Each lane is a pane in the T27 OS window; the owner watches it live and can type into it.
- **Same gates.** Nothing about a diff changes because a pane wrote it: the gate decides what lands.

## Today's verbs (they exist now)

The client is the `t27os-pty` binary of the running build, run with `T27OS_CTL=<verb>`; it makes one call to the running server's socket and exits. The Claude Code Bash sandbox cannot reach that socket (`no server`, exit 10), so run it in a **Terminal tab** (the `run_in_terminal` tool). Facts and traps: `docs/t27os/run-local.md`, section 5 and 6.

| Verb | Arguments (environment) | Prints |
|---|---|---|
| `list` | none | `pane 0 agent 0 state 2 flags 3 key 2` per pane |
| `spawn` | `T27OS_AGENT` (0 = claude, 9 = shell cell), `T27OS_CWD` (absolute), `T27OS_MODEL` | `spawned pane N` |
| `write` | `T27OS_PANE`, `T27OS_KEYS` | `sent` (text plus one carriage return) |
| `read` | `T27OS_PANE` | the oldest 4088 bytes of the scrollback, never the tail |
| `kill` | `T27OS_PANE` | `killed` |

Editor panes: `T27OS_AGENT=9` with `T27OS_MODEL=vim|nvim|hx|micro|nano`. State: 0 stopped, 1 working, 2 blocked, 3 idle, 4 done. `T27OS_KEYS` and `T27OS_CWD` are cut at 200 bytes, silently: type a short line, never the brief itself.

## Pending verbs (gHashTag/t27#11316 -- NOT AVAILABLE YET)

Do not call these; until they land, use the workarounds in the loop below.

- `spawn` with `T27OS_TITLE` (the pane's name in the window), `T27OS_PROMPT_FILE` (the brief), `T27OS_PERM` (a permission mode, allow-listed by the owner's decision).
- `wait`: returns `idle`, `exited` or `timeout`, driven by the agent's Stop hook.
- `status`: title, key, model, busy or idle, and the traffic light of #11314.
- `read` with `T27OS_TAIL=<bytes>`: the end of the scrollback.

## The lane loop

1. **One issue per lane**, in its own worktree and branch (`lane/<slug>-<issue>`), based on a fresh `origin/master`.
2. **The brief is a file** in the lane worktree outside the tracked tree (or in the scratchpad): what to do, the boundary (files it may touch), the gates it should run itself, the branch name, the commit trailer, and "push your branch, no PR, no `gh`". Cite issue numbers as `Closes #N` in the commit only.
3. **Spawn** the pane in the worktree: `T27OS_CTL=spawn T27OS_AGENT=0 T27OS_CWD=<worktree>`. Today it has no title, prompt file or permission mode: `write` one short line telling it to read the brief file (`T27OS_KEYS='read BRIEF.md and do it'`), and have the brief say to write its final report to a file in the worktree.
4. **Wait.** Today: poll `list` until the pane's state is idle (3) or done (4), at a sensible interval; after #11316 use `wait`. A pane in state 2 (blocked) needs a person or a permission mode: look at it, do not guess.
5. **Read the report** from the file it wrote (today `read` returns only the oldest bytes of the scrollback).
6. **Verify the diff yourself** (that is review-final): read it in full, rerun the checks, never trust the pane's own claim that tests pass.
7. **Land with the gates**: `t27c test-report` FAIL 0 with vacuous passes 0; `t27b test <spec> --check --quiet` exit 0; seals regenerated by a `t27c` built from master, not by the pane; seals of the importers; the types ratchet; the dupe scan as a subset of master's; no absolute home-directory path in a committed file; ASCII only. Merge through the usual auto-merge path.
8. **Clean up**: `kill` the pane, remove the lane worktree and the lane's Zig caches (`self-cleanup`).

## Rules

- **The gate, not the pane's model, decides what lands.** A weak model that passes every gate is a good diff; a strong one that fails a gate is not.
- **Escalation is the spec's** (`route`): a pane that fails the same lane twice goes to a Sonnet subagent, a Sonnet subagent that fails it twice goes to Opus, and the owner's word is Opus at any time. Say why in the lane's result.
- **At most as many panes as the machine allows.** On the 8 GB bench Mac that is about 3 working lanes. Stop starting lanes when free disk is under 4 GB, swap is over 6 GB or the load is over 40.
- **Never read, print or store a key.** Panes get their keys from the KEYS tab; a lane that needs a key asks the owner, and no brief, report or log contains one.
- **Nothing runs on a partner's machine beyond what its owner agreed.** A lane runs on this Mac or the lab, never on a partner's computer by this skill.
18 changes: 9 additions & 9 deletions .trinity/seals/claw_ClawModelRoute.json
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
{
"built_by": "t27c-bootstrap@0.5.2+ea35d9b72",
"built_by": "t27c-bootstrap@0.5.2+2d3203e90",
"closure": [],
"config": "gen=c,rust,verilog,zig;tests=zig",
"gen_hash_c": "sha256:dd581b1b0902f88cc5985caf5b9a6fffcb96d5e5bb136d361c39d3c907d80f94",
"gen_hash_rust": "sha256:0c02f37c6a0859250c13dcfd61998c5929c1d71c72f8fd46a726a557f99b1e67",
"gen_hash_verilog": "sha256:76412baed107fc229715114f3be64f0a6e1d7496eb563d569b32ded5ab9bf57d",
"gen_hash_zig": "sha256:711d11929e6ad2ad59beab7785ba672f5c7812eff2cc1a0aaa1e7e5ddc133ac6",
"gen_hash_c": "sha256:f389334bf5c80b21b056a49fd3c2c95674cb9aa971e93da8bd9f8f5ea1b2bb9c",
"gen_hash_rust": "sha256:17489afe3e8120783e4e8b5ceeb4103151478bce88c8ed55e445ea6db44be9d5",
"gen_hash_verilog": "sha256:155505cf4fb7d816971e0466f6994daa6689436e5935faf51dc62cfa4bab7519",
"gen_hash_zig": "sha256:0a8486007f976ba056ca3e3076920441f771e06324d26629bff358cadd688190",
"module": "ClawModelRoute",
"ring": 12,
"seal_schema": 2,
"sealed_at": "2026-10-19T07:01:31Z",
"sealed_at": "2026-10-20T10:01:38Z",
"sealed_by": "t27c-bootstrap@0.5.2",
"spec_hash": "sha256:4dd19b984389e06576034616ba920eb25171abc0b31fc9bde8cf728d248bd915",
"spec_hash": "sha256:841c0295252ade0ee3fb30578dc1efb327082c8c14794fbd99a8f4cba76dfa7b",
"spec_path": "specs/claw/model_route.t27",
"tests": {
"failed": 0,
"failing": [],
"forced": false,
"passed": 6,
"total": 6,
"passed": 9,
"total": 9,
"vacuous": 0
},
"toolchain": {
Expand Down
Loading
Loading