08 — Templates¶
This is the Workflow Templates chapter: the JSON blueprints that turn one click into a fully wired agent team.
§04 — Core Concepts introduced a Workflow Template as the blueprint that wires a Team's topology, agents, tools, and scaffolding; it used the Backend Developer from Templates/fullstack-web.json as its worked example. This section is the authoring view of that blueprint: the file shape, the fields that matter, how the four topologies wire up, and what ships in the box. A template becomes a project two ways — you pick one from the catalog (handled by TemplateService) or you describe a team in plain language and let Quick Create synthesize one (handled by TeamGeneratorService). Both are covered as services in §06 — Key Services; this section is about the JSON they consume and produce.
It is organized as the instantiation flow, then the schema by annotated example, a field reference, the topology wiring patterns, and the catalog of what ships today. The live template files are the ground truth: they live in Templates/ in the server project, and the schema below is exact as of this writing — diff against the real files if anything looks off.
From JSON to a running project¶
Picking a template kicks off a fixed sequence in TemplateService. The file is parsed and, when it is written in the preset form below, expanded into a full team by the composition resolver (each node's role_id is filled from the role library, the recipe sets the topology, the edges and the circuit-breaker defaults) — so every reader of a template, from the catalog list to the Studio, sees the full team. Its team block is cloned into a project-creation request, the workspace directories are scaffolded, the project and its agent roles are written to PostgreSQL, a container is provisioned, and — only if the template opts in — the knowledge base is seeded. Then the project opens in the Unified Studio.
flowchart TB
JSON[Template JSON<br/>embedded or user file]:::ext --> Parse[TemplateService parses,<br/>expands roles + recipe]
Parse --> Build[BuildCreateRequest<br/>clones the team block]
Build --> Scaffold[ScaffoldDirectories<br/>creates workspace folders]
Scaffold --> Persist[ProjectService writes<br/>Project + Roles to PostgreSQL]
Persist --> Container[Container provisioned]
Container --> Seed{seedKnowledgeBase?}
Seed -- yes --> KB[Knowledge seeder enqueued]
Seed -- no --> Studio
KB --> Studio([Project opens in Unified Studio]):::ext
classDef ext fill:#D5C3B0,stroke:#2D2D2D,stroke-width:1.5px,color:#1A1A1A
The clone step is worth one note: BuildCreateRequest round-trips the team block through JSON with the naming policy turned off, so the explicit snake_case names on the schema (below) drive both directions and survive the copy intact. Knowledge seeding is fire-and-forget — the seeder is enqueued and the project opens immediately rather than blocking on it, so a slow or failing seed never stalls the launch. The Quick Create path differs only at the front: instead of parsing a file, TeamGeneratorService asks an LLM to synthesize the team configuration from your description, then the result converges on the same persistence-and-provision sequence. Either way you end up with a TeamConfig, a set of roles, a scaffolded workspace, and a container. The mechanism behind each box — TemplateService, the provisioner, the knowledge seeder — is detailed in §06.
The schema, by example¶
A template file has two nesting levels, and the casing differs between them. The outer object is the project blueprint: id, name, description, icon, category, defaultTier, the directories to scaffold, and the seedKnowledgeBase flag — all camelCase. The team block inside it is the team configuration, and everything there is snake_case: topology, config, nodes, edges, circuit_breaker, target_collections. Keeping those two casings straight is the single most common authoring mistake.
The team block can be written two ways, and both resolve to the same thing. The full form spells out every node — prompt, tools, model, mcp_servers — and the topology and edges; that is the form the examples below use, and it is what the Engine stores and runs. The preset form, which the fifteen shipped files use since the team-composition redesign, names a recipe (single, hierarchical, sequential, roundtable or dynamic-parallel) instead of topology + edges, and each node is just { "id", "role_id" } pointing at an entry in the role library (Templates/composition/roles.json: one prompt, one ordered tool list, an mcp_servers list and a model-policy slot per role). The resolver fills every field a preset node leaves empty from its role, sets the topology from the recipe, generates the edges (a hierarchical recipe wires the supervisor to every other node in file order) and supplies the recipe's circuit-breaker block when the preset declares none. A preset node may still set any field itself — a system_prompt, a model_id, extra tools — and what it sets wins. Mixing the forms in one file is fine.
Here is fullstack-web.json — the same template §04 drew its Backend Developer from — in excerpts. The outer identity block:
{
"id": "fullstack-web",
"name": "Fullstack Web Application",
"description": "4-agent hierarchical team for fullstack web development: one rooted Node.js application at /workspace that serves its own pages and API, built by frontend, backend and QA specialists.",
"icon": "bi-globe",
"category": "Web Development",
"defaultTier": "Production",
"directories": ["src", "public", "tests"],
"seedKnowledgeBase": true,
"team": { ... }
}
The team block sets the topology and the team-wide defaults, and carries a circuit_breaker block that bounds an agent's tool-loop rounds, a plan step's and the session's wall clock, and how many repeats of one call count as a loop:
"team": {
"name": "Fullstack Web Team",
"topology": "hierarchical",
"base_image": "node:20-slim",
"config": {
"model": "google/gemma-4-e4b",
"temperature": 0.7,
"context_window": 32768
},
"circuit_breaker": {
"max_turns_per_agent": 30,
"step_timeout_seconds": 300,
"session_timeout_seconds": 1800,
"infinite_loop_threshold": 3
},
"target_collections": ["web-development"],
"nodes": [ ... ],
"edges": [ ... ]
}
The team block can also carry container_init_commands — a list of shell lines run once, as root, when the project container is first created. fullstack-web uses them to install the native-module build toolchain (python3, make, g++), curl, and the serve/http-server static servers its agents rely on, so packages like better-sqlite3 compile on the slim base image; api-service installs curl the same way, because node:20-slim ships without it and the backend and QA roles check health with it. Agent commands themselves still run as the non-root workspace user. A command that exits non-zero fails the run before any agent runs, with the command and its output in the run's error; both templates' installs used to be wrapped to swallow that and no longer are.
Each entry in nodes is one agent. This is the Backend Developer node in full (its system_prompt body abbreviated — in the file it is a ~1,600-character string of path rules and a build-and-verify workflow):
{
"id": "backend",
"name": "Backend Developer",
"role": "worker",
"is_supervisor": false,
"model_id": "google/gemma-4-e4b",
"description": "API routes, database models, middleware, and server-side logic.",
"system_prompt": "You are the Backend Developer for a fullstack web project. You build the API, database layer, and server-side logic. […]",
"tools": ["read_file", "write_file", "edit_file", "list_files", "search_files", "todo", "revert_file", "run_shell", "read_logs", "web_search", "send_message_to", "reply"],
"mcp_servers": ["workspace", "shell", "research", "team", "orchestration"]
}
The edges array wires the team. For a hierarchical team it is the supervisor pointing at each worker:
"edges": [
{ "from": "supervisor", "to": "frontend" },
{ "from": "supervisor", "to": "backend" },
{ "from": "supervisor", "to": "qa" }
]
The four nodes that array wires together:
| Node | Role | is_supervisor |
model_id |
|---|---|---|---|
supervisor (Project Lead) |
supervisor | true |
google/gemma-4-e4b |
frontend (Frontend Developer) |
worker | false |
google/gemma-4-e4b |
backend (Backend Developer) |
worker | false |
google/gemma-4-e4b |
qa (QA Engineer) |
worker | false |
google/gemma-4-e4b |
All four nodes currently run the same model, but each declares it through a per-node model_id, which overrides the team default — that override is exactly how you would give the planner a different model than the hands. The supervisor's tool list is deliberately thin (it plans and delegates; it does not write application code), while the QA Engineer carries the validation tools. The shape of each agent is its persona, its prompt, its tools, and its model — nothing more.
The team block also ships an optional verification section (enabled in this template with "scope": "step"): deterministic artifact checks after file writes, plus an LLM verifier at step boundaries that reads the step goal, the worker's report, and the actual file contents, then bounces a FAIL back to the authoring worker for up to max_retries fix cycles. Teams without the block inherit the AI_VerificationEnabled system setting; key reference in the Configuration Reference.
The minimal template¶
Strip away the team machinery and you get a single-agent template. quick-script.json is the floor of the schema in the full form — one node, no edges, topology: "single":
{
"id": "quick-script",
"name": "Quick Script",
"category": "Scripting",
"seedKnowledgeBase": false,
"team": {
"topology": "single",
"config": { "model": "google/gemma-4-e4b", "context_window": 32768 },
"nodes": [
{
"id": "coder",
"name": "Script Developer",
"role": "worker",
"is_supervisor": false,
"model_id": "google/gemma-4-e4b",
"tools": ["read_file", "write_file", "run_shell", "web_search", "web_fetch"],
"mcp_servers": ["workspace", "shell", "research"]
}
],
"edges": []
}
}
The file as shipped is the same team in the preset form — "recipe": "single" and a single node { "id": "coder", "role_id": "script-developer" }, with the config block beside them — and resolves to exactly the JSON above. In the full form a node needs an id, a name, a system_prompt, and a tools list; in the preset form it needs an id and a role_id; everything else has a default or is inferred.
Field reference¶
The fields you will reach for most, beyond the self-explanatory id / name / description:
| Field | What it sets | Notes |
|---|---|---|
topology |
team.topology — the wiring shape |
One of hierarchical, sequential, roundtable, single, plus dynamic-parallel. Defaults to hierarchical. Set by the recipe when one is given. See §04. |
recipe |
team.recipe — the preset form's shape |
Same five names as topology. The resolver sets topology from it, generates edges when the file has none (hierarchical: supervisor → every other node; sequential: a chain), and fills circuit_breaker from the recipe's defaults when the file has none. Only a hierarchical team gets a supervisor: in a sequential team every node resolves to is_supervisor: false, so no stage is granted delegate. Recipes live in Templates/composition/recipes.json. |
role_id |
Node — a role-library entry to build the node from | Every field the node leaves empty (name, role, description, system_prompt, tools, mcp_servers, model_id via the role's model-policy slot, with that slot's instance as preferred_instance and its max_tokens when the slot names them, is_supervisor for a supervisor-kind role, requires_evidence for a reporting role) comes from Templates/composition/roles.json; a field the node sets stays. Kept on the stored team as provenance. |
tools |
Node — the agent's tool allowlist | Narrowed against mcp_servers; the model only sees tools in both. See §07. |
mcp_servers |
Node — server-class allowlist | Omit it and the Engine derives it from tools. The eleven group keys are in §07. |
model_id |
Node — which LLM runs this agent | Falls through to team.config.model when absent or empty; an operator-set model in system settings overrides the template's team model. See §09. |
is_supervisor |
Node — marks the delegating agent | In a hierarchical team the supervisor is always given delegate and reply even if its tools omit them. |
required_output |
Node — the workspace-relative paths this agent owes | A plan step run by the node is failed when a path is missing, when the file at it is empty, or when the file's first bytes are not the format its extension declares — an existence test alone passed a zero-byte .png and a .png holding the words "Placeholder content". The format check covers the self-identifying extensions (PNG, JPEG, GIF, BMP, PDF, ZIP, MP3, MP4/M4A/MOV, WAV, WebP, AVI, Ogg, FLAC) and the 3D formats FBX (binary or ASCII), BLEND, GLB, UMAP/UASSET and USDZ — a 3D deliverable that is text under that name fails with the note that nothing in the workspace makes 3D files; any other extension is checked for emptiness only, and an unreadable file is never held against the step. A path that is a link, or that reaches the file through one, counts as missing: the Engine opens workspace files on its own host and does not follow links an agent can plant. An entry ending in /, /* or /** is satisfied by any real deliverable beneath it, and dir/*.png (any extension) by one of that format only — a directory count once accepted six text placeholders as six photographs. A glob may also prescribe a file-name prefix ("images/el-*.png >= 6"), and then only files whose names begin that way count: a run that produced six correct photographs under names of its own invention satisfied images/*.png >= 6 while the config it was built from named every file, and nothing but the QA report noticed; an entry may carry a " >= N" suffix ("menu/images/*.png >= 10") and is then satisfied only by N such files of distinct content, counted by hash, empty or mis-declared files not counting. The suffix is syntax the gate reads, never text an agent sees — prompts spell it as at least N distinct files. An entry may also carry " words A-B" ("show/episode-01-script.md words 430-470"): the step fails when the file's word count falls outside the range, naming the count. On a glob the band holds for each file the step wrote that the glob matches (a file owed by its own exact path keeps its own entry), and a count and a band can share one entry, count first: "guide/*.md >= 5 words 150-250" asks for five distinct pages of 150–250 words each. A script counts its spoken words — the words tts_render would speak, by the same parser — when the entry names a cast (speakers), its path says script, or its speaker lines carry at least half its words; any other file is a document and every word counts (its visible text, for a page). A last clause " speakers PIXEL,BYTE" names the labels that must each have at least one spoken line; a script where one of them never speaks fails the step naming the silent ones. A word range carries a ten-word grace either side: the band still steers the writing and the writer is still told the real target when it misses, but a near miss no longer costs the step. Four further clauses judge a file's content rather than its name. " self-contained" on a page ("site/index.html self-contained") fails a page that loads a stylesheet, script, font or image from anywhere else, links a file that does not resolve from its own directory, links a #fragment whose id does not exist in the page it points at — a page may link stations.html#Sallowmere_Halt five times with none of those ids present, and every earlier check passed it — or points an <img>, a poster, an <audio> or a <video> at a local file whose extension is not that kind of media: a page once showed its map as <img src="images/placeholder_map.txt">, the reference resolved, and the page passed with a broken picture in it. Missing anchors are reported as one sentence per target page, naming the page to edit. " names A,B,C" ("kiosk/index.html self-contained names KD-1991.004,The Brackwater Astrolabe") fails a file that never mentions one of the listed strings, compared case-insensitively; for an HTML deliverable the comparison is against the page's visible text, not its bytes, so an accession number that appears only inside an href fragment does not count as mentioned. " forbids a,b,c" is its mirror and fails a file that does contain a listed string, matched as a whole word: a ban on cure fails cure, cures and cured but not secure, while a token whose edge is punctuation (©) matches anywhere. A copyright ban also matches ©, © and "all rights reserved", and the owed-file note the agent reads says so — a config can say what a deliverable must not carry, which in prose alone it could not enforce. " lasting >= Ns", optionally " lasting >= Ns with audio" ("video/film.mp4 lasting >= 90s with audio"), fails a media file whose video stream is shorter than N seconds, or which carries no audio when the clause asks for it; the video stream is read rather than the container because the two disagree on exactly this defect, and a one-second slideshow reports a plausible container duration. " shows DIR/ >= N" ("site/index.html self-contained shows images/ >= 3") fails a page that does not DISPLAY the run's own output: at least N distinct non-empty files of the right format under DIR/, its subfolders included (folders whose names begin with a dot are not counted), must be referenced by the page, and the references must resolve from the page's directory. A reference that names one of those files but does not resolve is reported as a broken reference rather than an absent one. A DIR/ that is still empty when the page's step is gated gives the clause nothing to hold the page to, so a planner's page step is moved to run after the last step that owes files under DIR/ (a template's declared plan_steps keep their order), and the clause is checked again once the run is over: a page then short of its pictures, or linking one that does not resolve, is listed in the run's review notes. It exists because a run can generate every asset correctly and publish a page carrying none of them, with every other clause passing. The content clauses exist because every other clause asks whether a file resolves, how long it is or who speaks in it, and none of them can ask whether it is about the right thing. " verdict PASS" ("qa/report.md verdict PASS") holds a deliverable to the conclusion it reaches: the step fails when the file states a different verdict, and when it states none at all, because a report that reaches no conclusion certified nothing. A report that states the verdict it owes is also held to the workspace paths it cites: a cited path with a folder and an extension that is not on disk fails the entry, naming the paths, so a PASS cannot rest on files no stage wrote; a FAIL that names a missing file is a finding and is not checked, and a name with a placeholder part (images/mr-XX.png, stop-N.html) is a pattern for a family of files and cites none. On the plan path, only the node that owes a verdict report may write it: another node's write, edit or revert of that report is refused, naming its owner. It is the only clause that can fail a step for work that is wrong rather than missing — every other one asks whether a file resolves, how long it plays, who speaks in it or which strings it shows, and none can ask whether the thing is any good. A QA report once found five invented names and five invented dates, wrote OVERALL VERDICT: FAIL, and the run finished Completed because the node owed only that the word VERDICT appear. A stated verdict is the whole word "verdict" followed by a colon, a dash or the end of a heading line, then the value; decoration is ignored (## OVERALL VERDICT: FAIL, **Verdict:** pass and **Verdict:** `PASS` all parse), while prose such as "the verdict is final" or an "Activity verdicts" table heading states nothing. The value is never the word "verdict" itself (## Verdict over VERDICT: PASS reads PASS); under a heading, a line of prose is skipped (PASS with minor issues still reads PASS); a line that offers a choice (PASS or FAIL, PASS/FAIL) is a template and states nothing; and two verdicts on one line must agree, or the line states none. The last verdict wins, since a report that discusses the idea before reaching its own states it at the end. The repair message asks for the work to be fixed and re-checked, never for the verdict line to be changed. " transcribes A,B" ("site/listen.html transcribes audio/scripts/s1.md,audio/scripts/s2.md") holds a page to the words of the recordings it plays: every spoken line of each listed script — the lines tts_render speaks, by the same parser — must appear in the page's visible text, compared with case, punctuation and markup folded away, and one line in ten may be missing. The step fails naming how many lines are missing and the first of them, or naming a listed script that is not on disk. It exists because a page asked to play its stories "with their transcripts" could carry a one-line synopsis of each and pass every other clause. " links GLOB" ("guide/index.md links guide/*.md") holds an index to the set it lists: every file the glob matches on disk, other than the page itself, must be linked from it (a markdown link or an href/src), and every relative link it writes must name a file that exists — a link starting with / is read from the workspace root, any other from the page's own directory; external links and #fragments are not judged. The step fails naming the files not linked and the links to nothing. It exists because an index owed by its path alone passed twice, once linking five files that were never written and once as a single heading. " playable" ("site/index.html self-contained playable") holds a page to controls a visitor can use: the page is opened in a browser, each visible button and control is clicked once, and the step fails when none of them changes what the page shows. " keyboard-playable" is the clause for a game: the page must set window.game.state to { scene, x, y, frames } for the player, its frame loop must raise frames, and holding → or D for a second must move the player — the check focuses the page's largest canvas and holds each arrow key and W/A/S/D in turn. A generated team that makes a game owes keyboard-playable rather than playable, since a canvas game steered by keys has nothing to click. Clause order is free: self-contained, names, forbids, lasting, shows, verdict, transcribes, links, playable and keyboard-playable may be written in any order, except that names must precede forbids. Whatever its clauses, every HTML page a step owes by exact path is also opened in a browser when the step ends: a page whose script throws (on load or on a click), that asks for a file that does not load, or with text under 3:1 against its background is sent back to the step that wrote it, naming the error, the file or the element. A page that loads scripts from elsewhere but has no script of its own (no inline code, no local or module script, no inline handler) is sent back too, naming the libraries it loads: nothing on it runs. Pages whose only script works on its own (the Tailwind Play CDN and similar) are exempt. Its visible text is read too: a page that says it is a stand-in ("this is a placeholder …", "replace with actual …", lorem ipsum) is sent back with the words it carries. |
!!! note "Rendered audio is checked against its script"
This is not a clause and needs no syntax. `tts_render` writes a `<output>.render.json`
sidecar recording the script it read and the spoken line and word counts it rendered
from. Whenever a step owes that audio file, the Engine re-parses the named script with
the same parser and fails the step when the counts no longer match — a recording of a
script that has since been edited is out of date, however long it plays and however
well the script itself scores. Audio with no sidecar makes no claim and is not judged.
A page reference is read from `src`, `href`, `poster`, every candidate of a `srcset`, and
every CSS `url()`, for both the `self-contained` and `shows` clauses. The one script a
`self-contained` page may load from a file is the Engine's starter kit (below): a `kit.js`
beside the page that opens with the kit's own header. A script from anywhere else, or a
look-alike file of the same name, is still refused. A path a script builds in a template
literal (`${…}`) is not judged here; the browser check that opens every owed page finds it
if it does not load.
| tts_voices, tts_default_voice | Node — the cast for tts_render | A speaker label → voice map and the voice for unmapped speakers — each voice a reference voice name or a description (design: warm, unhurried woman in her sixties), which tts_render designs once per template and reuses — read by the tool itself when the model omits its voices argument (an explicit entry still wins). A key may instead name a recording — its path, its file name, or its stem ("audio/s1.mp3", "s1") — and when that is the file being rendered, its voice reads the file in place of the node's speaker keys; a default_voice the call passes still wins, and a bare key such as host stays a speaker label unless it is the stem of the file being rendered. A speaker-keyed map can name NARRATOR only once, so two recordings narrated by different voices need file keys. Declared on the node so the cast does not depend on the model repeating the map on every call. |
| enforce_node_required_output | team — hold a step to its node's list | false by default. When the planner declares a strict subset of a node's required_output, the node's list replaces the step's rather than the difference being logged and dropped — and when the planner declares none at all, which used to switch the gate off. It only ever asserts paths the node already declares. restaurant-kitchen is the one shipped template that sets it. A step's declarations are also split at its start into deliverables and assertions: an entry whose file is already on disk and already meets its clause is stated to the worker as already correct, do not rewrite, is kept out of the write orders and out of the in-loop missing set, and a write_file to it is refused — while the end-of-step gate still checks it, so re-declaring a finished file remains the way to catch a later step damaging it. |
| plan_serial_steps | team — hold the planner to a serial plan | false by default. When true, create_plan makes every step depend on the previous one, whatever the planner declared; explicit extra dependencies are kept. With enforce_node_required_output also on, every step assigned to a node that declares required_output carries that declaration at plan time: an empty list is filled and a planner's own paths are replaced (compared without /workspace/ and the gate suffixes), so the worker is told the checked paths at the start instead of failing for obeying the planner. |
| plan_steps | team — declare the plan | A list of { "agent_id", "description" }. When present, the planner's judgment on which steps and in what order is replaced: one step per entry in that order, assigned to that agent, serial, with the node's required_output. The entry's own description is what the worker reads; the planner's step for the same agent lends its description only where the entry leaves one out. It was the other way round until 2026-09-18, which meant an author's wording — the paths, the length, the cast — usually never reached the worker, and a planner could hand a step an instruction the template contradicts. |
| image_model | team — the team's image model | Unset renders comfyui_generate with SDXL on the comfyui service. "qwen" renders on Qwen-Image 2.1 through its own leased service, comfyui-qwen (the run's lease starts whichever ComfyUI matches image_model, whatever the template declared: comfyui-qwen for "qwen", comfyui-zimage for "z-image", comfyui otherwise), enables the tool's ratio and reference_images, paints transparent pieces itself (SDXL paints them on magenta, which the Engine cuts out), and makes the team eligible for the post-run 2K re-render when that is switched on (Aetherium:ImageFinishing:Enabled, off by default). See finish_image. The four shipped templates that make images — poster-pipeline, poster-studio, restaurant-kitchen and steam-frame-vr — set "qwen", and a generated team that holds comfyui_generate gets it too, including one that also holds tts_render (until 2026-10-11 such a team stayed on SDXL). The run's lease then holds both comfyui-qwen and the voice service. "z-image" renders on Z-Image Turbo (Apache-2.0) through comfyui-zimage: text to image only, with ratio and with transparent pieces painted on magenta and cut out as on SDXL; see comfyui_generate. |
| starter_kit_dir | team — where the canvas game kit goes | Unset copies nothing. A folder inside the workspace (site/kit): when the project is created, from a template or by Quick Create, the Engine copies its canvas game kit there — kit.js (the frame loop, keyboard and touch input, pictures, camera, scenes, overlap tests and the window.game hook the keyboard play test reads) and its README.md — and adds a paragraph to project-config.md saying where it is. Files already in the workspace are left alone. The generator sets site/kit when it holds a page to keyboard-playable, and tells that team's image role to draw the player and each collectable as separate transparent pieces as well as the scenes. |
| film_motion | team — a moving film | false by default. When true, the film a team cuts from its stills moves (each still slowly zooms in on a blurred fill of itself) instead of holding each still for its share of the narration. A template that sets it has the slideshow command baked into its prompts swapped for the moving one when a project is created from it. No shipped template sets it. |
| context_window | team.config.context_window — token budget | Defaults to 16384; the shipped templates set 24576–32768. Drives the truncation in §06. |
| discussion_rounds | team.config.discussion_rounds — Roundtable discussion rounds | Defaults to 2; 1 reproduces the legacy single-round behavior. Ignored by the other topologies. See §04. |
| reflection | Node — an optional self-critique pass | A ReflectionConfig object, off by default. No shipped template enables it; see §06. |
| target_collections | team — knowledge collections agents write into | Routes write_knowledge calls by injecting the collection names into agent prompts. A team the Quick Create generator made that names none is given its own domain when its project is created, named from the project's slug and id (docs-team-p42), so its run notes have a domain to be offered to and two projects with one name never share one; a template's team keeps exactly what it declares. See §09. |
| knowledge_packs | team — the reference packs the team searches | Unset means the Engine detects packs from the base image, prompts and tools; [] means none; a list is exactly those packs, plus detected ones when auto_detect_knowledge is true. A pack detected only from the base image reaches just the nodes that run code. A node holding comfyui_generate also gets the image packs for its team's image_model (image-common plus image-qwen or image-sdxl). Every shipped template sets it: fullstack-web names nodejs-20, react-19 and tailwindcss-4; api-service and quick-script-js nodejs-20; quick-script and code-review-panel python-3.12 and pytest; maintenance-team and quick-fix dotnet-9; the rest []. The team designer edits it, and a name the pack registry does not know is refused. |
| seedKnowledgeBase | Outer object (camelCase) — pre-seed the KB at creation | Independent of target_collections: this gates the one-time seeding job; target_collections governs runtime writes. See §09. |
| circuit_breaker | team.circuit_breaker — the team's safety limits | max_turns_per_agent, step_timeout_seconds, session_timeout_seconds, infinite_loop_threshold, max_replans_per_session and thinking_model_overrides; a preset that declares none gets its recipe's block. Team-wide, not per node. There are no tool-call or per-tool caps: per_tool_limits, max_tool_calls_per_session, max_turns_per_step, max_tool_calls_per_step and max_session_tokens were removed on 2026-10-08 and are ignored if a file still sets them. Fields and defaults in Schema. |
tool_defaults is not a template field
Argument-layer defaults (for example, pinning the collection argument on every write_knowledge call) are real, but they are not authored in a template. They are supplied per run on the orchestrator API request and flow through to the router as it dispatches each call — the runtime path is RunRequest → McpSessionContext.ToolDefaults → McpToolRouter, covered in §06 and §07. There is no tool_defaults key in any template.
Topology wiring¶
The edges array only does real work for one topology. Here is the shape each of the five takes.
Hierarchical — the supervisor points at each worker; the supervisor delegates, workers report back:
Sequential — a chain, each stage feeding the next:
Roundtable — no edges needed. At runtime the team discusses over team.config.discussion_rounds rounds (default 2): every node answers the same prompt independently in parallel in round one, and from round two on each sees its peers' previous-round responses to critique and reconcile:
Single — one node, no edges (the quick-script shape above).
dynamic-parallel — also empty edges, because the team is built at runtime. research-team.json expresses it with a dynamic-parallel topology, a lead node holding the spawn_subagents tool, and a team-level subagent_defaults block that shapes the children it spawns:
"topology": "dynamic-parallel",
"subagent_defaults": {
"tools": ["web_search", "web_fetch", "download_pdf", "write_knowledge"],
"mcp_servers": ["workspace", "research", "knowledge"],
"max_turns": 20
}
In research-team, the lead's prompt instructs it to call spawn_subagents immediately with one child per subtopic — the spawn is the plan, so the lead never calls create_plan. Effort scales by spawning two to six children, each inheriting the subagent_defaults tool and server allowlist. The children run in parallel and write their findings into the team's target_collections, which is why this template pairs dynamic-parallel with knowledge tooling rather than delegation edges.
A subtlety worth knowing: Sequential and Roundtable derive their execution from the nodes array — Sequential runs the nodes in file order, Roundtable runs them all in parallel each round — so their edges are documentation of intent rather than something the runtime reads. Only Hierarchical consumes edges to know who delegates to whom. Both shapes now ship as templates — sequential-docs and roundtable-review, three documentation roles apiece — so you can run either without authoring one; the full build-from-scratch walkthrough is in §12 — Extending the Engine.
The catalog¶
Twenty-six templates ship today. Three did the narrative work above (fullstack-web, quick-script, research-team); the rest fill out the catalog.
| Template | Topology | Agents | Category | Intended use |
|---|---|---|---|---|
fullstack-web |
Hierarchical | 4 | Web Development | React/Vue frontend, Node API, QA |
api-service |
Hierarchical | 3 | Backend Development | REST API backend with QA |
documentation-team |
Hierarchical | 4 | Documentation | Doc lead, writers, and an editor |
sequential-docs |
Sequential | 3 | Documentation | Outline, draft, and edit one document down a chain |
roundtable-review |
Roundtable | 3 | Documentation | Three peers review the same brief, each writing their own verdict |
research-team |
dynamic-parallel |
1 + spawned | Research | Lead researcher spawns parallel subagents |
maintenance-team |
Hierarchical | 4 | Maintenance | Repair, upgrade, and validate existing systems |
finishing-team |
Hierarchical | 6 | Quality | Takes a finished build from default-styled to customized, in two phases with an operator answer between them |
business-analysis |
Hierarchical | 4 | Demo | Market and financial analysis into a report |
writers-room |
Hierarchical | 5 | Demo | Conversational prose — scripts, debates, scenes |
steam-frame-vr |
Hierarchical | 8 | Demo | Valve Steam Frame VR games in Unreal Engine 5.8 — C++, OpenXR, a standalone Android APK for the headset at 72 Hz, generated art and voice lines, and the device tools that install and run builds on the headset with a human's approval |
pop-culture-roast |
Hierarchical | 4 | Demo | A pop-culture magazine issue with a comedy roast desk — sourced features, a roast column, a front page |
podcast-pipeline |
Sequential | 3 | Demo | Researcher → scriptwriter → show-notes editor; every script line carries a fact reference the editor audits |
pitch-panel |
Roundtable | 3 | Demo | Three critics score one pitch through different lenses, then revise over two discussion rounds |
poster-studio |
Hierarchical | 3 | Demo | Art director, copywriter and an image artist generating poster images with ComfyUI under the run's gpu_services lease |
poster-pipeline |
Sequential | 2 | Demo | The same poster job without a planner — the image artist generates and records real image paths, the copywriter writes the sheet from that record |
code-review-panel |
Roundtable | 3 | Demo | Security, correctness and maintainability reviewers file independent verdicts over one code brief, then reconcile in a second round |
game-night-podcast |
Sequential | 4 | Demo | Researcher → scriptwriter → show-notes editor → giveaway writer; every script line carries a fact reference |
local-threads-retail |
Hierarchical | 5 | Demo | A retail upsell program — SKU catalog as strict JSON, an upsell strategy, and the staff-facing sheets |
restaurant-kitchen |
Hierarchical | 5 | Demo | Menu architect, food stylist and a cost sheet; the stylist photographs every dish through ComfyUI under the run's gpu_services lease |
space-camp-kids |
Hierarchical | 4 | Demo | A kids' astronomy bootcamp — a sourced fact base, five day plans, and the parent-facing handbook |
quick-script |
Single | 1 | Scripting | One-off Python scripting and automation |
quick-script-js |
Single | 1 | Scripting | One-off Node.js scripting and automation |
quick-fix |
Single | 1 | Maintenance | Small, self-contained fixes |
quick-research |
Single | 1 | Research | Single-agent web research with KB writes |
quick-docs |
Single | 1 | Documentation | Focused README and guide writing |
The catalog splits into two families. The five quick-* templates are single-agent, fast, and mostly skip knowledge seeding — they exist to get one agent working on a contained task with minimal setup. The twenty-one team templates are multi-agent and seed a knowledge base scoped to their domain; thirteen are hierarchical, four are sequential (sequential-docs, podcast-pipeline, poster-pipeline, game-night-podcast), three roundtable (roundtable-review, pitch-panel, code-review-panel) and one dynamic-parallel (research-team). Categories above are the real category values from the files; Demo marks the thirteen presets kept for the showcase that no benchmark exercises (business-analysis, writers-room, steam-frame-vr, pop-culture-roast, podcast-pipeline, pitch-panel, poster-studio, poster-pipeline, code-review-panel, game-night-podcast, local-threads-retail, restaurant-kitchen, space-camp-kids), so the Templates page groups them apart. Six templates set config.reasoning_effort, so their worker agents think before they act — pop-culture-roast, podcast-pipeline, pitch-panel, code-review-panel, finishing-team and game-night-podcast; the setting is per template and off everywhere else. poster-studio, poster-pipeline and restaurant-kitchen are the three templates that declare gpu_services. Twelve templates pin both model_id and preferred_instance on every node, finishing-team among them; the gemma nodes of restaurant-kitchen, space-camp-kids, fullstack-web, local-threads-retail and finishing-team are pinned to the Worker slot, and no shipped template pins its step verifier, so the verifier follows the operator's Aetherium:RolePlacement. A node with a model but no instance is moved at run start to the Aetherium:VramOrchestration:DefaultNodeInstance slot (Worker as shipped) when that slot's standard set holds exactly one model its id matches, and otherwise resolves to the primary slot, where it is loaded beside or in place of the supervisor model (§09). A few templates ship with their model_id left empty — documentation-team and writers-room among them — which means the agent resolves to whatever model the operator has configured rather than a pinned one. That is a deliberate "use my hardware's model" choice, not a missing value.
The flagship: steam-frame-vr
The largest template in the box is steam-frame-vr — eight agents in a hierarchical team (a Project Orchestrator delegating to game-module C++, VR interaction, asset pipeline, rendering, QA, Android packaging and documentation specialists) that stands up a Valve Steam Frame VR project in Unreal Engine 5.8 with ComfyUI-backed art, voice lines through tts_render, the steam-frame knowledge pack, and the device tools for the headset itself. The container cannot build Unreal or reach the headset, so the role prompts hand those steps to the operator, and the device tools stop for a human's approval. It is the reference deployment that shows how far a single template scales — but it is one example of what the Engine can wire, not what the Engine is for. The Engine is general-purpose; this is a worked demonstration, the same way fullstack-web is.
Common authoring pitfalls¶
A handful of mistakes account for most templates that load but misbehave:
- Mixing the two casings. The outer object is camelCase (
seedKnowledgeBase,defaultTier,projectConfigTemplate); everything underteamis snake_case (model_id,is_supervisor,target_collections). Writeseed_knowledge_baseormodelIdand the field is silently ignored — the deserializer keeps the default instead of erroring. - Forgetting that
mcp_serversis derived when omitted. If you leave a node'smcp_serversout, the Engine infers it from the node'stools. That is convenient, but if you then add a tool whose server group you did not expect, the agent gains reach you did not intend. Declaringmcp_serversexplicitly keeps the allowlist under your control — see §07. - Reading
team.config.modelas the final word. It is only the team default. A node'smodel_idoutranks it, and an operator's model setting in the running instance outranks the template entirely. An emptymodel_idis not a bug; it defers to that chain.
Where to go next¶
- §06 — Key Services —
TemplateServiceandTeamGeneratorService, the two paths a template takes into a project. - §07 — MCP Architecture — the
mcp_serversgroup keys and thetoolsallowlist each node declares. - §09 — Configuration — model resolution,
target_collectionsand knowledge seeding. - §12 — Extending the Engine — authoring a Sequential or Roundtable team, or a template from scratch.
- The Technical Reference doc set — the exhaustive field-by-field schema (future doc).