AGPL-3.0-or-later · forever.

the path · 06

Code-review agent — a loop you do not write.

run itnika try 06-code-review

06-code-review.nika.yaml
# SPDX-License-Identifier: Apache-2.0# yaml-language-server: $schema=https://nika.sh/spec/v1/workflow.schema.json## 06 · Code-review agent — a loop you do not write.## The first five files are graphs: you decide the steps. Here you decide the# BOUNDS — which tools, how many turns, how many tokens — and the model# decides the steps inside them. That is the whole trade, and the reason the# grants below matter more than the prompt does.## Demonstrates ·#   - the `agent:` verb · model + tools, looping until done or out of budget#   - `tools:` · a default-deny whitelist · the agent gets ONLY these#   - `max_turns:` + `max_tokens_total:` · the two budget caps#   - `nika:done` · the completion sentinel that lets the loop end cleanly#   - the workflow boundary applies INSIDE the loop · granting the agent#     `nika:read` is not enough, the files need `permits.fs.read` too## Runs fully offline on a local tool-calling model, against a fixture# committed next to this file. Measured: 2 turns, ~1.6k tokens of the 8000# budget — it reads once, then reports. Wall clock is whatever your machine# and a resident-or-cold model make it (30s and 5m both observed here).## Needs · ollama with the model pulled · `ollama pull qwen3.5:4b`.## Run · nika run examples/06-code-review.nika.yaml   (from the repo root ·#   paths resolve against your working directory, not the file's location)nika: v1workflow:  id: code-reviewmodel: ollama/qwen3.5:4b   # local tool-calling model · zero key · the diff never leaves the machineconst:  target: "./examples/fixtures/review-me.rs"permits:  tools: ["nika:done", "nika:read"]  fs:    # The agent may call `nika:read`, and it may read THIS file. Those are two    # different permissions and both are required — a tool grant with no    # `fs.read` behind it fails mid-loop with    # « NIKA-SEC-004 · agent tool `nika:read` refused by the security boundary »,    # after the model has already spent a turn deciding to read.    #    # To review your own file, change this line AND `const.target` together.    # The permit does not follow the path around; that is what makes it a wall.    # (A bound cannot interpolate — `${{ const.target }}` here is NIKA-AUTH-007.)    read: ["./examples/fixtures/review-me.rs"]tasks:  review:    agent:      # Default-deny: this list IS the agent's universe. Anything not named      # here does not exist as far as the loop is concerned — there is no      # ambient toolbox to opt out of.      tools:        - "nika:read"   # open the file under review        - "nika:done"   # say "finished" and end the loop cleanly      # Two independent stops, because they fail differently: a model that      # ping-pongs forever hits `max_turns`, a model that reads something huge      # hits `max_tokens_total`. The loop also just ends when the model      # returns no tool call.      max_turns: 8      max_tokens_total: 8000      system: |        You are a senior Rust reviewer.        Read the file you are given with nika:read, then reply with your        findings as a short markdown list — one line per finding, each        starting with blocker/high/med/low. Then call nika:done.      prompt: "Review ${{ const.target }} and report bugs and style issues."# The agent's final message. It is free text — an agent answer is prose unless# you constrain it, and constraining it is a separate job (see 04 for the# schema machinery).outputs:  findings: ${{ tasks.review.output }}

nika-spec@6ac29351a · sha256 b5a6e43e79108d70