Skip to content
Writing
AI · part 9 of 107 min read

Harness engineering, worked through

Why AI agents fall apart, and the seven jobs a harness does so they don't. Shown on a real one: the setup that lets Claude publish to this site without a deploy.

Meer Habib

Senior Mobile Engineer · Chittagong

rari wrote a really good piece on harness engineering: why AI agents fall apart, and the seven jobs the system around a model has to do so they don't. I agree with all of it. But the examples are generic, and harness ideas only click when you watch one work on something real.

This week I built one. Claude and Codex can now write, preview and publish the projects, articles and builds on this site, with no commit and no deploy. So this is rari's framework retold, with that one harness as the example all the way through, in the same kitchen as what a harness is.

Why do agents fall apart?

An agent forgets a decision. It calls the wrong tool. It says "done" without checking. The usual fix is a longer prompt, then a bigger model, then a bigger context window.

Sometimes that helps. Usually the problem isn't how good the chef is. It's the kitchen.

Put a model in a chat box and it answers questions. Put the same model in a kitchen with tickets, recipes, stations, a pass and a log, and it can ship real work. The model didn't change. The kitchen did. Building that kitchen on purpose is harness engineering.

What does a harness actually do?

Seven jobs. The model sits in the middle and does the thinking. Everything around it is the harness.

Contractthe order ticketMapthe recipe indexToolsstations and keysStatethe ticket railThe chefClaude or CodexSensorstaste before it leavesPermissionsthe passTracesthe order logThe platework you can ship
Fig. 1Seven jobs around one chef. The prompt is just one ticket on the rail.

1. What exactly is the job?

In a kitchen, nobody cooks from "make something nice". The ticket says table 4, two pastas, no nuts, out by 8:15.

An agent needs the same: the goal, what done looks like, and what it must not touch, written down before it starts. Otherwise it finishes a different job and calls it a success.

In my harness the contract is the schema. A build is a typed list of blocks (demo, flow, system, code and a few more), defined once in convex/validators.ts. The database, the pages and the agent's tools all read that one definition, so Claude can't save something a page doesn't know how to draw. Ask for a diagram with an arrow to a box that isn't there, and the save is refused before anything lands:

Not saved:
- blocks[2] (system, "How it fits"): edge points at "cache", which isn't a node.

2. Where does it look things up?

A new cook gets the recipe index on day one, not every cookbook in the building.

Agents are the same. Give them a short map that says where things are, and let the details load only when the task needs them. A giant manual in every prompt just eats the room they need to think.

My map is one tool, guide. Its description tells the agent to read it first:

server.registerTool("guide", {
  title: "Read the content guide",
  description: "Read this first. Meer's rules for copy and visuals, how articles, projects and builds are shaped, every block type with an example, and the components MDX can use.",
});

It returns my rules (pictures over paragraphs, no em dashes, client work stays unnamed) and one example of each block. A full article body only comes in through get, when the task is about that article.

3. What is it allowed to touch?

Every cook can use the stove. Only the head chef has the key to the walk-in safe.

A tool isn't just a button. It's a promise: one job, a clear answer, a clear failure, and a boundary. Here's how the boundaries fall in mine:

ToolCan the agent do it alone?
list, get, historyYes. Read only.
save_article, save_buildYes, but new ones are drafts nobody can see.
publishA separate call, after the preview has been looked at.
deleteYes, and the old version stays in history.
Deploying code, changing secretsNot a tool at all. That's me.

4. What survives when the chat closes?

Tickets outlive the shift. The night crew reads the rail, not the day crew's memory.

The chat isn't the record. Decisions, files and failures belong outside it, so the next session picks up the work itself, not a summary of a conversation about the work.

In mine, every write stores the document as it was before and after:

await ctx.db.insert("revisions", { kind, slug, action, by, note, before, after });

So history shows who changed what and when (Claude and Codex each have their own token, and its name goes on every change), and restore puts any version back. Closing a chat loses nothing.

5. How does it know it worked?

A good kitchen tastes before the plate leaves the pass. It doesn't ask the cook whether it tastes good.

Agents need evidence, not opinions: tests, screenshots, validators. And they need eyes on their own output before anyone else sees it.

  1. 1Save a draftAgentsave_article or save_build
  2. 2Checked firstHarnessMDX compiles, components exist, arrows hit real boxes, copy has no em dashes
  3. 3Preview linkAgent + methe real page, visible only with the link
  4. 4PublishAgenta separate call, after a look
Fig. 2Every save is checked before it lands, and every draft comes with a private preview the agent can look at.

6. Who says yes?

A cook can want to send a plate. The pass decides if it goes.

The model can suggest any action it likes. Something outside the model has to approve it, especially when it's expensive, can't be undone, or touches someone else. Don't ask the same system to make the plan, judge the risk and press the button.

Mine has two locks. The site checks the agent's token. Then the database checks a content key, and refuses everything if that key isn't even set:

export function requireKey(key: string) {
  const want = process.env.CONTENT_KEY;
  if (!want || want.length < 16) throw new ConvexError("CONTENT_KEY is not set on this Convex deployment.");
  // …compared in constant time…
  if (diff !== 0) throw new ConvexError("Wrong content key.");
}

The best example happened to me while setting this up. Claude tried to copy the development key onto production, to save me a trip to the Vercel dashboard. Claude Code's own safety check blocked it: sharing one secret between development and production was my call, not the agent's. It asked. I said yes. The same action went through.

ClaudeSafety checkMeProductioncopy dev key to prodblocked: not your callshare one key with prod?yes, I'm fine with thatset the keydone
Fig. 3The model suggested it, the harness stopped it, a person decided. Then it ran.

7. What happens after it breaks?

When a plate comes back, a good kitchen doesn't just cook it again. It changes the recipe card so the same mistake can't happen tomorrow.

Fixing one bad output fixes one run. Fixing the reason it went wrong fixes every run after it. rari's version of this is the line worth keeping: a good harness turns mistakes into infrastructure.

Here's what that looked like this week:

What went wrongWhat it became
Production answered a wrong key with a blank "Server Error", so the problem looked like nothing at allEvery refusal is a readable error now: "Wrong content key."
An agent could save a diagram with an arrow pointing nowhereEvery save checks each arrow against the boxes
An article tried to import a component, which can't work from a databaseSaves refuse imports and name the components that exist
Em dashes kept slipping into copyEvery save warns about them

Why keep the brain apart from the hands?

Three parts, kept separate: the brain that thinks, the hands that act, and the history of what happened.

askstool callscheckedwritesbefore + afterClaude or Codexthe brainMeasks, previews, says yesMCP toolsthe handsConvexthe work itselfRevisionsthe history
Fig. 4Swap the model and nothing else moves. Close the session and the history is still there.

Claude and Codex use the same tools with different tokens. Either one can pick up where the other stopped, because the work lives in the database and its history, not in anyone's chat.

How much harness do you need?

Less than you think, to start. Build the smallest kitchen that can check its own work, and add a station when a failure asks for one.

  1. 1A prompt and a modelfine for a quick answer
  2. 2A guide and toolsit can work inside your project
  3. 3A contract, checks, draftsyou can trust what it hands you
  4. 4Keys, approvals, history, undoit can touch production
Fig. 5Move up a level when a real failure tells you to, not before.

This site used to sit at level one: Claude edited files, I pushed, Vercel deployed. Publishing live is what pushed it to level three.

Is your harness ready?

Before you hand an agent real work:

  • Is "done" written down before it starts?
  • Can it find what it needs without loading everything?
  • Does every tool say clearly when it failed?
  • Can it see its own result before anyone else does?
  • Does the work survive a closed chat?
  • Is anything permanent behind a check the model can't skip?
  • Can you undo any change it made?
  • Did the last failure turn into a check?

If a few of these are no, a smarter model won't save you. It will just fail faster, and cost more doing it.

The short version

  • A great chef in a bad kitchen still burns dinner. Fix the kitchen.
  • Seven jobs: write the order down, give it a map, hand out keys, keep the tickets on the rail, taste before it leaves, let the pass decide, and change the recipe when a plate comes back.
  • Fix the reason something broke, not just the plate in front of you.
  • Start with the smallest harness that can check its own work.

The seven jobs and the idea that mistakes should become infrastructure come from rari's article, which is worth reading in full. The kitchen, the examples and any mistakes are mine. And this article went through the harness it describes: drafted by Claude, checked on save, previewed, then published.

Related: the harness is the product, on why this is where the value is moving.

Building something like this?

Booking new projects for Q4. Replies within 24h.