Harness Engineering Cheat Sheet
By the Syncfusion DevTutorials team · Last updated
One page to come back to: where each harness file lives in Claude Code, Codex and Cursor, the lab's budgets, hook events and guard files, lint formats, definition-of-done and long-task templates, the evaluator rubric, the architecture test, the gardening skill, a failure-to-fix table, a module index and a glossary.
How to Use This Page
This page puts every file, format and checklist from the course in one place.
The lessons explain why. This page shows what.
For example:
- Where a skill lives in Claude Code, Codex and Cursor.
- The exact definition of done for each stack.
- A rules file, a skill and a rubric you can copy.
The examples use both course projects:
- Taskboard = the React app. It uses
pnpm. - Pricing API = the ASP.NET Core app. It uses
dotnet.
Tool file paths and hook events change over time.
This page was checked on 2026-10-15.
Check each tool's docs before you rely on a path. The Codex subagent format is still a draft.
Simple lesson: look things up here, learn them in the lessons.
Where Each File Lives
Each tool reads the same kinds of files. Only the paths differ.
| Part | Claude Code | Codex | Cursor |
|---|---|---|---|
| Rules | CLAUDE.md (add @AGENTS.md to share one file) | AGENTS.md, read from the git root down. AGENTS.override.md wins in its folder. | AGENTS.md, plus .cursor/rules/*.mdc |
| Skills | .claude/skills/<name>/SKILL.md | .agents/skills/<name>/SKILL.md | .cursor/skills/<name>/SKILL.md |
| Hooks | "hooks" in .claude/settings.json | .codex/hooks.json or [hooks] in config.toml | .cursor/hooks.json (camelCase events) |
| Permissions | "permissions" allow and deny in .claude/settings.json | approval policy and sandbox mode in config.toml | approval settings, and .cursor/cli.json for the CLI |
| MCP servers | .mcp.json | [mcp_servers.<id>] in .codex/config.toml | .cursor/mcp.json |
| Subagents | .claude/agents/<name>.md | .codex/agents/<name>.toml | .cursor/agents/<name>.md |
| Ignore | permissions.deny on reads | sandbox scope | .cursorignore |
| Starter files | /init | /init | settings UI for rules |
Codex loads project hooks only when you trust the .codex/ folder.
Click a tab to see one part in your tool.
- Path: where the file goes.
- Note: the extra detail for that tool.
Simple lesson: same parts, different paths.
Lab Numbers
The Harness Lab uses a few fixed numbers. They are the lab's simplified limits, not real tool settings.
| Name | Value | What it means |
|---|---|---|
| Root rules budget | 1200 tokens | The lab agent keeps this much of the root rules file. Real tools usually load the whole file, but long files dilute attention. |
| Doc budget | 800 tokens | The same limit for each docs/ file a pointer loads. |
| Skill match | 2 shared words | The lab's simple word match. Real tools let the model read each description and decide. |
| Session budget | 14 steps | The steps one lab session can use. Real sessions end when you close them, hit a limit, or get compacted. |
| Session count | 5 | The sessions in one long build. |
What real tools do instead:
- Rules file: usually loaded in full. Codex caps the size of its AGENTS.md files, by default at 32 KiB.
- Long chats: usually compacted into a summary. The rules file is normally kept. Details of the work are what get lost.
Simple lesson: the lab makes the limits easy to see. Real tools hide them, but the same habits help.
Hook Events
A hook runs at a set moment. The course uses two moments.
| Moment | Claude Code and Codex | Cursor |
|---|---|---|
| Before a tool call (can block it) | PreToolUse with a matcher | preToolUse for edits, beforeShellExecution for commands |
| When the agent wants to stop | Stop | stop |
The matcher names the tools a hook watches:
| Watches | Claude Code | Codex |
|---|---|---|
| File edits | Edit|Write|MultiEdit | apply_patch|Edit|Write |
| Shell commands | Bash | Bash|shell |
A hook blocks when it exits with code 2. Its message goes back to the agent.
So write the message like a lint message. Say what is wrong and what to do instead.
Simple lesson: a hook is a wall that runs in code.
Guard Files
Here is one guard plan in each tool's file shape.
The plan has four guards:
- block a skipped test
- protect
.env.production - block destructive commands
- run the tests before stop
Claude Code, .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Edit|Write|MultiEdit",
"hooks": [
{ "type": "command", "command": "scripts/hooks/block-skip.sh" },
{ "type": "command", "command": "scripts/hooks/protect.sh .env.production" } ] },
{ "matcher": "Bash",
"hooks": [{ "type": "command", "command": "scripts/hooks/block-destructive.sh" }] }
],
"Stop": [{ "hooks": [{ "type": "command", "command": "scripts/hooks/require-tests.sh" }] }]
},
"permissions": { "allow": [], "deny": ["Bash(rm:*)", "Edit(.env.production)"] }
}
Codex, .codex/hooks.json. Same shape, other matchers. Codex has no deny list here, so hooks do that job.
{
"hooks": {
"PreToolUse": [
{ "matcher": "apply_patch|Edit|Write",
"hooks": [
{ "type": "command", "command": "scripts/hooks/block-skip.sh" },
{ "type": "command", "command": "scripts/hooks/protect.sh .env.production" } ] },
{ "matcher": "Bash|shell",
"hooks": [{ "type": "command", "command": "scripts/hooks/block-destructive.sh" }] }
],
"Stop": [{ "hooks": [{ "type": "command", "command": "scripts/hooks/require-tests.sh" }] }]
}
}
Cursor, .cursor/hooks.json and .cursor/cli.json:
{
"version": 1,
"hooks": {
"preToolUse": [
{ "command": "scripts/hooks/block-skip.sh", "matcher": "Write" },
{ "command": "scripts/hooks/protect.sh .env.production", "matcher": "Write" }
],
"beforeShellExecution": [{ "command": "scripts/hooks/block-destructive.sh" }],
"stop": [{ "command": "scripts/hooks/require-tests.sh" }]
}
}
{ "permissions": { "allow": [], "deny": ["Shell(rm)", "Write(.env.production)"] } }
For ASP.NET Core, protect Pricing.Api/appsettings.Production.json instead.
Simple lesson: one plan, three file shapes.
Rules File Template
Keep the root file short. It is a map into docs/.
# Rules for agents (map)
Short on purpose. Details live in docs/.
## Always
- Read the failing test and the file you plan to change before you edit anything.
- Run `pnpm test` after every change, and before you say you are done.
## Where things are
- Conventions: docs/conventions.md
- Ticket statuses: docs/statuses.md
## Done and never
- Definition of done: `pnpm test` and `pnpm lint` both pass. Run them before you say you are done.
- Never edit `.env.production`. Never switch off a test. Never delete files.
For ASP.NET Core, use dotnet test and dotnet build -warnaserror.
Simple lesson: exact commands, exact files, pointers for the rest.
Definition of Done
Each line names exact commands. The agent must run them before it says done.
| Stack | Kind of task | Done line |
|---|---|---|
| React | One session | Definition of done: `pnpm test` and `pnpm lint` both pass. Run them before you say you are done. |
| ASP.NET Core | One session | Definition of done: `dotnet test` and `dotnet build -warnaserror` both pass. Run them before you say you are done. |
| React | Long task | Flip "passes" to true only after `pnpm test` and `pnpm e2e` both pass. |
| ASP.NET Core | Long task | Flip "passes" to true only after `dotnet test` and `dotnet test --filter Category=E2E` both pass. |
Simple lesson: done means the commands ran and passed.
Skill Template
A skill is a recipe for one repeat job.
---
name: add-ticket-status
description: Use when someone asks to add, create or need a new ticket status.
---
1. Add the status and its label to src/lib/status.ts.
2. Add a test case for it in src/lib/status.test.ts.
3. Add a row for it to docs/statuses.md.
4. Run `pnpm test` and make sure it passes.
- Description: says when the skill loads. Use the words people use in requests.
- Steps: say how to do the job.
Simple lesson: the description picks the skill, the steps run it.
Lint Messages That Teach
A lint message reaches the agent. So make it teach the fix.
React, in eslint.config.js:
"no-restricted-syntax": ["error",
{ selector: "CallExpression[callee.property.name='toLocaleDateString']",
message: "Do not format dates in components. Use formatDate() from src/lib/dates.ts, so every user sees the same date." }]
ASP.NET Core, in Pricing.Api/BannedSymbols.txt. One symbol;message per line:
P:System.DateTime.Now;Do not read the clock directly. Use TimeProvider.System.GetUtcNow(), so tests can control the time.
A teaching message has three parts:
- What is wrong.
- What to use instead.
- Why.
Simple lesson: "Not allowed." teaches nothing.
Long-Task Pack
Four files carry a long task across sessions.
init.sh starts the app the same way every session.
# init.sh (React)
#!/usr/bin/env bash
# Start the dev environment the same way every session.
set -e
pnpm install --frozen-lockfile
pnpm dev --port 5173 &
# init.sh (ASP.NET Core)
#!/usr/bin/env bash
set -e
dotnet restore
dotnet run --project Pricing.Api --urls http://localhost:5080 &
feature_list.json lists every feature. Each one starts as false.
A team may add an optional "owner" field.
[
{ "id": "filter", "description": "The user picks a status and only those tickets show.", "passes": false },
{ "id": "search", "description": "Typing in the search box shows only tickets whose title matches.", "passes": false }
]
progress.md is the handoff note.
# Progress
One entry per session. Newest at the bottom.
Add these lines to the rules file:
## Long tasks
- At the start of every session, run `bash init.sh`, then read progress.md and run `git log --oneline -5`.
- Pick the next item from feature_list.json where "passes" is false. Work on one feature per session.
- Never change a feature's description in feature_list.json. Only flip "passes".
- Flip "passes" to true only after `pnpm test` and `pnpm e2e` both pass.
- Before you stop: run `pnpm test`, commit, and append what you did and what is next to progress.md.
Simple lesson: start the same, do one thing, leave a note.
Evaluator Rubric Template
The evaluator is a separate agent. It did not write the code.
# Review rubric
You are the evaluator. You did not write this code. Answer PASS or FAIL.
FAIL if any line below is false.
- The unit tests pass.
- The end-to-end check passes in the running app.
- No test was skipped or switched off.
- The feature description in feature_list.json is unchanged.
- Only files for this feature changed; no unrelated edits.
Save it as evaluator/rubric.md. Every line is something a checker can see.
Simple lesson: a judge needs a checklist, not a feeling.
Architecture Test Template
An architecture test fails when one layer uses a layer it must not use.
// src/architecture.test.ts (lab helper; in a real repo use dependency-cruiser or eslint-plugin-boundaries)
forbidImport("src/components", "src/db", "Components must not read the database. Go through src/services instead.");
// Pricing.Tests/ArchitectureTests.cs (lab helper; in a real repo use NetArchTest or ArchUnitNET)
[Fact]
public void ControllersDoNotUseData() =>
Arch.ForbidDependency("Pricing.Api.Controllers", "Pricing.Api.Data", "Controllers must not use the database directly. Go through Pricing.Api.Services instead.");
Simple lesson: the message names the right way through the layers.
Subagent and MCP Templates
A subagent keeps noisy work out of the main context. An MCP server gives the agent real facts.
# .claude/agents/log-searcher.md (Cursor: .cursor/agents/log-searcher.md)
---
name: log-searcher
description: Searches the log files and returns only the lines that explain an error.
tools: Read, Grep
---
Read logs/*.log. Return the cause in at most 5 lines. Never edit files.
# .codex/agents/log-searcher.toml (draft format, check the docs)
name = "log-searcher"
description = "Searches the log files and returns only the lines that explain an error."
instructions = "Read logs/*.log. Return the cause in at most 5 lines."
{ "mcpServers": { "orders": { "command": "npx", "args": ["-y", "orders-api-mcp"] } } }
That JSON goes in .mcp.json (Claude Code) or .cursor/mcp.json (Cursor). Codex uses .codex/config.toml:
[mcp_servers.orders]
command = "npx"
args = ["-y", "orders-api-mcp"]
Name both in the rules file:
- For any search through logs/, use the log-searcher subagent. Do not read the logs yourself.
- Look up orders API URLs with the orders MCP server. Never guess a URL.
Simple lesson: a file alone is not enough. The rules must name it.
Gardening Skill Template
Docs go stale. A gardening skill finds them and reports them.
---
name: garden-docs
description: Use when someone asks to clean up, find or fix stale docs that no longer match the code.
---
1. List every file in docs/.
2. For each file path a doc mentions, check that the file exists.
3. For each command a doc mentions, check it is a real script in package.json or a real dotnet command.
4. Do not delete or rewrite docs. Report each stale doc and why, as one pull request description.
Simple lesson: report stale docs, never delete them.
Failure to Fix Layer
Find what you see. Then fix the gap in the cheapest layer that works.
| What you see | Gap | Layer that fixes it |
|---|---|---|
| Runs the wrong test command | guide | one line in the rules file |
| Ignores half of a long rules file | guide | cut it to a map, move details to docs/ |
| Does a repeat job a different way each time | guide | a skill with a clear description |
| Says "done" while a team rule is broken | sensor | lint in the definition of done |
| Sees an error but cannot fix it | sensor | a message that names the fix |
| Switches off a test, deletes files, edits production | wall | hook or permission |
| Repeats work in a new session | guide | progress.md and the handoff lines |
| Says the app is done too early | guide | feature_list.json with every item false |
| Edits a description to make the job easier | guide + sensor | a "never change a description" line, and an evaluator check |
| Leaves a broken build at the end of a session | guide | one feature per session, and a clean exit line |
| Unit tests green, but the feature is never turned on | sensor | e2e in the "flip passes" line |
| Marks its own work as perfect | sensor | a separate evaluator with a rubric |
| Fills its context with logs | context | a subagent |
| Guesses an API URL | tool | an MCP server |
| UI layer reads the database | sensor | an architecture test |
| Docs point to files that are gone | sensor | a gardening skill on a schedule |
| Harness grows with dead lines | upkeep | ablation, then remove what changes nothing |
The cheapest layers come first: rules, then skill, then test, then hook.
Simple lesson: every mistake points to one missing part.
Module Index
All 15 lessons, in order.
| Part | Lesson | What you build |
|---|---|---|
| Foundations | Coding Agents | The read, plan, act, observe loop, and a two-line rules file. |
| Foundations | Failure Gallery | Six failures, each sorted as a guide gap or a sensor gap. |
| Foundations | Rules Files | A short rules file that works as a map into docs/. |
| Foundations | Agent Skills | A SKILL.md whose description loads it and whose steps guide it. |
| Foundations | Tests and Linters | A definition of done, and lint messages that name the fix. |
| Foundations | Hooks and Permissions | Guards the agent cannot pass, and your v1 harness export. |
| Long-Running Work | Context Windows | A reset with a progress.md handoff instead of compaction. |
| Long-Running Work | Feature Lists | init.sh, a feature list, and rules that lock it. |
| Long-Running Work | Incremental Sessions | One feature per session, with a clean exit. |
| Long-Running Work | End-to-End Testing | A check that uses the running app, front and back. |
| Long-Running Work | Evaluator Agents | A separate judge with a calibrated rubric. |
| Scale and Maintain | Subagents and MCP | A small main context, and no guessed URLs. |
| Scale and Maintain | Architecture Tests | Layer rules as tests, and a doc-gardening skill. |
| Scale and Maintain | Continuous Improvement | The incident loop, and ablation for a lean harness. |
| Scale and Maintain | Capstone Project | One harness that passes every check, exported. |
Simple lesson: each lesson adds one part to the same harness.
Before You Export
Check each part before you export a harness.
| Part | Check | Lesson |
|---|---|---|
| Rules file | Short map, exact commands at the top | Rules Files |
| Definition of done | Tests and lint, by exact command | Tests and Linters |
| Lint messages | Each one names the fix | Tests and Linters |
| Guards | Block skipped tests, deletes and production edits | Hooks and Permissions |
| Long-task pack | init.sh, feature_list.json, progress.md, the long-task lines | Feature Lists |
| End-to-end check | Inside the "flip passes" line | End-to-End Testing |
| Architecture test | Fails with a teaching message | Architecture Tests |
| Subagent and MCP | Both named in the rules file | Subagents and MCP |
Before you add a new part, write down the incident that made you want it.
No incident? You probably do not need the part yet.
Simple lesson: every part earns its place.
Day 1 in Your Own Repo
From Rules Files and Hooks and Permissions:
- Create the rules file. Claude Code and Codex have an
/initcommand that drafts one. In Cursor, write it yourself or ask the agent to draft it. - Trim it to a short map that points into
docs/. - Add the exact test command, and when to run it.
- Add one "never edit" line for the file that matters most.
- Add one or two guards: no deletes, no switched-off tests.
- Commit the files, then give the agent one small real bug and read the diff.
Reviewing the Agent's Work
From Evaluator Agents:
- Read the whole diff. Too big to read? Ask for a smaller change.
- Check which files changed: unexpected files, protected files, test files.
- Look for tests switched off, deleted or weakened.
- Run the tests and the app yourself.
- Check that the feature list and
progress.mdmatch the diff. - Turn each catch into a rubric line or a check.
Safety and Cost
From Hooks and Permissions, Subagents and MCP and Continuous Improvement:
| Risk or cost | What to do | Why |
|---|---|---|
| Secrets in the repo | Keep them out. Deny reads of env files. | The agent can read what it can reach. |
| Orders hidden in files or web pages | Treat that text as data. Keep guards on. | This is prompt injection. |
| Auto-approve modes | Use them only in a sandbox or container. | Nothing stops a bad call. |
| MCP servers | Install only trusted ones, with the least access. | Each one runs with your access. |
| Evaluators, e2e runs, subagents | Add them after a real failure shows the need. | Each one costs time and money. |
| A small or solo repo | Start with rules, the test command, one never line and one or two guards. | More parts mean more upkeep. |
Glossary
- Ablation = remove one part at a time and run every check again.
- Agent = a model in a loop. It reads, plans, acts with tools, and looks at the result.
- Architecture test = a test that fails when one layer uses a layer it must not use.
- Auto-approve mode = a mode that runs every tool call without asking. Use it only in a sandbox.
- Calibrate = test a rubric on known samples until it agrees with a person.
- CI = continuous integration: every check, run on each change, on a clean machine.
- Clean exit = tests green, committed, nothing half-done, note updated.
- Compaction = the tool replaces older chat with a short summary and carries on. The rules file is usually kept. Details get lost.
- Context budget = in the lab, how much of the rules file the agent keeps.
- Context rot = attention drops as the context fills up.
- Context window = everything the model can see at one time.
- Decoy = a request that should not load a skill.
- Definition of done = the exact commands that must pass before the agent says done.
- e2e = short for end-to-end.
- End-to-end check = a test that uses the running app like a user or a client.
- Evaluator = a separate agent that answers PASS or FAIL with a rubric.
- Feature list =
feature_list.json. Every feature, each with a"passes"flag. - Garbage collection = cleanup that runs again and again, for stale docs and dead code.
- Generator = the agent that builds one feature, then asks for review.
- Guard = a hook or a permission. It runs in code, before the tool acts.
- Guide = a part that steers the agent before it acts.
- Handoff =
progress.mdplus the git log, read at the start of each session. - Harness = everything around the model: rules, skills, hooks, checks and tools.
- Hook = a script the tool runs at a set moment. Exit code 2 blocks.
- Incident = one real mistake, logged so you can fix it once.
- init.sh = a script that starts the app the same way every session.
- Initializer phase = the first session in a real project. It writes the plan and the setup, not features.
- Invariant = a rule that must always be true, checked by a test.
- Lean harness = only the parts a check needs.
- Lint = an automatic check of code style and patterns. A linter is the tool that runs it.
- Load-bearing = a part whose removal makes a check fail.
- MCP = Model Context Protocol. A way to give the agent a real tool, such as an API lookup.
- Migration = a small script that updates the database tables (EF Core in .NET).
- Permission = an allow or deny rule for a tool call. The tool enforces it.
- Planner = the agent that turns a goal into
feature_list.json. - Pointer = a rules line that names a docs file, so the agent can load it.
- progress.md = the handoff file. Each session reads it first and writes to it last.
- Prompt injection = text in a file or page that tries to give the agent orders.
- Reset = start the next session fresh, with a handoff.
- Rubric = the evaluator's checklist. Every line is something a checker can see.
- Rules file =
CLAUDE.mdorAGENTS.md. The short map the agent reads every run. - Sensor = a check that runs after the agent acts.
- Session budget = in the lab, the steps one session can use.
- Skill = a
SKILL.mdwith a description (when) and steps (how). - Subagent = a helper agent with its own context. It returns a short answer.
- Token = a small piece of text the model reads, about 4 characters.
- Wiring = the line that turns a feature on, such as a route in
Program.cs.
Simple lesson: if a word is new, look it up here.
Further Reading
- Effective harnesses for long-running agents, by Anthropic: the initializer, the feature list and the progress file.
- Harness engineering: leveraging Codex in an agent-first world, by OpenAI: rules as a map, custom lints and doc gardening.
- Harness engineering for coding agent users, on martinfowler.com: guides and sensors.
- Cursor hooks documentation: hooks.json and its events.
Back to: Coding Agents, to start the course again from the beginning.