the deterministic authoring path
etl-state
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
etl-state.nika.yaml# SPDX-License-Identifier: Apache-2.0# yaml-language-server: $schema=https://nika.sh/spec/v1/workflow.schema.json## TEMPLATE · etl-state · incremental data job with a state file.# The default shape for « only the NEW » jobs (feeds · exports · syncs)# and for batch pipelines that must survive bad input.## Instantiate · copy → fill `# SLOT:` → `nika check` → repair → re-check.# Two load-bearing moves ·# on_error: recover: bad data is DATA · the pipeline lives# state read→diff→write you only process what changednika: v1workflow: etl-state-template # SLOT: kebab-case workflow iddescription: "read state · fetch fresh · diff · process the delta · save state" # SLOTvars: source_url: "https://api.example.com/v1/records" # SLOT: the data source state_path: "./state/etl-state.json" # SLOT: the cursor filetasks: # First run · no state file yet · recover to an empty list. - id: empty invoke: tool: "nika:jq" args: { input: [], expression: "." } - id: previous invoke: tool: "nika:read" args: { path: "${{ vars.state_path }}" } on_error: on_codes: [NIKA-BUILTIN-READ-001] # not-found ONLY · a permission error still fails loudly recover: ${{ tasks.empty.output }} - id: fresh invoke: tool: "nika:fetch" # SLOT: fetch / read / exec · the fresh data args: url: "${{ vars.source_url }}" mode: jq jq: ".records" - id: delta depends_on: [previous, fresh] invoke: tool: "nika:json_diff" # RFC 6902 · empty patch = nothing new args: before: "${{ tasks.previous.output }}" after: "${{ tasks.fresh.output }}" - id: process depends_on: [delta] when: ${{ size(tasks.delta.output) > 0 }} invoke: tool: "nika:jq" # SLOT: the delta job (jq · infer · write…) args: input: "${{ tasks.delta.output }}" expression: "length" - id: save_state depends_on: [fresh] invoke: tool: "nika:write" args: path: "${{ vars.state_path }}" content: "${{ tasks.fresh.output }}" create_dirs: true overwrite: trueoutputs: changes: value: ${{ tasks.delta.output }} type: array description: "RFC 6902 ops since last run · empty = no-op run"sha256 c52e0e449427189b… · conformance-gated upstream · source
research / review / open-ended
- plan-then-execute
- default-deny tools
- budgets
- typed final message
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 →