path Is Relative to the File It Is Written InStatus: Superseded by ADR-286 — its D1-D3 are D16-D18 there
Date: September 22, 2026
Target Version: Nikaia 0.0.161
Narrows: ADR-002 D1 — the value with type removed reaches
Cargo verbatim. A path is resolved on the way through; everything else
still travels untouched
Answers: #150
Related: ADR-053 D1 (the directory that made the old rule
wrong), ADR-047 D2 (a Nikaia package’s path, which already
worked this way)
nikaia.toml names a Rust crate by path like this:
[dependencies]
hyper-shim = { type = "rust", path = "../../../../shim" }
Four .., because ADR-002 D1 says the value reaches Cargo
verbatim and the generated manifest used to sit at
target/nikaia/build/Cargo.toml.
It does not sit there any more. ADR-053 D1 gave every Nikaia
package a member directory of its own, so the manifest Cargo reads a path
against is target/nikaia/build/<package>/Cargo.toml — one level deeper — and
every one of those four .. is one short. All three projects under
examples/foreign-runtime/ have been unbuildable since, and nothing said so:
the tests that build them fetch hyper and tokio from crates.io and are
#[ignore]d, and CI does not run ignored tests.
And the compiler’s own reader disagreed with Cargo in the same way.
describe::crate_sources resolved the value against target/nikaia/build, so
nikaia describe and cargo build looked in two different places for one
crate’s sources — and only the first had a test, because
crates/nikaia/tests/describing.rs never runs Cargo.
The two arms of [dependencies] had two different rules, which
examples/hello-http/nikaia.toml explains in a comment because getting it
wrong costs a confusing message: a Nikaia package’s path
(ADR-047 D2) is relative to the file it is written in, and a Rust
crate’s was relative to a directory the author has never seen.
path in nikaia.toml is relative to nikaia.toml, for both arms of [dependencies].../shim means the directory beside this project’s, as it does in every other
file anybody writes. One rule for both arms, and it is the one a reader would
guess.
A relative path is resolved against the project root and written out as an
absolute one. Everything else in the table still reaches Cargo untouched:
version, git, tag, features, default-features, a package rename.
This is not a new mechanism. The generated member manifest already writes
nikaia-std = { path = "/…/crates/nikaia-std" } absolute, for the same reason:
a path in a generated file cannot be relative to a directory the generator
chose and the author never sees.
And the alternative was counted out and refused. Keeping the value verbatim
means writing ../../../../../shim — a number a person derives from a build
layout, which changes when the layout does, and which changed once already
without anybody noticing for as long as the tests that would say so stayed
ignored.
path is relative to is a property of the file, not of the ecosystem.The old split had one answer for a Nikaia dependency and another for a Rust one, and the difference was not about either ecosystem — it was about which generated file happened to hold the value. A rule that a person has to learn per arm is one rule too many for something this shape.
examples/foreign-runtime/ go from four .. to one, and those three projects build again.nikaia describe and cargo build agree about where a crate’s sources are, which they did not.crates/nikaia/tests/describing.rs and the foreign-runtime tests now mean the same thing by a path, so the first can keep standing in for the second.target/nikaia/build/ is written by every build and is never shipped (ADR-021 D1 owns what is stale inside it).git or version dependency is untouched, because neither has a path to resolve.All of it. examples/foreign-runtime/crossing, serve and smuggled name
../shim; describe::crate_sources and describe::crate_root resolve against
the project root; project::cargo_member writes the resolved path.