Gunnar

nordisk/korp-collectors

brokkr.toml

# korp-collectors 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): korp-collectors had none. Across the
# whole constellation — 38 repos under ~/git — `nornir/brokkr.toml` was the ONLY
# pipeline declaration until this morning, when Skidbladnir became the second. So
# the forge had no job for this repo: four variants were declared in
# `.nornir/build-variants.toml` and the only thing that ever compiled any of them
# was a human typing `cargo xtask build`.
#
# The size of the hole is already written down in this repo's own build space, in
# the note on the `korp-collectors-osm-daemon` cell: measured on oden 2026-08-22,
# `cargo test -p korp-collectors` runs **112** lib tests and the same command with
# `--features osm-daemon` runs **241**. 129 tests — the region-cut daemon and the
# post-condition suite that decides whether a freshly cut extract may replace the
# one being served — reachable only by remembering a flag. The `daemon` step below
# is what makes them something a push touches.
#
# ── WHAT EACH STEP KIND ACTUALLY DOES (read before adding one) ──────────────────
#
#   run = { variant = "…" }  → `nornir_build_thing::build_variant` — a real cargo
#                              BUILD under `--real`
#                              (dwarves/crates/dwarves/src/runner.rs:217,253).
#   run = { tests   = "…" }  → the variant's OWN argv with `build` swapped for
#                              `test`, through `buildgate::run_cargo` — a real cargo
#                              TEST run (runner.rs:174). Same package, same features,
#                              same profile; `--bin`/`--lib` are stripped so the
#                              package's whole suite runs.
#   run = { container = … }  → `UnwiredRunner`. FAILS. Do not declare one.
#   run = { vm = … }         → `UnwiredRunner`. FAILS. Do not declare one.
#
# ⚠ THE DECLARATION IS AHEAD OF THE INSTALLED FORGE. `RunKind::Tests` landed in
# dwarves `abe10d4` (2026-08-25). The `dwarves` on this box is 0.1.2 and predates
# it: `dwarves validate` from that binary REJECTS the `tests` steps below with
# "invalid: parse ./brokkr.toml". Validated instead against a `dwarves` built from
# the current source tree. Reinstall the forge; do not delete the test steps.
#
# ⚠ ONLY A CARGO KIND MAY BE NAMED. `build_variant` spawns cargo with
# `variant.cargo_args()` and branches on NOTHING
# (edda/crates/nornir-build-thing/src/provenance.rs:473). All four of this repo's
# variants are `kind = "exe"` or `kind = "lib"`, so the whole build space is
# declarable here.

[pipeline]
name = "korp-collectors-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)
kind = "cron"
cron = "0 4 * * *"

# ── the light pure-Rust core ─────────────────────────────────────────────────
#
# WHY GROUPED THIS WAY: this repo's four variants sit on a strict THREE-RUNG
# DEPENDENCY LADDER, declared in Cargo.toml and visible in `[features]`:
#
#   parsers      → pure Rust: RDF/PxWeb/ENTSO-E/REST/Trafikverket + the shared HTTP
#                  core + the OSM tag trie. No geo dep, no skade, no Iceberg.
#   osm          → parsers + `osm-katana` (the heavy geometry/WKB engine)
#   osm-daemon   → daemon(+redb, blake3) + osm + `rotaryengine` + libc
#
# One stage per rung, so a red names the rung — "the parsers broke" and "osm-katana
# broke" and "the region-cut daemon broke" are three different mornings. The two `parsers`
# cells (the driver bin and the library a consumer such as facett-demo links) share
# a stack and therefore share a stage; everything heavier needs `lib` and nothing
# else, because the rungs above do not depend on each other's success to be worth
# attempting.
[[pipeline.stage]]
name = "parsers"

[[pipeline.stage.step]]
name = "lib"
run = { variant = "korp-collectors-lib" }        # the object-safe parser/registry lib

[[pipeline.stage.step]]
name = "driver"
needs = ["lib"]
run = { variant = "korp-collectors" }            # the unified collector driver bin

# ── the heavier stacks, each on its own dependency ───────────────────────────
[[pipeline.stage]]
name = "heavy-stacks"

[[pipeline.stage.step]]
name = "geo"                                     # + osm-katana way→WKB / admin relations
needs = ["lib"]
run = { variant = "korp-collectors-osm" }

# The cell whose absence this repo already measured: `osm-daemon` compiles
# `osm_cut.rs` (41 unit tests), `osm_cut_verify.rs` (11) and `daemon.rs` (11),
# i.e. the region-cut daemon and the post-condition suite. It is also the only rung that
# links redb and rotaryengine.
[[pipeline.stage.step]]
name = "daemon"
needs = ["lib"]
run = { variant = "korp-collectors-osm-daemon" }

# ── the suites, at the SAME features the artifacts are built with ────────────
#
# `parsers-tests` is declared on `korp-collectors-lib` and NOT also on
# `korp-collectors`: a test step strips `--bin`/`--lib` from the variant's argv, so
# the two would resolve to the byte-identical `cargo test -p korp-collectors
# --features parsers --profile release`. One writer.
#
# `daemon-tests` is the 112 → 241 step. Both numbers are this repo's own, measured
# on oden 2026-08-22 and recorded on the `korp-collectors-osm-daemon` cell.
[[pipeline.stage]]
name = "test"

[[pipeline.stage.step]]
name = "parsers-tests"
needs = ["lib"]
run = { tests = "korp-collectors-lib" }

[[pipeline.stage.step]]
name = "geo-tests"
needs = ["geo"]
run = { tests = "korp-collectors-osm" }

[[pipeline.stage.step]]
name = "daemon-tests"
needs = ["daemon"]
run = { tests = "korp-collectors-osm-daemon" }

# ── WHAT IS DELIBERATELY NOT DECLARED HERE, AND WHY ──────────────────────────
#
# NINE features gate tests in this repo that NO `[[build.variant]]` enables, so no
# step above can name them and ~70 `#[test]` fns are compiled by nobody. None of
# the nine is in `[build] offers`, so a variant for any of them needs that list
# widened first (`BuildVariantSet::validate` refuses a cell reaching for an
# unoffered capability):
#
#   feature      | dark tests | what is dark
#   -------------+------------+----------------------------------------------------
#   gribnc       |     17     | src/gribnc.rs (gated at src/lib.rs:1542)
#   service      |  13 (5+8)  | tests/register_across_processes.rs + src/nursery.rs
#   systemd      |  10 (2+8)  | tests/systemd_unit_is_really_installed.rs +
#                |            | src/runtime_systemd.rs
#   kvm-guest    |      9     | tests/osm_chain_kvm.rs
#   csv          |      7     | tests/collectors_csv.rs
#   mainframe    |      7     | tests/collectors_mainframe.rs
#   znippy       |   4 (2+2)  | tests/collectors_znippy.rs + src/znippy_pack.rs
#   gtfs         |      3     | src/gtfs.rs (gated at src/lib.rs:1522)
#   skade-sink   |      0     | tests/collectors_e2e_skade.rs (a file with no #[test])
#
# `systemd` and `service` are the sharpest of these: this crate installs a real
# systemd unit through skidbladnir, and the test that asserts the unit is *really*
# installed is behind a feature no declared build point turns on. Left as a stated
# finding rather than fixed here — widening `offers` and adding five or six cells is
# a change to the shared build space, not to this pipeline. Add them and the steps
# belong here.