AGPL-3.0-or-later · forever.

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 hand
briefinfer
renderinvoke
manifestinvoke
persistinvoke

the file, whole

conformance-gated upstream on every spec push
media-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 contract

sha256 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

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 →