nika-action
August 7, 2026 · View on GitHub
nika-action · plan with receipts
Static pre-flight for Nika workflows
in CI: when a PR touches a .nika.yaml, this action posts what the change
would do before anyone runs it: the nika check verdict, an honest cost
floor, the models and secrets it needs, its egress statics, and the DAG
(rendered natively by GitHub):
✅ nika check: clean ·
flows/report.nika.yaml· 3 task(s) · 3 wave(s) 💰 cost floor ≥ $0.12 · ⚠ 1 unpriced task: never rendered as $0 🔐 requires: models:ollama/qwen3.5:4b· secrets:OPENAI_API_KEY🌊 schedule: 3 wave(s), max width 1 🗺 DAG (collapsible mermaid)
This is the same audit you get locally · a broken DAG is a named finding with its fix, before any token is spent:
name: nika
on:
pull_request:
paths: ['**.nika.yaml'] # fire only when a workflow file changes
permissions:
contents: read
pull-requests: write # the sticky comment: drop it and the body
# lands in the step summary instead
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: supernovae-st/nika-action@704e664ddeeee8513f953b75e00238d0ab7dc0ac # v1.0.11
with:
workflow: flows/report.nika.yaml
mode: check # or: test (offline mock golden lane)
The pin is the full commit SHA with the release in a comment: the form
Dependabot and Renovate bump for you. @v1 also works and tracks the
latest v1.x.y.
No workflow yet? nika new chain flows/report.nika.yaml scaffolds
one (authoring in 10 minutes)
- or start from this repo's own
fixtures/flow.nika.yaml, which is what the CI checks against itself.
What it does, and what it refuses to
| lane | what runs | secrets |
|---|---|---|
check (default) | nika check --json + nika inspect: static analysis, nothing executes | none |
test | + nika test against <file>.golden.json: the mock provider, offline, deterministic | none |
not provided. Executing workflows (which can carry exec: shell steps) under a CI token is a decision this action refuses to make for you. Run lanes belong in your own workflow, behind your own review, never on fork-origin events | none |
Security posture (the boring contract)
- The install is verified: the release tarball is checked against the
release's published
SHA256SUMSbefore extraction. No barecurl | tar. - Zero secrets by default: both lanes are static/offline. The action never reads provider keys.
- Fork PRs: the default
GITHUB_TOKENon a forkpull_requestis read-only: the comment degrades to the step summary automatically. Never wire this (or anything) viapull_request_target+ a checkout of the PR head: a.nika.yamlcan declareexec:steps by design, so "run the PR's file under a privileged token" is code execution with your secrets. Same-repo PRs get the sticky comment; forks get the summary. - One comment, forever: the comment is upserted by a hidden per-file marker: re-pushes edit it in place, never spam the thread.
- Pin this action by commit SHA: demonstrated in the usage block above, and what we do to nika itself inside. Full posture: SECURITY.md.
Honesty semantics (why "receipts")
- The cost figure is a floor, not a total:
spend ≥ floor. Rendered as≥ $X, always. - Unpriced is never $0: a task with no list rate renders as
unpricedwith its reason verbatim (NoTokenLimit, uncataloged model, …). A model the engine cannot price does not become free by omission. - The budget bound is stated, not hidden:
--max-cost-usd(in your own run lanes) stops new admissions. The worst-case overshoot is one full wave:spend ≤ floor_checked + W · c_max, whereWis the max wave width the comment prints. Tighten withmax_parallel:when the budget is strict. - Unknown
report_version→ the comment renders the stable subset and says so. This action never guesses at fields it does not know.
Inputs
| input | default | notes |
|---|---|---|
workflow | required | path to the .nika.yaml (one file; matrix over paths for more) |
mode | check | check | test |
comment | true | sticky PR comment (needs pull-requests: write) |
engine-version | 0.108.0 | the nika release to install (checksum-verified) |
native-strict | false | fail while native-first hints remain |
github-token | github.token | override for the comment upsert |
Outputs
check-exit (0 clean · 2 findings) · clean (true/false: chainable in
if:) · cost-floor (static floor in USD · empty when unavailable, never a
fake 0) · cost-unbounded (true when unpriced/unbounded tasks exist: a
0.0 floor with this true is NOT free; consume the pair, never the bare
number) · comment-file (rendered markdown path).
More than one workflow file
workflow takes one path; fan out with a matrix: one sticky comment per
file, upserted independently:
jobs:
check:
runs-on: ubuntu-latest
strategy:
matrix:
flow: [flows/report.nika.yaml, flows/triage.nika.yaml]
steps:
- uses: actions/checkout@v4
- uses: supernovae-st/nika-action@v1
with:
workflow: ${{ matrix.flow }}
FAQ
Does this run my workflow? No: and there is deliberately no mode that
does. check is static; test runs the offline mock provider. Execution
belongs in your own steps, behind your own review.
Why did the comment land in the step summary instead? Fork PR: the
default token is read-only there. That's the designed degradation, not a
bug; never grant pull_request_target to force the comment.
The cost line says unpriced: is that an error? No: a task without a
list rate renders as unpriced with its reason, because a model the engine
cannot price does not become free by omission.
Links
- Every door in one page: install paths, IDEs, agents, skills, MCP, CI, SDKs: docs.nika.sh/integrations/everywhere
- Engine: github.com/supernovae-st/nika (Rust, AGPL-3.0-or-later)
- Language spec: github.com/supernovae-st/nika-spec (Apache-2.0)
- Docs: docs.nika.sh
License
Apache-2.0: the adoption side of the Nika license split. The engine this action downloads stays AGPL-3.0-or-later; invoking it as a subprocess imposes nothing on your repository.
🦋 SuperNovae Studio · Paris
The city · where this repo sits
📜 nika-spec ──── the civil code · the law tables, the corpus, the exam
│ sync-pack: byte-gated mirror │ projectors: drift-gated
▼ ▼
⚙️ nika ───────── the engine + the catalog (the yellow pages)
│ the release train 🖥️ nika.sh · 📖 nika-docs
▼ the showroom · the manual
📦 homebrew-tap · npm · Docker ── the docks
🔌 nika-client · 🎨 nika-vscode · 🤖 nika-plugins · ⚡ gh-nika ── the doors
🏭 nika-action · 🧪 nika-actions-starter ── the CI district ◀── you are here
🏪 nika-registry ── the market · 🏛 nika-estate ── the land registry
This building · THE GATE IN CI · runs the check on every pull request and posts the verdict, the cost floor, the permits and the DAG as one sticky comment.
Root · neither · this building runs the ENGINE's released binary and reports what it says. It decides nothing about the language and nothing about the engine · nothing authoritative is typed here.
Consumes · the engine's released binary (pinned by version, checksum-verified) · the workflow files in the caller's repo.
Serves · any GitHub repository that wants a receipt before a token is spent.
Truth lives · the verdict is the binary's exit code and its JSON, never the Action's own prose · a green comment the binary did not produce is a bug, not a nicety.
All the buildings: nika-spec · nika · nika.sh · nika-docs · nika-client · nika-vscode · nika-plugins · gh-nika · homebrew-tap · nika-action · nika-actions-starter · nika-registry · nika-estate
Every fact has one home · everything else is a gated projection. The living map: nika.sh/map.