Run Compilation
Use this page when you are changing how workspace files become the reduced execution package the harness host actually receives.
The source of truth is:
runtime/api-server/src/workspace-runtime-plan.tsruntime/api-server/src/runner-prep.tsruntime/api-server/src/ts-runner.tsruntime/api-server/src/agent-runtime-config.tsruntime/api-server/src/harness-registry.ts
The Current Compile Path
For the shipped path, a run moves through these stages:
- collect the workspace references the runtime needs
- compile
workspace.yamlplus those references into aCompiledWorkspaceRuntimePlan - stage runtime-owned capability surfaces such as browser tools, runtime tools, MCP, skills, and commands
- load recent runtime context, session resume context, recalled memory, operator surface context, and pending user-memory proposals
- project the final runtime config passed into the harness
- build the harness-host request, persist a sanitized request snapshot, and launch the host
If you patch the wrong stage, the system usually still builds but exposes the wrong tools, prompt layers, or runtime context.
Stage 1: Collect Workspace References
compileWorkspaceRuntimePlanFromWorkspace() in runner-prep.ts starts from the workspace directory:
- reads
workspace.yaml - calls
collectWorkspaceRuntimePlanReferences() - loads the referenced files from disk
- passes everything into
compileWorkspaceRuntimePlan()
Today the reference collection is intentionally narrow:
AGENTS.md- every
applications[].config_pathfromworkspace.yaml
That means app manifests are runtime-plan inputs, but arbitrary workspace files are not unless later code stages them explicitly.
Representative authored inputs from workspace-runtime-plan.test.ts:
# workspace.yaml
template_id: social_operator
name: Social Operator
agents:
id: workspace.general
model: gpt-4o
applications:
- app_id: holaposter-ts-lite
config_path: apps/holaposter-ts-lite/app.runtime.yaml
mcp_registry:
allowlist:
tool_ids:
- holaposter.create_post
servers:
holaposter:
type: remote
url: "http://localhost:3099/mcp"
enabled: true# apps/holaposter-ts-lite/app.runtime.yaml
app_id: holaposter-ts-lite
healthchecks:
mcp:
path: /mcp/health
timeout_s: 60
interval_s: 5
mcp:
transport: http-sse
port: 3099
path: /mcp
env_contract:
- HOLABOSS_USER_IDStage 2: Compile workspace.yaml
compileWorkspaceRuntimePlan() turns the authored workspace contract into a structured plan with:
general_configresolved_promptsresolved_mcp_serversresolved_mcp_tool_refsworkspace_mcp_catalogresolved_applicationsmcp_tool_allowlistconfig_checksum
This is the runtime-owned interpretation of the authored workspace, not a loose YAML parse.
Strict Runtime-Plan Rules
workspace-runtime-plan.ts is intentionally strict. Current important rules:
workspace.yamlmust parse to a mapping objectmcp_registryis requiredtool_registryis explicitly unsupported- when
mcp_registry.allowlist.tool_idsis provided and non-empty, MCP tool ids must beserver.tool - when
mcp_registry.allowlist.tool_idsis omitted or empty, the runtime keeps all enabled configured MCP servers for that run and lets the host discover all tools from those servers at runtime - when some servers have explicit allowlisted tool ids and others do not, the runtime carries both: explicit
resolved_mcp_tool_refsfor the constrained servers and connected server payloads for the still-discoverable servers mcp_registry.servers.workspacemust be localapplicationsmust be a list of mappingsapplications[].app_idmust be uniqueapplications[].config_pathmust resolve to an app manifest in the workspace references- each
app.runtime.yamlapp_idmust match the declaredapp_id - each resolved app manifest must declare
mcp.port
If you are changing config shape, start here before you touch the harness or desktop.
Stage 3: Prepare Runtime-Owned Capability Surfaces
ts-runner.ts does not hand the compiled plan straight to the harness.
Before the harness launch it also:
- stages browser tools through the selected harness plugin
- stages runtime tools through the selected harness plugin, including runtime-owned mutate paths such as
write_report - resolves workspace skills
- optionally stages skills and workspace commands depending on the runner prep plan
- maps logical MCP server ids to physical server ids with
mcpServerIdMap() - starts the workspace MCP sidecar when
workspace_mcp_catalogis non-empty - bootstraps resolved applications and merges app-provided MCP servers into the prepared MCP payloads
This is where runtime-owned tool visibility stops being a static config file and becomes a run-specific capability surface.
For remote MCP auth values, runner-prep.ts also resolves header and environment placeholders of the form {env:ENV_VAR_NAME} before the host request is built. That syntax expects an environment variable name, not a literal secret value.
Correct:
headers:
CONTEXT7_API_KEY: "{env:CONTEXT7_API_KEY}"Also correct:
headers:
CONTEXT7_API_KEY: "ctx7sk-..."Incorrect:
headers:
CONTEXT7_API_KEY: "{env:ctx7sk-...}"That last shape is now rejected during runner prep instead of being forwarded as a bogus remote header.
Stage 4: Load Runtime Context
The runner then loads the context that is specific to this run:
- recent runtime context from turn results or compaction boundaries
- session resume context from artifacts, compaction boundaries, and session-memory files
- recalled memory context from the memory registry and vector recall path
- current user profile context
- operator surface context from the desktop browser capability base URL when desktop browser tooling is active
- pending user-memory proposals for the current input
Those inputs come from both filesystem state and runtime.db. They are runtime-owned context, not authored workspace config.
Stage 5: Project Agent Runtime Config
buildAgentRuntimeConfigRequest() and projectAgentRuntimeConfig() turn the compile output plus staged context into the final runtime config.
Important outputs include:
system_promptprompt_sectionsprompt_layersprompt_cache_profilemodel_clienttoolsworkspace_tool_idsworkspace_skill_idsoutput_schema_member_idoutput_formatworkspace_config_checksumcapability_manifest
This is also where response-delivery guidance and operator surface context become prompt-visible context for the run. If the harness sees the wrong tools, wrong prompt layers, wrong selected model, or wrong output schema, this is usually the page and code seam you wanted.
For MCP, this stage now distinguishes two cases:
- explicit allowlist:
resolved_mcp_tool_refscontains concrete tool ids, so the capability manifest and prompt can list them directly - omitted or empty allowlist:
resolved_mcp_tool_refsmay stay empty while the runtime still carries connected MCP server ids into the runtime config; the prompt can then say which servers are connected even though the host will discover the concrete tool names later - mixed mode:
resolved_mcp_tool_refscan contain concrete tool ids for some servers while other connected servers remain discovery-backed for that run
That keeps the prompt aligned with runtime truth without turning prompt text into the enforcement boundary.
Model selection and reasoning effort split here:
- the queued input can carry a selected
model projectAgentRuntimeConfig()resolves that intoprovider_id,model_id, andmodel_client- the queued input can also carry
thinking_value, but that value is not folded intoruntime-config.jsonor the projected model client
That separation is intentional. The runtime owns model routing, while the harness host owns executor-specific reasoning controls.
Representative projection shape:
const selectedModel = request.selected_model?.trim() || request.agent.model;
const target = resolveRuntimeModelTarget(selectedModel, request.default_provider_id);
return {
provider_id: target.providerId,
model_id: target.modelId,
model_client: resolveModelClientConfig(request, target),
tools,
workspace_tool_ids: workspaceToolIds,
workspace_skill_ids: request.workspace_skill_ids ?? [],
output_schema_member_id: outputSchemaMemberId,
output_format: outputFormat,
};Stage 6: Build the Harness Request and Snapshot It
After runtime config projection, the harness adapter builds the host request.
ts-runner.ts then:
- computes a request fingerprint
- measures the
persist_turn_request_snapshotbootstrap stage and callspersistTurnRequestSnapshot() - persists a sanitized
turn_request_snapshotinruntime.db - carries request-scoped execution metadata such as
thinking_valueinto the reduced host request alongside the resolved model client - includes MCP server mapping metadata and bootstrap timing details in the run-started payload
- launches the harness host with the reduced request payload
That snapshot is the runtime’s replay and debugging seam. It is how the system preserves what was actually sent across the execution boundary.
Change the Right File
- Change
workspace-runtime-plan.tswhen you are changing authored config parsing, validation, MCP registry rules, or app-manifest resolution. - Change
runner-prep.tswhen you are changing workspace reference loading, MCP payload shaping, or logical-to-physical MCP server mapping. - Change
ts-runner.tswhen you are changing bootstrap order, sidecar/app startup timing, context loading, request snapshots, or the handoff into the harness host. - Change
agent-runtime-config.tswhen you are changing prompt composition, capability manifests, selected model behavior, or output-schema projection.
Debugging Tips
workspace_config_checksumchanging unexpectedly usually means the authored workspace input changed, not the harness.- Missing MCP tools often come from
mcp_registrycompile rules or server-id mapping, not from the host implementation. - If MCP is configured but the prompt only summarizes connected server ids, that usually means the allowlist was omitted and concrete tool names will be discovered in the host at runtime.
- If a remote MCP server reports invalid auth and your header value looks like
{env:ctx7sk-...}, that is a config mistake. Use a literalctx7sk-...value or{env:CONTEXT7_API_KEY}. - Ambiguous
here,this page, orwhat am I looking atbehavior usually comes from operator surface context loading, not fromworkspace.yaml. - Wrong prompt context often comes from runtime context loading or
projectAgentRuntimeConfig(), not fromworkspace.yaml. - Wrong reasoning effort usually comes from the queued
thinking_value, catalog metadata, or harness-host normalization, not fromworkspace.yaml. - If the runtime-plan compile fails before a run starts, use the dedicated workspace-runtime-plan CLI entrypoint from
runtime/api-server.
Validation
npm run runtime:api-server:typecheck
npm run runtime:api-server:test
npm run runtime:harness-host:test
npm run runtime:testruntime/api-server/src/app.test.ts, runtime/state-store/src/store.test.ts, and the focused runtime package tests are the fastest executable references when this pipeline is unclear.