# Cheat Sheet

Course: [Harness Engineering](https://www.devtutorials.ai/learn/harness-engineering) · Section: Reference
Source: https://www.devtutorials.ai/learn/harness-engineering/reference/cheat-sheet

> 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`:

```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.

```json
{
  "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`:

```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" }]
  }
}
```

```json
{ "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/`.

```markdown
# 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.

```markdown
---
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`:

```text
"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:

```text
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.

```bash
# 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 &
```

```bash
# 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.

```json
[
  { "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.

```markdown
# Progress

One entry per session. Newest at the bottom.
```

Add these lines to the rules file:

```markdown
## 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.

```markdown
# 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.

```text
// 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.");
```

```text
// 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.

```markdown
# .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.
```

```text
# .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."
```

```json
{ "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`:

```text
[mcp_servers.orders]
command = "npx"
args = ["-y", "orders-api-mcp"]
```

Name both in the rules file:

```markdown
- 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.

```markdown
---
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**:

1. Create the rules file. Claude Code and Codex have an `/init` command that drafts one. In Cursor, write it yourself or ask the agent to draft it.
2. Trim it to a short map that points into `docs/`.
3. Add the exact test command, and when to run it.
4. Add one "never edit" line for the file that matters most.
5. Add one or two guards: no deletes, no switched-off tests.
6. Commit the files, then give the agent one small real bug and read the diff.

## Reviewing the Agent's Work

From **Evaluator Agents**:

1. Read the whole diff. Too big to read? Ask for a smaller change.
2. Check which files changed: unexpected files, protected files, test files.
3. Look for tests switched off, deleted or weakened.
4. Run the tests and the app yourself.
5. Check that the feature list and `progress.md` match the diff.
6. 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.md` plus 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.md` or `AGENTS.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.md` with 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](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents): the initializer, the feature list and the progress file.
- [Harness engineering: leveraging Codex in an agent-first world, by OpenAI](https://openai.com/index/harness-engineering/): rules as a map, custom lints and doc gardening.
- [Harness engineering for coding agent users, on martinfowler.com](https://martinfowler.com/articles/harness-engineering.html): guides and sensors.
- [Cursor hooks documentation](https://cursor.com/docs/hooks): hooks.json and its events.

Back to: **Coding Agents**, to start the course again from the beginning.
