Skip to Content
Jobs & Scheduling.job_workflow.yaml

.job_workflow.yaml

A job template defines a crew — a graph of jobs that get instantiated together and committed to the board. It names the skillet to run, declares the inputs a caller can supply, and lists the jobs with their dependencies. The schema is JobTemplateConfigZod (src/job_lane/job_template_zod.ts); it has a generated JSON Schema for editor support.

Top-level shape

name: crew_news_brief description: Research → analyze → write the daily intelligence brief. # Path to the skillet the workers run, relative to this template file. skillet: ./crew_news_brief.skilled_crew.yaml # Default workspace for every job in the crew (overridable per job). workspace: dir:./_data inputs: topic: type: string default: null description: The subject to research and write about. jobs: - id: research assignee: research_agent title: Gather sources for daily brief body: | Run the research stage. Topic: {{ topic | default('the config preset topic') }}. ...call job_complete with metadata { "clusters": [...] }. result_schema: type: object required: [clusters] properties: clusters: { type: array, minItems: 3 } - id: analyze assignee: analyst_agent title: Rank clusters into insights parents: [research] body: | Call job_show first — the research result is in parent_results... - id: write assignee: writer_agent title: Publish the daily brief parents: [analyze] body: | ...
FieldRequiredPurpose
skilletyesPath to the .skilled_crew.yaml the workers run, resolved relative to the template file.
jobsyesThe list of jobs (at least one), in topological order — a parent must appear before its children.
namenoHuman label, shown in template pickers.
descriptionnoLonger description, shown in template pickers.
workspacenoDefault workspace for the crew (scratch, worktree, or dir:<rel>). Default scratch.
inputsnoMap of input name → { type?, default?, description? }. Values are passed in at instantiation and exposed to templating.

Per-job fields

FieldRequiredPurpose
idyesTemplate-local id, unique within the file. Used to wire parents; must appear before any job that lists it as a parent.
assigneeyesThe agent (a key under agents: in the skillet) that runs this job.
titleyesShort label for the board.
bodyyesThe task prompt sent to the worker. Supports templating (below).
parentsnoList of job ids this job depends on. The job stays todo until all parents are done.
result_schemanoA small JSON-Schema subset the worker’s job_complete metadata is validated against. (resultSchema is accepted as an alias; result_schema wins if both are present.)
workspacenoPer-job workspace override.
maxRetriesnoHow many times a crashed/protocol-violating run is retried before the job is given up (default 2).

Templating

The body of a job and the values in inputs are rendered with a small built-in templating language (a mini-Jinja, no external dependency):

FormResult
{{ topic }}Substitute the value of topic.
{{ topic | default('the preset topic') }}Use topic, falling back to the literal when it’s empty/null.
{{ '...' if topic else '' }}Inline conditional.
{{ 'a' + topic + 'b' }}String concatenation with +.

A built-in today (formatted YYYY-MM-DD) is always available, so a job body can reference {{ today }} without it being declared as an input.

Instantiating and committing

A template becomes live jobs in two steps the runtime does for you:

  1. Instantiate — render every job’s body with the supplied inputs, resolve the skillet and any dir: workspaces to absolute paths, and produce an in-memory crew.
  2. Commit — write the jobs to the board, mapping each template-local id to a real database id and threading the parents links.

You rarely call these directly: jobs create does both from the CLI, a lane: job command does it from chat, and the web client’s “New job” widget and schedules do it for you.

Sample templates

Two working templates ship in the repo:

Last updated on