Skip to main content
The file proposes, the machine disposes. Arming a project means declaring beats in nika.yaml — what runs, when, under which policies — and letting the machinery fire them: an OS unit (launchd · systemd), or the resident nika serve where no OS scheduler exists. Reading this page schedules nothing; firing is always a machine act.

The arm: block

Each entry under arm: in nika.yaml is one beat:
signature: and budget: are reserved and refused today — they fail loudly during registry validation rather than silently doing nothing.

The miss policies (manqué:)

nika arm — the report

Bare, the verb READS the registry and reports what is armed, what fired, and what fires next — it schedules nothing:
The proof line separates PROUVÉ from DÉCLARÉ (law N3): the registry declares, only the sidecar proves the machine fired. A beat carrying the suspension triple is reported idle and never computed — asking a disarmed beat for its next slot would print a date nobody will see fire. A par: line is shown for what it is: declared, not verified. Sidecar directories no registry entry names are the orphelins — reported, NEVER erased (law N4). Exit 0 clean · exit 2 when the registry refuses, and the refusal teaches the fix. on-webhook is a declaration seam today, not a live listener. The report shows no clock slot and arm fire records an honest webhook skip; an event bridge that actually delivers the beat has not shipped yet.

nika arm fire <label> — the one firer

ONE function applies the on-time window, the miss policy, the overlap lock, the per-tick ceiling and the firing record — the emitted OS units and nika serve both end here, so the law lives exactly once. The label is the workflow file’s radical (workflows/doctor.nika.yamldoctor; a radical collision takes -2, -3 in file order). The stdout contract: a decision prints exactly one linefired … · skipped … · paused … · failed … — so a launchd or serve log stays byte-pure. Two honest exceptions: a registry refusal (exit 2) teaches multi-line, and an environment death (exit 3) rides stderr. What a fire guarantees:
  • The per-beat lock is taken BEFORE the decision and held until AFTER the receipt lands — at-least-once: a crash between the claim and its receipt leaves a durable unmatched claim in history.ndjson, never a silent double-fire. The current report does not fold that unsettled claim into its own line yet. Exactly-once is never promised; when the workflow’s effects are idempotent, the result is effectively-once.
  • Every fire is a FRESH run (law N2): a run you pause is parked with its trace (paused … · trace …), never resumed by the firer.
  • The clock is injectable (hidden --now) so a replay and a test are deterministic.
What a fire leaves — the sidecar .nika/arm/<label>/:

nika arm disarm <label>

Prints the disarm gesture (law N4): removing the line from the file is not it — actif: false + raison: + jusqu_au: is, because that leaves an auditable trail. --write removes only generated unit files and prints the launchctl bootout or systemctl --user disable --now command for the operator; it never executes that unload command itself. When it removed at least one unit, it then attempts to journal the gesture and reports a journal failure even though the file removal already happened.

nika arm --emit — the OS bridge

--emit launchd|systemd prints the OS unit that fires the beats instead of reading the registry; add --write to install it: launchd emits a macOS user agent (~/Library/LaunchAgents/nika.arm.<radical>.plist); systemd emits a user timer + service pair. Any of these flags set WITHOUT --emit refuses honestly — a flag that does nothing is a lie.

nika serve — the resident firer

In normal mode, the resident loop ends at the same firer as an emitted OS unit — for machines where no OS scheduler exists (a container, a bare server, a VM you own):
--dry is deliberately narrower than a real fire: it previews the cadence only. It does not take the beat lock or apply missed-run, overlap, cost, or execution policy, so would fire is not a promise that the real firer will run; nika arm fire can still answer skipped or refuse. Startup, registry, clock, and loop failures exit 1. Once the loop is running, each beat’s result is carried by its stdout line and ledger; a completed --once sweep exits 0 even when an individual run failed or paused. The hidden --now / --until pair injects the clock so a replay stays deterministic.
The honesty contract. Delivery is at-least-once and exactly-once is never claimed: a crash between the claim and the receipt leaves a visible orphan, not a silent double-fire. Keep a fired workflow’s effects idempotent and the result is effectively-once.