AI Harness Engineering

Harness Optimization
Context Playbooks to Workflow Search

Stop hand-tuning your agent. From ACE's evolving playbooks to MCE's bi-level skill evolution, Meta-Harness's Pareto search over harness code, and AFlow's MCTS over workflow graphs — this is the ladder that climbs from prompts to optimizer code, one rung at a time.

Prerequisites: Harness Engineering (HARNESS 19) + basic probability intuition. That's it.
10
Chapters
9
Live Simulations
0
Assumed Knowledge

Chapter 0: The Ladder of Optimization Targets

You shipped a coding agent last month. You gave it a harness — tools, a loop, file-system memory, the works from the previous lesson — and it is genuinely useful. It is also failing about 30% of its tasks. You have one week to make it better. Where, exactly, do you spend that week?

Sit with the options, because they are not one option. You could rewrite the system prompt — sharper instructions, better examples. You could restructure what goes into the context window — what gets loaded, what gets summarized, what gets thrown away. You could redesign the workflow — maybe a plan step before the code step, maybe a review agent after it. You could rewrite the harness code itself — the actual program that decides what the model sees and does. Or — and this is the strange one — you could write code that rewrites the harness for you, and spend the week building the optimizer instead of the thing being optimized.

Those five doors are not just five chores. They form a sequence, and Weng (2026) states it in one line that this whole lesson unpacks: the progression in the object being optimized in a harness system is roughly instruction prompts → structured context → workflow → harness code → optimizer code. Read it left to right and two things grow at once: the complexity of the thing being tuned, and the generality of the method doing the tuning. As models become more intelligent and powerful, we move toward more complex targets and more generic methods.

The one sentence to memorize: instruction prompts → structured context → workflow → harness code → optimizer code. Every method in this lesson is a rung on this ladder. When you meet a new paper, your first question should be: what object is it optimizing? That single question sorts the entire field.

Take the rungs one at a time. Rung 1: instruction prompts. The object is a string — the words handed to the model. "Optimizing" here means rewriting that string: hand-tuning, few-shot examples, or automated prompt search (systems like Promptbreeder evolve prompts with an LLM proposing mutations). This is the world of prompt engineering, and it was the frontier in 2022. The method is narrow because the object is small: you can only rearrange words.

Rung 2: structured context. The object grows from a string into a living document — a structured playbook of what the agent has learned, curated across many runs. Optimizing means deciding what enters, what merges, what gets deduplicated. This is where ACE and MCE live (Chapters 2–3). Notice the shift: nobody rewrites one prompt anymore; the system maintains a knowledge object that outlasts any single prompt.

Rung 3: workflow. Now the object is the shape of the run itself — which steps happen, in what order, with which checks. A workflow can be handcrafted by domain experts (AI Scientist, ScientistOne, Autodata — Chapter 5) or searched by algorithms (ADAS's meta-agent, AFlow's tree search — Chapters 6–7). The method gets more generic here: search algorithms do not care what domain the workflow serves.

Rungs 4 and 5: harness code, then optimizer code. At rung 4 the object is the repository — the actual program that orchestrates the model. Meta-Harness (Chapter 4) already stands here: a coding agent proposes whole new harnesses as code. At rung 5 the object is the optimizer itself — the loop that improves the improver — and that is the territory of the next lesson, Self-Improving Harnesses. Be honest about the boundary: it is soft. Meta-Harness optimizes harness code using an optimizer that is itself a harness, so it has one foot on each of the top rungs. The ladder is a map, not a fence.

Why does the industry climb at all? Because each rung has a capability threshold — the same theme you met in the previous lesson, where harness patterns only paid off once the model could exploit them. A weak model cannot rewrite harness code safely; hand it the repo and it will break the loop that feeds it. A strong model can grep through its own execution history, propose a code change, and evaluate the result. So as models strengthen, rungs that used to be reckless become tractable, and the community's energy visibly migrates upward: prompts were the game in 2022, structured context by 2023–24, workflow search through 2025, and harness-code optimization is the frontier of 2026. Higher rungs are not better — they are later, unlocked by capability.

And what does "more generic methods" mean, concretely? A prompt trick is bespoke: it works for this task, this model, this phrasing. A search algorithm is generic: MCTS does not know whether it is searching game moves or workflow graphs; a Pareto filter does not care what the two axes measure. As the optimized object becomes richer, the winning methods become less domain-specific, not more — because at rungs 3 and above the object is code, and code has universal structure that general-purpose search can grip.

Here is the whole ladder at a glance — keep this table in your pocket; every later chapter fills in one row:

RungObject optimizedWhat "optimizing" meansMethodsEra
1 · Instruction promptsa prompt stringrewrite the wordshand-tuning, few-shot, Promptbreeder2022
2 · Structured contexta living playbookcurate what enters, merges, dedupsACE, MCE2023–24
3 · Workflowthe shape of the runrearrange stages, checks, retriesAI Scientist (hand), ADAS, AFlow (search)2024–25
4 · Harness codethe orchestrating reporewrite the program itselfMeta-Harness2025–26
5 · Optimizer codethe improver loopimprove the thing that improvesSTOP-style (next lesson)2026→

The ladder also answers the opening question — where to spend your week — as a decision procedure. Read your failures first. If the agent misunderstands instructions, you are at rung 1: cheap, fast, do it today. If it forgets what it learned last run, rung 2: your context has no memory discipline. If it does the right steps in the wrong order, or never checks its work, rung 3: workflow. Only when the same class of fix keeps recurring across many tasks does it pay to climb to rung 4 and change the harness code — and only when you are making that kind of change weekly does rung 5, automating the changes themselves, earn its complexity. Climb no higher than your failure mode demands.

This lesson walks rungs 2 and 3 end to end — context engineering, then workflow design — with a working detour onto rung 4 for Meta-Harness. By Chapter 8 you will run an actual workflow search yourself, three strategies racing on one toy task, and you will see with your own eyes why tree statistics beat greedy hill-climbing when evaluations are noisy. Click the rungs below to start building the map.

The Ladder — Click a Rung, Then Play the Climb

Each rung shows the object being optimized, the methods that optimize it, and a capability gauge — how strong the model must be before this rung becomes tractable. Press Play the climb to watch the industry's migration 2022→2026.

Misconception: "The top of the ladder makes the bottom obsolete." It does not. A system optimizing its own harness code still contains prompts, still curates context, still runs a workflow. The higher rungs wrap the lower ones — an optimizer that rewrites harness code is rewriting the code that manages prompts and context. Climbing changes what you hand-tune, not what exists.
Check: What is the correct order of the ladder — the progression of the object being optimized in a harness system?

Chapter 1: The Context Problem

Before any clever machinery, feel the problem it solves. The naive way to run an agent is to append everything: every tool response, every model generation, straight into the context, turn after turn. For a five-turn task this is fine. But agentic jobs are getting long — and as the job horizon increases significantly, appended context grows out of control.

Put numbers on it. A serious agentic rollout — debug a repo, run experiments, fix what breaks — can easily run 100 turns. Each turn adds a tool response plus a generation, call it ~2,000 tokens. That is 100 × 2,000 = 200,000 tokens of raw transcript, and the job is not even exotic. Three things now go wrong at once. First, you exceed the window the model was actually trained to use well, so attention over the early material degrades — the model starts missing things it "read." Second, cost balloons: every new turn re-pays for the entire history. Third, the signal drowns: the one insight that matters from turn 12 is buried under 88 turns of scaffolding noise.

The cost story is worse than it first looks, because context is re-paid. Turn t doesn't just add 2k tokens — it re-sends all t × 2k accumulated tokens as input. Total input tokens over n turns is 2,000 × (1 + 2 + … + n) = 2,000 × n(n+1)/2. For n = 100: 2,000 × 5,050 = 10.1 million tokens billed for a job whose final transcript is 200k. Appending is not linear-cost; it is quadratic:

TurnsWindow at the endCumulative tokens billedRatio billed/window
1020k2,000 × 55 = 110k5.5×
50100k2,000 × 1,275 = 2.55M25.5×
100200k2,000 × 5,050 = 10.1M50.5×

Context management is the layer that fights this: it constructs a more structured and concise context for the LLM and manages persistent state, instead of letting the transcript pile up raw. You met the storage half in the previous lesson — the file system as persistent memory. This chapter is about the harder half: what earns a place inside the window at any given moment. In code, the difference is two data structures:

python
# naive: the transcript IS the context — grows without bound
context = []
def naive_turn(msg, tool_out):
    context.append(msg); context.append(tool_out)   # 100 turns → 200k tokens
    return llm("\n".join(context))

# managed: a bounded, structured playbook + this turn's working set
playbook = {}                                       # {identifier: description}
def managed_turn(msg, tool_out):
    working = select_relevant(playbook, msg, k=12)  # what EARNS the window
    reply = llm(render(working) + msg + tool_out)
    return reply                                    # persistent state lives in files

And it is not only money. A longer input is slower to process, so each turn's latency grows with the transcript; and models empirically attend unevenly across very long inputs — material buried in the middle of a huge window is recalled worse than material near either end. So the insight from turn 12, sitting mid-transcript at turn 90, is in the worst possible seat: paid for on every call, and least likely to be used.

A fair objection: "long-context research will fix this." Partly true — long-context modeling keeps making real progress, and every improvement raises the ceiling. But Weng (2026) is careful here: at the moment, long-context intelligence and context engineering intertwine. Better native long context means the model can hold more; engineering decides what deserves to be held at any budget. A bigger backpack does not pack itself. Even a 10-million-token window would leave the question of which 10 million tokens — and at what cost per step.

What long-context progress genuinely buys: a higher ceiling before degradation, fewer forced evictions mid-task, and cheaper mistakes when curation is imperfect. What it does not buy: relevance ranking, cost control, or persistence across sessions. Those three are context engineering's permanent job — which is why the two intertwine instead of one retiring the other.

So the obvious fix is compression: every so often, have the model rewrite its own context into a shorter summary. Try it, and after a few cycles you meet the two demons this chapter is named for. Here is a true-to-life miniature. On turn 9 of a debugging session your agent earns a hard-won, precise insight: "the flaky test only fails when the CI machine is in UTC+9, because the date-rollover assertion runs at local midnight." The context gets summarized. Rewrite one: "the flaky test is timezone-dependent (UTC+9, midnight rollover)." Rewrite two: "there are timezone-related test issues." Rewrite three: "timezone issues exist." Every rewrite was locally reasonable. The load-bearing detail — which timezone, why midnight — is gone, and the agent will re-derive it from scratch next week, paying the full debugging cost again.

Lay the decay out as a table and you can see exactly which load-bearing atom vanishes at each step:

RewriteWhat the context saysWhat just got lost
0 (original)"flaky test fails only when CI is in UTC+9 — the date-rollover assertion runs at local midnight"
1"flaky test is timezone-dependent (UTC+9, midnight rollover)"the causal mechanism — why midnight matters
2"there are timezone-related test issues"which timezone; which test; the trigger condition
3"timezone issues exist"everything actionable — a week of debugging, gone

Name the two demons precisely, because ACE is built to fight exactly them. Context collapse is the loss of hard-won detail under iterative rewriting: each summarize step is lossy, and losses compound multiplicatively across cycles. Brevity bias is the direction of the loss: a model asked to "summarize" is being asked to shorten, so every rewrite drifts toward the vague and the general. Collapse says information disappears; brevity bias says it disappears toward mush. The UTC+9 story shows both at once — the fact decayed (collapse) and what survived got vaguer at every step (brevity bias).

Worse, the two demons feed each other. Collapse deletes the specific anchors (identifiers, numbers, conditions) that would have kept the next rewrite honest; brevity bias fills the vacated space with safe generalities that compress even further next time. Each cycle leaves the summarizer less to hold onto and more license to generalize — the decay accelerates. That is why the fidelity curve you are about to see bends downward instead of falling in a straight line.

Misconception: "Just summarize the context each turn — summaries are what context management means." Summarization is lossy compression, and running lossy compression repeatedly on its own output is making a photocopy of a photocopy. One generation looks fine. Ten generations is grey fog. The fix is not a better photocopier — it is to stop re-copying: keep discrete items that are appended and edited individually, and never rewrite what you did not touch.

A fair follow-up: what happens when the itemized playbook itself grows to thousands of items? Then it no longer all fits the window either — and the answer is the select_relevant line in the managed-turn code above: retrieve the top-k items relevant to this task, leave the rest safely on disk. Itemization does not just stop decay; it makes retrieval possible at all, because discrete addressable items can be indexed and fetched, while a prose blob can only be included or excluded whole.

That last sentence is the entire preview of Chapter 2. If context is a single blob, every update must rewrite the whole blob, and every rewrite risks every item in it. If context is an itemized list — discrete entries with stable identifiers — then an update touches only the items it names, and the untouched 95% cannot decay, because nothing ever re-generates it. The playbook survives not because the model summarizes better, but because the update rule is structurally incapable of collapsing what it does not touch.

Play with the decay below. Drag the rewrite-cycles slider and watch seven real debugging insights pass through repeated monolithic rewrites — each item fades to vagueness, then vanishes, and the fidelity curve falls. Then flip to itemized mode and drag again: items persist untouched, new ones append, and the curve holds flat. The difference is not model quality. It is the update rule.

Photocopy Decay — Monolithic Rewrites vs an Itemized Playbook

Seven hard-won insights sit in context. Drag rewrite cycles to pass them through repeated summarize steps. In monolithic mode items grey out (vague) then vanish (lost). In itemized mode items are edited individually — nothing untouched can decay, and new items append in green.

Rewrite cycles 0
fidelity after n rewrites = rn,  where r = fraction of detail each rewrite keeps
r = 0.9, n = 10:  0.910 = 0.9 × 0.9 × … × 0.9 ≈ 0.349  →  65% of everything learned, silently gone
itemized (untouched items never regenerate): r = 1.0  →  1.010 = 1.0
Key insight: the failure is multiplicative. If each rewrite keeps 90% of the detail, ten rewrites keep 0.910 ≈ 0.35 — two thirds of everything the agent learned, silently gone. An itemized update rule makes retention 100% for untouched items by construction: 1.010 = 1.0. You do not beat compounding decay with a better summarizer; you beat it by refusing to compound.
Check: An agent's summarized context now reads "timezone issues exist" where it once held "flaky test fails only under UTC+9 at the midnight date-rollover." Which demon is the direction of this decay — the drift toward vague generalities?

Chapter 2: ACE — Context as an Evolving Playbook

Chapter 1 ended with a promise: keep context as discrete items and the decay demons lose their grip. Agentic Context Engineering (ACE; Zhang et al. 2025, ICLR 2026) is that promise built out into a full system. Its reframe is worth saying slowly: context is not an ever-lengthening prompt. It is an evolving playbook — a curated collection of bullet points, each carrying an identifier (a stable, unique name like pandas-groupby-copy) and a description (the actual insight). The identifier is the item's address; the description is what lives there.

ACE maintains this one playbook with three components, and each is a distinct role with a distinct job. Think of a sports team between games: players play, an analyst watches the tape, and an editor updates the playbook binder. Nobody does all three, because each job corrupts the others when merged.

The Generator is the player. It produces task trajectories — actual attempts at actual tasks — and it does so with reference to the bullet points: the current playbook is in its context, so hard-won insights from past runs shape present behavior. This closes the loop that makes the playbook worth maintaining. A playbook nobody reads is a diary; a playbook the Generator consults every run is institutional memory.

The Generator's trajectories are raw material, not knowledge. A 60-turn trajectory contains maybe two sentences worth of reusable insight, buried in tool noise. Extracting them is the Reflector's job: it distills insights from trajectories — and, critically, from successful and failed trajectories alike. The failures matter as much as the wins, and it is worth pausing on why. A success tells you one path that worked; a failure tells you a whole region that does not, and often why it does not. "The retry loop must cap at 3 because the API rate-limits at 4" is a failure-born insight no success would ever surface. A Reflector that only mines wins builds a playbook of lucky paths; one that mines losses builds a map of the minefield.

Then comes the role where ACE's real design lives: the Curator updates the structured context with incremental, itemized entries. Note the two adjectives. Incremental: it adds and edits; it does not regenerate. Itemized: its output is a set of (identifier, description) bullets, not prose. The Curator is an editor with a strict format, not an author with a blank page.

RoleConsumesProducesWhy it must be separate
Generatortask + current playbooktask trajectoriesdoing the work would bias what gets recorded about the work
Reflectortrajectories (✓ and ✗)candidate insightsdistilling needs distance — the actor's context is full of self-justification
Curatorinsights as (id, desc) deltasmerged playbookstate mutation must be boring and deterministic, never creative
The key design choice, stated in full: the Curator does not rewrite a full prompt blob. It outputs structured, itemized bullets of the form (identifier, description), and these bullets are merged into the context logbook with deterministic logic — ordinary code performs the merge, not a model. New identifier → append. Existing identifier → update that entry. Everything else → byte-for-byte untouched.

Why does deterministic merging beat asking a strong LLM to "produce the updated playbook"? Because a deterministic merge cannot collapse or vague-ify what it does not touch — not "is unlikely to," but cannot, structurally. An LLM rewriting the whole playbook re-generates every item, and every re-generation is a fresh roll of the brevity-bias dice from Chapter 1. The merge function never rolls those dice: an item it was not handed passes through as inert bytes. ACE moves the risky, creative work (distilling insights) into the Reflector, and makes the state-mutation step boring on purpose. Boring is what survives ten thousand cycles.

Items still need hygiene — a playbook that only ever appends will bloat with near-duplicates ("cap retries at 3" and "don't retry more than 3 times" from different runs). So ACE refines and deduplicates the context items periodically: an occasional, deliberate maintenance pass (embedding similarity catches near-duplicate descriptions), not a rewrite on every update. The dangerous operation happens rarely and in a controlled way, instead of constantly and incidentally.

Let's make the whole cycle concrete with a worked example. Suppose the Generator runs three trajectories on data-engineering tasks:

TrajectoryOutcomeWhat the Reflector distills
T1: fix a pandas pipeline✓ success after a bug(pandas-groupby-copy, "groupby().apply() returns a copy — mutations inside apply are silently lost; assign the result back")
T2: batch-call an API✗ failed twice, then passed(api-retry-cap, "the vendor API rate-limits at 4 req/s; cap retries at 3 with exponential backoff")
T3: another pandas task✓ success(pandas-groupby-copy, "...assign the result back; also true for transform on categorical columns")

The Curator's merge is now pure mechanics. After T1: playbook gains pandas-groupby-copy. After T2: api-retry-cap appends — and note it was born from a failure. After T3: the Reflector emitted an existing identifier, so the merge updates that one entry with the enriched description — the categorical-columns detail is folded in — while api-retry-cap is not re-generated, not re-summarized, not even read. Three trajectories, two items, zero collateral damage. Trace the playbook state itself:

python
# playbook state after each trajectory — the merge trace
after_T1 = {
  "pandas-groupby-copy": "groupby().apply() returns a copy; assign result back",
}
after_T2 = {
  "pandas-groupby-copy": "groupby().apply() returns a copy; assign result back",
  "api-retry-cap":       "vendor rate-limits at 4 req/s; cap retries at 3 + backoff",
}
after_T3 = {   # same id arrived → THAT entry updates; the other is untouched bytes
  "pandas-groupby-copy": "...assign result back; also true for transform "
                         "on categorical columns",          # ← enriched
  "api-retry-cap":       "vendor rate-limits at 4 req/s; cap retries at 3 + backoff",
}                                                            # ← byte-identical

And here is the update step as real code:

python
def reflect(trajectory, llm):
    # The ONLY generative step: distill (id, desc) deltas from a trajectory.
    # Works on successes AND failures -- failures map the minefield.
    prompt = ("Extract reusable insights from this trajectory as JSON "
              "[{identifier, description}]. Include lessons from failures.\n"
              + trajectory.render())
    return json.loads(llm(prompt))          # -> [(id, desc), ...]

def curate(playbook: dict, deltas: list) -> dict:
    # DETERMINISTIC merge -- plain code, no model, no dice.
    for d in deltas:
        key = d["identifier"]
        if key in playbook:
            playbook[key] = merge_desc(playbook[key], d["description"])
        else:
            playbook[key] = d["description"]   # append: new insight
    return playbook                          # untouched items: inert bytes

def dedupe(playbook, embed, thresh=0.92):
    # PERIODIC hygiene pass (not every update): drop near-duplicates.
    keys = list(playbook)
    vecs = {k: embed(playbook[k]) for k in keys}
    for i, a in enumerate(keys):
        for b in keys[i+1:]:
            if a in playbook and b in playbook \
               and cosine(vecs[a], vecs[b]) > thresh:
                del playbook[b]          # keep the older identifier
    return playbook

And the compact version — the whole ACE update step is genuinely this small once you see the shape:

python
def ace_step(playbook, task, llm):
    traj    = generate(task, context=playbook, llm=llm)   # Generator
    deltas  = reflect(traj, llm)                          # Reflector
    return curate(playbook, deltas)                       # Curator (deterministic)

Now run the machine below. Fire rollouts and watch insight bullets fly into the grouped playbook — duplicates land on an existing row and bump its counter instead of adding noise. Then flip to monolithic blob mode and run ten more: the whole panel rewrites on every rollout, and you will watch existing items flicker out in red. That flicker is Chapter 1's photocopy decay, live.

Playbook Evolution — Rollouts In, Itemized Bullets Merged

Green rollouts succeeded, red failed — both emit bullets (failures map the minefield). The ×N badge counts deduplicated re-discoveries. Toggle to monolithic blob to watch a whole-panel rewrite lose items on every run.

Misconception: "ACE works because the Curator is a very careful writer." No — the Curator's output format is doing the work, not its prose skill. Any model emitting (identifier, description) pairs into a deterministic merge inherits the no-collateral-damage guarantee. Swap in a sloppier model and you get worse new items, but the existing playbook still cannot decay. The safety lives in the merge rule, not the model.

One engineering edge case before moving on, because it is where itemized systems actually break in practice: identifier collisions. What if two runs distill contradictory descriptions under the same identifier — T2's api-retry-cap says "cap at 3," a later run says "cap at 5, the vendor raised limits"? The deterministic merge must have a policy: last-write-wins (simple, risks thrash), merge-both-with-timestamps (safe, grows), or flag-for-reflection (route the conflict back to the Reflector as a task). ACE's periodic refinement pass is where such contradictions get resolved. The lesson generalizes: deterministic merging removes accidental damage, but semantic conflicts still need a deliberate, occasional, model-in-the-loop pass — scheduled hygiene, not per-update roulette.

Now the honest limit, because it is the hinge to the next chapter. ACE learns insights from rollouts — that is real movement toward self-managed memory. But look at what is still frozen: the update rules and the overall workflow are handcrafted. A human decided that context should be bullets, that bullets should have identifiers, that merges should be deterministic, that dedup runs periodically. The content evolves; the mechanism is carved in stone. What if bullets are the wrong structure for some task — what if it needs a code library, or a retrieval index, or hierarchical notes? ACE cannot ask that question. Chapter 3 is about the system that can.

Check: What exactly does ACE's Curator output, and how does it enter the playbook?

Chapter 3: MCE — Evolving the Mechanism, Not Just the Content

ACE ended on a confession: its update rules are handcrafted. Someone decided context should be an itemized playbook, and that decision never gets revisited — no matter how many rollouts disagree. Meta Context Engineering (MCE; Ye et al. 2026) is built on one clean separation: the mechanism (how context is managed) versus the artifact (what is in the context). ACE evolves the artifact under a fixed mechanism. MCE runs skill evolution at the meta level and context optimization at the base level — two loops, one machine, and the outer loop's job is to question exactly what ACE froze.

The unit the outer loop evolves is called a skill. A skill s ∈ S (the space of possible skills) defines a context function cs = (ρs, Fs) that maps an input x to a context c:

c = Fs(x; ρs)

Every symbol, with its analogy. ρs = {ρ1, …, ρm} are the static components: prompts, knowledge bases, code libraries. This is the pantry — ingredients that sit on the shelf regardless of tonight's order. Fs = {F1, …, Fk} are the dynamic operators: search, selection, filtering, formatting. This is the cooking — the steps that turn shelf ingredients into a plated dish. And x is tonight's order: the actual input. So a skill is a recipe — pantry plus cooking steps — and the context c is the plated dish, prepared fresh for each input. ACE, in this vocabulary, is one fixed recipe. MCE breeds new recipes.

Two loops need two objectives, and here is the lesson's mathematical heart. Read it gently — every piece will be defined:

Inner:  cs* = argmaxcs  Jtrain(cs; s)
Outer:  s* = argmaxs ∈ S  Jval(cs*)

Symbol by symbol. Jtrain is a score — how well the agent performs on training tasks when given context cs. Jval is the same score measured on held-out validation tasks the inner loop never saw. argmax just means "the choice that makes the score biggest." The inner loop holds the recipe s fixed and perfects the dish: find the best context cs* this recipe can produce, scored on training data. The outer loop compares finished dishes across recipes and picks the best recipe s* — but scores them on validation data. If you know deep learning, this is precisely the classic split: inner = train the weights, outer = pick the hyperparameters/architecture. If you don't: inner = get the best meal out of one recipe; outer = decide which recipe deserves to stay in the family cookbook, judged by guests who didn't watch you cook.

Why Jtrain inside but Jval outside? This asymmetry is subtle and it is the single most important design decision in MCE, so take it in two steps. Step one: the inner loop should use training data — it runs many optimization steps, and it is allowed to squeeze everything it can out of the tasks in front of it. That is what optimization means. Step two: precisely because the inner loop squeezes so hard, its final Jtrain is a flattering, contaminated estimate. A skill can score brilliantly on training tasks by memorizing their quirks — hardcoding the pantry with answers, tuning the cooking to the exact menu. If the outer loop also judged by Jtrain, it would systematically breed quirk-memorizers: skills that look great and generalize terribly.

That failure has a name: meta-overfitting — overfitting one level up, where the mechanism (not the model) has specialized to the training distribution. The defense is exactly the one statisticians have used for a century: judge on data the optimizer never touched. The outer loop selects skills by Jval, so a memorizer's inflated training score buys it nothing — its validation score exposes it, and the crossover machinery breeds from honest winners instead. Watch it happen in numbers:

Candidate skillJtrainJvalOuter-loop verdict
skill-A: verbatim rollout log, keyword search0.810.79solid, generalizes (gap 0.02)
skill-B: memorize per-task answer hints0.88 ← best on train0.64REJECTED — meta-overfit (gap 0.24)
skill-C: itemized playbook + top-k retrieval0.840.82 ← best on valWINNER — selected as s*

Follow the arithmetic of the verdict. Greedy-on-train would rank B > C > A (0.88 > 0.84 > 0.81) and crown the memorizer. The outer loop instead ranks by validation: C > A > B (0.82 > 0.79 > 0.64), crowning C. And the gap Jtrain − Jval is the tell: A gaps 0.02, C gaps 0.02, B gaps 0.88 − 0.64 = 0.24 — twelve times wider. A big train–val gap at the skill level is the smoking gun of meta-overfitting, exactly as it is for an overfit neural net one level down.

How does the outer loop actually propose new skills? Not by random mutation — by informed breeding. MCE keeps a skill database tracking the full history of previous skills, their context functions, and both eval metrics:

Hk−1 = {(si, ci, Jitrain, Jival)}i=1…k−1

Every recipe ever tried, with its dish and both report cards — the breeding stock. A meta-level agent then performs agentic crossover: given the task τ (tau — the task at hand), it reads the history and writes a new skill combining the strengths of prior ones:

sk = crossover(τ, Hk−1)

The word agentic is load-bearing. In a genetic algorithm, crossover is blind splicing — half of parent A, half of parent B, hope for the best. Here the crossover operator is an LLM agent that reads the history: it can notice "A's verbatim logging scored well on debugging tasks, C's retrieval kept validation gaps small — combine A's logging with C's retrieval and drop B's memorization, which gapped 0.24." Then the base-level context engineer takes over and executes the new skill, learning the context from rollout feedback:

ck = engineer(τ, sk; c*k−1, Rk)

Read the arguments: it builds context for task τ, following recipe sk, warm-starting from the previous best context c*k−1 (no cold restarts — knowledge carries over), guided by rollout feedback Rk — what actually happened when the agent ran. The inner loop is itself agentic: try, observe, refine the files.

One degradation case to hold onto: what happens when the skill database itself grows large? The crossover agent must read Hk−1 to breed — and after hundreds of skills, the history no longer fits any window. The meta level has re-created the base level's context problem, one floor up, and needs its own retrieval and summarization over past skills. This recursion is not a flaw; it is the shape of the whole field — every optimization layer eventually needs the layer below it applied to itself.

Files — that word is literal, and this is the implementation detail that should make you smile. A context function c is instantiated as a collection of files in a dedicated directory: static components as skill.md, dynamic components as context and data rollout files. And both the meta level and the base level run in ordinary agentic coding environments with the standard toolset:

T = {Read, Write, Edit, Bash, Glob, Grep, TodoWrite}

No bespoke optimizer, no gradient machinery — skills are evolved by agents reading and editing files with the same seven tools a coding agent uses to fix your repo. If you have used Claude Code skills — a directory holding a skill.md that reshapes how the agent approaches a task — you are looking at the same machinery. This lesson was built by one. MCE's bet is that this machinery can breed itself: the recipe card is a file, files are editable, and editing files is the one thing coding agents are unambiguously good at.

What does such a skill directory actually look like on disk? Concretely, something like this — static pantry declared at the top, dynamic operators as instructions the base agent executes:

python
# skills/debug-ctx-v3/  — one MCE skill, instantiated as files
#
# skill.md            (static ρ: the mechanism, human-readable)
#   ## Static components
#   - knowledge/pitfalls.md      (curated pitfall list)
#   - lib/retrieve.py            (top-k retrieval over past rollouts)
#   ## Dynamic operators (F), run per input x:
#   1. Grep rollouts/ for errors matching x's stack trace
#   2. retrieve.py --k 8 --query "{x.summary}"
#   3. Format: pitfalls first, then retrieved traces, then x
#
# context.md           (dynamic: current best assembled context)
# rollouts/run_017.log  (dynamic: evidence the engineer learns from)
# scores.json           {"J_train": 0.84, "J_val": 0.82}
inner_step = lambda skill_dir, feedback: agent(
    tools=["Read","Write","Edit","Bash","Glob","Grep","TodoWrite"],
    prompt=f"Improve the context files in {skill_dir} following skill.md, "
           f"guided by this rollout feedback:\n{feedback}")

Now the contrast that locks in the concept. ACE enforces a heuristic structure rule: context shall be itemized bullets, merged deterministically — a good rule, chosen by humans, forever. MCE enforces no such rule: skills are free-form, storing whatever knowledge form serves the task best — and the skill and the skill-conditioned context evolve together, iteratively. If bullets win on this task family, MCE's outer loop can discover bullet-playbooks and effectively reinvent ACE. If a code library or a retrieval index wins, it discovers that instead. The mechanism has joined the artifact on the optimization table — that is rung 2 reaching toward rung 3 of the ladder.

DimensionACEMCE
What evolvescontext content onlymechanism (skill) + content, together
Structure rulefixed: itemized bulletsnone — free-form skills
Levels of optimizationonetwo (bi-level, inner/outer)
Update executordeterministic merge codeagents with T = {Read…TodoWrite}
Overfitting defensen/a (mechanism fixed)outer loop judged on Jval
Knowledge form(identifier, description) bulletswhatever the skill defines (files)

One caveat MCE inherits from every validation-judged system, worth stating because Chapter 8 will show it to you live: Jval is itself a noisy measurement. If the validation set is small, a mediocre skill can get lucky on it, and the outer loop will breed from a fluke — meta-overfitting's sneakier cousin, validation lottery. The defenses are the boring, honest ones: bigger validation sets, repeated evaluation, and preferring skills whose Jval is stable across re-runs over ones with a single shiny reading. Keep that in mind when you watch greedy search get fooled by noise in the showcase — the same disease, one rung down.

Two Loops, One Machine — Step the Inner, Step the Outer

Inner step: the base-level engineer edits the context files; Jtrain climbs noisily. Outer step: the skill is scored on validation (Jval), shelved into the history H, and the two best-by-val parents crossover into a new skill. Skills with a train–val gap > 0.12 get the red meta-overfit stamp — watch the outer loop refuse to breed from them.

Jtrain = 0.52 · Jval = — (not yet evaluated) · skills in H: 0
Misconception: "The outer loop is just the inner loop run twice." They optimize different objects on different data. The inner loop edits context files and reads Jtrain; the outer loop replaces the recipe itself and reads Jval. Collapse them — one loop, one score — and you either freeze the mechanism (back to ACE) or breed meta-overfit memorizers (skill-B wins). The two-loop, two-split structure is not architecture for its own sake; it is the anti-memorization defense.
Check: In MCE's bi-level optimization, what does the outer loop optimize, and on which data split is it scored?

Chapter 4: Meta-Harness — A Harness for Optimizing Harnesses

MCE put the context-management mechanism on the optimization table. Meta-Harness (Lee et al. 2026) moves one level deeper still: the optimized object is the code that determines and optimizes what information is stored, retrieved, and presented to the model. Not the playbook. Not the recipe for the playbook. The program — the harness source itself. The "Meta-" in the name says it plainly: it is a harness for optimizing harnesses. One agent system, whose job is to build and improve other agent systems, as code.

Four design details make this concrete, and each one earns its place. Design detail one: the proposer is itself a coding agent, and the final output is not a single winner but a collection of harness candidates on the Pareto frontier. Two ideas hide in that sentence, so unpack both. "The proposer is a coding agent" means new harnesses are written the way you would write them — an agent reads existing code, reasons about weaknesses, and writes a modified program. No mutation operators, no templates: the full expressiveness of code, wielded by something that understands code.

And the Pareto frontier? Build it from zero with two axes. A harness has (at least) two scores you care about: accuracy (higher = better) and token cost (lower = better). Plot every candidate as a dot: cost on the x-axis, accuracy on the y-axis. Candidate A dominates candidate B if A is at least as good on both axes and strictly better on one — cheaper and more accurate, say. If A dominates B, there is no reason B should ever exist: whatever you wanted from B, A gives you for less. The Pareto frontier is the set of candidates that nobody dominates. Concretely: a harness scoring 0.82 at 40k tokens and another scoring 0.74 at 9k tokens are both on the frontier — neither beats the other on both axes — while one scoring 0.73 at 30k tokens is dominated by each and gets dropped. Returning a frontier instead of a single champion is honest engineering: the "best" harness depends on your budget, so Meta-Harness hands you the whole menu of undominated trade-offs and lets deployment pick.

Work one tiny frontier by hand to make "dominated" mechanical. Five candidates, (cost in k-tokens, score): A(9, 0.74), B(30, 0.73), C(40, 0.82), D(22, 0.78), E(45, 0.80). Check each: A — nothing is both cheaper and better; frontier. D — cheaper than C/E, better than A/B on score… nothing beats it on both axes; frontier. C — highest score of all; frontier. B — D costs less (22 < 30) AND scores higher (0.78 > 0.73); dominated, drop. E — C costs less (40 < 45) AND scores higher (0.82 > 0.80); dominated, drop. The kept set {A, D, C} traces the staircase you will see in the sim. And note why the frontier beats collapsing to one number (like score − 0.01×cost): any fixed weighting bakes in a budget assumption, and a weighted sum can never even represent the deployment question "give me the best harness under 25k tokens" — the frontier answers every such question at once (here: D).

Design detail two: the entire execution history is accessible via the file system — and the proposer agent reads through it with commands like grep and cat, instead of shoveling everything into a single prompt context. Stop and enjoy this one, because you have seen it before. It is Pattern 2 from the previous lesson — file system as persistent memory — paying rent at the meta level. The history of every harness trial is far too large for any context window, so it lives as files, and the agent pulls exactly the slice it needs: grep "timeout" runs/*/log when investigating timeout-prone candidates. The optimizer eats its own dogfood: the same context discipline it is trying to build into harnesses is the discipline that lets it function at all.

Design detail three: a proposed harness is a dictionary in the file system, containing its own source code, its scores, its rollout trajectories, and its state updates. Every candidate is a self-describing bundle — not a loose diff plus a spreadsheet row somewhere, but one addressable object holding the code and the evidence about the code. When the proposer wants to learn from candidate #23's failures, everything is in one place: what it was, how it scored, exactly what happened when it ran.

Design detail four: the meta-harness loop iteratively creates new harnesses, and only qualified ones are kept. Propose, evaluate, filter, repeat. The filter is the frontier logic from detail one: a candidate that is dominated — worse on every axis than something already kept — is discarded, and the archive stays a clean frontier rather than a junk drawer of every attempt.

Notice what the dictionary bundling (detail three) quietly buys beyond tidiness: reproducibility and rollback. Because a candidate carries its own source, scores, and trajectories, any past harness can be re-run exactly, audited after the fact, or restored wholesale if a newer one misbehaves in deployment. When the thing being optimized is the production system itself, "every candidate is a checkpoint" stops being a convenience and becomes the safety story.

The four design details, on one line each: (1) a coding agent proposes; output = the Pareto frontier of candidates. (2) execution history lives in the file system, read via grep/cat — Pattern 2 at the meta level. (3) each candidate harness = a file-system dictionary of {source code, scores, trajectories, state updates}. (4) iterate; keep only the qualified (undominated).

Put the four details together and the outer loop is small enough to sketch faithfully:

python
# Meta-Harness outer loop — a harness for optimizing harnesses
kept = init_candidates()          # e.g. Terminus-KIRA, Terminus-2 as strong seeds
for t in range(MAX_ROUNDS):
    # (2) proposer reads history straight off the file system
    evidence = sh("grep -l 'timeout' runs/*/log | head; cat runs/best/scores.json")
    new_code = coding_agent("Improve on these harnesses. Evidence:\n" + evidence)
    scores   = evaluate(new_code)            # (score, token_cost, ...)
    # (3) each candidate = a self-describing dictionary in the fs
    save_dir(f"runs/h{t}/", source=new_code, scores=scores,
             trajectories=collect_rollouts(), state_updates=diff_log())
    # (4) keep only the qualified — the undominated
    kept = pareto_filter(kept + [(new_code, scores)])
return kept                        # the whole frontier, not one champion

Now the results — reported honestly, because the honest version teaches more. On text classification, Meta-Harness improves within a small number of iterations: modest domain, real gains, quickly. On TerminalBench-2 — a hard terminal-agent benchmark — the search was initialized from Terminus-KIRA and Terminus-2, two very strong existing harnesses. Read that nuance carefully: the system did not conjure a great terminal harness from a blank file. It started from the best human-engineered designs available and pushed them further. That is genuinely impressive — improving on strong baselines is where human experts spend careers — but it is refinement of a strong seed, not creation from nothing. Keep both truths in your head; hype dies in that gap.

ExperimentStarting pointResultThe honest read
Text classificationsimple candidatesimproves within a small number of iterationsreal gains, fast, in a modest domain
TerminalBench-2initialized from Terminus-KIRA + Terminus-2improves on the seedsrefinement of elite human designs, not from-scratch creation

The frontier output also changes how the result is consumed. A single-champion optimizer answers one question; the frontier answers a family of them — the accuracy-first team deploys the top-right candidate, the high-volume, cost-sensitive team the bottom-left one, both from the same search run. The optimizer's product is a menu, and the menu is reusable across deployments that the optimizer never knew about.

And then the sentence that survives the caveat, the takeaway Weng distills from this whole line of work: once harness design becomes an executable search space, a strong coding agent can exploit the same design space human engineers use. Nothing about harness engineering is privileged human ground — it is code, code is searchable, and a sufficiently strong coding agent can search it. The rest of this lesson, and the entire next one, is downstream of that sentence.

Run the search below. Each proposed harness lands as a dot in cost–score space; the frontier polyline pushes outward as batches arrive; dominated dots fade. Tap any dot to inspect its file-system dictionary. Then try the init from strong baselines toggle — that is the TerminalBench-2 nuance made visible: the frontier starts high and the search refines it, rather than climbing from the floor.

Meta-Harness Pareto Search — Watch the Frontier Push Out

x = token cost per task (lower is better) · y = score (higher is better). Teal dots sit on the Pareto frontier; faded dots are dominated (something is better on both axes). Tap a dot to open its candidate dictionary below.

tap a candidate dot → its file-system dictionary appears here
Misconception: "The search proved agents design better harnesses than people." Not what the evidence says. Text classification improved from simple starts; the TerminalBench-2 result started from Terminus-KIRA and Terminus-2 — elite human designs — and improved on them. The demonstrated claim is narrower and still remarkable: given an executable search space and strong seeds, a coding agent can find harness variants that beat the seeds. From-scratch superiority remains undemonstrated.
Check: Meta-Harness outputs a collection of candidates on the Pareto frontier. What makes a candidate belong to that frontier?

Chapter 5: Handcrafted Workflows — AI Scientist, ScientistOne, Autodata

Climb from context (rung 2) to workflow (rung 3): the shape of the run itself — which stages happen, in what order, checked by what. Before anyone searched this space with algorithms, domain experts handcrafted workflows, and the flagship domain is auto-research: systems that try to do science. Three systems, three lessons — and together they set up exactly why search had to come next.

The AI Scientist (Lu et al. 2026 — published in Nature, a milestone in itself) is a pipeline of six handcrafted stages, each stage's output feeding the next stage's input:

1 · Propose research ideas
in: literature, seed topics → out: ranked idea descriptions
2 · Write code
in: the chosen idea → out: an executable experiment implementation
3 · Run experiments
in: code + compute → out: logs, metrics, plots
4 · Analyze results
in: raw metrics → out: findings, comparisons, claims
5 · Write manuscript
in: findings + plots → out: a full paper draft
6 · Peer review
in: the draft → out: scores + critiques, looped back to revise

Every arrow in that diagram is a human design decision: an expert decided research decomposes into these six stages, in this order, with review at the end. It works — the pipeline coordinates a large portion of a real research loop. But notice what it inherits: every blind spot of its designers, frozen in. If great research sometimes needs analysis before more coding, or two idea stages, this pipeline cannot discover that. Handcrafting buys reliability and pays in rigidity.

There is also a quiet connection back to Chapters 1–3 hiding in the pipeline's shape: a staged workflow is itself a context-management strategy. Each stage receives a purpose-built context — the coding stage sees the chosen idea, not the whole literature scan; the review stage sees the manuscript, not the raw experiment logs. Decomposing the job into stages with typed handoffs is what keeps any single context window small and on-topic. Rungs 2 and 3 of the ladder are not neighbors by accident; workflow design and context engineering are two grips on the same problem.

ScientistOne (Meng et al. 2026) keeps the handcrafted pipeline but redesigns it around one constraint: verifiability. The motivating fear is concrete — a paper-writing system can produce a beautifully plausible manuscript with fabricated citations, invented numbers, or conclusions its own experiments do not support. Fluent text is cheap; grounded text is not. So in ScientistOne, every claim must trace to an evidence source, and claims are audited by Chain-of-Evidence checks — an explicit chain from each sentence back to the artifact that justifies it. The four claim types, all named, all audited:

Claim typeWhat must trace to evidence
Citation"Smith et al. showed X" → the cited paper exists and actually says X
Numerical"accuracy improved 4.2%" → the number appears in a real experiment log
Methodological"we trained with lr=3e-4 for 10 epochs" → the config/code actually did that
Conclusion"therefore X causes Y" → the inference is supported by the presented results

Mechanically, a Chain-of-Evidence check is an audit pass over the manuscript: extract each claim, classify it into one of the four types, resolve its declared evidence pointer (a citation entry, a log line, a config file, a results table), and verify the claim against the artifact. A claim whose chain does not resolve fails the audit — and a failed audit blocks the manuscript, the same way a failing test blocks a merge. Trust becomes a build step.

Why this obsession? Because a research workflow's failure mode is not crashing — it is succeeding at the wrong thing: producing artifacts that look like science without being science. Hold that thought; the next lesson's catalog of failure modes (over-optimism, "p-hacking and eureka-ing") is this fear, observed in the wild. ScientistOne's answer is architectural: make the workflow itself demand receipts at every stage, rather than hoping the model is honest.

The third system moves from writing papers to making data. Autodata (Kulikov et al. 2026) is an agentic data scientist for generating training and evaluation data. Its handcrafted workflow is a main agent managing four roles: a challenger that proposes problems, a weak solver, a strong solver, and a verifier/judge that checks solutions. The goal is to synthesize data at the "just right" level of difficulty — defined with beautiful precision: the strong solver succeeds AND the weak solver fails.

weak solver ✓weak solver ✗
strong solver ✓too easy — teaches nothingGOLD: keep this problem
strong solver ✗suspicious — broken problem or lucky guesstoo hard — no signal to learn from

Why is that band the gold zone for training data? Run the four cases. Both solvers pass → the problem is too easy; the weak model already knows this; training on it teaches nothing. Both fail → too hard; there is no correct trace to learn from, no signal, just noise. Weak passes but strong fails → suspicious; likely a broken problem or a lucky guess — the verifier should frown. Strong passes and weak fails → exactly the material sitting at the edge of the weak model's ability, with a verified correct solution available from the strong solver to learn from. If you know curriculum learning, this is its automated cousin: keep the learner in the zone where problems are hard enough to stretch but solvable with the available guidance. And the workflow is adaptive: the challenger prompt is updated iteratively according to feedback from the solvers and the verifier — too many both-pass outcomes and the challenger is told to push harder; too many both-fail and it eases off. A thermostat for difficulty.

Notice also what the whole loop leans on: the verifier/judge. Every kept problem is kept because the verifier said the strong solver's answer was right and the weak solver's was wrong. If the verifier is weak — fooled by confident-sounding wrong answers, or exploitable by degenerate problems that look hard but have trivial shortcuts — then the "gold band" fills with fool's gold, and the fine-tuned weak solver learns to imitate whatever fooled the judge. This is your first sighting of a theme the next lesson makes central: a self-referential data or optimization loop is only as trustworthy as the evaluator standing outside it.

Now the limitation, and it deserves careful unpacking because Weng flags it explicitly: the synthesized tasks are used to fine-tune weak solvers, but not strong ones. Trace the consequence. The loop's ceiling is the strong solver — problems only count when the strong solver can crack them, so nothing in the data ever exceeds what the strong model already knows. If the loop cannot iteratively improve the strong model, then what is really happening is closer to indirect distillation: the strong model's capabilities are being compressed into the weak model, over a cleverly generated prompt distribution rather than a static dataset. Distillation is useful! But it is capability transfer, not capability creation — the system's total knowledge never rises above its strongest member. That is why Weng says it has "less RSI flavor": a recursive self-improvement loop must lift its own ceiling, and this one, by construction, polishes the floor.

The "just right" criterion, as a formula you can act on: keep problem p when strong(p) = pass AND weak(p) = fail. That single predicate is a difficulty filter, a data-quality gate, and a curriculum, all in four words. Everything else in Autodata — the challenger updates, the verifier — exists to steer problem generation into that band and keep it honest.

The simulation below is Autodata's heartbeat. Problems flow from the challenger through both solver lanes; each outcome lands in a 2×2 grid; the difficulty trace at the bottom shows the controller steering into the gold band. Drag the difficulty slider to perturb the challenger and watch auto-adjust pull it back — then switch auto-adjust off and see the stream drift into worthless too-easy or too-hard territory.

The Difficulty Dial — Steering Into the (strong ✓, weak ✗) Band

Problems cross the weak and strong solver lanes and land in the 2×2 outcome grid — the glowing cell is the gold zone. The bottom trace shows challenger difficulty steering into the gold band. Perturb it; watch it re-converge.

Challenger difficulty 0.20
Misconception: "Synthetic data loops are self-improvement." Only if the loop can improve its own ceiling. Autodata fine-tunes the weak solver toward the strong one — capability flows downhill. For genuine RSI flavor the strong model itself would need to get better from the loop's output, and that is precisely the step Autodata does not take. When you evaluate any data-generation loop, ask one question first: who gets fine-tuned?
Check: In Autodata, a synthesized problem is at the "just right" difficulty when…

Chapter 6: ADAS — The Meta-Agent That Programs Agents

Chapter 5 closed on handcrafting's tax: every workflow inherits its designers' blind spots. And the design space for workflows is enormous — every possible arrangement of stages, checks, retries, ensembles, delegations. Weng's framing is the pivotal move of this whole lesson: naturally, we can think of workflow design as a search problem — and therefore we should be able to find good solutions by algorithms rather than only crafting them by hand. The moment you say "search problem," a century of optimization machinery wakes up and asks for the job.

Automated Design of Agentic Systems (ADAS; Hu, Lu & Clune 2025) is the first system in our story to take that job seriously. It formulates agent design itself as an optimization problem — meta-agent search — where a meta-agent proposes new designs of agentic workflows. One agent whose entire occupation is inventing other agents. The loop has six steps, and every one carries a design decision worth understanding:

1 · Initialize the archive
seed with simple agents: CoT (chain-of-thought), self-refine
2 · Meta-agent programs a new agent — in code
inspired by existing solutions in the archive
3 · Describe first, then implement
a high-level description of the workflow, then the code
4 · Two self-refine passes checking novelty
the model critiques its own draft, then refines it (×2)
5 · Evaluate; successful candidates join the archive
run on the benchmark; keepers become future inspiration
↻ repeat until max iterations

Walk the interesting steps. The archive (step 1) starts humble — chain-of-thought prompting and self-refine, the "hello world" agents of the field. It is not just storage; it is the gene pool. Every future design is proposed "inspired by existing solutions in the archive," so what the archive contains shapes what can be dreamed up next. As it fills with stranger, stronger designs, the inspiration pool compounds — late-iteration proposals combine ideas no single early agent contained.

The choice of seeds matters more than it looks, precisely because of that compounding. CoT and self-refine are good seeds for three reasons: they are simple (little code to imitate badly), they are well-understood (the meta-agent has seen thousands of descriptions of them in pretraining), and they are cheap to execute (early evaluations don't burn the budget). Seed the archive with one baroque, expensive agent instead, and every early proposal inherits its baroqueness — the gene pool is biased before the search begins.

Describe-then-implement (step 3) mirrors how good engineers work: design doc before code. Forcing the meta-agent to first write a high-level description of the new workflow — what it does and why it might win — and only then implement it in code separates invention from construction. A model that jumps straight to code tends to produce a mashup of syntax from its inspirations; a model that must first articulate the idea produces code in service of an idea.

Self-refine (step 4) is a named technique, so define it: ask the model to provide feedback on its own output, then ask the same model to refine that output based on the feedback (Madaan et al. 2023). ADAS runs the draft program through two self-refine passes, specifically checking novelty — is this genuinely a new design, or the archive's greatest hits reshuffled? Novelty checking is search-diversity maintenance: without it, the meta-agent drifts toward proposing tiny variations of the current best, and the search collapses into a crowd of near-clones (a failure you will meet again, formally, in the next lesson as diversity collapse).

Why exactly two passes, not one or five? Self-refine has sharply diminishing returns: the first pass catches most surface flaws (bugs, unoriginality the model can see once asked), the second catches what the first pass introduced — and beyond that, the model mostly rephrases its own approval while the token bill keeps growing. Two is the empirical knee of the curve: enough scrutiny to matter, cheap enough to run on every candidate.

Now the thesis hiding in three words of step 2: all in code. Why does it matter that ADAS's meta-agent writes programs rather than prompt strings? Three compounding reasons. First, the archive becomes executable — every stored design can be run, scored, and verified at any time; nothing is a vague idea. Second, new designs can compose old ones the way software composes: import the ensemble logic from one archived agent, wrap it in another's retry loop, add a fresh voting rule — try expressing that as a prompt-string edit. Third, the search space becomes the space of programs, which is universal: any workflow you can specify at all, you can specify as code. This is the same "code is the universal language" conviction that powers Meta-Harness one rung up, and it is the thesis of the entire optimization arc of this lesson.

python
# ADAS meta-loop, structurally faithful sketch
archive = [Agent("CoT", cot_code, score=eval_on(benchmark, cot_code)),
           Agent("SelfRefine", sr_code, score=eval_on(benchmark, sr_code))]

for k in range(MAX_ITERS):
    # (2) propose, inspired by the archive -- in code
    inspiration = render(archive)               # designs + scores as text
    # (3) describe FIRST, then implement
    desc = meta_agent("Given these agent designs and scores:\n" + inspiration
                      + "\nDescribe a NOVEL agentic workflow likely to score higher.")
    code = meta_agent("Implement this design as runnable code:\n" + desc)
    # (4) two self-refine passes, checking novelty
    for _ in range(2):
        feedback = meta_agent("Critique this program. Is it truly novel "
                              "vs the archive? Any bugs?\n" + code)
        code     = meta_agent("Refine the program using this feedback:\n"
                              + feedback + "\n" + code)
    # (5) evaluate; only successful candidates join the gene pool
    score = eval_on(benchmark, code)
    if score > min(a.score for a in archive):
        archive.append(Agent(name_of(desc), code, score))

A worked micro-run makes the dynamics concrete. Start: archive = {CoT: 0.62, SR: 0.66}. Run three iterations:

IterParents (inspiration)CandidateScore vs parent avgVerdictBest so far
1CoT, SRCoT+SC (self-consistency voting)0.69 > 0.640kept0.69
2SR, CoT+SCRefl (reflection wrapper)0.63 < 0.675discarded0.69
3CoT+SC, SREns3 (3-sample ensemble)0.73 > 0.675kept0.73

Two keeps, one discard — and the kept designs are already compositions the seed archive could not express: CoT+SC exists only because both parents were readable code. Also note the quiet cost column that isn't drawn: every iteration pays a full benchmark evaluation, which in practice dwarfs the cost of proposing. Evaluation, not generation, is the budget that limits archive search — the same economics you saw in Meta-Harness, and the reason ADAS stops at a max iteration count rather than searching to convergence. (Contrast AFlow next chapter, which stops on a measured plateau — a budget rule versus an evidence rule.)

Watch the loop run below. Each iteration draws inspiration lines from two or three archive parents, a candidate card appears in the eval bay, gets stamped with a score, and either slides into the archive with a green glow or fades away. The sparkline tracks the best score found so far — the signature staircase of an archive-driven search: flat stretches, then a jump when a genuinely new composition lands.

The Archive Grows — Meta-Agent Search, Iteration by Iteration

Purple lines = inspiration drawn from parent designs. The candidate is evaluated in the bay at right: kept designs join the archive shelf; discarded ones fade. Bottom: best-score-so-far sparkline.

Misconception: "The archive stores prompts, and the meta-agent writes better prompts." No — both halves are wrong, and the difference is the whole point. The archive stores executable programs (complete agentic workflows as code, with scores), and the meta-agent programs new agents. Prompts cannot import each other, cannot be composed with a retry loop, cannot be executed and scored as artifacts. Code can. "All in code" is what turns agent design from wordsmithing into a searchable, composable space.
Check: In ADAS, what does the archive store, and what does the meta-agent produce each iteration?

Chapter 7: AFlow — MCTS over Workflow Graphs

ADAS searches the open space of programs with archive inspiration — maximum freedom, minimum structure. AFlow (Zhang et al. 2025) makes the opposite bet: constrain the representation, then bring a principled search algorithm. The representation: an agentic workflow is a graph, where nodes represent LLM-invoking actions (generate, critique, ensemble-vote, revise…) and edges implement the logical operations in code — the deterministic glue deciding what flows where, what gets retried, what gets merged. Model calls at the nodes, plain code on the edges. A workflow edit is a graph edit: add a node, rewire an edge, swap an operator. Suddenly "improve the workflow" has the crisp structure that search algorithms love.

The algorithm AFlow brings is MCTS — Monte Carlo Tree Search — and you can learn everything you need about it right here, in this context. Picture a tree. The root is a starting workflow. Each child is a modified version of its parent — one graph edit away. Every node carries a score from actually running that workflow on evaluation tasks. Searching means repeatedly deciding: which existing node should I modify next? That single decision — where to grow the tree — is the entire art of tree search.

Here are AFlow's six steps, in full fidelity. (1) Initialize the starting workflow W0 in the tree from a template. (2) Select a workflow node using a soft mixture of score and uniform exploration. (3) Expand: ask an LLM to produce a modified workflow, conditioned on its evaluation performance — the model sees how the parent scored and where it failed, so the edit targets actual weaknesses. (4) Execute and evaluate the new workflow on real tasks. (5) Add it back to the tree if it shows improvement, within a budget of N rounds. (6) Repeat steps 2–5; stop when the top-k average score plateaus or the budget is hit.

1 · Initialize W0
root workflow from a template
2 · SELECT
sample a node: λ·uniform + (1−λ)·score-proportional
3 · EXPAND
LLM writes a modified workflow, conditioned on the parent's eval results
4 · EXECUTE + EVALUATE
run the new workflow on real tasks; score it
5 · ADD BACK if improved
within a budget of N rounds; otherwise drop
↻ repeat — stop at top-k plateau or budget

Step 2 hides the deepest idea, so slow down. Why not simply always pick the best-scoring node — pure greed? Because scores are noisy and the landscape has local optima. A workflow that scored 0.74 today might have been lucky on this eval batch; a 0.58 workflow might be one edit from greatness. Pure greed piles every expansion onto the current champion, digging one hole deeper and deeper while the actual peak sits two hills over, never visited. This is the exploration–exploitation trade-off: exploitation says "spend effort where scores are already high," exploration says "spend some effort everywhere, because your knowledge is incomplete." Every good search algorithm is a treaty between the two.

AFlow's treaty is the soft mixture: blend a score-proportional distribution (exploitation) with a uniform distribution (exploration), and sample from the blend. With mixing weight λ (lambda — the exploration knob, between 0 and 1) and n candidate nodes with scores si:

P(select node i) = λ · (1/n)  +  (1 − λ) · si / Σj sj

Read each piece. 1/n is the uniform vote: every node equally likely — pure curiosity, blind to scores. si / Σj sj is the score-proportional vote: each node weighted by its share of the total score — pure merit. λ is the trust slider between them: λ = 1 is a lottery, λ = 0 is a meritocracy with no second chances. Small λ (say 0.2) means "mostly follow the scores, but guarantee every node — even the worst — a real chance of being revisited." That guaranteed floor is what rescues the unlucky 0.58 node from oblivion.

Now the mandatory arithmetic — do it by hand once and the formula is yours forever. The tree holds four candidate workflows with scores 0.62, 0.71, 0.58, 0.74, and λ = 0.2. First the total score: 0.62 + 0.71 + 0.58 + 0.74 = 2.65. The uniform term is the same for everyone: λ · (1/n) = 0.2 × (1/4) = 0.050. Then each node's merit term, scaled by (1 − λ) = 0.8:

P(W1) = 0.050 + 0.8 × (0.62/2.65) = 0.050 + 0.8 × 0.234 = 0.050 + 0.187 = 0.237
P(W2) = 0.050 + 0.8 × (0.71/2.65) = 0.050 + 0.8 × 0.268 = 0.050 + 0.214 = 0.264
P(W3) = 0.050 + 0.8 × (0.58/2.65) = 0.050 + 0.8 × 0.219 = 0.050 + 0.175 = 0.225
P(W4) = 0.050 + 0.8 × (0.74/2.65) = 0.050 + 0.8 × 0.279 = 0.050 + 0.223 = 0.273

Sanity checks: the four probabilities sum to 0.237 + 0.264 + 0.225 + 0.273 = 0.999 ≈ 1 (rounding). The best node W4 leads at 27.3% — but the worst node W3 still gets 22.5%, not zero. Under pure greed W4 would get 100% and W3 would never be touched again. The mixture compresses a 0.16 score gap into a 4.8-point probability gap: merit tilts the odds, exploration keeps every door ajar.

Round two — watch the distribution update. Say the sampler picked W4, the LLM produced a modified child, and it evaluated to 0.70 — an improvement over some of the tree, so it is added (step 5). Now n = 5, total = 2.65 + 0.70 = 3.35, uniform term = 0.2 × (1/5) = 0.040. Recompute: P(W1) = 0.040 + 0.8×(0.62/3.35) = 0.040 + 0.148 = 0.188; P(W2) = 0.040 + 0.8×(0.71/3.35) = 0.040 + 0.170 = 0.210; P(W3) = 0.040 + 0.8×(0.58/3.35) = 0.040 + 0.139 = 0.179; P(W4) = 0.040 + 0.8×(0.74/3.35) = 0.040 + 0.177 = 0.217; P(W5) = 0.040 + 0.8×(0.70/3.35) = 0.040 + 0.167 = 0.207. Sum: 1.001 ≈ 1 ✓. Every incumbent's probability dropped — the newcomer claimed its share — and the ranking stayed merit-ordered with the floor intact. That is the algorithm breathing: each round redistributes attention over a growing tree.

What does "conditioned on its evaluation performance" (step 3) buy? Blind mutation would change the workflow at random and hope. AFlow instead hands the LLM the parent's report card: "this workflow scored 0.71 overall but failed 8 of 9 multi-hop questions." The natural edit — add a decompose node before the answer node — targets the actual weakness. It is ADAS's "inspired by the archive" idea sharpened into "informed by this exact parent's failures": expansion becomes directed repair, not random drift.

Here is the selection rule as running code — verbose with the arithmetic laid out, then the compact form:

python
def soft_mixture_select(scores, lam, rng):
    # P(i) = lam * (1/n)  +  (1 - lam) * s_i / sum(s)
    n, tot = len(scores), sum(scores)
    probs = []
    for s in scores:
        uniform = lam * (1.0 / n)          # curiosity: same for everyone
        merit   = (1 - lam) * (s / tot)    # each node's share of total score
        probs.append(uniform + merit)
    r, acc = rng.random(), 0.0             # SAMPLE — never argmax
    for i, p in enumerate(probs):
        acc += p
        if r <= acc: return i
    return n - 1

soft_mixture_select([0.62, 0.71, 0.58, 0.74], lam=0.2, rng=rng)
# probs computed inside: [0.237, 0.264, 0.225, 0.273]  ← the worked example

# compact (numpy):
probs = lam / len(s) + (1 - lam) * np.array(s) / sum(s)
pick  = np.random.choice(len(s), p=probs)

To feel what λ does, recompute round one at λ = 0.6 and compare. Uniform term: 0.6 × 0.25 = 0.150; merit scale: 0.4. P(W4) = 0.150 + 0.4 × 0.279 = 0.262; P(W3) = 0.150 + 0.4 × 0.219 = 0.238. Side by side:

Node (score)λ = 0 (pure greed)λ = 0.2λ = 0.6λ = 1 (lottery)
W1 (0.62)00.2370.2440.250
W2 (0.71)00.2640.2570.250
W3 (0.58)00.2250.2380.250
W4 (0.74)1.0000.2730.2620.250

Read the columns as a dial: λ = 0 is a monarchy (the champion gets everything), λ = 1 is a lottery (scores mean nothing), and the useful settings in between let merit lead by a margin proportional to (1 − λ). At λ = 0.2 the best node is 1.21× likelier than the worst; at λ = 0.6 only 1.10×. Tuning λ is tuning how loudly the scores are allowed to speak.

Two guardrails complete the design. The add-back rule (step 5) admits a new workflow only if it shows improvement within a budget of N rounds — a candidate that cannot demonstrate progress within its allowance is dropped, keeping the tree from silting up with mediocrity. And the stopping rule (step 6) watches the top-k average: not just the single best score (one lucky outlier could mask stagnation) but the average of the k best. When that average plateaus — several rounds without meaningful movement — the search has converged; spend no more.

Results: in experiments on QA, code, and math tasks, AFlow showed decent improvement over manually designed workflows — and over ADAS. That second comparison is the instructive one. ADAS searches the open space of arbitrary programs with archive inspiration — unbounded freedom, but its search signal is loose ("be inspired, be novel"). AFlow constrains everything to graph edits guided by tree statistics — a smaller space, but every candidate is one principled step from a scored parent, and selection provably balances exploration with merit. On these benchmarks, structure beat freedom: the constrained, statistics-guided search found better workflows than the unconstrained, inspiration-guided one. Not a universal law — but a deep lesson about search: the value of a bigger space is bounded by your ability to navigate it.

Grow the tree yourself below. Step through SELECT → EXPAND → EVALUATE → KEEP/PRUNE and watch the live soft-mixture arithmetic under the canvas — the same computation you just did by hand, updating as the tree grows. Push λ to the extremes: at 0 the search tunnels on one branch; at 1 it wanders aimlessly. The sweet spot is where merit leads and curiosity chaperones.

Grow the Tree — MCTS over Workflows, One Phase at a Time

Node color runs low scorehigh score. SELECT highlights the sampled node (purple ring) using the soft mixture below; EXPAND asks the LLM for a modified child; EVALUATE stamps its score; KEEP/PRUNE applies the add-back rule. A banner announces the top-3 plateau.

λ exploration 0.20
selection probabilities appear here as the tree grows
Misconception: "MCTS selection = pick the highest-scoring node." That is pure greed, and it is exactly what the soft mixture exists to prevent. AFlow samples from λ·uniform + (1−λ)·score-proportional — the champion is merely more likely, never certain, and the weakest node keeps a guaranteed floor of λ/n. Greedy search digs one hole deeper; mixed search keeps scouts on every hill.
Check: How does AFlow select which workflow node to expand next?

Chapter 8: The Workflow Search Playground

Everything so far has been someone else's search. Now run your own. The task: an agent must answer a 20-question quiz, and you get to design its workflow by chaining up to three operators from a library of six — direct (just answer), CoT (think step by step), self-refine (critique and revise), ensemble-3 (three samples, majority vote), verify-then-fix (check the answer, repair if wrong), and decompose (split the question first). Each operator carries a hidden true quality and a token cost, and some pairs have synergies — refinement after reasoning is worth more than refinement alone. With sequences of 1 to 3 operators, the space holds 6 + 36 + 216 = 258 workflows. Small enough to draw, big enough that brute force at ~5 tokens a pop would bankrupt the point.

The catch that makes this a real search problem: you never see true scores. Every evaluation returns true score + noise — a finite eval batch, so a mediocre workflow can test brilliantly once and a great one can stumble. Your budget is limited (10–100 evals). Three strategies compete, and you can run them side by side on identical random seeds: random search samples a fresh workflow every eval and keeps the best measurement it has seen. greedy hill-climb starts somewhere, mutates one operator at a time, and accepts any mutation that measures better — chasing single noisy readings. MCTS-lite is Chapter 7 distilled: a tree over operator prefixes, soft-mixture selection, and — the quiet superpower — revisit averaging: visiting the same branch again and again, so each branch's estimate is a mean of many noisy readings instead of one lucky roll.

The y-axis below plots the true quality of each strategy's current champion — the score you would actually get deploying its pick (the strategies themselves never see this line; neither would you in production). Two experiments to run. Experiment one: low noise, budget 60, all three strategies. Greedy should look respectable — with clean measurements, hill-climbing climbs. Experiment two: crank eval noise to high, reset seeds, run again. Watch greedy's curve stall or even sag: it accepts mutations on flukes and then can't escape, because its inflated champion measurement beats every honest challenger. MCTS-lite keeps grinding upward — averaging repeated visits burns the noise out of its estimates. You are watching, live, why AFlow carries tree statistics instead of a greedy pointer.

Workflow Search Playground — Three Strategies, One Hidden Landscape

Toggle strategies (all three can race at once), set the eval budget and noise, then Run. Top strip: the leading strategy's champion pipeline. Chart: true quality of each champion vs evals spent. Right meter: tokens burned. Reset seeds replays the identical randomness for fair A/B tests.

Eval budget 60
Eval noise med

Three structured experiments, if you want the guided tour:

Experiment 1 — clean world. Noise low, budget 60, all three on. Expected: greedy climbs fastest early (trustworthy local moves), all three converge near the top. Search is easy when measurements are honest.
Experiment 2 — noisy world. Noise high, Reset seeds, Run. Expected: greedy stalls or locks onto a mediocre pipeline (a fluke got enthroned); MCTS-lite keeps climbing; random lands mid-pack. Re-run several times — the variance of greedy's final pick is the real tell.
Experiment 3 — starvation. Noise med, budget 15. Expected: random is surprisingly competitive — tree statistics need revisits to pay off, and 15 evals barely builds any. Structure needs budget to beat luck; below that, everything is luck.
why revisits beat single readings:  SE = σ / √n
σ = 0.12 (high noise), n = 5 visits:  SE = 0.12 / √5 = 0.12 / 2.236 ≈ 0.054 — the estimate is 2.2× sharper than any single eval

Debrief — what you should have seen. At low noise, all three strategies climb, and greedy often climbs fastest early: local moves on trustworthy measurements are efficient. Random search rises in bursts — it has no memory, but 60 darts at 258 targets land somewhere decent. The separation happens at high noise. Greedy's acceptance test — "did this single measurement beat my champion's single measurement?" — becomes a coin flip decided by luck, and one inflated fluke poisons it permanently: the champion's lucky score becomes a bar no honest measurement clears. Random search is noise-tolerant but wasteful, never concentrating effort. MCTS-lite alone reduces noise, because a branch visited five times has its estimate averaged over five draws — the standard error shrinks by √5 ≈ 2.2×. Tree statistics are not decoration; they are a noise filter.

Now that you have searched blind, here is the ground truth you were searching over — the hidden operator table (reading it before searching would have spoiled the point):

OperatorTrue quality pullToken costSynergy
direct0.551
CoT0.683+0.03 when followed by refine
refine0.725(receives the CoT bonus)
ens-30.749+0.02 after decompose
verify0.766+0.04 when placed last
decompose0.704(feeds the ens-3 bonus)

Each added operator pulls the workflow score 55% of the way toward its own quality (diminishing returns — the second good operator helps less than the first), stacking the same operator three times is penalized, and the synergies reward ordered pairs. The best short pipelines live around CoT → refine → verify: reasoning, then repair, then a final check — which is, not coincidentally, the shape of most production agent workflows. If your search found something in that family, it found the real optimum.

Misconception: "With enough evals, any strategy wins — noise just slows things down." Not for greedy. Noise doesn't slow greedy; it corrupts its state. Once a fluke measurement is enthroned as the champion's score, every honest challenger loses to a number that was never real, and more evals just re-confirm the mistake. Random and MCTS degrade gracefully with noise; greedy fails structurally. The fix is always the same: compare averages, never single readings.

Second observation worth pausing on: the tokens meter. Ensemble-heavy workflows score well but burn 2–3× the tokens of lean ones — on a fixed token budget (rather than eval count), the "best" workflow changes entirely. You have rediscovered why Meta-Harness returns a Pareto frontier instead of a single champion: score-at-any-cost and score-per-token crown different kings, and only the frontier holds both answers.

Third: everything you just searched was fixed in advance — six operators, hand-defined, immutable. The search chose arrangements; it could not invent a seventh operator. Now imagine handing that limitation to a coding agent: "here are the six operators as code; write new ones." The search space stops being 258 points and becomes the space of programs — and you have walked the exact conceptual bridge from AFlow (search over a fixed graph vocabulary) to ADAS and Meta-Harness (search over code itself). The ladder's top rungs are this playground with the walls removed.

For the record, here is the machinery under the hood — the same seeded-noise evaluation and the greedy/MCTS-lite cores you just raced (abridged to essentials):

python
def evaluate(wf, rng, noise):
    # true_score is HIDDEN from strategies; they see only this noisy reading
    return true_score(wf) + rng.gauss(0, noise), token_cost(wf)

def greedy_step(state, rng, noise):
    cand = mutate(state.champion, rng)          # swap/add/drop one operator
    meas, cost = evaluate(cand, rng, noise)
    if meas > state.champ_meas:                 # trusts ONE noisy reading --
        state.champion, state.champ_meas = cand, meas   # flukes get enthroned
    return cost

def mcts_step(tree, rng, noise, lam=0.25):
    node, path = tree.root, []
    while node.children and rng.random() > 0.3:
        node = soft_mixture_pick(node.children, lam, rng)  # ch.7 formula
        path.append(node)
    child = node.expand_or_revisit(rng)
    meas, cost = evaluate(child.workflow, rng, noise)
    for n in path + [child]:                    # REVISIT AVERAGING:
        n.n += 1                                # running mean over many
        n.mean += (meas - n.mean) / n.n         # noisy draws -> noise shrinks
    return cost
The showcase's one takeaway: under noise, the winning strategy is the one whose beliefs are averages rather than single readings. Greedy stores one number per champion; MCTS stores (mean, count) per branch. That bookkeeping difference — two fields instead of one — is the entire gap between the curves you just watched.

Chapter 9: Connections & Cheat Sheet

Eight systems crossed this lesson. Lay them on one table and the ladder from Chapter 0 reappears as the first column — every method is defined by the object it optimizes, who proposes changes, who judges them, and how the search moves. (Scroll the table sideways on a phone.)

MethodObject optimized (rung)Who proposesWho evaluatesSearch methodKey design choiceMain limit
ACEcontext playbook (2)Reflector (LLM)rollout outcomesincremental accumulationdeterministic itemized mergemechanism is handcrafted
MCEcontext mechanism + content (2–3)meta-agent crossoverJtrain inner, Jval outerbi-level skill evolutionfree-form skills as filesneeds clean train/val splits
Meta-Harnessharness code (4)coding agentmulti-objective evalsiterate + keep qualifiedPareto frontier outputstrong-seed init on hard tasks
AI Scientistnothing — fixed workflow (3)human expertspeer-review stagenone (handcrafted)6-stage research pipelinedesigners' blind spots frozen in
ScientistOnenothing — fixed workflow (3)human expertsChain-of-Evidence auditsnone (handcrafted)every claim traces to evidenceverification adds cost/rigidity
Autodatachallenger prompt (1–3)challenger agentverifier + solver pairiterative feedbackkeep (strong ✓, weak ✗) bandnever improves the strong solver
ADASworkflow programs (3)meta-agent (code)benchmark evalarchive-inspired proposalsall in code + novelty self-refineloose search signal
AFlowworkflow graph (3)LLM expansionexecute + evaluateMCTS, soft-mixture selectgraph nodes/code edgesbounded by graph vocabulary
The Ladder, Revisited — Eight Methods Pinned to Their Rungs

The Chapter 0 staircase, one last time — now with every method you've met pinned where it lives. Note MCE straddling rungs 2–3 and Meta-Harness reaching from 4 toward 5: the boundaries were always soft.

And a decision guide — when you face a real system next week, which of these do you actually reach for?

Your situationReach for
Agent re-derives the same insights every run; summaries keep losing detailACE-style itemized playbook + deterministic merge
You suspect your context format itself is wrong, and you have train/val task splitsMCE-style bi-level skill evolution
Fixed set of workflow building blocks, cheap noisy evals, want principled searchAFlow-style MCTS with soft-mixture selection
Open-ended workflow design, budget for many benchmark evalsADAS-style archive + meta-agent proposals
Multiple objectives (score, cost, latency); strong existing harness to improveMeta-Harness-style Pareto search seeded from your baseline
Need training data for a weaker model, have a stronger one + a trustworthy verifierAutodata-style challenger/solver/judge loop
Output artifacts must be trustworthy (reports, papers, claims)ScientistOne-style Chain-of-Evidence gating

Now the cheat sheet — every formula and list from the lesson, compressed to what you should be able to reproduce on a whiteboard from memory.

The ladder: instruction prompts → structured context → workflow → harness code → optimizer code. As models get more capable, the optimized object gets more complex and the method more generic. First question for any paper: what object does it optimize?
ACE (context, fixed mechanism): Generator produces trajectories referencing bullets · Reflector distills insights from successes AND failures · Curator emits itemized (identifier, description) deltas · deterministic code merges them · periodic dedup. The merge cannot damage what it does not touch — that defeats context collapse and brevity bias.
MCE (mechanism evolves too): skill s defines cs = (ρs, Fs), context c = Fs(x; ρs) — pantry + cooking.
Inner: cs* = argmaxcs Jtrain(cs; s)  — perfect the dish, on training data
Outer: s* = argmaxs ∈ S Jval(cs*)  — pick the recipe, on validation (blocks meta-overfitting)
sk = crossover(τ, Hk−1)  ·  ck = engineer(τ, sk; c*k−1, Rk)  ·  T = {Read, Write, Edit, Bash, Glob, Grep, TodoWrite}
Meta-Harness (harness code): coding-agent proposer · output = Pareto frontier (kept = dominated by nobody) · execution history as files, read via grep/cat · each candidate = a dictionary {source, scores, trajectories, state updates} · iterate, keep only qualified. Takeaway: once harness design is an executable search space, a strong coding agent exploits the same design space human engineers use.
Autodata's gold band: keep problem p iff strong(p) = pass AND weak(p) = fail. Fine-tunes weak solvers only → closer to indirect distillation than RSI.
ADAS loop: archive(CoT, self-refine) → meta-agent programs new agent in code, archive-inspired → describe then implement → 2× self-refine checking novelty → evaluate, keepers join archive → repeat to max iterations.
AFlow: workflow = graph (LLM nodes, code edges) · init W0 from template · select by soft mixture · expand conditioned on eval performance · execute + evaluate · add back if improved within N rounds · stop at top-k plateau or budget.
P(i) = λ · (1/n) + (1 − λ) · si / Σj sj   (λ = exploration slider; every node keeps floor λ/n)

Where to go next, and why:

Harness Engineering — lesson 1 of this trilogy: the design patterns (workflow, file-memory, sub-agents) that everything here optimizes.
Self-Improving Harnesses — lesson 3: the optimizer becomes the optimized — STOP, Self-Harness, AlphaEvolve, DGM, and the failure modes.
Loop Engineering — the discipline of running these optimization loops unattended, and what gets to say "no."
Prompt Engineering — rung 1 of the ladder, where all of this began.
AI Evaluation — the hidden prerequisite: every Jtrain, Jval, and score badge in this lesson presumes an evaluator you can trust.
Agent Architectures — the workflow shapes (ensembles, debate, decomposition) that ADAS and AFlow search over.
Monte Carlo Tree Search — the full MCTS lesson: UCB selection, rollouts, backpropagation — the deep end of Chapter 7's pool.

Three questions to ask any new optimization paper — they will sort it onto this map in under a minute: (1) What object does it optimize? (its ladder rung). (2) Who evaluates, on what data, and can the optimizer touch that data? (its overfitting and reward-hacking exposure). (3) Does the loop ever raise its own ceiling, or only polish the floor? (its RSI flavor — the Autodata test).
Source: this lesson teaches the "Harness Optimization" sections (Context Engineering + Workflow Design) of Lilian Weng's "Harness Engineering for Self-Improvement" (Lil'Log, Jul 2026), covering ACE (Zhang et al. 2025), MCE (Ye et al. 2026), Meta-Harness (Lee et al. 2026), AI Scientist (Lu et al. 2026, Nature), ScientistOne (Meng et al. 2026), Autodata (Kulikov et al. 2026), ADAS (Hu, Lu & Clune 2025), AFlow (Zhang et al. 2025), and self-refine (Madaan et al. 2023).
Final check: place the methods on the ladder. Which line is correctly matched, method → object optimized?
"Once harness design becomes an executable search space, a strong coding agent can exploit the same design space human engineers use."
— Lilian Weng, on the lesson Meta-Harness makes unavoidable

You now hold the ladder: playbooks that refuse to decay, skills that breed under a validation judge, harnesses on a Pareto frontier, archives of executable workflows, and a tree search that samples with curiosity. Next lesson, the loop turns all the way around — the optimizer optimizes itself.