Gunnar

nordisk/edda

brokkr.toml

# edda CI/build pipeline — driven by the dwarves forge (brokkr descriptor +
# sindri scheduler). `dwarves validate` / `dwarves plan` / `dwarves run [--real]`.
#
# WHY THIS FILE EXISTS (MEASURED 2026-08-25): edda had none. Across the whole
# constellation — 38 repos under ~/git — `nornir/brokkr.toml` was the ONLY pipeline
# declaration until Skidbladnir got one this morning. edda is the shared-lib
# SAMLING the rest of the suite links to avoid circular deps, so the repo nothing
# could schedule was the one furthest upstream of everybody else.
#
# WHICH STEPS BUILD AND WHICH RUN — say it once, plainly.
#
#   `run = { variant = … }`  BUILDS. `nornir_build_thing::build_variant`, a real
#                            cargo build under `--real`, producing the artifact.
#   `run = { tests = … }`    RUNS. `RunKind::Tests` (dwarves `abe10d4`) takes the
#                            SAME variant's `cargo_args()` with `build` swapped for
#                            `test` — same package, same features, same profile —
#                            through `buildgate::run_cargo`. It names a variant
#                            rather than carrying its own feature list on purpose:
#                            a test run whose features can drift from the build's
#                            tests something the forge does not ship.
#
# `RunKind::Container`/`RunKind::Vm` still fall through to `UnwiredRunner`, so
# nothing here declares one. Until `abe10d4` there was no word for a test run at
# all, and every pipeline in the constellation declared compile coverage only.
#
# ⚠ VALIDATED AGAINST dwarves 0.1.5 (abe10d4173d0), built from source. The binary
# INSTALLED on this box is 0.1.2 (2026-07-17), which predates `RunKind::Tests` and
# REJECTS this file outright — `dwarves validate` prints "invalid: parse …" with no
# further detail. That is a stale validator, not a bad declaration; the file is
# deliberately ahead of the installed forge. Rebuild/reinstall dwarves before
# reading a red from it.
#
# Every step names a real row in `.nornir/build-variants.toml`. The two build steps
# carry no `needs` between them on purpose: `autonom` and `robotui` are unrelated
# binaries in unrelated crates, and an invented edge would mean a break in one
# stops the other from being attempted at all. Each test step needs only its own
# build, for the same reason.
#
# ⚠ THE SHAPE OF edda MAKES THIS PIPELINE NARROW, and that is worth saying rather
# than leaving to be discovered. edda is ~30 crates and declares TWO variants,
# both `kind = "exe"` — the only two shipped binaries in the samling. Every LIBRARY
# crate here (the actual product) is compiled by this pipeline only as a transitive
# dependency of those two bins, in whatever feature configuration they happen to
# pull. `.nornir/build-variants.toml` `offers` five features; the variants enable
# two (`scan`, `watch`), leaving `watch-wgpu`, `wire` and `bench` offered and built
# by nothing.
#
# ⚠ DARK TESTS, counted on this checkout 2026-08-25 — 96 library `#[test]` fns sit
# behind `#[cfg(feature = …)] pub mod` in a `src/lib.rs` where no variant enables
# the feature. That is the shape `nornir_testmatrix::audit_repo` cannot see at all,
# because it walks test TARGETS and these are unit tests inside the LIBRARY:
#
#   nornir-docgen  export      29   svg 3 · book 11 · apidoc 1 · ai_context 14
#   korp-installer forge       16   forge 12 · isocompose 1 · wgfixture 3
#   nornir-bench   runner      14
#   jera           agent-grpc  10
#   nornir-saga    web          6   (njord)
#   korp-installer secure-boot  5
#   nornir-robotui wire         4   · snapshot 4  (`snapshot` is not even offered)
#   nornir-build-thing plugin-host 4
#   autonom        bench        3   · nornir-saga bench 1
#
# None of it is fixable from this file: covering a feature means adding
# `[[build.variant]]` rows, and a variant is a claim about what edda SHIPS. Left as
# a decision for a human, and written down so it stops being invisible.

[pipeline]
name = "edda-ci"

[[pipeline.trigger]]              # fire when the poll sweep sees HEAD move
kind = "poll"
events = ["push"]

[[pipeline.trigger]]              # …and nightly at 04:00 (off nornir 03:00 / skidbladnir 03:20 / modgunn 03:40)
kind = "cron"
cron = "0 4 * * *"

# ── the two shipped binaries ─────────────────────────────────────────────────
[[pipeline.stage]]
name = "build"

[[pipeline.stage.step]]
name = "autonom"
# crates/autonom, bin `autonom` — the "code lacking tests" worklist CLI. Needs
# `scan` compiled in (cargo `required-features = ["scan"]`), mirrored in
# `[build.required]` so a bad combo fails with a message instead of a raw cargo
# error.
run = { variant = "autonom" }

[[pipeline.stage.step]]
name = "robotui"
# crates/nornir-robotui, bin `robotui` — the shipped accessible robot UI. Needs
# `watch` (eframe + the wire surface); an egui desktop app, not a suite service.
run = { variant = "robotui" }

# ── the suites those two feature sets light up ───────────────────────────────
[[pipeline.stage]]
name = "test"

[[pipeline.stage.step]]
name = "autonom-tests"
needs = ["autonom"]
# `cargo test -p autonom --features scan --release`. `autonom`'s `default = []`,
# so a plain `cargo test` compiles `feeder` and `report` — 20 library `#[test]`
# fns — out of existence entirely. This step is the only thing that runs them.
run = { tests = "autonom" }

[[pipeline.stage.step]]
name = "robotui-tests"
needs = ["robotui"]
# `cargo test -p nornir-robotui --features watch --release`. `nornir-robotui`'s
# `default = []` too, and `watch` gates 15 library `#[test]` fns in `src/watch.rs`.
# Checked before declaring it: `eframe::run_native` is on the shipping path only
# (`src/watch.rs:255`); the `#[cfg(test)] mod` starts at `src/watch.rs:940` and
# opens no window, so these run headless on a forge worker.
run = { tests = "robotui" }