---
title: Harness Control Loop
section: Ora — Foundation arguments
status: review
description: The harness owns the control loop and invokes the model as a bounded function at each step. A deterministic program decides what happens next — not the model.
authors:
  - The Ora Foundation
downloads:
  md: /papers/white/harness-control-loop.md
license: https://creativecommons.org/publicdomain/zero/1.0/
---

# Harness Control Loop

**The design claim: the harness owns the control loop, and the model is invoked as a bounded function at each step.** A deterministic program decides what happens next; it assembles a purpose-built context and a step-specific instruction, calls the model once, takes the output back, and decides the next move. The model never holds the loop. This is the inversion the whole architecture rests on, and it is the reason the system does not drift, forget, or take unaudited actions across a long process — those are properties of systems where the model holds the loop, and this system removes them by construction.

There are two ways to build a multi-step process around a language model.

In the first, the model holds the loop: you give it a goal and a set of tools, and it decides what to do next, calls a tool, reads the result, decides again, and continues until it judges the task done. This is the pattern the industry markets as an "agent." Its appeal is obvious — it needs almost no orchestration code. Its failure mode is equally obvious once you have run one in production: the model is a stateless reasoning primitive with a bounded context window and no internal verifier, so across a long autonomous loop it loses track of the task, compounds its own errors, and emits actions no one reviewed.

In the second, a program holds the loop and calls the model as one bounded step at a time. This is the harness. The model contributes reasoning to each step; the harness contributes everything else — sequencing, memory, tool execution, verification, and the decision about what happens next. This document specifies that second pattern as a mechanism: the step sequence, the points at which the harness intercepts, what context crosses each boundary, and how the model is constrained at every call.

Three terms, defined once. A **control loop** is the logic that decides, after each step, what the next step is. A **bounded function** is a model call that receives a purpose-built input, returns one output, and holds no state between calls. An **interception point** is a place in the loop where the harness acts on its own — assembling context, executing a tool, validating output, persisting state — without the model in the path.

---

## boot.md is the program the harness runs

The harness is thin by design. The behavior lives in a natural-language specification the harness loads and executes: `boot.md`, plus the per-mode specification files. `boot.md` is the system's constitution and standing rules — what it is, what it must never do (the anti-confabulation discipline foremost), the mode registry, the tool catalog, and the pipeline architecture. It is written in language a domain expert can read and modify, which is the point: the behavioral specification is not buried in code.

This is a validated pattern, not a novel bet. Claude Code runs on a roughly 27,000-token natural-language specification composed from over a hundred modules; the `AGENTS.md` convention is in use across tens of thousands of repositories. The architectural insight both share is that a thin orchestrator handles only the mechanical detect→execute→inject loop while the natural-language specification carries the behavior.

The harness does not inject all of `boot.md` into every model call. The full file carries architectural metadata — the mode registry, the full tool catalog, the pipeline description — that a single analytical step does not need and should not pay for in tokens. So for pipeline steps the harness injects a **trimmed behavioral preamble**: the constitution, the standing rules, and the canonical anti-confabulation block, and nothing else. Direct-response calls outside the pipeline still receive the full file. The split is a deliberate token-economy decision, and it is one of the open problems below, because "behavioral" versus "architectural metadata" is a heuristic line.

---

## The per-turn step sequence

One user turn drives one pass through the loop. The spine, in order:

1. **Open a forensic trace.** Before anything else, the harness opens a per-turn trace directory at `pipeline-traces/<conversation_id>/<turn-timestamp>/`. Every downstream step writes its input and output there. A turn marked *stealth* opens no trace and leaves zero residue — the privacy guarantee is enforced by producing nothing, not by cleaning up after.

2. **Deterministic short-circuits, before any model call.** Slash commands (`/render`, `/approve`, and the rest) are mechanical runtime operations; the harness runs them directly. A reply in the middle of a framework elicitation is routed to the elicitation handler. These paths never call the model, because they are cheaper and more reliable as code — and the elicitation case illustrates a load-bearing principle: *the conversation is the state.* The harness detects a mid-elicitation turn by reading a marker in the conversation history, not by consulting a persistence file. There is no separate state store to corrupt or desynchronize.

3. **Step 1 — cleanup.** The raw prompt enters Phase A, which normalizes it into *operational notation*: a cleaned, expanded form with transcription errors corrected and ambiguity resolved. In `assume` mode the harness resolves ambiguity silently and records each resolution to an `INFERRED_ITEMS` list; in `ask` mode it surfaces the ambiguity as a question. The inferences are carried forward explicitly so they are never mistaken for user-stated facts (see the next section).

4. **Pre-routing — classification and gear selection.** The harness classifies the operational notation and assigns a *gear* — the tier of analytical machinery the input warrants. This is the routing method specified in its own section below.

5. **Step 2 — context assembly.** The harness builds the *context package*: the retrieved vault context, the mode specification text, the user profile, and the resolved gear. This is the input from which every subsequent step's prompt is assembled.

6. **Gear-appropriate execution.** A gear-1 or gear-2 input takes a single bounded model call. A gear-3 input runs the sequential pipeline (analyze → review → revise). A gear-4 input runs the parallel adversarial pipeline. The gear determines both the pipeline's shape and the models selected for it; model selection is specified in its own paper and not repeated here.

The harness owns every transition between these steps. The model is called only inside steps 3, 5's downstream consumers, and 6 — and at each call it is bounded.

---

## The bounded-function interception point

The mechanism that makes a model call bounded is the per-step system-prompt builder. The pipeline has six step roles — **analyst, evaluator, reviser, verifier, consolidator, formatter** — and for each one the harness assembles a system prompt purpose-built for that role and that role only.

The assembly, in order, is:

- the trimmed behavioral preamble from `boot.md`;
- a **Phase A assumptions block** — the `INFERRED_ITEMS` from cleanup, injected under an explicit "not user-stated facts" header so the model treats them with appropriate uncertainty and can surface them back to the user;
- the **baseline brief, evaluation criteria, and verification criteria** for the mode — injected up front for every step, so the analyst writes toward the standard it will be graded against and the verifier grades against the same standard;
- exactly **one** role-specific section extracted from the mode file: the depth or breadth guidance for an analyst, the revision guidance for a reviser, the verification criteria for a verifier, and so on. Sections belonging to other steps are suppressed.

That suppression is the heart of the bound. An analyst model receives the analysis guidance and never sees the revision or consolidation guidance; it cannot wander into another step's job because the instruction for that job is not in its context. The harness calls the model once with this prompt, takes the single output back, extracts and validates it, and carries forward only what the next step needs.

This is a stage boundary, in the sense the term carries elsewhere in the architecture: context does not accumulate across steps. Each step receives a purpose-built window containing exactly what its role requires. The handoff between steps is explicit — the harness decides what crosses — rather than the entire prior context sliding forward and compounding.

The reliability argument is structural. A model that holds the loop drifts because the task definition decays as context accumulates across a long autonomous run. A model called once per step against a freshly assembled, role-bounded context cannot drift the loop, because it never holds the loop. The loop is code, and code does not drift. What the model can still get wrong is the content of its one step — and that is what the adversarial pipeline's evaluator, reviser, and verifier steps exist to catch, each itself a bounded call.

---

## Gear routing and input classification

Not every input deserves the same machinery. Routing a greeting through a five-minute adversarial pipeline wastes the pipeline; routing a wicked-problem analysis through a single model pass fails the analysis. The harness classifies each input and routes it to one of four gears of escalating cost and depth.

- **Gear 1 — direct response, no retrieval.** Greetings, system commands, mechanical translation. A small model, sub-three-second response.
- **Gear 2 — single pass with retrieval and tools.** Factual lookups that need retrieval but no judgment — "who holds this office now," "what is the capital of X." A fast model, five-to-fifteen seconds. The match condition is a retrieval-trigger signal *and the absence of a judgment marker*, so "should I bring an umbrella tomorrow" — retrieval plus judgment — does not land here.
- **Gear 3 — sequential adversarial pipeline.** Judgment-required prompts that do not invoke a specific analytical mode. One model analyzes, a second reviews, the first revises. The universal evaluate/revise/verify/consolidate scaffolding carries the discipline.
- **Gear 4 — parallel adversarial pipeline.** The deep-analysis modes that explicitly opt in. Two large models work the problem independently and in parallel, then the evaluator, reviser, verifier, consolidator, and formatter steps run over their output.

Classification runs as a staged pipeline, and the staging is itself a design response to a real failure. A **pre-Phase-A bypass check** runs first, on the raw prompt as the user typed it, because the alternative — classifying the post-cleanup operational notation — makes the classifier's input depend on what cleanup produced for this prompt, and substring detectors do not compose cleanly across that layer. ("Structured cui-bono analysis" contains the substring "no analysis"; a bypass detector run on expanded text fires on it.) Running the bypass check on user-controlled input is the layer where bypass triggers were designed to be evaluated. After cleanup, a defensive filter re-runs the same scan, then a **sufficiency analyzer** decides whether the prompt carries enough signal to dispatch a specific mode or needs a disambiguation question, and a **completeness check** verifies the mode's required inputs are present — offering a lighter sibling mode when they are not, rather than failing. A friction-reducer skips any disambiguation question the prompt already answered, so a fully specified prompt dispatches with no questions at all.

Routing is the input side of model selection: the gear sets the pipeline shape, and the configuration system resolves which model fills each slot at that gear. Mis-routing is a named failure mode with a defined response — a false bypass re-runs the filter in permissive mode; a disambiguation loop falls back to a thorough-by-default dispatch; a missing-input loop degrades to the lightest sibling that can run.

---

## Tools are a harness step, not a model power

When a step uses a tool, the harness owns the loop around it: the model emits a tool request, the harness detects it, executes the tool, and injects the result back into the context for the model to continue — the detect→execute→inject sequence. The default is stronger than model-initiated calling: a mode *declares* the tools its step needs, and the harness runs them at a fixed point in the step with parameters derived from the prompt, so tool use is deterministic rather than left to the model's discretion. A model-requestable escape hatch exists — a mode can expose an allowlist the model may draw from — but it is off by default, because weak local models emit malformed tool calls, and because keeping it off makes a run reproducible. Either way the harness executes the tool and logs the dispatch, so a tool call is an audited, harness-controlled interception, never an unobserved action the model took on its own.

---

## State and observability

The harness carries the state the model cannot. Three layers:

- **The conversation is the working state** within a turn and across a short exchange. Mid-process continuations are recovered by reading the history, not a side file.
- **The per-turn forensic trace** records every step's input, output, model, and timing under the turn's trace directory. This is the audit trail: any final output can be traced back through every step that produced it, which is the difference between a system you can defend to someone who was not in the loop and one you cannot.
- **The vault** carries long-term state across turns, sessions, and model swaps — specified in its own paper.

The trace is also the interception point that makes the loop debuggable. When a step fails, the trace shows which step, on what input, with what model — so a failure is a located event, not a vague bad answer.

---

## Why this is the reliability mechanism

State the property plainly. A stateless model invoked as a bounded function cannot drift the loop, because it never holds the loop. It cannot forget across steps, because the harness carries the state between them. It cannot take an unaudited action, because the harness executes and logs every tool call. It cannot hide its reasoning, because every step is traced. Drift, cross-step forgetting, and unaudited action are the characteristic failures of model-driven loops; the harness removes each one by moving the loop into code. That is the whole of the claim, and it is an engineering claim, not a slogan — each clause names a failure mode and the structural reason it cannot occur.

---

## Open problems

- **Classification is heuristic.** Routing runs on substring and signal-vocabulary matching with negation windows, not a trained classifier. It is fast and predictable, but brittle to phrasing, and the detector-layering risk it carries is what forced the pre-Phase-A / post-cleanup split. A principled gear classifier is deferred deliberately — the gear shapes are still under evaluation — so today's routing is a defensible approximation, not a settled mechanism.
- **The handoff is load-bearing and unchecked.** Each step sees only what the prior step's handoff carries forward. A handoff that silently drops a load-bearing detail degrades the downstream step, and there is no automatic check that a handoff preserved what the next step needs. The stage-boundary discipline that prevents context debt is the same discipline that can starve a step.
- **Bounded steps trade global coherence for drift-resistance.** A step cannot see the whole loop, so a genuinely cross-cutting insight that spans steps has nowhere to live except the handoff or the consolidator. The architecture bets that this trade is worth it; for most work it is, but the bet is real.
- **The boot.md trim is a heuristic split.** Deciding what counts as "behavioral" (kept in the per-step preamble) versus "architectural metadata" (dropped) is a judgment call, and a wrong call drops context a step needed. There is no test that the trimmed preamble is sufficient for every step.
- **Section extraction fails quietly.** A step's instruction is extracted from the mode file by heading. A malformed or missing heading yields an empty section for that step. Fallbacks cover the common cases, but the failure mode is silence, and silent degradation is the hardest kind to notice.
- **Short-circuits sit outside the trace.** The deterministic slash-command and elicitation paths bypass the pipeline, which is correct for cost and determinism, but means they are not captured in the per-turn forensic trace the way pipeline steps are.

None of these blocks the loop from running — it runs every turn — but each marks a seam where the current mechanism is a working approximation rather than a finished answer.
