the skeleton register
media-asset-pack
Your intent sounds like: generate image/audio assets from a brief 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
- 3tools grantedthe minimum for the job
- 308c54c2sha-pinnedre-hashed on every test run
the plan
derived from the file, never drawn by handbriefinfer
renderinvoke
manifestinvoke
persistinvoke
the file, whole
conformance-gated upstream on every spec pushmedia-asset-pack.nika.yaml
# SPDX-License-Identifier: Apache-2.0# yaml-language-server: $schema=https://nika.sh/spec/v1/workflow.schema.json## TEMPLATE · media-asset-pack · brief → generate assets → manifest.## [brief]──▶[render]────────────▶[manifest]──▶[persist]# infer nika:image_generate nika:jq nika:write## Turning a sentence into shippable art plus the paperwork that makes it# usable: a model writes the image prompt, the generator renders it, and a# manifest records WHAT was made from WHICH brief — so the pack can be# regenerated, audited, or handed to someone else.## Demonstrates ·# - `nika:image_generate` with `provider: mock` · byte-real assets, zero key# - the directory-AND-children permit · the one shape that passes `check` and# survives the run (see `permits.fs.write`)# - a JSON artifact built as a VALUE by `nika:jq`, never typed by hand## Needs · nothing. Scaffolds and runs green in an empty directory.## Run · nika run <file> --model mock/echo # offline rehearsal · assets land in ./out/assetsnika: v1workflow: id: media-asset-pack-template # SLOT: kebab-case workflow id # SLOT: one honest sentence — `nika new` matches intents against these words description: "brief → generate image assets → manifest"# SLOT: provider/model · local · zero key. Measured seat — `ollama/qwen2.5:14b`# holds the small schema below. `--model mock/echo` needs no seat at all.model: ollama/qwen2.5:14bconst: subject: "a calm cosmic landing hero" # SLOT: what the asset is about out_dir: "./out/assets" # SLOT: where assets landpermits: # the blast radius · default-deny once present tools: ["nika:image_generate", "nika:jq", "nika:write"] fs: write: # Two entries, and both earn their place: `check` judges the `output_dir:` # ARGUMENT (`./out/assets`), while the RUN gates every FINAL file path under # it — the asset, its provenance manifest, and manifest.json. Grant only the # directory and the file sails through check, then dies at run on the first # asset. `*` is one segment and never crosses `/`, which is all this needs: # image_generate lands its files flat, so no subtree grant is warranted. - "./out/assets" # SLOT: keep in step with const.out_dir - "./out/assets/*" # SLOT: idem · the files that land inside ittasks: brief: infer: max_tokens: 600 # SLOT: the creative direction · style · constraints. Keep slot markers # out of the block below — it is prompt TEXT, not YAML, so a stray # comment line is sent to the model verbatim. prompt: | Write one vivid, concrete image prompt for: ${{ const.subject }}. No text in the image · no watermark · a calm central zone. schema: type: object additionalProperties: false properties: image_prompt: { type: string } required: [image_prompt] render: with: brief_image_prompt: ${{ tasks.brief.output.image_prompt }} invoke: tool: "nika:image_generate" args: provider: mock # SLOT: local | openai | gemini | xai (local/mock first) prompt: "${{ with.brief_image_prompt }}" output_dir: "${{ const.out_dir }}" filename_prefix: "asset" # SLOT: filename stem manifest: with: brief: ${{ tasks.brief.output }} render: ${{ tasks.render.output }} invoke: tool: "nika:jq" args: expression: "{ brief: .[0], images: .[1].images }" input: - "${{ with.brief }}" - "${{ with.render }}" persist: with: manifest: ${{ tasks.manifest.output }} invoke: tool: "nika:write" args: path: "${{ const.out_dir }}/manifest.json" create_dirs: true # `.json` path → `content:` is ONE interpolation of a value the engine # serializes. `nika:jq` above BUILT that value; hand-typing braces # around an interpolation emits unquoted fields and breaks the artifact. content: "${{ with.manifest }}"outputs: manifest: ${{ tasks.manifest.output }} # SLOT: the callable contractsha256 308c54c2053138bb…. The copy above re-hashes to its pin on every test run (a copy is re-provable, never trusted). Source: media-asset-pack.nika.yaml in the spec pack · open it in the playground →
the patterns it locks
- `nika:image_generate`
- `nika:jq` manifest
- local/mock provider first
cross-references
Scaffold it locally: nika new media-asset-pack my-flow.nika.yaml. Try the shape in the playground, or walk the showcase for the same patterns on real work. Read the spec →