Design library — Riso document

RISO

2026-09-07 · Design Desk · Source: Running Claude Code across this Mac's lanes since July 2026

Claude Code's harness: the machine around the model

Claude Code is not a chatbot with file access. The model is one component; everything that wraps it — tools, context, delegation, stop conditions — is the harness, and the harness is where the leverage is.

269:1

The instinct when people start with Claude Code is to ask "which model?" and "what prompt?" Both questions are one level off. The model is an engine. The harness — the runtime that decides what the model can see, what it can do, and when the whole thing stops — is the machine. And unlike a chatbot, Claude Code's harness is not static: some of it ships with Claude Code itself, and some of it, on this machine, I've had to design and keep repairing myself.

The loop, not the prompt

A harness runs the model in a cycle. Assemble context — system prompt, tool schemas, conversation history, the latest message — feed it in, and wait for the model to emit text and/or tool calls. The harness executes whatever was requested, captures the results, and appends them to the context before going around again. The turn ends when the model stops calling tools. That's the whole game. The model never does anything — it requests actions, and the harness is the interpreter that closes the loop.

1. Assemble context → prompt + tools + history + latest input
2. Model emits      → text and/or tool calls
3. Harness executes → runs tools, captures results
4. Feed results back → appended to context
5. Repeat            → until the model stops calling tools

Two consequences fall out of that directly.

First, the model only knows what's in the context window this turn. Nothing from yesterday's session. Nothing a tool printed unless the harness feeds that output back. A file it hasn't read doesn't exist as far as it's concerned.

Second, reliability is a loop property, not a model property. Take a model that's right on roughly seven steps in ten: check and retry every step and the task as a whole can still land near-certain. A model that's right on nineteen steps in twenty still fails often across a thirty-step task if nothing verifies its work. The power of an agent harness isn't one brilliant response; it's many cheap, checkable steps. Design the loop, not just the prompt.

Tools are the instruction set

Tools are how the model touches the world — Bash, Read, Edit, Glob, Grep, WebSearch, and Task-style subagent spawning. Three properties decide whether they help or hurt.

Legibility of results. The model acts on what a tool result says. A tool that returns a clean conclusion steers the next turn well; one that dumps five thousand lines of raw output buries the signal and wastes context on top of it. On this machine that habit is codified: grep for the one fact you need before reading whole files, and prefer commands whose output is a verdict, not a haystack.

Determinism where it counts. Anything that must happen every time — a session auto-filed into the vault, a quote-line number verified against source — belongs in a hook or a script, never in a prompt that the model might "forget."

Surface size. Every tool definition costs context and adds a choice the model can get wrong. Claude Code deliberately runs a small tool surface; external tools arrive per-project via MCP, and even those are added only when they justify themselves. A tight, well-named surface beats a sprawling one, and a harness that hooks preferences into rigid hooks and then fights them every time you deviate is a harness working against itself.

Context is the real budget

Tokens meter cost, but context is what governs quality, and it's the scarce resource everything competes for. The window holds the system prompt, tool schemas, history, file contents, and tool outputs — all of it in the same box, all of it crowding each other out. On this machine that lesson arrived with interest: a benchmark that passed every test at 32k context still thrashed in an agentic session, because Claude Code's own system prompt and tool schemas left so little room that sessions autocompacted, refilled the window three turns running, and restarted with the model narrating a plan instead of making one. Set the real context ceiling for whatever model is wired in, or the harness assumes 200k and overruns what was loaded.

The reframe that survived all of this: context is a workspace, not a transcript. Curate it. What's in front of the model right now is a design decision, not a side effect.

And then: memory that lives outside the window

Two kinds of durable state live outside the context window on purpose.

Instructions. ~/.claude/CLAUDE.md and its @imports load into every session in every project. That's the only place guidance actually takes effect — anything that must apply every session goes in those files or as an imported reference, because features churn and plain files don't. The one-list-at-a-time discipline it enforces — grep before reading, evidence before claims — only works because it's injected, not suggested.

Knowledge. The Obsidian vault sits behind a vault-research-delegate agent: search and synthesis run in a subagent and only the findings come back. That conversion — roughly 150k of raw vault noise reduced to a paragraph of conclusions — is the same arithmetic that made delegation worthwhile in the first place.

269:1 — that's the measured compression ratio of delegating work to a subagent: ~150k tokens of search converted into ~1k tokens of findings.

Delegation is the amplifier, not "cheaper work"

A subagent is a fresh agent spawned for one scoped job. It runs in its own context window — that's the entire point. The noisy work happens over there; only the conclusion comes back. Three reasons to delegate, in order: context hygiene (biggest), specialisation (a purpose-built agent carries its own prompt and tools), and parallelism (independent chunks of work run concurrently, spawned in one message).

Two rules fell out of real failures here, not theory.

A subagent's report is data, not chat. Its final message returns to the orchestrator as a tool result — the user never sees it. If you're the orchestrator, relay what matters in your own words; silently acting on a report nobody can see is disorienting, and trusting prose summaries as evidence has burned this machine in both directions — false successes and false failures, all settled by git diff --stat or a filesystem check.

Route to the cheapest capable tier. Lookups, searches, and mechanical edits belong on the bottom rung (the GLM lane at roughly a third of top-tier cost); only genuinely hard multi-step reasoning justifies the top model. And probe the model before you fan out on it — one stalled GLM router stalled an entire slide deck for half an hour while the ladder fell through four rungs, each failed probe burning a full timeout.

Delegation is manual on this machine, not auto-routed: the session model plans, reviews, integrates; whether to delegate anything at all stays a human judgment call.

What must not depend on the model remembering

Hooked on this machine right now: a SessionEnd hook that auto-files substantive sessions into the vault; a UserPromptSubmit hook that nags for /handoff past 100k context tokens (reminder only — auto-reload after /clear was removed, because half-implemented reload machinery is worse than none); and a PreToolUse gate that denies unbounded Reads past 150 lines — with the known hole that cat/sed via Bash still leaks through, by choice. The design rule underneath: invariants get hooks; judgement calls stay in instructions.

Permissions cut the same way: --permission-mode acceptEdits covers Edit/Write/mkdir/mv/cp — never Bash. Headless -p runs have no TTY to approve a Bash call, so the model either bails instantly or loops against the wall until the wrapper's timeout kills it, logging "0 turns / 0 tokens" either way. The fix that survived a full forensic pass: scoped --allowedTools allowlists per command, not blanket bypassPermissions.

The stop condition is part of the design

A turn-loop ends when the model stops calling tools. A task should end on one of three states, declared before the work starts:

A harness that doesn't know these three will drift instead of finishing. And never poll for something the harness will notify you about — schedule one long fallback wake-up and let the notification do the work; short-interval "is it done yet?" loops are burned budget against a job that isn't happening.

What to catch yourself doing

The anti-patterns that recur across projects: one-shot faith (the self-correcting loop beats the brilliant guess), context hoarding (reading whole files, keeping every tool result — until the window is noise), silent truncation (top-N sampling that doesn't say so — "covered everything" when it didn't), premium-tier-for-everything (trivial lookups on the most expensive model: the most common cost leak), and silent fallback (a dead key or lane swallowed instead of surfaced — you keep "working" while quietly degraded).

The one-question test before any task: which lever am I pulling — loop, tools, context, delegation, or escalation? If you can't name one, you're decorating, not engineering.