showcase · tier 3 · builders
PR review swarm
One read-only review agent per changed file, in parallel and under budget. jq merges the findings.
The whole file
7 tasks · 6 waves · the plan falls out of the bindings, nothing is scheduled by hand.
t3-pr-review-fanout.nika.yamlsource
nika: v1workflow: id: pr-review-fanout description: "changed files → one read-only review agent each → merged REVIEW.md"model: ollama/qwen3.5:4b # local tool-calling model · swap for anthropic/claude-sonnet-4-6 for depthvars: base_ref: "main"tasks: changed: exec: command: ["git", "diff", "--name-only", "${{ vars.base_ref }}...HEAD"] files: with: changed: ${{ tasks.changed.output }} invoke: tool: "nika:jq" args: input: "${{ with.changed }}" expression: 'split("\n") | map(select(length > 0))' todo_sweep: invoke: tool: "nika:grep" args: pattern: "TODO|FIXME|HACK" path: "./src" reviews: with: files: ${{ tasks.files.output }} for_each: ${{ with.files }} max_parallel: 4 fail_fast: false on_error: recover: null # a budget-exhausted review yields null · the swarm lives agent: system: "You are a precise code reviewer. Read the file, then finish with ONLY the schema'd object ({file, findings: [{severity, message, line}]} · severity exactly one of blocker|high|med|low), then call nika:done." prompt: "Review ${{ item }} · bugs first, then risky patterns. Read it before judging." tools: - "nika:read" # read-only swarm · least privilege - "nika:done" max_turns: 6 max_tokens_total: 30000 schema: type: object required: [file, findings] properties: file: { type: string } findings: type: array items: type: object required: [severity, message] properties: severity: { type: string, enum: [blocker, high, med, low] } message: { type: string } line: { type: integer } merged: with: reviews: ${{ tasks.reviews.output }} invoke: tool: "nika:jq" args: input: "${{ with.reviews }}" # `. // []` · a clean diff SKIPS the swarm (empty for_each → null # upstream) and the fan-in must survive its own good news. expression: '(. // []) | map(select(. != null)) | map(.findings[] + {file: .file}) | sort_by(.severity)' summary: with: merged: ${{ tasks.merged.output }} todo_sweep: ${{ tasks.todo_sweep.output }} infer: prompt: | Merge these review findings into REVIEW.md · blockers first · ${{ with.merged }} Open TODO debt found by grep · ${{ with.todo_sweep }} End with a verdict · ship / fix-first / redesign. save: with: summary: ${{ tasks.summary.output }} invoke: tool: "nika:write" args: path: "./REVIEW.md" content: "${{ with.summary }}"outputs: findings: value: ${{ tasks.merged.output }} description: "All findings, severity-sorted, with file attribution" review: ${{ tasks.summary.output }}nika inspect · engine 0.105.0 · vendored graph, never re-derived
6 declared edges · 6 waves · the plan falls out of the bindings