Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/nornir

.nornir/jobs-guide.md

The job ledger — guide

Class: guide · audience: a techie reading the ledger or running a container as a job. The what is in jobs-design.md; the why in jobs-reasoning.md.

f0 — see what nornir is doing (the whole workflow)

Nearly every long op nornir runs is already a tracked job — you don't start jobs, you read them and drive the two ops that are jobs by hand (a container run).

  1. List — nornir jobs prints the ledger newest-first.
  2. Filter — --kind, --running, --limit.
  3. Script it — --json emits the same JobRecord array the viz renders.
  4. Run a container as a job — nornir container … tracks a one-shot podman/docker run like any other op.

Local vs server is automatic: with NORNIR_SERVER set, nornir jobs reads the server's ledger over Viz.Jobs; otherwise it opens the local jobs.redb read-only (snapshotting it aside if a running server holds the writer lock).

f1 — reading the ledger

nornir jobs                          # newest-first table, 50 rows
nornir jobs --running                # only non-terminal (queued / running) jobs
nornir jobs --kind bench_run         # one op family
nornir jobs --kind release_run --limit 10
nornir jobs --json                   # a JSON array of JobRecord (CLI ⟺ viz parity)
nornir jobs --json | jq '.[] | select(.status=="failed")'

The table columns are STATUS · KIND · TARGET · WORKSPACE · ELAPSED · RESULT, and it ends with a one-line summary (N job(s) · R running · F failed):

STATUS    KIND              TARGET                  WORKSPACE      ELAPSED  RESULT
✓ done    bench_run         facett                  nordisk        1240 ms  bench_runs:abc
… run     release_run       holger                  nordisk             —
✗ fail    docs_book         nornir                  nordisk         310 ms

RESULT is the result_ref — the pointer to the domain Iceberg row the op produced (e.g. bench_runs:<uuid>), so you can jump from "the job ran" to "here's what it made". A … run row with no elapsed is still in flight.

Reading heavy-job queuing. Benchmarks are exclusive — submit several and only one shows … run; the rest show queued. That is the scheduler making them serialize so neither perturbs the other's numbers:

nornir jobs --kind bench_run --running   # 1 running + the rest queued, draining one at a time

f3 — running a container as a tracked job

# Track a one-shot container; its exit code becomes nornir's exit code:
nornir container --image docker.io/library/alpine:3 -- echo hi

# docker instead of podman, with extra run flags and a custom job label:
nornir container --engine docker --image alpine:3 \
    --flag '-e' --flag 'FOO=bar' --target my-check -- sh -c 'echo $FOO'

The captured stdout/stderr is printed and the container's exit code is propagated, so shell && and CI gating work:

nornir container --image alpine:3 -- sh -c 'exit 7'   # nornir exits 7, job row = failed

Either way the run shows up in nornir jobs --kind container with its logs and exit code on the row (logs capped at 16 KiB/stream). A missing engine surfaces as a non-zero nornir exit and a failed job row.

Where else the same data shows up

The 🧬 nornir viz tab's job list reads the exact same records through the Viz.Jobs RPC — nornir jobs --json is its CLI twin. Nothing you run needs to opt into the ledger: release, docs, bench, scans, index/vector builds, coverage, and more already self-register, so nornir jobs is the one place to answer "what is (or was) running, and did it finish?".