Gunnar

nordisk/dwarves

.nornir/clone-and-build-for-idiots.md

# Clone dwarves and build it — on a machine that has nothing else

**`git clone dwarves && cargo build` DOES NOT WORK, and it never has.** It does not fail
at link time or on a missing system library — it fails before cargo resolves a single
crate:

```
error: failed to load manifest for workspace member `…/dwarves/crates/dwarves`
Caused by: failed to load manifest for dependency `autonom`
Caused by: failed to read `…/edda/crates/autonom/Cargo.toml`
Caused by: No such file or directory (os error 2)
```

Measured on a fresh clone of `main` 6052d02 (2026-08-07) into an empty directory.

That is not a bug in the clone. dwarves is one repo of a **constellation** that is wired
with `path` dependencies to its siblings, and `path` deps are not optional: cargo reads
every one of them to build the dependency graph. A `path` that is not there is a hard
error, so **the siblings are a build dependency exactly like a crate is** — they are just
not something cargo can fetch for you.

There is no `--offline`, no feature flag and no `[patch]` trick that avoids this. The only
fix is to have the siblings on disk, side by side.

## The layout — this is the whole secret

Every `path` is relative (`../edda/...`, `../facett/...`), so the repos must be **siblings
in ONE parent directory**. The parent's name does not matter; `dwarves` must not be nested
inside another repo.

```
<parent>/
├── dwarves/          ← the one you actually wanted
├── edda/             ← 53 path deps point here (the most load-bearing sibling)
├── facett/           ← 37 path deps (the UI framework)
├── nornir/
├── holger/
├── modgunn/          ← TRANSITIVE: nothing in dwarves names it; holger-server-lib does
├── znippy/
├── znippy-zoomies/
├── skade/
├── draupnir/
├── Skidbladnir/      ← capital S, and the directory name must match
├── tunnr/
└── funnel-core/
```

**Twelve repos, dwarves included.** Verified sufficient: with exactly these,
`cargo metadata` on a fresh `main` clone exits 0. Verified necessary the honest way — by
adding them one at a time and re-reading the error, which is how `modgunn` was found.

## Do it

```sh
mkdir -p ~/git && cd ~/git

git clone git@codeberg.org:nordisk/dwarves.git
git clone git@codeberg.org:nordisk/edda.git
git clone git@codeberg.org:nordisk/facett.git
git clone git@codeberg.org:nordisk/nornir.git
git clone git@codeberg.org:nordisk/holger.git
git clone git@codeberg.org:nordisk/modgunn.git
git clone git@codeberg.org:nordisk/znippy.git
git clone git@codeberg.org:nordisk/znippy-zoomies.git
git clone git@codeberg.org:nordisk/skade.git
git clone git@codeberg.org:nordisk/draupnir.git
git clone git@codeberg.org:nordisk/Skidbladnir.git
git clone git@codeberg.org:nordisk/funnel-core.git

# ⚠ tunnr is NOT under nordisk. It is mikronova, and it is an ssh:// URL.
git clone ssh://git@codeberg.org/mikronova/tunnr.git
```

Then check the wiring BEFORE starting a long build, because this is the step that tells
you a sibling is missing in one second instead of twenty minutes:

```sh
cd dwarves && cargo metadata --format-version 1 >/dev/null && echo "constellation OK"
```

`warning: patch … was not used in the crate graph` on that command is **normal** — the
`[patch.crates-io]` block in `dwarves/Cargo.toml` covers the fat/`holger` build, and a
default build does not resolve all of it. Warnings are not the verdict; the exit code is.

### Two traps that are not about dwarves

* **Do not clone every sibling at a tag or a pinned SHA.** These repos move together, and
  the `path` deps mean you compile whatever is checked out — not what `Cargo.lock` says.
  Clone the default branch and leave it alone.
* **The canonical branch is not `main` everywhere.** `znippy`'s is `master`. `git clone`
  gets this right on its own (it follows `origin/HEAD`); a script that hard-codes
  `-b main` gets it wrong. If you need to know, ask the repo, never a document:
  `git symbolic-ref refs/remotes/origin/HEAD`.

## Then build

`cargo build` is not the entry point either — what gets built comes from
`.nornir/build-variants.toml`, so the declaration cannot drift from the build:

```sh
cargo xtask build --suite ORIGINAL --list          # the five declared variants
cargo xtask build --suite ORIGINAL --variant dwarves-ui
```

(`--atom` is the deprecated spelling of `--variant`; it still works.)

The five variants are `dwarves-server` (the service), `dwarves` (the fat CLI), `dwarves-cli`
(the thin CLI), `dwarves-ui` (the thin desktop UI) and `dwarves-solo` (the UI with the
server compiled in). Pick the one you want; `dwarves-ui` is the desktop app.

Disk: the fat `dwarves-server` artifact alone is ~300 MB and the shared `target/` for the
whole constellation runs to tens of GB. On a laptop, point `CARGO_TARGET_DIR` at a disk
that can take it.

## Robot UI tests are WAYLAND ONLY

Do not reach for `Xvfb`, and do not set `DISPLAY`. The robot-UI drive supports Wayland and
nothing else — see `dwarves-gui/src/robot_mode.rs` and the `nornir-robotui` display
backend. On a Wayland session the visible replay is:

```sh
cargo xtask build --suite ORIGINAL --variant dwarves-ui
dwarves-ui --robot-mode headless    # off-screen, no window, exits non-zero on a red step
dwarves-ui --robot-mode watch       # a real window, replays the recorded tab tour
```

`--robot-mode headless` needs `--features robot-remote`; `watch` needs `--features watch`.
A binary built without them **refuses the flag and exits 2** rather than opening a plain
GUI window that drives nothing.