1 · Deterministic core, model at the edges
jq decides; the model explains. Anything that can be computed (filtering, sums, diffs, ranking) happens innika:jq or a data
builtin, deterministically and for free. The model gets the jobs only
a model can do: judgment, language, synthesis.
2 · Parallelism is the default · depends_on is the only ordering
Tasks with no edge between them run together. Don’t serialize out of
habit: declare the real data dependencies and let the engine schedule
the waves. If you reference ${{ tasks.X }} you must declare the
edge; the DAG has no invisible edges (NIKA-DAG-003).
Anti-pattern · a linear chain of tasks that never read each
other’s output. That is wall-clock spent on nothing.
Taught by · Standup digest ·
Social repurpose (the diamond) ·
CEO Monday brief (3-branch gather).
3 · Type the boundaries
Every place data crosses from a model into the deterministic world gets a contract:schema: on infer/agent (the model must return that
shape), nika:validate for second opinions, nika:assert as the hard
gate. Enums kill « kinda-strong » ratings.
Anti-pattern · prose in, prose out, regex in the middle. If a
downstream task indexes into a field, the producer needs a schema.
Taught by · Meeting actions ·
Contract guard (schema + validate + assert,
belt-and-braces) · Support triage (enums).
4 · Fan out with a leash
for_each over a runtime collection is the power move. Bound it with
max_parallel (providers rate-limit, GPUs thrash), make it resilient
with fail_fast: false (collect errors instead of aborting the batch)
and give each iteration its own retry and timeout.
Anti-pattern · an unbounded fan-out against a rate-limited API, or
leaving fail_fast on its default (true) for a batch where one bad
item is normal.
Taught by · Competitor radar ·
Localization factory ·
Resume screener.
5 · Plan, then execute
For open-ended work: a fast model writes a typed plan, anagent:
executes it under budgets, a thinking model synthesizes. Three stages,
three cost profiles, every intermediate auditable on disk.
Anti-pattern · one giant agent loop with no plan, no budget and no
typed output. Nothing about it can be audited or bounded.
Taught by · Deep research brief.
6 · Three gates, three meanings
when:· the skip gate. Routing, not failure (skipped ≠ failed).nika:assert· the fail-fast gate. The run is wrong, stop loudly.nika:prompt· the human gate. Blocks until a person decides.
prompt gate, or claims success without an assert, is
promising more than it checks.
Anti-pattern · when: ${{ tasks.a.status == 'success' }} as a
plain success gate. depends_on already does that (linter
one-obvious-way/001).
Taught by · Invoice chaser (human gate) ·
Incident war room (assert refuses
optimistic postmortems) · Release train
(all three in one file).
7 · Sovereignty is a model: line
Sensitive data (contracts, CVs, medical, financial) runs on a local
provider, ollama/… or lmstudio/…. Same file shape, zero cloud. The
canonical providers make this a one-line decision,
not an architecture meeting.
Taught by · Contract guard ·
Resume screener.
8 · Agents get budgets, tools get grants
agent: is default-deny: no tools: means no tools at all. Grant the
minimum (nika:read + nika:done makes a read-only reviewer), cap
the loop (max_turns · max_tokens_total), and let nika:done end
it cleanly.
Anti-pattern · tools: ["nika:*", "mcp:*/*"] on an agent that
only needed to read. Least privilege costs one line less.
Taught by · PR review fan-out (the
read-only swarm) · Code review
(foundation).
9 · Evidence always lands · on_finally
Two tools, one rule. on_finally: is per-task cleanup — it fires
when that task ran (success, failure, timeout, mid-flight cancel).
A terminal when: true task is the always-pattern — it runs on
EVERY outcome, including upstreams that failed or never started
(when: true replaces the default success-gate). Cleanup belongs to
the task; the record that must land at 3am belongs to a terminal task.
Taught by · Incident war room ·
Release train (the departure record is a
when: true terminal task — it lands even when the train aborts).
10 · One data language · jq, once
output: bindings, nika:jq, the fan-in zip (transpose), the
state diff: all of it is the same jq. Don’t invent per-task string
parsing and don’t ask the model to reshape JSON. One transform
language is already there.
Taught by · Localization factory
(the transpose zip) · ETL quarantine
(group_by accounting).
11 · Workflows are callable · type the outputs:
A workflow with typed outputs: is a building block: another workflow
(or a human, or CI) consumes a contract, not a log. Name what comes
out, type it, describe it.
Taught by · Deep research brief
({brief, sources}) · Schema retry
(foundation · the typed-outputs shape).
12 · Mock-first · runnable with zero keys
model: mock/echo makes a workflow CI-runnable and demo-safe. Write
it mock-first and swap the provider when it ships. Every example in
this documentation that can run without keys does.
Anti-pattern · a workflow you can’t validate without spending real
tokens. The conformance gate runs every example on every push, and
yours should pass the same way.
Taught by · the whole examples pack — and the
state-file pattern makes even stateful
workflows replayable.
The shape of a well-written file
the shape · a skeleton, not a runnable file
Four recipes the patterns compose into
The 12 patterns are the values; these are the moves you reach for when a real integration pushes back. Each is canonical in the spec — linked, not improvised.Poll until ready
retry: fires on errors, never on values — so make « not ready » an error
inside the task. A jq-mode fetch whose program errors on the pending shape
turns polling into typed, bounded retry:
retry_when: — retrying on a value
condition directly — is reserved for a future minor.)
Diamond join
Two exclusivewhen: branches, one consumer. A skipped branch’s output is
defined null (never an error), so the join is one jq filter:
Fan-out that survives partial failure
A failed iteration contributesnull at its index — positions stay aligned
with the input. Recover per-iteration when a placeholder is acceptable,
filter downstream:
Matrix expansion
A matrix is precomputed data, not control flow. Build the product with jq, fan out over it:See every pattern live
The examples gallery — 20 real jobs, every construct taught, every
file conformance-validated.