- Claude Code is Anthropic’s coding agent. It reads files, runs commands and edits code in a repository, in a terminal or an IDE.
- Context is everything the model has in front of it for a task. It is limited, and the model gets less reliable as it fills up, so what goes in it is a design decision.
- MCP (Model Context Protocol) is how an AI client talks to a tool server. Isomorphic itself is an MCP server, which matters in the last section.
The idea: five layers, from advice to enforcement
An instruction in a document is advice. The model weighs it against everything else in its context, and usually follows it. Some rules cannot depend on “usually”. So the setup is layered, and each rule lives on the lowest layer that makes it reliable:
The rest of this page walks through each layer.
1. CLAUDE.md: what every session needs
CLAUDE.md is loaded at the start of every Claude Code session in this
repository. It holds the commands, the testing policy, the shape of the system, and the
invariants that apply everywhere. For example:
Anything imported byBecause it is loaded every time, every line costs context in every conversation. Anthropic’s guidance is to keep it under about 200 lines and to cut any line whose removal would not cause a mistake. This repository enforces that limit:worker.tsruns on Workers and cannot usenode:*.
pnpm test:docs fails if the file passes 200
lines.
2. Rules that load with the files they describe
Most knowledge only matters when you touch a particular part of the code. It lives in.claude/rules/, one file per subsystem, and each file declares the paths
it covers:
scripts/rules-for-change.ts), so the reviewer knows which ones might now be out of date.
3. Skills for procedures
Some tasks are a sequence of steps that is easy to get partly right. Adding a tool to the MCP server means registering it, classifying it for analytics, deciding whether it has a web address, testing it, and updating the docs. Missing any one step fails the build, but only after the fact. A skill packages that sequence..claude/skills/add-tool/SKILL.md
opens by asking whether the tool should exist at all:
Every advertised tool costs context in every conversation. Before adding one: extend an existing tool when this is a variant of one.Claude uses a skill when a task matches its description, or you invoke it by name (for example
/add-tool). This repository has five: add-tool, add-battery (a new test suite),
new-migration (a database change), ui-baselines (accepting an intended visual change),
and regen-pr, which only a person can start because it pushes code.
4. Hooks for what must always hold
A hook is a command Claude Code runs before or after an action, and it can refuse that action. The hooks here are wired in.claude/settings.json:
scripts/agent-hooks.ts. When Claude tries to
apply a database migration to production by hand, the command never runs, and Claude reads:
- refuse edits to generated files and to database migrations that are already committed;
- refuse remote database writes, and refuse committing files that hold secrets;
- when Claude finishes a turn, rerun the code generators if their sources changed, and hand the turn back if that changed a file.
5. Tests that keep the instructions true
Everything above is only useful while it is accurate. When the code changes and a document does not, an agent reads the stale sentence and acts on it with confidence. Three test suites guard against that:-
pnpm test:docsreadsCLAUDE.md, the rules, the skills and the docs, and checks every file path, function name, tool name and constant they mention against the code. Renaming a function without updating the prose fails the build: -
pnpm test:hookschecks each hook refuses what it should and allows its everyday neighbour (a local migration passes, a remote one is refused). It also checks that the settings file still wires every hook. -
pnpm test:wiringfails if a test suite exists but CI does not run it.
CLAUDE.md asks one more thing of every change: break the new code on
purpose and watch its test fail before calling it tested. A test that stays green against
broken code proves nothing.
Writing for a model
Isomorphic is itself used by a model. When Claude connects to it, Claude reads two kinds of text: each tool’s description, and the server’s instructions (src/lib/server-instructions.ts). The same discipline
applies to both:
-
Each tool description stands alone. A host’s tool search ranks tools by their
descriptions, so a description that mentions another tool can outrank it. That happened here:
view_pageonce said “prefer this overread_page”, a search forread_pagereturnedview_page, and the agent concluded it could not read pages. -
Guidance across tools goes in the server instructions, which the host loads once:
Use
read_page(notview_page) only when YOU need the raw content to reason over it; useview_pagewhen the goal is for the USER to see it. -
Fewer tools beat more. Moving and deleting attachments reuse
move_pageanddelete_pageand do not get tools of their own.
Try it
You need Node 24 or newer, pnpm, and Claude Code.- Ask: “What does the write path do when two edits race?” Watch Claude open
src/tools/librarian.tsand load.claude/rules/write-path.mdalong with it. - Ask Claude to edit
src/lib/app-bundle.generated.ts. The hook refuses, and Claude edits the source underapp/instead. - Type
/add-batteryand describe a small check. Read the plan it follows before it writes anything. - Rename a function mentioned in
CLAUDE.md, runpnpm test:docs, and read the failure. Then undo it.
pnpm try <folder> serves any git repository of
markdown as a brain; see Getting started.
Read the history
Every change arrives as a pull request whose description records what was decided and why. A few that show the approach:Further reading
- Claude Code best practices
- Memory and rules, Skills, Hooks
- Effective context engineering for AI agents
- Writing effective tools for agents
- Architecture, for how a tool call travels through the system