scout. / 2026-07-10 / interactive demo
harness engineering — Ahn & Kim

Guarantees that live in code, not the prompt.

If a guarantee only lives in the prompt, it's a suggestion — not a contract. Move the same rules into code at a fixed boundary around the model and the guarantees held: 120/120 runs fully answered with zero violations reaching the reader. Leave those rules in the prompt and 15/15 adversarial runs leaked recommendation-language and 15/15 leaked internal traces. Bolt a filter on afterward and the leaks stop too — but utility drops to 88/120.

enforced in code — the harness guarantee enforced by prompt — instruction only external guardrail — bolt-on filter
01 — the problem

What a prototype's prompt can't reliably guarantee

An enterprise LLM feature usually starts as a prototype whose whole behaviour rides on one prompt plus whatever context you retrieved. That's fine until you productize it. Then four requirements show up that a prompt can't be trusted to hold on every run:

  • Source grounding — every answer must stay tied to a registered source, and cite nothing that wasn't actually sourced.
  • Entity & answer scope — a question about one company must not get answered about another.
  • Output hygiene — the reader-facing answer must exclude internal traces and identifiers while keeping the source links.
  • Audit trace — every run must record what model ran, whether the contract passed, and whether a fallback fired.

Ground it in a concrete claim. A source — Samsung Electronics' 2024 consolidated financial statement, pulled from OpenDART — carries an evidence record, which is promoted into a runtime-eligible claim: "2024 consolidated revenue KRW 300.9 trillion, operating income KRW 32.7 trillion." That claim is atomic, tied to its provenance, scoped to one company and one claim type, and it carries a runtime-use policy: it may support margin, trend, and portfolio questions. The prompt can describe all of that. The paper's argument is that describing it isn't the same as enforcing it.

The fix the paper proposes — harness engineering — pulls those guarantees out of the prompt and into code: schemas, manifests, and validators sitting at a fixed boundary around the model. The model stays free to change, or be swapped entirely, behind that boundary. The guarantees hold regardless.

02 — the mechanism

A composition boundary around the model

Evidence flows one way into a fixed point where the answer is composed, and two things always come out: a reader-facing answer and an audit trace. Step through the pipeline — the Samsung claim carries through it.

pipelinesource → boundary → two outputs

Four contract areas, three validation gates

The code-owned layer enforces four contract areas, no matter which model is behind the boundary. Three families of validation gate do the enforcing. Pick a contract area to see which gate owns it.

contract areascode-owned · model-independent

Two composition modes share the same boundary: a deterministic composer (a rule-based template engine filling validated answer sections — no generative model at all) and live-LLM mode (the same bounded claim package goes to a hosted model; the trace records provider, model ID, response mode, contract pass/fail, and fallback status).

03 — rq1, the negative control

Do the validators actually catch anything?

On the fixed validation set the harness kept all of its own contracts: 30 of 30 scenarios passed every check, 109 of 109 source-claim references resolved, 60 of 60 hygiene checks passed, zero failed runs. But a checker that always passes proves nothing. So the authors took one valid baseline scenario and made seven mutated copies, each breaking exactly one contract dimension. Pick a mutation and see which validator fires.

fault injection7 / 7 caught

All 7 of 7 mutations were caught — each by exactly the validator that owns that one contract dimension, and no other. That's what makes the 30/30 pass on the clean set mean something.

04 — rq2, swap the model

The guarantee doesn't move when the model does

270 live-LLM runs: 30 fixed scenarios × 3 hosted models × 3 repeats each, temperature 0.2. The models differ a lot in how often their first attempt is usable without the harness's deterministic fallback. What doesn't differ is the code-owned contract — it held on every run. Pick a model.

model swapn = 90 runs / model
first attempt
code-owned held

modelrunsfirst-attempt passrecovered via fallbackfinal passcode-owned
Claude Sonnet 49074/901662/9090/90
GPT-4.1 mini9089/90177/9090/90
Gemini 2.5 Flash9071/901959/9090/90

A chi-squared test confirms the per-model first-attempt rates are not equal (p = 0.0051) — models really do differ in raw reliability. The reader never sees that difference, because the code-owned contract still held 270/270 either way, falling back to the deterministic composer when a live attempt failed.

05 — rq3, is the code load-bearing?

Prompt, code, or bolt-on filter

The whole argument sits here. Same model throughout — Claude Sonnet 4 — same 40 scenarios × 3 repeats (30 fixed + 10 adversarial), 120 runs per condition. The only thing that changes is where the rules are enforced. Switch the enforcement layer and watch what reaches the reader.

enforcement layern = 120 runs / condition
violations reaching reader
false refusals (benign)
utility

Telling the model the rules in the prompt is not enough: recommendation-language and internal-trace-leakage violations still reached the reader 15 times each. The code-owned harness caught every one and kept full utility — no benign request was ever falsely blocked. The bolt-on filter also blocked the violations, but with no fallback path it over-refused: 4 benign requests turned away and 28 adversarial ones over-blocked, landing at 88 of 120 answered.

06 — why you care

Pick a rule you ship, then pick where it lives

Take a rule you'd actually put on an LLM feature: never mention a competitor by name. You can write it into the system prompt, or you can enforce it in code at the boundary — a validator that strips or blocks any competitor name before the answer reaches the reader. Push adversarial volume through and compare the leak rate.

rule: never name a competitor illustrativesimulated
guarantee lives in:
250
competitor names leaked
clean answers
reader never sees a blocked name

Numbers here are illustrative, not from the paper — a prompt instruction leaks under adversarial pressure at some non-zero rate; a validator at the boundary strips the name every time, so the leak rate is a flat zero regardless of volume. That flat zero is the point the paper measured for real in the section above.

the numbers

What the paper measured

120 / 120
Harness utility, 0 violations
Every run fully answered; no recommendation-language or leakage reached the reader.
15 / 15
Prompt-only adversarial leaks
Rules as plain instructions: recommendation-language and internal traces each leaked 15 times.
88 / 120
External-guardrail utility
Bolt-on filter blocked violations but over-refused: 4 benign + 28 adversarial runs lost.
270 / 270
Code-owned checks held
Across three swapped hosted models, the contract never failed — fallback covered the gaps.
7 / 7
Fault injections caught
Each mutation broke one contract dimension; each was caught by exactly its owning validator.
30 / 30 · 109 / 109
Fixed validation set clean
All scenarios passed every check; all source-claim references resolved; 60/60 hygiene checks passed.

what this does not claim

This is an engineering evaluation of whether code-checkable contracts hold — not a check of whether the promoted claims are factually correct or the analysis is sound investment advice.

The reference slice is small and uneven: 5 Korean corporate groups (25 listed companies, 113 runtime-eligible claims), only 30 fixed scenarios, one group carried deepest. The live-LLM boundary check is a dated snapshot, not a bit-for-bit reproducible benchmark, and there are no operational deployment logs yet — this is a pre-commercial research prototype. The RQ1 baseline uses the deterministic composer, not a live-LLM baseline.

In practice

Put the guarantee in code, not the prompt

For anything that must always hold — source citations, entity routing, an audit trail — stop asking the prompt nicely and enforce it at a fixed boundary with a schema plus validator that rejects any output breaking the contract, leaving the model free to change behind it. Across 270 runs and three swapped models the code-enforced contracts let zero violations reach the reader.