Paul Jialiang Wu agentic-portfolio 🌐 中文 · Español · 한국어 · 日本語 — in progress✉️ Free list
← Back to portfolio

AI-Native Series · Agent Engineering, Compared: Prompt · Context · Harness · Loop · Graph

Harness Engineering Is the Envelope, Not the Agent

Episode 3 of 6. This series compares the ways people engineer AI agents — prompt, context, harness, loop and graph engineering: where each came from, what it controls, who is driving it, and which principles survive measurement. This episode is the rung where the agent stops talking and starts touching things.

By Paul Jialiang Wu · agentic-portfolio-lovat.vercel.app · 2026-09-11 · Episode 3 of 6

Cover: white ground with a black left rail. Eyebrow AGENT ENGINEERING SERIES · EPISODE 3 OF 6 · HARNESS ENGINEERING above the serif title Harness Engineering Is the Envelope, Not the Agent, then two grey lines: What an agent is allowed to do — tools, permissions, validation, retries — is a boundary you design and measure. A row of six boxes labelled SIX WAYS TO SHAPE AN AGENT — THIS EPISODE IS RUNG 3, THE HARNESS: 1 PROMPT the words you send, 2 CONTEXT what it can see, 3 HARNESS what it can do (highlighted black), 4 LOOP how it corrects itself, 5 GRAPH how many agents coordinate, 6 EVALUATION what counts as right. Below, three grey cards: WHO IT'S FOR — AI engineers, founders, executives, investors, high-school builders; YOU LEAVE WITH — a free Colab lab (no API key) + 2 apps with CI gates; THE FINDING — pass@8 = 1.000 with or without an envelope, pass^8: 0.15 → 0.58, with a grey scope line reading stub agent · N=100 × k=8.
Episode 3 of 6: harness engineering, the rung where an agent gets hands — and the metric that can see whether it kept them inside the envelope you drew.

1-minute takeaway — what you'll walk away with

What this is. A comparison series on the ways to engineer an AI agent — prompt engineering (the words you send), context engineering (what it can see), harness engineering (what it can do), loop engineering (how it corrects itself) and graph engineering (how many agents coordinate). This episode covers harness engineering: its history from ReAct in 2022 to the Model Context Protocol and beyond, who drives it, and the principles that hold up under measurement.

Why it matters. An agent with tools can spend money, delete files, email customers and change a ledger. The harness — allow-list, dry-run, validation, retry budget, kill switch — is the only thing deciding which of those it may do, and the benchmark number on your dashboard probably cannot see whether it is working. In my own run, pass@8 read 1.000 for the configuration that executed 73 forbidden tool calls (stub agent, N=100 seeded tasks, k=8 attempts each). For a founder that is the gap between a demo and something you can leave running; for an engineer, an afternoon's work and one CI line.

What you can do after reading. Draw your agent's envelope explicitly, measure pass^k instead of pass@k, count the actions that left it. You get a free Colab notebook (no API key) and two apps built from nothing: a home file-organiser whose envelope turned 974 destroyed files into 974 refusals, and a finance reconciliation desk whose CI gate exits 1 when the envelope is removed — and which taught me, by failing, that one of its three safety devices was never tested at all.

Season 7 — the series spine
  1. Prompt engineering — a specification, not a spell episode 1
  2. Context engineering — a budget, not a bigger window episode 2
  3. Harness engineering — the envelope, not the agent this episode
  4. Loop engineering — an external referee, not a retry episode 4
  5. Graph engineering — a schedule, not a cast of personas episode 5
  6. Evaluation engineering — the truth function, not a leaderboard episode 6

What this episode covers, and why harness engineering comes third

This series takes six disciplines one per episode, in the order of what each lets you control: prompt → context → harness → loop → graph → evaluation. Five of them are named in Feng et al.'s survey (arXiv:2608.21156) — prompt, context, harness, loop, graph. The sixth, evaluation engineering, is this series' own addition. Every rung gets the same four questions: where it came from, what exactly you control, who is driving it, and which failure pushes you up to the next one.

Harness engineering is rung three, where the stakes change in kind. On rungs one and two, an agent that misunderstands you produces a bad sentence. On rung three it produces a bad action: a deleted file, a duplicated payment, an email that has already left. Everything here follows from that asymmetry. A sentence can be thrown away. An action cannot.

Table titled THE SIX DISCIPLINES — WHAT EACH CONTROLS · WHO LANDMARKED IT · WHERE IT BREAKS, with columns Discipline · landmark year, What you control, Landmark sources (who · when), and Primary failure. Row 1 Prompt engineering, 2020, episode 1 — the words you send: instructions, examples, the shape of the answer — GPT-3 few-shot OpenAI 2020, chain-of-thought Google 2022, DSPy Stanford 2023 — the model needs facts the words cannot carry. Row 2 Context engineering, 2025, episode 2 — what the model can see: retrieved docs, memory, tool results, budget — Anthropic engineering post 2025, Feng et al. survey 2026 — it sees the right facts and still cannot act. Row 3 Harness engineering, 2024, this episode, highlighted with a grey band and a black left bar — what it can do: tools, permissions, execution, validation, retries — Building Effective Agents Anthropic 2024, Weng 2026, Dhage measurement 2026 — one run is right and the next run differs. Row 4 Loop engineering, 2022, episode 4 — how it corrects itself: measure, reflect, retry, improve — self-consistency Google 2022, Reflexion 2023, Feng et al. 2026 — one agent's capacity is the bottleneck. Row 5 Graph engineering, 2023, episode 5 — how many agents coordinate: roles, edges, schedule — AutoGen Microsoft 2023, LangGraph 2024, Feng et al. survey 2026 — the system must learn from what came back broken. Row 6 Evaluation engineering, 2022, episode 6 — what the whole system learns from what came back wrong — HELM Stanford 2022, LLM-as-a-judge LMSYS 2023, AI Agents That Matter Princeton 2024 — the eval itself gets gamed (Goodhart).
The series in one table, with this episode's row marked. Landmark years mark the source that made each discipline nameable, not the coining of the term. This episode's row is sourced in the References below; the other rows' landmarks are cited in their own episodes.

Who this is for. An AI engineer about to give an agent write access to something that matters; a founder whose pilot customer has just asked what happens when the demo is wrong; an executive signing off on an autonomous system without knowing which question to ask; a high-school builder wondering why "give the AI some tools" is an engineering discipline. The only prerequisite is having watched a model call a function once.

Where harness engineering came from, and who is driving it

[DEF] A harness is the code around the model that runs it: the tools it may call, the arguments it may pass, what happens before an action commits, what happens when one fails, and when the whole thing stops. Lilian Weng, who wrote the canonical survey of rung one three years ago and the canonical post on this rung this July, defines it directly: "A harness is the system surrounding a base model that orchestrates execution and decides how the model thinks and plans, calls tools and acts, perceives and manages context, stores artifacts, and evaluates results."

This episode's argument is about one word in that definition — decides — and what follows from taking it literally. The history is how the industry got from "can a model use a tool" to "which tools, under what conditions, checked how".

WhenLandmarkWhoWhat it changed
2022-10ReAct: Synergizing Reasoning and Acting in Language Models (arXiv:2210.03629)Yao et al., Princeton and Google ResearchInterleaves a reasoning trace with actions, so that "reasoning traces help the model induce, track, and update action plans as well as handle exceptions, while actions allow it to interface with external sources…". The loop that every agent framework still runs.
2023-02Toolformer (arXiv:2302.04761)Schick et al., Meta AI"a model trained to decide which APIs to call, when to call them, what arguments to pass, and how to best incorporate the results into future token prediction". Tool use becomes a trained capability, not a prompt trick.
2023-05Gorilla (arXiv:2305.15334)Patil et al., UC BerkeleyAPI calls at the scale of a real registry, and the first serious treatment of the catalogue problem: "When combined with a document retriever, Gorilla demonstrates a strong capability to adapt to test-time document changes, enabling flexible user updates or version changes."
2023-06Function calling in the APIOpenAITool use stops being something you parse out of prose and becomes a typed field in the protocol. The harness gets a schema.
2024-05SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering (arXiv:2405.15793)Yang et al., PrincetonThe result this episode is built on: holding the model fixed and changing only the interface moves the score. Names the agent-computer interface (ACI).
2024-06τ-bench (arXiv:2406.12045)Yao et al., SierraIntroduces pass^k. Measuring an agent eight times and requiring all eight to pass turns out to be a different benchmark from measuring it eight times and keeping the best.
2024-11Model Context ProtocolAnthropicDescribed by its announcement as "an open standard that enables developers to build secure, two-way connections between their data sources and AI-powered tools". The harness's tool layer becomes portable between vendors.
2024-12Building Effective AgentsAnthropic (Erik Schluntz, Barry Zhang)The practitioner canon for this rung, and the sentence everyone quotes: put as much effort into the agent-computer interface as into a human one.
2026-07Harness Engineering for Self-ImprovementLilian WengNames the discipline and gives it the definition quoted above. One author, three rungs, three years.
2026-08The ladder (arXiv:2608.21156) and the determinism study (arXiv:2608.26197)Feng et al.; DhageHarness engineering placed as rung three of the published set — and measured, with a result nobody wanted.

[DESIGN] The trajectory runs from capability to constraint: 2022 and 2023 ask "can it use a tool", 2024 onward asks "which tools, checked how, and what happens when it is wrong". The standards arrived before the measurements — MCP shipped November 2024; the first serious public measurement of whether a harness makes agents more reliable is from August 2026.

Who drives it now. The model labs own the protocol layer and the practitioner canon — Anthropic's Building Effective Agents and MCP, OpenAI's function-calling schema. The coding-agent products (Claude Code, Codex) push the design hardest: a coding agent is a harness with a model in it, not the reverse. Academia owns the measurement — Princeton for the ACI result, Sierra for pass^k.

The mental model: the restaurant kitchen

One picture carries all six episodes, stated as a tool rather than a story. Every cook climbs the same ladder — cooking for yourself, then for family, then in a restaurant kitchen, then running a fast-food chain, then the national supply chain — and every engineer climbs a matching one: school learner, junior developer, senior developer, AI tech lead, CTO, frontier leader. Each rung of agent engineering is one kitchen on that ladder.

Rung one was the recipe card handed to a cook you cannot talk to. Rung two was the pantry they work from. Rung three is the kitchen itself — three things in it. Which stations this cook may work. Which tools are in reach, which are in the drawer, which are locked in the office. And the pass: the counter every plate crosses, where somebody who is not the cook checks it before it reaches a table.

A senior developer's job is not to cook better than the juniors. It is to design a kitchen where a competent stranger can work a shift without setting anything on fire, and where a wrong plate is caught at the pass rather than at the table — a bounded environment for a worker you did not train and cannot supervise. That is the whole of harness engineering.

RungKitchenWho you areWhat it lets you shape
1. promptthe recipe card — cooking for yourselfschool learnerthe written instructions the cook works from
2. contextthe pantry — cooking for familyjunior developerwhat is on the counter and in the fridge while the work happens
3. harnessthe restaurant kitchensenior developerthe stations, the tools in reach, the pass where every plate is checked
4. loopthe fast-food chainAI tech leadtaste, measure, correct, roll the fix out to every store
5. graphthe national supply chainCTOmany kitchens, one schedule, one fleet of trucks
6. evaluationthe recall logfrontier leaderwhat the whole food system learns from what came back wrong

Two axes, not one. This is not a ladder you climb and leave behind: every kitchen has all six layers at once. A home cook has a card, a pantry, a stove, a taste, a supplier and a memory of what went wrong last time. What changes as you climb is how many layers you may touch and how rigorously you run each one — by feel, then measured, then gated, then self-correcting. A national chain with a beautiful supply-chain diagram and no check at the pass is less mature than a single restaurant that inspects every plate. So the six disciplines are layers (different kinds of object: spec, state, control envelope, control flow, topology, truth function); seniority is maturity. Graph is not automatically more advanced than loop.

The analogy is allowed to introduce a claim. It is never allowed to be the evidence for one. Every element is pinned to exactly one thing in code in the 1:1 map below.

The claim, classed

[DEF] A harness is a control envelope: the set of actions the system can take, and the conditions under which each becomes legal. Not a framework, not a wrapper — a boundary you draw, that the agent's behaviour lives inside.

[EVIDENCE] The reason to treat the envelope as a design surface rather than plumbing is that somebody held the model fixed and changed only the interface. Yang et al.'s SWE-agent (arXiv:2405.15793, 2024-05-06) starts from an observation that sounds obvious once said and had not been said: "we posit that LM agents represent a new category of end users with their own needs and abilities, and would benefit from specially-built interfaces to the software they use." The result is that "SWE-agent's custom agent-computer interface (ACI) significantly enhances an agent's ability to create and edit code files, navigate entire repositories, and execute tests and other programs", reaching a pass@1 rate of 12.5% on SWE-bench — state of the art at the time, and achieved by redesigning the tools rather than the model.

Anthropic's Building Effective Agents (2024-12-19) turned the same finding into a rule of thumb: "One rule of thumb is to think about how much effort goes into human-computer interfaces (HCI), and plan to invest just as much effort in creating good agent-computer interfaces (ACI)." Their three stated principles for agents put it third and last, after simplicity and transparency: "Carefully craft your agent-computer interface (ACI) through thorough tool documentation and testing".

[EVIDENCE] So the interface matters. Does the envelope make agents reliable? Dhage's study (arXiv:2608.26197, 2026-08-25) set out to show exactly that — a deterministic execution layer with finite-state control, forced tool selection, output validation and bounded retry, two synthetic tasks, two open-weight models, four cells. The first pass is this rung's own paper failing on its own terms: the harness "significantly improves reproducibility in one of four model-task cells, significantly degrades it in two, and has no effect in the fourth."

Two of four cells got worse. The guardrails went on and the dashboard filed a complaint. The trace-level diagnostic found the cause upstairs at rung one — one unconstrained free-text planning step — and constraining it took three of four cells to 1.000 at N=100. The closing line, written by somebody who had just been embarrassed by this rung: harness engineering is a real discipline, "but its cost is model-dependent and must be measured, not assumed."

[DESIGN] Note precisely what splits, because this is the number most often repeated wrong. The paper's own words: "token cost falls in every cell, but latency shows a genuine, sample-size-robust split by model -- one becomes faster under the constraint, the other markedly slower." Token cost went down everywhere. It is latency that splits by model. An envelope is not automatically expensive; it is unpredictably expensive, in a direction you cannot read off the architecture diagram.

[DERIVATION] Put the two together and you have this episode's claim. The interface changes what an agent can achieve (SWE-agent); the envelope does not reliably change how consistently it achieves it (Dhage). Two properties, conflated because the metric almost everyone reports cannot tell them apart.

[DEF] Which brings us to the two words this episode turns on, and they are not typos of each other.

[EVIDENCE] Yao et al. reported the gap in the same breath: "We also propose a new metric (pass^k) to evaluate the reliability of agent behavior over multiple trials. Our experiments show that even state-of-the-art function calling agents (like gpt-4o) succeed on <50% of the tasks, and are quite inconsistent (pass^8 <25% in retail)." A frontier tool-calling agent solving fewer than half the tasks — and fewer than a quarter of them eight times running.

1:1 technical map

Every element of the kitchen maps to one thing in code. A metaphor may introduce a claim; it may never evidence one — so here is the translation, nothing decorative.

The kitchenThe systemWhere it lives
which stations this cook may workthe allow-list of toolsALLOW = {"mkdir","move","log"} — a set membership test before dispatch
the knife locked in the officean ungranted verbUNGRANTED = {"wire_transfer","close_period",…} — refused, logged, never dispatched
tasting the sauce before it goes on the platethe dry-rundry_run(verb, src, dst) — returns (ok, reason), touches nothing
the pass — the check before the plate leavesoutput validationa schema checked on the tool result, before the caller sees it
remaking a plate the pass sent backbounded retryfor _ in range(retries + 1) — a budget, not a loop
writing the same ticket twice on a busy nighta duplicated side effectthe idempotency key: sha256(line_id|ledger_id|cents)
the head chef killing servicethe kill switchMAX_ADJUSTMENTS_PER_RUN and a blast-radius cap
throwing away one bad platea reversible actionthe undo log — undo.jsonl, replayed in reverse
throwing away one bad ingredientan irreversible actionnothing in the undo log helps; only the allow-list does
"we served 200 covers, so we're fine"pass@kat least one of k attempts succeeded
"nobody was served a bad plate all night"pass^kall k attempts succeeded
Infographic headed THE ENVELOPE — FIVE ELEMENTS BETWEEN A MODEL'S PROPOSAL AND THE WORLD, with a scope line: Elements 1 to 4 from Lab 03, a stub agent, N=100 seeded tasks times k=8; element 5 from the-ledger-desk, N=100 books times k=5; “remove it” = what that one element was worth. Two small bands flank the row of cards: on the left THE MODEL PROPOSES AN ACTION, arrow; on the right arrow, AND ONLY THEN DOES THE WORLD CHANGE. Five cards in a row, each with a description, WHAT IT STOPS and REMOVE IT. 1 ALLOW-LIST: a verb not on the list cannot run at all; stops delete_tree, post_webhook, chmod_world_writable; remove it pass^8 minus 0.220, 93 forbidden calls executed without it. 2 DRY-RUN: every action is checked before it commits; stops the dot-dot-slash escape and the right verb aimed at the wrong target; remove it pass^8 minus 0.200, catches, does not fix. 3 VALIDATION: the result must satisfy the output schema; stops a wrong answer handed back to the caller looking perfectly fine; remove it pass^8 minus 0.330, 107 silent failures without it. 4 BOUNDED RETRY, shown as a black card: a caught failure gets up to 2 more draws; stops nothing by itself — it converts what 2 and 3 caught; remove it pass^8 minus 0.380, the largest single effect in the lab. 5 KILL SWITCH: a bound on one run's blast radius; stops one run quietly rewriting the whole accounting period; remove it pass^5 unchanged, it never fires on a healthy workload. Below, two banner boxes: a grey one reading WHAT THE BENCHMARK METRIC SEES, pass@8 = 1.000, in all six configurations — including no envelope at all; and a black one reading WHAT ONE CUSTOMER GETS, pass^8 = 0.150 to 0.580, no envelope to full envelope, same runs, same tasks. Two footnote lines close the figure: pass@k: at least one of k attempts succeeded; pass^k: ALL k attempts succeeded (Yao et al., arXiv:2406.12045). And: Stub simulator, not a language model — it shows WHERE variance enters and WHICH element removes it, never a magnitude a real model would give.
The five envelope elements, what each one stops, and what removing it cost in my own runs. The two banner numbers are the same runs read two ways — 800 attempts (N=100 × k=8) per configuration, six configurations.

Prediction Gate

Commit before you scroll — a prediction you did not record is one you will believe you got right. A tool-using agent has four envelope elements: an allow-list, a dry-run, output validation, a bounded retry of two. You run 100 tasks, eight attempts each, ablating one element at a time.

Q1. With no envelope at all — no allow-list, no dry-run, no validation, no retry — what does pass@8 read?

Q2. Rank the four elements by how much removing each one costs pass^8, most damaging first.

Q3. You add a bounded retry of two to an agent that has no allow-list, no dry-run and no validation — the retry is the only switch you change. What happens to the number of forbidden tool calls that actually execute: down, unchanged, or up?

Answers are in the Notebook and the Failure Room.

Notebook

Lab 03 — The envelope: pass^k under a harness. Open In Colab

03_envelope_passk.ipynb · Colab free tier · no API key · ~5 seconds of CPU · every cell executed before publication. Source, plus both projects below: gist.github.com/wjlgatech/218a4a45…

Eight cells. The worker is a seeded stub with three independent hazards per draw — a tool outside the allow-list, an allowed tool aimed at the wrong target, a result that fails the output schema — each scaled by a fixed per-task difficulty, because a uniform task set makes pass^k decay as a tidy p**k instead of collapsing the way τ-bench reports.

[EVIDENCE] The ablation, N=100 tasks, k up to 8, printed by cell 3:

envelope                  pass@1  pass@8   pass^1  pass^5  pass^8   out-of-env  silent
none — no envelope         0.640   1.000    0.640   0.240   0.150        73       87
full minus allow-list      0.820   1.000    0.820   0.500   0.360        93        0
full minus dry-run         0.850   1.000    0.850   0.560   0.380         0        0
full minus validation      0.820   1.000    0.820   0.410   0.250         0      107
full minus retry           0.740   1.000    0.740   0.340   0.200         0        0
full envelope              0.950   1.000    0.950   0.770   0.580         0        0

Answer to Q1: 1.000. Every configuration. Including the one that executed 73 forbidden tool calls and handed back 87 wrong answers dressed as right ones. Graded on pass@8, the most dangerous agent in the lab is the valedictorian.

That row is the argument of this episode. pass@k saturates, because that is its job: it measures whether the capability exists at all, and never claimed to be a reliability metric. pass^8 separates the same six configurations across a range of 0.430, on identical runs.

[EVIDENCE] Answer to Q2, from cell 5: retry (−0.380), validation (−0.330), allow-list (−0.220), dry-run (−0.200). Retry first is the part I did not predict. Three of the four elements are detectors — allow-list, dry-run and validation all find something wrong and refuse — and only the retry can convert a detection into a success. A detector with no retry budget is a smoke alarm wired to a diary: telling the truth, at length, to nobody who can act on it.

Which does not make retry the important one. Removing it costs 0.380 of pass^8 and zero safety; removing the allow-list costs 0.220 and 93 executed forbidden calls. Different kinds of loss, and no single ranked number tells you that.

[EVIDENCE] Answer to Q3: unchanged — and the reason is the point. The table shows 73 forbidden executions for none and 93 for full minus allow-list, and it is tempting to read that as the retry's doing. Those two rows differ in three switches. Cell 7 changes one at a time, allow-list off throughout:

configuration                        pass@1   pass^8   out-of-env   silent
none                                  0.640    0.150           73       87
+ dry-run                             0.640    0.150           74       85
+ validation                          0.640    0.150           74        0
+ bounded retry (= no allow-list)     0.820    0.360           93        0
none + bounded retry ONLY             0.640    0.150           73       87

A retry on its own does nothing. 73 → 73, and pass@1 does not move either. With no dry-run and no validation nothing is ever caught, so the loop never iterates. A retry budget with no detector in front of it is dead code — the same fact as "three of the four elements are detectors", arriving from the other direction and contradicting the neat story I wanted to tell here.

[DERIVATION] The containment cost is real and belongs to the pair: add the retry on top of the two detectors and out-of-envelope executions go 74 → 93, up 19, a 25.7% increase. The detectors make the retry fire; every extra draw is another reach for the locked drawer. So the rule is not "retries are dangerous" — it is that detection and retry are one unit, and that unit multiplies draws. Ship it without an allow-list and you have bought reliability with containment, in one commit, invisibly.

[EVIDENCE] Cell 6, the sweep, because one reading is not a measurement. Hold the envelope fixed and vary how noisy the worker is:

noisepass@1pass@8pass^5pass^8gap@8
0.250.9901.0000.9500.9400.060
0.500.9901.0000.8900.8500.150
1.000.9501.0000.7700.5800.420
1.500.9001.0000.6100.4200.580
2.000.8001.0000.4400.3100.690

pass@8 is 1.000 at every noise level — flat through an eightfold change in how badly the worker behaves — while pass^8 falls 0.940 → 0.310. A pass@k number is not a quality report; it is a smoke test with a percentage sign on it.

[DESIGN] Why a stub and not a live model. A simulator reproduces where actions leave the envelope and which element puts them back — in seconds, free, no key, every hazard rate set by hand in cell 1 where you can argue with it. It does not reproduce magnitudes. Nothing it prints is evidence for τ-bench's pass^8 <25% or Dhage's 1.000 at N=100. The last cell is the bridge: swap one function for a real tool-calling loop and the measurement code is unchanged, because pass_at_k and pass_pow_k do not know what produced the boolean.

Failure Room

Break it deliberately. A mechanism you cannot break is a claim, not a mechanism.

Failure 1 — the envelope makes it safe, not good. Project 1 below: the envelope took the safe pass^5 from 0.000 to 1.000 and left the complete pass^5 at 0.000 both ways. A perfect envelope around a mediocre worker gives you a mediocre worker that cannot hurt you. Worth having; not the same as a good agent — and every deck that shows a permissions diagram as evidence of quality makes this exact substitution.

Failure 2 — the undo log that cannot undo. Reversibility is a property of the verb, not of the log. My file-organiser writes every committed move to undo.jsonl and replays it in reverse — a complete safety story until the agent deletes something. Run the demo with the envelope off: readings.csv, the file my own stub decided was junk, disappears, and the undo replay dutifully reverses fourteen moves and restores nothing. The allow-list protects irreversible verbs; the undo log protects reversible ones. Shipping only the second is the common shape of this mistake.

Failure 3 — the detector-and-retry pair that multiplies the blast radius. Q3 as a rule: anything that multiplies attempts must sit inside the thing that bounds them. A retry alone multiplies nothing; it is detection plus retry that turns one draw into three, and with no allow-list in front of the pair that cost 19 forbidden executions. The fix is ordering, not fewer retries.

Failure 4 — the safety device that is never tested. My enterprise build has three envelope elements and a make gate-broken target that removes each and asserts the gate exits 1. Two failed the gate. The kill switch did not — on a healthy workload it never fires, so no pass-rate metric can observe it. I had written a test that could not fail, printed a green tick, and would have shipped it. The fix is in the repo: inject the fault the kill switch exists for and gate on blast radius. A safety device whose test can never fail is one you have not tested.

Failure 5 — pass@k reported as reliability. Report it beside pass^k and an out-of-envelope count, or not at all.

Projects

Two builds, from nothing. No frameworks, nothing beyond the standard library, both run before this section was written, so every number is measured. Both live in the same public gist as the notebook.

Project 1 — real life: the-desk-tidy

desk_tidy.py · python3 measure.py

A file-organiser that sorts a messy folder into categories, inside the smallest envelope that makes it safe to run unattended: an allow-list of three verbs (mkdir, move, log), a dry-run checking every destination is inside the folder, an undo log. Nobody writes a permissions policy for their Downloads folder, which is why it is the right place to learn what one buys you.

[EVIDENCE] N=100 desks × k=5 independent trials = 500 runs each way:

                             ENVELOPE ON  ENVELOPE OFF
pass@5  (safe)                     1.000         0.480
pass^5  (safe)                     1.000         0.000
pass@5  (complete)                 0.480         0.480
pass^5  (complete)                 0.000         0.000
out-of-envelope deletes                0           625
actions refused                      974             0
writes outside the root                0           349
files lost (= both)                    0           974

Two criteria, separated on purpose. SAFE: nothing destroyed, nothing written outside the folder. COMPLETE: every file also landed in the right category.

[DERIVATION] 625 destructive calls plus 349 escapes — the block counts them on separate lines because only the first destroys a file, though both are outside the envelope — is 974 files lost with the envelope off, against 974 actions refused with it on. The same number twice. The envelope did not improve a single decision; it converted every destructive action into a refusal. That is the whole trade, and it is why the complete row does not move: the price of a refusal is an unfinished job. readings.csv is still sitting unsorted on the desk. It is also still a file.

Project 2 — enterprise: the-ledger-desk

ledger_desk.py · make gate · make gate-broken · make gate-killswitch

A reconciliation agent: bank statement against ledger, post the difference. Three envelope elements, three jobs. Tool permissions decide which verbs exist at all (wire_transfer, close_period and delete_entry do not). Idempotency keyssha256(line_id|ledger_id|cents) — are the only thing that makes a retry safe. A kill switch bounds one run at twelve adjustments and $250.

[EVIDENCE] N=100 books × k=5 runs = 500 runs per row:

envelope                   pass@5   pass^5   out-of-env   dupes-land   kills
full envelope               1.000    1.000            0            0       0
minus permissions           0.960    0.080          324            0       0
minus idempotency           0.490    0.000            0          945     116
minus killswitch            1.000    1.000            0            0       0
no envelope                 0.340    0.000          324          982       0

make gate exits 0; with permissions removed, 1; with idempotency removed, 1.

[EVIDENCE] Read the minus permissions row slowly. pass@5 = 0.960 — a number you would sign off on — next to 324 ungranted tool calls that executed, and a pass^5 of 0.080. One reading of pass@k cannot see an agent that is outside its envelope in four runs out of five.

The minus idempotency row shows an interaction a single-element ablation would hide: 945 duplicate adjustments landed, and the kill switch tripped 116 times. The kill switch is invisible until another element fails — then it is the only thing between one flaky tool call and a rewritten accounting period.

[OPEN] Which is why minus killswitch reads identically to the full envelope, and why this project cost me a Makefile rewrite. pass^k on a healthy workload cannot test a device that only fires on an unhealthy one. make gate-killswitch is the fix — inject the fault (idempotency off, retries duplicating) and gate on blast radius:

killswitch ON   : 116 trips · worst run 12 postings (cap 12) · 299 lines flagged → exit 0
killswitch OFF  :   0 trips · worst run 18 postings (cap 12) ·   0 lines flagged → exit 1

Two of three elements are gated by pass^k. The third needs its own fault injection, and nothing about pass^k would have told you so.

Build it in 30 minutes — the contract

Everything the envelope needs, in the order an engineer will ask for it. Inputs: a proposed (verb, args). Output: one Result, or a refusal with a reason. Invariant: no side effect until the action has passed every gate, in order. Stop rule: a refusal escalates to a human; the retry budget is spent only on caught failures, never on refusals.

ALLOW     = {"read", "move", "log"}           # 1. the boundary. set membership.
UNGRANTED = {"delete", "wire_transfer"}       # exists in the world, not in the envelope

def act(verb, args, retries=2):
    if verb not in ALLOW:                     # 1. ALLOW-LIST — before anything else
        return refuse(verb, "not on the allow-list")
    for _ in range(retries + 1):              # 4. BOUNDED RETRY — a budget, not a loop
        ok, why = dry_run(verb, args)         # 2. DRY-RUN — nothing has touched disk
        if not ok:
            return refuse(verb, why)          #    a refusal is NOT retried
        key = idem_key(verb, args)            # 6. IDEMPOTENCY — same action, once
        if key in ledger:
            return ledger[key]
        if blast_radius() + cost(args) > CAP: # 5. KILL SWITCH — bound the run
            return halt_and_flag(verb, args)
        result = execute(verb, args)          #    the ONLY line with a side effect
        undo_log.append(inverse(verb, args))  # 7. UNDO — only helps reversible verbs
        if validate(result):                  # 3. VALIDATION — the pass
            ledger[key] = result
            return result
    return refuse(verb, "retry budget exhausted")

# CI gate (make gate): N=100 tasks x k=5 runs
#   pass^5                 >= 0.99
#   out_of_envelope_calls  == 0
#   blast radius under fault injection <= CAP
# any line below threshold -> exit 1 -> the build fails

[DESIGN] Four properties make this enterprise-shaped rather than a demo, and each earns its place:

Deliberately missing: concurrency (two runs race on the undo log), a human approval queue behind the escalation path, a real model, a real database — the idempotency ledger is a dict. The honest status of both builds is runnable and gated, not deployed and measured.

Same story, five exits

One spine, then a door for each reader. Take one decision and one action; leave the rest.

If you areThe decision this episode changesOne action, this week
a high-school builder"Giving the AI tools" is designing a kitchen, not handing over a knife.Run the Colab lab (5 seconds, no key) and explain pass@k versus pass^k to one friend.
an AI engineerYour reliability number is probably pass@k, and it saturates at 1.000 while the agent misbehaves.Compute pass^5 for one agent you own, next to a count of actions that left the envelope.
a founderA permissions diagram is not evidence of quality; an envelope makes an agent safe, not good.Ask your team for pass^k and the out-of-envelope count. If nobody has the second number, that is the finding.
an executiveThe question to ask about an autonomous system is which actions are irreversible and what bounds them.Add "which verbs are ungranted, and what is the blast-radius cap" to the next architecture review.
an investorAny agent company can show a good demo; the envelope and its gate are the part that is hard to copy.Ask for pass^k and pass@k side by side. One without the other is theater.

Reality Mission

Fifteen minutes, on something you already run this week.

  1. Pick one agent or automation you have given tools to — anything that can write, send, spend or delete.
  2. Write down every verb it can call — not the ones you meant to give it, the ones reachable from the code. This step alone is usually the finding.
  3. Mark each verb reversible or irreversible. Irreversible verbs need an allow-list; reversible ones can be handled by an undo log. Most people have the log and not the list.
  4. Run one representative task five times and count how often all five succeeded. That is your pass^5; compare it to the number you have been quoting.
  5. Find one safety device and ask when it last fired. If the answer is never, write the test that makes it fire.

If step 2 turns up verbs you did not know were reachable, stop there. Everything else in this episode is optimisation on top of a boundary you have not drawn yet.

Research Challenge

Pre-register the prediction before it runs — a result you did not predict is a result you will rationalise.

Take one agent task with a checkable outcome and a real tool surface. Establish pass@k and pass^k at k ∈ {1,3,5,8} over N ≥ 100 at fixed settings, plus a count of actions outside a declared allow-list. Then ablate one envelope element at a time — allow-list, dry-run, output validation, bounded retry — in randomised order, reporting the marginal effect of each on pass^k and on the out-of-envelope count separately. Repeat on two models.

Pre-register: whether the element that carries most of the pass^k effect is the same element that carries most of the containment effect, and whether that ordering is stable across the two models.

My stub's answer: they are different elements — retry carries reliability, the allow-list carries containment — and Dhage's paper is the reason to expect the ordering to move between models. One element carrying both on a real system would be a more interesting result than mine.

What moved

State of this rung at press time, from a live weekly feed sync across 30 pioneer sources in research-anything (make watch-sync), ranked by technical signal, never by reach. Sync date: 2026-09-11. Every item was fetched, not summarised from a feed title.

[OPEN] What has not moved: no replication of arXiv:2608.26197, and no published study I could find reporting pass^k alongside an out-of-envelope action count on a real agent — the exact measurement this episode argues for, and the one my stub can only show the shape of. If you know of one, correct me in public.

Exit test

Check yourself without scrolling up.

  1. Define pass@k and pass^k, and say which one a customer experiences.
  2. Name the five envelope elements and say which three are detectors.
  3. A bounded retry added on its own changed nothing. What has to be present before a retry raises the forbidden-call count, and why?
  4. An agent scores pass@8 = 1.000. Name two things that tells you and two it does not.
  5. Why could make gate-broken not test the kill switch, and what replaced it?

Cliffhanger

You draw the envelope.

Every verb on an allow-list. Every destination checked before it commits. Every result validated at the pass. A retry budget that is a budget and not a loop, and a kill switch with a number on it. The gate reads pass^5 1.000 and out-of-envelope 0, and for one afternoon you believe you have solved the kitchen.

Then you look at the complete row, the one that never moved, and you notice something the envelope was never going to fix.

The plates that came back from the pass came back for a reason. Nobody wrote it down. The same mistake at the same station every shift; every shift it is caught; every shift it is remade. Nothing about tonight differs from last Tuesday except that you paid for the ingredients twice.

A kitchen that catches every bad plate and never learns why is not improving. It is very good at wasting food.

Episode 4 — loop engineering. One run is right and the next run differs, and the fix is not a retry. It is a referee who does not work for the cook.

Read next

Episode 3 of Intelligence Engineering Adventures, Season 7 — The Kitchen Ladder. Claims in the series source are tagged by class — definition, derivation, evidence, engineering choice, open question — and a metaphor may introduce a claim but never serves as evidence for it. Every episode ships a Colab notebook that runs on the free tier with no API key, plus two applications built from nothing — one personal, one enterprise-shaped. The five-rung ladder is Feng et al.'s (arXiv:2608.21156); the sixth rung, evaluation engineering, is mine. The "what moved" section is generated from a live weekly feed sync of 30 pioneer sources, ranked by technical signal and never by audience reach. This article contains no material from any employer or client. — Paul Jialiang Wu · agentic-portfolio-lovat.vercel.app

References

  1. Yang, J., Jimenez, C. E., Wettig, A., Lieret, K., Yao, S., Narasimhan, K., Press, O. (2024). SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering. arXiv:2405.15793, submitted 2024-05-06. — artifact read: the arXiv abstract page. Source of the two ACI quotations and the SWE-bench pass@1 rate of 12.5%.
  2. Yao, S., Shinn, N., Razavi, P., Narasimhan, K. (2024). τ-bench: A Benchmark for Tool-Agent-User Interaction in Real-World Domains. arXiv:2406.12045, submitted 2024-06-17. — artifact read: the arXiv abstract page. Source of pass^k and of the quoted <50% / pass^8 <25% figures.
  3. Anthropic — Schluntz, E., Zhang, B. (2024-12-19). Building Effective Agents. anthropic.com/engineering. — artifact read: the live engineering post, fetched 2026-09-11. Source of the HCI/ACI rule of thumb and the third core principle.
  4. Dhage, S. (2026). Harness Engineering for Predictable Agentic Systems: An Empirical Study of Deterministic Execution Constraints. arXiv:2608.26197, submitted 2026-08-25. — artifact read: the arXiv abstract page. Source of the 1-of-4 / 2-of-4 first-pass result, the token-cost-versus-latency split, and the closing "measured, not assumed".
  5. Weng, L. (2026-07-04). Harness Engineering for Self-Improvement. lilianweng.github.io. — artifact read: the live post. Source of the definition of a harness quoted in full.
  6. Feng, Y., Xiang, Z., Yang, C., Ma, Q. et al. (2026). Graph Engineering in the Era of LLM Agents: From Individual Intelligence to System Intelligence. arXiv:2608.21156v2, revised 2026-08-26. — artifact read: the arXiv abstract page. The published statement of the ladder, including "Harness Engineering to organize external tools and resources".
  7. Yao, S., Zhao, J., Yu, D., Du, N., Shafran, I., Narasimhan, K., Cao, Y. (2022). ReAct: Synergizing Reasoning and Acting in Language Models. arXiv:2210.03629, submitted 2022-10-06. — artifact read: the arXiv abstract page.
  8. Schick, T., Dwivedi-Yu, J., Dessì, R. et al. (2023). Toolformer: Language Models Can Teach Themselves to Use Tools. arXiv:2302.04761, submitted 2023-02-09. — artifact read: the arXiv abstract page.
  9. Patil, S. G., Zhang, T., Wang, X., Gonzalez, J. E. (2023). Gorilla: Large Language Model Connected with Massive APIs. arXiv:2305.15334, submitted 2023-05-24. — artifact read: the arXiv abstract page.
  10. OpenAI (2023-06-13). Function calling and other API updates. openai.com. — described, not quoted.
  11. Anthropic (2024-11-25). Introducing the Model Context Protocol. anthropic.com/news. — artifact read: the live announcement, source of the quoted definition. Current specification documentation: modelcontextprotocol.io (undated docs home, cited for the spec rather than for a date).
  12. Model Context Protocol maintainers (2026-08-22). The New MCP Roadmap. blog.modelcontextprotocol.io. — artifact read: the live post.
  13. Yan, T. (2026). Do User-Authored Permission Policies Improve Protection Against AI Agent Overreach? arXiv:2608.27443, submitted 2026-08-27. — artifact read: the arXiv abstract page, source of the quoted −20.1 percentage-point result.
  14. Jiang, W., Chu, M., Tian, Y. et al. (2026). HarnessEvolve: Learning from Reference Trajectories for Reliable Agent Self-Evolution. arXiv:2609.00829, submitted 2026-09-01. — artifact read: the arXiv abstract page.
  15. Jiang, Jiajun, Zheng, S., Vidra, N., Setty, S. (2026). Beyond Pass@k: Measuring Reliability and Security of Agentic Code Generation. arXiv:2608.14711, submitted 2026-08-11. — listed in "What moved" as just outside the window.
  16. Jimenez, C. E., Yang, J. et al. (2023). SWE-bench: Can Language Models Resolve Real-World GitHub Issues? arXiv:2310.06770. — the benchmark the 12.5% pass@1 figure is measured on.
  17. Lab 03 and both projects, every number in this article reproducible from them: gist.github.com/wjlgatech/218a4a45533812ebe0b3f0e577dd6be7.
  18. Intelligence Engineering Adventures, Season 6 — The Governor Engine, episodes 1 and 3, for the argument that the nesting diagram does not survive contact and that no label here can express stability.