Skip to content
Aditya Karnam
AI researcher building the infrastructure layer for reliable agents.

Building QuECTO: From Minimal Agent Harness to Evaluable Coding Agent

24 min read

Part two of the QuECTO build series. Start with What Is an AI Agent Harness? if you want the conceptual map first.

The first post defined the harness as the software layer that turns a language model into an acting system: context, tools, state, policies, verification, recovery, and budgets. Since then, QuECTO has moved from a compact proof of concept into a real coding-agent runtime. The interesting part is not just the feature list. It is the order in which the system had to grow.

QuECTO started with a deliberately tiny question:

How much agent infrastructure can we build before the harness stops feeling minimal?

The answer, so far, is more than I expected: a 1.3 MB core, a 3.5 MB coding agent, no async runtime, first official releases, native reasoning controls, verification, persistence, OpenTelemetry, MCP, subagents, multimodal image input, and a native Rust evaluation harness.

That sounds like a lot. The design pressure was to keep each piece small enough to remain observable.

The build arc

QuECTO build arc from core to evaluation harnessA left-to-right timeline showing QuECTO's build arc: core transport, coding agent, chat and persistence, observability, subagents, multimodal support, releases, and native evals. The eval layer loops back into the agent, showing that measurement informs the next runtime changes.QUECTO BUILD SERIES · PART TWOCoreOpenAI-compatiblesync transport1.3 MBAgent looptools, approvals,sandbox, verify3.5 MBStateful UXchat, resume,undo, flavorsusableObservabilitytraces, tokens,turns, latencymeasurableRuntime expansionAnthropic provider, MCP, subagents,image input, markdown and Mermaid chatNative eval harnesseval.yaml, script graders, telemetrythresholds, SQLite run historyRelease surfacequecto v0.1.0 and quecto-agentv0.1.0 with macOS arm64 binariesevals turn runtime behavior into feedback

There are three phases hiding inside that diagram:

  1. Transport: make model calls boring, synchronous, inspectable, and vendor-neutral.
  2. Agency: add tools, approvals, verification, state, and UX without hiding the loop.
  3. Measurement: record enough traces to make behavior comparable across models, providers, policies, and releases.

The third phase is where a harness becomes research infrastructure.

Technical architecture

QuECTO's current architecture is a deliberately layered Rust workspace. The core crate stays a synchronous JSON transport primitive; the agent crate owns the control loop, policy, tools, state, rendering, and provider normalization; the eval crate runs the agent as an instrumented subject under repeatable tasks.

QuECTO technical architectureA detailed architecture diagram showing quecto core transport underneath quecto-agent. Inputs such as CLI flags, environment variables, flavors, AGENTS instructions, and MCP servers feed configuration, context, and tools into the agent. The agent loop builds messages, calls provider adapters, executes normalized tool calls through policy and sandbox gates, records sessions and telemetry, and is driven by quecto-eval for repeatable evaluation.QUECTO TECHNICAL ARCHITECTURE · SYNC CORE, POLICY-RICH AGENT, REPLAYABLE EVALSConfiguration inputsCLI flags > env varsflavor.toml manifeststrust hashes, max stepsreasoning + approval modeContext inputsAGENTS.md / CLAUDE.mdrepo root, git snapshotchat history, observationsoptional image payloadsExternal tool sourcesbuilt-in Rust toolsMCP STDIO / HTTPsubagent registryflavor allow-list filterquecto-agent cratemain.rs / chatclap, REPL, slashcommands, rendererflavor / trustmanifest mergeTOFU hash gateagent.rs control loopbuild_messages(system + history + observations)buffered model turn -> parse_assistant()tool dispatch -> observation -> verify -> retrypolicy.rsallow / ask / denyapproval presetsinteractive fallbacktool registryJSON schemasnative or text callsstructured errorsprovider.rsOpenAI-compatibleAnthropic Messagesreasoning mappingtool_use normalizationmodel endpointsOpenAI / compatibleOllama, vLLM, LM StudioAnthropic nativevision-capable modelsquecto corequecto_raw()quecto_stream()blocking ureq + JSONsandbox.rsrepo-scoped cwddenylist beats fulltimeout + process groupoutput cap + redactionstate + telemetrysession.rs SQLiterecorder.rs TraceEventOTEL spans, token usageresume / undo / diffquecto-eval crateeval.yaml discovery, workspace setup, agent child processScriptGrader, TelemetryGrader, LlmRubricGraderSQLite run history for regression and compatibility analysisJSONL traces + graded outcomes

At the implementation level, the technical shape is:

BoundaryConcrete mechanismReason it exists
Model transportquecto_raw(url, headers, body) and quecto_stream(...) over blocking ureqThe core can carry arbitrary provider payloads without learning agent semantics.
Provider normalizationprovider.rs maps OpenAI-compatible chat completions and Anthropic Messages into the same assistant/tool-call shapeProvider swaps become a controlled variable instead of a rewrite of the agent loop.
Control loopagent.rs builds messages, calls the model, parses assistant output, dispatches tools, appends observations, and enforces max_stepsThe agent remains a sequential, inspectable state machine.
Tool ABITool { name, description, schema, run } plus registry filtering from flavor allow-listsBuilt-ins, MCP tools, and future custom tools share one dispatch surface.
Safety gatepolicy.rs decides `allowask
Edit semanticsapply_patch uses exact search/replace blocks, ambiguity rejection, line-ending preservation, and prior-content recordingFile mutations are replayable and undoable rather than vague text edits.
Persistencesession.rs records messages, file changes, reasoning settings, and session metadata in SQLiteChat, resume, diff, and undo are runtime features, not terminal scrollback.
Observabilityrecorder.rs emits JSONL TraceEvents; optional OTEL spans cover runs, steps, tool dispatches, completions, and reasoning tracesEval graders can reason over turns, tokens, latency, and tool usage, not just final text.
Evaluationquecto-eval runs task workspaces from eval.yaml, injects trace output, grades with scripts/telemetry/rubrics, and stores resultsHarness changes can be compared across versions, models, providers, and policies.

The core loop is still synchronous on purpose. Tool-call turns use buffered model responses because the harness needs the complete tool_calls array before dispatch; streaming deltas are useful for final text output, but partial function-call assembly is the least portable part of OpenAI-compatible local serving stacks. The optional MCP feature can bring in an async runtime for MCP clients, but the primary agent loop does not become async as a side effect.

There is a second important separation: tool-call transport and tool execution are different layers. A model response can use native tool calls or a text-based fallback protocol, but both normalize into the same internal tool call structure. Only after that does the policy layer decide whether the call is allowed, the registry route it, the sandbox constrain it, the session recorder persist it, and the renderer summarize it to the user.

That is the technical version of "minimal but observable": the harness avoids a large framework core, but it does not avoid boundaries. It names them.

How the lean-core decision was made

The important architectural move was made before the first implementation task: define the maximal harness, then compress against it.

QuECTO's docs/superpowers notes include a full coding-agent harness reference: CLI session, model adapter, instruction loader, repository context engine, tool registry, coding tools, agent loop, edit engine, sandbox, verification loop, session state, renderer, MCP, observability, and evaluation. Instead of putting that whole list into the quecto crate, the design asks a stricter question:

Which of these responsibilities must live in the smallest possible core?

The answer was exactly one: model transport.

That is why the core implementation plan was so constrained:

  • Four public functions: quecto_raw, quecto_stream, quecto_to, quecto
  • Two source files: src/lib.rs and src/main.rs
  • Two direct dependencies: ureq and serde_json
  • No tokio
  • No reqwest
  • No CLI framework
  • No custom error enum
  • No typed provider abstraction in the core
  • Public serde_json::Value instead of a bespoke model-response struct

Those constraints are not arbitrary size golf. They are architectural pressure. serde_json::Value stays in the public API because quecto_raw is supposed to carry any provider-shaped JSON body and return the complete JSON response. If an agent wants tool calls, usage metadata, reasoning traces, or provider-specific fields, the core should preserve them rather than prematurely normalize them away.

Looking back through the specs, I can see the lean design came from a handful of repeated decisions:

Lean-harness moveWhat it means in QuECTO
Compress against the maximal systemI first described the whole coding-agent harness, then let the core implement only the irreducible transport piece.
Preserve raw provider shapequecto_raw returns serde_json::Value, so the core does not need typed abstractions for every provider feature.
Normalize one layer upOpenAI vs. Anthropic differences belong in quecto-agent/src/provider.rs, where they can change without touching the core.
Make sync the defaultThe common path is blocking, sequential, and easy to inspect; async runtimes appear only behind boundaries that genuinely need them.
Quarantine dependenciesMCP, OTEL, rendering, SQLite, and eval machinery live outside the core or behind feature flags, so they do not tax the smallest build.
Treat execution as policyTools, approval, sandboxing, verification, trust, and persistence are runtime decisions, not model-transport concerns.
Evaluate from outsidequecto-eval drives quecto-agent as a subject and consumes traces, instead of mixing benchmark logic into the agent loop.

That is how the harness stays lean while still becoming capable. It is not lean because features are missing; it is lean because every feature has to justify both its existence and the layer it enters.

The companion-crate boundary follows directly from that:

Harness concernWhere it wentWhy it stayed out of quecto core
Tool loop and step limitsquecto-agentRequires state, policies, renderer, repeated-action guards, and task outcomes.
Tool schemas and dispatchquecto-agentThe model transport can carry tools, but execution belongs to the runtime.
Approval and sandbox policyquecto-agentSecurity policy depends on repo paths, interactivity, denylist rules, and command execution.
Session persistencequecto-agentResume, undo, diff, and chat history require SQLite and file-change recording.
Flavors and trustquecto-agentProject config can declare commands and loosen approvals, so it needs TOFU and explicit precedence.
MCPquecto-mcp / optional agent featureMCP needs transports, JSON-RPC, server trust, and sometimes async internals; none belong in the default core.
OpenTelemetryoptional otel feature in quecto-agentTracing is valuable, but the default build should not pay for exporter/runtime machinery.
Native evalsquecto-evalEvaluation should run the agent as a subject and consume traces, not become part of runtime transport.

The decision trail is visible in the docs/superpowers folder itself:

Decision docArchitectural conclusion
2026-07-09-full-harness-reference.mdCapture the maximal coding-agent harness first, so QuECTO can choose what to compress and what to exclude.
2026-07-09-quecto-harness-design.mdDefine quecto as the model adapter only: arbitrary JSON in, arbitrary JSON out, with convenience helpers layered above the raw primitive.
2026-07-10-quecto-core.mdLock the core implementation constraints: two source files, two dependencies, four public functions, no async runtime, no framework.
2026-07-10-quecto-agent-architecture.mdMove the loop, tools, sandbox, context, verification, renderer, and SQLite sessions into quecto-agent.
2026-07-10-quecto-agent-flavors-design.mdKeep extensibility above the core by making flavors configure the agent library and default binary.
2026-07-15-quecto-mcp-design.mdPut MCP in a separate library and optional agent feature so tokio, JSON-RPC transports, and server trust never enter the default core.
2026-07-14-quecto-agent-otel-design.mdKeep tracing behind an optional feature, with exporter machinery outside the synchronous default path.
2026-07-19-quecto-evals-sota-design.mdRun evaluation as an external harness around quecto-agent, using trace files and graders instead of bloating runtime transport.

This is also why later specs keep repeating the same pattern. Reasoning modes live at request construction, not in the agent loop. Provider-specific Anthropic details live in provider.rs, not in the core. Markdown, Mermaid, and spinner behavior stay in the renderer, not in the model layer. MCP isolates async transport behind a synchronous public API and a feature flag. OTEL can spawn a small background runtime only when tracing is enabled.

So QuECTO's architecture was not "start tiny, then bolt everything on." It was closer to:

  1. Draw the full harness.
  2. Assign each responsibility a home.
  3. Let the core own only the JSON transport primitive.
  4. Let the agent own policy-rich execution.
  5. Let evals observe the agent from outside.

That is the real lean-core decision: the core remains small because the system already knows where complexity is allowed to live.

1. Keep the core boring

The quecto crate is intentionally narrow: send a prompt to an OpenAI-compatible endpoint and return a response, buffered or streamed. It can talk to local systems like Ollama, LM Studio, and vLLM, or to cloud endpoints with the same API shape.

That core is not where the agent magic lives. That is the point.

By keeping the transport layer tiny, the rest of the system has fewer places to hide behavior. If a task succeeds or fails, I want the explanation to live in the model, the prompt, the tools, the runtime policy, the verifier, or the budget, not in a thick framework layer whose choices are hard to inspect.

This is why QuECTO's minimalism is not only aesthetic. It is an experimental control.

2. Add agency as explicit policy

quecto-agent turns the core into a coding agent by adding the familiar pieces: file tools, search, patching, shell commands, git inspection, approval presets, a hard denylist sandbox, verification commands, and SQLite-backed sessions.

The important design choice is that those features are treated as policies, not ambient powers. Writes and commands are gated. Dangerous commands are denied even under permissive approval. Project-local flavor manifests use trust-on-first-use. Session state can be resumed, diffed, or undone. Verification is a first-class stop condition, not something remembered after the final answer.

The UAT pass was a good sign: 41 black-box scenarios, 41 passes, no blocking defects. But the more useful result was qualitative. The harness had become legible enough that testers could isolate behavior by area: CLI, tools and safety, persistence, and flavors.

That is what I want from an agent harness. Not just "it works," but "when it fails, the failure has an address."

3. Make the chat loop useful enough to live in

After the first usable agent landed, a surprising amount of work went into the terminal experience: bracketed paste, clearer activity summaries, slash commands, session-scoped /reasoning, markdown rendering, inline Mermaid rendering, better /status and /context, and quieter non-TTY output.

This sounds like polish, but for coding agents it is closer to instrumentation. If the user cannot see what the agent did, or cannot tell whether a tool call created 1 line or changed 449 lines, the harness is less trustworthy.

The chat UI now exposes more of the loop:

  • Which tool ran
  • What changed
  • Whether verification passed
  • Which reasoning mode is active
  • Whether a session can be resumed or undone
  • Whether a project flavor is trusted

The user interface is part of the harness because it shapes supervision.

4. Support provider differences without pretending they are the same

QuECTO started with OpenAI-compatible endpoints because that API shape is common across local and hosted runtimes. The next provider step was native Anthropic Messages API support.

That was not just a URL change. Anthropic has different wire concepts: system extraction, tool_use and tool_result content blocks, x-api-key auth, required max_tokens, and optional thinking budgets. QuECTO maps its reasoning_mode onto Anthropic's thinking.budget_tokens, while keeping the rest of the agent loop intact.

This is exactly the kind of boundary I care about for AgentABI-style questions. If the model changes, the provider changes, or the reasoning control changes, what behavior stays stable? What breaks? What becomes more expensive? What takes more turns?

Provider support is not only compatibility work. It creates more controlled substitutions to measure.

5. Add subagents without losing observability

The first subagent tool was synchronous: invoke_subagent runs another agent and waits. The newer concurrent path is more interesting. spawn_subagent starts a background agent and returns an id. monitor_subagents reports status, elapsed time, and recent activity. cancel_subagent stops one early. A capped buffer records progress, and background runs render to a null renderer so their output does not corrupt the foreground session.

The constraint matters: concurrency is useful only if the supervisor can still tell what is happening.

So the subagent design exposes monitoring as a read-only tool, caps concurrency, and keeps cancellation explicit. In a larger agent system, this becomes the difference between "the agent delegated work" and "the agent lost control of the runtime."

6. Treat multimodal input as another harness boundary

The new --image flag lets quecto-agent send base64-encoded images into a vision-capable model. It was verified end-to-end against a local vision model.

Again, the interesting thing is the boundary. Image support is not just a model feature. The harness must decide:

  • How the image is represented
  • How it enters the request payload
  • Which providers can receive it
  • Whether the rest of the tool loop still behaves normally
  • How eval tasks can grade visual understanding

This gives QuECTO a way to test coding-adjacent visual tasks: reading screenshots, identifying UI states, inspecting rendered assets, or grounding a bug report in an image.

7. Move from demos to evals

The biggest shift since the first post is quecto-eval, the native Rust evaluation harness.

The earlier eval layer included a 10-task TerminalBench-style smoke suite and a Harbor adapter for Terminal-Bench 2.x. The native harness makes the system more self-contained: tasks are described with eval.yaml, setup runs in isolated workspaces, graders are composable, telemetry is parsed from JSONL trace events, and SQLite stores historical run data.

QuECTO evaluation loopEvaluation YAML files define tasks, setup scripts, graders, and telemetry thresholds. quecto-eval creates a workspace, runs quecto-agent with trace output enabled, grades the result using script, telemetry, or LLM rubric graders, stores the run in SQLite, and feeds results back into runtime changes.NATIVE EVAL LOOP · BEHAVIOR BECOMES DATAeval.yamlprompt, setup,graders, thresholdsWorkspacesetup script createstask statequecto-agentruns task withtrace outputGradersscript, telemetry,LLM rubricSQLite historyruns, tokens,turns, latencyregression signal

This changes the development loop. A harness feature is no longer done when a demo passes. It is done when it can be run again, graded, traced, and compared.

The smoke suite already showed 10/10 deterministic passes on tasks like git conflict resolution, package refactoring, advanced shell text processing, OpenSSL workflows, Docker builds, debugging a C crash, SQLite queries, and fixing a Rust build. The next step is less about adding more tasks randomly and more about sorting tasks by purpose:

  • Regression tasks: things the harness should keep passing at near 100%.
  • Capability tasks: harder tasks for hill-climbing.
  • Compatibility tasks: same prompt, same workspace, different runtime or provider.
  • Telemetry tasks: tasks where the pass/fail result is not enough without turns, tokens, latency, and tool usage.

This is where QuECTO connects back to the research question from the first post: agent performance is an emergent systems property. The eval harness is how that property becomes observable.

What has shipped so far

Here is the compressed changelog view:

DateMilestoneWhy it matters
2026-07-10Core crate and size-optimized buildProved the tiny synchronous transport layer and release profile.
2026-07-12Full quecto-agent M1-M7bAdded tools, editing, sandbox policy, verification, persistence, and flavors.
2026-07-14UAT and bug-fix passTurned rough edges into clean behavior across 41 scenarios.
2026-07-15Smoke evals, Harbor adapter, OTELStarted measuring behavior through tests and traces.
2026-07-16Chat and delegation improvementsMade the live loop easier to supervise.
2026-07-18Anthropic provider, reasoning, Mermaid, subagentsExpanded provider/runtime substitution and concurrent delegation.
2026-07-19Native quecto-eval and image supportMade evaluation and multimodal inputs first-class.
2026-07-22quecto and quecto-agent v0.1.0Published the first official releases with macOS arm64 binaries.

The through-line is not "add every feature." It is "make each harness boundary explicit enough to test."

The lesson so far

I went into QuECTO thinking the hard part would be making a small agent feel capable.

That was only half true. The harder part is making the capability inspectable. Tool calls need summaries. State needs a database. Trust needs hashes. Provider support needs a stable internal shape. Subagents need monitors. Images need a request representation. Reasoning needs controls and traces. Evals need graders and telemetry.

Minimalism did not mean avoiding those pieces. It meant adding them in a way that keeps the system understandable.

That is the build principle for the next phase:

A harness is not complete when it can act. It is complete when its actions can be replayed, measured, compared, and trusted.

The next posts in this series will go deeper into the individual layers: reasoning controls, subagent orchestration, multimodal evals, and behavioral compatibility across providers and harness versions.

Related reading

© 2026 Aditya Karnam. AI Researcher.
NowStackField NotesCurrent SystemsStatus