T1 fan-out · creators / media:nika:globlists the folder,for_eachfans onenika:image_fxtask per photo, and a fixed ops chain (grayscale → dither → pixelate) renders each one. No model in the loop — the pipeline is pure pixels, so the same input produces the same bytes on every run, on every machine.
The job
Batch-stylizing a shoot means opening an editor, replaying the same five clicks per photo, and hoping you replayed them identically. This workflow IS the preset: the ops chain is written once in YAML, the batch fans out in parallel, and re-running it on the same pinned engine with the same input and ops yields the same bytes. An engine upgrade is a new pin — re-render before treating an old hash as current.The shape
The file
image-fx-batch.nika
Two things this file teaches
- The ops vocabulary is a closed set —
dither: { mode: bayer4 }is one of the engine’s named modes; misspell it (bayer) andnika checkanswers with the whole legal set instead of guessing. The checker teaches the vocabulary before the run spends a cycle. - A fan-out that can produce nothing still binds — the collect
step guards with
${{ tasks.stylize.output != null ? … : [] }}(quoted, because a bare CEL ternary contains:and YAML would claim it). An empty folder degrades to an empty manifest, not a crash.
Run it
Rehearsal writes nothing:fx.nika, point
const.photos at an existing directory of PNG files, then:
nika compile does not instantiate this showcase slug and does not plant
sample photos. nika compile --list names exact skeletons; only a Ready
candidate plus an explicit destination writes.