the language
Every word.
The whole language, in one file you can read.
Projected from workflow.schema.json · the same contract your editor validates against. Every word opens its own room. The four verbs keep theirs.
the envelope · mark AND name
# first model call · one local seat, one bounded sentencenika: hello-aimodel: ollama/llama3.2:3b # local · zero key · swap for any provider in the catalogpermits: {}tasks: greet: infer: prompt: "Say hello in one sentence." max_tokens: 64outputs: greeting: ${{ tasks.greet.output }}- 59wordsthe whole surface
- 66declarations10 surfaces
- 4verbsrooms of their own
- schemathe sourcenever prose
- envelopethe file itself
- taskone step of the plan
- inferinside infer:
- execinside exec:
- invokeinside invoke:
- agentinside agent:
- for_eachinside for_each:
- retryinside retry:
- on_errorinside on_error:
- liftinside a lift entry
59 words across 10 blocks, and 6 of them speak in more than one. Point at a word to see its other homes.
the envelope9
a task15
infer8
exec7
invoke3
agent9
for_each3
retry6
on_error3
lift3
A filled corner marks a word the schema requires at that surface; +n counts the other blocks the same word speaks in. Both derive from workflow.schema.json: the served contract, not a second opinion about it.
- 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
- argsinvoke
- invoke
objectArguments passed to the tool · an object whose shape is the TOOL's own schema, not the language's. Each builtin and each MCP tool declares its own (spec/02-verbs.md §invoke) · `${{ }}` may appear in any leaf value.
- retry
integerCeiling on the computed delay, in milliseconds · default 60000, one minute. Exponential growth stops climbing here.
- retry
integerInitial delay between attempts, in milliseconds · default 1000. `backoff_strategy` decides how it grows from there.
- retry
stringWhich curve the delay follows between attempts · `fixed` · `linear` · `exponential` · default `exponential`. Each attempt waits `backoff_ms` flat, `backoff_ms × attempt`, or `backoff_ms × 2^(attempt-1)` capped at `backoff_max_ms`.fixedlinearexponential
- lift*
stringThe non-empty justification · recorded in the run receipt with the taint path and the value digest.
- captureexec
- exec
stringWhich stream becomes the task's output · `stdout` (default) · `stderr` · `combined` · `structured` = `{ stdout, stderr, exit_code }`. This is the SOURCE; `decode:` is how that string becomes a value.stdoutstderrcombinedstructured
- 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:`.
- constenvelope
- envelope
objectNamed constants · `${{ const.X }}` · a fixed value baked into the workflow. Either a bare literal, or a `{ type, value }` typed constant whose `value:` MUST conform to `type:`. The literal half of the dead `vars:` block (R3a · LAW-SURFACE-0201) · everything that is not `required: true` lands here per the E-split total rule.
- cwdexec
- exec
stringWorking directory for the subprocess · default = the engine's own cwd.
- decodeexec
- exec
stringHow the captured string becomes a value · `text` (default) · `json` · `jsonl` · `bytes`. Illegal with `capture: structured`, which already IS an object (NIKA-PARSE-025) · a non-parsing stream settles the task `failure` inside `on_error:` scope (spec 09 §decode).textjsonjsonlbytes
- envexec
- exec
objectOS environment variables for THIS subprocess · a key→value map applied over the composed environment. Nothing is inherited — the ambient environment reaches a task only through `permits.env` (spec/01-envelope.md §permits).
- task
- the second verb: run a command, captured and typed; its room: /verbs/exec
- extracttask
- task
objectNamed jq-expression bindings extracted from the verb's raw response · read downstream as `${{ tasks.X.<name> }}`. The field names the OPERATION (run this jq); it does not write `output` — `${{ tasks.X.output }}` stays the raw response (spec/03-dag.md §extract). 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). A binding may not shadow a record projection: output · status · error · started_at · ended_at · duration_ms are forbidden at parse time (spec/04-variables.md §Rules) — enforced via propertyNames.
- fail_fastfor_each
- for_each
booleanWhether the fan-out abandons the batch on the first failure · default true. False finishes every item and yields null at a failed index, so the batch reports what it could.
- for_eachtask
- task
objectFan this task out over a collection · ONE block, so the concurrency is visible where the fan-out is declared. `max_parallel` and `fail_fast` live here because they have no meaning without it.
- fromlift
- lift
stringREQUIRED for law 'taint', forbidden otherwise · the ONE binding this entry raises (e.g. inputs.p · config.region · tasks.fetch.output) · a dotted value-binding path.
- grouptask
- task
stringFan-in MEMBERSHIP · this task joins the named group, and a consumer folds the whole group with one `${{ group.<name> }}` binding in its `with:` (spec/03-dag.md §group). Membership is DECLARED, never matched: a renamed member leaves its group loudly (NIKA-DAG-008 on the reference), where a glob would shrink the fold in silence. A group exists iff at least one task declares it. An `unwind` task may not join one (NIKA-DAG-009 · cleanup never schedules).
- task
- 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. 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). The typed half of the dead `vars:` block (R3a · LAW-SURFACE-0201) · a `required: true` value lands here per the E-split total rule.
- task
- the third verb: call a tool (nika: builtin or mcp: server); its room: /verbs/invoke
- for_each*
string | arrayThe collection · a `${{ ... }}` reference OR a literal array. Evaluated EXACTLY ONCE, before the fan-out; no iteration feeds back into it (spec 03 §where this sits).
- retry
booleanRandomize the computed delay so tasks retrying the same upstream do not synchronize · default TRUE. Engines SHOULD use a full-jitter or equal-jitter family — the anti-thundering-herd default (spec/05-errors.md §Retry policy).
- lift*
stringWHICH law this entry lifts · 'taint' raises ONE binding from untrusted to trusted (NEP-0004 · LAW-AUTH-0325) · 'data-as-code' declares this task's fetch a code-bearing artifact it will never load or run (NEP-0006 · LAW-AUTH-0327). Neither ever lifts the net boundary or the SSRF floor.taintdata-as-code
- lifttask
- task
arrayThe authored doors · each entry lifts exactly ONE named law, with a mandatory reason, check-visible and receipt-recorded. A lift is NEVER a permit bypass — the value still sits inside the declared boundary, and no law but the named one moves. The law set is CLOSED and normative (a NEP amends it): this is why a new law costs zero language fields (spec/10-authority.md §the authored doors).
- retry*
integerTotal attempts, counting the first try · integer ≥ 1 · the one required field of a `retry:` block. Engines honor it strictly and surface the LAST error if every attempt fails (spec/05-errors.md §Retry policy).
- max_parallelfor_each
- for_each
integerCap concurrent iterations · default unbounded · 1 = sequential.
- max_tokensinfer
- infer
integer | stringper-call output token cap
- max_tokens_totalagent
- agent
integer | stringCumulative token budget across every turn of the loop · the SPEND ceiling, where `max_turns` is the step ceiling. Default is engine-configurable (spec/02-verbs.md §agent).
- 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*
stringThe file's NAME · kebab-case · and the mark that says « this is a nika file ». One word, one meaning across both document types: `nika.yaml` carries the PROJECT name, a `*.nika.yaml` carries the WORKFLOW name. The document TYPE is read from `tasks:` — present means workflow, absent means project — so it survives a blob, a paste or an HTTP body where the filename does not travel. This key held the literal `v1` until the envelope nuke; it carried zero bits as a version (there is no `nika: v2` — ever) and now carries the file's most necessary field instead.
- retry
arrayRetry ONLY on these canonical `NIKA-<NS>-<NNN>` codes · absent, the engine retries anything transient. Codes, never HTTP status numbers · the retry-side mirror of `on_error.on_codes`.- 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).
- task
- the catch side: recover with a value, skip, or fail loudly, per error code
- 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.
- envelope
objectThe declared capability boundary · once present every category is default-deny unless listed (spec/01-envelope.md §permits · NIKA-SEC-004).
- infer*
stringthe model job, in words: interpolate ${{ }} references, never paste secrets- agent*
stringthe model job, in words: interpolate ${{ }} references, never paste secrets
- on_error
- Recovery output · a `${{ }}` ref OR a literal (merges the former fallback/value per spec/05-errors.md).
- task
- Retry policy for this task · `{ max_attempts, backoff_ms, backoff_strategy, backoff_max_ms, jitter, on_codes }`. Retries run BEFORE `on_error` sees anything — the catch handles the last error only (spec/05-errors.md §Retry policy).
- returnstask
- task
- The task's output contract · the TYPED door, where a verb-level `schema:` is the out-of-core hatch. The two are exclusive on one task (NIKA-TYPE-003 · spec 09-types.md).
- runenvelope
- envelope
objectThe run's entropy and clock declaration · every source of randomness and of time is declared, never ambient. The two dimensions couple: only `ambient × system` (the status quo) and `none | seeded × virtual` (the deterministic states) are legal, and a declared contradiction refuses at parse (NEP-0010).
- infer
objectJSON Schema · the structured-output contract the model's reply must satisfy. The out-of-core hatch; the typed door is task-level `returns:` (both on one task is NIKA-TYPE-003).- agent
objectJSON Schema · the contract the agent's FINAL message must satisfy. The out-of-core hatch; the typed door is task-level `returns:` (both on one task is NIKA-TYPE-003).
- 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 · explicit static paths only, no globs and no templates. They follow the agentskills.io shape, and are loaded at compose time and injected into the system context.
- on_error
booleanSwallow the error and let the DAG continue · the task produces no output, and the original error stays readable at `tasks.<id>.error`. Exactly one of `recover` · `skip`.
- stdinexec
- exec
stringData written to the command's standard input · may interpolate `${{ }}`.
- infer
stringSystem prompt · the standing instruction, sent ahead of `prompt:` and unchanged by it.- agent
stringSystem prompt · the standing instruction, sent ahead of `prompt:` and unchanged by any turn of the loop.
- 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 | stringSampling temperature · 0 is the steadiest the provider offers, 2 the loosest. A number in 0-2, or a `${{ }}` reference.- agent
number | stringSampling temperature for every turn of the loop · 0 is the steadiest the provider offers, 2 the loosest. A number in 0-2, or a `${{ }}` reference.
- thinkinginfer
- infer
objectExtended thinking · `{ enabled, budget_tokens }` — reasoning the model may spend before it answers (spec/02-verbs.md §infer).
- timeouttask
- task
stringGo-duration string · quoted · e.g. "30s" "5m" "1h30m" "2.5s". Max 24h.
- 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.
- visioninfer
- infer
arrayImage inputs for the call · each entry `{ source: file | url, path | url }` · the images `prompt:` is allowed to refer to.
- whentask
- task
boolean | stringLOCAL business condition · false skips the task · evaluated POST-gate over {vars · env · with · item · index}. Referring to `tasks.*` is illegal here (NIKA-VAR-021 · hoist it into `with:`) · spec/03-dag.md §when.
- task
objectTask-level scope injection · `${{ with.X }}`.
- workflowinvoke
- 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).
the three families
14 of 59 wordsMost words carry no family and say nothing about one. These do: the contract declares where each may legally appear, so a surface can colour by MEANING instead of by syntax class. It is the same fact the panel tints and the room states in a sentence.
the boundary2
the wiring2
the failure grammar10
the blocks around the words
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 →