Workspace Manifest DSL Reference
The workspace manifest DSL is a schema-driven YAML specification. This page documents the schema structure, validation rules, and expression syntax.
Manifest Structure
Every workspace manifest has this root structure:
1# yaml-language-server: $schema=https://xema.dev/schemas/workspace/v1/WorkspaceManifest.json
2apiVersion: xema.dev/workspace/v1
3kind: WorkspaceManifest
4extends: xema://manifest/primary-agent-[email protected] # optional
5metadata:
6 slug: my-manifest
7 version: 1.0.0
8 title: "My Workspace"
9 description: Optional description
10spec:
11 inputs: {} # variables callers supply
12 mounts: {} # which workspace slots are mounted
13 agent: {} # agent identity and role
14 seedFiles: [] # files to bootstrap in /workspace
15 env: [] # environment variables| Field | Type | Required | Description |
|---|---|---|---|
apiVersion | string | Yes | Always xema.dev/workspace/v1 |
kind | string | Yes | Always WorkspaceManifest |
extends | string | No | Base manifest ref to inherit from (single-level) |
metadata.slug | string | Yes | Manifest identifier (lowercase kebab-case, 1–64 chars) |
metadata.version | string | Yes | SemVer format (MAJOR.MINOR.PATCH) |
metadata.title | string | No | Human-readable label |
metadata.description | string | No | Optional one-line summary |
spec.inputs | object | No | Input variable definitions |
spec.mounts | object | No | Workspace mounts configuration |
spec.agent | object | Yes | Agent identity |
spec.seedFiles | array | No | Files to bootstrap |
spec.env | array | No | Environment variables |
extends: — manifest inheritance
A manifest may inherit from a base template by setting extends to a
xema://manifest/<slug>@<version> reference. Resolution happens at
compile time: the base is fetched (user → project → org → biome →
system precedence — the standard five-tier ladder), merged into the
child, and the resulting effective shape is what the runtime consumes.
1extends: xema://manifest/primary-agent-[email protected]
2metadata:
3 slug: clarification-coordinator
4 version: 1.0.0
5spec:
6 agent:
7 slug: clarification-coordinator
8 stage: clarification
9 role: coordinator
10 mounts:
11 deliverable-specs: { mode: read-only } # added on top of baseMerge rules
mountsandinputs— key-union. Child's per-slot declaration wins on conflict.seedFilesandenv— concatenated (base first), with duplicates bypath/namekeeping the LAST occurrence (child wins).agent— child's block fully replaces the base's. The agent identity is not inheritable.
Constraints
- Single-level only — the base manifest itself MUST NOT carry an
extends:field. Multi-level inheritance is rejected at compile time. - Version-pinned — the URI requires a concrete
@MAJOR.MINOR.PATCHso old runs always resolve to the row they were authored against. - Fail-fast — missing base → typed error; malformed base → typed error.
Built-in templates
The platform ships four base templates at the system tier:
| Slug | Use Case |
|---|---|
primary-agent-base | Coordinator / unit-worker shape (inputs RO, references RO, uploads RO, deliverables RW) |
subagent-base | Same as primary-agent-base minus uploads |
reviewer-base | Read-only verdict shape (inputs/references/repos/deliverable-specs/deliverables, all read-only) |
agent-session-base | Chat-driven runs (inputs RO, references RO, uploads RO, deliverables RW) |
Biomes register their own base manifests by shipping additional
*.workspace.yaml files under workspace-manifests/ — at install
time each one is compiled and seeded into the LLM Registry as a
published agent composition, and other manifests can extends: it by
xema://manifest/<slug>@<version>. Orgs publish org-private base
manifests the same way: ship them via an org-scoped biome or author
them directly in the Agent Studio.
Inputs
Declare variables that callers supply at dispatch or session start. All ${input.x} references in the manifest must have a corresponding input declaration.
1spec:
2 inputs:
3 repoRef:
4 type: string
5 required: true
6 description: Repository owner/name
7 pattern: "^[a-zA-Z0-9-]+/[a-zA-Z0-9-]+$"
8
9 theme:
10 type: string
11 required: false
12 default: light
13 enum: [light, dark]
14 description: Visual theme preference
15
16 maxTokens:
17 type: number
18 required: false
19 default: 4000
20 minimum: 100
21 maximum: 10000
22
23 enableDebug:
24 type: boolean
25 required: false
26 default: false
27
28 kbSpaceIds:
29 type: array
30 required: false
31 default: []
32 description: Knowledge-base space IDs to includeInput Type Reference
| Type | Example | Notes |
|---|---|---|
string | "github.com/example/repo" | Can include pattern, enum, minLength, maxLength |
number | 4000 | Can include minimum, maximum, exclusiveMinimum, exclusiveMaximum |
boolean | true or false | No additional constraints |
array | ["space1", "space2"] | Items are strings or objects; no nesting |
object | { key: "value" } | Free-form; reserved for complex structures |
Input Constraints
1spec:
2 inputs:
3 # String with pattern
4 githubToken:
5 type: string
6 required: true
7 pattern: "^ghp_[a-zA-Z0-9]{36}$"
8 description: GitHub personal access token
9
10 # Number with bounds
11 concurrency:
12 type: number
13 required: false
14 default: 4
15 minimum: 1
16 maximum: 16
17
18 # Enum (closed set)
19 architecture:
20 type: string
21 required: true
22 enum: [monolith, microservices, serverless]Expression Syntax
Manifest values can include interpolation tokens resolved at bind time. The grammar is intentionally narrow — parameterized templates, not control flow — so the only form is single-brace ${ … }, and the only reference the manifest interpolator resolves is a declared input:
- Input:
${input.repoRef}— resolves to aspec.inputsvalue (a whole-value token preserves the input's declared type, e.g. an array stays an array). - Input with default:
${input.repoRef | default: 'main'}— falls back to a string ('foo'/"foo"), number, or boolean literal when the input is absent.
spec.env values may additionally reference a declared credential as ${credential.NAME} — see Environment Blocks. No other token namespaces (env.*, bare literals, arithmetic, function calls) are supported; an unrecognised token is a compile-time error (unsupported interpolation token).
Examples
1spec:
2 inputs:
3 repo:
4 type: string
5 required: true
6 debug:
7 type: boolean
8 default: false
9
10 mounts:
11 repos:
12 work:
13 - url: "https://github.com/${input.repo}"
14 role: primary
15
16 seedFiles:
17 - path: /workspace/config.json
18 content: |
19 {
20 "debug": ${input.debug},
21 "repo": "${input.repo}"
22 }
23
24 env:
25 - name: DEBUG_MODE
26 value: "${input.debug}"Expression Resolution Rules
- Type coercion: Expressions in YAML values are resolved to their declared type
- Missing input: If
required: trueinput is missing → compile error - Default fallback: If
required: falseinput is missing → usedefaultvalue - Null handling:
nullvalues are rejected unless explicitly allowed - No nested tokens:
${ ${ … } }is invalid - String interpolation: Expressions can be embedded in strings:
"prefix-${input.x}-suffix"
Validation Tiers
The manifest compiler validates in three tiers:
Tier 1: Structural
YAML syntax, required fields, type compatibility.
Examples of Tier 1 errors:
- YAML syntax error (bad indentation, invalid escape)
- Missing required field (
spec.agentnot present) - Type mismatch (
agent.slugis a number instead of string)
When caught: At manifest compile time — in your editor via the schema hint, and again when the biome that ships the manifest is installed.
Tier 2: Policy
Format rules, enums, closed-set validation.
Examples of Tier 2 errors:
- Slug format invalid:
"My Manifest"→ must be kebab-case"my-manifest" - Version not SemVer:
"1.0"→ must be"1.0.0" - Unknown enum value:
role: "unknown"→ must be one ofunit-worker,coordinator,gate-reviewer,clarification-coordinator,scope-validator,agent-session,brainstorming,generic-agent,auditor, orengineer. The compile error names the offending field as$.spec.agent.role.
When caught: At manifest compile time — in your editor via the schema hint, and again when the biome that ships the manifest is installed.
Tier 3: Semantic (Deferred to Runtime)
Agent existence, phase availability, mount source availability.
Examples of Tier 3 checks (deferred):
- Agent slug
"nonexistent"→ resolved at dispatch, fails if not found in LLM Registry - Phase key
"invalid"→ resolved at dispatch - Mount source (KB space, external repo) → resolved at dispatch
When caught: At workflow dispatch or session start (not at biome install / composition seed time)
Why deferred: These depend on runtime state (which org, which agent versions are available). The DSL validates what it can offline; the runtime validates the rest.
Schema URL
The manifest schema is published at the apiVersion-aligned URL (Kubernetes convention <group>/<version>/<Kind>):
GET https://xema.dev/schemas/workspace/v1/WorkspaceManifest.json
Add the schema hint to your YAML files for IDE autocomplete:
1# yaml-language-server: $schema=https://xema.dev/schemas/workspace/v1/WorkspaceManifest.jsonSchema Updates: breaking changes bump the apiVersion (e.g. xema.dev/workspace/v2) and ship at a new URL. Old apiVersions stay served until fully deprecated.
Error Messages
When manifest compilation fails — at biome install time, or in your editor via the schema hint — each error carries a structured code:
1{
2 "ok": false,
3 "errors": [
4 {
5 "code": "VALIDATION_ERROR",
6 "path": "spec.agent.slug",
7 "message": "agent slug must not be empty"
8 },
9 {
10 "code": "VALIDATION_ERROR",
11 "path": "spec.seedFiles[0].template",
12 "message": "template 'my-custom-template' does not resolve"
13 }
14 ]
15}Offline validation reports every failure as VALIDATION_ERROR with a path locating it and a message describing it. There is no per-cause code vocabulary at this layer — the path is what you branch on.
| What the message describes | Next step |
|---|---|
| A schema or structural failure | Fix the YAML at the reported path |
| A referenced template that does not resolve | Ship the template as a skill-bundle resource the manifest references |
| An expression that does not parse | Check the ${ … } token syntax |
Tier-3 (runtime) failures cannot be detected offline at all; they surface at dispatch.
Best Practices
- Always include the schema hint in your YAML files for IDE completion
- Use required inputs sparingly — most inputs should have sensible defaults
- Validate early — let the editor's YAML language server flag schema errors as you author
- Version your manifests — use SemVer; breaking changes bump MAJOR
- Document your inputs — include descriptions for all inputs so callers understand what they're for
- Use enums for closed sets — prefer
enum:over free-form strings for better validation
Related: Authoring Guide · Mounts Reference
Previous: ← Authoring Next: Mounts Reference →