2026-08-27 · Updated 2026-08-27 · 9 min read
Harness engineering architecture
A layered blueprint for harness engineering: five boundaries — orchestration, context, evidence, lifecycle, and failure — each with a named contract, using YYLO's runner contracts as the worked example.
By Juno AI INC · harness-engineering · architecture · yylo
Harness engineering is the discipline of designing the software that surrounds a coding agent: the seams that decide what runs next, what the model sees, what gets recorded, how work is admitted to a repository, and where execution stops. If you already know what a harness is and where its boundary runs, the next question is architectural — and it has become urgent, because agents are now asked to deliver long-running application development that spans hours or days instead of one context window. Anthropic's engineering write-up on effective harnesses names the core problem: "The core challenge of long-running agents is that they must work in discrete sessions, and each new session begins with no memory of what came before."
The industry's answer is converging: the harness around the model is engineered software, not a prompt. The 12-factor agents project puts it plainly — good agents "are comprised of mostly just software," not a prompt, a bag of tools, and a loop — and organizes that software into named factors like "Own your prompts," "Own your context window," "Unify execution state and business state," "Own your control flow," and "Make your agent a stateless reducer." Anthropic's team solved their long-running problem with "an initializer agent that sets up the environment on the first run, and a coding agent that is tasked with making incremental progress in every session, while leaving clear artifacts for the next session."
This reference turns those shared findings into one citable blueprint: five boundaries every harness architecture must own, each stated as a contract with a worked example. The worked example is YYLO's own runner contracts — the committed, inspectable machinery of its control plane — so every boundary below lands on real code you can read, not vendor aspiration.
The blueprint: five boundaries, five contracts
- Orchestration — who decides what runs next? Sequencing, fan-out, and delegation live in deterministic code outside the model. The contract: an explicit component owns dispatch, and the model proposes work rather than routing it. Without it, "which agent should run now" is answered by whichever loop happened to be alive.
- Context — what does the model see, and what survives the window? Prompt assembly, session state, and handoff artifacts are designed surfaces. The contract: state that must outlive a session lives outside the context window, in files a fresh session can read. Without it, every restart begins with amnesia and guesswork.
- Evidence — how is what happened inspectable later? Runs write durable artifacts: manifests, responses, sessions, receipts. The contract: a defined record survives every process boundary — crash, handoff, review — and comes from captured terminal events, not the agent's own prose. Without it, debugging means re-reading scrollback you no longer have.
- Lifecycle — how does work start, gate, and land? Task truth, isolation, admission, and integration are coordinated above the harness. The contract: work starts from recorded intent in an isolated workspace, passes explicit gates, and lands through a serialized transition. Without it, changes blend together and nothing is attributable.
- Failure — where does execution stop, and what may recover? Bounds, stop conditions, and recovery rules are chosen before launch. The contract: failure stops at declared points, is recorded as evidence, and recovery never infers success that was not verified. Without it, automation fails silently or recovers by inventing completion.
Read as a stack: orchestration routes work, context feeds it, evidence records it, lifecycle admits it, failure contains it. The rest of this guide maps each boundary to the YYLO contract that implements it.
Orchestration is code, not model judgment
The first architectural decision is which component owns sequencing — and the answer must be deterministic code. YYLO splits orchestration by data coupling, the same split any harness should make: when a step consumes an earlier step's {{ steps.<id>.response }}, its file, or its captured session, an ordered Workflow Runner owns the chain; when items are independent, a Parallel Runner fans them out and aggregates per-item status instead of pretending an order exists. The decision rule and its commands have their own guide.
Two contracts keep orchestration from multiplying into an unmaintainable mess. Process ownership is singular: managed agent steps delegate to one canonical managed_agent_runner.py, so spawning, outer capture, and provider selection are implemented exactly once rather than re-invented per workflow. And delegation stays bounded above the harness rather than inside it — YYLO drives installed agent CLIs from the control plane instead of embedding routing decisions in any one agent's context, which is what keeps the agent, model, and harness swappable per run.
Context survives the window it was built in
Every long-running harness confronts the same wall: the context window ends mid-project. Anthropic's answer is instructive because it is mechanical, not clever — an initializer agent writes a progress file and an initial git commit, and every later session reads that state before working: "the best way to elicit this behavior was to ask the model to commit its progress to git with descriptive commit messages and to write summaries of its progress in a progress file." The 12-factor factors "Own your prompts" and "Own your context window" say the same thing from the framework side: context assembly is your code, and durable state belongs outside the window.
YYLO's context contracts follow both. Sessions are captured as artifacts — steps that invoke the agent CLIs record session ids, later steps template them, and continue_from_step hands a specific session to the next operator, with yy cc continuing from the same shell-scope truth that yy continue-scope --json reports. And prompt assembly is treated as an attack surface: command substitutions are timeout-bounded and run with stdin closed, so a substitution cannot hang the harness waiting on input that will never arrive — the contracts are covered in prompt and shell-context safety.
Evidence is a contract, not console output
The evidence boundary decides what counts as the record after the process is gone. YYLO's contract is the run directory: per-step streams and responses, a manifest with statuses, exit codes, and session ids, and a run contract with hash-bound checkpoints per attempt. When the producer dies mid-run, those checkpoints are what recovery verifies against — the full artifact map and recovery commands are in auditable agent workflows.
Two contracts are architectural, not incidental. Evidence comes from captured terminal behavior, not assistant text: yy pi --execution-envelope emits one juno_execution_envelope.v1 JSON object as the sole stdout payload, and its provider, model, session, version, and cost fields are taken from marked backend terminal evidence. And evidence binds to mutation: managed steps hash tracked and untracked worktree content before and after dispatch, so the receipt can prove where a run was allowed to write — which is the bridge to the failure boundary below.
Lifecycle gates admission, not just completion
The lifecycle boundary is the one most teams skip, and its absence is why agent work stays unreviewable at scale. YYLO coordinates it above every harness: work starts from recorded intent — YYLO Ledger preserves Git-native task truth in safe Markdown plus hash-chained ledgers, while explicitly archived tasks become immutable packs that normal discovery never reads — and each task implements in its own isolated worktree, the safety boundary that keeps concurrent agents from colliding. Admission is explicit: yy task preflight is the read-only check, yy task finish is the guarded final boundary, and one serialized merge queue advances the target by expected-old-SHA compare-and-swap so only one mutation lands at a time. The task-truth side of this boundary has its own guide, and the current operational reference lives in the YYLO documentation.
The architectural point generalizes beyond this product: completion and admission are different gates owned by different evidence. An agent finishing a loop is one event; a repository accepting the change is another, and conflating them is how unreviewed work ships.
Failure boundaries are chosen before launch
The failure boundary is where automation earns trust, and it is the boundary you must design while consequences are still cheap. YYLO's contracts, in the order a run meets them: iteration bounds cap every loop before it starts (-i 1 is the bounded loop habit); fail_workflow: true marks the steps where continuing past failure would be worse than stopping; and a detected agent command that exits zero with an empty response is marked failed — silence cannot masquerade as completion.
Typed managed-agent steps fail closed hardest, and their declarations are worth reading as failure-boundary design:
The policy forbids side effects and disables lifecycle hooks by declaration, not by asking nicely; stage_boundary names one absolute worktree root and the exact paths a step may touch, and any mutation outside them writes stage_boundary.json, fails the step before its successor runs, and performs no cleanup. With require_terminal_result: true, the provider capture must declare a typed completed, blocked, incomplete, or failed result, and only completed can succeed. Recovery honors the same boundary: recover-attempt refuses active, partial, non-contiguous, cross-run, or drifted evidence, records an interrupted attempt with the verified prefix, and resumes at the reported first invalid step — it never infers semantic completion. Designing the failure modes themselves — taxonomies, blast-radius analysis, repair loops — is a deeper discipline than this blueprint needs; the architectural contract is just that the stopping points exist, are declared, and are enforced by machinery rather than prose.
Rules that keep the seams from leaking
Boundaries only hold while nothing crosses them by accident. Five invariants, each grounded in a contract above, are the part of this blueprint most worth citing:
- The prompt is never the enforcement boundary. Managed-agent receipts record the effective no-hooks policy, and stage boundaries are enforced by hashing bytes, not by instructions the model might ignore.
- The console is a view; artifacts are the record. Observers, dashboards, and quiet modes are presentation choices — review reads the run directory, never the terminal that scrolled away.
- Silence is failure. An agent command that exits zero without a response is failed by contract, because automation cannot grade what it cannot read.
- Recovery never infers completion. A resumed run re-verifies the unchanged prefix and resumes at the first invalid step; verified evidence is reused, unverified work is redone.
- Durable state lives outside product trees. Run artifacts retain selected session ids while mutable history and runtime markers stay outside the worktree, so evidence survives cleanup that state would not.
Reuse this blueprint
The five boundaries — orchestration, context, evidence, lifecycle, failure — and the five invariants are the citable artifact on this page. Its methodology: the public claims were verified against the linked Anthropic article (published November 26, 2025) and the 12-factor agents README at its live default branch, and every YYLO contract was traced to the committed product documentation, all on 2026-08-27; the blueprint itself is this page's original synthesis, versioned by the evidence date above. Re-verify both sides before relying on any quote after the sources change, and cite the stable URL below with attribution rather than paraphrasing the contracts uncredited — attribution is what keeps a public reference like this maintained.