Templates
A run template's file format and header fields, the eight shipped templates and their purposes, where operator templates live, override and reset, and what validation refuses.
A template is the graph a run executes: model nodes, builtins and the cords between them. This page says what the code does; file:line citations are in parentheses. The nodes themselves are on nodes.
01the template file#
A template is one YAML file with a template: header beside the patch document, which is inline. The patch document is version, nodes and cords. See nodes. The file is read strictly: an unknown key is an error, and a file with more than one YAML document is refused.
The header is a Declaration:
name: lowercase letters, digits and dashes. It must match the file name.purpose: one line.base: optionally the template a proposal is a variant of.inputs: what a run starts from, each with anameandrequired. A name appears once.roles: roles that presets can play, each aroleand the modelnodethat plays it. The node must be a model node.writes: true when the run writes to a workspace.closes_ticket: true when the run closes its ticket.entryandinlet: the node and message inlet that the run's first envelope goes to.terminal: optionally the node whose completed invocation ends the run. Nothing is wired after it, so the run is drained once it finishes.
A ticket template is any template that closes tickets and has a required tickets input (TicketTemplate). It is read from the declaration, never from the name. A ticket template launches one previewed run per ticket.
02shipped templates#
batch: take several small tickets as one batch with no lead ticket, sorted, worked one at a time in sort's order, checked once, reviewed and gated per ticket, then a commit and a close for eachmock-first: take one ticket from intake through a drawn mock the operator signs off, then plan, work, checks, review and a human gate to a landed, closed ticket built to match that mockquick-fix: take one small ticket from intake straight to one worker, checks, a human gate and a landed, closed ticket, with no research and no model reviewresearch: research a question with claude (and grok when it is on PATH), check every claim's citations against the pages they fetched, and keep the findings at a human gateresearch-brief: research a question with claude (and grok when it is on PATH), check every claim's citations against the pages they fetched, keep the findings at a human gate, then turn them into a short brief saved with the findingsticket-close: close the ticket of a run that landed but was not closed, once the missing evidence existsticket-completion: take one ticket from intake through plan, work, checks, review and a human gate to a landed, closed ticketwrite: write a document from a brief, have a second model critique the draft, and keep the result at a human gate as one saved document
03where templates live#
The eight above are embedded in smith (internal/harness/*.yaml). Smith's code registers some input templates as well. They are read-only, and saving a template under their name is refused. Which ones they are is not checked here.
An operator's templates live at <library_root>/templates/<name>.yaml (TemplatesDir, Library.Dir at 385-390).
- A file of a shipped template's name in that folder is what loads. The shipped bytes are used only when there is no such file.
- A shipped template whose file differs from the shipped bytes is shown as changed.
- Reset removes that file and its marker (below), so the shipped bytes load again. A name that is not a shipped template has nothing to reset to and is refused.
- Save writes
<name>.yamlthere, but only if the template validates. Its caller is the desk window's save route, not a run and not a conductor tool. - A library with no root holds only the shipped templates.
04a saved copy of a shipped template or role#
A saved copy of a shipped template, or a role folder that overrides a built-in role, stands in one of three states. Settings shows the state beside it, with a reset back to the shipped one (templates: Library.List; roles: Role.Shadow, set by find in internal/contextsource/persona/persona.go).
same as shipped: the copy's bytes equal the shipped ones. It only freezes the copy off future shipped changes, so reset it. For a role, bothrole.mdandkit.jsonmust match the embedded ones (or both have no kit).edited: shipped changed since: the copy differs, and the shipped version is not the one it was saved against. Read the shipped one again before keeping the copy.edited: the copy differs, and nothing says the shipped version has moved.
"Shipped changed since" is a stored hash. Saving a copy of a shipped template writes a marker beside it, <templates dir>/.<name>.shipped, and saving a role writes <role folder>/.shipped. Each holds the sha256 of the shipped version at that save (ShippedRevision, BuiltInRevision). The copy is flagged when that hash differs from today's shipped one. Reset removes the marker with the copy, and the marker is never listed as a template. A copy saved before markers existed, or edited by hand without a save through smith, has no marker, so it reads edited and never edited: shipped changed since. A role saved through the settings page is trimmed and its kit rewritten, so it can read edited while its words are unchanged.
A reset of a library template from settings is POST /api/desk/settings/template-reset, the operator's own press. A role resets through POST /api/desk/role with reset, which removes the role's folder.
05validation#
ValidateTemplate is the one validator. Library load, propose and save all call it. It refuses a template when:
- the name is not lowercase letters, digits and dashes, or the purpose is empty or has more than one line;
- an input is unnamed or named twice;
- the patch itself is invalid (the patch rules are in
internal/patch, and are not listed here); - a model node declares no profile;
- a node is a subpatch, because a template is one file;
- a builtin is not one of the known builtins (
KnownBuiltins); - a tool node declares anything beyond naming a tool by id;
- the entry node or its inlet does not exist, or the terminal node does not exist;
- a role names something that is not a model node;
- a land node can be reached without passing a human gate first;
- the template closes tickets and lacks the ticket contract: a required
ticketsinput, aticket_intakemodel node as the entry at inletstart, alandnode (workspace_land), aticket_closemodel node and acompletion_proofthat is acapability_assertovercomplete_ticket; - the template does not declare
closes_ticketbut has a model node that may mutate tickets, and it lacks acapability_assertovercomplete_ticket.
06propose_template#
propose_template takes a name, a one-line purpose and the whole template file. The desk validates it and puts a template card in the conversation. The card shows the graph, each node's role, model and profile, what each node can touch, the validator's problems, and a diff against the template it varies: the header's base, else the template of the same name. Nothing is written. Only the operator's save to library on a valid card adds it, and the desk tells the conductor what they chose. Once saved, draft_launch can carry it. See launch.