Compiler Specification
Spawnfile Compiler v0.1
Section titled “Spawnfile Compiler v0.1”This document is the implementation companion to SPEC.md.
SPEC.md defines the canonical source format. This file defines the reference compiler shape for v0.1 so the project can move into implementation.
Related specs: CONTAINERS.md (container compilation), SURFACES.md (portable communication surfaces), RUNTIMES.md (runtime registry and version pinning).
The v0.1 compiler should do four things well:
- load a Spawnfile graph deterministically
- resolve effective runtime and execution configuration
- hand resolved nodes to runtime adapters
- emit stable runtime-native outputs, generated team-context artifacts, container artifacts, and a machine-readable compile report
It should not try to solve packaging, publishing, deployment orchestration, runtime-native channel setup, or runtime coordination in v0.1. The compiler does not inject custom MCP tools, proxy/router processes, or team-internal RPC mechanisms.
The compiler has a write-only runtime boundary. It may write generated files, config, env files, mounted credential stores, generated secrets, and future explicit operator-triggered updates. It must not read spawned runtimes, containers, runtime homes, or agent workspaces to discover identity, infer organization state, rewrite rosters, or maintain live coordination state.
Compiler Pipeline
Section titled “Compiler Pipeline”The reference pipeline is:
- Parse the root
Spawnfile. - Validate local schema and file references.
- Walk the manifest graph through
members[*].refandsubagents[*].ref. - Detect cycles and incompatible duplicate references.
- Resolve effective
runtimeandexecutionfor every graph node. - Resolve
descriptionfor every agent node (from manifest or derived fromworkspace.docs.identity). - Resolve workspace, skills, and environment inheritance context for each concrete agent.
- Resolve schedule lowering constraints.
- Build a normalized intermediate representation.
- Group resolved nodes by runtime.
- Invoke runtime adapters.
- Resolve team representatives, team-context files, roster files, and team-network artifacts.
- Merge generated files into compiled workspaces.
- Place or point to generated team-context orientation through each runtime’s system-instruction surface when possible.
- Attach compiler-owned capability outcomes and warning diagnostics.
- Enforce policy after report augmentation.
- Generate container artifacts from adapter container metadata.
- Emit
spawnfile-report.json.
The compiler should operate on resolved IR, not on raw YAML, after the graph phase.
Graph Rules
Section titled “Graph Rules”Node Identity
Section titled “Node Identity”Graph nodes are keyed by canonical manifest path.
This is intentionally simple for v0.1:
- one manifest path = one logical node
- repeated refs are allowed only if they resolve identically
If the same manifest path resolves to different effective runtime or execution values in one compile graph, compilation should fail.
Stable Node IDs
Section titled “Stable Node IDs”The compiler should expose a stable node id separate from the internal manifest-path key.
Recommended rule:
- start from
<kind>:<name> - if that collides within one compile run, append a short hash of the canonical manifest path
Examples:
agent:analystteam:research-cellagent:assistant#4f2a9c1d
Output directory names should use the slug portion of that id.
There are two edge kinds:
team_membersubagent
The compiler should preserve edge kind in the IR because adapters may treat them very differently.
Cycles
Section titled “Cycles”Cycles are invalid in v0.1.
Examples that must fail:
- team A includes team B and team B includes team A
- agent A includes subagent B and subagent B includes subagent A
- any mixed cycle across teams and subagents
Effective Resolution
Section titled “Effective Resolution”The canonical resolution rules for runtime, execution, and other surfaces are defined in SPEC.md sections 2.4, 2.5, 3.4, and 4.4. This section covers implementation notes beyond what the spec defines.
Runtime Normalization
Section titled “Runtime Normalization”runtime should be normalized internally to:
runtime: name: openclaw options: {}even when authored as a shorthand string. This normalization happens once during graph loading so that adapters always receive a consistent shape.
Resolution Implementation
Section titled “Resolution Implementation”The compiler resolves effective configuration during graph walking, not as a separate pass. Each node’s effective runtime, execution, workspace.docs, workspace.resources, workspace.skills, environment, and schedule are computed when the node is first visited, and the result is cached by canonical manifest path with a fingerprint to detect conflicting resolutions.
Intermediate Representation
Section titled “Intermediate Representation”The compiler should normalize manifests into two node types and one plan.
Resolved IR is not Spawnfile authoring syntax. The examples below use compiler field names after canonical workspace.*, environment.*, shared.workspace.*, and shared.environment.* declarations have been resolved. Fields such as docs, workspaceResources, skills, mcpServers, env, secrets, and packages MUST NOT be accepted as top-level authored manifest fields.
Resolved Agent
Section titled “Resolved Agent”kind: agentid: agent:analystsource: /abs/path/to/Spawnfileruntime: name: openclaw options: {}execution: model: primary: provider: anthropic name: claude-sonnet-4-5 auth: method: api_keysurfaces: discord: access: mode: allowlist users: - "987654321098765432" bot_token_secret: DISCORD_BOT_TOKEN telegram: access: mode: allowlist users: - "123456789" bot_token_secret: TELEGRAM_BOT_TOKEN slack: access: mode: allowlist users: - "U1234567890" app_token_secret: SLACK_APP_TOKEN bot_token_secret: SLACK_BOT_TOKENdocs: []workspaceResources: []skills: []mcpServers: []env: {}secrets: []packages: []subagents: []Resolved Agent (additions)
Section titled “Resolved Agent (additions)”The resolved agent IR also includes:
description: "Research analyst that finds, evaluates, and synthesizes information"description is the agent’s short summary. If the manifest does not declare one, the compiler derives it from workspace.docs.identity by extracting the first non-empty paragraph, truncated to 200 characters. If no workspace.docs.identity is declared, the description is left empty.
Resolved Team
Section titled “Resolved Team”kind: teamid: team:research-cellsource: /abs/path/to/Spawnfiledescription: "Research team that finds, analyzes, and writes up findings"docs: []workspaceResources: []shared: env: {} mcpServers: [] secrets: [] packages: [] skills: []members: []mode: swarmlead: nullexternal: []networks: []Note: mode, lead, external, and networks are top-level team fields, not nested under structure. Team manifests do not carry an auth field in the current v0.1 contract.
Compile Plan
Section titled “Compile Plan”root: /abs/path/to/Spawnfilenodes: []edges: []runtimes: openclaw: nodeIds: [] picoclaw: nodeIds: []The important property is that adapters receive resolved nodes, never unresolved inheritance logic.
Team Roster Compilation
Section titled “Team Roster Compilation”When compiling a team, the compiler generates context-scoped team artifacts after resolving all member nodes and after runtime adapters emit their base files.
Compilation Steps
Section titled “Compilation Steps”- Resolve each member’s
descriptionfrom the agent manifest’sdescriptionfield, or derive it fromworkspace.docs.identityif available. - Build membership-context records keyed by
(agent-source, team-source, member-slot-id). The same agent source may fill several team roles without merging those contexts. - Resolve the representative interface for nested team slots using
external,lead, and swarm fallback. - Resolve
workspace.resources,workspace.skills, andenvironmentfor each concrete member from team-local and direct inheritance, preserving the manifest scope that declared each resource so team-shared backing storage can be scoped to that team. - Resolve team networks. Moltnet parent-room members that name child-team slots expand only to the child team’s selected concrete representatives.
- Emit a generated resource plan that lists each resource’s declared mount, concrete agent-visible link path, backing path, and sharing mode.
- Generate namespaced direct-membership
TEAM.mdfiles under.spawnfile/team-contexts/<team-context-key>/TEAM.md. - Generate context-scoped roster YAML under
.spawnfile/rosters/<team-context-key>.yaml. - Generate representative parent-context
TEAM.md, rosters, team cards,.spawnfile/team-contexts.yaml, and.spawnfile/team-contexts.mdfor selected representatives. - Emit root
TEAM.mdand.spawnfile/roster.yamlaliases only when a compiled agent has exactly one direct team membership. - Build coordination-graph diagnostics for each emitted team-context roster.
- Build schedule-wake capability diagnostics for declared schedules against adapter wake contracts.
- Attach compiler-owned capability outcomes before policy enforcement.
Roster Schema
Section titled “Roster Schema”team: research-cellmode: hierarchicallead: orchestratorself: researchercontext_kind: direct
members: orchestrator: role: lead description: "Coordinates the research team, assigns tasks, synthesizes results" surfaces: [moltnet, slack] addresses: moltnet: local_lab: fqid: "molt://local_lab/agents/orchestrator" rooms: [research-room] slack: user_id: "U1234567" writer: role: member description: "Writes reports and articles from research findings" surfaces: [moltnet] addresses: moltnet: local_lab: fqid: "molt://local_lab/agents/writer" rooms: [research-room]Roster entries carry context-scoped derivable per-surface addresses. The compiler does not synthesize routed endpoints. Moltnet addresses are derivable. Slack, Discord, Telegram, and WhatsApp addresses appear only when the agent manifest declares the corresponding surfaces.<name>.identity field. Portable HTTP addresses never appear because surfaces.http is not part of the alpha surface schema.
The roster is a per-member view:
selfidentifies which member this roster belongs to. The self agent does not appear inmembers.roleisleadfor the team lead,memberfor everyone else. For nested team entries,roleisteam.descriptioncomes from each agent’s resolved description.- In
hierarchicalmode, non-lead members only see the lead in their roster. The lead sees all members. - In
swarmmode, all members see all other members.
For nested team members, the inner team appears as a single entry with role: team, its own description, a team card path, and selected representatives. The outer team does not see the inner team’s full internal roster.
Context Artifacts
Section titled “Context Artifacts”Every direct membership receives:
.spawnfile/team-contexts/<team-context-key>/TEAM.md.spawnfile/rosters/<team-context-key>.yamlRepresentative agents also receive parent-context artifacts:
.spawnfile/team-contexts.yaml.spawnfile/team-contexts.md.spawnfile/team-contexts/<team-context-key>/TEAM.md.spawnfile/rosters/<team-context-key>.yaml.spawnfile/team-cards/<team-context-key>/<parent-member-slot-id>.mdTEAM.md is emitted literally from the team’s shared.workspace.docs.system source document. It bypasses runtime document-role mapping so it does not replace the agent’s own system instructions. The compiler must not merge several TEAM.md files.
EmittedFile remains a plain file-output contract:
interface EmittedFile { path: string; content: string; mode?: number;}Team-context discoverability uses runtime adapter metadata after files are emitted:
interface RuntimeSystemInstructionSurface { resolvePath(input: { node: ResolvedAgentNode }): string; placement: "append_pointer" | "append_inline" | "replace_generated_block";}The compiler performs this placement as post-processing. If a runtime cannot expose a system-instruction surface, it reports team.context_orientation as degraded or unsupported. Merely placing .spawnfile/team-contexts.md adjacent to runtime files is not enough when the runtime has a system-instruction surface.
Team Network Lowering
Section titled “Team Network Lowering”Team networks are provider-backed organizational communication topology. Moltnet is the current provider.
Rules:
- A parent team’s
networks[].rooms[].memberslist may name direct agent member IDs or direct child-team member IDs. - Direct child-team IDs expand through the child team’s representative chain, not to arbitrary descendants.
- The compiler synthesizes Moltnet room attachments for selected representatives because the parent room is declared organization membership, not a proxy.
- Moltnet member IDs are direct member slot IDs and must be unique across the reachable nested team graph.
- The compiler resolves each concrete generated attachment into a process-group key and emits Moltnet node configuration using
MoltnetNodetopology where possible. - Default process-group key is one concrete agent.
- The same Moltnet network-id may be reused across teams. Compatible duplicate attachments for the same
(network_id, member_id)merge rooms; incompatible duplicates fail compilation. - Moltnet
replypolicy isauto | neverin this alpha.manualis rejected or normalized out before generated config. - For each
(process group, network URL, network id, auth mode, token class)tuple, oneMoltnetNodemay carry multiple attachments; different tuples require separate nodes.
Moltnet Server/Auth/Store Lowering
Section titled “Moltnet Server/Auth/Store Lowering”serverblocks are required for networks that are materialized locally and are normalized by(provider, server.mode, server.url, server.listen, store, auth, pairings, managed server flags)identity.server.mode: managedlowerings generate a server config and a managed server process slot under the local lifecycle graph.server.mode: externalgenerates client/node config only.- Managed server config requires:
- explicit
server.listen - required
server.store - required
server.auth
- explicit
server.store.kindis mapped to Moltnetstoragesemantics:sqlite+ configured or defaultpathjson+ configured or defaultpathpostgres+dsn_secretmemory
- Durable
sqlite/jsonstores emitcontainer.persistent_mounts[];spawnfile runandspawnfile upmount them as Docker named volumes. - Generated open-mode agent token directories emit
container.persistent_mounts[]so first-claim credentials survive container replacement. - Secret-backed store fields (postgres DSN secret) are materialized into private runtime files at startup.
- Auth token materialization is always private and source-controlled outputs never include inline token values.
- In managed mode,
server.auth.tokens[]drives server config; each token is emitted as a secret-backed token entry using declared secret names and scopes. server.auth.clientis normalized into one oftoken_id,token_env, ortoken_pathand rejected if more than one is set.server.auth.modemapping:none: no client auth emitted.bearer: emits attach-capable client credentials for generated nodes. Whenserver.auth.agent_registration: openis declared without a client credential, generated node/client config lowers toauth_mode: open,registration: open, and per-agent writable token paths.open: emitsauth_mode: open,registration: open, and per-agent writable token paths unless a static token client source is provided.
server.auth.public_readandserver.auth.agent_registrationlower into native Moltnet auth config without changing generated node room authority.- Per-agent writable token paths are derived from the compiled agent slug and Moltnet member id so the generated
MoltnetNodeand generated.moltnet/config.jsonpoint to the same durable credential file. - Managed bearer mode requires
token_idand requires the referenced token to includeattachandwritescopes. - Managed and external open static token mode requires
static_token: trueon the configured client source. server.pairingsentries are materialized into managed server config and rejected on non-managed networks.- Managed
server.human_ingress,server.direct_messages,server.debug_events,server.console.analytics,server.trust_forwarded_proto, andserver.allowed_originslower directly into the Moltnet native server config. networks[].rooms[].visibilityandnetworks[].rooms[].write_policylower directly into native Moltnet room config after representative expansion. Member expansion still controls concrete room membership; room write policy controls who may send.
Coordination Diagnostics
Section titled “Coordination Diagnostics”For every emitted team-context roster with more than one visible concrete participant, the compiler builds a coordination graph. Nodes are visible concrete participants. Edges are shared declared coordination surfaces: a shared agent surface key in that context, or a shared team-network room after representative expansion.
The compiler reports warnings, not errors, when a visible concrete participant has no edge to another visible participant, when the whole cross-member graph has no edges, or when one concrete agent has the same surface binding tuple mapped to multiple team contexts. These diagnostics belong in spawnfile-report.json.
Compile Report
Section titled “Compile Report”The compile report should include team.roster for context-scoped roster emission and should attach warning diagnostics to the affected team node.
Adapter Contract
Section titled “Adapter Contract”Runtime adapters should implement the smallest interface that can work across all current runtimes.
Required Adapter Operations
Section titled “Required Adapter Operations”compileAgent- required
compileTeam- optional
validateRuntimeOptions- optional but recommended
Suggested Interface
Section titled “Suggested Interface”compileAgent(input): files: [] capabilities: [] diagnostics: []
compileTeam(input): files: [] capabilities: [] diagnostics: []Where:
files- emitted files relative to the node output directory
capabilities- per-capability outcomes
diagnostics- warnings and errors discovered by the adapter
Adapters may also expose systemInstructionSurface metadata so compiler post-processing can place or point to generated team-context orientation. The resolver returns a runtime-output-relative path for the concrete agent, not a static global path.
Team Lowering Rule
Section titled “Team Lowering Rule”The compiler should assume this default:
- every agent node is always compilable independently by its runtime adapter
- team-level compilation is optional and adapter-dependent
That means:
- if a team’s relevant members all belong to one runtime and the adapter supports teams,
compileTeammay emit native team artifacts - if a team spans multiple runtimes, the compiler should still compile the member agents and report any loss of native team semantics
This keeps multi-runtime teams possible without requiring a universal native team primitive.
Output Layout
Section titled “Output Layout”The default output root for v0.1 should be ./.spawn.
Within that root, the compiler should emit:
.spawn/├── Dockerfile├── entrypoint.sh├── .env.example├── container/│ └── rootfs/│ └── var/lib/spawnfile/instances/...├── runtimes/│ ├── openclaw/│ │ ├── agents/│ │ │ └── analyst/│ │ └── teams/│ │ └── research-cell/│ └── picoclaw/│ └── agents/│ └── researcher/└── spawnfile-report.jsonRules:
- one directory per runtime
- one stable directory per compiled node
- agent and team outputs are separated
- container artifacts are always emitted at the root for the full resolved graph
- the report file is always emitted at the root
If a runtime adapter emits nothing for a team node, the report should still record the attempted lowering and capability outcomes.
Compile Report
Section titled “Compile Report”The report should be JSON by default and written to spawnfile-report.json.
Top-Level Shape
Section titled “Top-Level Shape”{ "spawnfile_version": "0.1", "root": "/abs/path/to/Spawnfile", "nodes": [], "diagnostics": [], "container": {}}Node Entry Shape
Section titled “Node Entry Shape”{ "id": "agent:analyst", "kind": "agent", "source": "/abs/path/to/Spawnfile", "runtime": "openclaw", "runtime_ref": "v2026.3.13-1", "runtime_status": "active", "output_dir": "runtimes/openclaw/agents/analyst", "capabilities": [], "diagnostics": []}Capability Entry Shape
Section titled “Capability Entry Shape”{ "key": "execution.model", "outcome": "supported", "message": ""}Canonical Capability Keys
Section titled “Canonical Capability Keys”The compiler should use these keys by default:
workspace.docs.identityworkspace.docs.soulworkspace.docs.systemworkspace.docs.memoryworkspace.docs.heartbeatworkspace.docs.extras.<name>workspace.skills.<name-or-ref>mcp.<name>execution.modelexecution.sandboxagent.scheduleworkspace.resourcesworkspace.skillsenvironmentagent.subagentsteam.membersteam.modeteam.leadteam.externalteam.rosterteam.context_orientationteam.representativesteam.networksteam.networks.<provider>team.networks.<provider>.<network-id-key>team.sharedteam.shared.workspaceteam.shared.environmentteam.nestedsurfaces.<name>.identity
Adapters may add runtime-specific keys under:
runtime.options.*runtime.native.*
Those keys are informative, not part of the portable core.
Container Report Extension
Section titled “Container Report Extension”The compile report may include a container object describing the generated container artifacts for the full compile graph.
At minimum, this should cover:
- generated root files (
Dockerfile,entrypoint.sh,.env.example) - installed runtimes
- published ports
- required model/runtime secrets
- runtime instance config/home paths
- effective model auth methods per runtime instance
Recommended Validation Phases
Section titled “Recommended Validation Phases”Validation should happen in three layers:
1. Static Validation
Section titled “1. Static Validation”- YAML validity
- required fields
- path existence
- basic enum checks
- duplicate names and ids
2. Graph Validation
Section titled “2. Graph Validation”- cycle detection
- duplicate node resolution conflicts
- runtime resolution
- team mode/lead/external references
- team representative resolution
- team network member references
- duplicate Moltnet
member_iddetection across reachable nested teams - skill
requires.mcpresolution - duplicate
workspace.resourceIDs and overlapping agent-visible mounts within each concrete agent context - duplicate workspace resource identities within inherited resource sets and incompatible shared resource definitions
team.networks[].servernormalization checks (mode/store/auth/client/path/token/pairings compatibility)team.networks[].servermode/auth/store/dms and pairings compatibility checks- schedule lowering checks against declared adapter wake contracts
3. Adapter Validation
Section titled “3. Adapter Validation”- runtime option validation
- runtime-native config constraints
- capability preservation checks
This split will make error reporting much easier to keep sane.
Container Compilation
Section titled “Container Compilation”After adapters emit runtime-specific files, the compiler generates container artifacts at the output root.
See CONTAINERS.md for the full spec. The key rule is:
- one compile = one container
- the Dockerfile and entrypoint are derived from adapter-provided container metadata
- all agents, subagents, and team members in the compile graph share a single container
This is part of the main compile pipeline, not a separate authoring step.
Moltnet Team Conversation E2E
Section titled “Moltnet Team Conversation E2E”Compiler unit tests are not sufficient to prove that emitted team-network artifacts can coordinate at runtime. Spawnfile v0.1 requires an opt-in, release-gating Moltnet conversation E2E for the team-network contract.
The E2E should compile, build, and run a fixture with a parent team, nested child teams, explicit child representatives, a parent Moltnet room, and at least one representative that also belongs to its own child-team room. It must verify behavior through Moltnet room history rather than runtime stdout:
- parent room membership is exactly the direct parent agent plus selected child representatives
- non-representative descendants are absent from the parent room
- a real agent-to-agent exchange occurs in the parent room using
moltnet send - the same representative can also answer in its child room
- room history contains expected sentinels and compiled Moltnet member IDs
- failures print Docker logs and relevant room histories
Slack, Discord, Telegram, and WhatsApp do not require equivalent team-chat E2Es for this contract because Spawnfile only carries their declared identity/roster metadata. Moltnet is the provider Spawnfile provisions and lowers.
Operational Docker E2E
Section titled “Operational Docker E2E”Compiler unit tests are also not sufficient to prove that spawnfile up
materializes a working organization. Spawnfile v0.1 requires an opt-in
operational Docker E2E for schedule, Moltnet, and workspace-resource behavior.
The operational E2E should run spawnfile up on a deterministic fixture and
verify the running container rather than only inspecting compile output:
- a managed Moltnet server starts and passes health checks
- the runtime starts and exposes its local API inside the container
- the agent’s Moltnet node registers and attaches to the declared room
- a cron schedule wakes the agent through runtime-native scheduling
- agent-owned workspace resources are linked inside the agent’s runtime working directory
- team-shared workspace resources are linked inside the agent’s runtime working directory
- model credentials are not required for the smoke path
Coverage Targets
Section titled “Coverage Targets”Compiler and adapter verification should target feature behavior, not statement/line counts.
At minimum for v0.1:
- 90% coverage of feature-behavior scenarios for schedules, resources, roster generation, and team-network lowering.
- Line-based metrics are allowed for tooling only and are not an acceptance gate by themselves.
- Any behavior in
specs/not covered by tests is a blocker for feature completion.
Deferred For Later Versions
Section titled “Deferred For Later Versions”These should stay out of the core compiler architecture for v0.1:
- package builders
- publish flows
- lockfile and reproducibility records
- runtime-native auth bootstrap
- runtime-native chat features outside declared portable surfaces
- memory engine contracts
- multi-container orchestration (Docker Compose, Kubernetes, etc.)
First Fixtures
Section titled “First Fixtures”Before building adapters, the compiler should be tested against three canonical source projects:
- Single agent
- one runtime
- docs, skills, MCP, execution
- Agent with subagents
- inherited runtime
- merged execution
- Multi-runtime team
- direct members on different runtimes
- shared skills/MCP
- team with mode and lead
If these three fixtures compile cleanly and produce stable reports, the v0.1 foundation is strong enough to start adapters.