the language
Every word.
Every key a .nika.yaml can carry, projected from the one contract the engine serves: workflow.schema.json, the same file your editor validates against. Descriptions are the schema's own. Every word opens its own room; the contract, the word in a real file, the block it lives in; the four verbs keep theirs. The deeper grammar lives in the spec.
- 59wordsthe whole surface
- 73declarations10 surfaces
- 4verbsrooms of their own
- schemathe sourcenever prose
- envelopethe file itself
- workflowinside workflow:
- taskone step of the plan
- inferinside infer:
- execinside exec:
- invokeinside invoke:
- agentinside agent:
- retryinside retry:
- on_errorinside on_error:
- on_finallyinside an on_finally step
- aftertask
- task
objectThe CONTROL boundary · {producer-task: predicate}. Each entry is one control edge (spec/03-dag.md §after). State, never data — observe outcomes through with:.
- task
- the fourth verb: a budgeted tool-calling loop; its room: /verbs/agent
- on_finally
- the fourth verb: a budgeted tool-calling loop; its room: /verbs/agent
- argsinvoke
- invoke
objectthe tool call’s arguments, validated against the builtin’s declared contract before anything runs
- backoff_max_msretry
- retry
integerceiling on the computed backoff delay
- backoff_msretry
- retry
integerbase delay between attempts
- backoff_strategyretry
- retry
stringhow the delay grows between attemptsfixedlinearexponential
- captureexec
- exec
stringwhich stream becomes the task outputstdoutstderrcombinedstructured
- commandexec
- exec
arrayargv — the program and its arguments, execve, NO shell. Each element substituted independently (the injection-safe form). Shell features (pipes · redirects · globs) live in `shell:`.
- configenvelope
- envelope
objectTyped non-sensitive runtime config · `${{ config.X }}` · supplied by the deployment or environment, may appear in logs (R3a · LAW-SURFACE-0201 · succeeds the dead `env:` block). Each entry is a typed declaration; a `default:` MUST conform to `type:` (LAW-TYPE-0211 · NIKA-DEFAULT-001).
- constenvelope
- envelope
objectNamed constants · `${{ const.X }}` · a fixed value baked into the workflow (R3a · LAW-SURFACE-0201 · the literal half of the dead `vars:` block · everything that is not required:true lands here per the E-split total rule). Either a bare literal, or a `{ type, value }` typed constant whose `value:` MUST conform to `type:`.
- cwdexec
- exec
stringworking directory for the command
- decodeexec
- exec
stringHow the captured string becomes a value (spec 09 §decode) — illegal with capture: structured (NIKA-PARSE-025)textjsonjsonlbytes
- descriptionworkflow
- workflow
stringone honest sentence about the workflow; it shows in listings and traces
- envexec
- exec
objectenvironment variables for the exec command, a name to value map
- task
- the second verb: run a command, captured and typed; its room: /verbs/exec
- on_finally
- the second verb: run a command, captured and typed; its room: /verbs/exec
- fail_fasttask
- task
booleanfor_each abort-on-error · default true.
- fail_workflowon_error
- on_error
booleanescalate: a caught error still fails the run after recovery
- for_eachtask
- task
string | arrayMap this task over a collection · `${{ ... }}` reference OR a literal array.
- workflow*
stringWorkflow id · kebab-case · the document-type discriminator (W1: the envelope became an object).
- task
- the first verb: call a model; its room: /verbs/infer
- on_finally
- the first verb: call a model; its room: /verbs/infer
- inputsenvelope
- envelope
objectTyped workflow inputs · `${{ inputs.X }}` · the parameters an author declares and a caller supplies (R3a · LAW-SURFACE-0201 · the typed half of the dead `vars:` block · a required:true value lands here per the E-split total rule). Each entry is a typed declaration whose `type:` speaks the full TypeExpr of 09-types (R3b · LAW-GRAMMAR-0211 · the flat 6-enum is dead · LAW-SURFACE-0211).
- task
- the third verb: call a tool (nika: builtin or mcp: server); its room: /verbs/invoke
- on_finally
- the third verb: call a tool (nika: builtin or mcp: server); its room: /verbs/invoke
- jitterretry
- retry
booleanrandomize delays so retries never stampede in phase
- retry*
integerthe retry ceiling, the leash on flake
- max_paralleltask
- task
integerCap concurrent for_each iterations · default unbounded · 1 = sequential.
- max_tokensinfer
- infer
integer | stringper-call output token cap
- max_tokens_totalagent
- agent
integer | stringthe agent’s total spend bound across all turns
- max_turnsagent
- agent
integer | stringthe agent’s loop bound; the worst case is finite
- envelope
stringDefault model · `<provider>/<name>` (e.g. ollama/qwen3.5:9b · anthropic/claude-sonnet-4-6 · mock/echo).- infer
string- agent
string
- envelope*
"v1"Language contract version · exactly `v1` for the v1 lifetime. NOT `v1.0` · `1` · `1.0`.
- retry
array- on_error
arrayOptional catch-side filter (mirror of retry.on_codes · same regex) · the action applies ONLY when the final error code is listed · unlisted codes fall through to the default fail (spec/05-errors.md §Fields).
- on_errortask
- task
- the catch side: recover with a value, skip, or fail loudly, per error code
- on_finallytask
- task
arrayCleanup mini-tasks · ALWAYS run (success/fail/timeout/cancel) · sequential · best-effort.
- outputtask
- task
objectNamed jq-expression bindings · `${{ tasks.X.<name> }}`. jq is the single data extraction-and-transform language (the former RFC 9535 JSONPath was dropped · jq is a superset · per spec/04-variables.md §216-225). Reserved names forbidden at parse time (spec/04-variables.md §Rules): output · status · error · started_at · ended_at · duration_ms — enforced via propertyNames.
- outputsenvelope
- envelope
objectThe workflow's return value · symmetric to inputs. Each entry is a `${{ tasks.X.output }}` reference (untyped form · string) OR a typed declaration { value · type · description }. Powers `nika run` result + the output half of the callable-workflow schema.
- permitsenvelope
- envelope
objectThe declared capability boundary · once present every category is default-deny unless listed (spec/01-envelope.md §permits · NIKA-SEC-004).
- policyenvelope
- envelope
objectNamed workflow law · hard families (require/forbid/allow/limits) are judged at check (NIKA-POLICY-001) · soft families (prefer/optimize) are recorded, never judged in v1 (spec/10-authority.md).
- infer*
stringthe model job, in words: interpolate ${{ }} references, never paste secrets- agent*
stringthe model job, in words: interpolate ${{ }} references, never paste secrets
- recoveron_error
- on_error
- Recovery output · a `${{ }}` ref OR a literal (merges the former fallback/value per spec/05-errors.md).
- retrytask
- task
- the leash: attempts, backoff, jitter, per-code filters
- returnstask
- task
- The task's output contract (spec 09-types.md) — exclusive with a verb-level schema: (NIKA-TYPE-003)
- infer
objectJSON Schema · structured output contract.- agent
objectJSON Schema · validate the agent's final message as structured output.
- secretsenvelope
- envelope
objectVault-backed masked references · `${{ secrets.X }}` · never inline literals.
- shellexec
- exec
stringOne shell line, run via /bin/sh -c — the EXPLICIT dangerous door (pipes · redirects · globs). The blocklist applies here; interpolating untrusted values here is on the author. Exactly one of command|shell.
- skillsagent
- agent
arrayAgent Skill (SKILL.md) file paths · agentskills.io shape · explicit static paths only (no globs · no templates) · loaded at compose time and injected into the system context.
- skipon_error
- on_error
booleantreat the failed task as skipped and continue the plan
- stdinexec
- exec
stringtext piped to the command’s standard input
- infer
stringthe standing instruction: who the model is for this task- agent
stringthe standing instruction: who the model is for this task
- envelope*
objectThe task map · the KEY is the task's identity (snake_case · CEL-safe). Source order is presentation only — the graph alone schedules.
- infer
number | string0-2 · number or `${{ }}`.- agent
number | string
- thinkinginfer
- infer
objectextended-reasoning budget for models that expose it
- task
stringGo-duration string · quoted · e.g. "30s" "5m" "1h30m" "2.5s". Max 24h.- on_finally
string
- toolinvoke
- invoke
stringTool reference · nika:<path> (closed v0.1 builtin set) OR mcp:<server>/<tool> (requires the slash). The namespace set is CLOSED at v1 (spec/02-verbs.md) — an x-<vendor>: prefix is RESERVED, not valid (engine-specific tools route through mcp: · spec/06-stdlib-contract.md §Namespace ownership).
- toolsagent
- agent
arrayWhitelist · DEFAULT-DENY (no tools if absent) · gitignore-style globs · `!` negation.
- typesenvelope
- envelope
objectNamed type declarations (spec 09-types.md · PascalCase · acyclic)
- visioninfer
- infer
arrayimage inputs for multimodal models
- task
boolean | stringLOCAL business condition · evaluated POST-gate over {vars · env · with · item · index} — tasks.* is illegal here (NIKA-VAR-021 · hoist into with:). false → skipped (spec/03-dag.md §when).- on_finally
boolean | stringCleanup condition · may read the PARENT task's record (tasks.<parent>.status / .error — the only legal tasks.* target inside on_finally · spec/03-dag.md §on_finally).
- withtask
- task
objectTask-level scope injection · `${{ with.X }}`.
- envelope*
objectThe workflow object · a stable home for identity and metadata (W1 'the map').- invoke
stringCompose another workflow (spec 14) · a STATIC target: a filesystem path OR registry:owner/name@version (pinned). A templated target is refused at check (NIKA-COMP-001). Exactly one of tool: | workflow: (the invoke tagged union · G21).
A key the schema doesn't declare is a nika check finding before anything runs. The language teaches itself. Try it in the playground, walk the four verbs or the standard library. Read the spec →