the skeleton register
chain
Your intent sounds like: take data, produce words, save them One of 10 skeletons the pack ships, green as-is under nika check. Fill the # SLOT: lines, repair from the fix lines, re-check. Machines read the catalog; the binary lists them with nika new '?'.
- 11SLOT pointsthe only lines you edit
- 3patterns lockedarrive correct, stay correct
- 2tools grantedthe minimum for the job
- 2ffe6e1esha-pinnedre-hashed on every test run
the plan
derived from the file, never drawn by handgatherinvoke
thinkinfer
persistinvoke
the file, whole
conformance-gated upstream on every spec pushchain.nika.yaml
# SPDX-License-Identifier: Apache-2.0# yaml-language-server: $schema=https://nika.sh/spec/v1/workflow.schema.json## TEMPLATE · chain · gather facts → one model step → persist.## [gather]────▶[think]────▶[persist]# nika:read infer nika:write## The everyday shape: pull in something real, run exactly ONE model job over# it, write the result somewhere you can find it again. Most useful workflows# are this and nothing more — reach for a bigger skeleton only when this one# genuinely cannot hold the job.## Demonstrates ·# - the three-verb spine · invoke → infer → invoke# - `with:` as the DAG edge · a task reads its input from a binding, and the# binding IS what orders the graph (nobody writes the arrows)# - `on_error: recover:` · the skeleton rehearses green in an empty# directory, before it is pointed at real data## Needs · nothing. Scaffolds and runs green in an empty directory.## Run · nika run <file> --model mock/echo # offline rehearsal · zero keysnika: v1workflow: id: chain-template # SLOT: kebab-case workflow id # SLOT: one honest sentence — `nika new` matches intents against these words description: "gather → think → persist"# SLOT: provider/model · local · zero key. Measured seat — `ollama/llama3.2:3b`# answers this prompt in ~50s. `--model mock/echo` needs no seat at all and is# the path this file guarantees.model: ollama/llama3.2:3bconst: source: "./README.md" # SLOT: your input · README.md exists in most repos destination: "./output.md" # SLOT: where the result landspermits: # the blast radius · default-deny once present tools: ["nika:read", "nika:write"] # Two literal paths, one each way. `check` can read these (they come from # `const:`) and proves them against the boundary before anything runs — keep # them in step with the two `const:` entries above. fs: read: ["./README.md"] write: ["./output.md"]tasks: gather: invoke: # SLOT: the fact source · nika:read / nika:fetch / exec tool: "nika:read" args: { path: "${{ const.source }}" } on_error: # Offline rehearsal. A freshly scaffolded directory has no README, and a # skeleton that dies on its first run teaches nothing — so a not-found # recovers into a literal standing in for the real document. `on_codes:` # keeps that narrow: ONLY not-found is forgiven, a permission error # still fails loudly. Delete this block once the source really exists. # In an EMPTY directory check prints one [inputs] hint that this read # would fail — the run does not: this recover carries it (measured · # rc=0 · « 1 recovered »). The hint retires once your source exists. on_codes: [NIKA-BUILTIN-READ-001] recover: "REHEARSAL · no source document here yet." think: with: gather: ${{ tasks.gather.output }} infer: max_tokens: 800 # SLOT: the spend ceiling · sized for the SEAT, not the answer # SLOT: the one model job. Keep slot markers OUT of the block below — # everything indented under `prompt: |` is prompt TEXT, so a stray # `# SLOT:` line is sent to the model verbatim (visible in the mock # echo). Comments belong out here, where YAML eats them. prompt: | Summarize the following in a short paragraph. ${{ with.gather }} persist: with: think: ${{ tasks.think.output }} invoke: tool: "nika:write" args: path: "${{ const.destination }}" # SLOT: destination · same path as permits.fs.write content: "${{ with.think }}" # ALWAYS pass content · a write without it writes nothingoutputs: result: ${{ tasks.think.output }} # SLOT: the callable contractsha256 2ffe6e1e85a5616b…. The copy above re-hashes to its pin on every test run (a copy is re-provable, never trusted). Source: chain.nika.yaml in the spec pack · open it in the playground →
the patterns it locks
- deterministic gather
- one model job
- explicit persist
cross-references
Scaffold it locally: nika new chain my-flow.nika.yaml. Try the shape in the playground, or walk the showcase for the same patterns on real work. Read the spec →