Skip to Content
Jobs & Scheduling.job_workflow.yaml

.job_workflow.yaml

A job workflow 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 JobWorkflowConfigZod (src/workflow/job_workflow_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 workflow file. skillet: ./crew_news_brief.skilled_crew.yaml # Default workspace for every job in the crew (overridable per job). workspace: dir:./_data # Deterministic code-workers owned by this workflow (optional). Jobs reference # them as `assignee: codeWorkers/<name>`; the skillet's `agents:` map holds # only LLM agents. codeWorkers: publisher_codeworker: filePath: ./_src/publish_worker.ts inputs: topic: type: string default: null description: The subject to research and write about. jobs: - id: research assignee: agents/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: agents/analyst_agent title: Rank clusters into insights parents: [research] body: | Call job_show first — the research result is in parent_results... - id: write assignee: agents/writer_agent title: Publish the daily brief parents: [analyze] body: | ... - id: publish assignee: codeWorkers/publisher_codeworker title: Publish the brief parents: [write] body: | Shown in the inbox/UI — a code-worker body is not a model prompt.
FieldRequiredPurpose
skilletyesPath to the .skilled_crew.yaml the workers run, resolved relative to the workflow file.
jobsyesThe list of jobs (at least one), in topological order — a parent must appear before its children.
namenoHuman label, shown in workflow pickers.
descriptionnoLonger description, shown in workflow pickers.
workspacenoDefault workspace for the crew (scratch, worktree, or dir:<rel>). Default scratch.
codeWorkersnoMap of code-worker name → { filePath }. A code-worker is a deterministic module (.ts/.js, relative to the workflow file) exporting a runJobWorker entry; jobs assigned to it run that code instead of an LLM agent — used for fully deterministic steps such as an approval gate.
inputsnoMap of input name → { type?, default?, description? }. Values are passed in at instantiation and exposed to templating.

Per-job fields

FieldRequiredPurpose
idyesWorkflow-local id, unique within the file. Used to wire parents; must appear before any job that lists it as a parent.
assigneeyesNamespaced reference to who runs this job: agents/<name> (a key under agents: in the skillet) or codeWorkers/<name> (a key under this workflow’s codeWorkers:).
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 workflow 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, any dir: workspaces, and any codeWorkers module paths to absolute paths, and produce an in-memory crew.
  2. Commit — write the jobs to the board, mapping each workflow-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 workflows

Two working workflows ship in the repo:

Last updated on