the deterministic authoring path
agent-loop
Agents do not invent structure — they instantiate it. Each skeleton below is a complete, valid workflow with # SLOT: markers at every decision point: route your intent, copy, fill the slots, nika check, done. Machines read the same pack at /templates/catalog.json; the originals live in the spec, conformance- gated on every push.
- 10skeletonsroute · copy · fill
- 94slot markersthe only blanks
- validevery fileconformance-gated
- sha256every copypinned, re-proven
take data, produce words, save them
- deterministic gather
- one model job
- explicit persist
watch X, act when Y
- jq extraction
- CEL skip-gate
- often zero model calls
do this for EVERY item
- runtime collection
- the full leash (max_parallel
- fail_fast
- retry)
only what changed since last run · survive bad input
- state read→diff→write
- `on_error: recover:` quarantine
research / review / open-ended
- plan-then-execute
- default-deny tools
- budgets
- typed final message
agent-loop.nika.yaml# SPDX-License-Identifier: Apache-2.0# yaml-language-server: $schema=https://nika.sh/spec/v1/workflow.schema.json## TEMPLATE · agent-loop · plan with a fast model, execute with a# budgeted agent, validate the typed result.# The default shape for open-ended work (research · review · triage).## Instantiate · copy → fill `# SLOT:` → `nika check` → repair → re-check.# Three leashes (pattern 5 + 8 · NEVER ship an unleashed agent) ·# tools: default-deny · grant the MINIMUM · nika:done ends cleanly# max_turns + max_tokens_total · the worst case is bounded# schema: the final message is TYPED · prose is not a contractnika: v1workflow: agent-loop-template # SLOT: kebab-case workflow iddescription: "plan → budgeted agent → typed result" # SLOTmodel: ollama/qwen3.5:4b # SLOT: a tool-calling model · local by defaultvars: goal: type: string required: true description: "What the agent must accomplish" # SLOTtasks: - id: plan infer: prompt: "Break '${{ vars.goal }}' into at most 4 concrete steps." # SLOT schema: type: object required: [steps] properties: steps: { type: array, items: { type: string } } - id: execute depends_on: [plan] agent: system: "Work the plan step by step. Call nika:done when finished." # SLOT prompt: "Plan · ${{ tasks.plan.output.steps }}" tools: # SLOT: the MINIMUM grant for the job - "nika:read" - "nika:done" max_turns: 15 # SLOT: the loop bound max_tokens_total: 80000 # SLOT: the spend bound schema: # SLOT: the typed final-message contract type: object required: [findings] properties: findings: { type: array, items: { type: string } } - id: confirm depends_on: [execute] invoke: tool: "nika:assert" args: condition: "${{ size(tasks.execute.output.findings) > 0 }}" message: "Agent returned no findings, do not trust an empty run" # SLOToutputs: findings: value: ${{ tasks.execute.output.findings }} type: array description: "The agent's typed findings" # SLOTsha256 4df4e00ef5b20ef2… · conformance-gated upstream · source
anything irreversible (deploy · send · publish)
- parallel gates
- assert
- `nika:prompt` GO
- `on_finally` record
understand a site (domain · theme · assets) from a URL
- fetch `traverse:` crawl
- one typed infer
- explicit persist
- zero exec
generate image/audio assets from a brief
- `nika:image_generate`
- `nika:jq` manifest
- local/mock provider first
call a product API: upload a file, then create from it
- fetch `multipart:` upload
- masked secrets header
- mode/jq extraction
read a system's state (docker · kubectl · gh), explain it, keep the report
- argv-array exec (provable allowlist)
- parallel reads
- exec ledger
- one artifact
The protocol is mechanical: route with the phrases above, instantiate, fill the slots, then nika check teaches anything you missed before anything runs. Try one in the playground, or install and start from a real file. Read the spec →