brokkr

August 25, 2026 · View on GitHub

Command orchestrator and development utility for pbfhogg, elivagar, nidhogg, litehtml-rs, and sluggrs. Single Rust binary that provides benchmarking, verification, profiling, visual reference testing, and operational commands across all projects.

Built with LLMs. See LLM.md.

Install

cargo install --path ~/Programs/brokkr

How it works

Run brokkr from any project root. It reads ./brokkr.toml to detect which project you're in and resolves datasets, paths, and host-specific configuration. Commands are project-gated - running a pbfhogg command from elivagar's root produces a clear error. (check is the exception - it works in any Rust+git repo, with or without brokkr.toml.)

cd ~/Programs/pbfhogg
brokkr inspect --tags --dataset denmark             # run once, print timing
brokkr inspect --tags --dataset denmark --bench     # 3 runs, store in DB
brokkr inspect --tags --dataset denmark --hotpath   # function-level timing
brokkr verify sort                                  # cross-validate against osmium

cd ~/Programs/elivagar
brokkr tilegen --dataset denmark --bench            # full pipeline benchmark
brokkr pmtiles-writer --hotpath                     # micro-benchmark hotpath

cd ~/Programs/nidhogg
brokkr serve                                       # start the nidhogg server
brokkr api --dataset denmark --bench                # API query benchmark

cd ~/Programs/litehtml-rs
brokkr visual --all                                 # visual reference tests

Commands

Measurement modes

Every measurable command supports these flags:

FlagBehavior
(none)Build, run once, print timing. No DB storage.
--benchFull benchmark: lockfile, 3 runs, best-of-N stored in DB (all N per-iteration walls stored too, in execution order)
--bench NSame but N runs
--hotpathFunction-level timing via hotpath feature (1 run)
--hotpath NSame but N runs
--allocPer-function allocation tracking (1 run)
--stop <marker>Kill the child when this FIFO marker is emitted (bench any phase in isolation)

All measured modes automatically attach a sidecar that samples /proc metrics at 100ms (see Sidecar profiler below).

All commands also accept --dataset, --variant, --commit, --features, --force, --verbose, --wait.

pbfhogg commands additionally accept --direct-io and --io-uring to enable O_DIRECT and io_uring I/O paths. These add the required cargo features to the build, pass the flags to the binary, and show up in the cli_args/brokkr_args columns of the results DB (query via brokkr results --grep direct-io). --direct-io works with all commands. --io-uring is only supported by apply-changes, sort, cat --dedupe, diff --format osc, repack, and degrade - brokkr rejects it for other commands before building. io_uring preflight checks run automatically.

Shared (all projects)

CommandDescription
checkRun gremlin scan + dependency rules + clippy + tests (extra args forwarded to cargo test)
test [-p <PKG>] <NAME>Run one cargo test in release mode via cargo test -p <PKG> <NAME> with --include-ignored --nocapture --test-threads=1; streams output, prints a [test] PASS/FAIL footer per run. Package resolution: explicit -p > [test] default_package in brokkr.toml > built-in default (pbfhogg-cli, nidhogg). -N repeats, -j passes through to cargo, --raw disables filtering. Gated off for litehtml/sluggrs (use brokkr visual).
envShow hostname, kernel, governor, memory, drives, tool versions, dataset status
resultsQuery the results database (.brokkr/results.db)
invalidateHard-delete results + sidecar rows by UUID or commit prefix (dry-run unless -f)
clean [--worktrees]Remove scratch/temp files; --worktrees also purges persistent benchmark worktrees
pmtiles-statsPMTiles v3 file statistics
historyBrowse global command history
previewRun full pipeline (enrich → tilegen → ingest → serve) and open map viewer
lockShow who holds the benchmark lock

check filters cargo output into one line per diagnostic. Compilation noise is stripped; each error or warning becomes error[CODE] file:line:col message or warning[rule] file:line:col message. Passing tests are aggregated (e.g. cargo test: 137 passed (4 suites, 1.45s)), failures become FAILED name location message. Use --raw for unfiltered cargo output, or --json for NDJSON with full-fidelity structured diagnostics (one JSON object per line). Falls back to raw output automatically if parsing fails.

A gremlin scan runs before clippy and fails the check if any banned Unicode character is found in tracked .rs/.toml/.md/.js/.sh files. Covers invisible/zero-width characters, non-breaking spaces, soft hyphen, line/paragraph separators, bidi marks/overrides/isolates, em/en dashes, typographic single and double quotes, plus U+0003, U+000B, and U+FFFC. Text mode prints one line per hit (file:line:col U+XXXX NAME); JSON mode emits gremlin / gremlin_summary events. See src/gremlins.rs for the full banned set.

If brokkr.toml contains [[dependency_rule]] entries, check then validates direct Cargo dependency boundaries from cargo metadata --no-deps. Example:

[[dependency_rule]]
name = "app-db-boundary"
from = "app"
forbid = ["db", "service-state"]

This rejects direct app -> db or app -> service-state dependencies before clippy/tests run. from and forbid each accept either one string or an array of package names; forbid can name workspace crates or external crates.

When many diagnostics are found at once (e.g. picking the checker up on an existing codebase), text mode caps each phase at --limit N entries (default 20) and prefers files changed on the current branch so the most actionable hits surface first. The cap applies to both the gremlin phase and the clippy phase (independently), with a trailer summarising what's hidden (+N more in this branch, +M in unchanged files (--triage to see)). Use --triage to see everything, or --limit N to override the cap. --raw and --json bypass the cap.

check runs clippy and tests against the same list of "active sweeps" - one cargo invocation per sweep, both phases. The sweep list is built from brokkr.toml, in priority order:

  1. CLI --features X / --no-default-features → a single ad-hoc sweep with those exact flags. Useful for spot-checking one combo without editing brokkr.toml. Skips [[check]] and any profile entirely; no build_packages.
  2. CLI --profile <name> or [test].default_profile → the named profile's resolved sweep list, with the profile's libtest filters (only / skip / include_ignored / test_threads / env) applied to the test phase. Each sweep references a [[check]] entry by name.
  3. [[check]] is configured but no profile applies → every [[check]] entry runs in declaration order, no libtest filters.
  4. None of the above → one --all-features sweep, identical to check's pre-[[check]] behaviour. Projects that haven't migrated keep working unchanged.

Each [[check]] entry declares name, optional features = [...] (explicit list - the features = "all" sentinel is rejected), optional no_default_features, and optional build_packages = [...]. build_packages rebuilds those cargo packages with the entry's feature flags before the test phase, so tests/cli_*.rs integration tests don't silently invoke a stale binary built for a different feature set. Diagnostics from clippy are deduped across sweeps and tagged in text mode ([<sweep-name>] or [both]) and in JSON mode (sweeps: [...] field on each diagnostic, sweep field on the per-sweep summary). Cost is ~Nx cold (cargo keeps separate target/ per feature set); incremental is small.

check also works without a brokkr.toml, so you can drop it into any Rust+git repo and get the same clippy + tests + gremlins pipeline (single --all-features sweep, no profiles).

test [-p <PKG>] <NAME> is the narrow counterpart to check for drilling into one specific test. It invokes cargo test -p <PKG> <NAME> (no --test), so both unit tests and integration tests inside the selected package are matched by the name substring. Package resolution runs in order: explicit -p/--package on the command line, then [test] default_package in brokkr.toml, then the project's built-in default (pbfhogg-cli for pbfhogg, nidhogg for nidhogg). Multi-crate workspaces (e.g. ratatoskr) have no built-in default, so they either pass -p on every invocation or set default_package in their brokkr.toml. It always builds release and always adds --include-ignored --nocapture --test-threads=1. Sweep selection mirrors check's ladder: if [test].default_profile is set, the test runs against every [[check]] entry the profile references; else if [[check]] is non-empty, every entry runs in declaration order; else fall back to a single --all-features sweep. Profile-level libtest filters (only / skip / tests) are intentionally dropped here - the user's <NAME> is the filter, and combining the two would silently produce zero matches when the test lives in a mod the profile would otherwise skip. Each sweep's build_packages are rebuilt with matching feature flags before the test phase. The test's own println!/eprintln! streams live; cargo compile progress, warning and error blocks, and the test harness framing (running N tests, test foo ... ok, test result:) are stripped. Each run ends with a single [test] line - PASS / FAIL / BUILD FAILED / SKIP - that includes wall time and, on failure, the panic message and location. SKIP means the name didn't match any test in that sweep (usually because the test is #[cfg(feature = "...")]-gated and the feature isn't enabled here); as long as at least one sweep saw a real match, brokkr exits 0. Only when every sweep skips does the whole run exit non-zero with a "check the package/name" hint. Because cargo test <name> is a substring filter, identically-named tests in different modules of the same package all run together - use a more qualified name (module path) to disambiguate. -N <n> repeats the test per sweep (for flaky-test hunting), -j <n> sets cargo -j N for parallel compile, and --raw bypasses all filtering. Litehtml and sluggrs projects are rejected here - use brokkr visual for fixture tests there.

pbfhogg

Every pbfhogg CLI command is a top-level brokkr subcommand: inspect, check-refs, sort, cat, add-locations-to-ways, build-geocode-index, apply-changes, merge-changes, extract, tags-filter, getid, diff, repack, degrade, etc.

Several commands are flag-driven umbrellas that previously had separate subcommand names:

  • brokkr inspect - no flag: metadata; --nodes: node stats; --tags: tag frequencies (narrow with --type node|way|relation)
  • brokkr cat - bare: indexdata-generation passthrough (no re-decode); --type way|relation: filtered full-decode; --dedupe: two-input dedupe path (supports --io-uring); --clean: full-decode / re-frame Framed path. Flags are orthogonal and combinable. Bare cat defaults to --variant raw since that's the natural bootstrap input.
  • brokkr tags-filter - --filter EXPR (default w/highway=primary), -R for single-pass (drop referenced), --input-kind osc to read an OSC diff instead of a PBF
  • brokkr getid - hardcoded ID set; --add-referenced for two-pass, --invert to negate
  • brokkr extract - --strategy simple|complete|smart picks the extract algorithm
  • brokkr diff - --format default|osc picks the output shape

add-locations-to-ways accepts --index-type (dense, sparse, external; default: hash). Lands in cli_args verbatim.

repack re-encodes a PBF with a configurable elements-per-blob cap (--elements-per-blob N, default 8000). degrade produces an adversarial PBF: --unsort (clear Sort.Type_then_ID and force one adjacent same-kind blob pair to overlap per kind), --strip-locations (drop LocationsOnWays), --strip-indexdata (clear per-blob indexdata). Flags compose; pbfhogg requires at least one transformation flag on degrade. Both write to scratch by default and overwrite each --bench iteration. Both also accept --as-snapshot KEY to promote the final iteration's artifact into the dataset graph - repack always writes under pbf.indexed; degrade writes under pbf.raw when --strip-indexdata is set, otherwise pbf.indexed. KEY=base is reserved (CLI sentinel for the dataset's primary data); existing keys are rejected unless --replace-snapshot is passed. Subsequent runs address the promoted artifact via --snapshot KEY on any snapshot-aware command (apply-changes, merge-changes, tags-filter --input-kind osc, diff, repack, degrade, diff-snapshots). Closing measurement loops blocked on adversarial inputs:

brokkr repack --dataset planet --elements-per-blob 8000 --as-snapshot packed-8k
brokkr getparents --dataset planet --snapshot packed-8k --bench
brokkr degrade --dataset planet --unsort --as-snapshot unsorted
brokkr sort --dataset planet --snapshot unsorted --bench 1

Multi-variant benchmarks: read, write, merge, extract (with --strategy, --modes, --compressions flags - --compressions is plural because the single-value --compression collides with pbfhogg's passthrough flag on write / merge).

merge-changes accepts --osc-seq <N> for a single OSC file (back-compat) or --osc-range LO..HI to merge a contiguous range of configured OSC entries in one invocation. The range form lands in cli_args verbatim, so brokkr results --command merge-changes --grep 'osc-range' finds the range-form runs.

brokkr diff (with or without --format osc) derives its second input by running apply-changes on the dataset's PBF + OSC and caching the result at <scratch>/<pbf-stem>-osc<seq>-bench-merged.osm.pbf. The cache key includes the OSC seq so different --osc-seq invocations don't silently reuse each other's merged files. In any measured mode (--bench/--hotpath/--alloc) the cache is rebuilt before the run so total invocation wall time is reproducible; pass --keep-cache to opt back into reuse. Run mode (no measurement flag) always reuses the cache for dev-loop speed. Cache hit/miss + age land in the result row's metadata as meta.merged_cache and meta.merged_cache_age_s.

diff-snapshots benchmarks pbfhogg's diff against two independent point-in-time snapshots of the same dataset (e.g. planet-20260223 vs planet-20260411). Unlike diff (with or without --format osc) - which derives its B side from apply-changes and therefore preserves blob-level byte equality with the A side - diff-snapshots forces every blob through full decode on both sides. Different working set, different peak memory, different wall time. The dataset's primary (legacy top-level) PBF is referenced as base; additional snapshots registered via brokkr download <region> --as-snapshot <key> are referenced by their snapshot key. The --format flag selects between summary diff (default) and OSC-format output. The --from/--to/--format choices are recorded verbatim in cli_args - query osc-only runs via brokkr results --command diff-snapshots --grep 'format osc'.

brokkr diff-snapshots --dataset planet --from base --to 20260411 --bench 1
brokkr diff-snapshots --dataset planet --from 20260411 --to 20260418 --format osc

suite pbfhogg runs the full benchmark suite.

Verification (brokkr verify <subcommand>): cross-validates against osmium, osmosis, and osmconvert. Subcommands: sort, cat, extract, multi-extract, tags-filter, getid-removeid, add-locations-to-ways, check-refs, merge (apply-changes), derive-changes (diff → osc roundtrip), renumber, diff, and all (runs them all). Verify subcommands keep their own short names; they don't mirror the consolidated CLI umbrella shape.

verify renumber is a special case. Most verify commands require pbfhogg's output to be byte-identical (or element-identical) with osmium's. renumber does not: pbfhogg's orphan-reference handling in relation members is a documented intentional deviation (see pbfhogg's DEVIATIONS.md and notes/renumber-planet-scale.md section 5b), so a small non-zero diff is expected and does not indicate a regression. The goal of the command is to separate "expected delta" from "actual regression" without a human having to triage every diff.

brokkr verify renumber                              # default: denmark
brokkr verify renumber --dataset europe --verbose   # print detail on mismatch
brokkr verify renumber --start-id 1,1,1             # forwarded to both tools

Per run it renumbers the input PBF with both tools, runs pbfhogg diff -s -c -v on the two outputs, and classifies the result:

  1. Parses the Summary: left=N right=M same=X different=Y line to get element counts.
  2. Scans the detail output for *n<id> / *w<id> / *r<id> block headers and counts diff blocks per element type.
  3. Runs pbfhogg inspect on the osmium output to recover the total relation count for the threshold check.

PASS when element counts match, no node or way block headers appear in the diff, and the total diff count stays under 0.10 * total_relations (sanity threshold - calibrated from measured rates like Denmark's 306 orphan-ref diffs ÷ 46,103 relations ≈ 0.66%; the threshold catches regressions that would typically be orders of magnitude higher without flagging normal transboundary delta).

FAIL when any of those three checks fire: divergent element counts, any node/way diff, or relation diffs that blow past the sanity threshold. On failure the diff log at target/verify/renumber/verify-renumber-<dataset>-diff.txt is preserved alongside both renumbered PBFs for human review. On success all three scratch files are removed. The --verbose flag additionally prints the first 50 lines of the diff to the terminal when any mismatch (expected or not) is found. verify all includes renumber as part of the pre-release sweep.

Other: download <region> [--osc-seq N] fetches datasets from Geofabrik. Accepts short aliases (denmark, europe) or full Geofabrik paths (europe/france, asia/japan/kanto). Skips files that already exist (checked against brokkr.toml filenames). --osc-seq N downloads all missing OSC diffs from the last configured seq through N, hashes them, and appends entries to brokkr.toml. New downloads use dated filenames matching the project convention (e.g. europe-20260329-seq4716.osc.gz).

download <region> --as-snapshot <key> registers a new historical snapshot of an existing dataset under [host.datasets.<region>.snapshot.<key>] instead of touching the dataset's primary pbf/osc tables. Requires the dataset to already exist (run brokkr download <region> first). The snapshot key must match [a-zA-Z0-9_-]+; base is reserved as the CLI sentinel for the dataset's legacy/primary data. Files are written with snapshot-specific names (<region>-<key>.osm.pbf, etc.) and the indexed PBF is generated automatically.

download <region> --refresh rotates the dataset to a newer upstream snapshot. HEAD-checks upstream Last-Modified first; no-ops if not newer than the existing pbf.raw's mtime / download_date (use --force to rotate anyway). On rotation: archives the existing primary pbf/osc tables under a [snapshot.<key>] block (key derived from download_date or file mtime as YYYYMMDD), downloads the new PBF, generates the indexed PBF via pbfhogg cat, updates download_date to today, and resets the OSC chain. Errors with a clear message if the derived snapshot key collides with an existing snapshot block. After refresh, the archived state is reachable via brokkr diff-snapshots --from <key> --to base and brokkr apply-changes --dataset <region> --snapshot <key> --osc-seq <N>.

Every measurable pbfhogg command accepts --snapshot <key> to read its input from a historical snapshot rather than the dataset's primary tables. Producers (apply-changes, merge-changes, tags-filter --input-kind osc, diff including --format osc, repack, degrade) consume both PBF and OSC from the snapshot's tables; read-side consumers (sort, cat, inspect, add-locations-to-ways, getid, getparents, renumber, check-refs, check-ids, time-filter, tags-filter, extract, multi-extract, build-geocode-index) read their PBF input from the snapshot's pbf.<variant> table. --snapshot base (or omitting the flag) preserves the existing behavior - script-friendly when parameterizing over snapshot keys. The --snapshot flag lands verbatim in cli_args, so brokkr results --grep 'snapshot 20260411' (or just --grep 20260411) finds every command run against that snapshot.

Calling plain brokkr download <region> against a dataset whose pbf.raw is already configured is a SKIP (no auto-refresh), and prints a multi-line message naming both --refresh and --as-snapshot so the user knows the alternatives without having to read the source.

elivagar

CommandDescription
tilegenFull tile generation pipeline (with all pipeline flags)
pmtiles-writerPMTiles writer micro-benchmark (--tiles N)
node-storeSortedNodeStore micro-benchmark (--nodes N)
planetilerPlanetiler comparison
tilemakerTilemaker comparison

suite elivagar runs the full benchmark suite.

Other: compare-tiles, download-ocean, download-natural-earth.

nidhogg

Server: serve, stop, status.

Operations: ingest, update, query, geocode.

Benchmarks: api (query performance), nid-ingest (ingest), tiles (tile serving).

Verification: batch, nid-geocode, readonly.

litehtml-rs

Visual reference testing and fixture preprocessing. All commands are top-level (no brokkr litehtml namespace). Shared visual testing commands (visual, list, approve, report, visual-status) dispatch to litehtml or sluggrs based on the detected project. visual was formerly named test; that name is now owned by the generic cargo single-test runner in the Shared table above.

SubcommandDescription
visualRun fixtures against Chrome reference artifacts (pixel diff + element comparison)
listShow fixtures, tags, and approval state
approveRecord current divergence as accepted baseline
visual-statusDashboard of all fixtures vs approved baselines
reportShow results for a past test run
prepareNormalize raw email HTML into self-contained fixture (images → gray PNGs, inject Ahem font, strip external resources, pretty-print)
html-extractExtract sub-fixture by CSS selector (--selector) or sibling range (--from/--to)
outlineStructural overview with section markers, content previews, and suggested selectors

Fixture workflow:

brokkr prepare raw-email.html fixtures/email-prepared.html
brokkr outline fixtures/email-prepared.html --selectors
brokkr html-extract fixtures/email-prepared.html \
  --from "div:nth-of-type(2) > table > tbody > tr > td > div:nth-of-type(4) > div" \
  --to   "div:nth-of-type(2) > table > tbody > tr > td > div:nth-of-type(7) > div" \
  fixtures/creatine_products.html
brokkr visual creatine_products

prepare and html-extract shell out to a Node.js script (requires Node + pnpm; auto-installs dependencies on first use). Node is already required for Puppeteer-based Chrome capture.

Preview pipeline

brokkr preview runs the full data pipeline and opens a map viewer for visual inspection:

brokkr preview                          # full pipeline, default dataset/variant
brokkr preview --from tilegen           # skip enrich, start from tile generation
brokkr preview --from serve --no-open   # just restart server, don't open browser
brokkr preview --dataset japan --variant raw

Steps: enrich (pbfhogg add-locations-to-ways) → tilegen (elivagar run) → ingest (nidhogg ingest) → serve (nidhogg serve + browser). Use --from to skip upstream steps when iterating on a single project.

Requires a [hostname.preview] section in brokkr.toml pointing to each project's source tree:

[plantasjen.preview]
pbfhogg = "/home/folk/Programs/pbfhogg"
elivagar = "/home/folk/Programs/elivagar"
nidhogg = "/home/folk/Programs/nidhogg"

Artifacts are written to .brokkr/preview/ (enriched PBF, PMTiles, ingest data dir). Works from any of the three project roots.

Benchmark harness

All benchmarks run through BenchHarness, which provides:

  • Exclusive lock - prevents parallel bench/verify/hotpath runs via lockfile
  • SQLite storage - results stored in .brokkr/results.db per project with git commit, hostname, and full environment snapshot
  • Multiple timing modes - in-process (N runs, best-of-N), subprocess (external binary), and distribution (min/p50/p95/max)
  • Retroactive benchmarking - --commit <hash> builds and benchmarks old commits via a persistent git worktree (sibling .brokkr-worktree-<project>-<short>/). Reused on subsequent runs at the same commit so cargo target/ survives. Run brokkr clean --worktrees to garbage collect.
  • OOM protection - memory availability checks before large-scale runs

Sidecar profiler

Every measured run (bench, hotpath, alloc) automatically samples /proc/{pid}/stat, /proc/{pid}/io, and /proc/{pid}/status at 100ms intervals. Data is stored in .brokkr/sidecar.db (gitignored - local to the machine that ran it). The main results in .brokkr/results.db stay small and git-tracked.

The child process receives BROKKR_MARKER_FIFO env var pointing to a named pipe for application phase markers and counters. Markers are lines of the form <timestamp_us> <name>, counters are <timestamp_us> @<name>=<value>. Markers are point-in-time bookmarks - the protocol has no notion of spans or pairs. brokkr sidecar <uuid> --durations derives pair durations from a FOO_START / FOO_END convention if the emitter happens to follow it, but nothing else assumes that structure.

--stop <marker> kills the child process as soon as the named marker is emitted, allowing benchmarks of individual phases without waiting for the full run to complete. The SIGKILL exit is treated as success.

Sidecar data is stored even when the child is OOM-killed - the /proc trajectory up to the kill is the most valuable use case.

Querying sidecar data

A UUID prefix is required (except for --compare) - use brokkr results to find one. The dirty pseudo-UUID resolves to the most recent failed/dirty-tree run.

brokkr sidecar <uuid>                              # per-phase summary (default view)
brokkr sidecar <uuid> --human                      # same, as a fixed-width table
brokkr sidecar <uuid> --samples                    # raw /proc samples (JSONL)
brokkr sidecar <uuid> --samples --fields rss,anon --every 10  # project + downsample
brokkr sidecar <uuid> --samples --where "majflt>0" --tail 20  # filter + range
brokkr sidecar <uuid> --samples --phase STAGE2                # filter to a marker phase
brokkr sidecar <uuid> --samples --range 10.0..82.0            # time window filter
brokkr sidecar <uuid> --markers                    # raw marker events (JSONL)
brokkr sidecar <uuid> --durations                  # START/END pair timings
brokkr sidecar <uuid> --counters                   # application counters
brokkr sidecar <uuid> --stalls                     # *_wait_ns counter stall attribution (see conventions below)
brokkr sidecar <uuid> --stat anon                  # min/max/avg/p50/p95 for a field
brokkr sidecar <uuid> --stat anon --phase STAGE2   # per-phase stat
brokkr sidecar --compare <uuid_a> <uuid_b>         # phase-aligned comparison
brokkr sidecar dirty --stat anon                   # inspect last failed/dirty run

Filter flags (--phase, --range, --where) compose with --samples and --stat; --phase also composes with --counters. --grep <SUBSTR> filters --counters by counter name, and composes with --phase.

Sidecar conventions

The FIFO protocol is convention-free - brokkr doesn't mandate any naming scheme on markers or counters. Two optional conventions unlock richer views when emitters adopt them:

  • FOO_START / FOO_END marker pairs. Used by --durations to derive per-span timing. Brokkr pairs a FOO_START with the next FOO_END of the same base name; unpaired starts render as standalone markers. In --human, span names seen more than once collapse into a single aggregate row (NAME xN total … min/avg/max) so a high-frequency span can't bury the phase rows; the JSONL form stays one object per span. The split is on observed cardinality, not on the name. --stop FOO_END kills the child on the end marker verbatim; --stop -FOO and --stop FOO are shorthand aliases that resolve to FOO_END (the fallback form prints a one-line notice so the resolved name is visible). Markers are for the small set of true phase boundaries only - a high-frequency span emitted as marker pairs drowns every phase-oriented view at once (each marker is treated as a boundary), so per-event blocking spans belong in counters, not markers (see below).

  • <category>_wait_ns stall counters. Accumulated blocking time is a counter concept, not a marker one. Emit a strictly-monotonic counter named <category>_wait_ns - one atomic add of the blocked nanoseconds per blocking event (channel sends, mutex waits, I/O backpressure, one-shot joins). brokkr sidecar <uuid> --stalls takes the max value per name (monotonic, so max is the cumulative total), strips _wait_ns for the category, and reports each as ms + % of wall. The % can exceed 100 for a counter accumulated across concurrent threads - it's the average number of threads parked in that category at any instant (e.g. 430% ~= 4.3 threads in decode-send); single-threaded waits read as clean sub-100% fractions. The view spans projects: it rolls up pbfhogg's pipeline_decoded_send_wait_ns, pipeline_raw_send_wait_ns, ... alongside elivagar's sort_chunk_write_wait_ns, assemble_partition_batch_wait_ns, pmtiles_write_wait_ns, ... in one table. Runs with no *_wait_ns counters produce a clear "no *_wait_ns counters" message rather than silent empty output.

Both conventions are additive - projects can opt in incrementally. Existing emissions keep working with no change, and the only required mechanical migration is whichever blocking points you want to instrument. (Historical note: --stalls originally paired WAIT_<CATEGORY>_START/_END markers; it moved to *_wait_ns counters so the marker stream stays reserved for phase boundaries and the stall view unifies with pbfhogg's existing wait counters. Brokkr no longer treats any WAIT_-prefixed marker specially.)

See notes/sidecar.md for the scoped plan these conventions were introduced with; LLM.md has broader history.

Results database

Query stored benchmarks with brokkr results:

brokkr results                                      # table of last 20 results
brokkr results 0b74fb6f                             # look up by UUID prefix
brokkr results --command read                       # last 20 matching 'read'
brokkr results --commit a65a                        # filter by commit
brokkr results --mode hotpath                       # filter by measurement mode
brokkr results --grep pipelined                     # substring-match cli_args + brokkr_args
brokkr results --grep apply-changes --grep uring --grep zstd:1   # stack --grep: AND across terms
brokkr results --grep apply-changes --grep-v uring  # exclude: the arm WITHOUT io_uring
brokkr results --dataset europe                     # filter by dataset (substring on input file)
brokkr results --command tags-filter --dataset eu   # combine filters
brokkr results --meta merged_cache=miss             # filter by runtime-observation metadata key
brokkr results --command diff-snapshots --grep 'format osc'     # osc-only diff-snapshots runs
brokkr results --meta merged_cache=miss --command diff           # cold-cache diff runs only
brokkr results --compare a65a 911c                  # compare two commits side-by-side

Columns that drive the filters:

  • command - the bare subcommand id (read, cat, diff-snapshots, api, …); no bench /hotpath prefix.
  • mode - the measurement mode only (bench / hotpath / alloc).
  • cli_args - literal subprocess argv (pbfhogg/elivagar/nidhogg). Every flag and axis the user passed is here verbatim.
  • brokkr_args - literal brokkr <...> invocation the user typed. Same-column-dual-view, recorded so --grep can find either.
  • meta.* (via --meta KEY=VALUE) - runtime observations only: resolved paths, detected modes, cache hit/miss. Anything derivable from cli_args or brokkr_args is NOT duplicated here. meta. prefix is implicit; multiple --meta flags AND together; rows missing the requested key are silently excluded. Available keys depend on the command - e.g. diff emits meta.merged_cache + meta.merged_cache_age_s; elivagar's tilegen emits meta.locations_on_ways_detected; historical meta.format/meta.index_type/meta.start_stage keys were migrated out in v13 (they live in cli_args now).

Use --grep for anything that's a flag/axis (--grep zstd:1, --grep 'snapshot 20260411', --grep 'index-type external'). Use --meta for genuine runtime observations.

--grep-v is the negative form: repeatable, and a row is excluded if it matches any term (--grep ANDs, --grep-v ORs). It's the only way to select an A/B arm defined by an absent flag - --grep uring finds the ON arm, but the OFF arm has no distinguishing token to match, so it needs --grep-v uring. Both are literal substring matches (% and _ are not wildcards), and both apply to --compare as well as the row listing.

brokkr results <uuid> additionally shows what a table row can't: the per-iteration walls of a --bench N run in execution order, and the prev.* pairs naming what ran immediately before. --compare A B annotates a pair with a host: line when the two runs saw different available memory, governor, or kernel.

The dataset column in the output table is the first dash-separated component of the input filename - europe-20260301-seq4714-with-indexdata.osm.pbf renders as europe. This is a display heuristic: filtering via --dataset always substring-matches the full input_file column, so filters still work even when the short name collapses distinct datasets (e.g. a hypothetical europe-west would display as europe). The full filename and size are shown in the single-result detail view (brokkr results <uuid>) as the input field.

The compare view shows timing, output size, peak RSS, rewrite ratio, and blob distribution columns as applicable. Hotpath comparisons include function-level timing diffs.

Invalidating results

A benchmark run under the wrong pretences (wrong dataset, wrong features, wrong git state, interrupted sidecar, …) produces numbers that will skew every future comparison. brokkr invalidate hard-deletes such runs from both .brokkr/results.db (runs + run_distribution + run_iterations + run_kv + hotpath_functions + hotpath_threads) and .brokkr/sidecar.db (samples, markers, summary, counters, meta, and any sidecar_latest pointer rows like dirty that resolve to a deleted UUID).

Dry-run by default - the command prints each matched UUID and tags whether sidecar data is present, then exits. Pass -f / --force to actually delete.

brokkr invalidate 0b74fb6f              # preview by UUID prefix
brokkr invalidate 0b74fb6f -f           # perform
brokkr invalidate --commit a65a         # preview every run on matching commits
brokkr invalidate --commit a65a -f      # perform
brokkr invalidate dirty -f              # nuke the last dirty/failed run's sidecar

Sidecar-only UUIDs (dirty-tree or failed runs with no results DB row) are picked up too, so a single invalidate call cleans both sides. Deletions in .brokkr/results.db should be committed like any other change to that file - git history preserves the audit trail.

Quick runtime timing

By default, every measurable command builds and runs once with timing output - no DB, no harness overhead:

brokkr inspect --tags --dataset denmark
# [run] /path/to/pbfhogg inspect tags denmark.osm.pbf --min-count 999999999
# ... command output ...
# [run] elapsed=1234ms

Add --bench to enable the full harness with DB storage:

brokkr inspect --tags --dataset denmark --bench    # 3 runs, best-of-N stored
brokkr inspect --tags --dataset denmark --bench 10 # 10 runs

For ad-hoc passthrough with raw args: brokkr passthrough -- <args>.

Configuration

Each project has a brokkr.toml in its root:

project = "pbfhogg"

# Host-specific config (matched by hostname)
[plantasjen]
data = "data"
scratch = "data/scratch"
target = "target"
port = 3033
drives.source = "nvme"
drives.data = "ssd"
features = ["linux-direct-io", "linux-io-uring"]

[plantasjen.datasets.denmark]
origin = "Geofabrik"
download_date = "2026-02-20"
bbox = "8.0,54.5,13.0,58.0"

[plantasjen.datasets.denmark.pbf.indexed]
file = "denmark-with-indexdata.osm.pbf"
xxhash = "a1b2c3d4e5f6..."
seq = 4704

[plantasjen.datasets.denmark.pbf.raw]
file = "denmark-raw.osm.pbf"
seq = 4704

[plantasjen.datasets.denmark.osc.4705]
file = "denmark-4705.osc.gz"
xxhash = "f1e2d3c4b5a6..."

# Optional historical snapshots - additional point-in-time captures of the
# same dataset, registered via `brokkr download denmark --as-snapshot <key>`.
# The legacy top-level pbf/osc data above is implicitly snapshot `base`.
# `diff-snapshots --from base --to 20260411` diffs the two.
[plantasjen.datasets.denmark.snapshot.20260411]
download_date = "2026-04-11"
seq = 4969

[plantasjen.datasets.denmark.snapshot.20260411.pbf.raw]
file = "denmark-20260411.osm.pbf"
xxhash = "..."

[plantasjen.datasets.denmark.snapshot.20260411.pbf.indexed]
file = "denmark-20260411-with-indexdata.osm.pbf"
xxhash = "..."

# Cross-project source trees for preview pipeline
[plantasjen.preview]
pbfhogg = "/home/folk/Programs/pbfhogg"
elivagar = "/home/folk/Programs/elivagar"
nidhogg = "/home/folk/Programs/nidhogg"
  • project - which project this is (pbfhogg, elivagar, nidhogg, or litehtml-rs)
  • [hostname.datasets.*] - named datasets with PBF variants, OSC diffs, PMTiles entries, and bounding box
  • [hostname.datasets.*.snapshot.<key>] - additional historical snapshots of the dataset (different point-in-time PBFs of the same region). Each snapshot has its own pbf and optional osc tables. The legacy top-level data is implicitly snapshot base (a reserved name). Snapshot keys must match [a-zA-Z0-9_-]+. Snapshots are first-class for diff-snapshots and addressable via --snapshot <key> on every measurable pbfhogg command (apply-changes, merge-changes, tags-filter, diff including --format osc, repack, degrade, sort, cat, inspect, add-locations-to-ways, getid, getparents, renumber, check-refs, check-ids, time-filter, extract, multi-extract, build-geocode-index). Refresh-mode downloads (brokkr download <region> --refresh) populate snapshot blocks automatically by archiving the previous primary state. Generative commands brokkr repack --as-snapshot KEY and brokkr degrade --as-snapshot KEY register newly-produced artifacts under this same shape
  • xxhash - optional XXH128 hash for file integrity checks (sha256 accepted as alias during migration). Run brokkr env to see computed hashes for updating config
  • [hostname] - per-host path overrides, port, drive configuration, and default cargo features; defaults to data/, data/scratch/, and cargo target dir
  • features - cargo features appended to every build (all measurable commands, verify, serve, ingest, update). Not applied to check. CLI --features are additive on top
  • [[check]] - top-level array of tables (not host-scoped). Each entry is one (clippy + test) sweep with the entry's feature flags. Fields: name (required, unique label - referenced by name from [test.profiles.*].sweeps), features = [...] (explicit list; the features = "all" sentinel is rejected so adding a new feature to Cargo.toml doesn't silently broaden coverage), no_default_features = true|false, build_packages = ["pbfhogg-cli", ...] (rebuilt with the entry's features before the test phase, keeping tests/cli_*.rs CliInvoker calls honest). Multiple entries catch lints that feature-gated proc-macro rewrites mask under any single feature set. The legacy [check] table form (with consumer_features) is rejected at parse time
  • [[dependency_rule]] - top-level array of direct Cargo dependency boundary rules enforced by brokkr check. Fields: optional name, from = "app" or from = ["app", "rtsk"], and forbid = "db" or forbid = ["db", "rusqlite"]
  • [test] - top-level (not host-scoped). default_package = "..." is the cargo package brokkr test uses when no -p/--package is given; required for multi-crate workspaces that lack a built-in default. Explicit CLI -p always wins. default_profile = "..." is the validation profile brokkr check uses when no --profile is passed - typically tier1 for fast inner-loop runs. [test.profiles.<name>] declares a profile with sweeps = [...] (list of [[check]] entry names), optional tests/only/skip/include_ignored/test_threads/env, and optional extends = "<other>" (single-parent inheritance, child collections replace parent's). Profiles use Rust module paths as the annotation surface: tests inside mod tier2 { ... } / mod platform { ... } / mod serial { ... } are filtered by name substring (only = ["tier2::"], skip = ["platform::"]). Dangling sweep references (profile names a [[check]] entry that doesn't exist) are caught at parse time. The legacy [test.sweeps.*] map form is rejected; sweeps live in [[check]] now

License

Apache-2.0