The Harness Is a Boundary, Not the Whole System
In holaboss-ai/holaos, the harness is the part that executes a prepared run, not the owner of the entire agent environment. This chapter examines the runtime–harness boundary and the session-specific capability surface around it. The practical value is diagnostic: when a run behaves unexpectedly, the reader should be able to distinguish a workspace or runtime problem from an executor problem, and understand why two sessions in the same workspace may receive different tools.
The distinction follows from the model established by the workspace contract. The runtime has already collected authored inputs, loaded relevant context, and built a reduced execution package. At that point, a question remains: what should be allowed to vary with the chosen executor, and what must remain stable if the environment is to support a role across time?
The runtime owns the environment; the harness owns the loop
The runtime is responsible for the conditions under which a run is meaningful. It interprets the workspace contract, resolves memory and continuity, projects capabilities, orchestrates apps and integrations, resolves model configuration, and decides what crosses the execution boundary. These are environment responsibilities because they span runs or define the authority of a run before the executor begins.
The harness owns the mechanics that can vary with an executor. It runs the agent loop, interprets tool calls, manages retries and waiting, applies stop conditions, handles executor-native context, and translates runtime inputs into the executor's own session representation. When the executor emits thinking deltas, output deltas, tool events, or terminal states, the harness maps those events back into a normalized runtime contract.
This separation is more than an organizational preference. Imagine placing durable memory inside one executor's private session format. A harness replacement would then require a memory migration, and the meaning of “resume this work” would change with the brand of agent being used. Or imagine letting a harness decide which external tools exist by reading the entire workspace on its own. The runtime could no longer guarantee that its capability policy, app lifecycle, and prompt description referred to the same environment. A stable boundary keeps those invariants outside the loop that happens to execute them.
The boundary has three important participants. A runtime adapter describes what an executor path supports and builds the reduced request. A harness host receives that request, creates the executor-native session, wires the appropriate tools, and normalizes events. The executor performs the loop itself. The host is not a second runtime: it is the translation and control surface that lets a particular executor operate inside the runtime's prepared environment.
The diagram's arrows are ownership relationships, not just call order. The workspace supplies authored intent. The runtime decides how that intent and current context become a run. The adapter carries the decision across the boundary. The host makes it executable. The executor owns the loop but not the definition of the environment. Results return through the host and runtime so the desktop can expose them as status, outputs, memory, or continuity.
What crosses the boundary is deliberately reduced. The current request can carry workspace, session, and input identity; the instruction and context messages; the selected provider and model client; run-scoped reasoning metadata; attachments; projected tools and skills; prepared MCP servers and tool references; and a workspace configuration checksum. It does not give the harness an uncontrolled copy of all product state. That reduction makes the execution surface explicit enough to inspect and normalize.
What comes back is equally important. The runtime needs more than a final string. It needs normalized lifecycle events, streamed thinking and output deltas, tool-call and skill-invocation events, waiting-user states, and terminal success or failure. The desktop can then observe an execution without knowing how one executor names its internal events. A tool call that is represented one way by an executor can still appear as a common runtime event to the rest of holaOS.
A swappable boundary is not the same as equal executor maturity
The repository's current architecture supports the idea of more than one harness, but its documentation and registry need to be read with some care. The conceptual harness pages describe pi as the shipped OSS path. The pi adapter declares support for waiting-user interactions, skills, and MCP tools, while it does not declare native structured output. In the captured code, the runtime registry also constructs CLI-based claude-code and codex plugins alongside pi.
Those facts are not necessarily contradictory if “shipped path” means the primary deeply integrated path. They do mean that registration should not be turned into a stronger claim than the evidence supports. The CLI plugins are thin runtime shells: they bring their own tools and authentication, and reach shared browser or runtime capabilities through a runtime-tools MCP bridge. The registry proves that these paths are represented in the current code; it does not prove equal feature parity, identical setup, or the same level of production readiness as the default pi path.
The useful invariant is therefore not “every harness behaves identically.” It is “every supported harness must consume a compatible environment contract.” A different executor can choose different loop semantics, compaction behavior, native session handling, or tool wiring. It should not silently redefine workspace policy, durable memory, app lifecycle, or the authority of an MCP allowlist. If it cannot satisfy the boundary, the runtime should treat that as a capability or readiness issue rather than allowing the executor to invent a second environment model.
This distinction also limits what a harness swap promises to the user. The workspace identity, authored instructions, app and integration relationships, memory and continuity surfaces, per-run capability policy, and output association should remain recognizable. The exact prompt serialization, native session file, retry behavior, latency, and available executor-specific features may change. A stable environment contract gives portability of operating context, not identical responses or identical performance.
Why the visible tool surface depends on the session
The runtime does not expose every capability to every session by default. In the current model, a main session is coordination-heavy. It may plan work, retrieve memory, delegate tasks, manage apps, and produce reports without carrying every browser, shell, or integration schema in its prompt. A delegated subagent session is more execution-oriented and may receive the browser, shell, file, and narrower direct tools needed to complete the work it was assigned.
This is not merely a user-interface distinction. It reduces the chance that a coordination loop will act with a capability surface it did not need, while allowing an execution loop to do the concrete work. A front session can decide that a browser-heavy task should be delegated; the delegated run can then receive the browser bridge and the relevant file tools. Both runs belong to the same workspace and can share its durable state, but their immediate authority is different.
Browser access shows the rule clearly. The runtime stages browser capability only when the desktop browser is actually configured and when the session kind is allowed to use it. In the current pi path, direct browser tools are generally surfaced to delegated subagent runs, while the main session usually routes browser-heavy work through delegation. The runtime may still carry browser bridge context in a prepared request, but the presence of that context does not mean every session can call every browser action.
The host also separates capability breadth from prompt size. Some bulky families—such as integration tools, browser tools, and MCP tools—can remain registered while their full schemas are deferred after session creation. The model can reach a family through a compact gateway that describes or calls it; the first successful call promotes the family for subsequent turns. This preserves access without forcing every possible tool definition into the hot context of every request. It is a latency and cognitive-budget trade-off: the rare tool family may cost an extra discovery step, but ordinary turns avoid carrying a large catalogue.
The same principle applies to skills. The runtime resolves workspace and embedded skills, while the host handles executor-specific prompt serialization and local widening over tools or commands that a skill is allowed to manage. A skill can therefore be explicit and inspectable without becoming a permanent expansion of the entire system prompt. The runtime owns what the skill means and what grants it declares; the host enforces the local executor-specific part of those grants.
The critical distinction is between capability existence and capability visibility. An app may be installed in the workspace, an MCP server may be configured, or a skill may be present on disk. None of those facts alone tells you whether the current run can call it. The relevant questions are: did the runtime resolve it, did the session kind permit it, did the host materialize it, and did the app or server pass its readiness and authorization checks? A prompt that mentions a tool cannot answer those questions by itself.
The desktop makes the boundary observable
A runtime–harness boundary would be difficult to use if it existed only in internal logs. holaOS's desktop contract treats the workspace as an operator surface. Files, browser surfaces, and installed apps are parallel views into the same workspace. The operator should be able to see the active configuration, the connected integrations, recent session state, durable memory, outputs, and proposed work without first translating those concepts into raw paths or executor-specific terminology.
This matters when an agent works through a live app. The app surface is not just a rendered result after the fact; it can be the place where work is currently happening. The operator can watch the agent act, take over, and inspect what was produced. If the result is linked to an app, the desktop should reopen it in that app's surface rather than degrading it into a generic file or browser link. The operator surface therefore connects execution events to a place where a person can make a decision.
It also grounds references such as “this page” or “continue from here.” The runtime can carry operator-surface context when the browser capability is active, but it must keep user and agent browser surfaces distinct. Otherwise a natural-language reference could point to the wrong tab or imply that the agent is operating in a place the operator did not authorize. Context is useful only when its owner and scope are clear.
The operator's role is not to micromanage every internal event. It is to have enough visibility to answer what the agent is doing, what it can do, what it changed, and where intervention is needed. Streaming output, tool events, app status, durable artifacts, and memory views serve that purpose. A run can remain autonomous while its important boundaries stay inspectable.
Use the boundary to diagnose a real run
Suppose a product-launch workspace asks the agent to update an app and save a brief. If the agent says it lacks the app's tool, begin before the executor. Was the capability declared in the workspace? Did the runtime resolve and project it? Is the tool visible in this session, or was the work supposed to be delegated? Each question belongs to a different boundary.
If all of those checks pass but the executor still produces an incorrect tool call, the host or executor becomes the more plausible place to investigate. The host may have serialized the tool name incorrectly, a deferred gateway may have failed to promote the family, or the executor may have mishandled its native tool-call protocol. The runtime's normalized events help distinguish a tool that was never available from one that was available and misused.
A wrong answer has a similar diagnostic tree. Missing standing instructions points toward workspace reference collection or prompt projection. Missing recent work points toward session continuity. A stale account fact points toward memory retrieval and freshness handling. A result that exists in the executor's response but not in the operator's outputs points toward runtime association or desktop presentation. The architecture does not guarantee that every diagnosis will be easy, but it prevents all failures from collapsing into the unhelpful label “the AI was wrong.”
The design also places a limit on claims about swappability. A stable boundary can preserve state ownership and capability policy while the executor changes. It cannot guarantee that a new harness has the same browser support, skill behavior, structured-output support, authentication path, or operational reliability. The current code's coexistence of a deeply integrated pi path and thinner CLI paths is a useful reminder: architectural openness and product parity are different achievements.
The essential lesson is that the harness is an execution boundary inside holaOS, not a replacement for holaOS. The runtime decides what the environment means and what a particular run may see; the host translates that decision; the executor manages the loop. Once that division is clear, the next design problem comes into focus: an environment still needs a durable unit for domain capability—something larger than a tool call, but smaller than the runtime itself.