the skeleton register
gate-and-act
Your intent sounds like: watch X, act when Y 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
- 2tools grantedthe minimum for the job
- a548c0e5sha-pinnedre-hashed on every test run
the plan
derived from the file, never drawn by handcheckinvoke
actinvoke
the file, whole
conformance-gated upstream on every spec pushgate-and-act.nika.yaml
# SPDX-License-Identifier: Apache-2.0# yaml-language-server: $schema=https://nika.sh/spec/v1/workflow.schema.json## TEMPLATE · gate-and-act · check a condition, act only when it holds.## [check]────with · when(cond)────▶[act]# nika:fetch nika:notify## The watcher. Read a number, stay silent unless it crosses a line. Most runs# do nothing at all — and "nothing happened" is a SUCCESS here, not a failure.# Often zero model calls, so it is cheap enough to run on a schedule.## Demonstrates ·# - `when:` as a SKIP gate · routing, not failure · a skipped task is not an# error and does not fail the run# - flow vs capability · `egress:` sanctions the SEND, `permits.net.http`# grants the REACH · an alert path needs BOTH (see `permits.net`)# - `on_error: recover:` · the gate rehearses CLOSED, so the skeleton is# green before the real source is wired## Needs · nothing to rehearse. To alert FOR REAL: ALERTS_WEBHOOK_URL in the# environment and a live source in `const.source_url` — then delete the# `on_error:` so a dead source is loud.## Run · nika run <file> # offline rehearsal · the gate stays closednika: v1workflow: id: gate-and-act-template # SLOT: kebab-case workflow id # SLOT: one honest sentence — `nika new` matches intents against these words description: "watch a value · act only when the condition holds"const: source_url: "https://api.example.com/v1/value" # SLOT: what to watch threshold: 100 # SLOT: the trigger condition valuepermits: # the blast radius · default-deny once present tools: ["nika:fetch", "nika:notify"] net: http: - "api.example.com" # SLOT: the watched host from const.source_url - "hooks.slack.com" # SLOT: the host ALERTS_WEBHOOK_URL points at. # `host_from_self` below means the host is not # knowable at check — it is judged at RUN against # this list, so an unnamed host fails mid-flight.secrets: webhook: source: env key: ALERTS_WEBHOOK_URL # SLOT: where the act lands egress: - to: "nika:notify" host_from_self: true # the secret value IS the destination URLtasks: check: invoke: tool: "nika:fetch" args: url: "${{ const.source_url }}" mode: jq jq: "." output: value: ".value" # SLOT: the jq path to the watched field on_error: # Offline rehearsal · a sample UNDER the threshold — the gate stays # closed, the skeleton runs green before you wire the real source. recover: { value: 42 } act: with: value: ${{ tasks.check.value }} # the binding IS the edge · check → act # when: is a SKIP gate — routing, not failure (a skipped task is not an error) when: ${{ with.value > const.threshold }} # SLOT: the CEL condition invoke: tool: "nika:notify" # SLOT: the action · notify / write / exec args: channel: webhook target: "${{ secrets.webhook }}" message: "Threshold crossed · ${{ with.value }}" # SLOT severity: warningoutputs: value: ${{ tasks.check.value }}sha256 a548c0e551f7efb5…. The copy above re-hashes to its pin on every test run (a copy is re-provable, never trusted). Source: gate-and-act.nika.yaml in the spec pack · open it in the playground →
the patterns it locks
- jq extraction
- CEL skip-gate
- often zero model calls
cross-references
Scaffold it locally: nika new gate-and-act my-flow.nika.yaml. Try the shape in the playground, or walk the showcase for the same patterns on real work. Read the spec →