Status: Superseded by ADR-281 — its D1-D3 are D36-D38 there
Date: September 20, 2026
Target Version: Nikaia 0.0.86
Related: ADR-039 D8 (the decision this builds), D2 (the rule
it is self-control of), D6, ADR-033 D8 (the precedent: a default
that cannot be switched off is imposed), ADR-037 D4 (a switch is a
cache-key dimension) and D7, ADR-021 D7 (dimensions coexist; they
do not share), ADR-057 D2 and D3 (what the mark costs),
ADR-167 D1 (which made NK2203 reach real programs),
Part I 1.2, Part II 12.3
ADR-039 D8 decided that the runtime re-entrancy check becomes a build option, on by default, in the manifest. #175 carried it as one of the lock section’s refusals nothing raises.
Measuring it first found the note that describes it to be wrong:
Implementation status: Not implemented. Nothing refuses the nesting of Part II 12.3, no re-entrancy check is emitted, and
nikaia.tomlhas no key for this option.
Two of those three clauses were false. NK2203 refuses the nesting and has for
some time; the runtime check is in std’s crossing shape, as a mark read before
every acquisition. Only the third — no key — was true. A stale status note is a
defect in its own right (README.md §1), because a reader cannot tell a plan
from a promise, and this one told a reader that a guarantee they have is one
they do not.
reentrancy-check is a [build] key, yes or no, and yes is the default.A third spelling is refused rather than guessed at — on reads like this option
and is not it — because a build that took the default for a word it did not know
would ship the guarantee the manifest declined. The key is checked against
[build]’s own list, so reentrancy_check with an underscore is the mistake
that list exists to catch.
No command-line override, which D8 asks for in one direction and this makes explicit in the other: it lives in the manifest, because a shipped build that could be changed from the command line is not reproducible. Part I 1.2 names the two options a single build may override, and this is not one of them. The environment carries the resolved word to the wrapper, which is a channel and not a second front door — Cargo owns the wrapper’s arguments.
std’s own tree.The first is ADR-037 D4’s rule for any switch. The second is new
here and is what the other two switches did not need: the check lives in the
nikaia-std crate, so the word decides what that crate contains. Without it
the second project on a machine links the first one’s answer, and a guarantee
declined in one manifest ships from another’s cache
(ADR-021 D7: dimensions coexist; they do not share).
The mechanism is a Cargo feature on nikaia-std, on by default, and the
generated manifest writes default-features = false for a package that declined
it — a switch the compiler acts on has to reach the crate the code is in.
std has two lock shapes (ADR-059). The crossing one carries
a mark this compiler wrote — an atomic load before the acquisition and a store
after it, which ADR-057 D2 makes free at one thread and D3 charges
only on values that actually cross — and that is what the switch declines. The
single-threaded one is a RefCell, whose borrow flag notices a re-entrant
borrow whatever anyone asks: it is the language below’s, and no option of ours
switches it off.
That is more noticing than the switch promises, which costs a program obeying the rule nothing and is stated rather than left to be discovered.
std built without the mark, in
a tree of its own.cfg.locks
column cannot answer for without refusing correct programs, and NK2201 and
NK2503, which need a question answered and a reachability walk respectively.All three decisions. crates/nikaia/tests/reentrancy_switch.rs holds the key,
the default, the refused third spelling, the mistyped key, and both cache
dimensions; crates/nikaia-std’s own tests hold the check firing where the
build carries it — reached by writing the nesting in Rust, because under D2 no
Nikaia program can get there — and the RefCell’s flag noticing either way.