FIG L · learn · 5 minutes
One file, line by line.
A workflow is a file you can read — and eight small ideas make you fluent in it. Every fragment below is real, spec-correct YAML, read through the same editor surface you'll use in the playground.
8 steps · spec-correct
- 01 · the file
A workflow is a file you can read
The whole thing is one plain-text file. Two lines make it real: name the language, name the workflow. That header is the whole ceremony — no project setup, no boilerplate, no config.
nika: v1 means the format is frozen — files you write today won’t break.
weekly-radar.nika.yamlnika: v1workflow: weekly-radar - 02 · the inputs
Declare what can change
Inputs live in vars. A bare value is a default you can override from the command line; a typed var documents itself and gets validated before anything runs.
Use it anywhere as ${{ vars.topic }}. Change the input, not the file.
varsvars: output_dir: "./radar" topic: type: string required: true description: "Subject to research" - 03 · the model
Pick a brain. Any brain.
One line chooses the default model — any model: local Ollama, or any API. Start on your own machine (no key, no cloud) and swap providers whenever you want; nothing else changes.
model# fully local · no cloud neededmodel: ollama/llama3.1# or swap to any cloud provider:# model: mistral/mistral-large - 04 · the verbs
A task is a verb
Each task does exactly one thing, with one of the four verbs. This one thinks: it sends a prompt to the model and keeps the answer as its output.
infer thinks · exec runs a command · invoke uses a tool · agent delegates.
taskstasks: - id: digest infer: prompt: "Summarize in 5 bullets: ${{ tasks.fetch_news.output }}" - 05 · the plan
Order is one word. The plan is free.
depends_on is all you write. Tasks that don’t wait on each other run in parallel automatically. You never schedule anything — the plan (which tasks wait on which) falls out of the file.
fetch_news and repo_log run at the same time. digest waits for both.
depends_on- id: fetch_news invoke: tool: "nika:fetch"- id: repo_log exec: command: "git log --since='1 week'"- id: digest depends_on: [fetch_news, repo_log] # waits for BOTH infer: prompt: "Cross-reference news with our work…" - 06 · the branch
Branch like an adult
when: makes a task conditional — a yes/no test over what already happened. Waiting for success is free (depends_on already does it); when: is for conditions beyond it, like a value check.
when- id: alert depends_on: [check] when: ${{ tasks.check.output.errors > 0 }} invoke: tool: "nika:notify" - 07 · the failure
When things fail, you get data
Errors come back typed: a stable code, a category, and whether retrying could help. Tasks declare their own retry policy and a fallback. No stack-trace archaeology.
A failed call retries with backoff; if it still fails, the cached result steps in.
retry · on_error- id: research retry: max_attempts: 3 backoff_ms: 1000 on_error: recover: ${{ tasks.cache.output }} infer: prompt: "…" - 08 · the outputs
Name what comes out
output: binds pieces of a task result to names; the workflow declares what it returns. Downstream tasks (and you) read clean names, not raw API responses.
output · outputstasks: - id: digest infer: prompt: "…" output: result: ".choices[0].message.content"outputs: brief: ${{ tasks.digest.output.result }}
Errors are data, not noise.
typed · greppableEvery failure is a typed structure with a stable code, a category, and a transient flag that says whether retrying could help. Your workflow can read errors the same way it reads any other value, and recover.
{ "code": "NIKA-INFER-001", "category": "provider_error", "message": "the model call failed", "transient": true, "details": { "provider": "ollama", "status_code": 503, "retry_after_secs": 30 }, "task_id": "research", "attempt": 2}codea stable, greppable identifier. The same failure always has the same name.transienttrue means retry might work. The engine retries with backoff before giving up.detailsstructured fields, not prose. Youron_error:can act on them.
That's the whole language.
Eight ideas, four verbs, one file. Install it, write one, run it — or open the playground and check your file as you type.
8 steps · 4 verbs · every fragment spec-correct — real YAML, never pseudo-code