Introduction
Recently I wrote about setting up a harness (CLAUDE.md / Hooks / Skills) for Claude Code in my side-project app:
https://zenn.dev/pythona/articles/claude-code-harness-first-setup
That post was a hands-on record of what I built. This time I want to step back and organize the underlying idea — the three principles of harness engineering — as a mapping table: which Claude Code feature is responsible for which principle. The principles themselves come from an article by OpenAI; I learned about them through a Japanese summary page (linked under "References" at the end).
Note: I was only able to read the first part of the reference page (the principles table, the prompt structure, and the beginning of the CLAUDE.md guide). My interpretation here is partly filled in from my own practice.
Mapping the 3 principles to Claude Code features
| Principle | In one line | Claude Code feature |
|---|---|---|
| ① Hand over a map, not a manual | Teach the big picture and where to look, nothing more | CLAUDE.md (+ docs/) |
| ② Enforce invariants, don't dictate implementation | Mechanically guard only the rules whose violation is an incident | Hooks |
| ③ Codify missing capabilities | Turn every failed procedure into an asset | Skills |
Reduced to these three lines, it becomes clear that each plays a completely different role. Let's go through them.
① The map — CLAUDE.md as a signpost under 200 lines
The temptation is to hand over the whole manual. But a long manual eats context, and the most important lines are exactly the ones that get skipped. The map principle says: keep CLAUDE.md a signpost under 200 lines, push the details out into docs/, and let the agent follow links when it needs them.
The recommended structure is five sections, with the project overview first:
# CLAUDE.md (skeleton)
## 1. Project overview <- always goes first
## 2. Tech stack & common commands
## 3. Invariants (violations get blocked by Hooks)
## 4. Recommended conventions (recoverable if broken)
## 5. Where to Look (details live in docs/)
In my own project, following this policy got CLAUDE.md down to 48 lines. The default move becomes "evict to docs/" rather than "add another line".
② Enforcement — don't mix "please" with invariants
Natural-language instructions are, at bottom, all "please" — they are followed probabilistically. Meanwhile, accidents like reading .env or directly editing native directories are unacceptable even once.
So this principle says: mechanically block only the things whose violation is an incident, using a PreToolUse Hook (exit 2), and leave everything else as "recommended" in CLAUDE.md. The other direction matters just as much: don't use Hooks and rules to pin down how to implement things. Over-constrain the means and you block off the model's own toolbox of solutions. Guard the conditions; delegate the implementation.
The concrete Hook setup and how I tested it are in my previous post, so I'll defer to that.
③ Accumulation — Skills grow one per failure
The third principle is the least flashy and the most effective. Every time something fails, write the correct procedure down as a Skill. From then on, you just invoke it and never trip over the same step again.
---
name: new-screen
description: Steps for adding a new screen
---
1. Create the expo-router route file under app/
2. Put the screen component under components/screens/
3. Run type checks and tests after adding it
Each one is a small memo, but the longer the project runs, the more "handrails appear exactly where you fell", so the effect compounds.
Workflow in practice: build the harness before the app
Once the mapping table is in your head, the opening move of a new project changes. My first prompt now generates the harness structure, not the app itself:
Set up a Claude Code harness for this repository.
1. CLAUDE.md (under 200 lines, 5 sections, project overview first)
2. PreToolUse hooks under .claude/hooks/ that guard invariants (e.g. protect .env)
3. Skill skeletons for /commit and /review under .claude/skills/
Split detailed documentation into docs/.
When moving on to app implementation, I state the design and verification conditions up front: e.g. "monotone color scheme", "no emoji as decoration", "after implementing, always run the lint → type check → test verification loop". Handing these over as initial conditions is far faster than going back and forth later with "this looks AI-generated, please fix it". Assets and layout tweaks, on the other hand, I treat as something that won't land in one shot — those get refined through iterative instructions.
Summary
- Map (CLAUDE.md): a signpost under 200 lines. Evict details to docs/ and let the agent follow links
- Enforce (Hooks): mechanically block only the invariants whose violation is an incident. Don't constrain how to implement
- Accumulate (Skills): document the procedure every time something fails. The effect compounds
Thinking in these three layers — map, enforce, accumulate — you get a development environment that stops repeating the same mistakes through structure, not through prompt-writing skill. Before polishing the prompt, suspect the harness. That's the order I think in these days.
Top comments (1)
For the accumulation layer, I'd attach the original failing example and an observable success condition to each new skill. Then check two things separately: does the agent load the skill for the intended task, and does following it actually fix that failure? Writing the procedure down doesn't establish either. A nearby task where the skill should not activate is useful too, otherwise a fix for one incident can become an instruction applied everywhere.