AGPL-3.0-or-later · forever.

the skeleton register

docker-report

Your intent sounds like: read a system's state (docker · kubectl · gh), explain it, keep the report 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 '?'.

  • 10SLOT pointsthe only lines you edit
  • 3patterns lockedarrive correct, stay correct
  • 1tool grantedthe minimum for the job
  • b17d9ca7sha-pinnedre-hashed on every test run

the plan

derived from the file, never drawn by hand
psexecdfexec
diagnoseinfer
keepinvoke

the file, whole

conformance-gated upstream on every spec push
docker-report.nika.yaml
# SPDX-License-Identifier: Apache-2.0# yaml-language-server: $schema=https://nika.sh/spec/v1/workflow.schema.json## TEMPLATE · docker-report · read real state via a pinned CLI → explain it → keep the report.##   [ps]────┐#   [df]────┴──▶[diagnose]────▶[keep]#   exec ×2      infer          nika:write## The "ask the machine what is actually true, then explain it" job. Two reads# run in parallel because nothing connects them, one model pass turns raw CLI# output into a report a human can act on, and the report is kept on disk. Swap# `docker` for `kubectl`, `gh`, `systemctl` — the shape does not change.## Demonstrates ·#   - argv-ARRAY `exec:` · one program, exactly these arguments, no shell to#     inject into · and `permits.exec` names the single program allowed#   - implicit parallelism · `ps` and `df` share no edge, so the scheduler runs#     them together · nobody writes "in parallel" anywhere#   - `on_error: recover:` on an exec · the skeleton rehearses green on a host#     with no Docker at all## Needs · nothing to rehearse. For a REAL report: a running Docker daemon —#   then delete the two `on_error:` blocks so a broken daemon is loud.## Run · nika run <file> --model mock/echo    # offline rehearsal · no daemonnika: v1workflow:  id: docker-report-template    # SLOT: kebab-case workflow id  # SLOT: one honest sentence — `nika new` matches intents against these words  description: "read the daemon's state · explain it · keep the report"# SLOT: local-first · `--model mock/echo` for the offline rehearsal. Measured# seat — `ollama/llama3.2:3b` answers this prose prompt in well under a minute.model: ollama/llama3.2:3bpermits:                            # the blast radius · default-deny once present  exec:    - "docker"                      # SLOT: the ONE program the reads may launch  tools:    - "nika:write"  fs:    write:      - "./docker-health.md"        # SLOT: where the report lands (must match `keep`)tasks:  # The reads run IN PARALLEL (no edges between them) — the  # scheduler proves it from the DAG, nobody orders it.  ps:    exec:      # SLOT: argv ARRAY form — one program, exactly these arguments. The array      # is why there is no shell here: no word-splitting, no globbing, nothing      # to quote wrong. `permits.exec` above is the provable allowlist.      command: ["docker", "ps", "--all", "--format", "{{.Names}}\t{{.Status}}\t{{.Image}}"]    on_error:      # Offline rehearsal · a host with no daemon answers with a literal that      # LOOKS like the real thing, so `diagnose` reads the same shape either      # way. Delete this once the daemon is really there.      recover: "REHEARSAL\tno docker daemon on this host\tn/a"  df:    exec:      command: ["docker", "system", "df"]   # SLOT: the second read (drop the task if one suffices)    on_error:      recover: "REHEARSAL · disk usage unavailable without a daemon"  diagnose:    with:      ps: ${{ tasks.ps.output }}      df: ${{ tasks.df.output }}    infer:      max_tokens: 600               # SLOT: the spend ceiling for this call      # SLOT: what should the model DO with the readings? Keep slot markers out      # of the block below — everything indented under `prompt: |` is prompt      # TEXT, and a stray comment line is sent to the model verbatim.      prompt: |        You are reading a Docker host's state. Containers (name·status·image):        ${{ with.ps }}        Disk usage:        ${{ with.df }}        Write a short health report: what is running, what exited, what        looks unhealthy (restart loops · old exits), and whether disk        usage needs attention. Plain prose, no preamble.  keep:    with:      diagnose: ${{ tasks.diagnose.output }}    invoke:      tool: "nika:write"      args:        path: "./docker-health.md"  # SLOT: same path as permits.fs.write        content: "${{ with.diagnose }}"outputs:  report:    value: ${{ tasks.keep.output }}    description: "Where the report landed — nika:write hands back the path it wrote"

sha256 b17d9ca7da5a0177. The copy above re-hashes to its pin on every test run (a copy is re-provable, never trusted). Source: docker-report.nika.yaml in the spec pack · open it in the playground →

the patterns it locks

  • argv-array exec (provable allowlist)
  • parallel reads
  • one artifact

Scaffold it locally: nika new docker-report my-flow.nika.yaml. Try the shape in the playground, or walk the showcase for the same patterns on real work. Read the spec →