Changelog

Since 0.0.8, every change package raises the patch number by one, and a heading below is one package: what it decided, what it changed, what it left open. The version is the specification’s; the compiler’s crates carry their own. A change package is a change to the language - the compiler, the parser, std or the specification. A change only to the guide, the notes, the README, the website, the tests, the examples or the tooling around them raises nothing and has no heading here; CLAUDE.md draws the line.

[0.0.1343] — 2026-11-03

immutable: the reserved word, the binding inside a function and the deep rule NK1331 (ADR-389 D1, D2, D6)

[0.0.1342] — 2026-11-03

check: a lent match binding is the pattern of a text method as it is, with no & of its own (check_host a_lent_part_is_a_reference_already) (#1176:, #1178); check: a copy of a tuple with view parts is a tuple of its own, each view part copied (ADR-293 D24) (#1184); check: a number type is not a std module, so u64::from no longer asks for use std::u64 (#1186); checker: a text item a walk lends looks up a text-keyed map as str (#1190, #1190); checker: a let of a field of a call’s owned result moves it out (#1191); checker: an element read past a jump of a struct declared beside is a view (#1183); checker: a let of a field of a call’s owned result moves it out (#1191); describe: crate_root (describe.rs into describe.nika) (#1193); lowering: text into a fs::Path parameter the callee only reads is lent as a PathBuf (#1195); lowering: m.get(k)?.field lends the member like m[k]?.field (#1194); lowering: text into a fs::Path parameter the callee only reads is lent as a PathBuf (#1195); std: String::pop and String::insert_str; OutText’s take_lend and brace inserts move into emit_out.nika (#1180); ; specbook: which reading got furthest, and the report, in Nikaia; contracts: infer_package_noted’s pass order, the keeps fixpoint, evaluate_defaults and expression_ensures move into ledger_items.nika (contracts/mod.rs, Part: #125)

[0.0.1341] — 2026-11-02

; text_tiers: .to_string() of an owned or number local stays owned (ADR-282 D8) (#1005); std: every set has iter() (ADR-collections-2 D3) (#1006); check, emit: sum() names its result type (#1007); ; ; ; check: a lambda typed -> T? wraps its non-null arms in Some (#1017); ; checker: a let that shadows a fn parameter is not the lent parameter (Fixes #1023); check: a fn parameter lent to a kept position is refused with NK2101 (#1024); keeps: a parameter a kept lambda names is kept (#1025); ; ; ; keeps: a lambda handed out keeps the fn parameters it names (#1034); ; buffers: a tuple result with a view part keeps the argument made in the call (Fixes #1036); ; ; emit: an untyped let lambda run by a parameter that may pause is an async closure (#1046); ; ; ; check: a map’s read as the fallback of ?? beside an owned T? is copied or refused (ADR-293 D15) (#995); ; ; contracts: the stored-code fold moves into ledger_items.nika; the dead LedgerOps::candidates goes (C1b); contracts: keeps’ inference moves into keeps.nika (K4); ; type_rules: chunk 2 (expand, literals, given) moves into type_rules.nika (R-3); Parts taken out of a mut parameter, and a narrowed mut option, change in place; A part handed to a mut parameter is lent, not given away; Remove the architect role: the supervisor writes the briefs, the driver stops for it; sysroot: the tools package is assembled in sysroot_package.nika (S1); std: Entry::or_default and Seq::cloned (Fixes #1082); std: process::run takes stdin, stdout and stderr options (Fixes #920); ; std: fs::Metadata::modified, links, file_id and time::since_epoch

[0.0.1340] — 2026-11-01

a taking arm over an enum of another file; a number bound under a match on a part held in place (#125)

[0.0.1339] — 2026-11-01

a variant part of code keeps its lambda; a text field asked of a list of text compares as text (#125)

[0.0.1338] — 2026-11-01

a part handed to a mut parameter is lent; parts a taking arm takes out of a mut parameter change in place (#125)

[0.0.1337] — 2026-11-01

rs2nika: a type the cut does not declare has its methods freed too; rs2nika: a text cut at a number goes through its bytes (ADR-374); rs2nika: let with every name mut is let mut (..)

[0.0.1336] — 2026-11-01

A function type may take a function parameter (#1120)

[0.0.1335] — 2026-11-01

Optional code narrows to a callable function; a match arm keeps a moved part of a by-value parameter (#1117, #1092, #125)

[0.0.1334] — 2026-11-01

self-hosting: project.rs’s ledger and proofs writing moves into project_write.nika; rs2nika R334 (#125)

[0.0.1333] — 2026-11-01

self-hosting: a project’s drive and drive_to move into project_drive.nika (#125)

[0.0.1332] — 2026-11-01

self-hosting: the prover’s run moves into prove_run.nika (#125)

[0.0.1331] — 2026-11-01

self-hosting: proofs.rs’s answers move into proofs_answers.nika (#125)

[0.0.1330] — 2026-11-01

check: a list or map element past a jump compares with a value (#1112)

[0.0.1329] — 2026-10-31

spec: ref in front of a tuple (#1115); a path orders, equates and hashes (ADR-319 D9, #1116)

[0.0.1328] — 2026-10-31

Comptime hands a turn’s field to a function; a sync call with type arguments in brackets (#1087, #1095)

[0.0.1327] — 2026-10-31

prover: loop turns and collection lengths bound counters, argument lengths, every length below 2^63, sound preconditions; claims in the figure (#1053, #1054, #1069, #1076, #1084, #1090)

[0.0.1326] — 2026-10-31

Grammars build a struct from keyed members; a function takes a reflected field (#1019)

[0.0.1325] — 2026-10-31

Grammar rules choose a pattern by a type’s kind

[0.0.1324] — 2026-10-31

views: a by-value parameter that holds no text is no view source (#1070); views: a list of views handed back may be a literal or grown with extend (#1071); tiers: a view field read off a match payload of a lent struct is a view (#1072)

[0.0.1323] — 2026-10-31

Grammar rules take type parameters

[0.0.1322] — 2026-10-31

nikaia test on parallel workers; loop checks through the prover’s candidates; –bounds names its reasons (#1053, #1054)

[0.0.1321] — 2026-10-31

check, emit: type arguments at a call reached by a path (ADR-grammar-2 D11)

[0.0.1320] — 2026-10-31

spec: NK1233’s example is one a value already says (ADR-grammar-2 D11, ADR-330 D9)

[0.0.1319] — 2026-10-31

parser, check, emit: a type as an argument, type arguments at a call (ADR-330 D1, D9, ADR-grammar-2 D11, #859 steps 2, 8)

[0.0.1318] — 2026-10-31

Grammar: hex_digit is one hexadecimal digit (#1002)

[0.0.1317] — 2026-10-31

check, emit: type kinds, a reflected field’s .ty, match on a kind (ADR-330 D2-D6, #859 steps 3, 1, 4)

[0.0.1316] — 2026-10-31

Kept callbacks: a fn parameter lent to a kept position is refused with NK2101 (#1024), a parameter a kept lambda names is kept (#1025), NK1264/NK1265 (#1012); grammar labels on literals and one-element groups bind (#1027); a call in a guard is a call site (#1032)

[0.0.1315] — 2026-10-31

The prover follows short loops turn by turn and a match as an if (ADR-361 D9, D12; #639, #1009, #1021); NK1241 refuses a wide integer as scalar (#1003); a callback through a trait borrows (ADR-277 D17 step 1, #1012); the NK2104 note no longer names seq (#994)

[0.0.1314] — 2026-10-30

ADR-grammar-2 accepted: a grammar entry takes a type and is emitted per type; members[S] builds the struct; json::read forwards to the grammar (#604)

[0.0.1313] — 2026-10-30

ADR-277 D17: a trait method’s keeps column (#1012); sum() names its type (#1007); a returned map read of literal text is copied (#996); a binding inside a bound text(..) is kept (#1013)

[0.0.1312] — 2026-10-30

an impl’s method keeps what its trait method keeps (#1008)

[0.0.1311] — 2026-10-30

sets are walked by iter(); a kept function value is handed on more than once (#1006, #1008)

[0.0.1310] — 2026-10-30

text tiers: .to_string() of an owned or number local stays owned (#1005)

[0.0.1309] — 2026-10-30

check, emit: a let-bound lambda takes its parameter types from its first use (#997); text tiers: to_string() of a number, bool or char is new text, also of a part a pattern binds (#998)

[0.0.1308] — 2026-10-30

The concurrency design is closed (ADR-async-2 D9, ADR-async-3 D7, ADR-async-4 D13-D15, #983). async and sync stay, and the option names handle, deferred, detached and on_timeout are final. within: on async(...) is a deadline from the start on every form; the task::TimedOut it gives is a failure and meets the use-site catch, then the fallback, then the receiver. h.join(within: d) limits that wait: when it runs out, task::TimedOut belongs to the join, the task keeps running and the handle stays live (NK2109 asks for it to be taken on that path too); on_timeout: task::OnTimeout::Cancel cancels it instead (changes ADR-346 D3, D4). The lambdas of fold, par_pieces and par_fold never pause (NK2209, whose help names map then fold). Part I 8.1 is now “Pausing Without Marking Functions”, and Part II 11.1 “Implicit Pausing and the Scheduler”. Nothing of it is built yet (#984-#991).

[0.0.1307] — 2026-10-30

**fmt: a closure’s tuple pattern is not cut (fix-forward, CI red since 0f9a525); lowering: text a method hands back as its own is this frame’s buffer, and reaches no parameter (#933); check: NK2605 only for a failing cleanup still held where its block ends (#934); text tiers: a viewed text moved into a literal is kept, or refused at the move where the field owns (#942); text tiers: a literal bound by a let is declared String, never a String? taken from the program (#943); text tiers: a struct result viewing a local takes the caller’s keep; project_lower_published takes the ledger’s pairs out of the entry instead of copying them (ADR-views-1) (#949); a part taken out of a let mut view or a mut parameter is given back before its block is left (ADR-views-1); ; check_core_call_on: the two receiver/argument binding merges use BTreeMap::entry; check_program, fold_bound, throws: contains_key-then-insert sites with the key given away use BTreeMap::entry; rs2nika: for x in xs.iter_mut() / &mut xs is drafted as for mut x in xs (#951 step 4); let mut x = m.get(k) ?? over a mut map or list is a mutable view (get_mut) (#952); check: a name an arm binds by ref is a view in the comparisons of its body (#954); ; text tiers: the pass reads the package's published fields; a tuple looked up keeps its literal parts (#956, #956); ; ; ; ; buffers: a text handed whole to push through an if or match arm is moved, not put in the frame's keep (#969); sharing, text_tiers: sites_by_slot, put_in_class and the ties of a stored value use BTreeMap::entry; check: a let mut of an Entry's or_insert binds the &mut itself and lowers without Rust mut (#974); check: a mutable view handed to a mut parameter is lent on as &mut *x (#975); check: or_insert_with's lambda hands back the map's value, and the text its body ends in is built (#977, #977); check, emit: an assignment to a let mut view writes through it, and a view of a number is read as *c (#976); ; extend: a list literal argument is walked against Vec[T], so text literals into a Vec[String] are owned again; extend's element check calls a receiver's str element String, as it does the argument's: no false NK1102; text tiers: an element reached by index, get or a for binding owns the text changed through it (#967); view changes: NK2305's for-mut read help names the list's place (#971); manifest: Manifest and its readers into Nikaia (MF)**

[0.0.1306] — 2026-10-30

async is reserved, overlap leaves the reserved-word list (ADR-async-4). let async = 3 is refused as a reserved word, with async_ suggested; the grammar has no async expression yet. overlap { … } still parses through its own rule and stays refused as a name until it is removed (#991). The compiler’s list matches Part I’s again.

[0.0.1305] — 2026-10-30

async is an expression prefix and the one way to start a task (ADR-async-2, -3, -4, #983). async e starts at once and yields the value, waited where it is first needed - a mention, the call that receives it as an argument, an operation - so f(async g(), async h()) runs both beside each other; it cannot be cancelled. async(handle) gives a group-bound handle with join and cancel, async(deferred) one that runs on start(), async(detached) a free handle that may outlive its group (it replaces spawn, which stays accepted until the build). Cancelling is .cancel() on a handle only; task::cancel is gone. A catch after an async expression is the last handler before the receiver, for every form. Every task is a child of a group with one capture, failure and memory rule. Fan-out over a collection is par_iter, whose lambdas may now pause, under the same rules (NK2104, kept failures, limit: replaces workers:); the async loop is removed, and async inside a loop body is NK2120. Codes NK2119-NK2122 added, NK2113, NK2114, NK2116, NK2117 retired. Option names are placeholders until the naming round.

[0.0.1304] — 2026-10-30

The grammar engine is winnow-grammar 3b2dd3d: reading from a Read (its ADR 27)

[0.0.1303] — 2026-10-30

async replaces overlap and task::scope (ADR-async-1, #983). The specification now writes structured concurrency with one keyword and no await. async let x = e starts a child that runs beside the code after it and is waited for before the first statement naming x; async let xs = for … { … } is a group whose value is the list in input order; async e, async fn { … } and async for start children without a value; async { … } is a group boundary. Options limit:, within: and admission:; without limit: an exchangeable admission strategy starts a child only where its memory block and a descriptor fit. Failures: a use-site catch first, then the binding’s fallback catch, else the error leaves from the wait point; every crossing error records started and arrived sites. task::cancel(x); a full memory block arrives as task::OverBudget at every boundary, and only a bug panic cancels siblings (changes ADR-327 D6); the leaving error leads (changes ADR-346 D7). New codes NK2110-NK2118. The compiler is built in #984-#991; until then it still takes overlap.

[0.0.1302] — 2026-10-29

Checker defects (lane C). A text field of a borrowed subject passed to a parameter whose contract keeps nothing of it is lent, not refused NK1131; a parameter that keeps its value still refuses it (#936). A ref T? parameter proved present reads as ref T, and an option bound by reference out of a lent subject is put back into a ref T? slot as Option::as_ref(x) (#898). A tuple literal given to a kept generic parameter is checked part by part, and a lent part is refused NK1102 with the .clone() help (#909). The element, key or value argument of push, insert, contains, remove, get, contains_key and entry gives an untyped integer the receiver’s element type (#917). What sum or product hands back of a list of number literals takes that list’s type, i64 when nothing asks for another (#931).

[0.0.1301] — 2026-10-29

Lowering defects (lane E). A ?? default of split_once/rsplit_once stays a pair of views when every default part is a literal or a place, otherwise both halves become owned Strings (#908). A trim* or trim_*_matches followed by .clone() is a copy, not a view (#915, #910). A String place assigned an either-text gets .into(); the runtime gains From<EitherText> for String and PartialEq<EitherText> for String (#911, part of #925). ?.clone() of a ref Bytes? gives owned bytes, and ref Bytes? is lent as Option<&[u8]> (#913). A match arm whose body is an if lowers as a block, a field read in a nested match subject is copied out, and an else after arms naming every variant of an enum the file declares is not written (ADR-357 D5) (#930, #940, #918). An f"…" argument reading a mut argument of the same call is hoisted (part of #899); its holes read before a ref mut self call are copied first, and a changed binding lowers as *x, parenthesised only as a postfix base (#970, #937).

[0.0.1300] — 2026-10-29

Text tiers: an element reached by index, get or a for binding owns the text changed through it (#967, #981); NK2305 names the list’s place (#971). A text field of a list or map element changed in place through v[i], a let of the element or a for binding, and a text element of a list or map changed in place through a let of xs[i], m[k] or m.get(k) (also before ?? return), now makes that element text of its own instead of lowering a borrowed text the change cannot write to. NK2305’s help for a for mut loop whose list is read in the body names the list’s place (for i in 0..<self.pending.len()), not its root.

[0.0.1299] — 2026-10-29

ADR-error-sets-1 (#625, specified, not built): a code parameter that the function’s body does not keep fails, at each call, only with what that call hands it (for private and pub functions alike), while a kept parameter keeps the union of everything stored in it (ADR-351 D8).

[0.0.1298] — 2026-10-29

A let over a part of a mut parameter, ref mut self or a let mut view is a view while the name is only read, and takes the part out with no copy when the name is kept. Inside a loop, the part is given back before the turn ends. A refusal that comes from the taking names the line that keeps the name (ADR-views-1 D6, Part I 6.5, #979). Specified; not built yet.

[0.0.1297] — 2026-10-29

A lambda handed to a parameter that runs it at most once may give away what it captures; a captured value it hands over is gone after the call (no NK2105 repeat inside such a lambda). std names these parameters with the contract key once (Entry::or_insert_with, Entry::and_modify, task::scope, Locked::access, SharedMut::access; not update). For user code, once-ness is inferred, lowered as impl FnOnce, and never written (ADR-lambdas-1, Part I 5.4 D, #973). Specified; not built yet.

[0.0.1296] — 2026-10-29

An assignment or compound assignment to a let mut view (c = v, c += 1) writes through it, and an entry view of a number, bool or char reads as the value; before, it was NK1105 or rustc’s E0368 (#976). The text an or_insert_with lambda ends in is built as the map’s value type; before, rustc reported mismatched types (#977).

[0.0.1295] — 2026-10-29

let mut x = m.entry(k).or_insert(v) (or or_insert_with) binds the &mut that Rust hands back, and lowers with no mut (#974). A let mut view (an entry’s or_insert, m[k] ?? jump, m.get(k) ?? jump, v[i]) passed to a mut parameter is lent on as &mut *x; before, it was NK1102 or rustc’s E0596 (#975).

[0.0.1294] — 2026-10-29

A part of a let mut view of a place (let mut c = m[k] ?? continue, a for mut element) or of a mut parameter, handed to something that keeps it, is taken out with no copy (std::mem::take, or mem::replace with a stand-in) and must be given back by assignment before the view’s block is left (NK2108, naming the view and its place); the whole view handed over is NK1131 (ADR-views-1, #949).

[0.0.1293] — 2026-10-29

Text a call returned, handed whole to push or insert through an if or match arm beside a literal arm (ps.push(if w == "()" { "_" } else { w })), is moved as its own instead of being kept as a view, which rustc refused (#969).

[0.0.1292] — 2026-10-29

extend of a list literal of text into a Vec[String], HashSet[String] or BTreeSet[String] builds owned text again (rustc E0271 since 0.0.1286); extend of text literals into a list or set of text views is no longer refused with a false NK1102 (since 0.0.1288) (#950, #963).

[0.0.1291] — 2026-10-29

Collection methods (push, insert, contains, get, …) check a composite argument part by part (tuples, lists, options) (#965); a text view fits String, a character fits a byte, and declared types fit each other again, which ends the false NK1102 of 0.0.1289 (#964).

[0.0.1290] — 2026-10-29

for mut x in xs writes through to a list or an array held mut (a view of the element, lowered iter_mut()), and is a let mut of the turn over drain() or a range; mut before one name of a tuple is NK1307 (not yet supported); mut on a map key, a set element or an adapter’s element is NK1306; a read of the list in the body is NK2305 (#951, steps 1-3). extend of a comptime array of text into a String list or set compiles (rustc E0271, #966).

[0.0.1289] — 2026-10-29

push, insert, contains, contains_key, remove, get and entry on lists, sets and maps refuse an element, key or value of the wrong plain type with NK1102 instead of a rustc error (Fixes #964).

[0.0.1288] — 2026-10-29

extend on a list, a HashSet or a BTreeSet refuses elements of the wrong type with NK1102 instead of a rustc error (ADR-collections-1, Fixes #963).

[0.0.1287] — 2026-10-29

let mut e = v[i] and let mut f = s.field (a struct of numbers) are mutable views: reading the list or struct before the view’s last use is NK2305 instead of a rustc error, and a write through f reaches s (Part I 6.8, Fixes #962).

[0.0.1286] — 2026-10-29

BTreeMap::entry, HashSet::extend and BTreeSet::extend exist; Vec::extend takes a list, a set or a sequence; s.extend(xs.clone()) copies the elements, not the list (ADR-collections-1, part of #950).

[0.0.1285] — 2026-10-29

A name bound to a text literal and changed in place (push_str, +=) is declared String, and the set, list of tuples or typed let it goes into holds String, not &'static str (ADR-282 D17, Fixes #961).

[0.0.1284] — 2026-10-29

Array[T, N] and a run ref Array[T] get iter() with the sequence surface of Vec::iter(): zip, map, filter, enumerate and rev follow (ADR-collections-2 D1, Fixes #955).

[0.0.1283] — 2026-10-29

let mut e = v[i] over a mut list binds a view of the element (&mut v[i]), also for a struct of numbers: a write through e reaches the list instead of being lost in a copy (Fixes #959).

[0.0.1282] — 2026-10-29

A for lends its list: changing it in the body (a mutating method, an element assignment, an assignment of the whole list) is NK2305 instead of a rustc error; reading stays allowed (Part I 3.3, Fixes #960).

[0.0.1281] — 2026-10-29

A name bound to text literals, used as a String part of a tuple looked up in a container that holds text of its own (set.remove((1, other))), or kept in a published String field of a struct another file of the package declares, is declared String: the tier pass reads the package’s own published fields (ADR-282 D17, Fixes #956).

[0.0.1280] — 2026-10-29

A null in a tuple of a list literal whose declared element holds an option there takes that option, wherever in the list it stands (Fixes #957).

[0.0.1279] — 2026-10-29

A pull parser over a moved text runs, and a tuple of String takes views, with no ref written (ADR-353 D8, ADR-282 D1-D2, #948, #929)

[0.0.1278] — 2026-10-29

A match arm name bound by reference over a value (an option field of an .iter() loop variable) compares with a view or a value as it should, instead of Rust code rustc refused (Fixes #954).

[0.0.1277] — 2026-10-29

let mut x = m.get(k) ?? <jump> over a mut map or list is a mutable view of the entry, as m[k] is (get_mut), instead of Rust code rustc refused (Fixes #952).

[0.0.1276] — 2026-10-29

A text a live view still reads can be moved into a struct, tuple, list or variant literal (ADR-353 D8-D10, Fixes #942).

[0.0.1275] — 2026-10-29

A constructor that moves its text parameter into a view field lends it; a struct or enum result viewing a local takes the caller’s keep (#945, #942 steps 2 and 5; ADR-353 D8, D10).

[0.0.1274] — 2026-10-29

ADR-353 D8-D10, decided (#942, #945): a text moved into a struct, variant, tuple or list while a view of it lives is kept, not copied.

[0.0.1273] — 2026-10-29

A view made from one field of a struct leaves its other fields free (#944).

[0.0.1272] — 2026-10-29

A text moved into a struct field, a tuple or a list while a view of it is still used is followed there: changing it in place is NK2305 (ADR-353 D3, D4, #942).

[0.0.1271] — 2026-10-29

A list field of text literals, drained and collected where Vec[String] is declared, keeps text of its own (#941).

[0.0.1270] — 2026-10-29

**; A view result of two ref parameters is lowered with their lifetime (#655); ; rs2nika: if let Some(x) = &self.f becomes if self.f != null with x read as self.f; the let stays only where the block writes the field (ADR-382 D7); R48 leaves a non-name pattern with code before it to R23. Not yet: else if let, let-chains and && let in one-line closures stay TODO. Field row of selfhosting-gaps removed, ADR-382 built (#869); A field read through an index or a call is not a path: its NK1102 says so (ADR-382 D4); Messages and Part I name scalar, not char, among the types that copy, hash and order (#874); A value with no text form handed to a print call is NK1201, as in an f-string hole (#873); A view handed to a parameter the callee only reads is lent as it is (#872); A binding in a condition is refused with words that name match (ADR-382 D6) (#878); ; buffers: a clone of a cut of a text copies text, so a String moved into it stays owned (Fixes #881); check, emit: a struct field at a T? position narrows after its null arm (#886); check: a plain value in a tuple literal at a T? part is wrapped (#887); check: a proved name bound whole by a let or assignment that owns it is given up (#888); match: a text literal in a tuple pattern; a literal is the same part (#890, #890); keeps: a part moved out by ?? keeps its parameter, a tuple part as a field (#891); check: a value moved in an arm of an if or match is handed over (NK2105) (#892); check: a proved arm bound at a T? let or assignment is given up (#893); emit: an if/match lent whole with view arms and arms that make a value (#894); to_string() of a String parameter is handed on as the parameter is; check: an owned local in an arm of a lent argument is lent, not moved (#897 step 2); check: a variant’s or struct’s list-of-text part an arm takes is owned; a drained part asks no mut (#905); **

[0.0.1269] — 2026-10-28

A list of text lent whole to a ref Vec[String] parameter keeps text of its own (ADR-282 D25)

[0.0.1268] — 2026-10-28

A grammar takes values: rule parameters and options, bounds and guards that are expressions, count(p) an i64 (ADR-grammar-1 D2-D5, #932)

[0.0.1267] — 2026-10-28

A text field taken whole from a struct a call made is moved, not viewed; comptime’s tree work in Nikaia (#125)

[0.0.1266] — 2026-10-28

A grammar action stands after -> again; a brace after a pattern is a bound (ADR-grammar-1 D1, #932)

[0.0.1265] — 2026-10-28

A view of a list in a field is a slice, as a parameter’s is (#125)

[0.0.1264] — 2026-10-28

winnow-grammar 9b15c55 and winnow 1.0 (#932)

[0.0.1263] — 2026-10-28

A list, a map and a walk have what converted code calls (#125)

[0.0.1262] — 2026-10-28

A lambda’s parameter takes a tuple apart (ADR-358 D1, D2, D5; #623, the lambda half)

[0.0.1261] — 2026-10-28

A mut parameter is a source of the result’s views; text given to a ref String field is lent (#929, ADR-283 D25, ADR-282 D2)

[0.0.1260] — 2026-10-28

view_changes in Nikaia (ADR-294, #125; ADR-353 D3, D4)

[0.0.1259] — 2026-10-28

An argument made in a call to a function with a keep of its own is the caller’s to keep (ADR-283)

[0.0.1258] — 2026-10-28

A keep for a struct of views a call makes, across the files of a package; text of either kind looks a key up (ADR-283, ADR-282 D14)

[0.0.1257] — 2026-10-28

std::regex: run-time patterns without positions; a match over error names a type by its name (#673, ADR-regex-1, ADR-357)

[0.0.1256] — 2026-10-28

a catch block that begins with an arm is a match over error (ADR-catch-1)

[0.0.1255] — 2026-10-28

A file’s canonical name and a path’s prefix in std::fs

[0.0.1254] — 2026-10-28

std::regex typed by its engine; a function specialised per build-time value: comptime?, @unroll, decided branches (ADR-regex-1, ADR-specialization-1, #673, #906)

[0.0.1253] — 2026-10-28

Node ids from a pass of their own; owned parts and parameters; ?. on a file’s name

[0.0.1252] — 2026-10-28

std::regex decided: RE2 dialect, known patterns compiled as source, run-time patterns on Rust’s automata crates (ADR-regex-1, #673)

[0.0.1251] — 2026-10-28

A tuple variant’s value narrows after its null arm (#902)

In a match, R::V(name) after R::V(null) binds name as the T, as a tuple position and a struct field do (ADR-352 D1): the earlier arm is the same variant, unguarded, and at each other value takes all or says the same. A variant or a literal at such a value is matched against the T. A pattern with .. among its values is not narrowed yet.

[0.0.1250] — 2026-10-27

A cut of opt ?? "" is text too (#901)

(line.strip_prefix("for ") ?? "").split_once(" in") ?? ("", "") gives two texts like a cut of a named text: a .clone() of a part keeps no view of line, so line stays movable (the converter’s for X in line, R217).

[0.0.1249] — 2026-10-27

A part of split_once that is cloned keeps no view of the text it was cut from (#901)

The two parts of a split_once/rsplit_once of a text (also with ?? ("", "")) are text: a .clone() of one keeps no view of the text cut, so that text is not parked in the frame’s keep and a later move of it (out.push(line)) compiles.

[0.0.1248] — 2026-10-27

replace hands back text of its own

The result of replace and replacen is text of its own, not a view of the text they are called on, so s = out.replace(a, b) no longer puts out into the frame’s keep, and a pattern built in a loop and handed to replace stays an owned String (found in the converter; &&String was no pattern, and a kept buffer could not be pushed to).

[0.0.1247] — 2026-10-27

A value moved in an arm is handed over

A value moved in an arm of an if or match is handed over: reading it after the statement is NK2105, as after let s = t (#892).

[0.0.1246] — 2026-10-27

A text literal in a tuple pattern, and a literal is the same part

A text literal at a String or String? position of a tuple pattern is matched against a view of that position, so match (a, b) { ("x", s) => .., .. } compiles (#890). A literal at a tuple position counts as the same part for narrowing (ADR-352 D1): (1, s) after (1, null) binds s as the T, and a different literal does not.

[0.0.1245] — 2026-10-27

A proved name bound whole by a let is the value itself

A name proved not null and bound whole by a let, or assigned to a place that owns it, is the value itself, so let s: String = if b != null { b } else { "none" } compiles (#888).

[0.0.1244] — 2026-10-27

?? on a part of a lent tuple

p.0 ?? ".." on a part of a lent tuple parameter no longer fails in rustc: a part moved out by ?? keeps its parameter, and a tuple part is treated as a field (#891).

[0.0.1243] — 2026-10-27

A struct field narrows after its null arm

In a match, F { name, .. } after F { name: null, .. } binds name as the T, as a tuple position does (ADR-352 D1, #886); the fields of a plain struct pattern are now typed (they were unknown).

[0.0.1242] — 2026-10-27

A plain value in a tuple at a T? part is wrapped

A plain value in a tuple literal at a T? part (an argument, a let, a return, a nested tuple) is wrapped as Some(..), as it is when passed to a T? parameter: let p: (i64?, String) = (1, "a") compiles (#887).

[0.0.1241] — 2026-10-27

A catch on a call used as a statement drops the call’s value

save(file) catch { log("not saved") } no longer needs its block to hand back what save returns: a catch whose value is not used (a statement, a loop body’s tail, the tail of an arm whose value is dropped) has a block that runs for its effect, and the call’s value is dropped (Part I 7.1, #885).

[0.0.1240] — 2026-10-27

A match guard narrows, and so does a tuple position

What a match guard proves holds in that arm’s body, as after an if (ADR-382 D2): in (x, _) if x != null => x, x is a String, && in the guard carries its proof, and no other arm sees it. In a match over a tuple, a bare name at a T? position binds the T once an unguarded arm before it has null there and covers the other positions (ADR-352 D1): after (null, null) and (null, y), (x, _) binds x as the T, and y is set too. A variant or literal at a T? position is matched against the T. A name proved not null that a function hands back whole, by return or as its tail, is now the value itself, also for text; it was lowered as a reference, which rustc refused (#879).

[0.0.1239] — 2026-10-27

A catch whose block hands back null gives T?

let port = read_port() catch { null } is an i64?, the way a ?? fallback or an if arm with null widens. A value that is already T? stays T?. A block that hands back another type is still NK1103, and its help now says “Hand back an i64, or null to make the value an i64?” instead of telling you to change a declaration a catch doesn’t have. A catch may now also stand in the head of a match, if, while or for: match parse(line) catch { null } { v => use(v), null => {} } (#884).

[0.0.1238] — 2026-10-27

A String local assigned whole from another kept String local compiles (#883). When the result was the trimmed view of the source local, the keeps walk put the assigned local in the keep a second time, and rustc refused the lowered code; it is now kept once (tools/buffers.nika). Test string_reassigned_trim_result.

[0.0.1237] — 2026-10-27

A Bytes has iter, starts_with and ends_with (#877). A Bytes, s.bytes among them, has iter() (a Seq[ref(self) u8], as Vec::iter), starts_with(prefix) and ends_with(suffix) over a ref Bytes; they were NK1171 before. Test bytes_iterate_and_compare_their_ends.

[0.0.1236] — 2026-10-27

A String parameter read through find stays lent (#882). The keeps walk (tools/keeps.nika) looked a String receiver’s method up as String::find; the ledger names text methods str::, so it fell back to every find, Seq::find among them, which takes its receiver. Passing a text to a function that only searches it no longer hands the caller’s text over (NK2105), a for element passes (NK1102), and the slice after it lowers to &str. A String? read through ?? or match is still kept (#882 stays open). Tests a_text_parameter_read_through_find_stays_lent, a_for_element_is_lent_to_a_text_parameter_read_through_find.

[0.0.1235] — 2026-10-27

find, rfind, replace and replacen take a scalar needle (#877, Part I 2.6). s.find(',') was NK1102 while contains and split took a character; a needle is now a text or one scalar for all of them. Test a_text_search_takes_a_scalar_needle.

[0.0.1234] — 2026-10-27

Two ref Array[T] compare with == where their elements do (#877, Part I 4.7). A view of a run was refused with NK1188 whatever it held; it now compares as the run does. A lent pointer of the C boundary still does not. Test two_byte_views_compare_by_their_bytes.

[0.0.1233] — 2026-10-27

A clone of a cut of a text copies text (#881). A String local that starts as "" and is moved into a variable that later takes x[a..].clone() of itself was lowered as a view, and rustc refused the move; it is lowered as a move. Test string_empty_then_moved.

[0.0.1232] — 2026-10-27

A clone of a trimmed x ?? "" copies text too (#880). copies_text (tools/buffers.nika) now also counts (x ?? "").trim().clone() and its kin as a copy, the form rs2nika’s R92 writes for a map’s entry, so a String moved beside it stays owned. Test string_move_not_view.

[0.0.1231] — 2026-10-27

A String moved after a clone of x.strip_prefix(..) ?? "" stays owned (#880). The lowering’s text-copy check (copies_text, tools/buffers.nika) did not count (x ?? "").clone() as a copy, so the moved String kept the origins of the text it was cut from and was lowered as a view, which rustc refused. A clone of a ?? b now copies when either side does. Test string_move_not_view.

[0.0.1230] — 2026-10-27

A binding in a condition is refused with words that name match (#878, ADR-382 D6). if let v = x, while let v = x and if e is Leaf(v) now read “A condition can’t bind a name”, with the help match x { ... } or x != null, instead of the reserved-word message (let_) and “Expected {”. Tests an_if_let_binding_in_a_condition_is_refused, an_is_binding_in_a_condition_is_refused, a_while_let_binding_is_refused.

[0.0.1229] — 2026-10-27

A comptime number below i64::MIN is NK1116; the compiler builds no i128 (#871, ADR-285 D19). comptime BIG = -9223372036854775807 - 5 was carried as an i128 and reached rustc; it is refused at the declaration, as a value over u64::MAX already was. An integer literal is a magnitude and a sign (int_literal(value, negative)), and int_value is gone, so the self-hosting gap row “i128 has no twin type” goes. Test a_comptime_number_below_i64_min_is_refused.

[0.0.1228] — 2026-10-27

A T? handed to a print call is NK1102 (Part I 2.3). println(x) with x: String? reached rustc and failed there with an Option display error; it is now refused as the f-string hole already was (f"{x}"): what an absent value reads as is the program’s to say, println(x ?? "none"). Built in tools/check_refusals.nika (a_maybe_printed), called from the print branch of tools/check_arguments.nika; test a_maybe_handed_to_println_is_refused. The syntax tour shows it.

[0.0.1227] — 2026-10-27

ADR-382 replaces ADR-350: a null test narrows a path of fields too. Decided, not yet built (Part I 2.3):

[0.0.1226] — 2026-10-27

lending fixes, views of locals, one statement a line, operator types, seeds tables in Nikaia (#655, #665, #848, #864-#867)

[0.0.1225] — 2026-10-26

owned literals, NK1301, emit a_stmt in Nikaia (#856, #853, #857)

[0.0.1224] — 2026-10-26

Part III 17.3: json::parse and json::Value (#604)

[0.0.1223] — 2026-10-26

integer as scalar, match arm naming a struct type (#604)

[0.0.1222] — 2026-10-25

process::run within, option defaults, map get pair view (#846, #847, #845, #675)

[0.0.1221] — 2026-10-25

emit: a kept Bytes follows the receiver’s method; grammar patterns in Nikaia (#756, #842, #583)

[0.0.1220] — 2026-10-25

EitherText for an assigned if/match, the emitter’s item writer in Nikaia (#841, E11-1)

[0.0.1219] — 2026-10-25

let lends a field read through a ref, a let-bound function lends its parameters, NK1131 alone (#744, #839, #582, #747)

[0.0.1218] — 2026-10-25

compiler bugs and –cpu with RUSTFLAGS (#792, #777, #773, #760, #787, #757, #829, #762, #764, #765, #758, #830, #831, #832, #776, #799, #834, #779, #790, #835, #836, #837, #753, #780, #838)

[0.0.1217] — 2026-10-25

Some(x)/None over T? refused; list elements past a jump are views; T? slots widen (ADR-352 D5, #803, #819, #824, #827, #828)

[0.0.1216] — 2026-10-24

String::quoted(); panic(..) works in tools; methods are matched by receiver type in keeps (#804, #805, #791)

[0.0.1215] — 2026-10-24

Check: the kept-function call, the findings order and the unsigned-slot verdicts are Nikaia (C7-a to C7-c, #125)

[0.0.1214] — 2026-10-24

Check: the walk’s settling and the unrolling report are Nikaia (U-d6, #125)

[0.0.1213] — 2026-10-24

Self-hosting: the checker’s program walk is Nikaia (U-d5)

[0.0.1212] — 2026-10-24

The lowering cache is keyed by the compiler’s source fingerprint, not by the package version (#801)

[0.0.1211] — 2026-10-24

self-hosting: the checker’s function walk, grammar action walk and growing-text scan are Nikaia (U-d4)

[0.0.1210] — 2026-10-24

Proofs resting on an assume are marked in the reports (ADR-381 D6)

[0.0.1209] — 2026-10-24

binary_search requires a sorted list; sort ensures it (ADR-381 D5)

[0.0.1208] — 2026-10-24

Named properties: a pure bool call is proved by name (ADR-381)

[0.0.1207] — 2026-10-24

lower-std: one generated Rust module per tools file

[0.0.1206] — 2026-10-24

Self-hosting: the checker’s defaults, item constants and folds are Nikaia (U-d3)

[0.0.1205] — 2026-10-24

tools: attributes check moved (U-d2); lower-std caches each file of a package (#667, #125)

[0.0.1204] — 2026-10-24

nikaia fmt formats a package, --check reports

[0.0.1203] — 2026-10-24

nikaia fmt: the printer and its guard; a trailing , after parameters and arguments

[0.0.1202] — 2026-10-24

The repository’s unsafe crates ship their reviewed ledgers

[0.0.1201] — 2026-10-24

use names a Rust crate; a crate may ship its reviewed ledger (ADR-290 D22)

[0.0.1200] — 2026-10-24

nikaia run applies the user’s linker and flags again

[0.0.1199] — 2026-10-24

A method argument that lends the receiver’s local to a mut parameter is bound before the call

[0.0.1198] — 2026-10-24

An argument that reuses a mut argument’s local is bound before the call

[0.0.1197] — 2026-10-24

The prover follows the length of a mut list

[0.0.1196] — 2026-10-24

check: --asserts lists an unproved claim as refused (ADR-380 D1)

[0.0.1195] — 2026-10-24

Unproved asserts refused: NK1300, unproved-claims (ADR-380)

[0.0.1194] — 2026-10-22

An unconditional write before the ifs that cannot tell (built)

[0.0.1193] — 2026-10-22

A field through one element is a name of the walk

[0.0.1192] — 2026-10-22

An unconditional write before the ifs that cannot tell (decided)

[0.0.1191] — 2026-10-22

The emitted code is unsafe only where a switch or the program says so

[0.0.1190] — 2026-10-22

A proved +, -, * tells the optimiser it does not overflow

[0.0.1189] — 2026-10-22

x += d and x ?? d keep what bounds them

[0.0.1188] — 2026-10-22

simd::splat(';') into byte lanes is a byte

[0.0.1187] — 2026-10-22

Mask.first() of byte lanes is one pmovmskb

[0.0.1186] — 2026-10-21

low_bits cuts by a count up to 255 with one bzhi

[0.0.1185] — 2026-10-21

find_all takes how it goes from one hit to the next

[0.0.1184] — 2026-10-21

find_all looks for the next hit right after the last one

[0.0.1183] — 2026-10-21

The prover reads if expressions and comptime conditions

[0.0.1182] — 2026-10-21

target::has, built

[0.0.1181] — 2026-10-21

target::has, not cpu::has

[0.0.1180] — 2026-10-21

target::has: a program asks its CPU level (decided)

[0.0.1179] — 2026-10-21

hash::crc32c(crc, word)

[0.0.1178] — 2026-10-21

find_all’s offset never passes the end of the run

[0.0.1177] — 2026-10-21

a proved shift is written as the shift that may assume its count

[0.0.1176] — 2026-10-21

find_all cuts what it has not covered from the front

[0.0.1175] — 2026-10-21

a map compares text keys by words

[0.0.1174] — 2026-10-20

a proved index is a place without its check

[0.0.1173] — 2026-10-20

x.low_bits(n) is one bzhi where the build has BMI2

[0.0.1172] — 2026-10-20

the grammar runtime’s advance always inlined

[0.0.1171] — 2026-10-20

x.low_bits(n)

[0.0.1170] — 2026-10-20

a method’s precondition is proved at its call

[0.0.1169] — 2026-10-20

find_all over a written set fixed at compile time; a believed assume’s arithmetic exact; cuts compared in place; low_bits

[0.0.1168] — 2026-10-20

a field of every element of a list as one summary (decided)

[0.0.1167] — 2026-10-20

facts at a call; signed shifts bounded; rotate_left and rotate_right

[0.0.1166] — 2026-10-20

vectors a program holds, built: simd::Lanes, simd::Fixed, simd::Mask

[0.0.1165] — 2026-10-20

a fold’s hot loop inlined alike at any unit count; text used as a map key checked on a miss

[0.0.1164] — 2026-10-20

vectors: simd::Lanes and simd::Fixed (provisional)

[0.0.1163] — 2026-10-20

a type’s rule a method cannot break is proved; the bounds walk bounds shifts, bounded sums and break guards

[0.0.1162] — 2026-10-20

a checked assume is no Rust unsafe

[0.0.1161] — 2026-10-20

prove-through-loops; a ref argument keeps its length

[0.0.1160] — 2026-10-20

unsafe { assume(claim) }

[0.0.1159] — 2026-10-20

A grammar’s par_fold names winnow_grammar’s ParseContext in full

[0.0.1158] — 2026-10-20

unsafe { assume(claim) } (ADR-376, decided); a type’s rule another package can break is no fact (ADR-369, built)

[0.0.1157] — 2026-10-20

A search’s positions and a shift’s count in the bounds walk (ADR-375 D5, ADR-306 D18, built)

[0.0.1156] — 2026-10-20

A type’s rule is a fact only where nothing can break it unchecked (ADR-369 D2, D3)

[0.0.1155] — 2026-10-20

A type’s rule: an assert after a struct’s fields (ADR-369 D1-D3, built in part)

[0.0.1154] — 2026-10-20

@inline is built (ADR-370)

[0.0.1153] — 2026-10-20

A shift’s count is a goal of the walk (ADR-306 D18, decided)

[0.0.1152] — 2026-10-20

A position find_all yields is a fact for the proof (ADR-375 D5, decided)

[0.0.1151] — 2026-10-20

prove-through-loops: the compiler’s own checks asked through a loop (ADR-361 D10, decided)

[0.0.1150] — 2026-10-20

The bounds walk: a u64 is not bounded by i64::MAX; facts survive a bound as wide as i64

[0.0.1149] — 2026-10-20

A number read through brackets past a jump is the number in any position

[0.0.1148] — 2026-10-20

A grammar’s par_fold claims its pieces while the work runs (ADR-372 D4, built)

[0.0.1147] — 2026-10-20

Bytes::par_pieces and par_fold options (ADR-372 D3-D8, built in part)

[0.0.1146] — 2026-10-20

bytes::find_all: the next hit costs a tzcnt and a cleared bit, one length test a block

[0.0.1145] — 2026-10-20

bytes::ByteSet: a buffer searched for a set of bytes at the build’s width (ADR-375, built)

[0.0.1144] — 2026-10-20

Par::fold, par_fold and a grammar’s fold take an in-place step or merge (ADR-372 D9, built)

[0.0.1143] — 2026-10-20

a ?? whose left is a view and whose fallback owns text is NK1185 again

[0.0.1142] — 2026-10-20

Par::fold’s step and merge return the accumulator or change it in place (ADR-372 D9, decided)

[0.0.1141] — 2026-10-20

task::cores(), Par::fold(init, step, merge; order:, workers:), par_iter(piece:) (built, ADR-372 D1, D2, D4-D7)

[0.0.1140] — 2026-10-20

A ref Bytes the body keeps takes the Bytes with its owner (built, ADR-373 D1, D4)

[0.0.1139] — 2026-10-20

A Bytes carries what owns its memory; Bytes(v) copies nothing; a mapping is a Bytes (built, ADR-373 D1-D3)

[0.0.1138] — 2026-10-20

spec: a ByteSet holds any byte; a set proved empty is refused, one empty at run time finds nothing (ADR-375 D2)

[0.0.1137] — 2026-10-20

spec: a Bytes searches for a set of ASCII bytes: AsciiSet, find, find_all, split; a sequence never loses its producer (ADR-375)

[0.0.1136] — 2026-10-20

–cpu wins over the user’s RUSTFLAGS; a ?? fallback name of text and a text literal in a tuple of text lower (ADR-002 D6)

[0.0.1135] — 2026-10-20

A T? beside an unsettled number is refused; a ref String name as a text view’s ?? fallback lowers (built)

[0.0.1134] — 2026-10-20

**codegen-units in [build.]; --cpu=LEVEL on build, run and test (built)**

[0.0.1133] — 2026-10-20

**spec: codegen-units is a key of [build.], the CPU level is the build command's --cpu; no word is read past the end (ADR-002 D6, ADR-320 D13, #692)**

[0.0.1132] — 2026-10-20

process::run sets the child’s environment: env, env_remove, env_clear; the program is found on the caller’s PATH (built)

[0.0.1131] — 2026-10-20

bits_as_ reads the same bits with the other sign; u8 gets every fixed-width name; S {} is a struct literal after throw (built)

[0.0.1130] — 2026-10-20

A map takes its types from what is written into it (built)

[0.0.1129] — 2026-10-20

spec: process::run’s env, env_remove and env_clear; the program is found on the caller’s PATH (ADR-243 D5-D8, #691)

[0.0.1128] — 2026-10-20

spec: a text position is a text::Pos; find gives a Match, a text is cut by before/after/between (ADR-374, #743)

[0.0.1127] — 2026-10-20

A map takes its types from what is written into it

[0.0.1126] — 2026-10-20

A lambda’s text item compares with text; a keep through a chain of calls; a literal beside a lent field

[0.0.1125] — 2026-10-20

mut p: ref T is mut p: T; a list into Vec[T?] wraps its elements

[0.0.1124] — 2026-10-20

A view read through ?? is watched; a ref Bytes is a run of bytes

[0.0.1123] — 2026-10-20

spec: the same bits with the other sign are bits_as_; truncating_ only narrows (ADR-285 D42, #742)

[0.0.1122] — 2026-10-20

An element or field read through a borrowed parameter is lent into the result; a local kept in a longer-lived list is handed over

[0.0.1121] — 2026-10-20

A String? parameter handed back is lent; mixed text into a published position is refused; an unlinked list keeps one type

[0.0.1120] — 2026-10-20

Lowering: text tiers, the solver and result sources redo only what changed (#727)

[0.0.1119] — 2026-10-20

A float beyond f32 is NK1116 in every f32 place; a mixed String? result lowers

[0.0.1118] — 2026-10-20

f32 is a whole type: as f32, comparisons and arithmetic

[0.0.1117] — 2026-10-20

A function’s contract is found as a view; owned fields keep no view (#727, #735)

[0.0.1116] — 2026-10-20

A result’s sources are what it borrows from; a field behind a view is lent (#735)

[0.0.1115] — 2026-10-20

A mut argument says mut, and a float literal is an f32 where one is asked

[0.0.1114] — 2026-10-20

Text orders against text; the prover answers repeats without normalising (#733, #727)

[0.0.1113] — 2026-10-20

Five more programs the checker accepted now compile or are refused

[0.0.1112] — 2026-10-20

Lowering: the solver and hot lookups stop copying (#727)

[0.0.1111] — 2026-10-20

Four programs the checker accepted now compile, and a fifth is refused

[0.0.1110] — 2026-10-20

Every program a checker test accepts is checked by rustc

[0.0.1109] — 2026-10-20

Lowering: the solver copies less and the ledger pass skips report-only walks (#727)

[0.0.1108] — 2026-10-20

A pattern of another type is a refusal; a fold’s value start and a text match compile

[0.0.1107] — 2026-10-20

Lowering: the solver, proofs and diagnostics stop repeating work (#727)

[0.0.1106] — 2026-10-20

Four more checker gaps from the rustc sweep are refusals

[0.0.1105] — 2026-10-19

Four checker gaps from the rustc sweep are refusals

[0.0.1104] — 2026-10-19

An impl narrower than its trait’s declaration is the type’s own method and a bridge

[0.0.1103] — 2026-10-19

A binding may shadow a comptime; a part of a mut parameter is handed on in place

[0.0.1102] — 2026-10-19

Lowering runs each analysis once (#727)

[0.0.1101] — 2026-10-19

spec: u8 has every wrapping, saturating, checked and truncating name (ADR-285 D41, #729)

[0.0.1100] — 2026-10-19

A word is never a view inside a type either; TaskHandle and two-view function types lower

[0.0.1099] — 2026-10-19

Lowering looks methods up by name (#727)

[0.0.1098] — 2026-10-19

An integer literal is no float; a lambda takes what it names

[0.0.1097] — 2026-10-19

spec: a Bytes carries what owns its memory; a ref Bytes only read takes any run of bytes (ADR-373, #718)

[0.0.1096] — 2026-10-19

view changes: methods are looked up by name, not by a scan of every function

[0.0.1095] — 2026-10-19

a caught method call adds nothing to the caller’s error channel (#725)

[0.0.1094] — 2026-10-19

check: an open number is handed on once; a + a + … + a checks in linear time

[0.0.1093] — 2026-10-19

The user cache is bounded by size, least recently used first (#724)

[0.0.1092] — 2026-10-19

a parameter handed to a position the callee keeps is kept, even where the callee’s result could point into it (#720)

[0.0.1091] — 2026-10-19

text tiers: a tie is held once; a place that reads itself no longer doubles its ties (#702)

[0.0.1090] — 2026-10-19

A program’s std no longer compiles the compiler (#723)

[0.0.1089] — 2026-10-19

a catch also catches a failing call inside the expression it guards (#721)

[0.0.1088] — 2026-10-19

A path of fields is a name of the bounds walk and the prover (ADR-306 D17, #711)

[0.0.1087] — 2026-10-19

a name read later, or a parameter arm, goes into a keeping parameter as a view (#716)

[0.0.1086] — 2026-10-19

fs::map maps bytes; m.text() checks the mapping once (ADR-016 D5, #710)

[0.0.1085] — 2026-10-19

spec: parallel folding without a grammar (ADR-372, #717)

[0.0.1084] — 2026-10-19

an arm that is a name still read later is a view of it (#712, #708)

[0.0.1083] — 2026-10-19

Vector lanes, crc32c and prefetch are portable std; an architecture’s instruction is a safe function (ADR-371)

[0.0.1082] — 2026-10-19

@inline asks, the compiler adds the hint where it knows better (ADR-370)

[0.0.1081] — 2026-10-19

a view of a parameter kept in another parameter’s place points into the caller’s argument (ADR-283 D27, #706)

[0.0.1080] — 2026-10-19

A Bytes reads a word: u32_le, u64_le, u32_be, u64_be (#709)

[0.0.1079] — 2026-10-19

A type’s invariant: an assert after a struct’s fields, and found where none is written (ADR-369)

[0.0.1078] — 2026-10-19

A field is a fact of the prover while nothing can change it (ADR-306 D17, #384)

[0.0.1077] — 2026-10-19

programs without ref no longer refused for the text tiers’ choice in three shapes (#708)

[0.0.1076] — 2026-10-19

A field is a fact of the prover while nothing can change it (ADR-306 D17, #384)

[0.0.1075] — 2026-10-19

a view of a parameter kept in another parameter’s place points into the caller’s argument (ADR-283 D27, #706)

[0.0.1074] — 2026-10-19

fs::map hands back bytes; m.text() checks them once (ADR-016 D5)

[0.0.1073] — 2026-10-19

A Bytes reads a word in the order its name says (ADR-320 D12)

[0.0.1072] — 2026-10-19

Bytes::find searches with memchr; range indexes inline (#701)

[0.0.1071] — 2026-10-19

drain takes its container away (#707)

[0.0.1070] — 2026-10-19

a chain of map reads past a jump copies numbers out; a method’s result ties the text handed to it (#694, #702)

[0.0.1069] — 2026-10-19

A Bytes read and i + k under a bound are proved (#698, #699)

[0.0.1068] — 2026-10-19

A struct viewing a kept String parameter, stored where it outlives it, owns its text (#702)

[0.0.1067] — 2026-10-19

A struct of views read through a local does not keep the argument it views (#697)

[0.0.1066] — 2026-10-19

A struct handed back through a let, and a parameter handed to a reader, lend their String parameter (#678)

[0.0.1065] — 2026-10-19

split_once parts past ?? continue are views (#687)

[0.0.1064] — 2026-10-19

An idle task pool at yes sleeps (#693)

[0.0.1063] — 2026-10-19

owned text assigned whole is moved; a map read past a jump is a view (#686, #694)

[0.0.1062] — 2026-10-19

four defects that kept develop red, from text tiers and and_modify (#678, #680, #684)

[0.0.1061] — 2026-10-19

process::run’s max_rss_kib on Linux counts the caller’s high-water mark (ADR-368 D3)

[0.0.1060] — 2026-10-19

A map’s text keys and values are views where only views go in (#659)

[0.0.1059] — 2026-10-19

Four lowering defects found by the 1BRC programs (#683, #684, #685, #676)

[0.0.1058] — 2026-10-19

a program reads the clock, and what a program it ran cost (ADR-368)

[0.0.1057] — 2026-10-19

A String parameter changed through a name bound to it owns its text (#681)

[0.0.1056] — 2026-10-19

A variant’s String payload is decided by what flows into it (#680)

[0.0.1055] — 2026-10-19

a program can measure time and what another program used (ADR-368)

[0.0.1054] — 2026-10-19

A String parameter handed back unchanged is lent (#678)

[0.0.1053] — 2026-10-19

The specification’s examples write String where ref said nothing more (#654)

[0.0.1052] — 2026-10-19

ref String outside the C boundary is a warning (NK1261, #654)

[0.0.1051] — 2026-10-19

a fold’s counters proved from the input’s length

[0.0.1050] — 2026-10-19

A list repeat keeps what it repeats (#654)

[0.0.1049] — 2026-10-19

a fold’s counters proved from the input’s length (ADR-367)

[0.0.1048] — 2026-10-19

A shift written only in literals is folded exactly (#661)

[0.0.1047] — 2026-10-19

A String parameter or option the body only reads is handed on as a view (#654)

[0.0.1046] — 2026-10-19

A shift written only in literals is folded exactly (#661)

[0.0.1045] — 2026-10-19

a grammar run takes in a small rule it calls

[0.0.1044] — 2026-10-19

std’s map hash spreads integer keys

[0.0.1043] — 2026-10-19

A cached build says its warnings again

[0.0.1042] — 2026-10-19

Static views of text: a published list and build-time values own their text; winnow-grammar 3c50bc4 (#659, #453)

[0.0.1041] — 2026-10-18

Static views of text: a parameter only read is &str again (ADR-282 D5, D29, #659)

[0.0.1040] — 2026-10-18

A map is hashbrown’s table (#420, #434)

[0.0.1039] — 2026-10-18

rfind cuts its text (ADR-320 D5, #453)

[0.0.1038] — 2026-10-18

lower-std: five times faster, and seconds when nothing changed (#667)

[0.0.1037] — 2026-10-18

A text is cut by a position it handed out (ADR-320 D5, #453)

[0.0.1036] — 2026-10-18

A position only literals flow into is a view of static text (#659)

[0.0.1035] — 2026-10-18

a ‘…’ is typed by its uses, also through a let (ADR-320 D8, #453)

[0.0.1034] — 2026-10-18

s.normalized(text::Form::Nfc); a ‘…’ of several scalars (ADR-320 D8, D10, #453)

[0.0.1033] — 2026-10-18

s.scalars and s.graphemes walk a text (ADR-320 D3, D4, D9, #453)

[0.0.1032] — 2026-10-18

s.bytes kept beyond the text is copied wherever it goes; Bytes has clone (ADR-320 D11, #453)

[0.0.1031] — 2026-10-18

s.bytes is a Bytes, borrowed (ADR-320 D11, #453)

[0.0.1030] — 2026-10-18

s.bytes is a Bytes; a normal form is an argument; any over bytes

[0.0.1029] — 2026-10-18

A grammar reads Bytes (ADR-366)

[0.0.1028] — 2026-10-18

A String result that hands back a view of ref self is a view result (#663)

[0.0.1027] — 2026-10-18

A grammar reads Bytes (ADR-366); NK1116 once, a caret under a number, a tuple let over a lent item (#662, #602, #626)

[0.0.1026] — 2026-10-18

An option is lent as a parameter is

[0.0.1025] — 2026-10-18

An overflow rustc would refuse is NK1116 first: shifts of open numbers, a followed let mut, <<= and -x

[0.0.1024] — 2026-10-18

A position only literals flow into holds a view of static text

[0.0.1023] — 2026-10-18

A tail if/match with a return end puts only its values into Some

[0.0.1022] — 2026-10-18

A String that only views reach is a view at grammar rules, parsed parameters, std views and map keys

[0.0.1021] — 2026-10-18

A tail if/match takes the wanted T?; a shift past the width is NK1116

[0.0.1020] — 2026-10-18

A throw of a call, a map’s read past a jump, a variant fallback and a kept pausing function lower as written

[0.0.1019] — 2026-10-18

An assert on an option is a precondition

[0.0.1018] — 2026-10-18

A match on a lent name, and an indexed write on an Array, build

[0.0.1017] — 2026-10-18

split_once and rsplit_once on String

[0.0.1016] — 2026-10-18

nikaia run compiles a program that needs only std without Cargo

[0.0.1015] — 2026-10-18

(A, B)? is a type

[0.0.1014] — 2026-10-18

a build without –target is for the machine it runs on; nikaia run shows no Cargo progress

[0.0.1013] — 2026-10-18

Part I 4.3 says what classes bundle, what goes wrong with them, and what Nikaia writes instead

[0.0.1012] — 2026-10-18

standard input and files are untrusted unless marked trusted: true (ADR-365, #649)

[0.0.1011] — 2026-10-18

std: list changes in place, clamp, min/max by key, flat_map and ordered-set walks

[0.0.1010] — 2026-10-18

a tuple may be nullable: (A, B)? is written (ADR-364, #648)

[0.0.1009] — 2026-10-17

The prover follows a let mut through each assignment and branch (ADR-361 D9, #642)

[0.0.1008] — 2026-10-17

The toolchain’s Nikaia package is lowered at user_parallelism = yes (ADR-363 D1, #646)

[0.0.1007] — 2026-10-17

[v; n] is n copies of a value that copies (ADR-362, #643)

[0.0.1006] — 2026-10-17

The toolchain’s own Nikaia package is lowered at user_parallelism = yes (ADR-363, #646)

[0.0.1005] — 2026-10-17

[v; n] is a list literal of n copies of a value that copies; a list of one range is warned (ADR-362, #643, #644)

[0.0.1004] — 2026-10-17

nikaia.proofs keeps each loop invariant (ADR-361 D4, #640)

[0.0.1003] — 2026-10-17

A claim carried back to main’s entry that reads no parameter is not a precondition (ADR-360 D3)

[0.0.1002] — 2026-10-17

The prover reads a claim past a for over a range through its invariant (ADR-361 D1, #640)

[0.0.1001] — 2026-10-17

The prover follows a let mut through each assignment (ADR-361 D9, #642)

[0.0.1000] — 2026-10-17

The prover reads through a while loop: closed forms and intervals find an invariant, three queries check it (ADR-361 D1-D5, D7, #640)

[0.0.999] — 2026-10-17

A build-time value that runs code is compiled, never interpreted (ADR-321 D1, #636)

[0.0.998] — 2026-10-17

a proposer is a child process, one JSON line per problem each way (ADR-361 D6)

[0.0.997] — 2026-10-17

inside its package a callee’s body answers a claim left open; a quotient by a known number is a term; the –asserts report in three groups (#632)

[0.0.996] — 2026-10-17

a comptime whose compiled run stops is NK1244 (#637)

[0.0.995] — 2026-10-17

NK1201 refuses a range, a tuple, and a struct or enum with no text form in an f-string hole (#638)

[0.0.994] — 2026-10-17

the prover reads through a loop: Horn clauses, a portfolio of strategies, every invariant checked, proposers only checked (ADR-361)

[0.0.993] — 2026-10-17

a walk of a list of number literals hands its lambdas numbers (#594)

[0.0.992] — 2026-10-17

A struct, an enum, a range or a tuple in an f-string hole is refused (NK1201)

[0.0.991] — 2026-10-17

A method named by its path where a function value is kept runs (#601)

[0.0.990] — 2026-10-17

the –asserts report puts main’s preconditions between the unproved and the proved, in light blue

[0.0.989] — 2026-10-17

the –asserts report lists what is not proved first, in red, and what the build proved last, in green

[0.0.988] — 2026-10-17

ADR-359 D5, D7, D11-D13: a quotient of known numbers, a comptime and a call with known arguments are values to the prover (#632 step 1, #633)

[0.0.987] — 2026-10-17

A subject T? of main may be left out (ADR-360, #635)

[0.0.986] — 2026-10-17

The prover reads an int (#630)

[0.0.985] — 2026-10-17

int: an i64’s values, the representation the compiler’s; Rust’s other widths are refused (ADR-285 D36-D39, #629, #630)

[0.0.984] — 2026-10-17

ADR-360: a subject T? of main may be left out; NK1274 is an option named help (#635)

[0.0.983] — 2026-10-17

main’s parameters are the command line (ADR-360, #635)

[0.0.982] — 2026-10-17

main’s parameters are the command line (ADR-360, #635)

[0.0.981] — 2026-10-17

A named function handed to a parameter the callee keeps runs (#601)

[0.0.980] — 2026-10-17

A call with known arguments that a claim needs is compiled and run while building (ADR-359 D11-D13, #633)

[0.0.979] — 2026-10-17

ref on a word refused at every position (#628)

[0.0.978] — 2026-10-17

Inside its package a callee’s body answers a claim that needs it; a quotient by a known number is a term; across a package, a hint (ADR-359, #632)

[0.0.977] — 2026-10-17

The prover reads a call inside a let’s value by its callee’s ensures (ADR-314 D3, #631)

[0.0.976] — 2026-10-17

int: the values of an i64, its representation the compiler’s choice (#630)

[0.0.975] — 2026-10-17

ref String only at the C boundary; ref on a word refused everywhere (#628)

[0.0.974] — 2026-10-16

a text literal handed to a String option is a String (ADR-282 D4)

[0.0.973] — 2026-10-16

every target Rust knows is a target; Android has short names (ADR-037 D9)

[0.0.972] — 2026-10-15

check: a loop body’s tail if or match, and a discarded choice’s arms, ask no common type (#596)

[0.0.971] — 2026-10-15

a parameter of code called in a throws function hands its failure on; from(p) chains through a parameter handed on and a mut parameter’s place (ADR-351 D3, D4, part of #620)

[0.0.970] — 2026-10-15

a Rust adapter names a tools map’s hasher, and the tools lower the same in any file order (ADR-010 D9, #581 steps 8-10)

[0.0.969] — 2026-10-15

A hand-over under a view is NK2305, a move into a local keeps the buffer, a pub type keeps a place per view field (ADR-353 D3, D6, D7, #614)

[0.0.968] — 2026-10-15

check: what the core crossed back into Rust for, in Nikaia (C-7 U-d1, #125, #558)

[0.0.967] — 2026-10-15

A parameter may take a tuple apart, in a function and a lambda (#623)

[0.0.966] — 2026-10-15

A catch naming every error type is complete; kept code’s set belongs to its storage (#625, #620)

[0.0.965] — 2026-10-14

Lowering defects D2, D4, D9, D19; four tools workarounds removed

[0.0.964] — 2026-10-14

a viewed argument given a new value in a nested block lives in the frame’s keep; a union field takes one lifetime over its group (ADR-353 D2, D3, part of #614)

[0.0.963] — 2026-10-14

a function’s set names the code it was given, from(p) (ADR-351 D4, part of #620)

[0.0.962] — 2026-10-14

A pub type keeps a place per view field (#614)

[0.0.961] — 2026-10-14

Lowering defects D10, D11b, D18, D26, D37, D39, D40

[0.0.960] — 2026-10-14

a map’s read past a jump, bound by a let to a declared owned type, is refused or copied out (ADR-293 D15)

[0.0.959] — 2026-10-14

check: what a test proves not null in Nikaia (C-7 U-d0, #125, #558)

[0.0.958] — 2026-10-14

Handing viewed data to a new owner is NK2305 (#614)

[0.0.957] — 2026-10-14

std ledger: Restart::decide keeps its history

[0.0.956] — 2026-10-14

a function value’s set meets what reaches it: mut names, push, field assignment and with carry the union (ADR-351 D3, part of #620)

[0.0.955] — 2026-10-14

a view’s argument may be given a new value while the view is used; a change in place under a view is NK2305 (ADR-353 D3, D4, part of #614)

[0.0.954] — 2026-10-14

check: the walk’s hub and dispatcher in Nikaia (U-c11, #125, #558)

[0.0.953] — 2026-10-14

a parameter handed to a trait’s method by value is kept

[0.0.952] — 2026-10-14

a struct of views takes one lifetime per view field, and a result’s fields their parameters’ (ADR-353 D1, part of #614)

[0.0.951] — 2026-10-14

lowering defects: with keeps its base, a typed throw into a boxed trait channel, names_read sees statements, a match in a guard names the brackets

[0.0.950] — 2026-10-14

Owned text is lent to strip_prefix and strip_suffix; a literal asked of a list of text is a view

[0.0.949] — 2026-10-14

a result’s views keep their own sources by path; the ledger writes them per path (ADR-353 D1, D5, part of #614)

[0.0.948] — 2026-10-14

check: operators, ??, the lambda and spawn in Nikaia (U-c10, #125, #558)

[0.0.947] — 2026-10-14

a function named as a value, a published field and an open set make a catch need else (ADR-351 D4, part of #620)

[0.0.946] — 2026-10-14

list patterns keep elements, guards read copies; a struct or list proved not null is a T (#622, #621)

[0.0.945] — 2026-10-14

a result built from a call handing back views of its arguments makes no buffer; a method’s result may view its receiver and a parameter (ADR-353 D1, part of #614)

[0.0.944] — 2026-10-14

impl Hash: a hand-written hash for a type with a hand-written equality (#591)

[0.0.943] — 2026-10-14

check: match, if, select, overlap, catch and unsafe in Nikaia (U-c8, #125, #558)

[0.0.942] — 2026-10-14

spec: a hash map hashes a declared type from its fields (ADR-010 D11, NK1260, #591)

[0.0.941] — 2026-10-14

a parameter of code fails with what its callers hand it (ADR-351 D4, part of #620)

[0.0.940] — 2026-10-14

a call through a variable or a list element fails with what it was given (ADR-351 D1, D4, part of #620)

[0.0.939] — 2026-10-14

a let and a for head take a nested tuple of names; a variant there is NK1259 (ADR-356, #611)

[0.0.938] — 2026-10-14

a call through a function field fails with what was stored there (ADR-351 D1-D3, part of #620)

[0.0.937] — 2026-10-14

List patterns, and .. as the rest of positional values (ADR-355, #618)

[0.0.936] — 2026-10-14

A result may point into several parameters (ADR-353 D1, D2, #614)

NK2302 no longer refuses longest or pair: the ledger lists every parameter the result reaches, and the lowering ties them and the result to one buffer group.

[0.0.935] — 2026-10-14

An element or a field read off a place is a view under ref T (ADR-354, #613)

let y: ref Expr = given[0] over a given: ref Vec[Expr], and given[0] as an arm under ref Expr?, are views of the element, lowered with &; both were NK1103. A word by its kind written so, let y: ref i64 = given[0], is NK1257, whose help writes let y = given[0]. A generic ref T is a view whatever T becomes (Part I 6.5).

[0.0.934] — 2026-10-14

spec: a result’s views point where the body says; a viewed buffer outlives its owner’s name (ADR-353, #614)

[0.0.933] — 2026-10-14

null is a pattern in a match over a T? (ADR-352)

[0.0.932] — 2026-10-14

check: a test that proves a T? not null makes it a T (ADR-350, #619)

[0.0.931] — 2026-10-14

D36: a ?. reach on get, first or last takes no extra .as_ref()

m.get(k)?.clone() ?? x cloned a &&T into a &T; a read that already lends its value is now matched as it is, so the clone is the value.

[0.0.930] — 2026-10-14

check: fields, struct literals, with, an index, a cast and a range, in Nikaia (U-c9, #125, #558)

[0.0.929] — 2026-10-14

spec: a function value keeps the error set inferred for it (ADR-351, #620)

[0.0.928] — 2026-10-14

spec: a test that proves a T? not null makes it a T (ADR-350, #619)

[0.0.927] — 2026-10-14

check: a statement and a block, in Nikaia (U-c7, #125, #558)

[0.0.926] — 2026-10-14

std: text search is inlined; brc’s naive.nika reads the temperature as naive.rs does

[0.0.925] — 2026-10-14

Text search: a one-character pattern is a char, and split_at takes find’s i64 (#616, #617)

[0.0.924] — 2026-10-14

check: a method call, x.m(…) and x?.m(…), in Nikaia (U-c6, #125, #558)

[0.0.923] — 2026-10-14

cache: evict on write, nikaia cache clean (#615)

[0.0.922] — 2026-10-14

check: a method call on a known receiver, and ?., in Nikaia (U-c5, #125, #558)

[0.0.921] — 2026-10-14

task: a scope’s function may throw; a sum keeps a later failure of another member (#605)

task::scope’s function may throw (fn(ref task::Scope) -> T throws): its error is the scope’s, in the place after every task it started (ADR-346 D7, Part II 12.7), at both settings and with tasks that pause. In a sum channel, a later failure of a member other than the first one’s joins the first in the envelope’s list of other-typed failures (Thrown::joined_by_other) instead of being dropped, and prints under it. Open: a catch on supervisor::run and the type of its error (decision basis on #605).

[0.0.920] — 2026-10-14

A variant’s rest pattern E::V(..) (ADR-349, NK1255, #610)

The owner’s decision on #610: E::V(..) matches a variant whatever values it carries and binds none, as .. is already the rest of a struct’s fields; it lowers to Rust’s E::V(..) and NK1253’s count is not asked of it. NK1255 refuses it on a variant without values, and refuses a positional rest E::V(a, ..) (left undecided) with a message saying it is not part of the language. Part I 3.4’s pattern table and Part III’s code table; lowering-defects D27 built (match_patterns.rs).

[0.0.919] — 2026-10-14

Program end: skip-free-at-exit:on leaves main’s memory to the OS (ADR-348, #607)

The owner’s decision on #607: freeing stays the default; the optimization word skip-free-at-exit:on (nikaia.toml optimization, or --optimization) writes an allocator into the program whose free is skipped from the end of main’s last statement until the drain. Every value still dies and every cleanup runs; output, exit status and the detached-task log are unchanged (process_exit.rs). brc: 300 192 748 instructions by default, 300 184 756 with the option.

[0.0.918] — 2026-10-14

check: a method’s function parameter is counted past its receiver, so a lambda it only runs is not kept (#125)

check_core_args_runs (0.0.912, U-c3) counted self as a call’s first argument, so a lambda handed to a method’s run parameter was recorded as kept and lowered as Kept<…> where the method takes impl Fn (red language: defects_found_running.nika, supervision.nika). It now counts past the receiver, as Rust’s arguments() did.

[0.0.917] — 2026-10-14

task: a scope’s catch lets its tasks borrow; a sum joins; a fatal error keeps its site

[0.0.916] — 2026-10-14

check: a free call, a tuple and a list literal in Nikaia (U-c4, #125, #558)

[0.0.915] — 2026-10-14

task: task::scope hands its tasks’ errors on

[0.0.914] — 2026-10-14

The pool’s error registration is quiet in rustc

[0.0.913] — 2026-10-14

lowering: a field option of a get local past a jump (D35); (A, B)? refused with NK1254 (D5)

[0.0.912] — 2026-10-14

check: a call’s arguments, a door over several locks, a list literal and a called field in Nikaia (U-c3, #125, #558)

[0.0.911] — 2026-10-14

A statement match arm is a statement

[0.0.910] — 2026-10-14

check: a comptime and a dsl block over a grammar in Nikaia (U-c2, #125, #558)

[0.0.909] — 2026-10-14

task: a typed error crosses the pool; a supervisor’s hand-over carries the child’s error (#605)

[0.0.908] — 2026-10-14

A variant pattern names every part

[0.0.907] — 2026-10-14

check: the leaves of the core in Nikaia (U-c1, #125, #558)

[0.0.906] — 2026-10-14

task: a program’s own fatal types are fatal in tasks; a supervisor hands a fatal error on as supervisor::Handed (#605)

[0.0.905] — 2026-10-14

check: the arguments of a call in Nikaia (#125, #558)

[0.0.904] — 2026-10-14

lowering: an option in a list element past a jump, a match on what get handed back, an iterator’s text beside a literal (D30, D31, D32)

[0.0.903] — 2026-10-14

check: a view kept where the receiver says text in Nikaia (#125, #558)

[0.0.902] — 2026-10-14

task: detached errors reach the log at main’s end; a supervisor hands a fatal Exit on (#605)

[0.0.901] — 2026-10-14

check: the leaves of a choice, arms asked one by one, map keys and handing over in Nikaia (#125, #558)

[0.0.900] — 2026-10-14

check: overflow, slot wraps, Copy and the collected types in Nikaia (#558, #125)

[0.0.899] — 2026-10-14

check: NK2109 on every path (#605)

[0.0.898] — 2026-10-14

contracts: filter_map/find_map lambdas read as lambdas; the project sync inference reads described crates (#579)

[0.0.897] — 2026-10-14

check: NK2109, a forgotten task handle (#605)

[0.0.896] — 2026-10-14

task: join(within: d) (#605)

[0.0.895] — 2026-10-14

lowering: a let-bound function value is lent its views, owned text lent to a pattern; Seq::find_map, Args::len (#579)

[0.0.894] — 2026-10-14

check: whether a type compares, in Nikaia (#125, #558)

[0.0.893] — 2026-10-14

task: a catch at join names the body’s error; a failing task on the pool (#605)

[0.0.892] — 2026-10-14

task: a task’s error reaches join, and detach (#605)

[0.0.891] — 2026-10-14

check: a_fatal_error_passes, an_exit_code_out_of_range and an_operator_needs_a_bound in Nikaia (#125, #558)

[0.0.890] — 2026-10-13

An enum with one variant is written by its type’s name (ADR-347, #608)

[0.0.889] — 2026-10-13

spec: every task error has a receiver, ADR-346 (#605)

[0.0.888] — 2026-10-13

check: an arm of an unworked-out type beside a null is wrapped, so a lambda’s if/else with null lowers (#586)

[0.0.887] — 2026-10-13

check: a T? as an operand or a hole, a list as a text pattern and impl Fatal without impl Error are refused (#586)

[0.0.886] — 2026-10-13

std: reductions and searches on Seq, rsplit and split_at on text

[0.0.885] — 2026-10-13

json: an explicit null takes a field’s default (ADR-344 D4, #604)

[0.0.884] — 2026-10-13

json: a member the type has no field for is skipped (ADR-344 D8, #604)

[0.0.883] — 2026-10-13

Fatal errors; process::exit throws process::Exit (ADR-345, #606)

[0.0.882] — 2026-10-13

json: a missing member takes the default or null in a T?, json::Value put back (#604)

[0.0.881] — 2026-10-13

process::exit built (ADR-343)

[0.0.880] — 2026-10-13

check: payload_of, claims_report, byte_literal, peeled and an_or_a in Nikaia (#125, #558)

[0.0.879] — 2026-10-13

json: an official package in packages/json, read[T]/write[T] by ADR-299’s rules, a build-time schema, json::Value (ADR-344, #604)

[0.0.878] — 2026-10-13

process::exit(code) decided (ADR-343)

[0.0.877] — 2026-10-13

check: None is answered with null (#125)

[0.0.876] — 2026-10-13

std: rfind and replacen on text, is_uppercase, is_lowercase, is_ascii_digit on scalar (#125)

[0.0.875] — 2026-10-13

std: cli::args().nth(at) takes an i64 (#125)

[0.0.874] — 2026-10-13

emit, parser: a composite ?? fallback is bracketed, and a - after one is refused (#125)

[0.0.873] — 2026-10-13

Check: articles, tail_of and unpromised in Nikaia

[0.0.872] — 2026-10-13

emit: a cast is cast once and borrowed whole (lowering defects D7, D8; #125)

[0.0.871] — 2026-10-13

Check leaves in Nikaia: wholly_known, a_map, a_copied_part, is_a_literal_other_than

[0.0.870] — 2026-10-13

Checker: an open-number arm is asked against the written type

[0.0.869] — 2026-10-13

Expressions carry spans

[0.0.868] — 2026-10-13

checker: node-level facts keyed by NodeId (#558)

[0.0.867] — 2026-10-13

checker: casts keyed by the operand’s node (#558)

[0.0.866] — 2026-10-13

checker: a view in a local struct that never leaves is no store (#585)

[0.0.865] — 2026-10-13

lowering: a view into a String parameter only read is lent as it is (#598)

[0.0.864] — 2026-10-13

checker: NK1245’s help for disagreeing arms asks for no annotation

[0.0.863] — 2026-10-12

checker: into_either over an if/match of views and text is each arm’s; an annotated mixed let works (Part of #562)

[0.0.862] — 2026-10-12

checker: a mixed let is bound as the view it may be; the position it flows into decides (Part of #562)

[0.0.861] — 2026-10-12

lowering: a mixed text position wraps each arm of an if/match; ref String and String arms meet at text (#562)

[0.0.860] — 2026-10-12

checker: an if/match value is asked where it goes; arms that disagree are NK1245 (#596)

[0.0.859] — 2026-10-12

ADR-327 D11: update_all copies only where both values copy; a read-only door never poisons (#490)

[0.0.858] — 2026-10-12

ADR-327 D11: an update of a value that copies works on a copy, so a panic in it poisons nothing (#490)

[0.0.857] — 2026-10-12

ADR-327 D8-D9: a copy toward Rust is written, never inserted (NK2506); a value that left its task counts against its receiver (#490)

[0.0.856] — 2026-10-12

ADR-342 and ADR-327 D8-D10: a chain runs element by element; a task’s block holds nothing that outlives it (#595, #490)

[0.0.855] — 2026-10-12

NK2302: a std lookup typed by the receiver’s type parameter points into the receiver alone (#585 item 4)

[0.0.854] — 2026-10-12

nikaia check: the checker’s findings for one file, nothing written (#590)

[0.0.853] — 2026-10-12

Eq and Ord: bounds, operators, compare, impl Eq / impl Ord, NK1247 for declared keys, NK1248, NK1249, NK1164 (ADR-341, #592)

[0.0.852] — 2026-10-12

ADR-341: Eq and Ord are sync and lower to the standard library’s traits (#592)

[0.0.851] — 2026-10-12

ADR-341: Eq and Ord are traits of the language; a bound gives the operators; impl Eq / impl Ord for declared types (#592)

[0.0.850] — 2026-10-12

check: an ordered map’s key must have an order, NK1247, ledger column orders (#589, ADR-010 D10)

[0.0.849] — 2026-10-12

tools: trust and emit_locals key their maps by Symbol, no copy of the name (#589)

[0.0.848] — 2026-10-12

Shapes of the held patch: no rustc error for a barrier handed a self map, a pub function’s map handed to a private one, or a grammar rule’s type (ADR-010 D9, #581 step 6 fixes)

[0.0.847] — 2026-10-12

The ledger’s maps key: one line per group of map positions, from(..) or keyed; a function declares one hidden parameter per group its body joins (ADR-010 D9, #581 step 6)

[0.0.846] — 2026-10-12

A pub type declares one hidden parameter per map position, in field order (ADR-010 D9, #581 step 5 fix)

[0.0.845] — 2026-10-12

Barriers: a map in a trait method’s signature, a described crate’s, or handed to one is the keyed map (ADR-010 D9, #581 step 7)

[0.0.844] — 2026-10-11

A map at a package’s boundary is a type parameter each use fills (ADR-010 D9, #581 step 5)

[0.0.843] — 2026-10-11

Map classes: a map keeps the fast hash across a private function’s parameter and result (ADR-010 D9, #581 step 4)

[0.0.842] — 2026-10-11

NK2302 payoff: two world clones return the view (ADR-283 D25, #580 step 8)

[0.0.841] — 2026-10-11

NK2302: a result of a struct another file declares is walked like one declared here (ADR-283 D25, #580 B12)

[0.0.840] — 2026-10-11

Package-wide trust: a map is keyed in every file when any file of the package reads an untrusted source (ADR-010 D9, #581 step 1)

[0.0.839] — 2026-10-11

NK2302: the ledger, the lowering and the message follow the one source (ADR-283 D25-D26, #580 steps 5-7)

[0.0.838] — 2026-10-11

One map, one hasher: decided per package, a type parameter at the package boundary, decided (ADR-010 D9, #581)

[0.0.837] — 2026-10-11

NK2302: a result points into the one parameter its body hands back, checked (ADR-283 D25-D26, #580 steps 1-4)

[0.0.836] — 2026-10-11

NK2302: a result points into the one parameter its body hands back, decided (ADR-283 D25-D26, #580)

[0.0.835] — 2026-10-11

emit: the flow of the core is Nikaia: Closure, Binary, Coalesce, TryCatch, Try, Return, Break, Throw, Spawn (#558, #125, E9 U8)

[0.0.834] — 2026-10-11

check: expect and expect_kept are Nikaia (C-5g, #558, #125)

[0.0.833] — 2026-10-11

check: made_from_text, a_name_in_text, may_fail_here, the reference at a call, gives_back (C-5f, #558, #125)

[0.0.832] — 2026-10-11

check: the walks below the core: overlap branches, guards inside boxes, claims, what a task keeps across a pause (C-5f, #558, #125)

[0.0.831] — 2026-10-11

emit: the places of the core are Nikaia: SafeMethod, Field, SafeField, Index, Cast, StructLit, With, Tuple, ListLit (#558, #125, E9 U7)

[0.0.830] — 2026-10-11

emit: the calls of the core are Nikaia: call, call_inner, method_call, the MethodCall arm (#558, #125, E9 U6)

[0.0.829] — 2026-10-11

emit: the arguments and the call of the core are Nikaia: args, called (#558, #125, E9 U5)

[0.0.828] — 2026-10-11

check: the core’s scaffolding (baton, core.rs, the hubs expr and block) and the pilot grammar_call, a_compound_write_to_a_map_slot, an_instantiation, a_mut_parameter_given_away (C-6, U-c0, #558, #125)

[0.0.827] — 2026-10-11

check: the leaves below the core: sequences asked for what they are not, cleanup moves, bounds, buffers, C entry points, members nothing writes down, arithmetic on declared types (C-5e, #558, #125)

[0.0.826] — 2026-10-11

check: task crossings, the number fold, binding and boundary, arms at null are Nikaia (C-5d, #558, #125)

[0.0.825] — 2026-10-11

emit: the match of the core is Nikaia: patterns, boxed parts, arm bodies, match over a sum

[0.0.824] — 2026-10-11

emit: the reads of the core are Nikaia: held receivers and indices, comparisons against a length, receivers, field calls, index reads, overlap, select

[0.0.823] — 2026-10-11

check: the number knot, kept lambdas and a few field readers are Nikaia (#558, #125, C-5c)

[0.0.822] — 2026-10-11

emit: the leaves of the core are Nikaia: keys, postfix bases, operands, strings, f-strings, options, guards, grammar entries, templates, blocks, ifs (#558, #125, E9 U2)

[0.0.821] — 2026-10-11

check: patterns and boxes, small part questions and the open numbers are Nikaia (#558, #125, C-5b)

[0.0.820] — 2026-10-11

The questions below the emitter’s core are Nikaia: the ?. holds, resize fills, the options of a call, what a catch guards, the keeps a statement declares (tools/emit_core_below.nika, #558, #125, E9 U1b).

[0.0.819] — 2026-10-11

check: the fallible and pausing walks, cleanups, reads and views below the core are Nikaia (#558, #125, C-5a)

[0.0.818] — 2026-10-11

The type group of the emitter is Nikaia: ty, ty_counted, the tethered wrapper and the extern pointer (tools/emit_core_types.nika, #558, #125, E9 U1a).

[0.0.817] — 2026-10-11

check: CheckHost and the seven functions that needed the compiler (#558, #125, C-4)

[0.0.816] — 2026-10-11

The emitter’s core has a host: EmitHost, the walk baton and three moved methods (tools/emit_host.nika, emit/host.rs, #558, #125, E9 U0b).

[0.0.815] — 2026-10-11

A trait method’s mut parameter lowers to &mut T in the declaration (ADR-094 D3, #558, U0a).

[0.0.814] — 2026-10-11

check: the checker’s walk state is a Nikaia CheckFlow (#558, #125, C-3)

[0.0.813] — 2026-10-11

EmitState holds the hand-over tables the core needs (#558, step E5b).

[0.0.812] — 2026-10-11

The checker reads a file’s items through a view: ParsedView, and fifteen item-reading functions are Nikaia (tools/parsed_view.nika, tools/check_items.nika, #558, #125, C-2).

[0.0.811] — 2026-10-11

The emitter’s entry point, the program’s own main and the held structs are Nikaia (tools/emit_entry.nika, #558, step E8).

[0.0.810] — 2026-10-11

The questions the emitter’s walk asks of the file and of its keeps are Nikaia (tools/emit_below.nika, tools/emit_keeps.nika, #558, step E7).

[0.0.809] — 2026-10-11

A method’s function value is lent on, as a free function’s is (#558, #125).

[0.0.808] — 2026-10-11

The grammars, the keep plans and the methods are tables of EmitState (#558, step E5).

[0.0.807] — 2026-10-11

The emitter’s small kinds are Nikaia: Tail, Lifetimes, Pointer, Method, and the halves Flow splits into (tools/emit_kinds.nika, #558, step E6).

[0.0.806] — 2026-10-11

A field in a struct pattern takes a pattern (ADR-291 D25, #573).

[0.0.805] — 2026-10-11

The checker’s walk holds no address, no i128, no closure boundary and no lazy cell (#558, #125, C-1).

[0.0.804] — 2026-10-11

A lent parameter, a map’s first write, a comptime asked for a type and a field holding code are Nikaia (tools/check_fields.nika, #558, #125).

[0.0.803] — 2026-10-11

The checker’s method and stored-code tables and the grammar pattern types are Nikaia (tools/check_state.nika, tools/check_grammar.nika, #558, #125).

[0.0.802] — 2026-10-11

The emitter’s questions about calls, patterns and shapes are methods of EmitState (tools/emit_exprs.nika, emit_misc.nika, #558, step E4).

[0.0.801] — 2026-10-11

The error sums and what the emitter asks of a type are methods of EmitState (tools/emit_sums.nika, emit_types.nika, #558, step E4).

[0.0.800] — 2026-10-11

The emitter’s state is three Nikaia structs (tools/emit_state.nika, #558, step E3).

[0.0.799] — 2026-10-10

Two number types in one operation, array literals and collect by use are Nikaia (tools/check_numbers2.nika, #558, #125).

[0.0.798] — 2026-10-10

Field reads through ?., a ?. or ?? over a value never absent, and text built where String is wanted are Nikaia (tools/check_fields.nika, tools/check_texts.nika, #558, #125).

[0.0.797] — 2026-10-10

The checker’s facts are one Nikaia struct, CheckFacts, the emitter reads (#558, #125).

[0.0.796] — 2026-10-11

The emitter’s output buffer and its error-name questions are Nikaia (tools/emit_out.nika, emit_items.nika, #558, step E2).

[0.0.795] — 2026-10-11

Out records a span with a mark and a record, not a closure (emit, #558, step E1).

[0.0.794] — 2026-10-10

CheckFacts is declared in Nikaia (tools/check_facts.nika, #558, #125).

[0.0.793] — 2026-10-10

A type variable two integer types are handed to, and a build-time run that stopped, are reported from Nikaia (tools/check_widen.nika, tools/check_reports.nika, ADR-285 D32, ADR-321 D1, #558, #125).

[0.0.792] — 2026-10-10

What the contracts say about a call the emitter writes is Nikaia (tools/emit_calls.nika, #558, #125).

[0.0.791] — 2026-10-10

A computed value as the program holds it, and eight reports about one expression, are Nikaia (tools/check_consts.nika, tools/check_exprs.nika, ADR-311, ADR-152 D4, #558, #125).

[0.0.790] — 2026-10-10

The emitter’s pausing reach and its never-negative locals are Nikaia (tools/emit_reach.nika, emit_locals.nika, #558, #125).

[0.0.789] — 2026-10-10

The emitter’s first leaves are Nikaia: operators, text, items and the walk (tools/emit_ops.nika, emit_text.nika, emit_items.nika, emit_walk.nika, #558, #125).

[0.0.788] — 2026-10-10

Six reports of the checker are Nikaia (tools/check_reports.nika, #558, #125).

[0.0.787] — 2026-10-10

Which integer type holds both, where a value is widened and where text stands for a file’s name are Nikaia (tools/check_widen.nika, ADR-285 D32, ADR-319, #558, #125).

[0.0.786] — 2026-10-10

What was taken, what was read after and what a loop or a lambda takes again are Nikaia (tools/check_takes.nika, ADR-293, ADR-094 D7, #558, #125).

[0.0.785] — 2026-10-10

A field in a struct pattern takes a pattern (ADR-291 D25, #573).

[0.0.784] — 2026-10-10

No table is keyed on the address of anything: a match arm’s facts are keyed on its body’s id, a ?. reach on its receiver’s (ADR-340 D4, #558, step 4).

[0.0.783] — 2026-10-10

argument_shape is gone: the call arguments, the struct-literal fields, the asserts and the tier pass’s hole wraps are keyed on the id (ADR-340 D4, #558, step 4).

[0.0.782] — 2026-10-10

The checker’s tables about an expression, kept by statement and the text of its shape, are keyed on its id (ADR-340 D4, #558, step 4).

[0.0.781] — 2026-10-10

The key at stood for on a list, a text literal and a with is the node’s id (ADR-340 D4, #558, step 4).

[0.0.780] — 2026-10-10

The host callbacks node_of are gone: the tier pass and the bounds walks name a node by its id (ADR-340, #558, step 5).

[0.0.779] — 2026-10-10

The checker and the emitter key their tables about an expression on its id, not its address (ADR-340 D4, #558, step 4).

[0.0.778] — 2026-10-10

A node the compiler builds takes a new id, a clone keeps its own (ADR-340 D2, #558, step 3).

[0.0.777] — 2026-10-10

Every Expr node carries an id the parser gave it (ADR-340, #558, step 2).

[0.0.776] — 2026-10-10

The checker’s refusals about pauses, locks and build-time runs are Nikaia

[0.0.775] — 2026-10-10

The checker’s state, its scope and the refusals that need only these are Nikaia (#558, #125).

[0.0.774] — 2026-10-10

The checker’s state begins to be Nikaia (#558, #125).

[0.0.773] — 2026-10-10

develop is green under clippy again.

[0.0.772] — 2026-10-10

lower-std reports every refusal of a file (#575).

[0.0.771] — 2026-10-10

Four lowering defects the self-hosting sessions found (#125, #567, #570, #574).

[0.0.770] — 2026-10-10

A field moved out of a borrowed value is refused wherever the value is lent, not only for ref self (NK1131, #576).

[0.0.769] — 2026-10-10

An arm that takes parts of a mut parameter and writes the whole back lowers (ADR-094 D7, #576).

[0.0.768] — 2026-10-10

NK2108 is built: a part taken out of a mut parameter is given back before the function is left (ADR-094 D7, #576).

[0.0.767] — 2026-10-10

A part taken out of a mut parameter is given back before the function is left (ADR-094 D7, #576).

[0.0.766] — 2026-10-10

A mut parameter is assigned and read through its reference (#576).

[0.0.765] — 2026-10-10

A mixed String reassigned from a view of itself lowers (#563).

[0.0.764] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.763] — 2026-10-10

The tools package no longer warns on valid shapes (#565).

[0.0.762] — 2026-10-10

A constructor call of a struct another file of the package declares lowers (#560).

[0.0.761] — 2026-10-10

drain() over a mut parameter takes the elements (#564).

[0.0.760] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.759] — 2026-10-10

A ?? whose fallback is an index read lowers to a value (#561).

[0.0.758] — 2026-10-10

A view cut from a call’s result no longer outlives it (#559).

[0.0.757] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.756] — 2026-10-10

The grammar language can say what the compiler’s own grammar says (ADR-339, #557).

[0.0.755] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.754] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.753] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.752] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.751] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.750] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.749] — 2026-10-10

More of the toolchain is Nikaia

[0.0.748] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.747] — 2026-10-10

More of the toolchain is Nikaia

[0.0.746] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.745] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.744] — 2026-10-10

More of the toolchain is Nikaia

[0.0.743] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.742] — 2026-10-10

More of the toolchain is Nikaia (ADR-294, #125).

[0.0.741] — 2026-10-10

develop is green under failing closed (ADR-286 D41, #497).

[0.0.740] — 2026-10-10

More of what failing closed refused is typed, or asked where it belongs (ADR-286 D41, #497).

[0.0.739] — 2026-10-10

NK1245 waits for the other refusals (ADR-286 D41, #497).

[0.0.738] — 2026-10-10

An expression whose type is not known is refused: NK1245 (ADR-286 D41, #497).

[0.0.737] — 2026-10-10

A lambda of a declared type, a field through Shared, dec[f64], a choice of rules and an if beside a literal are typed (#497).

[0.0.736] — 2026-10-10

Four more names the checker can type (#497).

[0.0.735] — 2026-10-10

Three places the checker knew a type and kept it unknown (#497).

[0.0.734] — 2026-10-10

What is written down is what can be called, and an error of several is an Error (ADR-286 D36, ADR-280 D30, #509, #507, #512).

[0.0.733] — 2026-10-10

Text becomes a number by a function per type (ADR-285 D35, #504).

[0.0.732] — 2026-10-10

A list written in numbers has one open number for its elements (ADR-285 D34, #513).

[0.0.731] — 2026-10-10

An open number where no number fits is refused at that use (ADR-285 D23, #554).

[0.0.730] — 2026-10-10

A supervisor’s child takes a policy: policy: and supervisor::Restart (ADR-328 D5, D9, #95).

[0.0.729] — 2026-10-10

A task in task::scope may pause, and the scope polls it: built (ADR-328 D10, #95).

[0.0.728] — 2026-10-10

A C library’s error codes are built: std’s below 100 000, its own from 100 000 (ADR-284 D33, #88).

[0.0.727] — 2026-10-10

pub extern enum is built: a tagged union, by value (ADR-284 D32, #88).

[0.0.726] — 2026-10-10

A handle to an enum with payload is read through _kind and a getter per field (ADR-284 D31, #88).

[0.0.725] — 2026-10-10

A C library’s absent number is built: the value, then bool *present (ADR-284 D30, #88).

[0.0.724] — 2026-10-10

A C library’s error codes: std’s below 100 000, its own from 100 000 (ADR-284 D33, #88).

[0.0.723] — 2026-10-10

An enum with payload at a C library’s boundary (ADR-284 D31-D32, #88).

[0.0.722] — 2026-10-10

A C library reports an absent number with a flag, not a status (ADR-284 D30, #88).

[0.0.721] — 2026-10-10

A task in task::scope may pause at every setting; the scope polls it (ADR-328 D10, #95).

[0.0.720] — 2026-10-09

A build-time run that stopped has a code of its own (ADR-321 D16-D19, #525).

[0.0.719] — 2026-10-09

A dsl block names its entry, unless the grammar marks one main (ADR-299 D21, #518).

[0.0.718] — 2026-10-09

No debug info unless a project asks for it (ADR-002 D5, #555).

[0.0.717] — 2026-10-09

db everywhere the specification named std::db (ADR-338 D2): Part II 11, Part III 17.2, and ADR-299 D4 and D8 noted.

[0.0.716] — 2026-10-09

The database protocol is an official package, db (ADR-338 D2, #539). Decided, not built: the compiler gives it no place a format lacks. Part III 17.2, 17.3.

[0.0.715] — 2026-10-09

json, toml and csv read and write (ADR-338 D5, #539). Decided, not built. Editing a text in place, comments kept, is left open. Part III 17.3.

[0.0.714] — 2026-10-09

std is the core; formats and protocols are official packages (ADR-338, #539).

[0.0.713] — 2026-10-09

A refusal is tested as a file beside the program (ADR-269 D22, #535).

[0.0.712] — 2026-10-09

A file can be a command, and nikaia new writes a project (ADR-260 D6-D8, #537).

[0.0.711] — 2026-10-09

A directory of a project is a package, and a module of one crate (ADR-286 D37-D40, #538).

[0.0.710] — 2026-10-08

A handler’s error where several error types arrive is an Error, one of them (ADR-280 D30, #512).

[0.0.709] — 2026-10-08

inf, -inf and nan are read in any case (ADR-285 D35, #504). Decided, not built. Part III 17.1.

[0.0.708] — 2026-10-08

parse_f32 and parse_f64 read inf, -inf and nan (ADR-285 D35, #504). Decided, not built. Part III 17.1.

[0.0.707] — 2026-10-08

Text becomes a number by a function per type (ADR-285 D35, #504).

[0.0.706] — 2026-10-08

A list written in number literals is one open number (ADR-285 D34, #513).

[0.0.705] — 2026-10-08

nikaia fmt indents with tabs (ADR-337 D1, #101).

[0.0.704] — 2026-10-08

What nikaia fmt does (ADR-337, #101).

[0.0.703] — 2026-10-08

Every narrowing as is written only where it is shown to fit (ADR-285 D12, D13, #552).

[0.0.702] — 2026-10-08

f64 as f32 is written only where it is shown to fit (ADR-285 D33, #552).

[0.0.701] — 2026-10-08

A type the language does not offer is refused; f32 is offered (#470).

[0.0.700] — 2026-10-08

What is written down is what can be called (ADR-286 D36, #509).

[0.0.699] — 2026-10-08

task::scope (Part II 12.7, ADR-328 D8; #95). task::scope fn(s) { … } runs its function with a scope. s.spawn fn { … } starts a task that borrows what the function holds, and the scope returns once every task has ended.

[0.0.698] — 2026-10-08

The Node binding’s _async form is a Promise (ADR-284 D9, D19, D28; #88). For a function that may pause, nikaia bind node adds <name>_async(…).

[0.0.697] — 2026-10-08

nikaia bind node (ADR-284 D26, D28; #88). It writes target/nikaia/c-library/node/<package>.c, an N-API module in C over the library, and a binding.gyp that builds it against the library with node-gyp. No napi dependency enters the emitted crate.

[0.0.696] — 2026-10-08

A library takes the caller’s allocator (ADR-284 D6, D10; #88). <package>_set_allocator(alloc, free, ctx) hands everything the library holds to the caller’s functions, alloc(size, align, ctx) and free(at, size, align, ctx). The library’s heap (c_boundary::Heap) chooses once, at that call or at its first block, whichever comes first, so every block goes back to the allocator that handed it out. Called after init or after any other call, or with a NULL function, it is E_ARGUMENT.

[0.0.695] — 2026-10-08

An extern struct’s array field may hold any C value (ADR-284 D15; #88). Array[T, N] of a scalar, of an enum without payload or of another extern struct crosses as T name[N] too. Each element is converted as a field would be, and an element that is no value of its type is E_ARGUMENT.

[0.0.694] — 2026-10-08

A library’s _async calls run on threads that are kept (ADR-284 D9; #88). rt::library hands a call to a thread that is waiting for one, or starts one where none is idle. A thread that waited 60 s for nothing ends. Before, every _async call started a thread and ended it. No call waits for a thread, because a call may pause for as long as its work does.

[0.0.693] — 2026-10-08

An extern struct’s Array[T, N] field crosses to C (ADR-284 D15, ADR-152 D2; #88). A field that is a fixed array of a number or bool is T name[N] in the header and ctypes.c_double * N (and so on) in the Python binding, laid out as C lays it out. An array of scalar, of an enum or of another struct is not exported yet.

[0.0.692] — 2026-10-08

A run of pub extern structs crosses to C (ADR-284 D16; #88). An entry point takes ref Array[T] or Vec[T] of one as const T* and a count. Each struct is checked as a single one is, and a bad one is E_ARGUMENT. A Vec[T] comes back into the caller’s buffer counted in structs, with the size query and E_TOO_SMALL (c-boundary’s hand_back_run). In the Python binding it is a list in and a list out.

[0.0.691] — 2026-10-08

A pub extern struct’s methods are entry points (ADR-284 D16; #88). <prefix>_<Type>_<method> takes self as const T* where the method reads it and as T* where it changes it, with no lock, since the struct is the caller’s memory. A changed struct is written back only where the method returned. In the Python binding the method belongs to the ctypes.Structure. A changing method that pauses is not exported yet.

[0.0.690] — 2026-10-08

Two corpus measurements re-read for the supervised hello-http (fixed forward). tests/sharing.rs finds its three counters atomic because they cross into a supervisor’s children. That is a decided crossing, not the floor’s answer. The one undecided Shared is still fortunes.nika’s. In tests/lock_column.rs, hello-http is the one example that opens locks of its own, each door a single update or get.

[0.0.689] — 2026-10-08

develop is green again (fixed forward). Since 0.0.673, two corpus tests (sequences, typecheck) refused examples/c-library: they check each file without its manifest, so a library’s entry points were NK1238. Both now drop that code for a package whose [build] says artifact = "c-library". hello-http reads request.path() once, and the corpus’s count of method calls on receivers the loose harness cannot type is back under its ceiling.

[0.0.688] — 2026-10-08

SharedMut::supervised(fn { … }) (ADR-327 D3, ADR-328 D9; #95 step 7). A shared value that is built again when a panic poisons it. The holders keep their handle, and the door that finds the poison asks std’s restart policy (supervisor::Backoff):

Both lock shapes, Local and Crossing, carry it. The builder may not pause, because a door cannot. SharedMut:: needs no use, as SharedMut(…) needs none. One for all restarts the siblings no longer races at yes: the crashing child waits for its sibling’s first attempt.

[0.0.687] — 2026-10-08

A stream is a generator in the Python binding (ADR-284 D20, D21, D27; #112). A function taking one callback that answers bool is also <name>_iter(…), its other parameters the same. The call runs on a thread, each item waits until the consumer asks for the next, and closing the generator early answers False, which is the caller’s stop.

[0.0.686] — 2026-10-08

A kept function takes what it captures, and a pausing one lends it to every call (Part I 8.3, ADR-328 D7; #95 step 6). A lambda a callee keeps (a supervisor’s child, a handler) is called after the statement that made it, so a value it captures that a move takes away goes with it. Using that value again is NK2101, with the help a task’s capture has; before, it was rustc’s borrow of moved value. A lambda that pauses owns such a value once, behind an Arc, and each call’s future holds a count of it rather than a copy. Before, the first call moved it out and rustc refused the program. hello-http reads its address in main again.

[0.0.685] — 2026-10-08

The Python binding’s _async form (ADR-284 D19, D27; #112). A pausing entry point gets <name>_async(…, done=None). With done, the value or the exception is handed to it on a library thread, and the returned Ticket cancels the call. Without done, under a running asyncio loop, it returns a future settled through call_soon_threadsafe, and cancelling the future cancels the call at its next pause point. What the call holds stays alive until done, and the ticket is freed after it. Not for a result into a buffer or a call taking a callback.

[0.0.684] — 2026-10-08

nikaia bind python (ADR-284 D26, D27, D29; #112). It builds the library and writes target/nikaia/c-library/<package>/__init__.py, a ctypes module over it, from the same reading of the package as the header and carrying the same ledger hash:

A call refused for its handle (E_REENTRANT, E_ARGUMENT) now says so in last_error. Not built yet: the _async form’s awaitable, a stream as a generator, and nikaia bind js, which waits on the WebAssembly build.

[0.0.683] — 2026-10-08

A library’s Shared values are safe on the caller’s threads (ADR-284 D1, D2; #113). A build with artifact = "c-library" keeps the sharing analysis’s answer at user_parallelism = no too: an entry point’s public signature and a public field stay at the atomic floor (Arc, the crossing lock), and only what the analysis proves never crosses gets the plain count. Before this, every count was plain at no. A handle holding a SharedMut called from two C threads at once was an Rc raced from both. c-boundary’s handles now require Send + Sync, so such a value can no longer hide behind the raw pointer. tests/c_library.rs hits one handle from two pthreads and counts 2001.

[0.0.682] — 2026-10-08

hello-http runs under a supervisor (ADR-328, Part II 12.8; #95 step 8). The server and a refresher are two Permanent children of one OneForOne supervisor, sharing three SharedMut counters. /crash asks the refresher to crash. It panics, is started again, and counts on, while the server answers throughout. /refreshed and /crashes show it, and tests/project.rs checks it. The address is read inside serve: a pausing child capturing a String does not compile yet (step 6).

[0.0.681] — 2026-10-08

A response body may be bytes, or a file the program never read (ADR-289 D22, D23, D27, D28; #120). The http package’s Response.body is a Body: Text, Bytes or File. http::File(path, root) looks at the file when it is made. Its size is the content-length and its type follows its extension (else bytes). A file that is not there is a 500, and a name that leaves its root a 404, both settled before the status line. The bytes are read through the reactor only when the answer is sent, and a failure then closes the connection short and is logged. hello-http serves /source. Fixed on the way: a type a program declares, or one it names qualified (http::File), is no longer taken for a std type of the same last name (fs::File) when its cleanup is asked for. Not built yet: sendfile and the mapping as D24’s other mechanisms, the send-file key, and D29’s kept mappings.

[0.0.680] — 2026-10-08

fs::open and File (Part III 17.1; #546). A file held open is read (into a list the call changes in place), written, moved about in with seek (fs::Seek::Start, Current, End; a position before the start is refused) and measured with len. It is flushed by its cleanup when the scope that owns it last ends, or with close(). The options are write, append, create and truncate, and the name is resolved under its root as every fs call’s is. Each call is the system’s own, as exists’s is. Fixed on the way: a len() that can fail is not cast to i64. It is one already, and inside a catch the cast landed on the Result.

[0.0.679] — 2026-10-08

std::fs makes, removes, renames, copies and reads directories (Part III 17.1; #546). create_dir and remove (each with recursive), rename, copy, metadata (len, is_dir, is_file) and read_dir. read_dir gives the names in byte order, the same on every machine. Each resolves its names under the root as exists does, so a name that leaves an fs::Root::Dir is Outside before anything is touched. tests/guide makes its projects with fs::create_dir and no longer runs mkdir -p. Not built yet: open and File, and Metadata’s modified, whose type no record names.

[0.0.678] — 2026-10-08

.clone() of a text method’s view is text of its own when returned or assigned (#547). The cause was not the copy but three text methods and Seq::next, which nothing in std’s ledger described: a function calling one was taken to pause, and what the method handed back had no type, so .clone() stayed a view where a String or a String? was wanted. str::trim_start_matches, str::trim_end_matches, str::strip_prefix and str::strip_suffix now hand back views into their receiver, and Seq::next the first element or nothing. trim_matches is left undescribed: below, a text pattern cannot be searched from both ends. tests/guide drops its .to_string().

[0.0.677] — 2026-10-08

text.matches(p).count() is an i64 (#548). str::matches has its entry in std’s ledger, a Seq of views as split has, so its count() gets the conversion every count gets. 0 - closes is -1 and the program goes on. tests/guide drops its as i64. The guide’s SharedMut block is a fn main() throws, because join throws since 0.0.657.

[0.0.676] — 2026-10-08

A pausing entry point is exported _async too (ADR-284 D9, D19; #88). <prefix>_<fn>_async(…, done, ctx, op) takes the same parameters as the blocking form and runs the call on a library thread. done(status, ctx) is called exactly once: with the work’s status, or E_CANCELLED when <prefix>_cancel(op) came first, which cancels at the next pause point. Cancelling twice or after done is OK. <prefix>_op_free frees the ticket after done; before it, the answer is E_ARGUMENT. A NULL op makes no ticket, and a NULL done is E_ARGUMENT. What C hands the call stays C’s to keep until done; the move to the library thread goes through c-boundary’s Sent. Not built yet: a library executor in place of one thread per call.

[0.0.675] — 2026-10-08

A library’s entry point may pause (ADR-284 D9, D10; #88). Its blocking form drives the call on the calling thread over the shared reactor, with the runtime started first; a task the call spawns runs too. init starts the runtime, at user_parallelism’s setting. shutdown drains what is still running within cleanup-deadline and answers E_CLEANUP for what is left; the process goes on (rt::Started::drain, which finish now uses too). Not built yet: the _async form with its ticket and <prefix>_cancel (D19).

[0.0.674] — 2026-10-08

pub extern struct: C’s layout, crossing by value (ADR-284 D14-D17, ADR-324 D6; #88). The struct is lowered #[repr(C)] with every field pub. NK1145 refuses a field that is not a C value (text, an optional, a list, a struct without the word, …) and its help names the handle. NK1237 refuses an extern struct that is not pub, and type parameters are NK1145. In a library build each one is a typedef struct in the header. An entry point takes it and hands it back by value; an enum field no variant has, or a scalar that is none, is E_ARGUMENT. Not built yet: Array[T, N] fields at the boundary, its methods, and a Vec of them.

[0.0.673] — 2026-10-08

examples/c-library: a library C calls (ADR-284; #88). A word tally C keeps as a handle, feeds text and asks; an enum and a callback that stops the walk. use.c is its C program. tests/c_library.rs builds it in place under --locked and checks what use.c prints.

[0.0.672] — 2026-10-08

null crosses to C as NULL (ADR-284 D5; #88). An entry point takes ref String? as an address and a length where a NULL address is null, and an empty text is not. It takes ref T? of a handle as a pointer where NULL is null, and hands T? of a handle back as a new handle or NULL. Not built yet: String? handed back, and T? of a number, whose <PACKAGE>_NONE status has no value yet.

[0.0.671] — 2026-10-08

A library’s entry point takes a callback (ADR-284 D5, D11; #88). A fn(A…) -> R sync parameter is a C function pointer R (*f)(A…, void *ctx) and void *f_ctx, called on the calling thread; NULL is E_ARGUMENT. The callback receives numbers, bool, scalar, an enum without payload and ref String (an address and a length that live for the call), and hands back a number, a bool or nothing. A call from a callback into a handle the library holds is E_REENTRANT. Fixed on the way: an entry point that hands back nothing did not compile. Not built yet: a callback that throws or hands back text, and the async form’s library thread.

[0.0.670] — 2026-10-08

A library starts and stops (ADR-284 D8, D10; #88). <prefix>_init() and <prefix>_shutdown() are in the header. After shutdown every call is E_NOT_RUNNING until init. A panic’s poison is cleared by shutdown and then init; init alone leaves it. The library still starts by itself on the first call. Not built yet: shutdown draining cleanups within cleanup-deadline (E_CLEANUP), and set_allocator.

[0.0.669] — 2026-10-08

A library hands C a pub struct as a handle (ADR-284 D5, D11, D13; #88). An opaque <prefix>_<Type> in the header: its extern anonymous constructor is <prefix>_<Type>_new, its extern methods <prefix>_<Type>_<method> with self first, a getter <prefix>_<Type>_<field> per pub field, and <prefix>_<Type>_free. An entry point takes one as ref T and hands a new one back. Every call holds the handle’s lock - shared for ref self and ref T, alone for ref mut self - and a call on a handle the thread already holds is E_REENTRANT; NULL is E_ARGUMENT. Two symbols with one name are refused. The lock and the raw pointers live in c-boundary. A handle taken by value, a method taking self by value, and a field whose type does not cross are not exported yet.

[0.0.668] — 2026-10-08

A library’s entry points take and return an enum without payload (ADR-284 D5, D13; #88). It is a C enum in the header, <prefix>_<Type>, its values <PREFIX>_<TYPE>_<VARIANT> numbered in declaration order; a number no variant has is E_ARGUMENT. An enum with payload, T?, structs and callbacks are not exported yet.

[0.0.667] — 2026-10-08

A library’s entry point may throw (ADR-284 D7; #88). Each variant of an error enum the entry file declares and an entry point throws is a positive status, 1… per library in declaration order, <PREFIX>_E_<VARIANT> in the header. <prefix>_last_error(out, cap, written) renders this thread’s last failure with its raise site, into the caller’s buffer; after a panic it says the library panicked. An entry point throwing std’s errors, or more than one type, is not exported yet.

[0.0.666] — 2026-10-08

A library’s entry points take text, bytes and a run of numbers, and hand text and bytes back into the caller’s buffer (ADR-284 D5, D6; #88).

[0.0.665] — 2026-10-08

A sync function may panic (Part I 7.2). panic stops and never pauses, as a failed assert does, and the sync inference and NK2202 read it so: a function that panics may be sync, and an entry point C calls (ADR-284 D5) may panic on purpose. Found building #88.

[0.0.664] — 2026-10-08

A package is built as a library C calls (ADR-284 D4, D5, D7, D8, D12, D13; #88, steps 2 and 3 for D5’s first row, and the header).

[0.0.663] — 2026-10-08

pub extern fn with a body parses, and four shapes of an entry point are refused (ADR-284 D4, D5, ADR-324 D6; #88, the first step of ADR-284 §5).

[0.0.662] — 2026-10-08

NK1229: a supervisor’s child that captures a mut binding is refused (ADR-328, #95 step 5, first half). Its function is called again for every attempt, and the binding would carry one attempt’s changes into the next; the help says to build it inside the child’s function or take a .clone() there. A binding without mut is lent to every attempt. A captured value an attempt would keep or use up is not refused yet.

[0.0.661] — 2026-10-08

supervisor::run restarts the tasks it runs (ADR-328, Part II 12.8, #95 steps 3 and 4).

[0.0.660] — 2026-10-08

A '…' is a u8 where a byte is wanted (ADR-320, #453 step 2, first half).

[0.0.659] — 2026-10-08

There is no char: one character is a scalar (ADR-320, #453 step 1, second half).

[0.0.658] — 2026-10-08

net::serve(at) fn(conn) { … } runs each connection in a task of its own (ADR-326 D1, #121 step 3).

[0.0.657] — 2026-10-08

join on a task that panicked throws task::Crashed (ADR-328 D8, #95 step 1).

[0.0.656] — 2026-10-08

A panic in a task ends that task, and a poisoned value is never read (#121 steps 1 and 2).

[0.0.655] — 2026-10-08

A map’s first write types it, and the examples are tested in place.

[0.0.654] — 2026-10-08

Three faults filed by 0.0.652 are fixed.

[0.0.653] — 2026-10-08

Layers with phases; the handler runs at most once, under the runtime alone (ADR-335, #121).

[0.0.652] — 2026-10-07

Seven compiler faults the move to tests/language turned up are fixed, each with its tests moved to the Nikaia packages.

[0.0.651] — 2026-10-07

375 tests of what a program computes are Nikaia tests now. They were Rust tests that lowered a program, compiled it with rustc, linked std and ran it, one program at a time; they are test blocks in tests/language and tests/build-time, one file per topic, built once per setting by nikaia test --both-settings and run at both settings, where most ran at one. What stays in Rust is what a test block cannot say: refusals, the emitted Rust, the ledger, and programs that need files, input, arguments, an exit code or a switch.

[0.0.650] — 2026-10-07

What the language means is tested in the language. tests/language is one Nikaia package, a file per topic, whose test blocks run programs and assert what they computed (ADR-269). nikaia test builds it once per setting and runs each test in a process of its own; crates/nikaia/tests/language.rs runs it in place, so an unchanged package compiles nothing.

[0.0.649] — 2026-10-07

The test suite builds std once and keeps it, and runs its compiler optimised.

[0.0.648] — 2026-10-07

A tree of routes is a comptime value (ADR-334, #121).

[0.0.647] — 2026-10-07

A function names an array’s length: fn f[comptime N: i64](xs: Array[i64, N]) -> Array[i64, N] (ADR-333 D4, #121).

[0.0.646] — 2026-10-07

A grammar run’s parsers share one target directory per machine (issue #178’s build-time parser).

[0.0.645] — 2026-10-07

The library build-time code links against is built once per machine, not once per project (ADR-321 D3).

[0.0.644] — 2026-10-07

A value parameter is marked comptime; a function may take one, written as any other argument (ADR-333, #121).

[0.0.643] — 2026-10-07

Routes name their values; a type takes values in its brackets (ADR-332 revised, ADR-333, #121).

[0.0.642] — 2026-10-07

The ceiling of unanswered method calls is 17, what the corpus measures, down from 48. The old and the new test both count 17 in 103 programs.

[0.0.641] — 2026-10-07

The corpus test of unanswered method calls no longer re-reads the whole corpus for every tool (sequences::the_corpus_has_no_more_unanswered_method_calls_than_it_had). For each of the ~80 self-hosted tools it re-read and re-parsed every other tool, and inferred the whole package’s ledger anew. Each program is now parsed once, and the tools’ package ledger is inferred once, as lower-std infers it. It was half of the local test suite’s time.

[0.0.640] — 2026-10-07

A handler gets only what its signature declares (ADR-332, #121).

[0.0.639] — 2026-10-07

develop is green again: three tests still expected what 0.0.630 and 0.0.638 changed.

[0.0.638] — 2026-10-07

scalar is a type, and char is read as it (#453 step 1’s first half, ADR-320, Part I 2.6).

The rest of step 1 is still to come: moving std, the grammar builtins and the self-hosted tools to the word, then refusing char with NK1135.

[0.0.637] — 2026-10-07

A Bytes is read as a run of bytes (#453 step 5, ADR-320 D6-D7, Part I 2.6).

[0.0.636] — 2026-10-07

A cut with an open end (#527, Part I 2.6). Inside brackets, [..<b] and [..b] start at 0, and [a..] runs to the end.

[0.0.635] — 2026-10-07

s.find(needle; from:) on text (#453 step 4, ADR-320 D6, Part I 2.6). It gives the byte position of the first match at or after from (default 0) as an i64?.

Found: a cut with an open end (line[..<sep], line[sep + 1..]) does not parse, although Part I 2.6 shows both (#527).

[0.0.634] — 2026-10-07

A field of a list element is changed in place (#526). items[1].tags.push(x) read the element through index::get and handed rustc a shared reference to change: E0596, about a file nobody wrote. A call that changes a field reached through an index now writes the element as the place it is (items[1].tags.push(…)), as items[1].push(…) already did. The ledger reader goes back to the direct form it was moved off.

[0.0.633] — 2026-10-07

An attribute’s argument is any build-time value (#496, ADR-331 D1).

This finishes #496’s build list.

[0.0.632] — 2026-10-07

A type’s and a variant’s attributes are read too (#496, ADR-331 D6, D8).

[0.0.631] — 2026-10-07

A field’s attributes and its default are read while the program is built, and they cross into other packages (#496 steps 4-5, ADR-331 D5, D6, D8).

Not built yet: T::attribute(X) and a variant’s .attribute(X), and attributes of types and variants in the ledger.

[0.0.630] — 2026-10-07

Attributes are written and checked (#496 steps 2-3, ADR-331 D1-D4, Part II 10.3). @path(…) lines stand after a declaration’s /// lines and before a struct, an enum, a variant, a field, a fn (a method, a trait’s method), a parameter, a trait or an impl.

Nothing of an attribute reaches the running program. Reading one (field.attribute(X)) and the ledger are #496’s steps 4 and 5. Until step 5, an attribute declared in another package is refused as unmarked.

[0.0.629] — 2026-10-07

A struct’s field may have a default (#496 step 1, ADR-331 D5, D8). The default is written after the type: size: i64 = 50. A literal that leaves the field out takes the value:

Part I 4.1’s Page example now lowers and compiles (tests/specification).

[0.0.628] — 2026-10-07

An edited nikaia.proofs is a new build (#448): the build cache key carries a digest of the committed nikaia.proofs and of the files in nikaia.proofs.d, so removing or changing an entry no longer hands back the binary built under the old proofs. A project without such a file keeps the key it had.

[0.0.627] — 2026-10-07

develop builds on the toolchain floor again (rust_floor, red since 0.0.620): a match guard with if let is not stable on Rust 1.88. The dsl block’s arm asks contains_key in its guard and reads the value in its body.

[0.0.626] — 2026-10-07

A dsl html <for> name has the list’s element type (#524).

[0.0.625] — 2026-10-07

A tuple pattern’s parts and an empty list’s elements are typed (#522, #523).

[0.0.624] — 2026-10-07

sort_by_key hands back nothing, and its key is the lambda’s result (#497).

[0.0.623] — 2026-10-07

develop green again (red since 0.0.616): anonymous_constructors expected take(&S::new) for a parameter that may pause. Since #516 that is an async closure around the constructor, the only form that fits the AsyncFn the parameter is.

[0.0.622] — 2026-10-07

A par_fold entry of a package’s grammar is driven in pieces (#521).

[0.0.621] — 2026-10-07

Two dsl blocks over a grammar in one statement are two values (#518).

[0.0.620] — 2026-10-07

dsl G { … } eod over a grammar the program declares (#518 step 1, #505).

[0.0.619] — 2026-10-07

Attributes, and defaults on struct fields (ADR-331, #496).

[0.0.618] — 2026-10-07

pub grammar offers its entries to other packages (ADR-296 D25, #520).

[0.0.617] — 2026-10-07

A comptime reads an earlier one that holds a std type’s value (#519).

[0.0.616] — 2026-10-07

A parameter that may pause runs a declared function of either kind, and a list is not collected (#516, #517).

[0.0.615] — 2026-10-07

A function named as a value has its declaration’s type (#502, #507 steps 2 and 4).

A tuple position it does not have is NK1107 (#515): t.2 on a pair.

[0.0.614] — 2026-10-07

a or an by the word a message names (#511).

A grammar’s bindings have the types Part II 10.8 gives them (#500).

nikaia run f.nika names f.nika (#510).

[0.0.613] — 2026-10-07

f[T]() is allowed, with a warning that asks for its use case (ADR-330 D9, #514).

[0.0.612] — 2026-10-07

Full names stay out (ADR-330 §6, #121).

[0.0.611] — 2026-10-07

panic(…) has the type never (ADR-276 D27, #506).

A call through a head nothing declares is NK1181 (ADR-286 D31, D33, #488).

A handler’s error has the type the guarded calls throw (Part I 7.1, #501).

A float literal is an f64 (#503).

A dsl html block is a String, and a std enum’s case is a value of its enum (#505 step 1 for html, #507 step 1).

[0.0.611] — 2026-10-07

A view or a shared handle has the kind of what it reaches; T::access says how (ADR-330 D8, #121).

[0.0.610] — 2026-10-07

Arrays, tuples, scalar, views and shared handles have a kind (ADR-330 D3, #121).

[0.0.609] — 2026-10-07

A grammar names no type of the program: Named is gone (ADR-330 D7, #121).

[0.0.608] — 2026-10-07

A type is passed where a function asks for one, and answers its kind (ADR-330, #121).

[0.0.607] — 2026-10-07

benches/envelope builds again (fixes develop, red since 0.0.604).

[0.0.606] — 2026-10-07

Part I 7.1’s error type carries an fs::Path (ADR-319, #449 step 10).

[0.0.605] — 2026-10-07

fs::walk hands back fs::Paths, and to_text() fails only for a name the system handed over (ADR-319 D5, D7, #449 steps 3 and 6).

[0.0.604] — 2026-10-07

A file’s name in an f"…" used as text is NK1201; IoError and fs::Root::Dir carry fs::Path (ADR-319 D4, D7, #449 steps 5 and 8).

[0.0.603] — 2026-10-07

What fallible collections cost: measured (ADR-327 D4, D5, #490 step 1).

[0.0.602] — 2026-10-06

What a memory block per task costs: measured (ADR-327 D4, D5, #490 step 2).

[0.0.601] — 2026-10-06

A static C library a dependency package names links with GNU ld (#499).

[0.0.600] — 2026-10-06

A run reads the defaults the program’s ledger computed (ADR-321 D1, #468, #498).

[0.0.599] — 2026-10-06

A call through a head nothing declares is NK1181, as a value is (#488, ADR-286 D31 and D33). Decided with the owner; not built yet.

[0.0.598] — 2026-10-06

The build-time program leaves a package’s foreign parts out (ADR-321 D14, #479).

[0.0.597] — 2026-10-06

Two projects of one name, built at the same time, each run their own program (#495).

[0.0.596] — 2026-10-06

Part III 15.2’s image example says what the rule above it says (#487, decided with the owner).

[0.0.595] — 2026-10-06

The inline-assembly example names its grammar: dsl x86::Asm (ADR-299 D20, #485).

[0.0.594] — 2026-10-06

Part III 17.1 lists html::Raw’s constructor as a program writes it (#486, decided with the owner).

[0.0.593] — 2026-10-06

A reflected field answers .ty and .doc (ADR-329, #121).

[0.0.592] — 2026-10-06

A grammar’s build-time project holds the package’s other files (ADR-321 D13, #479).

[0.0.591] — 2026-10-06

The build-time program holds no C, not even linked: tested (ADR-321 D14, #479).

[0.0.590] — 2026-10-06

Build-time code calls its package’s Nikaia dependencies (ADR-321 D13, #479).

[0.0.589] — 2026-10-06

Timers and keep-alive for http (ADR-326 D6-D9, #121).

[0.0.588] — 2026-10-06

An action that reaches C is refused once (ADR-321 D13/D15, #479).

[0.0.587] — 2026-10-06

Part I 6.2’s three places a Shared is written no longer wrap one twice (#484, decided with the owner).

[0.0.586] — 2026-10-06

Supervision (ADR-328, #95).

[0.0.585] — 2026-10-06

A block under any expression is walked (#494).

[0.0.584] — 2026-10-06

An argument made in the call is a source of the keep plan (ADR-283 D24, #493).

[0.0.583] — 2026-10-06

A view handed back is handed back silently (ADR-283 D23-D24, #442).

[0.0.582] — 2026-10-06

dsl package::Grammar parses, and a package’s name alone is NK1228 (ADR-299 D20, #485).

[0.0.581] — 2026-10-06

A map’s read past a jump, assigned to a name that owns its value, is copied out or refused (ADR-293 D15, #492).

[0.0.580] — 2026-10-06

A path in a text f"…" is NK1201; IoError::Other stays a message (#449, ADR-319 D4 and D7). Decided with the owner; not built yet.

[0.0.579] — 2026-10-06

A text binding has its length and its class (ADR-314 D1, #491).

[0.0.578] — 2026-10-06

ADR-314 D5, measured: the temperature’s checks are proved and cost nothing; the two left are the accumulator’s (#421).

[0.0.577] — 2026-10-06

A view of what a function owns, or of a parameter, is handed back silently (#442, ADR-283 D23-D24). Decided with the owner; not built yet.

[0.0.576] — 2026-10-06

Many connections at once, and what a crash leaves behind (ADR-326, ADR-327, #121).

[0.0.575] — 2026-10-06

A negation is a site too (ADR-314 D5, #421).

[0.0.574] — 2026-10-06

A grammar’s action is proved from what its bindings matched (ADR-314 D1, #421).

[0.0.573] — 2026-10-06

A callee’s ensures is a fact at the call; an expression function publishes what it returns (ADR-314 D3, #421).

[0.0.572] — 2026-10-06

A char’s code is a number of the proof (ADR-314 D2, #421).

[0.0.571] — 2026-10-06

A dependency’s prover questions are its own (ADR-270 D23, #448).

[0.0.570] — 2026-10-06

--locked says where a question it would have searched was asked (ADR-270 D22, #448).

[0.0.569] — 2026-10-06

The tools below Nikaia take no orders from the project (ADR-325, #480).

[0.0.568] — 2026-10-06

A dsl block names a grammar from a package as package::Grammar, always (ADR-299 D20, #485).

[0.0.567] — 2026-10-06

A package links the system libraries it names, through pkg-config (Part III 13.4, ADR-324 D2-D4, #483 steps 2-6).

[0.0.566] — 2026-10-06

Every open issue carries one status label (docs/README.md, CLAUDE.md).

[0.0.565] — 2026-10-06

The commit that finishes an issue says Fixes #N (CLAUDE.md).

[0.0.564] — 2026-10-06

The tools below Nikaia read no configuration from the project (ADR-325, #480).

[0.0.563] — 2026-10-06

extern without a convention, and extern(library: "x") (ADR-324 D6, #483 steps 1, 4 and 5 in part).

Every bench and example lowers byte for byte as before.

[0.0.562] — 2026-10-06

extern(library: "sqlite3") and [library.sqlite3] pkg-config = "sqlite3"; extern without "C" (ADR-324 D2, D3, D6, #483).

[0.0.561] — 2026-10-06

A body’s last expression is a typed slot like return (ADR-285 D32, #439).

Every bench and example lowers byte for byte as before.

[0.0.560] — 2026-10-06

x += a over two integer types is x = x + a (ADR-285 D32, #439).

Every bench and example lowers byte for byte as before.

[0.0.559] — 2026-10-06

A map’s trust is the map’s (ADR-010 D1, #431).

Every bench and example lowers byte for byte as before.

[0.0.558] — 2026-10-06

docs/open-decisions.md is retired; a question for the owner is an issue labelled decision (6a7f4ead).

[0.0.557] — 2026-10-06

There is no build script; a package links a C library by name (ADR-324, #483).

[0.0.556] — 2026-10-06

A warning is said once (#481).

[0.0.555] — 2026-10-06

?? and ?. after a value that cannot be absent warn (ADR-279 D13, #476).

Every bench and example lowers byte for byte as before.

[0.0.554] — 2026-10-06

A grammar calls its package’s Nikaia; no foreign code runs while a program is built, not even by being loaded (ADR-321 D13-D15, #479).

[0.0.553] — 2026-10-06

The prover’s logic layer is a package any package may ask, also while it is built (ADR-270 D24-D26, #478).

[0.0.552] — 2026-10-06

A dsl block may name the struct its rows are: dsl … -> T { … } eod (ADR-299 D15-D19, #114; build: #477).

[0.0.551] — 2026-10-06

A value the compiler shows is not negative goes into an unsigned type (ADR-285 D32, #439 steps 9 and 10).

Every bench and example lowers byte for byte as before, and their nikaia.proofs do not move.

[0.0.550] — 2026-10-06

?? and ?. after a value that cannot be absent warn; after a list’s index they are still refused (ADR-279 D13, #476).

[0.0.549] — 2026-10-06

The help says when a number goes into another type on its own (ADR-285 D32, #439 step 11).

[0.0.548] — 2026-10-06

NK1215: a computation whose result depends on the width says which width (ADR-285 D32, #439 step 8; #474).

Every bench and example lowers byte for byte as before.

[0.0.547] — 2026-10-06

A stated type reaches the operands (ADR-285 D32, #439 steps 6 and 7).

Every bench and example lowers byte for byte as before.

[0.0.546] — 2026-10-06

Every pair of integer types compares as numbers, and a type variable is not widened (ADR-285 D32, #439 steps 4 and 5).

Every bench and example lowers byte for byte as before.

[0.0.545] — 2026-10-06

Integers widen where no value can be lost (ADR-285 D32, #439 steps 1–3).

Every bench and example lowers byte for byte as before.

[0.0.544] — 2026-10-06

A file’s name is printed as its bytes, and what is printed is only read (ADR-319 D4, #449 step 5; ADR-279 D5, #473).

Every bench and example lowers byte for byte as before.

[0.0.543] — 2026-10-06

An f"…" with a file’s name in it is a name, joined by its bytes (ADR-319 D4, #449 step 5, first half).

Every bench and example lowers byte for byte as before.

[0.0.542] — 2026-10-06

What a program does with a file’s name, it does on the fs::Path (ADR-319 D3, D6; #449 steps 4 and 7).

Every bench and example lowers byte for byte as before.

[0.0.541] — 2026-10-06

One integer type takes another without as where no value can be lost (#439, ADR-285 D3 amended, D32 new). Decided with the owner; not built yet.

[0.0.540] — 2026-10-06

A grammar describes a type as data, and a row’s fields are named by table (ADR-323; ADR-299 D12-D14; #114). meta::column(name, type) and meta::parameter(name, type) take a std::meta::Type — scalars, Maybe, List, Record and Named — from which the compiler writes a dsl block’s row and parameter types; a message about such a type names the block that declared it. A row has a field per table (u.a.id) and a flat field per unique column name; a shared name is no flat field, and NK1107’s help names the qualified paths and AS; a name that cannot be a field is refused at the query. Whether two blocks with the same columns share a type is not decided. Decided and specified; not built.

[0.0.539] — 2026-10-06

A socket read through a method is an untrusted source (#472, ADR-010 D1/D2).

[0.0.538] — 2026-10-06

std::fs asks for a file’s name (ADR-319 D2, #449 step 3, first half).

[0.0.537] — 2026-10-06

A number literal where no number is declared is refused (#471).

Every bench and example lowers byte for byte as before, and the specification’s blocks keep their verdicts.

[0.0.536] — 2026-10-06

fs::Path exists, and text stands wherever a file’s name is asked (ADR-319 D1–D2, #449 steps 1–2).

Every bench and example lowers byte for byte as before.

[0.0.535] — 2026-10-06

develop is green again after 0.0.534. nikaia_std::build_time::Parts stood after the module’s tests, which clippy refuses (items_after_test_module); it now stands before them.

[0.0.534] — 2026-10-06

A span of time can be computed while the program is built: comptime T: time::Duration = 30.seconds(), and an option’s default of the type (ADR-318 D3–D5 with ADR-321, #374).

Every bench and example lowers byte for byte as before. std.contracts changes only in time::Duration’s entries.

[0.0.533] — 2026-10-05

The specification’s blocks and the module tests are checked through a workshop, and a lowering asks for build-time values once (ADR-321 D1, #468 stage 4).

With this, no test outside a run’s own sub-program reaches the interpreter for a call. Inside one, a computed default is still checked again. Every bench and example lowers byte for byte as before.

[0.0.532] — 2026-10-05

The comptime and default tests run as a build does, through a workshop (ADR-321 D1, #468 stage 4). tests/comptime.rs, tests/comptime_item.rs and tests/option_defaults.rs now check, infer and lower with a workshop, so they test the compiled path rather than the interpreter. The helpers live in tests/common.

[0.0.531] — 2026-10-05

develop is green again after 0.0.529. tests/expressions.rs still counted 1.5E-4 among the exponents a float may carry; ADR-322 refuses it, and tests/number_literal.rs holds the refusal. The test now lists the lower-case spellings only.

[0.0.530] — 2026-10-05

A comptime raises its own bounds: comptime(steps: 50G, ram: 16Gi) (ADR-321 D11, #468 step 5b). Built as decided in 0.0.527.

The bounds are part of the run’s program, so a changed bound runs again. Every bench and example lowers byte for byte as before.

[0.0.529] — 2026-10-05

1G and 4Gi are numbers, and an exponent is written e (ADR-322, #469). Built as decided in 0.0.527.

No .nika file in the repository used either spelling; every bench and example lowers byte for byte as before.

[0.0.528] — 2026-10-05

The ledger’s pass no longer computes a comptime (ADR-321 D1, #468). The ledger’s inference checks every file to learn what each call resolves to. That check also computed every comptime and computed default, through the interpreter, before the real check compiled and ran them again.

Every bench and example lowers byte for byte as before.

[0.0.527] — 2026-10-05

A whole number may carry a scale, and a comptime names its bounds (ADR-322; ADR-321 D11). 1G is a billion and 4Gi four gibibytes: a decimal whole number may end in K M G T P (decimal) or Ki Mi Gi Ti Pi (binary), which is a spelling of its value, typed by its use like any literal. An exponent is written with a lower-case e; 1E5 is refused. A comptime raises its bounds at its declaration, comptime(steps: 50G, ram: 16Gi) TABLE = …, and NK1109 refuses another name. Exa and a scale on a float are not decided. Decided and specified; not built.

[0.0.526] — 2026-10-05

An option’s computed default is compiled too (ADR-318 D1, ADR-321 D1, #468). A default is a comptime without a name. It is now computed as one is: compiled and run in the build’s workshop, both where the checker asks about it and where the ledger records its value.

A read with no workshop still gets the interpreter. Every bench and example lowers byte for byte as before.

[0.0.525] — 2026-10-05

A build-time call the rule forbids is named as it resolves (ADR-321 D2, #468). The rule was read off the ledger before a comptime was compiled, but the refusal named the callee only when it was called by a bare name. It now also names one called by its path (io::read_to_string) and a method by its key (Reader::read), looked up in this program’s entries and then in std’s.

build_time’s 47 tests now run through a workshop, as a build does, so they exercise the compiled path. Three of them now expect ADR-321’s answers:

[0.0.524] — 2026-10-05

A program that declares a grammar compiles its comptimes too (ADR-321 D3, #468). The library build-time code links against now names the grammar runtime beside std, so a program with a grammar is lowered whole into a comptime’s program, and an ordinary call in it runs compiled. Running the grammar itself, Cfg::file(…), stays the grammar run’s (ADR-177); whether it joins this crate is ADR-321 §6’s open question. Every bench and example lowers byte for byte as before.

[0.0.523] — 2026-10-05

What an earlier comptime came to is carried into a later compiled one, whatever its shape (ADR-321 D1, #468). A comptime program carries the earlier ones as the literals they came to. Lists, tuples, structs and variants are now carried as well as numbers, truth values and text. So comptime BIG = ORIGIN.scaled(10).sum() after comptime ORIGIN = origin(1, 2) is compiled rather than handed to the interpreter. Every bench and example lowers byte for byte as before.

[0.0.522] — 2026-10-05

A file the build reads is handed to a compiled call (ADR-310, ADR-321 D1, #468). comptime N = twice(asset("data.txt")) went to the interpreter. Now the build reads the file by ADR-310’s rules, the asset stands as the text it read, and the call is compiled and run. A file the allowlist does not name is NK1175, and a computed path NK1176, before anything is compiled. A program with a grammar is still the interpreter’s. Every bench and example lowers byte for byte as before.

[0.0.521] — 2026-10-05

A comptime in a program of several files is compiled and run too (ADR-321 D1, Part I 9.1, #468). Each file of the package is lowered with the others into the comptime’s program, as a package is lowered, so a callee in another file runs from its own body. A stop there is named by the function it is in: It stopped in sq, in another file of the program. A program with a grammar and an initialiser that reads an asset are still the interpreter’s. Every bench and example lowers byte for byte as before.

[0.0.520] — 2026-10-05

A comptime may call a method, std’s or the program’s (ADR-321 D2, #468). comptime LOUD = "abc".to_uppercase() was NK1127, because the interpreter could run only code written in the program; compiled, it is "ABC", and Point { x: 3, y: 4 }.sum() runs the program’s own method.

Any initialiser with a call in it is now compiled. The rule is asked of what it reaches: the ledger derived for the function that hands the value back, so a method that prints or pauses is NK1152 before anything is compiled, naming the callee where the initialiser calls it by name. An initialiser that reads an asset, a program of more than one file and one with a grammar are still the interpreter’s. Every bench and example lowers byte for byte as before.

[0.0.519] — 2026-10-05

A build-time run is bounded: steps, call depth and memory (ADR-321 D7, D10, D12; #468, second stage).

The allocator is in the library and not in each run’s program, because a program linked against std dynamically uses its library’s allocator. How a comptime raises a bound (D11) is not decided; this compiler’s tests ask a workshop for smaller ones. Every bench and example lowers byte for byte as before.

[0.0.518] — 2026-10-05

A comptime that calls a function is compiled and run (ADR-321 D1, #468, first stage). The initialiser and what it calls are lowered as the program is and compiled for the machine that builds, so the build-time answer is the run-time one:

How:

The first stage takes a program of one file without a grammar, and an initialiser of numbers, names and calls to the file’s own functions. Elsewhere, and in a bare check with no workshop, the interpreter still answers. The step budget, one crate per package and the interpreter’s removal are the next stages. Every bench and example lowers byte for byte as before.

[0.0.517] — 2026-10-05

A build-time run is bounded in steps and in memory (ADR-321 D7, D10-D12). The step budget is 10 000 000 000 by default, chosen so that a mistake costs about twenty seconds; the run’s memory is counted by its own allocator and bounded at 4 GiB, so a list that grows without end stops the build instead of the machine. A comptime that needs more raises either bound at its declaration, never in the manifest or on the command line; how it is written is not decided yet. A run that takes more than a few seconds is reported by name. ADR-287 D17 and ADR-310 §6 change with it. Decided and specified; not built.

[0.0.516] — 2026-10-05

No Rust code but std’s runs at build time (ADR-321 D9, corrected). 0.0.515 let a Rust crate’s function run where its description said build_time = true; that column is gone. A description is a judgement, not a derivation, and a second claim beside it is the same judgement written again. A call that reaches a Rust crate’s function, directly or through Nikaia code, is refused at build time (NK1152); std’s Rust half, this project’s own code, still runs.

[0.0.515] — 2026-10-05

A dependency’s code runs at build time only on what this build checked (ADR-321 D8, D9). Compiling build-time code (0.0.513) would have run a dependency’s functions natively on the strength of the ledger it ships, which is believed while its sources hash as recorded and is not re-derived; a package could ship honest hashes beside a false touches = []. Before a Nikaia dependency’s function runs at build time, the build now derives that package’s ledger from its sources and refuses where the two disagree; a package without sources runs nothing then. A Rust crate’s function needs build_time = true in its description, a column nikaia describe never writes. A Rust dependency’s build.rs and procedural macros are outside this and not decided. Decided and specified; not built.

[0.0.514] — 2026-10-05

An integer comptime is an open number, typed at each use (ADR-287 D21, #467). comptime FIB_10 = fib(10) lowered to const FIB_10: i32 = 55;, so let y: i64 = FIB_10 was refused by rustc, about a file nobody wrote, and comptime N = 4 * 1024 handed to an i64 parameter the same.

Now, for an integer comptime with no written type:

Seven build_time tests expected the old, value-picked i32 and now expect the call’s i64. Every bench and example lowers byte for byte as before. Typed arithmetic at build time (step 5) is left to ADR-321, which compiles build-time code rather than interpreting it.

[0.0.513] — 2026-10-05

Build-time code is compiled and run, not interpreted (ADR-321). The interpreter that ran a comptime’s calls was a second implementation of the language: sq(100000) with an i32 result came out as 10000000000 at build time where the program stops on the overflow, and a std function written in Rust could not run. A comptime that calls a function is now compiled against the package’s dependencies, which are linked through one generated dynamic library, one crate per package, with its results cached; a build-time run is bounded by a step budget counted in loop turns and calls (NK1152). ADR-287 D16 and D18 and ADR-318 D6 change with it. Decided and specified; not built. The budget’s default and how a program raises it are not decided.

[0.0.512] — 2026-10-05

A mapped file is text wherever text is read (#452). Three spellings passed the checker and failed in rustc, about generated code. All three now work:

Every bench and example lowers byte for byte as before. Bytes’ content and text’s find are still open, and wait on #453’s decision.

[0.0.511] — 2026-10-05

fs::exists(path, root) is in std (#454, Part III 17.1, ADR-108 D1). It was in the specification and nowhere else, so both spellings passed the checker and rustc refused them. The name goes through the root check the other path calls use: one that leaves an fs::Root::Dir, by .. or by a link, throws io::IoError::Outside rather than answering false. The answer comes from std::fs::exists, so a directory that may not be read is PermissionDenied and not false. It is sync, one stat. A call with no root is NK1101, and --trust lists one written with Anywhere. Part I 7.1’s throw example now also reports NK2605, because the call it makes is described and can fail.

[0.0.510] — 2026-10-05

A part an expression arm binds as a view is handed on as it is (#456, found moving the prover’s terms into Nikaia, #436). SolverTerm::Var(name) => given.get(name) ?? id over a match of an owned value binds name ref, and the call still wrote the compiler’s & in front of it, a &&String rustc refused. A block arm was already right, found by its statements; an expression arm’s arguments that name a lent part are now marked by their node (Checked::views_handed_on). prover_arena.nika’s substitute is back to the plain arm, and the compiler’s own Nikaia half loses eleven &s it wrote in front of such parts. Every bench and example lowers byte for byte as before.

[0.0.509] — 2026-10-05

The aggressive bounds and overflow walk is Nikaia (ADR-294, #435 step 3). tools/bounds_walk.nika holds what bounds.rs’s Walk did: the facts kept at each point of a body, the linear terms and intervals read off expressions, the expression functions followed into, the bounds on a list’s values found and then checked (ADR-306 D7), and the questions to the solver whether an index is inside its list and an operation inside its type. The terms live in the prover’s TermArena; the solver and the node an expression is are handed in. bounds.rs keeps the options, the program’s mut parameters and the loop over the bodies. What is proved is unchanged: the bench and example lowerings with both removals on and their --bounds reports are byte for byte the same.

[0.0.508] — 2026-10-05

comptime marks the name, and an integer comptime is an open number (ADR-287 D20, D21; #115). The schema in ADR-299 and in Parts II and III was bound with let app = comptime asset(…), which does not parse; it is comptime APP = asset("schema.sql"), and there is no comptime expression. An integer comptime without a written type now takes, at each use, the type the use asks for, checked against its value; where no use asks, its expression’s type. The question leaves open-decisions.md. The typing is decided and specified, not built.

[0.0.507] — 2026-10-05

A list of tuple literals builds each tuple’s text (#466). let pairs: Vec[(String, i64)] = [("a", 1), ("b", 2)] was NK1103, a list of (ref String, ?), where one tuple literal and a list of plain text literals were already accepted: the list’s tuples are now built as one tuple is.

[0.0.506] — 2026-10-05

A tuple taken apart out of a place is lent, and a list’s position is converted (#465, found moving the prover’s passes into Nikaia, #436). let (name, n) = pairs[1] moved out of a borrowed element; it now lends the tuple, as a one-name let over a place does, and a number, bool or char part is read out of its view. A part taken out of a lent tuple is a view where it is a map’s key, so counts[first] after let (first, _) = key is no longer a &&String. Vec::remove, swap_remove and truncate are described in std’s ledger: a function that called them was taken to pause, and their i64 position is converted as insert’s is (ADR-293 D20). prover_passes.nika’s workarounds go.

[0.0.505] — 2026-10-05

The prover’s passes are Nikaia (ADR-294, #436 step 4). tools/prover_passes.nika repeats the walk until the preconditions, then the postconditions, stop changing (ADR-269 D15, D17), strikes the postconditions an exit did not show, and makes the checked entries (D20), what the ledger publishes (D18), how many calls carry each precondition’s check, and how a claim no walk reached is held. prove.rs builds the walk, hands in the compiler’s callbacks and turns the answer into the compiler’s types: 300 lines. Three shapes rustc refused on the way are worked around there and open in #465. Every bench and example lowers byte for byte as before, and so do their --bounds reports.

[0.0.504] — 2026-10-05

A recursive method hands its function value on whole (#464). #463’s rule lent a method’s function parameter to a call of the method itself, and each level of the recursion became a new type, &&&…F, until rustc stopped at its recursion limit. A call of the function it stands in gets the value whole, as before 0.0.501.

[0.0.503] — 2026-10-05

The prover’s walk is Nikaia (ADR-294, #436 step 4). tools/prover_walk.nika walks every function, test and bench: what each binding makes known, the branches and loops, every call by name - an edge of the call graph, and where the callee has a precondition, what the call shows of it - each assert, each exit, and another package’s contract, read back once per function. The compiler hands in only what it alone can answer: the solver, a name as the language below spells it, the holes of a literal, a package’s alias, an expression as written and as the checker keys it, and a condition parsed by its own parser (TermArena::adopt takes its nodes). prove.rs keeps the passes that repeat the walk until its answers stop changing, the checked entries and what the ledger publishes: 493 lines, from 2,271. Every bench and example lowers byte for byte as before, and so do their --bounds reports.

[0.0.502] — 2026-10-05

An assert is held in Nikaia (ADR-294, #436 step 4). ProverState::an_assert proves a claim from what is known before it, makes it a precondition of the function it stands in (ADR-269 D5, D15) or a candidate postcondition before a return (D17), refuses a claim about data from outside (D6, NK1202), shows the values that break it (D8, NK1207) and otherwise says why it is checked where it is reached (D4). prove.rs hands it the condition as written and the names it reads. Every bench and example lowers byte for byte as before, and so do their --bounds reports.

[0.0.501] — 2026-10-05

A function value a method took whole is lent on to code that only runs it (#463). A method’s code parameter is never lent (lends.nika waits on knowing which entry a method call reaches, ADR-288), so it is an impl Fn taken by value, and handing it to another method moved it - in a loop, before the second turn. A call now writes &f for such a parameter where the callee only runs the code at that place, by its entry or by every method of that name; &F is a Fn wherever F is. ProverState::carry_back and precondition_call are methods again.

[0.0.500] — 2026-10-05

A call to a function with a precondition is Nikaia (ADR-294, #436 step 4). prover_state.nika’s precondition_call proves each of the callee’s claims with the call’s arguments, carries one back to the caller’s entry in the first pass (ADR-269 D15), finds the call breaking it - every time, or for a state that reaches it (D8, NK1207) - or makes the check the call carries, and says how the call reaches the callee (D20). Where a body is, Where, is ProverWhere. carry_back and precondition_call are free functions for now (#463). Every bench and example lowers byte for byte as before.

A method’s nullable text parameter takes a String? inside its option (#462). The rule that writes x.as_deref() for a ref String? parameter looked the parameter up by the callee’s name, and a method call names only the method: the argument went as &x. Where no entry has the bare name, the methods of that name are asked, where they agree.

[0.0.499] — 2026-10-05

Whether a claim reads data from outside is Nikaia (ADR-294, #436 step 4). prover_calls.nika’s from_outside walks an expression as contracts::sync’s walk does and finds a tainted binding or a call of a source std’s ledger calls untrusted (ADR-010 D2), which makes a claim NK1202. Every bench and example lowers byte for byte as before.

[0.0.498] — 2026-10-05

What the prover reads of a callee is Nikaia (ADR-294, #436 step 4). tools/prover_calls.nika’s free_function finds a free function of the program by name - its parameters, whether it throws, whether it hands back a whole number - and may_throw says whether a call may throw out of the function it stands in. Every bench and example lowers byte for byte as before.

[0.0.497] — 2026-10-05

Another package’s contract is read back in Nikaia (ADR-294, #436 step 4). prover_claims.nika’s foreign_shape reads which names a ledger’s conditions may use and whether the function hands back a whole number, and foreign_from makes the PreClaims and PostClaims with the assert each came from (ADR-269 D18); parsing a condition stays the compiler’s. Every bench and example lowers byte for byte as before.

A let over a list element is a view in a comparison (#461). let held = xs[at] binds the element lent, and made != held compared a String with a &String; the comparison reads such a side as it reads any other view.

[0.0.496] — 2026-10-05

Text is read through a view that names its unit, and there is no char (ADR-320, #453). Text stays UTF-8 and a position in it is a byte position. s.bytes indexes and cuts in constant time; s.scalars and s.graphemes are sequences. There is no s.len() and no s[i]; s.is_empty() stays. A position the text hands out - s.find(needle; from:) -> i64?, a grammar’s match - cuts it without a view, and a number the program makes up needs one (NK1214). char gives way to scalar and u8, and a '…' is either by its use (NK1212; a decomposed one that normalises to one scalar is taken, with NK1213). Bytes is read as a run of bytes. Graphemes are std’s, == and hashing compare bytes, and normalising is written. Part I 2.2 and the new 2.6, Part II’s grammar table and Part III’s code table say what; the compiler side is open in #453.

[0.0.495] — 2026-10-05

Carrying a precondition back and showing a postcondition are Nikaia (ADR-294, #436 step 4). ProverState::carry_back decides whether a precondition a call cannot show becomes the caller’s (ADR-269 D15), ProverState::exit whether an exit shows the postconditions standing (D17), and postconditions_for what a call’s postconditions say of the name its result is bound to; TermArena::text writes a term as a reader does. Every bench and example lowers byte for byte as before.

A method’s number keeps nothing alive (#460). tools/buffers.nika gave a method call’s result the origins of its receiver and arguments, also where the method hands back an i64: total += into.add(length) put length’s buffer into total, the buffer went into the frame’s keep, and known.contains(&length) was a &&String. A method of this package whose result holds no view hands back its own, as a function already did.

A function takes the parameters the program wrote (#459). One with more than seven, the receiver counted, is written with #[allow(clippy::too_many_arguments)]: the lint is a rule about Rust written by hand, and the warning was about a file nobody wrote.

[0.0.494] — 2026-10-05

What the prover asks the solver is Nikaia (ADR-294, #436 step 4). tools/prover_solver.nika holds the two questions behind a refutation: refuting_values, the values that show a claim false every time it is reached, and breaking_values, a state that reaches a claim and makes it false. The solver stays nikaia-logic, reached through two callbacks that read the walk’s arena - ask (a SolverAnswer) and model. Every bench and example lowers byte for byte as before.

[0.0.493] — 2026-10-05

A list a function value only reads is a slice (#458). A ref Vec[T] parameter is written &[T], and the parameter of a function type that read one was written &Vec<T>, so read(xs) with read: fn(ref Vec[i64]) -> i64 and xs: ref Vec[i64] was refused by rustc. A function type’s list parameter is now a slice too, as its text parameter was already a &str.

[0.0.492] — 2026-10-05

What the prover’s walk keeps across functions is Nikaia (ADR-294, #436 step 4). tools/prover_state.nika’s ProverState holds the preconditions and postconditions found so far, the calls between functions (with reaches), the postconditions an exit broke, and which pass the walk is in; prove.rs’s Prover keeps the program, the ledgers, its findings and the solver’s copy of the terms. Every bench and example lowers byte for byte as before.

[0.0.491] — 2026-10-05

The claims the prover carries between functions are Nikaia (ADR-294, #436 step 4). tools/prover_claims.nika holds a precondition’s claims (PreClaim, with the call it was carried back from as a CarriedFrom), a postcondition’s (PostClaim), a function’s Precondition and another package’s contract read back (Foreign); what a failed check says is PreClaim::failure. Every bench and example lowers byte for byte as before.

[0.0.490] — 2026-10-05

What the prover hands the rest of the compiler is Nikaia (ADR-294, #436 step 4). tools/prover_results.nika holds how an assert is held (ClaimHeld), what a checked entry checks (PreconditionCheck), how a call reaches a function with a precondition (CallReach, joined by joined_reach) and the contract the ledger publishes (Published); prove.rs names them as before. Every bench and example lowers byte for byte as before.

[0.0.489] — 2026-10-05

A number read past a jump is the number (#456, the first of its two lowerings). let v = m[k] ?? return null over a map of numbers bound the map’s view, and handing v on - out.insert(name, v) - gave the map a &i64, which rustc refused; m.get(k) ?? return null did the same. A number, a bool or a char read through the brackets or a get past a jump is now read out of the view where it is bound, as a loop binding is, and the name is the value everywhere below. prover_arena.nika’s int_value goes back to the plain form.

Where a claim stands at the entry is Nikaia (ADR-294, #436 step 4). ProverScope gains at_entry, on_the_path, precondition_at, postcondition_at and pinned_names (the names a breaking state is made of); prove.rs’s versions are one-line adapters, and RESULT, PRECONDITION_TERMS and its own is_a_parameter go. Every bench and example lowers byte for byte as before.

[0.0.488] — 2026-10-05

What the prover knows at a point is Nikaia (ADR-294, #436 step 4). tools/prover_scope.nika’s ProverScope holds the walk’s variables, facts, entry values and path, with rebind, has_length, not_negative and blind; prove.rs’s Scope and length_of go. A term is a place in the Nikaia arena throughout the walk, and nikaia-logic’s copy is made when the solver is asked. Every bench and example lowers byte for byte as before.

[0.0.487] — 2026-10-05

A ?? whose fallback block ends in a jump is a fallback that jumps (#457). known ?? { s.insert(name) return 0 } handed name over for the rest of the function - NK2105 at the next read - and was written as .into() on a block of type !, which rustc refused. The checker asks whether the fallback leaves, a block ending in a jump included, and the emitter writes such a fallback as the match a bare jump gets.

[0.0.486] — 2026-10-05

The prover’s walk holds its terms in Nikaia (ADR-294, #436 step 4). Every term prove builds and reads - its facts, claims, substitutions and the terms prove_terms makes - is in tools/prover_arena.nika’s arena; the solver reads a copy in nikaia-logic’s arena, kept node for node, so a term has the same place in both. bounds::into_arena goes: prove_terms builds into the arena directly. Every bench and example lowers byte for byte as before.

[0.0.485] — 2026-10-05

What the language offers is not measured by what this repository calls. ADR-288 D11 says how a std entry is made - by hand, reviewed, never copied from Rust’s whole surface - and now says that this is all it says: what std provides is decided from what the language is for, the coherence of its types and what other languages count as basic. Fourteen records had cited it, or its wording, as waits for a program that needs it to leave a capability out (ADR-019, 042, 105, 150, 247, 282, 283, 284, 290, 291, 295, 299, 302, 312); each now says not decided here. ADR-286’s Cargo defaults and the owner’s own until someone needs it in ADR-287 D19 and ADR-293 stay.

[0.0.484] — 2026-10-05

The prover’s terms can be held in Nikaia (ADR-294, #436 step 4, the first of two parts). tools/prover_arena.nika holds an arena of SolverTerm nodes, with the operations the prover’s walk uses: constructors, mentions, variables, constant, int_value, substitute and size. A test checks each one against nikaia-logic’s Arena on the same terms; substitute builds the same nodes in the same places. The walk does not use it yet. Two places are written around lowerings rustc refuses (#456).

[0.0.483] — 2026-10-05

A view of a list takes a list as its default (ADR-318 D4, #374). An option xs: ref Vec[i64] may default to a list of numbers or bools, computed (= three()) or written (= [4, 5]). The ledger records it as [4, 5], and a call that leaves the option out lends it, &vec![4, 5]. A list the option would own is still NK1167. A list of anything else is still refused as not built yet.

An option of a ref type lends what it is passed (#455), as a ref parameter does (ADR-094 D1). total("c"; xs: [10, 20]) was refused with NK1106; the checker now accepts the owned value and the emitter writes the &.

[0.0.482] — 2026-10-05

The names a claim reads are Nikaia (ADR-294, #436 step 4). names_in and is_a_parameter move from prove.rs into tools/claim_names.nika, which reads the claim the way contracts::sync reads it: every expression in it, the holes of its literals included. Every bench and example lowers to byte-identical Rust.

[0.0.481] — 2026-10-05

Part I 7.1’s fs::exists names its root. The example wrote fs::exists(path); ADR-108 D1 lists fs::exists among the calls that take an fs::Root right after the path, as Part III 17.1 does. The question leaves open-decisions.md: the record had settled it. std has no exists yet.

[0.0.480] — 2026-10-05

Why a check stayed is said by Nikaia (ADR-294, #435). The reasons --bounds gives for a check it kept (a field, a list of lists, a product of two unknowns, a call’s result, or no fact at all) are read off the site in tools/bounds_reasons.nika now, not in bounds_report.rs. The reports for every bench and example are the same, line for line.

[0.0.479] — 2026-10-05

An expression as a term of the proof is Nikaia (ADR-294, #436 step 4, the first half). lin and claim move from prove.rs into tools/prove_terms.nika. lin reads a whole number as a linear term, and claim reads a comparison, &&, || and ! as a formula over those terms. The Nikaia functions do not reach the compiler’s arena. They append each node to a list in the order it is made, with its children named by place, and bounds::into_arena replays the list. The arena therefore gets the same terms in the same order as before. Every bench and example lowers to byte-identical Rust.

[0.0.478] — 2026-10-05

What leaves a block is Nikaia (ADR-294, #436 step 3). leaves_stmt, leaves_block and leaves_expr move from prove.rs into tools/leaves.nika. They decide whether a return, break, throw, ?, a counted continue or a call that may fail can end what is being proved before the code after it runs. The walk keeps contracts::sync’s reading of the tree exactly: the expressions a statement holds, the holes of its literals (handed in, since only the compiler parses them), and then the blocks that walk’s companion finds. Every bench and example lowers to byte-identical Rust.

[0.0.477] — 2026-10-05

A contract that changed in the breaking direction is said by Nikaia (ADR-294, #436 step 2). changed_contracts, the NK1208 warning about a pub function that asks more of its callers or promises them less than the committed ledger, moves from prove.rs into tools/contract_changes.nika. Whether one list of conditions implies another stays the prover’s question, handed in as implies.

[0.0.476] — 2026-10-05

How the prover writes a term is Nikaia (ADR-294, #436 step 1). The text helpers are moved from prove.rs into tools/prove_text.nika:

They read the terms one node at a time through tools/solver_terms.nika’s SolverTerm, which the interval propagation now shares. prove.rs keeps thin wrappers that pass in the arena and the emitter’s escaping. Every bench and example lowers to byte-identical Rust.

A binding in scope hides an item of its name (#451). A call through a parameter of function type, twice(4) in fn apply(twice: fn(i64) -> i64 sync), resolved to a free function twice of the program. The call was then checked against that function’s signature: twice takes 3 arguments. The local binding is now the callee, as a let already was.

[0.0.475] — 2026-10-05

The interval propagation is Nikaia (ADR-294, #435 step 2). Range, the closed interval with either end open, and the propagation that proposes a candidate bound for a linear term are moved from bounds.rs into tools/bounds_interval.nika. The terms stay in nikaia-logic’s arena and are read one node at a time through term_of, which needs TermId::index and Arena::at. The arithmetic is checked i64 rather than i128. A candidate is confirmed by the solver before it is used, so an overflow loses a candidate and never makes a wrong bound. Every bench and example lowers to byte-identical Rust with both removals on.

Two compiler defects the move found are fixed in the compiler (D3):

[0.0.474] — 2026-10-05

A file name is fs::Path, the platform’s own bytes (ADR-319). Part I 7.1 and Part III 17.1 wrote Path and nothing declared it (NK1135); std took names as text, so fs::walk stopped with NotText at a name that is not UTF-8 and such a file could not be reached. A Path holds the bytes as Unix has them, WTF-8 on Windows and UTF-8 where names are Unicode. Text stands wherever one is asked, an f"…" builds one where its use asks, to_text() fails only for a name the operating system handed over, on every target, and display() shows �. Part I 2.2 now says a String is always valid UTF-8. Decided and specified; not built.

[0.0.473] — 2026-10-05

A call to an expression function is its expression (ADR-314 D3, #421, in part). The bounds walk knew nothing of a call’s result, so xs[before(xs.len())] with fn before(n: i64) -> i64 { return n - 1 } kept its check. A free function of the file whose body is one return e and that returns a whole number is now read at the call as e, with the arguments for its parameters, through a helper over a helper up to a depth of four. A name in e that is not a parameter belongs to the callee and is not read. Where e overflows, the callee’s own check stops the program. Not built yet: a callee’s published ensures from the ledger, across packages (the rest of D3), and D1-D2.

[0.0.472] — 2026-10-05

--locked checks nikaia.proofs (ADR-270 D22, #448 step 4). A lowering now hands its book of answers back to the build instead of writing it itself: the rustc wrapper, which lowers too, writes nothing, and the command a person ran writes the file once per package, beside its ledger. Under --locked the file is not written. The build fails when a question had no entry that checks, or when the file holds an entry no question asked, with run nikaia build and commit nikaia.proofs. A cache hit asks nothing and checks nothing, as for the ledger.

[0.0.471] — 2026-10-05

nikaia.proofs is written and read (ADR-270 D4, D19-D21, #448 steps 2-3). Every question the prover and the bounds walk ask now goes through one place, nikaia::proofs::ask. It puts the query in normal form, keys it by the SHA-256 of that form, and answers from the project’s nikaia.proofs where an entry checks: a certificate is replayed against the normal form, a model is evaluated. A missing entry, or one that does not check, is searched for and written in. A project build writes the file beside nikaia.contracts: one line per question, sorted by key, proved with its certificate, refuted with its model, unknown with the solver. An entry nothing asked is removed, and a certificate longer than 4 KiB goes to nikaia.proofs.d/. A build from the same sources leaves the file byte-identical. The examples’ files are committed. --locked (D22) does not read the file yet. A dependency’s questions are recorded in the consumer’s file rather than in the dependency’s (D23).

[0.0.470] — 2026-10-05

A query in normal form, and an answer as text (ADR-270 D19, #448 step 1). nikaia-logic gains Normal: a query rewritten so that the same question has one form wherever it is asked. >= and > are turned round, the facts are put in an order by their shape with repeats dropped, and the variables are renamed v0, v1, … by first occurrence; named maps a model back to the program’s names. A certificate is found on, and checked against, the normal form. certificate_text and model_text write an answer as one word, R(h0,h2,c0:1:1:2,t3), S1[…|…] and v0=3,v1=6, and read it back. This is what a nikaia.proofs entry will be keyed by and hold; nothing reads or writes the file yet.

[0.0.469] — 2026-10-05

A refused default’s way out is a T? (ADR-318 D7, #374). NK1127 and NK1152 at a default offered what they offer a comptime: write let timeout = …, or call it with let instead of comptime. An option was never a comptime, so that was not something its author could do. At a default they now say to make the option a T? and compute it in the body. A std method with no Nikaia body, 30.seconds(), is refused there with NK1127 naming .seconds() (step 8).

[0.0.468] — 2026-10-05

The build cache knows when the compiler’s Nikaia half changed (#447). ADR-021 D3 keys the build cache on the compiler’s fingerprint, a hash over its sources. Since ADR-294 part of the compiler lives in nikaia-std/src/tools (the ledger grammar, absorb_into, the syntax tree), and that was not hashed, so an edit there served the previous compiler’s output from the cache. The fingerprint now covers it.

[0.0.467] — 2026-10-05

A computed default reaches another package (ADR-318 D3, #374 step 4). Absorbing a dependency’s ledger qualified an option’s type, at: lib::Point, but left the default as the library wrote it, Point { x: 1, y: 2 }, and the consumer’s Rust could not find Point. The default’s type names are now qualified the same way, lib::Point { x: 1, y: 2 }; a word inside a string or already after a :: is left alone. A two-package test runs a library’s at: Point = origin() and scale: i64 = 10 * 3 from a caller that leaves both out.

[0.0.466] — 2026-10-05

A struct is a default (ADR-318 D3, D4, #374). A default whose value is a struct or an enum variant of literals is recorded in the ledger as its literal, at: Point = Point { x: 1, y: 2 }, and a call that leaves the option out receives it. The ledger’s signature grammar reads a brace group as one piece, so the struct’s commas are not taken for the signature’s. A struct with a field that owns memory is NK1167 at the default, naming the field, as a comptime of it is. A list as a default is still refused as not built yet.

[0.0.465] — 2026-10-04

A default is refused at the default, and one that owns memory is NK1167 (ADR-318 D4, D7, #374). An option had no span of its own, so a refused default was pointed at the body’s first statement; ConfigParam now carries default_span, and NK1127, NK1152, NK1167 and NK1168 stand on the default. A default whose value is a list, for an option of an owning type (xs: Vec[i64] = three()), is refused as a comptime of it is: NK1167, xs is a Vec[i64], which a default can’t hold, with the length the build computed in the way out, rather than NK1127’s not built yet.

[0.0.464] — 2026-10-04

A function the build calls is not never used (#446). A comptime’s value and an option’s default reach the Rust as literals, so the Rust no longer called the function that computed them, and rustc’s dead_code warning reached the reader as Function three is never used about a function the program used. The emitter now collects what a comptime and a default call, transitively through the program’s own functions, and writes #[allow(dead_code)] above each; a function nothing calls is still warned.

[0.0.463] — 2026-10-04

A function with options runs at build time, and a default that needs itself is a ring (ADR-318, #374 step 2). The build-time evaluator skipped every function that declared an option, and said of it that it comes from a package, which was not true. It now calls such a function: an option the call names takes that value, one it leaves out takes its default, evaluated in the callee’s file with nothing of the call in scope. While a default is worked out its option stands in the chain of names being resolved, so comptime X = f() above fn f(t: i64 = X) is refused once with NK1168, the chain is t of f → X → t of f, rather than with two NK1127s.

[0.0.462] — 2026-10-04

An option’s default may be computed (ADR-318 D1-D3 and D7, #374, the first steps). The grammar took only a literal after the = of an option. It takes an expression now: timeout: i64 = 30 * 1000, tries: i64 = twice(BASE) over an item-level comptime, name: ref String = "a" + "b". The default is evaluated once, by the build-time evaluator, where the function is declared, and the ledger records the literal of its value, so a call that leaves the option out, in this package or another, receives the value. A default that cannot be computed is refused with a comptime’s codes (NK1152, NK1127) and the option’s name. A default whose value is a struct, a list or a time::Duration is not built yet: the crossing of such a value and the ledger’s constant column are the next steps.

[0.0.461] — 2026-10-04

1BRC is counted, not timed, and tuned is its own binary. brc.sh now builds, checks every output against naive’s, counts instructions and mispredictions with callgrind and prints each program’s length in tokens (scripts/tokens.py, new). The Rust programs are three binaries (naive, tuned, gen) instead of one: inside one binary LLVM had not inlined tuned’s hash-table entry, which cost it 40 instructions a row. The honest table: Nikaia 300.1 instructions and 1.58 mispredictions a row against tuned’s 272.2 and 3.70, 618 tokens against 809; the overflow checks cost 1.9. The README and the home page say within a tenth of tuned Rust, which corrects 0.0.458’s fewer instructions. The benchmark page keeps the method, the table and where the instructions go, and no longer a diary. CLAUDE.md says how to measure.

[0.0.460] — 2026-10-04

What is proved no longer depends on whether a check is dropped (ADR-306 D14, #390, steps 2 and 3). remove-bounds-checks and remove-overflow-checks take on and off, both off by default, and say only what is written: the walk runs at every build, the loop over a list’s own length and every fact the solver decides together. basic and aggressive are refused with the word they now are (on). --bounds reports the same proofs whatever the build writes, and says in its first line whether this build writes them without their checks.

[0.0.459] — 2026-10-04

An option’s default is a build-time value (ADR-318, #374). Part I 5.1 said a default is a literal, so timeout: time::Duration = 30.seconds() did not parse and an option’s unit moved into its name. A default may now be anything a comptime may hold. It is evaluated once where the function is declared, the ledger records its value, and a call that leaves the option out receives it in the form a comptime crosses in. A std type the compiler cannot see into crosses through a new ledger column, constant, which names a constructor and its parts. A std function runs at build time when it is written in std’s Nikaia half. Decided and specified; not built.

[0.0.458] — 2026-10-04

The README and the home page measure against tuned Rust, in counts. Their performance sections compared Nikaia with loops this project wrote itself; they now compare examples/1brc.nika with the single-core tuned entry that maps the file and skips UTF-8 validation: 300 against 313 instructions a row, 1.58 against 3.61 mispredictions, 618 against 809 tokens of source, no unsafe against one. Release builds, no LTO on either side, callgrind over a million rows. The benchmark page records the overflow checks’ cost (1.9 instructions a row) and how the tokens are counted.

[0.0.457] — 2026-10-04

Performance is counted, not timed. On a shared machine the same binaries swap places from one round to the next, so the 1BRC benchmark page, the getting-started guide, the examples’ overview and the roadmap drop their seconds; instructions and mispredictions per row stay. The README’s and the home page’s performance sections follow once they are measured against tuned Rust.

[0.0.456] — 2026-10-04

?? after a value that is never absent is NK1211 (ADR-279 D12, #342). xs[2] ?? return false over a Vec[i64], or x ?? 0 over an i64, reached rustc as mismatched types or i64: Or is not satisfied. It is refused now: the left side always has a value, and after a list’s index the help names the length check. An index into a list of T? keeps its ??, which replaces a null element; one past the end still stops. And ys[1] ?? "none" over a Vec[String?] no longer moves the element out of the list: it is lent, and the answer is a view, as a map’s is.

A forward reference to a comptime in a body is NK1117 alone (ADR-287 D19, #380). comptime D = C + 1 above comptime C = 1 also said D can’t be computed while the program is built, which named the wrong cause. Tests hold the other three rows: forward references at item level, an item-level ring (NK1168), and a body constant reading an item constant.

[0.0.455] — 2026-10-04

An assert is a contract, on the README and the home page. “An assert is a proof” read as an unproved assert does not build, which ADR-269 D4 rejects: proved where the compiler can, refused where it shows it false, checked when the program runs otherwise. The headings and the opening sentences now say all three. Both pages also say that removing index and overflow checks is something a build asks for, not the default (ADR-306 D14).

[0.0.454] — 2026-10-04

The self-hosting count, recounted, on the README and the home page. 30.6 % of the toolchain is Nikaia, 18 625 lines in 61 modules, about 4.8 percentage points a day since September 28. The home page’s bars gain October 4 and run to 40 %; the README’s table, its project figures (1 233 commits, 60 959 lines, 0.0.454) and the roadmap’s two mentions say the same.

[0.0.453] — 2026-10-04

ADR-306 D12 and D13 are built: a length is below 2^60 where its elements take space (#383). len() of a list, text, Bytes, a map or a set whose elements take at least a byte is a fact the walk starts from. A list of a zero-sized type is bounded by how it was built, and extend(other) now adds other’s length. x.len() + 1, a sum and a difference of lengths are proved; a product of two (#386) is not.

Found building it, and fixed (#444): no i64 or u64 operation had ever been proved. The solver counts in i64, and the goal term <= i64::MAX, negated, is 2^63. A proof now states its goal inside ±2^62, a sub-range of the type. Over the repository’s .nika files, the checks left standing at aggressive went from 551 to 442 (docs/solver-workload.md §8.5).

The code reached develop in 024e7ab without this heading, with a conflict in ADR-306 left unresolved: D12 and D13 had been written there at the same time (0.0.451). This package resolves it to that text, with a note on how it was built.

[0.0.452] — 2026-10-04

The specification states rules, not their status; the README’s version badge shows the version. Twelve decided, not built yet and Built notes leave Parts I-III; what is not built is the ADR index’s column, which gains ADR-296 D25’s pub grammar. The version badge’s visible text had stayed at 0.0.437 while its title moved: scripts/badges.py redraws it, and CI now runs it with --check.

[0.0.451] — 2026-10-04

Proving and dropping checks are two settings, and a length is below 2^60, in the records. ADR-306 D12-D13 (#383): len() of a container whose elements take space is below 2^60 on every target; a list whose elements take no space has only the length it was built with. D14-D16 (#390): proving says how hard the compiler proves, remove-bounds-checks and remove-overflow-checks are on/off and only choose what is written (off by default); an index or an arithmetic operation the compiler shows fails is NK1207; the walk’s answers go into nikaia.proofs. Part III 13.3 and the code table follow; proving’s levels wait for the measurements. Not built yet.

[0.0.450] — 2026-10-04

Two decisions written into the records and the specification. ADR-287 D19 (#380): a comptime at item level may read one declared below it, a ring is NK1168, and one inside a body is read in written order as a let is. ADR-279 D12 (#342): ?? after a type that is not T? is refused with the new NK1211; a list’s index is a T, and over a list of T? the index is a T? and ?? replaces a null element. Part III’s NK1188 row now says what ADR-204 D1 says since 0.0.445. The compiler side of both is open in its issue.

[0.0.449] — 2026-10-04

--bounds: every check the optimizations may drop, and what became of it (#389, Part III). What remove-bounds-checks and remove-overflow-checks proved could be seen only in the lowered Rust. nikaia build --bounds and nikaia lower --bounds now list every index into a list and every +, - and * they may drop the check of, by line, proved or checked, and for a checked one the shape that stopped the walk - a field (#384), a list of lists (#385), a product of two unknowns (#386), a call’s result (#382), or no fact on what it reads (#432, #383, #388). At the build’s levels, or at aggressive, saying so, where it asks for none. On the solver kernels: 36 of 44 indexes and 23 of 45 operations proved.

[0.0.448] — 2026-10-04

A number’s common methods are in std’s ledger (#443). Since 0.0.438 a method of a number no entry describes is NK1210, and the ledger had the checked, wrapping and saturating arithmetic but not what a program reaches for first: a.to_string(), a.pow(2), c.min(d) on an i32, f.floor(), f.round(), f.max(g), f.is_nan() and the rest were refused where they had compiled before. The pure methods of Part I 2.2’s number types - text, min, max, pow, abs, signum, div_euclid, rem_euclid, the bit counts, and a float’s rounding, powers, logarithms and tests - have entries now, as do bool::to_string and char::to_string, so a function calling them stays a plain one.

[0.0.447] — 2026-10-04

return ref x has one spelling left: none (ADR-202 D2, #96). The compiler wrote the & at a return of a place inside a borrowed self, but not inside a parameter lent to the function: fn of(row: ref Row) -> ref String { return row.name } was NK1104, and return ref row.name was the only way to say it. A parameter declared ref T now lends as ref self does. With every root a lending return can have covered, a ref written there is NK1137, as it is at a for and at a call. A let bound to a view keeps its own words.

[0.0.446] — 2026-10-04

A compound assignment is proved as the operation it is (ADR-306 D5a, #387). x += d and x = x + d mean the same, but only the second could lose its overflow check: the first is a statement, not a +. It is now recorded by the statement’s first byte and proved as x + d with what is known of x before it, and a proved one is written x = <T>::wrapping_add(x, d). And xs[i] += d on a list of numbers at a proved position is the value, the position, one proven::read, the operation and one proven::write - the element read once, as += reads it; a list of text keeps its checked index.

What it changes on the workloads: nothing yet. The solver kernels’ checksum += … and i += 1 are counters with no bound the walk knows (#432), and 1BRC’s Stats::add writes fields, of which the walk keeps no facts (#384).

[0.0.445] — 2026-10-04

Which types compare when nobody knows them (#379, ADR-204 D1). A known type is one with a declaration or a ledger entry, std’s or a described crate’s, and without compares = true it does not compare; a described crate is std in this, its description being reviewed like code. A name nothing knows - nowhere::Handle, and a declared type holding one - is no longer NK1188: it is already NK1135’s, NK2504’s or rustc’s, and the second refusal named the wrong cause. nikaia describe now reads impl PartialEq for T (and impl Copy) as it reads the derive, for a trait written without an argument and a type in the same module.

[0.0.444] — 2026-10-04

An if or a match of literals, kept, is declared String (ADR-282 D17, #441). let m = "bob" pushed into a Vec[String] compiled, but let q = if k > 1 { "x" } else { "y" } was refused with write .clone(), as if the text belonged to something else. A branch whose every value is a literal is static text whichever way it goes, and is now declared String where it is kept, each literal built where it stands; only looked at, it stays a view.

Found on the way: D17 borrowed the type node it writes from a field, a parameter or a result declared String, and a program with none - a main that pushes into a Vec[String] - had no node, so even let n = "ada" stayed a view there and the push was refused. The compiler makes the node itself now.

[0.0.443] — 2026-10-04

A span of time is read back as timeout.in_seconds() (#377, ADR-150 D5). in_hours, in_minutes, in_seconds, in_millis and in_micros mirror the five constructors: whole units, cut off (90 seconds is one minute), as an i64, and a span longer than an i64 counts reads as i64::MAX, as a count too large to make a span saturates. Rust’s own as_secs and kin stay out of the ledger. Part III 17.1 shows std::time; ADR-150 D2 says saturates, which is what the code did, where it said stops. On the way: time::Duration’s ledger entry lacked copies = true, so a span handed to time::sleep was gone for the next line (NK2105) although D1 says it copies.

[0.0.442] — 2026-10-04

Standard input is read 64 KiB a hop, a pipe’s capacity (#423, ADR-263 §6). io::lines() asked a worker for 8 KiB at a time - BufReader’s size - and on a pipe that is already full (cat data | prog) the hops were the cost: 4 M lines (60 MB) took 791 ms at 8 KiB and 414 ms at 64 KiB, best of 9, and a file redirected to standard input 671 against 389 ms. Still one read a hop, so a slow pipe hands over whatever is there, as before. Not zeroing the buffer before the read, measured on its own, saved nothing that showed. Whether a calling-thread path saves what is left stays open.

[0.0.441] — 2026-10-04

Four more of #381’s records, made to say what is built. ADR-283 D6 said the compiler warns where a small view keeps a large buffer alive; it reports it, in --tethers, and now says so. ADR-251 D4 says ref self and ref mut self are short for self: ref T and mut self: ref T, the form that can name type arguments (ADR-288 D17) - std.contracts writes both. ADR-282 D25 keeps its literal case as a backstop D17 leaves unreached, and says why. ADR-280’s table gives the three fields’ cost as ADR-211 measured it: a Result 96 bytes wide where the bare error is 16, the fields alone 88.

[0.0.440] — 2026-10-04

What a fold’s step calls directly is written #[inline] (ADR-296 D41, #433). The step is the loop body over the input and runs once per item, but LLVM left 1BRC’s Summary::record out of line - hashbrown’s insert path makes it too big for its heuristic, and no profile setting changed that. The emitter now finds the functions and methods a fold’s or a par_fold’s step calls, by name, and writes them #[inline]; nothing else gains the hint. 1BRC, callgrind over 1 M rows, the same output: 319.1 → 300.1 instructions a row, below tuned’s 313.

[0.0.439] — 2026-10-04

Five of #381’s smaller inconsistencies, settled. RESERVED_BELOW’s comment said it held only words this language does not reserve; ref, trait, extern and unsafe are reserved in both, and they stay because after . and :: a reserved word is a name (ADR-298 D3) - a test now holds the four to the escape. contracts/keeps.rs said nothing reads the column; three readers do. ADR-080 D1’s three parts are three checks under two codes, and says so. ADR-282 §2 cites ADR-283 D19-D21 for Bytes, not ADR-289 D22. ADR-013 D3 says ADR-280 changed how throws lowers.

[0.0.438] — 2026-10-04

A method of a number that std does not describe is refused in this compiler’s words (Part III C.1, #438). a.min(b) on an i64 had no entry in std.contracts, and the checker took the call as nothing is known: the function around it was lowered async, and the call went to rustc as written

i64::min, i64::max and i64::div_euclid are in the ledger, as #435’s interval walk needs them, and so are i32::abs and i64::abs, which Part I 2.2 already names. No program in the repository called a method of a number that the ledger lacks; tests/number_methods.rs holds the five entries and the refusal.

[0.0.437] — 2026-10-04

An impl on a type the program does not declare is refused in this compiler’s words (Part I 4.7, Part III C.1, #375). impl time::Duration { … } came back as rustc’s cannot define inherent impl for a type outside of the crate, about generated code. tools/types.nika now refuses an impl with no trait on a built-in type, a std type or a type written with a module in front with NK1209, and its help names the way that works: a trait of the program’s own, implemented for the type.

Found writing the check in Nikaia, and fixed in the compiler (ADR-294 D3): NAMES.contains(name) for a comptime NAMES: Array[ref String, N] and an owned name was a call no ledger knew - the function around it was lowered async, and the value went below where a &&str was taken. Array::contains is in std’s ledger, it is a lookup, and a list of text asked about any text compares as text (nikaia_std::list::contains). And let t = if c { x } else { return } had no type: an arm that leaves now gives the other arm’s type, as a match arm that returns already did.

#376’s program (a program trait’s method called on a std type) already lowers with its .await; a test now holds it.

[0.0.436] — 2026-10-04

What the aggressive bounds walk reads off a body is Nikaia (ADR-306 D4, D7, ADR-294, #435). bounds.rs’ Shape moves into tools/bounds_shape.nika as BodyShape: the names bound once to a literal and their value, the names an unsigned type keeps from being negative, the lists whose every write the walk sees (the roots) and the names that stand for their elements (the aliases), with writes_a_value, only_removes, key_root, root_name, elements_key and tracks. One walk reads them, told which by a Look. What the program around a body says about a call - each function’s mut parameters and the methods that take one - is an Around, built once. --optimization=remove-bounds-checks:aggressive drops the same checks: the benchmark programs and examples lower to the same Rust. 30.8 % of the toolchain is Nikaia.

[0.0.434] — 2026-10-04

A method on a written number takes the type its use asks for (Part I 2.4, ADR-285, #417). A number written as a method’s receiver had no type here, so (-7).wrapping_add(2) was a method on nothing known: the function around it was taken to pause and lowered async, the language below could not tell which integer it was, and the negative literal lost its parentheses - -7.wrapping_add(2) is -(7.wrapping_add(2)) below, a different number. The receiver now takes the integer type the first use of the call asks for - a let’s, a return’s, a parameter’s, an assignment’s - as 7 + 1 does, and the first type that holds it where nothing asks; the type is written on the literal, and a negative one stands in parentheses. 5.seconds() is written 5i32.seconds() below, where both integer types have the method.

[0.0.433] — 2026-10-04

Fewer cuts of the text, and the rest seen to be safe (winnow-grammar#23, its ADR 24 §12). winnow-grammar 3ca83d3 no longer slices an unbound element of an indexed run, no longer cuts twice after a scan’s hit to look back for a \r where the scan has no line_ending, and compares the hit’s byte before the cut so that LLVM removes the character-boundary check. No unsafe. 1BRC, callgrind over 1 M rows: 337.1 → 319.1 instructions a row, the same output and mispredictions; 6 instructions a row from tuned.

[0.0.432] — 2026-10-04

A fold’s step changes the accumulator in place (winnow-grammar#22, its ADR 24 §11). fn(acc, m) { acc.record(m) } was lowered as |mut acc, m| { acc.record(m); acc }, which moved the accumulator - 1BRC’s table of stations

[0.0.431] — 2026-10-04

What a loop’s body does is Nikaia (ADR-306 D4, D7, ADR-294, #125). The questions the aggressive bounds and overflow walk asks of a body - whether a break leaves the loop from it, which lists a turn pushes onto exactly once, and which names a lambda, a task, an overlap branch or a select arm changes or hands on - move into tools/bounds_body.nika, answered by one walk told which by an Ask. 30.2 % of the toolchain is Nikaia.

Text bound out of a value the match was lent is read where it is compared: wanted in Ask::Named(wanted) over a ref Ask is a &String below, though it types as the view a ref String parameter is (a &str there), and (x ?? "") == wanted compared a String with a &String. Such a binding is lent, and the comparison reads it.

[0.0.430] — 2026-10-04

A trusted map hashes text with rustc-hash 2’s byte hash (ADR-316 D5, #419). nikaia_std::hash::FxHasher finished a key with three branches on its remaining length, which a station name’s changing length mispredicted. Text now goes through the byte hash rustc-hash 2 uses - a key of up to 16 bytes read as two overlapping words, the length mixed in - before the Fx step, copied with its origin and held to the crate by a test. Numbers keep the Fx step alone. With callgrind, output equal: 1BRC 364.9 → 344.1 instructions and 3.42 → 1.58 mispredictions a row, below tuned’s 3.61; k-nucleotide 1292 M → 1204 M instructions and 3.26 M → 1.29 M mispredictions; nikaia lower unchanged. Measured against Fx with an overlapping tail of its own, foldhash, all of rustc-hash 2 and two variants tuned for 1BRC, in benches/brc/README.md.

[0.0.429] — 2026-10-04

A call’s ( is on the line of what it calls (ADR-317, Part I 5.1, #426). A ( at the start of a line begins what is written there - a tuple, a parenthesised expression - and never the arguments of the line above it, the rule ADR-135 D3 made for [. return null if open followed by (x, 2) was the call open(x, 2): a bool called, the function taken to pause and lowered async. It is a guard and a tuple, as it reads, and tools/bounds_basic.nika no longer writes around it.

[0.0.428] — 2026-10-04

The basic bounds-check level is Nikaia (ADR-306 D3, ADR-294, #125). remove-bounds-checks:basic - xs[k] inside for k in lo..<xs.len() whose body leaves xs’s length alone and does not bind k again - moves into tools/bounds_basic.nika, with which methods keep a list’s length and whether a body binds a name again, both of which the aggressive level asks too. The walk recurses over the tree it is given, so each index it names is one the emitter finds again. 29.8 % of the toolchain is Nikaia.

An if ending a nested match arm is a statement: a match that is the whole body of a statement-match arm had value arms, so if a > b { out.insert(a) } inside one lost its ; and rustc refused the if for having a value without an else. Vec::clear is in the ledger: a call no ledger knew made a function that emptied a list pause and lower async.

A line that starts with ( continues the expression on the line before it - return null if open then (x, 2) is a call of open - which is a question for the language: #426.

[0.0.427] — 2026-10-04

A map’s hasher follows its keys’ trust and shape (ADR-316). There is no hasher that is best for every map: Fx’s tail branches cost 1BRC mispredictions on names of varying length and cost k-nucleotide’s keys of one length nothing. Trust (ADR-010) still decides the class, and the keys’ shape, as the compiler knows it (a fixed-width type, text of a known length, text of unknown length), chooses the function within it, never weakening trust. The choice shows only in --trust and the ledger. A program may name any hasher with hasher: where it makes the map, and that override is recorded. A shape gets its own function only once a real program of that shape is measured better. Until then nothing changes. #419 is narrowed to the first shape, 1BRC’s names. Decided, not built.

[0.0.426] — 2026-10-04

A number that does not fit is said in Nikaia, and counted as one (ADR-285, ADR-294 D11, #125). NK1116 - the number, the type, what that type holds and the way out - moves into tools/check_numbers.nika. The constant the checker keeps is integers.nika’s Integer rather than an i128 with a sentinel one past u64::MAX for a fold that left the 65 bits: such a fold is marked as one, each place that compared a wide number against a type’s ends asks fold.nika’s integer_fits, and the overflow at an operation is counted with integers.nika’s arithmetic (ADR-315’s checked_ underneath). The build-time evaluator’s i128 adapters are gone. 29.1 % of the toolchain is Nikaia.

[0.0.425] — 2026-10-04

What is left between 1BRC and tuned is written down, and each item is an issue. benches/brc/README.md lists, in the order they are to be done, the hash of a name branching on its length (#419), the par_fold loop’s per-item work (winnow-grammar#22), boundary checks on cuts the generator already knows are safe (winnow-grammar#23), the stats update (#420), building ADR-314 (#421) and, last, a grammar over bytes with only text checked (#422) - with what callgrind attributes to each today, and what was measured out of the list.

[0.0.424] — 2026-10-04

fs::map checks UTF-8 with SIMD (ADR-016 D4). checked-text checks each piece with simdutf8 behind a new simd feature, which nikaia-std turns on: its fast check for text, its compat half for the offset of a bad byte, so the verdict and the offset are the standard library’s, held to it by a test across the vector check’s block edges. ADR-016 had rejected it while the check was 10 ms of a 140 ms program; on 1BRC it had become the largest thing left against the loop that does not validate. Over 1 M rows: 390 → 365 instructions a row, mispredictions 4.28 → 3.42, the same output and the same refusal of a stray byte; on the clock the same or faster, pinned and unpinned, and the same on an all-ASCII file.

[0.0.423] — 2026-10-04

ADR-314 is decided: what a grammar matched and what a callee promises are facts of the walk (Option B, not built). At remove-overflow-checks:aggressive the walk will enter a grammar’s actions knowing what each binding matched - a digit{1,2} is one or two digits, and a dec[T] over n digits is below 10^n, which is how 1BRC’s whole * 10 will be proved now that its action reads dec[i32] (0.0.419); a char’s code is a number; a callee’s ensures holds at the call, and a sync function that is one return e publishes result == e. Walking a short loop turn by turn, the proposal’s third fact, is left open: its one site was the loop dec replaced.

[0.0.422] — 2026-10-04

A grammar’s entries are entry rule, and pub belongs to the grammar (ADR-296 D25). pub rule said two things at once: that the program may call the rule, and - in a language whose privacy is per package (ADR-286 D2) - that other packages may; a one-file program had to write it for the first. Now entry rule file says the rule was written to be entered, rule keeps a helper such as 1BRC’s NAME the grammar’s own, and offering a grammar to other packages will be pub grammar, for all its entries at once, as a pub enum offers its variants. An entry is never inferred from a call, which would make one out of a typo. pub rule is refused with both words in the message; pub grammar is refused as decided and not built, since a grammar is entered only from its own file today. entry is a word of the grammar sublanguage, a name everywhere else. Every grammar in the tree moves, the toolchain’s own in tools/ included; examples/1brc.nika has neither pub nor sync left. A test that cut struct Reading out of the example by its old pub is fixed with it.

[0.0.421] — 2026-10-04

examples/1brc.nika writes only what 1BRC needs. Seven syncs, the comment explaining the word and its place in the header’s list are gone: the compiler infers that these functions never pause, and the word is a promise to other packages, which a one-file program has none of (Part II 12.1). So are pub on its structs and constructors, which a package’s own code does not need (ADR-286 D2) and which made the build note that two pub functions do not promise sync. pub rule file stays: it is the grammar’s entry point, the rule main calls from outside the grammar block. Same output, same 390 instructions a row.

[0.0.420] — 2026-10-04

Arithmetic and a conversion can be asked whether they fit (ADR-315, Part I 2.4). Beside aborting, wrapping and stopping at the limit (ADR-285 D6), checked_add, _sub, _mul, _div, _rem, _neg and _abs answer T? for i32, i64, u32 and u64 - the number, or null where there is none, a division by zero included - and checked_i32(), checked_i64(), checked_u32() and checked_u64() convert out of a type that has values the destination lacks, lowered as <T>::try_from(n).ok(). There is no checked_shl or checked_shr: Rust’s check the count and not the bits.

The compiler’s own integers ask instead of comparing (#125): tools/integers.nika no longer wraps and compares to find out whether a sum or a product fitted, nor counts an i64 out of a magnitude in fifteen lines, and tools/fold.nika folds with integers.nika’s arithmetic rather than its own copy of it - 41 lines fewer. A method called on a bare literal ((-7).wrapping_add(2)) still reaches rustc without a type: #417.

[0.0.419] — 2026-10-04

A build has no debug assertions, incremental is a switch, and 1BRC reads its temperature with dec[i32] (ADR-002 D5, #392). The generated profile is Cargo’s dev because a Nikaia build has one profile, and it inherited dev’s debug assertions: debug_assert! in every dependency and the standard library’s precondition checks in every inlined function. It now writes debug-assertions = false, with no key to change it. incremental is a key of [build.<target>] beside opt-level and lto, on where absent; off, a program compiles as one piece and runs faster, which benches/brc/brc.sh now writes along with optimization = "…:aggressive". winnow-grammar moves to bbc8683, where a one-byte literal is compared as a byte in the fast pass, and examples/1brc.nika reads whole:dec[i32](digit{1,2}) instead of folding the digits’ text in its action. 1BRC over 1 M rows: 516 → 390 instructions per row (debug assertions 78, dec 21, the separator 27), against tuned’s 313 and naive’s 863. benches/brc/README.md now says how every figure on it was taken, that the clock on this box does not separate differences under 10 %, that Nikaia’s own optimizations change nothing in this program, and where the 77 instructions left against tuned are.

[0.0.418] — 2026-10-04

winnow-grammar moves to 9446f25: a fixed-width run is matched by index (#392). Its ADR 24 withdraws ADR 16 and carries frames forward, and measures the two shapes #392 named one at a time. "-"? digit{1,2} "." digit is now a few byte comparisons and one advance in the fast pass instead of four parser calls; one search over the file instead of one per line was built, came out slower (706 against 608 instructions per row upstream, +17 % on the clock), and is not in it - tuned’s walk back to the last ; would also accept a;b;1.0, which the grammar rejects. Here, examples/1brc.nika over 1 M rows: 587 → 566 instructions per row (−3.6 %), and on the clock nothing outside the noise: the one-or-two-digit branch moved from the parser’s repetition into the action’s for d in whole.chars() rather than going away (mispredicts 4.22 → 5.22 per row). The compiler is unchanged, 382.54 M → 382.56 M instructions lowering tools/ty.nika. benches/brc/README.md has the tables; a value made while matching (digit{1,2} handed over as an i32) is the open question it leaves.

[0.0.417] — 2026-10-04

What a build-time expression comes to is Nikaia (ADR-287, ADR-294, #125). The value the evaluator computes - a number, a truth value, text, a list, a tuple, a variant or a struct of the program’s own - is tools/build_values.nika’s BuildValue, and what an operator makes of two of them and what an index reads out of a list move with it. 29.1 % of the toolchain is Nikaia.

A type holding one declared in another file compares: whether a type has == was asked of the types of its own file, so a struct or enum carrying one from a file beside it lost its PartialEq below and refused a comparison. The types of the files beside it are now asked too.

[0.0.416] — 2026-10-04

A build-time integer is a magnitude and a sign (ADR-287, ADR-294 D11, #125). The build-time evaluator kept an integer in an i128, which no Nikaia number is - and which was the reason its move kept being put off. It is now the shape the tree gives a literal and the fold counts in: tools/integers.nika’s Integer, with the sum, difference, product, quotient and remainder, the order, the bit operators on the bits the value has (ADR-285 D9) and the shifts written in Nikaia. A literal holds no more than 65 bits, and a step whose answer leaves them is one the evaluator does not take rather than a number it invents.

A conversion after a value an operator makes keeps the whole value: a return a - 1 into an i64? whose operands’ type was open went below as a - 1.into(), the conversion taking the 1 alone. Such a value is now held in parentheses.

[0.0.415] — 2026-10-04

What the checker reads off a callee’s signature is Nikaia (ADR-288, ADR-294, #125). The arguments a call passes, what the receiver’s and the arguments’ types bind a signature’s variables to, what a sequence a call hands back is as a whole (ADR-293 D16) and when a chain over one that pauses pauses too move into tools/check_calls.nika; what the bindings of a for take (ADR-105, ADR-152 D4) joins tools/check_types.nika. 28.8 % of the toolchain is Nikaia.

[0.0.414] — 2026-10-04

A nested choice beside a null is Some(…) where its values are made (Part I 2.3, #410). An arm that meets a null arm is wrapped below; where that arm was itself an if, a match or a block, the wrap was put around the whole, and the lowering writes such an arm as one - so if c { if d { a } else { b } } else { null } handed its inner branches to rustc as String where the choice is a String?. The wrap now goes to each branch’s last value. Found moving the checker’s type helpers into Nikaia (0.0.412), which wrote around it.

[0.0.413] — 2026-10-04

A file operation on an idle runtime runs on the calling thread (ADR-263 D1, D2, D4, #378, #320). When main is the one future being polled - no task alive on this thread’s queue or on the pool, no branch of an overlap or a select being polled, nothing outstanding on the ring, a worker or the readiness poller, no timer armed - a read of a regular file, and on the completion path a write, is made on the calling thread and its future is ready when first polled. Anything else goes through the runtime as before. D2 now names the case it missed: an overlap’s first branch finds nothing in flight because its second has not started, and would have read to the end before it did. Over 1,000 reads, what a read costs for being async falls from about 2,600 to about 770 instructions at user_parallelism = no and from about 2,550 to about 930 at yes (runtime-cost §7.4). Two test binaries of their own, one per setting, hold every case D2 names.

[0.0.412] — 2026-10-04

What the checker asks of one type is Nikaia (ADR-294, #125). Whether handing a value out takes it away or copies it (ADR-152 D2), a view of a type and what a view is of, what a hull holds and what goes into one (ADR-037 D7), which types are lists, collections or text, the numbers Part I 2.2 offers, and the one way out a mismatch offers (Part III C.2) move into tools/check_types.nika. 28.5 % of the toolchain is Nikaia.

A gap found on the way, and written around rather than fixed here: an if used as a value inside a match arm whose function hands back a T? does not put a plain branch in the option (#410).

[0.0.411] — 2026-10-04

The words the checker’s refusals are made of are Nikaia (ADR-294, #125). An article, a count, a list of names, the nearest spelling of a name, an indented block, the {…} groups of a literal, the note on a word another language reserves (loop, const, macro) and on another package’s entry that never promised sync (ADR-288 D28), which std lookups lend their key, and how a boxed member is named (ADR-246) move into tools/check_words.nika. A clause made a sentence is keep.nika’s capitalised, which already said it. 28.2 % of the toolchain is Nikaia.

[0.0.410] — 2026-10-04

An expression is written back in Nikaia (ADR-269 D2, ADR-294, #125). The checker’s written and written_pattern - an expression or a pattern as the source wrote it, with parentheses where the tree needs them and nowhere else, for an assert’s failure and a refusal’s quote - move into tools/written.nika. The first of the checker’s own helpers to move. 28.0 % of the toolchain is Nikaia.

[0.0.409] — 2026-10-04

What a build says about pausing, and a test build’s dispatcher, are Nikaia (ADR-269 D1, ADR-288, ADR-294, #125). The notes on pub functions that never pause, or pause only where their lambdas do, and do not promise it (D29); the call path and the unplaced form of the warning that one which could not pause now can (NK2211, D32); a test’s function name; and the dispatcher a test build appends to its entry move into tools/modules.nika. 27.7 % of the toolchain is Nikaia.

[0.0.408] — 2026-10-04

A package’s ledger is taken in and published in Nikaia (ADR-100, ADR-286, ADR-294, #125). What a build does with a package’s ledger as a whole moves into tools/ledger_ops.nika:

A trait method’s declared entry joins tools/declared.nika (ADR-295 D9). 27.6 % of the toolchain is Nikaia.

[0.0.407] — 2026-10-03

What a declaration says is read in Nikaia (ADR-294, #125). The type a .nika declaration names (Ty::from_ast) and the ledger entry a function’s declaration makes - its key, pub, its sync word, whether it may fail, its signature with bounds, options and mut parameters, what its result may point into, and whether it changes its subject - move into tools/declared.nika. Every column about the body is still the inferences’ to answer afterwards. 27.3 % of the toolchain is Nikaia.

views’ own reading of a function type agrees with the lowering’s: a function type holds a view only where its result does (0.0.405), which the check tools/views.nika runs had kept in its own copy of the old rule.

[0.0.406] — 2026-10-03

A ledger is written out in Nikaia (Part III 13.5, ADR-294, #125). nikaia.contracts, the record beside it of what it was derived from (ADR-251 D1), a description’s header (ADR-290 D5) and the throws list a diagnostic quotes move into tools/ledger_text.nika, beside ledger.nika, which reads the same format back. The files are written byte for byte as before. 27.0 % of the toolchain is Nikaia.

str::replace is described in std’s ledger. Undescribed, a call of it was a method nothing describes, and every function around it was taken to pause (ADR-288’s polarity).

[0.0.405] — 2026-10-03

What a declared String is below is decided in Nikaia (ADR-282, ADR-294, #125). text_tiers’ walk - every value’s kind of text, the positions it flows into, the fixpoint that decides each one owned, a view or mixed, and where a value is handed into a mixed position - moves into tools/text_tiers.nika (1 256 lines), and so does reading a use alias (Parsed::unaliased). What stays in Rust is writing the answer into the syntax tree. 26.5 % of the toolchain is Nikaia.

Two compiler defects the move found, fixed in the compiler (ADR-294 D3):

[0.0.404] — 2026-10-03

Where each buffer lives is walked in Nikaia (ADR-283, ADR-294, #125). contracts::keep’s walk - which locals point into which buffers, where each view leaves its scope, and the keep each buffer goes into, with NK2304 where none can hold it - moves into tools/buffers.nika (1 243 lines). What stays in Rust is the plan in the types the emitter reads. 24.5 % of the toolchain is Nikaia.

Two compiler defects the move found, fixed in the compiler (ADR-294 D3):

[0.0.403] — 2026-10-03

A claim the compiler shows false with values refuses the program (ADR-269 D8). NK1207 - an assert, or a call to a function with a precondition, false every time it is reached or for a value the compiler shows reaches it - was a warning, so that a better prover would never refuse what an older one built. That is turned round: a program the compiler has shown to break its own contract does not build, because fewer defects in production is what the language is for, and what a newer prover refuses was wrong before it was refused.

The bypass is [build] refuted-claims = "warn", or --refuted-claims warn for one build: each such claim is a warning with a note and stays D4’s check where it is reached. It is resolved with the other build options, handed to the wrapper, part of the cache key at warn, and a word other than error or warn fails the build. The tally counts the refusals apart from the type errors (1 claim shown false), and the note that the program stops where the claim is checked moved to the bypass, where it is true. The tests that show a check failing at run time build with the bypass, and first show the program refused without it.

[0.0.402] — 2026-10-03

A callee’s precondition is carried back through the call, and a claim false for a value that reaches it is warned about with that value (ADR-269 D8, D15). Two gaps between what ADR-269 says and what prove.rs did, found by trying the home page’s claim that the compiler shows the call that breaks a contract, with the value that breaks it.

Two tests named the call inside the function that could not show the precondition; they now name the caller that passed the value, which is D5’s reason for checking at the call. The prover grew by Rust: 22.5 % of the toolchain is Nikaia.

[0.0.401] — 2026-10-03

Which count each Shared gets is walked in Nikaia (#125, ADR-294). The walk contracts::sharing runs before its classes are coloured (ADR-312, ADR-037) moved into tools/sharing.nika beside the decisions already there: every handle a unit makes, names, aliases, stores in a field or hands to a call, joined into allocation classes; a public signature’s and a public field’s forced atomic (ADR-037 D8); a spawn, a lambda handed to a walk that runs in parallel (ADR-235 D1) and a callee nothing describes reaching what they use; a handle handed on by value duplicated (ADR-312 D1); a door over several locks and a get that copies out of one recorded (ADR-281 D18, D3); the holes of a literal walked as source (ADR-309 D13). What a written type means is the compiler’s to say, handed in. The sharing, count and lock suites pass unchanged.

Moving it found one gap, closed in the compiler (ADR-294 D3): a parameter handed to a method of the program that keeps it is kept. The walk asked each candidate’s keeps column, which is what the fixpoint is still filling, so every method of the package answered keeps nothing - a fallback handed to Slots::force, which stores it, was lent, and the declaration &Option<Fallback> met a method wanting the value. A candidate of the package is now asked through the fixpoint, as a free call is.

And one that was there before it, which the new walk’s allocations brought out: a literal in an f-string’s hole keeps the type its place gives it. What the checker records against a node is keyed by the node’s address, and the checker and the emitter each read a hole as a copy of its own - they met only where the allocator handed back the same address, so let s = f"{double(3000000000)}" came out 3000000000i64 for a u32 parameter on main already. Both now read a hole where it stands in the tree, unless the tier pass wraps something inside it. 22.7 % of the toolchain is Nikaia.

[0.0.400] — 2026-10-03

What a body does with each of its parameters is walked in Nikaia (#125, ADR-294). The walk contracts::keeps runs before its fixpoint (ADR-094 D2) moved into tools/keeps.nika: each statement’s expressions classified where they stand, the holes of a literal included, then the blocks it holds - a lambda’s body among them, a spawn’s not; an assignment, a return and the value a body ends in hand a parameter out of the call, through an if’s arms, a match’s, a block’s last expression and a name a let bound to one; a struct, list or tuple literal keeps what it is given; ??, ?. and ? consume what they stand on; a match takes apart what the author did not lend; a task keeps what it names; a call records what it hands on for the fixpoint, and a method call asks every entry the bare name could reach. What needs a type - the parameters, the ones the author lent, each one’s fields, whether the result is a view - is worked out by the compiler and handed in.

The tools package lowers byte for byte as it did, every & its 12 000 lines are declared with included, and the keeps and lending suites pass unchanged. 21.8 % of the toolchain is Nikaia.

[0.0.399] — 2026-10-03

What a function keeps is settled in Nikaia (#125, ADR-294). The least fixpoint contracts::keeps runs over the uses each body’s walk found (ADR-094 D2) moved into tools/lends.nika beside the lending rule it feeds: it starts from what each body keeps on its own evidence and adds what the callees it hands a parameter to keep, until nothing changes; a callee of the package answers from the fixpoint in progress, any other from its ledger, where an absent keeps on a present entry keeps nothing and an absent entry keeps everything. The body’s walk and writing the ledger stay Rust; the keeps and lending suites pass unchanged. 21.0 % of the toolchain is Nikaia.

[0.0.398] — 2026-10-03

What a body does to its claim to be sync is walked in Nikaia (#125, ADR-294). contracts::sync’s walk joined its fixpoint in tools/sync.nika: every function and method of a unit, the calls by name each goes to by callee_named, an overlap or a select joining on the executor (ADR-292 D16), a code parameter whose lambda decides (ADR-288 D29), the type checker’s method calls merged in, where each body first pauses itself (D32), the edge from an implementation to the trait method it implements (D2), and a trait’s methods and a grammar’s pub rules as leaves (D24; ADR-296 D35). The check of what a sync function may not call moved with it, Violation included. The notes a build prints and writing the ledger stay Rust. A field that was never set - the code parameter a body runs - went with the move: the inference writes inferred, as it always did.

Moving it found one gap, closed in the compiler (ADR-294 D3): a plain value lent to a ref T? is put in the option. name_of(Kind::B) for a k: ref Kind? is Some(&Kind::B); the wrap was never recorded where the argument was lent, so the value was opened as if it were an option already - (Kind::B).as_ref(), rustc’s words about a file nobody wrote. 20.9 % of the toolchain is Nikaia.

[0.0.397] — 2026-10-03

What a body reaches is walked in Nikaia (#125, ADR-294). The walk contracts::touch runs before its fixpoint moved into tools/touch.nika (ADR-288 D22): every function, method and pub grammar rule of a unit - a rule over every action of its grammar (ADR-296 D24) - the calls by name each goes to by callee_named, the type checker’s method calls merged in, a described callee’s touch set taken over unless it names a parameter, and a spawn, a dsl statement or an unnamed call leaving the claim unknown (ADR-292 D3). Writing the answer, only where nobody said and in the order a set is kept, stays Rust; the touch and overlap suites pass unchanged.

Moving it found one gap, closed in the compiler (ADR-294 D3): an else if lends its arguments as an if does. The condition is a statement of the else block, and the checker records the & it owes under that statement; the emitter wrote the chain under the outer if’s, so else if !has(r.items, x) handed a Vec to a slice parameter - rustc’s words about a file nobody wrote.

The walks of throws and touch take their graph as one record with the type checker’s method calls in it, which keeps every lowered function within seven arguments - the clippy bound CI holds the tools package to. 20.4 % of the toolchain is Nikaia.

[0.0.396] — 2026-10-03

Which errors leave a function is gathered in Nikaia (#125, ADR-294). The graph contracts::throws builds before its fixpoint moved into tools/throws.nika beside the walk and the fixpoint already there: every throws function and method of a unit, what its body throws itself and whom in the package it calls, merged with the type checker’s method calls (ADR-288) - a resolved callee of the package is a call, one of a library brings what its ledger says it fails with, and one no ledger carries, or a call that did not resolve, is ? (ADR-010 D1). What one expression reaches is sync’s answer and stays Rust, handed in; so does writing the ledger. The contracts and error suites pass unchanged.

[0.0.395] — 2026-10-03

Which functions can open a lock is walked in Nikaia (#125, ADR-294). The walk of contracts::locks (ADR-281 D3) joined its fixpoint in tools/locks.nika: the doors (D10), what a body opens itself and whom it calls

The walk is the first tools file to hand a code parameter to a function of another file (foreign.nika’s walk), and that showed the guard of ADR-002 D4 comparing the tools one file at a time: read alone, the callee is one nobody can see, so the parameter read as kept and lowered differently per switch. The package is lowered as one (ADR-286 D1), so the guard now compares the package as it is lowered, at both settings. 20.1 % of the toolchain is Nikaia.

[0.0.394] — 2026-10-03

What nikaia describe drafts is decided in Nikaia (#125, ADR-294). The entries a description holds, the crate types they name and what the describer saw and did not claim (ADR-290 D8, D10) moved into tools/describe.nika, over the surface tools/surface.nika already reads. The manifest, the crate’s files, their hashes and the ledger’s header stay Rust.

Moving it found two gaps, closed in the compiler (ADR-294 D3):

19.8 % of the toolchain is Nikaia.

[0.0.393] — 2026-10-03

What a grammar matched, what a callee promises and how often a loop turns, measured and proposed (ADR-314, open). The checks left in 1BRC’s TENTHS - value * 10 + digit_value(d) over digit{1,2} - stay because the walk of ADR-271 does not enter a grammar’s actions, reads nothing across a call (std.contracts has not one ensures), and forgets what a loop changes at its head. They cost 4.8 instructions a row (1.0 %). Read over the corpus, these three facts unblock few of the 106 overflow sites the walk does not prove; an upper bound on a length, values in lists of lists and fields of self block many more (solver-workload.md §8.4), and ADR-314 §7 leaves them open. No tool found derives facts from a text grammar; SPARK’s two automatic rules - an expression body is a postcondition, a short counted loop is unrolled - are what the proposal transfers. Nothing in the compiler changed.

[0.0.392] — 2026-10-03

The encoder a grammar run prints its result with is written in Nikaia (#125, ADR-250). The statements grammar_run writes into the sub-program that runs a grammar while the program is built (issue #178) moved into tools/dump.nika: text through the one escape set, whole numbers, bool, floats by {:?}, a list, an array or a slice as a run, an enum by a match with an arm per variant, a struct field by field, and the type that cannot cross named before a parser is compiled. Reading the program’s declarations into the shapes it walks, building and running the sub-project and the decoder stay Rust. 19.7 % of the toolchain is Nikaia.

[0.0.391] — 2026-10-03

What the \ in a written literal means is decided in Nikaia (#125, ADR-250). The one escape table build_time reads a literal through (ADR-188 D2) moved into tools/escapes.nika, with its two readers - the build-time decoder and NK1184’s refusal - and the inverse that spells a value back as a literal. Which numbers name a character is Unicode’s table, handed in from Rust as char::from_u32. The digits are now read exactly as rustc reads them, where from_str_radix had read more and less: \x+1 and \u{+41} are refused rather than decoded, seven digits in \u{…} are refused as overlong, and \u{1_F600} - an _ between the digits, which rustc accepts - is no longer refused, the one direction this compiler may never err in (Part III C.4). 19.5 % of the toolchain is Nikaia.

[0.0.390] — 2026-10-03

Which functions stay sync is decided in Nikaia (#125, ADR-250). The greatest fixpoint contracts::sync runs over the calls it collected (ADR-027 D1) moved into tools/sync.nika: every candidate starts sync and loses the claim on reaching a function without it, a callee the graph does not hold reads as one that pauses, and the answer does not depend on declaration order. With it, the functions that pause only where their lambdas do (ADR-244 D2) and the shortest way through the package’s calls to a pause, breadth-first in key order (D5). Collecting and resolving the calls, the spans and writing the ledger stay Rust; the tools package lowers byte for byte as before. 19.1 % of the toolchain is Nikaia.

[0.0.389] — 2026-10-03

What a build may read is decided in Nikaia (#125, ADR-250). What the compiler’s assets decides about a list and a path without touching a file moved into tools/assets.nika: why a read is refused (Denied) and the sentence and way out NK1175 gives for each reason (ADR-072 D1, D3), the allowlist’s own lines (D2), a path that leaves the project root by .. or by being absolute - decided lexically, as the line reads (D4) - and the entries nothing read (D8). Reading the files, the lock that records what was read and the digest stay Rust. 19.0 % of the toolchain is Nikaia.

[0.0.388] — 2026-10-03

A lowercase par_fold rule skips no whitespace, and 1BRC is 10.8 % fewer instructions for it. winnow-grammar is pinned to 28cf576, which generates a par_fold rule as lexical whatever its name (winnow-grammar#21). Before, examples/1brc.nika’s pub rule file skipped whitespace at its start and before every item at user-parallelism = "no" and not in pieces, so the core count changed the answer: ` b;2.0 was station b sequentially and b in parallel, and a blank line passed in one and failed in the other - against ADR-009 D2. tests/one_brc.rs now pins both cases. Measured with the hand-written halves of benches/brc (#358, taken here): 658 to 587 instructions a row, against 860 for naive and 312 for tuned; on the clock 0.51 s to 0.49 s over 8 million rows. The README says where the rest of the distance to tuned` is.

[0.0.387] — 2026-10-03

What the sharing walk asks of one expression is Nikaia (#125, ADR-250). The questions contracts::sharing’s walk over the bodies asks without its state moved into tools/sharing.nika: what a file declares that a slot can belong to (Fallback::ForeignFile), whether a let is itself the place the first handle is made (ADR-064 D2), what an unseen origin is said to be (Fallback::UnseenOrigin), the handle and the type a name in scope has, a walk that runs in parallel (ADR-235 D1) and the name a call is written with. They read borrowed fields beside a jump directly, as ADR-275 allows. 18.9 % of the toolchain is Nikaia, of a compiler that grew by the other session’s ADR-272.

The move found one defect, fixed in the compiler (ADR-250 D3): a map’s read bound by a let and handed to a ref V? parameter was written as index::Found, because the lend in front of the read cancelled its *; a map’s read keeps its * now, and the binding is the Option<&V> the checker types it as.

[0.0.386] — 2026-10-03

What every write into a list keeps in bounds is known of every read from it (ADR-272, accepted). --optimization=remove-overflow-checks:aggressive writes a +, - or * the solver proves inside its type as <T>::wrapping_*, with no unsafe; the checker records each operation’s whole-number type so that the proof and the emitted operation agree on it. The walk of ADR-271 learned a bound on the values of every local list whose writes it all sees, found in two passes and kept only where every write is proved within it; x % n; the length a loop that pushes once per turn leaves; constants bound once; and that an unsigned name is never negative. A bound’s constants come from interval propagation, each confirmed by the solver. The watch kernel goes from 10.44 M to 9.41 M instructions with overflow checks on (Rust by hand: 10.69 M), lowering it costs 16 ms more, and the rest of the corpus is within noise. emit::Needs::of no longer proves every check a second time. 1BRC gains nothing; solver-workload.md §8.3 says why.

[0.0.385] — 2026-10-03

A borrowed left side of ?? beside a jump is a view wherever it stands (ADR-275, the owner’s choice). let s = c.signature ?? return false and let label = c.label ?? continue over a lent c were refused as a part of a loan taken (NK2106): ADR-259 D1 makes a let keep the answer, and nothing can take a part of a loan. The left side is now opened, not taken - match c.signature.as_ref() { … } below, .as_deref() for text - and the let binds a view, as let found = m[k] does. A view kept past the loan is refused as any view is, with .clone(). tools/lends.nika writes the line directly instead of going round it through a helper.

[0.0.384] — 2026-10-03

Whether a callee lends a parameter is decided in Nikaia (#125, ADR-250). The rule the declaration, the call and the checker all read (ADR-094 D1, D2) moved into tools/lends.nika with its two questions: whether a ledger says a type copies (ADR-252 D4.1), and whether a value moves when it is handed on - an array as its elements (ADR-152 D2), a function value always (ADR-102 D1), the numbers, the machine-width names, a Duration and the hulls never. The walk that fills the keeps column stays Rust. 19.0 % of the toolchain is Nikaia.

[0.0.383] — 2026-10-03

What a method’s name says about text, and which containers text is followed into, is Nikaia (#125, ADR-250). text_tiers’ tables and the questions on them moved into tools/tiers.nika: the methods whose result is text of its own, a view of what they are called on, or an element of it; the methods that put their argument into a container; the runs, sets and maps whose elements are positions; and whether a written type is a plain String or a container (ADR-222, ADR-224 D1). The walk that follows text into positions and the tiers it decides stay in Rust: its kinds and positions are keys of ordered sets and maps, which a type this language declares has no order for. 18.9 % of the toolchain is Nikaia.

[0.0.382] — 2026-10-03

Where ??’s answer is only read, a value that is a word by its kind is copied and everything else is lent (ADR-274, the owner’s choice, option B.b). A number, a bool, a char, an enum whose variants hold nothing and a type the ledger says copies are copied; a copy type made of parts - a struct, a tuple, an array, an enum with a payload - is lent at every size, as it was until 0.0.376: total(found ?? spare) over a 64-byte Grid is or(found.as_ref(), || &spare) again, not a copy of both sides at every read. No size is computed. The record keeps the options looked at: copy every copy type (A), and three exceptions to lending - the scalars only (B.a), a word by its kind (B.b) and a size the compiler computes (B.c). What 0.0.377 decided about keeping - a copy type handed on is not taken - stays.

[0.0.381] — 2026-10-03

Whether ?? lends a value that copies, prepared for the owner (ADR-274, open). 0.0.377 taught the checker that a copy type a program declares copies, and as a side effect a read position of ?? - total(found ?? spare) - now copies both sides where it lent them before, although ADR-259 D1 says nothing is copied there. Measured in optimised Rust: an 8-byte struct compiles to the same instructions either way, a 512-byte one costs a memcpy per read. The record compares Rust, C#, Swift, C++ and Kotlin, weighs copying, lending and a size line, and proposes lending all but the language’s own scalars. Nothing in the compiler changes until it is decided.

[0.0.380] — 2026-10-03

A map of T? values reads one T?, and writing null stores it (ADR-273, the owner’s decision on the question open-decisions.md held). Flat, as in Kotlin, TypeScript and Dart, where the nullable cannot nest - which Part I 2.3 already says of this language. m[k] and m.get(k) on a map of T? are one T?: a key that is absent and a key that holds null both answer null, and contains_key tells them apart (D1). m[k] = null stores null and remove removes - not Swift’s = nil (D2). The read stays a view, Option<&T> below, through the new nikaia_std::index::flat (D3). It was an Option<&Option<T>>: a stored null read as present, and m[k] ?? null and a ref T? argument did not compile.

A map’s read kept as a T? of its own (D4), for every map: let k: Kind? = m[1] and kept.push(m[1]) copy the value out where it copies, and are refused (NK1102) with m[k]?.clone() where it does not. Both reached rustc as mismatched types. With them, found writing the record’s tests:

[0.0.379] — 2026-10-03

What a file declares, and whether its use lines may name what they name, is Nikaia (#125, ADR-250). modules’ two questions about one parsed file moved into tools/modules.nika: the names a file declares into its package’s one namespace (ADR-047 D1, ADR-144 D2), now read by the same name_declared the checker’s NK1148 uses, and the four refusals of a use - a path into a package, one name twice, a package no dependency declares or a file alone cannot have, and a file beside this one that its package already sees (ADR-046 D2, D4, D5). Reading the files, the manifest and the ledgers stays Rust. 18.8 % of the toolchain is Nikaia.

The move found one defect, fixed in the compiler (ADR-250 D3): text a call made, moved whole into a tuple or a list, was counted by the keep walk (ADR-209) as a view of it kept outside the loop - named.push((first, c)) put c into the frame’s keep and handed the tuple a reference its String refused. A struct literal already moved it.

[0.0.378] — 2026-10-03

A bound on a list’s values, measured and proposed (ADR-272, open). ADR-271 §6’s next step, overflow checks dropped by the same proof, was measured before it was built, and buys nothing: the checks the walk proves are the ones LLVM already drops. The watch kernel’s +0.7 % with overflow checks on is one addition, start + at, where start is read out of a list - not 2 * v + 1 as solver-workload.md §8.1 had it, now corrected. Removing from the lowered Rust exactly the checks a bound on a list’s values would prove takes the kernel from 10.44 M to 9.68 M instructions (−9.4 % against Rust by hand), and with a filling loop’s length to 9.41 M (§8.2). ADR-272 compares how Wuffs, Ada, SPARK, Pascal, Liquid Haskell, Dafny, Astrée, Clousot, LLVM and the JITs answer the same question, weighs four options, and proposes the bound inferred by ADR-271’s walk with a criterion to meet before it is accepted. Nothing in the compiler changed.

[0.0.377] — 2026-10-03

The sharing classes are decided in Nikaia (#125, ADR-250). The union-find over Shared handles, what forced a class atomic, the duplications and the copies out of a lock, the atomic floor for a slot another file owns (Fallback::ForeignFile) and reading every handle’s count, reason and lock off the classes (ADR-037 D7, ADR-238 D2) moved into tools/sharing.nika as a Slots record. The walk over the bodies stays in Rust and fills it. 18.7 % of the toolchain is Nikaia.

The move found seven defects, fixed in the compiler (ADR-250 D3):

What a read of a map of T? values is - a stored null beside an absent key - is a question for the owner (docs/open-decisions.md).

[0.0.376] — 2026-10-03

What a keep asks of a type, a method and a ledger is Nikaia (#125, ADR-250). The questions contracts::keep (ADR-209) asks outside its walk moved into tools/keep.nika: which structs of a unit hold a view, and why a value of a type cannot go into a handle (ADR-221 D3); the struct of views a written type names; whether a ledger type holds a view; the methods that keep what they are given, drop entries, hand back what they remove or hand back a value of their own; whether a contract takes a keep and the key of a method that does; the name a callee is written as and the local an expression is rooted in. The walk that follows views by origin and the plan it decides stay in Rust, keyed by statement numbers this language has no usize for. 18.3 % of the toolchain is Nikaia.

[0.0.375] — 2026-10-03

The sharing records and the --sharing report are Nikaia (#125, ADR-250). The closed list of reasons a count stays atomic (Fallback, ADR-037 D8) with each one’s remedy, what one Shared value got (Decision) and all of them (Sharing), turning every count plain where nothing can cross (ADR-061 D2), the report’s text, the slot keys, what holds a Shared or a word, the sentences a decision is explained with, and what Shared[T] and Locked[T] lower to moved into tools/sharing.nika. The union-find over handles and the walk of the bodies stay in Rust for now. 17.9 % of the toolchain is Nikaia.

The move found three defects, fixed in the compiler (ADR-250 D3):

std’s ledger describes BTreeMap::keys.

[0.0.374] — 2026-10-03

The view states are solved in Nikaia (#125, ADR-250). The analysis half of contracts::tether (ADR-008 D2, D7) - the states one function’s views solve to (receiver, parameters and result that carry a view, all Borrowed until keep::infer says a view leaves), whether a pub rule’s result may point into the text it parsed, and whether an expression makes a buffer of its own (to_owned, the text form of a literal or a number, a call whose ledger entry hands back owned text or bytes) - moved into tools/tether.nika. Writing the states into the ledger, the --tethers report and the refusal stay in Rust beside the keep plan they read. 17.4 % of the toolchain is Nikaia, by scripts/self_hosting.py over today’s larger compiler.

[0.0.373] — 2026-10-03

The lowering closes the solver kernels’ gap to Rust by hand, piece by piece, and an index check a proof shows unneeded is not emitted (ADR-271). With link-time optimisation, as Part III 13.3 names it for throughput, the lowered Nikaia now retires fewer instructions than the Rust by hand on all three kernels; with --optimization=remove-bounds-checks:aggressive the big-integer kernel retires 34 % fewer. docs/solver-workload.md §8.1 has the attribution and the tables.

[0.0.372] — 2026-10-02

The solver’s kernels, lowered from Nikaia, against Rust by hand (ADR-270 D8 step 1). benches/solver-kernels.nika holds three kernels a CDCL(T) solver lives in - combining sparse rows, scanning watch lists, multiplying big integers - and benches/solver-workload/kernels.rs the same algorithms in Rust; the_solver_kernels_lowered_against_rust_by_hand (ignored, callgrind) checks both print the same checksums and counts their instructions.

[0.0.371] — 2026-10-02

The assert and solver records, consolidated; the solver is written in Nikaia. No code changes.

[0.0.370] — 2026-10-02

ADR-268: the search is free; what a build uses is a committed proof file. By the owner’s decision the solver’s search no longer has to be deterministic - it may use time limits, race engines, share clauses at any moment and keep local caches - on the condition that what one machine proved is never rejected by CI or another machine. No code changes.

[0.0.369] — 2026-10-02

ADR-267: the solver’s architecture, decided before its algorithms. The goal is a universal solver that wins SMT-LIB’s QF_LIA against z3 and cvc5 and then widens; ADR-265 D4 (deterministic, a budget in work) and a small checker are fixed conditions. No code changes.

[0.0.368] — 2026-10-02

The solver splits where a branch needs it (ADR-265 D4, D5). It took a query’s disjunctions apart up front, every combination a case: nine != - nine guards return … if x == k - were 512 cases, past the budget of 256, and the claim stayed checked at run time.

[0.0.367] — 2026-10-02

The solver’s numbers are i64 (ADR-265 D2, D8). nikaia-logic held its constants, coefficients, certificate multipliers and model values in an i128, a width Nikaia does not have (ADR-252 keeps it out). They are i64 now, so a port to Nikaia needs no new number type. A step that leaves i64 answers unknown (overflow), never a wrong answer; a program literal no i64 holds is not read, and its claim stays checked at run time. ADR-265 D2 records the rule and names the way further: exact integers of any size built from u32 pieces, not a wider type.

[0.0.366] — 2026-10-02

A proof as Alethe (ADR-265 D6). nikaia_logic::alethe::write writes the reference solver’s proof of a query in Alethe, for a checker this crate does not trust. Each assertion is taken apart into clauses of comparison literals with Alethe’s Boolean rules (and, not_or, and_pos, and_neg, or_pos, or_neg, not_not; a negated equality through la_disequality); every choice of one literal per clause is refuted by the solver, its certificate becoming la_generic lemmas - a bound read off a literal, a combination, an integer tightening, the final c <= 0; and a resolution tree folds the choices back to (cl).

[0.0.365] — 2026-10-02

A contract crosses the package boundary (ADR-266 D4-D7).

[0.0.364] — 2026-10-02

The --overlaps report is Nikaia (#125, ADR-250). What every overlap { … } block was allowed (ADR-050 D3) - under the function that writes it, the header read off the pairs (the first two branches that may not run together, and why), and each branch with the half of D6 it falls in - moved into tools/order.nika, so contracts::order holds nothing but adapters now. The blocks a body holds are still found by the emitter’s walk, and whether a branch pauses is still its answer. tests/overlap.rs holds the report’s text, which nothing did. 18.1 % of the toolchain is Nikaia.

[0.0.363] — 2026-10-02

A statement is reduced to an operation in Nikaia (#125, ADR-250). The walk half of contracts::order (ADR-033) - a let of one name or a bare expression statement, looked through a catch whose handler cannot divert (ADR-034), taken apart into the calls it performs, and each call answered from the ledgers: what it touches, an unknown kind, a failure nobody catches, a result that may not cross back from the closure overlapping builds - moved into tools/order.nika beside the verdicts. 17.9 % of the toolchain is Nikaia.

Found moving it, fixed in the compiler (ADR-250 D3): a ?? fallback that jumps takes its takings with it (ADR-213 D4), as a branch that leaves does. let c = f(k) ?? return Refused(k) hands k over only on the path that leaves, and a use of k on the next line was NK2105. tests/defects_found_running.rs holds it.

[0.0.362] — 2026-10-02

Whether statements keep their order is decided in Nikaia (#125, ADR-250). The verdict half of contracts::order (ADR-033) - a statement reduced to an Operation or the reason it could not be (Accounted, with the sentence a report prints), and whether two operations, or a whole run of them, may overlap (verdict, group_verdict, group_of: a data dependency, a shadowed name, two touch sets meeting on a write, two names for what may be one destination) - moved into tools/order.nika. The walk that reduces a statement stays in Rust for now. 17.3 % of the toolchain is Nikaia.

[0.0.361] — 2026-10-02

Whether a value may cross a thread is Nikaia (#125, ADR-250). The verdict NK2501, NK2502 and NK2503 stand on - plain data, containers answered by what they hold, the lock family whose answer is the destination’s (ADR-045 D2, D3; a shared count into code nothing describes, ADR-061 D1), a described type’s own word or its fields walked transitively (Group B), and which of a task’s bindings are held across a pause (ADR-055 D6) - moved from contracts::send into tools/threads.nika. 17.0 % of the toolchain is Nikaia.

Found moving it, fixed in the compiler (ADR-250 D3): BTreeSet::remove is described. Nothing was, so a walk that marks a name and takes the mark off again was taken to pause and lowered async. tests/defects_found_running.rs holds it.

The refused part and its field are answered one at a time (refused_part, refused_at): a nullable pair is not a type Part I 2.3 writes, and the parser says so on purpose.

[0.0.360] — 2026-10-02

A claim is carried back to the entry and becomes a precondition (ADR-266 D2, D3, D8 within a package). The prover keeps each whole number’s value at the function’s entry - y after let y = x - 1 is x - 1, and x after let x = x + 1 the parameter plus one - and the branches and guards on the way. A claim that then reads only the parameters is a precondition, path → claim, and its callers prove it: let x = x + 1; assert(x > 3) asks each call for x > 2, and g(3) leaves no check behind.

[0.0.359] — 2026-10-02

A claim the facts rule out is a warning with values (ADR-264 D8; step 3 of ADR-265 D8). The solver answers refuted with a model - found by back-substitution through its eliminations, each value as near to zero as its bounds allow, and checked by evaluating the query. The prover warns, NK1207, where it proves, with a checked certificate, that what is known before a claim rules it out: the claim is false every time it is reached, and a model of what is known gives its names’ values. The same at a call that breaks its callee’s precondition every time, with the parameter’s value as the call gives it. The claim stays a check; --asserts says why, with the values.

[0.0.358] — 2026-10-02

Two of the compiler’s messages are Nikaia (#125, ADR-250). A backend mismatch at a boundary said as the stale ledger it is (ADR-100 D6, ADR-237 D2) - which package or described crate to name, and what the message and its notes become - moved into tools/boundaries.nika; NK2202, a sync promise something in the body breaks, into tools/render.nika beside the layout it is written in. 16.7 % of the toolchain is Nikaia.

Found moving it, fixed in the compiler (ADR-250 D3): str::splitn is described. Nothing was, so a function that split a name once was taken to pause and lowered async; its count is an i64 the lowering converts, as chunks’ is (ADR-212 D5). tests/defects_found_running.rs holds it.

[0.0.357] — 2026-10-02

A proof comes with a certificate, and the prover checks it (ADR-265 D5, D6; step 2 of D8). Fourier-Motzkin hands back, for every case of a proved query, the derivation of its contradiction - the case’s own bounds, positive combinations, and integer tightening - kept to the steps it needs. nikaia_logic::verify computes the query’s cases itself, so a certificate cannot leave one out, and replays each step. The prover checks every certificate before it leaves a check out; one that does not check keeps the check, and --asserts names it as a fault of the compiler.

[0.0.356] — 2026-10-02

A finding’s layout is Nikaia (#125, ADR-250). The shape every message a reader gets has - the headline, --> file:line:column, the lines with each named place underlined (^ where the error is, - where something explains it, ... between lines that are far apart), then the notes and the help as sentences - moved from the compiler’s diagnostics into tools/render.nika. A span is in bytes and a column in characters, so the mapping between them is written there too; tests/diagnostics.rs holds it on a line with ü before the place. 16.6 % of the toolchain is Nikaia.

[0.0.355] — 2026-10-02

views is Nikaia (#125, ADR-250). Where a naked view parameter’s view ends up - a field of the subject, a struct the function builds, its result, a task - and so whether it is refused (NK2302) or written as a view of the buffer the subject or a struct parameter names (ADR-008 D1) moved into tools/views.nika. Whether a let’s value is a buffer of its own is still contracts::tether’s answer, handed in. 16.3 % of the toolchain is Nikaia, up from 14.5 %.

Found moving it, each fixed in the compiler (ADR-250 D3), each held by a test (tests/defects_found_running.rs, tests/sysroot.rs, tests/modules.rs):

The variant a result is stored in is Destination::Returned: one named Result made rustc print std::result::Result in full, and a stale description’s Result mismatch was no longer recognised as one.

[0.0.354] — 2026-10-02

types is Nikaia (#125, ADR-250). The check that a file declares a name once (NK1148), that a bound names a trait (NK1135) and that every written type is one something declares (NK1135, NK1158 for a &mut away from C, NK1182 for an Array[T] with no length outside a parameter) moved into tools/types.nika. What the file declares as types and what an impl’s target makes a type parameter are contracts’ answers, handed in. 14.5 % of the toolchain is Nikaia, up from 13.8 %.

Found moving it, each fixed in the compiler (ADR-250 D3), each held by tests/defects_found_running.rs:

[0.0.353] — 2026-10-02

The prover asks nikaia-logic (ADR-265 D1-D4, step 1 of D8). A crate with no dependencies now holds the logic layer: terms by u32 id in an arena (TermId, Arena), a self-contained Query of facts and a goal, an Answer of proved or unknown with the reason, a Budget counted in cases and bounds, the Solver trait, and Fourier-Motzkin as the reference solver. prove.rs is the frontend: it walks the program and builds terms, and asks one query per claim. What a program sees is unchanged, and the suite holds it.

[0.0.352] — 2026-10-02

ADR-266: a written assert is a published contract. The owner’s answer to what crosses a package boundary: no clauses in a signature - the assert the author writes is the promise, and a guard is only an observation, read as absent by another package. A claim is placed by computing it back to the entry (weakest precondition: a let substitutes, an if becomes an implication) or forward to every exit, so a claim after let y = x - 1 is a precondition about x. The ledger gains requires, ensures, inferred and from; a contract that changes in the breaking direction, decided by the solver against the committed ledger, warns its author; a function with a precondition gets a checked and an unchecked entry. Amends ADR-264 D5. Not built.

[0.0.351] — 2026-10-02

ADR-265: the prover is a frontend, a logic and a solver. The owner asked for a layer between the prover and its solver, so that the solver can serve other problems and languages, and for a check that nothing in it stands in the way of the plan: a solver written in Nikaia that outdoes others on parallelism and performance. The record cuts prove.rs into the walk over the tree and a dependency-free nikaia-logic crate, and fixes the promises a later solver needs: terms by id in an immutable arena, self-contained queries without SMT-LIB’s assertion stack, answers that depend on a budget counted in work and never on time or threads, certificates so that a fast solver need not be trusted, and SMT-LIB 2 only as an export and import. Within a query, parallelism splits into deterministic cubes or shares learned facts at epochs fixed by work counts; a certificate is per theory and small, with Alethe as an export. Not built.

[0.0.350] — 2026-10-02

What changed is said in Nikaia (#125, ADR-250). project’s changed_lines - the lines of a ledger a --locked build or a stale check finds different from the committed one, each under its entry - and the line half of difference - what an output test prints between the output it expected and the one it got, two lines of context around each run - moved into tools/diffs.nika; whether the bytes are text at all is still asked in Rust. tests/diffs.rs holds both, which nothing tested directly before. 13.9 % of the toolchain is Nikaia, up from 13.7 %.

[0.0.349] — 2026-10-02

A claim the compiler cannot prove is checked when the program runs (ADR-264 D4-D7). prove.rs no longer refuses an assert it cannot prove: the claim is held AtRunTime with the reason, and the emitter writes the check it already wrote for a test’s claim. NK1202 is left to D6 - a claim about data from outside the program, which a guard checks instead.

[0.0.348] — 2026-10-02

An assert is a contract: proved where the compiler can, checked where it cannot (ADR-264). The two records on test and assert are withdrawn and replaced by one. The first made an assert a claim the compiler proves, refutes or checks at run time; the second took the run-time row away outside a test, so a claim the prover could not read refused the program. With a prover that reads linear whole-number arithmetic and nothing else, that kept every invariant about fields, matches, lists and calls unwritable - including the ones the compiler’s own Rust states with unreachable!, which self-hosting has to carry into Nikaia as something. ADR-264 keeps everything else of both: test blocks, nikaia test, output tests, the pure condition, preconditions, the refusal of a claim about data from outside the program (a guard’s job), and the prover. What changes is D4: a claim that is not proved is checked where it is reached, and stops the program when false; a proof only removes the check. --asserts (D7) is how a reader finds the claims still checked.

[0.0.347] — 2026-10-02

A parse error’s reading is Nikaia, the text half (#125, ADR-250). What the parser’s better readings look back at - the token before the caret, the quote, the bracket and the block comment never closed - and the notes a refusal gets from what a reader sees on its line (a ??’s fallback, a let taking apart a nested tuple, & for ref, an effect on a lambda), with the split of a written message into its headline and the rest, moved into tools/parse_notes.nika. It answers in characters, and the compiler turns a position into the byte a span counts (tests/parse_error.rs holds an unclosed quote after umlauts). 13.7 % of the toolchain is Nikaia, up from 13.3 %.

[0.0.346] — 2026-10-02

How a message is set down is Nikaia (#125, ADR-250). diagnostics’ said - a message as a sentence, capital and full stop (Part III C.2) -, headed - error[NK1102] - and word_in - the column a finding’s word stands at on its line, and how wide it is - moved into tools/rustc_words.nika beside the rewriting of rustc’s words. Every message the suite renders, the error corpus’s among them, is what it was. 13.3 % of the toolchain is Nikaia, up from 13.2 %.

[0.0.345] — 2026-10-02

A rustc message is put in the program’s words by Nikaia (#125, ADR-250). What diagnostics does to a message the language below wrote about the generated Rust - Rc<T> and Arc<T> back to Shared[T] with the brackets matched, the lock’s two shapes back to Locked, a trusted map back to HashMap, Option::map back to the ?. the program wrote (ADR-056 D1, ADR-057, ADR-189), the note that names something a .nika file cannot reach, and the expected A, found A that marks this compiler’s own defect (D2) - moved into tools/rustc_words.nika. tests/diagnostics.rs holds a nested hull, a name that only ends in Rc<, and the lock and map. 13.2 % of the toolchain is Nikaia, up from 13.0 %.

[0.0.344] — 2026-10-02

The specification’s blocks are read in Nikaia (#125, ADR-250). The text half of specbook - a page’s ```nika blocks, whether the page is discussing a refusal in one, whether an elision stands where code would, the programs a block can be read as, and the line a report names it by - moved into tools/specbook.nika. Reading the pages and taking each reading through the compiler stay Rust. 13.0 % of the toolchain is Nikaia, up from 12.7 %.

[0.0.343] — 2026-10-02

sync reads a body off the walk in Nikaia (#125, ADR-250). Both of contracts::sync’s walks - what a body reaches for the inference, and what breaks a promise for the check - read the list tools/foreign.nika’s walk hands back, and resolve each call by name with calls::callee_named. The walk says where an overlap and a select stand (Seen::Joins, which join on the executor and so pause, ADR-163 D1) and gives a spawn and an Opaque their statement’s span, and it says what an expression is before the holes inside it, which is the order a refusal names the first pause in. joins_on_the_executor and called are gone with the walks: seven readers of the tree share one walk now. Every ledger and every refusal the suite meets is what it was.

[0.0.342] — 2026-10-02

What a body touches is read off the walk in Nikaia (#125, ADR-250). contracts::touch’s own walk is gone: it reads the calls of a body off tools/foreign.nika’s walk, which now says where a spawn, a dsl statement or a call of something that is not a name stands (Seen::Opaque, Seen::Spawn), and resolves each call by name with calls::callee_named - callee_of with the expression already read, the one resolution every analysis shares (ADR-028). Six readers of the tree share one walk now, and foreign.nika’s copy of calls.nika’s written_path is gone. Every ledger the suite writes is what it was.

[0.0.341] — 2026-10-02

What a body throws is walked in Nikaia (#125, ADR-250). contracts::throws’ walk - what a function names in a throw, whom in its package it calls, and what a catch that does not pass its error on keeps from it (Part I 7.1, ADR-157 D2) - moved into tools/throws.nika beside the fixpoint it feeds (0.0.321). What a call resolves to stays the compiler’s single answer (ADR-028), handed in per expression. The set of expressions the Rust kept by address to say caught is a mode the walk carries into a guarded half, and the handler blocks it kept by address are the ones a catch hands its handler to, which is the only place a handler stands. Every ledger the suite writes is what it was. 12.7 % of the toolchain is Nikaia, up from 12.2 %.

[0.0.340] — 2026-10-02

Every name a statement mentions is Nikaia (#125, ADR-250). contracts::send’s names_used_in_stmt - which of a task body’s names are held across a pause (ADR-055 §2 D6) - asks tools/names.nika’s names_in_statement, which names_in_block is now made of, instead of walking the statement with sync’s visitor and asking names_in of every expression it met, each one walked again inside the last.

[0.0.339] — 2026-10-02

What a body keeps is asked of the walk in Nikaia (#125, ADR-250). The walk of tools/foreign.nika names every bare name and every spawn it meets where it is asked to (with_names; a build’s every-file walk does not pay for them), and starts from one expression as well as from a block or a program. contracts::keep’s and contracts::keeps’ own walks of names are gone: five readers of the tree share one walk now. Every ledger the suite writes is what it was.

[0.0.338] — 2026-10-02

Every name an expression mentions is Nikaia (#125, ADR-250). contracts::order’s names_in and names_in_block - what decides whether two statements may change places (ADR-033 D9), and what contracts::send asks of a task’s body - moved into tools/names.nika, whole: still total, with an arm for every variant of the tree and no else, which Nikaia’s NK1151 holds the way Rust’s exhaustiveness held the Rust. Every ledger and every refusal the suite meets is what it was. 12.2 % of the toolchain is Nikaia, up from 11.8 %.

[0.0.337] — 2026-10-02

locks reads a body with the walk in Nikaia (#125, ADR-250). The walk of tools/foreign.nika takes one block as well as a whole program (seen_in), and contracts::locks reads the free calls of each function’s body and each grammar action off it - its own walk and free_call are gone. Three readers of the tree now share one walk (foreign, --trust, locks); every ledger the suite writes is what it was.

[0.0.336] — 2026-10-02

--trust reads the program with the walk in Nikaia (#125, ADR-250). The walk of tools/foreign.nika meets calls as well as paths now (Seen), and for a call by name it says what the call hands a parameter called root where the callee has one. contracts::trust reads its reasons and its roots off that list, and its own two walks - sync::walk_calls and roots_of - are gone: one walk of the tree, two readers. 11.8 % of the toolchain is Nikaia, up from 11.7 %.

Found moving it, each fixed where it was (ADR-250 D3):

[0.0.335] — 2026-10-02

dsl is Nikaia (#125, ADR-250). The rest of the compiler’s dsl moved into tools/dsl.nika beside the scan of a body that went first (0.0.248): whether a statement’s holes are deferred, the shadow type’s name, the shadow types a program needs, the functions that take a statement’s parameters, and the check of every call that hands them over (NK1109, NK1112, NK1113). The compiler’s dsl is the calls. 11.7 % of the toolchain is Nikaia, up from 11.1 %.

Found moving it, each fixed where it was (ADR-250 D3), and held by tests/defects_found_running.rs:

tests/range_spelling.rs reads only a for head for the old range spelling: a message that says in and names ...args is text.

[0.0.334] — 2026-10-02

The first walk of the tree in Nikaia (#125, ADR-250). foreign’s walk - every qualified name a unit writes, for NK2504/NK2505 and for nikaia describe - moved into tools/foreign.nika: every item, statement and expression, read the way contracts::sync’s walk reads them, a hole of a literal included. What a literal’s holes are and what a file’s alias stands for are the compiler’s to answer, handed in as two functions (holes_of, unaliased), because a template’s holes are parsed out of its text. 11.1 % of the toolchain is Nikaia, up from 10.6 %.

[0.0.333] — 2026-10-02

A signature is written in the ledger’s spelling by Nikaia (#125, ADR-250). contracts::spelling moved into tools/ledger.nika as spell, beside spelled_signature that reads the spelling back, so both directions of ADR-251 D4 are one file of Nikaia; the Rust module is gone. Every ledger the suite writes is byte for byte what it was. 10.6 % of the toolchain is Nikaia (scripts/self_hosting.py), up from 10.2 %.

[0.0.332] — 2026-10-02

A caret on a token that is not a word stays where the parser stopped. The renderer looked for the token found as a whole word, and asked a } or a space for word edges it cannot have; failing that, it took the first match on the line, which put the caret of f"x } y"’s refusal in column 1. Word edges are now asked only where the token begins or ends with a word character, and the stray } of an f"…" is refused on the brace itself rather than after it.

[0.0.331] — 2026-10-01

The emitter builds an f"…" from its parts (#107, ADR-262 D5). The format string is the text runs as written and {} or {:spec} per hole (emit::format_of); the body as written is gone from the tree (Expr::LitInterpolated { parts }), and with it emit::interpolation, the scanner that found holes in text. A hole carries the byte its { stands at, which is what the tier pass’s wraps are keyed by now - per hole, where the hole’s text was shared by every hole that read the same. The Rust emitted for benches/template.nika, page.nika, jumps.nika, awaits.nika and examples/1brc.nika is byte for byte what it was, so they measure the same.

[0.0.330] — 2026-10-01

The grammar parses the holes of an f"…" (#107, ADR-262 D1-D4). f"…" is a rule of text runs and holes, and a hole is the ordinary expression rule; the tree holds the parts (Expr::LitInterpolated { text, parts }, an FPart each), so the checker, the emitter, the tier pass and the build-time evaluator read the holes the grammar parsed rather than parsing the text again. Every walk sees a hole as a child of its literal - which a walk written in Nikaia needs, having no parser of its own.

[0.0.329] — 2026-10-01

A file read stops doing work nobody asked for (#320, ADR-263 D3). The ring’s read buffer is offered to the kernel as capacity instead of being zeroed first - a memset of the whole file the kernel then overwrote - and its length grows by what each completion reports (file-ring’s README lists the one set_len that takes). fs::read_to_string, read, write, map and the root check write a path into an error’s text only when there is an error, where every call used to. A program that never overlaps pays about 2,500-2,600 instructions a read for the runtime’s I/O, down from 3,100-3,500 (docs/history/runtime-cost.md §7.3). What is left is the hand-off itself, which ADR-263 D1 is measured against next.

[0.0.328] — 2026-10-01

Two decisions, recorded.

ADR-262: the grammar parses the holes of an f"…" (#107). A hole was found in the literal’s text after parsing and parsed again on its own - which a walk written in Nikaia cannot do, so every walk that must see a hole stayed Rust. f"…" becomes a rule of text and holes, a hole an ordinary expression; the tree holds the parts; a hole is written as code is written, f"{emphasised("Ada")}", and \" is refused there; a broken hole is a parse error at its place; the emitter builds from the parts once it measures the same. Takes ADR-032 §3’s option (a), deferred there for now.

ADR-263: a file operation on an idle runtime runs on the calling thread (#106). Go and Java’s virtual threads do the syscall on the calling thread; the pool runtimes hand off every time, which is what Nikaia did. A regular-file operation with nothing else in flight - no other task, no outstanding operation, no timer - is a blocking syscall where it was asked, decided by std and not by the compiler or a setting, since what overlaps is already written in the source. First the normal path stops doing work it does not need, and the rule enters std only if it then measures.

Where a read’s 3,321 extra instructions go (docs/history/runtime-cost.md §7.2): the ring ~1,030, allocation and copying ~830 - the read’s buffer is zeroed before the kernel fills it - the executor ~450, and ~425 for the text of an error fs::read_to_string writes on every call and reports on none.

[0.0.327] — 2026-10-01

What being async costs a program that never overlaps is measured (#106). benches/awaits.nika reads a small file n times, one read after the other, three .awaits deep, and measure.rs holds it against the blocking lowering ADR-055 replaced and against the futures alone (docs/history/runtime-cost.md §7). An .await costs nothing measurable - 3 to 25 instructions a read at user_parallelism = no. The read’s road through the runtime’s I/O costs about 3,100 to 3,500 instructions a read more than a read(2) on the caller’s thread, at both settings. Whether a read with nothing else in flight should take the caller’s thread is left open, with its number.

[0.0.326] — 2026-10-01

The emitted code writes no unsafe for a view (#84, ADR-218 D4’s last step). A value a task takes with it was packed beside a clone of the task’s keep, its views stretched to 'static through tether::forever inside unsafe { … } on the promise that nothing derived outlived the handle. It is a Holding over the task’s keep now: built first, then carried in by Rebase, which finds every view again in the keep by its address - so the promise is checked rather than trusted. A buffer that may end in a task’s keep, or in a keep a caller hands in, is put with put_viewed, so the views are found rather than copied. tether::forever is gone; the emitted code writes unsafe only around a call into foreign code (ADR-124 D3).

Rebase reaches every shape a task takes: a map, a set, a tuple, a Box, and a number, a bool or owned text as they are. A task that took a HashMap[ref String, i64] would otherwise be a file rustc refused.

[0.0.325] — 2026-10-01

What a call resolves to is answered in Nikaia (#125). tools/calls.nika holds sync::reached, the one rule sync, throws and touches resolve a call by (ADR-028): this package’s functions first, then the anonymous constructor’s key, then the library by both names; a variant of a type a ledger knows or this file declares builds a value and is no call; anything else is a call nobody can name. The walk that hands it each expression stays Rust: it reads the holes of a text literal, which only the parser can.

Moving it found three things the compiler did wrong, each fixed in the compiler (ADR-250 D3):

[0.0.324] — 2026-10-01

What an operation reaches is decided in Nikaia (#125). tools/touch.nika holds the two decisions of contracts::touch:

Reached is the Nikaia record now, and contracts::order compares two calls with its methods. Reading a touches entry, the list of kinds and the walk stay Rust, the walk with sync’s. Nothing a program is told changed; the move compiled as written once the language’s own rule was followed - self.named ?? … on a borrowed self is NK2106, and self.named?.clone() is what it asks for.

[0.0.323] — 2026-10-01

A list with a null in it is a list of T? (#312). let xs: Vec[i64?] = [null, null, 7] passed the checker and reached rustc as vec![None, None, 7]; the elements beside a null meet it as the arms of a choice do, and the 7 is Some(7) below. And x ?? -1 over an element a loop binds - a view of an i64? - reads the copy, as a view of any copy value does: no Or below took a &Option<i64>. A text literal beside a null where the list is declared Vec[String?] is still a view where text of its own is wanted, as it is in the arms of an if.

[0.0.322] — 2026-10-01

Which functions can open a lock is settled in Nikaia (#125). tools/locks.nika holds the least fixpoint of contracts::locks, as throws.nika holds throws’: a function that reaches a holder holds, a variant is a value and opens no door, a callee no ledger describes is the doubt it is, and a published function field stays undecided because packages that do not exist yet store into it (ADR-230 D1). The walk stays Rust with sync’s. Nothing a program is told changed.

Moving it found two things the compiler wrote wrong, each fixed in the compiler (ADR-250 D3):

Writing the test found a third, filed as #312: a list of optional elements, [null, 7] as Vec[i64?], writes 7 unwrapped.

[0.0.321] — 2026-10-01

Which errors leave a function is settled in Nikaia (#125). tools/throws.nika holds the least fixpoint of contracts::throws - every function starts with what it throws itself and grows by what its callees throw, a callee the walk did not see still bringing the set the ledger gives it (ADR-173 D3) - and what a throw names: io::IoError::NotFound is io::IoError, ConfigError::BadSyntax { line } is ConfigError. The walk that finds what each function throws and whom it calls stays Rust: it is sync’s walk (ADR-028), and it moves with it. Nothing a program is told changed, and the throws tests are unchanged. The move compiled as written - the first ring that found nothing in the compiler.

[0.0.320] — 2026-10-01

The trait check is Nikaia (#125). What traits.rs checked - a method of an impl Trait for T the trait does not declare, and one it declares that the impl leaves out (NK1130), a receiver written differently from the declaration’s (NK1196), and a method that can pause or fail where the trait says it does not (NK1129, NK1140) - is tools/traits.nika, and traits.rs is the adapter that hands it the tree and the ledger. What a check of the toolchain reports is one record, Finding in tools/findings.nika: a code, a span, a message, notes, a help and whether it is a warning - the shape the next rings report in too. Rust reads it into its own Finding. The messages are the same words, so the trait tests are unchanged.

Moving it found four things the compiler did wrong, each fixed in the compiler (ADR-250 D3):

The ceiling of unanswered method calls in the corpus is 48, from 50: a tool is read there with the Rust it reads described, as lower-std reads it.

[0.0.319] — 2026-10-01

A name bound to a map read is read as one (#297). let found = calls.get(key) holds a view of what the map keeps, exactly as calls[key] does, and it is now typed and refused as one: found ?? [] is NK1185 with found?.clone() ?? … handed over - where it reached rustc as the trait bound &Vec<String>: From<Vec<_>> is not satisfied - and found?.clone() copies the list rather than the view. m.get(k) is a map read wherever it stands, as m[k] was. And a map’s text beside a name that is a view of text, imports.get(call) ?? call, is a view whichever side answers, written as it is: it was an .into() rustc could not infer. The last line of tools/surface.nika written around it is written the language’s way now (ADR-250 D3).

[0.0.318] — 2026-10-01

The toolchain’s Nikaia is one package (ADR-261). Every .nika in crates/nikaia-std/src/tools/ shares one namespace, as the files of a package do, and nikaia lower-std checks and lowers them together into one file, src/tools/package.rs, where seventeen .rs files stood. A tool names another tool’s function or type with nothing in between: the BESIDE table is gone, and with it the copies it forced - one slice, one find_text, one split_top_level, one Refused where three files each declared the same one. The names two files both declared are renamed once (rust.nika’s Item is RustItem, manifest.nika’s Shape is DependencyShape, and five more). One module per file at the end of package.rs re-exports what that file offers, so a Rust caller still writes nikaia_std::tools::ty::Ty.

An error another file of a package declares is the package’s own. A function that throws it travelled as Box<dyn Error> below, where one written beside its impl Error travelled in its envelope - found by the package above, and as true of a program of several files.

ADR-260’s status says what was built (0.0.298-0.0.300).

[0.0.317] — 2026-10-01

A guard reads what a box holds (ADR-246 D5 item 2, #97). The arm opens a part that holds its own type at its head, and a guard runs before that:

NK1193 is left for a pattern that looks inside a box inside a boxed part, and for one that does so in one alternative of an |.

[0.0.316] — 2026-10-01

nikaia describe writes its entries and notes in Nikaia (ADR-104 D3, ADR-193 D3-D5, #124). A function’s entry from its signature is signature::contract; a type’s entry from its fields and derives is crossing::type_entry; the notes on the crate’s promises and on a function that reaches a thread are crossing::promise_lines and crossing::thread_lines. describe.rs keeps the driving: the manifest, the files, the grammar into the surface, and the file it writes. A draft says what it said before, word for word.

[0.0.315] — 2026-10-01

nikaia describe reads what a crate offers in Nikaia (ADR-104 D2-D4, ADR-193 D3-D5, #124). tools/surface.nika holds the crate’s table: every fn by the path it is defined at, which modules are pub, what a pub use carries out of a private one - a glob too - every pub type with its fields and derives, and every unsafe impl. It reads the Rust grammar’s items and the path work beside it. describe.rs keeps driving: the manifest, the files, and turning the table into a draft. A draft says what it said before, and a glob re-export has a test of its own now.

Which tool reads which module beside it is one table (sysroot.rs’s BESIDE): a tool named there reads exactly its list, and every other tool reads the tree, because a package is one namespace and the grammar’s Item is not the tree’s.

Walking a set does not pause (#302). A for over a collections::BTreeSet[String] or a HashSet bound a name of unknown type; its .clone() resolved to nothing and read as a call that may pause, and the function and every caller of it became async. A set’s element is its one argument now, and every check on the binding sees it.

[0.0.314] — 2026-10-01

nikaia describe reads a Rust signature in Nikaia (ADR-104 D3-D4, ADR-193 D4, #124). tools/signature.nika holds one Rust type in the ledger’s words and whether a value of it is kept, what a Result throws, and which parameters a Send bound reaches. It reads the ledger’s records for Ty. A draft says what it said before; what is left in describe.rs is the crate’s table of what it offers and the drafting around it.

A name a for lends over a list of text compares with text of its own (#300). for one in words { if one == named { … } } for a named: String compared a &String with a String below - rustc about a file nobody wrote. The lent binding is read there now, as a lent number already was.

[0.0.313] — 2026-10-01

An Array written without its length is told the length (NK1166). comptime WORDS: Array[ref String] = ["a", "b"] was answered with make it an Array[ref String] - the type it already said. It says now that the Array has no length, which a comptime needs, and hands over Array[ref String, 2], the length the build computed.

[0.0.312] — 2026-10-01

A for over a T? is refused (NK1206, #296). for word in pick(1) for a pick that hands back a Vec[String]? was checked as a walk over the list and lowered as a walk over the option - once, with the whole list as word - so word.len() counted the list and the program printed a number nobody asked for. A map read is a T? too, so for x in m[k] was the same. The help hands over ?? []; for a map, NK1185 then hands over the copy.

[0.0.311] — 2026-10-01

nikaia describe says what crosses a thread in Nikaia (ADR-193 D3-D5, #124). tools/crossing.nika holds whether a type’s fields let it cross, whether a bound names Send as a word, the scalars that travel unchanged, the list of thread sinks, and the shortest way a function reaches one through the crate’s own calls. It reads the ledger’s records beside it for Crosses, as ledger.nika does. A draft says what it said before. std’s ledger gains char::is_alphanumeric, which the Send word test needs.

[0.0.310] — 2026-10-01

A copy of a view is text of its own, written type or not (#293). let last = current.trim() followed by out.push(last.clone()) read the copy as the view itself, because only a local with String written on it counted as text: current went into the frame’s keep, and a current = "" elsewhere assigned a String where the keep’s place stood - rustc about a file nobody wrote. A local bound from text - a literal, a trim, a copy - is text now, whatever its let wrote.

A kept buffer assigned again goes into its keep too. Where a buffer really is kept - a view of it outlives it - current = text.clone() now puts the new value into the same keep, and the views taken before go on reading the old one.

[0.0.309] — 2026-10-01

nikaia describe spells a crate’s names in Nikaia (ADR-195 D4, #124). tools/paths.nika holds what was string work in describe.rs: the module a file is, a path joined from its parts, what one pub use offers, where its path could resolve, and a split on the commas outside brackets. Lowered to paths.rs by nikaia lower-std; describe keeps the table the answers fill. A draft says what it said before.

Asking a view whether it is empty no longer pauses. std’s ledger had String::is_empty and no str::is_empty, so .is_empty() on a ref String read as a call that could pause and made its function async - and every caller of that function. It has its entry now, sync.

[0.0.308] — 2026-10-01

nikaia describe walks a crate in Nikaia (ADR-195 D4, ADR-196 D4, #124). The Rust half no longer walks: tools/sources.nika reads every .rs under a crate’s src with fs::walk and fs::read_to_string, is lowered by nikaia lower-std to sources.rs beside it, and describe drives it to its end. ADR-196’s sign of a stall - fs has a walk and the Rust half is still doing the walking - no longer holds. What a draft says is unchanged.

[0.0.307] — 2026-10-01

fs gains a directory walk (ADR-195 D4, #124 step 1). fs::walk(path, root) -> Vec[String] hands back every file under a directory, as names relative to it with / between the parts, sorted. Directories are not listed and each is visited once, so a link back up the tree ends the walk. Under fs::Root::Dir the root holds for every name found, not only the one asked for: a symlink that points out of it is left out rather than failing the walk. It throws where the directory cannot be read and where a name is not UTF-8. This is what nikaia describe needs to read a crate’s sources from .nika.

[0.0.306] — 2026-10-01

A pattern looks inside a part that holds its own type (ADR-246 D5 item 1, #97). Expr::Add(Expr::Num(0), b) => simplify(b) was NK1193, with the advice to bind the part and match on it in the arm; it is written as it reads now. The lowering binds the part to a name, asks in the arm’s guard whether it has the shape - so the next arm is tried where it does not, as for any pattern - and takes it apart in the arm. Such an arm covers no variant on its own: a match whose only Add arm looks inside its box is missing a case (NK1151), said here rather than by rustc about the guard. Still refused by name: a pattern that looks inside a box inside that part, one that does so in one alternative of an |, and a guard that reads a name bound in there.

[0.0.305] — 2026-10-01

A build warns where a pub function lost the sync it had (ADR-244 D5, #129), and with it ADR-244 is built (#92). Where the committed ledger says a pub function could not pause and the build finds that it now can, the build says so - NK2211, a warning - at the statement that made it, with the calls that lead there:

warning[NK2211]: `parse` could not pause before, and now it can.
  --> src/main.nika:4:5
 4 |     time::sleep(1.millis())
   = note: `parse` calls `read_section`, which pauses here.

The author learns it at the change, rather than a consumer at an upgrade. Committing the ledger the build wrote acknowledges it, and it is not said again. The sync inference keeps, per function, the first statement that pauses it and finds the shortest way to one through the package’s calls.

[0.0.304] — 2026-10-01

A build names what its pub functions could promise and do not (ADR-244 D2, #128). Since 0.0.303 another package counts only on a written sync, so the author is told where one could be written: nikaia build, run, test and lower end with a note listing the package’s own pub functions that never pause and do not say sync, and those that pause only where the lambdas they are given do and do not say sync(f), each with the word to write. The sync inference keeps a call to a pausing lambda apart from every other pause to find the second list; it still writes no promise itself. Only the entry package is named, once per build and the same on a cache hit.

[0.0.303] — 2026-10-01

Another package’s function is sync where its source says so (ADR-244 D1 and D3, #127). A dependency’s sync = "inferred" is what its body happens to be today, and a change inside it withdraws it; it was read as a promise, so a log line in a library could break callers it never saw. A consumer’s check now reads it as may pause: a sync function, a lock’s door, a par_iter lambda or an action that calls such a function is refused, and told that the callee doesn’t promise that it never pauses - with the word to write in the other package - rather than that it pauses. The lowering still reads the body, so the call is not awaited (D3); only a caller whose sole pausing call is an unpromised one becomes may pause itself. Inside a package nothing changes. Part III 13.5 says so.

[0.0.302] — 2026-10-01

Part III 14 says what assert is now (#104). 14.1 and 14.2 had been rewritten to ADR-245’s function and ADR-256’s proof; what was left said that nothing proves a claim and every row of --asserts reads run time, which the report has not printed since the prover was built. It and --help now name what the report says: proved, a precondition its callers prove, checked by a test, or refused. And the explaining switches are written as switches of nikaia build and nikaia lower rather than as nikaia --trust, which stopped being a command line in 0.0.299.

[0.0.301] — 2026-10-01

[0.0.300] — 2026-10-01

nikaia run f.nika compiles and runs one file (ADR-260 D4, #132). A file outside any project is built as a project of its own, kept in the user’s cache directory under a name made from the file’s path, through the same Cargo workspace nikaia build makes; then the program starts. The interpreter is not what run uses: it handles a part of the language, and a program means one thing (Part I 1.2). The file is lowered where it is before anything is copied, so a refusal names the file the user wrote and not the kept copy, and the copy is rewritten only when the file changed. A file inside a project has to be that project’s entry point, or is refused with the entry and both ways out named. nikaia run without a file is unchanged; a file and --project together are refused.

Found on the way, and filed: a new project is compiled again on its second build although nothing changed (#280), and killing nikaia run leaves the program it started running (#281).

[0.0.299] — 2026-10-01

The single-file commands are verbs (ADR-260 D3 and D5, #131). nikaia --input f.nika named an argument and not what happened to it, and three jobs shared its switches. They are commands of their own now, and each one’s --help says in its first sentence what it does and what it does not:

Was Is
nikaia --input f.nika nikaia lower f.nika: writes f.rs and the ledger beside it, and does not call rustc
nikaia --input f.nika --backend interpreter nikaia interpret f.nika: runs the file, writes nothing
nikaia --input f.nika --explain nikaia explain f.nika: reads rustc --error-format=json on stdin and reports it against the .nika

[0.0.298] — 2026-10-01

A file inside a project is a file of that project (ADR-260 D1 and D2, #130). nikaia --input on a project’s file read the [build] section of its nikaia.toml and handed the lowering no packages, so use http in examples/hello-http/src/main.nika was refused with add it to [dependencies] about a line that was already there. It now reads the dependencies the manifest declares, through the same packages_of that nikaia build uses, and lowers the file to what nikaia build emits for it (the abort table names the file as it was handed in). The cache key gains the dependencies with them: their files are units of the program, so an edit to a dependency is a miss. A file outside any project is standalone, and a use of a package in it is told that a single file outside a project has no dependencies, and that they are declared in a nikaia.toml.

And the lowering got faster again, measured on this compiler’s own use (#269, #273, #274, #277):

Lowered with --no-cache 0.0.297 0.0.298
1brc 73.8 M 54.2 M (−27 %)
n-body 72.6 M 60.5 M (−17 %)
k-nucleotide 69.5 M 55.6 M (−20 %)

[0.0.297] — 2026-10-01

The ledger is read by grammars (ADR-257 §4.6), and what made them fast was built in winnow-grammar, where every grammar gains it. The reader in tools/ledger.nika walked the text a character at a time through Vec[char]; it is now grammar LedgerText for the lines, grammar LedgerValue for a list and grammar SignatureText for a signature, slicing views out of the text rather than copying it, and reads std’s ledger to the same records as before. Reading std.contracts went from 11.4 M instructions to 5.9 M.

Measured on this compiler’s own use, winnow-grammar now

Lowered with --no-cache 0.0.296 0.0.297
an empty program 22.8 M 17.4 M (−24 %)
1brc 102.8 M 73.8 M (−28 %)
n-body 94.5 M 72.6 M (−23 %)

Parsing 1brc alone went from 21.1 M to 12.4 M (−41 %). Diagnostics are the diagnosing pass’s, which tries every alternative as before.

[0.0.296] — 2026-10-01

std’s ledger is read once per run (ADR-257 §4.6). Most of what the measurement called reading std.contracts was reading it again: seven places parsed STD for themselves - the emitter twice, the checker’s propagation twice, Program::emit_reading, Foreign::library and the cache that was meant to be the only one. Every reader now borrows that one parse (contracts::std_ledger), and one that adds a package’s own entries takes a copy (std_library).

Lowered with --no-cache before after
an empty program 93.4 M 22.8 M (−76 %)
1brc 186.8 M 102.8 M (−45 %)
n-body 178.5 M 94.5 M (−47 %)

[0.0.295] — 2026-09-30

What the checker asks of a type is Nikaia (ADR-257 step (d)), which finishes the record: fits, erase, parameterise, as_a_view, is_seen, unseen, is_a_view and is_unknown are Ty’s methods in tools/ty.nika, and bind, substitute, qualify and renamed its functions. The toolchain is 8.4 % Nikaia.

[0.0.294] — 2026-09-30

?? lends its left side at every position that only reads the answer (ADR-259 §4, #98): an f"…" hole, a comparison of text and the receiver of a method that only reads it, beside a call’s argument and the print calls. A name used again after such a line needs no .clone() now, where it was NK2105.

[0.0.293] — 2026-09-30

A part an arm binds as a view is handed on as it is (#270). ADR-242 binds a match arm’s names ref where the arm only reads them, and a call that lends such a name wrote &key all the same: a view of a view, the same code below, and the lint nikaia-std allowed on its tools for ledger.nika’s reader (0.0.292). The allowance is gone.

[0.0.292] — 2026-09-30

Reading a ledger back is Nikaia (ADR-257 step (c)), whole: the file’s shape, what every table and key means, a signature back from the file’s spelling, and each record’s text. Ledger::parse is one call into tools/ledger.nika. The toolchain is 7.4 % Nikaia, from 5.5 %, and the compiler reads std’s ledger in fewer instructions than the Rust did.

[0.0.291] — 2026-09-30

Ledger and Notes are Nikaia, which finishes ADR-257 step (b): every record a ledger holds is declared in tools/ty.nika. The toolchain is 5.5 % Nikaia.

[0.0.290] — 2026-09-30

Signature, FnContract and TypeContract are Nikaia (ADR-257 step (b)), and two defects the move found are fixed. The toolchain is 5.4 % Nikaia.

[0.0.289] — 2026-09-30

The ledger’s small records are Nikaia (ADR-257 step (b)), and a field on the left of ?? is a place. The toolchain is 5.1 % Nikaia.

[0.0.288] — 2026-09-30

?? lends its left side where the answer is only read (ADR-259), the owner’s choice of option (1).

[0.0.287] — 2026-09-30

Four weights for a design question (ADR-258), the owner’s philosophy.

[0.0.286] — 2026-09-30

A type’s text is read in Nikaia too (ADR-257 step (c)), and three defects the move found are fixed. The toolchain is 4.7 % Nikaia.

[0.0.285] — 2026-09-30

Ty and Shape are Nikaia (ADR-257 D1, step (b)’s first half), with the text a type is written as. The toolchain is 4.1 % Nikaia.

[0.0.284] — 2026-09-30

NK1167’s way out is one a program can write.

[0.0.283] — 2026-09-30

The checker’s types move to Nikaia as they are, and the measurement that decided it (ADR-257).

[0.0.282] — 2026-09-30

A variant matched on a T? is refused (NK1205, #180).

[0.0.281] — 2026-09-30

contracts::trust is Nikaia (ADR-250 D1, D2), and three defects the move found are this compiler’s now.

[0.0.280] — 2026-09-30

The defects list is empty (#171 and #170): four found moving fold into Nikaia, and two they led to.

[0.0.279] — 2026-09-30

Three lowerings that reached rustc are this compiler’s now (#161 and #171).

[0.0.278] — 2026-09-30

fold is Nikaia, and a constant counts in a magnitude and a sign (ADR-252 D6, decided by the owner).

[0.0.277] — 2026-09-30

A type a ledger says copies is copied (ADR-252 D4.1, the reading, for a described type).

[0.0.276] — 2026-09-30

A tool reads the tree’s names as described (ADR-252 D4.3, the rest of it).

[0.0.275] — 2026-09-30

The syntax tree is declared in Nikaia (ADR-252 D1, D5 step 4).

[0.0.274] — 2026-09-30

A described Rust type says whether it copies and compares (ADR-252 D4.3).

[0.0.273] — 2026-09-30

The prover reads lengths and sees into lambdas (ADR-256 D5).

[0.0.272] — 2026-09-30

A parse error names the mistake where the reader made it (Part III C.2, rule 5). A parse stops where the input stopped making sense, often a line after the mistake; a failed parse is now read once more for the mistake itself. All twenty-four failing rows of docs/error-corpus.md say what a reader needs, where 0.0.266 left twelve that misled.

[0.0.271] — 2026-09-29

Every assert outside a test is proved while the program is built (ADR-256), or the program is refused. A check at run time was a cost nobody wrote down and an abort nobody planned for; now there is none.

[0.0.270] — 2026-09-29

What the specification’s examples found in the compiler, fixed.

[0.0.269] — 2026-09-29

A jump may carry its condition after it (ADR-255). return 250 if speed > 250, throw TooFast(speed) if speed > 250, continue if n % 2 != 0, break if done: each is if cond { jump }, and the parser builds exactly that if, so nothing after it changed.

[0.0.268] — 2026-09-29

A checked statement runs on a connection it names as its subject, and the connection’s type carries its schema (ADR-254). The owner answered open-decisions.md’s database question with option 1, and added the schema: a statement checked against one schema handed a connection to another is refused while the program is built.

[0.0.267] — 2026-09-29

A list has no + (ADR-253). The owner answered open-decisions.md’s question: no operator on a list, and NK1191 stays.

[0.0.266] — 2026-09-29

Every message in plain words, in one layout (Part III C.2, rule 5). Good messages are a claim this language makes, so the rest of the codes follow the two 0.0.265 began with.

[0.0.265] — 2026-09-29

A message says what you wrote, why it cannot work and what to write, at the places it is about (Part III C.2, rule 5). Feedback on the website: its example message read like rustc’s.

[0.0.264] — 2026-09-29

A declared type whose every part is a copy derives Copy (ADR-252 D4.1, first half): a unit-only enum, a struct of two u32, and anything made only of numbers, bool, char and such types. It is what the syntax tree’s Rust readers need of BinaryOp and Span once Nikaia declares them.

[0.0.263] — 2026-09-29

An integer literal is its magnitude and its sign, and the tree derives no Default (ADR-252 D5 step 2), still in Rust: two things Nikaia has no word for, taken out of the tree before it moves.

[0.0.262] — 2026-09-29

A Span is two u32 (ADR-252 D5 step 1), still declared in Rust: the largest diff on the road to moving the tree, and entirely mechanical, so taken alone.

[0.0.261] — 2026-09-29

The syntax tree moves to Nikaia whole, and a Span is two u32 (ADR-252), the record ADR-250 D2 asked for before the tree moves. The leaves are gone - fixed, emit::template and manifest are Nikaia - and every module left reads the tree, which no Nikaia module can read while Rust declares it. The owner chose the tree moving whole and u32 offsets.

[0.0.260] — 2026-09-29

What nikaia.toml may say is decided in Nikaia (ADR-250 §5). tools/manifest.nika decides which [build] keys exist, have moved or are withdrawn, which machines and keys [build.<target>] may carry, and which of the three shapes a dependency is; Manifest::parse reads the file with the toml crate, which stays behind the adapter (ADR-250 §3), and asks it about every key. The toolchain is 2.5 % Nikaia.

[0.0.259] — 2026-09-29

A read’s answer reaped by another thread wakes its reader (ADR-121 §5). The arm64 CI job has hung six hours on every run since at least 0.0.252, in fs::many_threads_reading_and_writing_at_once_each_get_their_own_answer, and the job cancelled by the next push hid it.

[0.0.258] — 2026-09-29

The ledger’s reader is Nikaia, the half that knows the file’s shape (ADR-250 §5). tools/ledger.nika reads which lines of a nikaia.contracts say something, which open a table, where a key ends, what a quoted string holds and where a list’s entry ends; Ledger::parse keeps what each key means. The toolchain is 2.3 % Nikaia.

[0.0.257] — 2026-09-29

keeps no longer names a receiver the author lent (#173, found at 0.0.254 in the ledger tools/template.nika lowers to; ADR-251 §5). Position::escapable(ref self), whose body is one match self, was recorded keeps = ["self"]; so was Splitter::until(ref mut self, …), for calling itself; and Scan::feed(ref mut self, c: char) kept its char.

[0.0.256] — 2026-09-29

CI is green again on Rust 1.98. Stable moved under the repository (ADR-001 D1 names the channel, not a version), and 1.98’s clippy brought explicit_counter_loop, which failed main at 0.0.252 through 0.0.255 on the Rust the emitter wrote for template.nika’s here: a for over a word’s characters beside a let mut counting into the scan.

[0.0.255] — 2026-09-29

sync(f) is in the language (ADR-244 D4). A function that runs a lambda promises that it pauses only where the lambda does: fn apply(f: fn(i64) -> i64, x: i64) -> i64 sync(f). std has written the promise by hand for map, filter and the lock doors since ADR-029 D3; a program can now write it.

[0.0.254] — 2026-09-29

The ledger speaks Nikaia where Nikaia can say it (ADR-251 D4, D5).

[0.0.253] — 2026-09-29

The ledger is the contract, and what it was derived from is beside it (ADR-251 D1-D3). Asked before the ledger’s reader moves to Nikaia: the format was two files in one, carried prose the compiler never read, and Part III 13.5 described a file no longer written.

[0.0.252] — 2026-09-28

The road to Stage 1, and the fifth module on it (ADR-250). ADR-001 D4 named Stage 1 - Nikaia compiling Nikaia - and this says how it is walked: a module at a time, leaves of the module graph first, the Rust module left as the adapter and the whole suite as the proof. The compiler is 2.0 % Nikaia (876 of 44,718 lines that say something), measured by scripts/self_hosting.py and drawn into a README badge.

[0.0.251] — 2026-09-28

A number without an annotation is typed by its uses, and this compiler decides it (ADR-249). Part I 2.4’s the use decides had been the language below’s inference; ADR-248’s u64 and u32 gave a number a second answer, and an index gave rustc none.

[0.0.250] — 2026-09-28

u64, u32 and the bit operators (ADR-248), decided because fixed.rs asked for them - and fixed.rs is Nikaia, the fourth piece of the compiler moved by ADR-196’s route.

[0.0.249] — 2026-09-28

Three things the lowering wrote that it should not have, each found moving Rust into Nikaia at 0.0.248.

[0.0.248] — 2026-09-28

Two more pieces of Rust are Nikaia, by ADR-196’s route: written as .nika under nikaia-std/src/tools/, lowered ahead of time by nikaia lower-std, committed beside it and include!d.

[0.0.247] — 2026-09-28

The lock column resolves a call the way sync does, and 11 of its 12 doubts in examples/ were only that. The analysis NK2201 and NK2203 are to read (ADR-039 D3) answered undecided for 12 of 61 functions in examples/, and the roadmap still said 24 of 59. Traced call by call, the doubts were:

What is left is one function, http::answer, which calls its handler parameter: whether that takes a lock is the caller’s lambda’s answer, which is a real doubt and not a lookup.

The ledger entry found a program that broke a rule unseen. examples/escaping wrote html::Raw::new(built); with std describing the constructor, ADR-140 D2’s NK1149 applies and the example writes html::Raw(built), which lowers to the same call.

[0.0.246] — 2026-09-28

Patterns, lookups and a map’s lists: found by writing a graph and a few shapes and running them, and kept in crates/nikaia/tests/defects_found_running.rs.

[0.0.245] — 2026-09-28

Loops, keys and lists of functions: six programs found by writing small ones and running them, each kept in crates/nikaia/tests/defects_found_running.rs and run at both settings of user_parallelism.

[0.0.244] — 2026-09-28

Four programs that reached rustc as a file nobody wrote, each found by running something and each kept, as the program that found it, in crates/nikaia/tests/defects_found_running.rs, run at both settings of user_parallelism.

[0.0.243] — 2026-09-28

A program whose result is a file is tested like one that prints — ADR-247, decided after comparing how other languages and test tools do it: no language has syntax for it, and the tools agree on a directory per test, trees in and out, blessed expectations and differences.

[0.0.242] — 2026-09-28

The examples test themselves — ADR-245 D8’s first users.

[0.0.241] — 2026-09-28

Output tests, and --asserts — ADR-245 D8 and D6, built.

[0.0.240] — 2026-09-28

nikaia test — ADR-245 D1 and D7, built.

[0.0.239] — 2026-09-28

assert is a claim — ADR-245 D2 and D3, built.

[0.0.238] — 2026-09-28

The first piece of the compiler is written in Nikaia - ADR-196’s route, used to take Rust out of the compiler.

[0.0.237] — 2026-09-28

std reads a whole number from text — atoi, written in Nikaia.

[0.0.236] — 2026-09-27

A type may hold itself — ADR-246, the owner’s answer to how a type holds itself: the compiler puts the box in.

[0.0.235] — 2026-09-27

A map keyed by numbers can be read — closing #169.

[0.0.234] — 2026-09-27

Two more shapes refused here rather than by rustc, and the question each leaves on open-decisions.md. Both found on the way to writing a compiler in Nikaia.

[0.0.233] — 2026-09-27

std::process: another program, started and waited for — ADR-243, the subprocess ADR-195 D4 decided and nothing built, and the thing a compiler written in Nikaia needs to run rustc.

[0.0.232] — 2026-09-27

Two defects found writing a miniature compiler in Nikaia, to measure how far self-hosting is.

[0.0.231] — 2026-09-27

An impl of a trait nothing declares is refused here, not by rustc.

[0.0.230] — 2026-09-27

Four defects found while checking the examples — closing #160, #154, #155 and #163 #160–1.20.

[0.0.229] — 2026-09-27

A match over a place lends what an arm only reads — ADR-242, closing #168.

[0.0.228] — 2026-09-27

A library’s error travels in an envelope too — ADR-241, closing #199.

[0.0.227] — 2026-09-27

A sequence’s count() is an i64 — closing #164.

[0.0.226] — 2026-09-27

An error keeps its site when it hops into the box — ADR-240, closing what #199 #199 named as the box’s remaining cost.

[0.0.225] — 2026-09-27

An overlap branch settles its own cleanups and joins its own error — ADR-239 D3, closing the cleanup half of #199 (ADR-115 D3).

[0.0.224] — 2026-09-27

Ownership says when a value dies, and the compiler settles its cleanup there — ADR-239, refining and building ADR-006, closing #188 and the call half of #199’s cleanup attachment.

[0.0.223] — 2026-09-27

A word-sized value that crosses is the word, and update is a compare-and-swap — ADR-238, closing #184 (ADR-110’s speed half).

[0.0.222] — 2026-09-27

A cleanup point that moves is narrated, and a described call that fails compiles — ADR-236 and ADR-237, closing #186 and #194.

[0.0.221] — 2026-09-27

par_iter() walks a list on every core — ADR-235, closing #185 (ADR-105 step 4).

[0.0.220] — 2026-09-27

A lazy walk of io::lines() carries its failure to the walk — ADR-234, closing #182.

[0.0.219] — 2026-09-27

A lambda that pauses is handed to std’s pausing counterpart — ADR-233, closing #187.

[0.0.218] — 2026-09-26

The machine named is the machine built for: aarch64-linux is a target, the default is the machine the compiler runs on, and the triple reaches Cargo as --target (ADR-037 D1).

[0.0.217] — 2026-09-26

The whole suite runs on 64-bit ARM in CI: a new arm64 job on ubuntu-24.04-arm.

[0.0.216] — 2026-09-26

A published parameter takes either kind of text from its own package — ADR-232, the README’s row about handing a view to a pub function.

[0.0.215] — 2026-09-26

The target-info probe test runs in every sweep again, closing #201.

[0.0.214] — 2026-09-26

A value read through a view is the value — ADR-231, closing #220’s filter defect and the class it belonged to.

[0.0.213] — 2026-09-26

A function field is reached through what is stored in it — ADR-230, closing #175 (ADR-039 D7’s stored lambda).

[0.0.212] — 2026-09-26

A value handed over inside an f"…" hole is handed over there — ADR-229, closing #200.

[0.0.211] — 2026-09-26

A ?. view out of a temporary is held for the rest of the block — ADR-228, closing #192.

[0.0.210] — 2026-09-26

collect() builds what the place it goes into declares — ADR-227, closing #208’s collect() into a declared map, with the owner’s choice over a refusal.

[0.0.209] — 2026-09-26

A view handed back out of a parameter that holds views is the buffer’s — ADR-226, closing ADR-222 §3’s last row.

[0.0.208] — 2026-09-26

A view cut from text is typed as one — ADR-225, closing #260’s a view pushed into a list a published function handed back reaches rustc.

[0.0.207] — 2026-09-26

The rest of the text wall — ADR-224, #200’s remainder.

[0.0.206] — 2026-09-26

Every declared String is a position — ADR-223, the owner’s Nikaia does not build walls, it takes them down.

ADR-222’s three tiers now cover a function’s parameter, an annotated let, and the element of a list or a set and the key and value of a map, each decided by what the package puts there. A view handed to a function that keeps it, let s: String = line.trim(), a list of the trimmed lines of a file, and a map counting its words all compile and run with nothing copied. The last three used to pass the checker and fail in rustc. A mixed position gets each value as value.into(); the checker reads it as text of its own, so it is lent and copied as a String is. A published parameter is never both kinds. A value reaching a mixed position only through a string’s hole leaves that position text of its own (its line cannot be rewritten), which keeps ADR-208’s refusal for it.

[0.0.205] — 2026-09-26

What the first green-by-design run of 0.0.204 found, on a runner with fewer cores than the machine it was written on.

[0.0.204] — 2026-09-26

CI is green again, and runs what a change needs, once. It had been red since 0.0.200, and not for one reason.

[0.0.203] — 2026-09-26

A String field or result is below what flows into it — ADR-222, the owner’s decision on #200: the costs are carried by the special case, not by the general one.

Right after parsing, every field declared String and every function declared -> String is read for what the package puts there. With text of its own only, it stays String and nothing changes. With views only (literals count as views), it becomes ref String, and ADR-209 places the buffer. With both, it becomes nikaia_std::either_text::EitherText, a Cow<str> that borrows a view and moves text of its own in at each line. A published field or result is never both; that one case keeps ADR-208 D2’s refusal. A name bound to a literal and kept as text of its own is declared String. The README’s wall is gone for a field and a return: a ref String parameter, a slice of a buffer the function read (which used to pass the checker and fail in rustc), a name bound to a literal, and return line.trim() from -> String all compile and run. --tethers reports the tiers. A kept argument, an annotated let and ?String are still refused as before (#200).

[0.0.202] — 2026-09-26

A list of structs of views that drops entries is held, one handle per buffer — ADR-221, the owner’s decision on the last open question (option 3).

The records program NK2304 refused compiles and runs: each element is a tether::Holding beside the keep of every buffer it points into, the struct is lowered once, and the compiler writes Views, Rebase and Hold for it. An element is read through get (brackets, for, iter, first, last, get) and a field written through with_mut; an element taken out with remove stays held. A view is carried in by address — found in its keep, or copied where it points into none, such as a literal — so the plan is checked rather than trusted, and tether::hold, the one unsafe fn the emitted code called for a held view, is gone. Two defects of ADR-209 D4 on the way: entries dropped only after the loop no longer make a container a pruning one (it was refused for a struct and paid a handle per view for text), and a removal bound by a let now counts. A map of structs of views, a struct with type parameters and a view of something other than text stay NK2304, by name. open-decisions.md is empty.

[0.0.201] — 2026-09-26

Edition 2024, and no .await point needs a change — ADR-220, the owner’s question.

The migration lints found no if_let_rescope and ten tail_expr_drop_order sites. Both changes make temporaries live shorter, and none of the ten holds anything across a pause. crates/nikaia, nikaia-std, orchestrator and the six crates/unsafe/ crates are on 2024; nested if lets became let chains, and imports follow rustfmt’s 2024 order. The emitted code is 2024 as well, which broke two things the emitter now writes differently: unsafe extern "C", and gen escaped as r#gen. The floor stays 1.88, which scripts/check-floor.sh confirms, because nothing needs 1.94.

[0.0.200] — 2026-09-26

The oldest Rust Nikaia works with is measured: 1.88 — ADR-219, the owner’s request.

The README and Part III promised 1.75 (ADR-109 D4). Building and testing with each version found four things that need more: NonNull::map_addr (1.84, in std), impl AsyncFn and async closures (1.85, in the emitted code), std::io::pipe (1.87, in std’s tests) and the compiler’s dependency home (1.88). The whole suite and all six crates/unsafe/ crates pass on 1.88.0.

rust-version = "1.88" is in every manifest, RUST_FLOOR (which every generated Cargo.toml carries and the build checks) says 1.88, and so do the README, Part III 13 and rust-toolchain.toml’s comment. scripts/check-floor.sh installs exactly that toolchain and runs the suite on it, and CI runs it as a job of its own, so the number cannot go stale again.

[0.0.199] — 2026-09-26

nikaia-std holds no unsafe — ADR-218 D4, the owner’s follow-up.

Five more crates under crates/unsafe/, each with its README, its unsafe table and its checks: ref-or-box (an error’s tail in one word), checked-text (UTF-8 checked once, chunked; a mapped file), c-text (a C string, returned by an extern declaration as Option<CText>), polled (a poller that owns what it watches), file-ring (io_uring with owned buffers, and a bell rung by safe writes). A test’s mkfifo is the command. nikaia-std says #![forbid(unsafe_code)] and no longer depends on libc, memmap2, polling or io-uring directly. The emitted code’s unsafe (tether::hold, forever) is what is left, as #84.

[0.0.198] — 2026-09-26

Every unsafe topic is a small crate of its own — ADR-218, the owner’s decision.

[0.0.197] — 2026-09-26

The open question about structs of views names its crate. Option 3 on docs/open-decisions.md — one handle per element — is built on self_cell, which keeps unsafe out of the emitted code at the cost of one dependency with none of its own. Tried by hand on the entry’s own program; yoke compared and not chosen.

[0.0.196] — 2026-09-26

The open lists say what is open — the owner’s request.

the former backlog file loses the history of why it is laid out as it is: the header, and the openings of §1 to §3, are the rules and the current order and nothing else. #181 (the kept lowering of a function value) closed with 0.0.195 and is deleted. The question ADR-217 left — whether a container of structs holding views that drops entries in a loop should be held rather than refused (NK2304) — is on docs/open-decisions.md, with three options and a recommendation.

[0.0.195] — 2026-09-26

What the specification stated and the compiler did not build — ADR-217, the owner’s request after 0.0.194.

[0.0.194] — 2026-09-26

The specification states the contract and nothing else — the owner’s decision that ADR references, rationale, history and implementation status cost every reader too many tokens and too much time, and are redundant with the ADRs, this changelog and the roadmap.

STYLE.md is rewritten to that rule, and the three parts follow it: no ADR- citation is left, no Design rationale, no Implementation status note, no withdrawn, since or today. Headings and section numbers are unchanged, and every ```nika block keeps its code and its verdict (EXPECTED.txt lists the same results); three blocks lost an ADR citation from a comment. Rules that stood only inside a status note were kept as plain rules. The three parts shrank from 64,392 to 47,182 words. NK1114 (removed from the compiler) and NK1171 (a refusal of what is not built yet) are no longer described. Also fixes the parts’ Date: line, which 0.0.193 left at the day before.

[0.0.193] — 2026-09-26

The text form of text is the text itself — ADR-216 D4, the owner’s why must to_string be a copy? and "nobody" is enough.

.to_string() on text now answers the receiver’s own type — a view stays a view, a literal a literal — and is not written below. Only .clone() copies. A bare literal is text of its own wherever it is kept, now also in a tuple’s parts and after ?? inside an f-string hole; the latter uncovered a literal-position collision between holes, fixed by keying hole literals by statement and hole. The keep and tether analyses read to_string accordingly. Test programs, examples and Part I write the bare literal. Open: whether a redundant .to_string() on text should get a hint.

[0.0.192] — 2026-09-25

A copy has one word: .clone() — ADR-216, the owner’s question whether .to_owned() still belongs to the language.

That a copy is written stays — it is the language’s promise that nothing large is copied behind the program’s back. But it had three spellings and two sets of advice: Part I named .to_owned() for text, which no ledger entry described, while the ledger and NK2105 said .clone(). The language below needs two words only because its .clone() of a reference copies the reference; this language has no reference to copy, and since 0.0.191 the compiler writes to_owned below itself where it is needed.

So .clone() is the copy, of text and of everything else, and .to_owned() is NK1189, naming it. .to_string() stays: it is the text form of a value, for every type. A clone of a slice hands back a list (to_owned below), and the keep and tether analyses read a copy of text as a buffer of its own, as they read to_owned. Every help text, Part I, Part III and both READMEs say .clone(); the test programs that wrote .to_owned() were rewritten.

[0.0.191] — 2026-09-25

What was left of ADR-212 §5 — ADR-215, the owner’s yes to the last four finds, and a fifth found on the way.

[0.0.190] — 2026-09-25

A taking has a place in its statement and a path in its value — ADR-214, the owner’s fix the open points: ADR-213 §3’s three.

Within one statement. NK2702 and NK2105 ordered reads by statement, so keep(name, name) and s.count() + s.count() went to rustc. Every read now has its order in the walk, and a later read in the same statement is after the taking. name = name + "x" stays a program: the assignment writes after it reads.

Parts. A read of p.name is a read of that path. After xs.push(p.name), p.x is still there; p.name and p as a whole are not (NK2105), and an assignment to either gives it back. A part of something only lent — a ref parameter, a for binding over a list, a let over a place — handed to what keeps it is NK2106. And the probes found a wall under it: fn f(p: P) { xs.push(p.name) } lowered p to &P and rustc refused the move; a part handed to what keeps it now keeps its parameter, as return p.name did.

The stray warning. Every read through the brackets was (*get(…)), and m[k] ?? 0 or let x = xs[1] came with unnecessary parentheses — the one the README told testers to expect. The parentheses are written only where a postfix needs them now, and a test compiles such a program and reads rustc’s warnings back. The README no longer mentions it.

[0.0.189] — 2026-09-25

A key is not a position, and what is handed over is gone — ADR-213, the owner’s yes to repairing the first wall ADR-212 §5 found.

Maps whose keys they own. Every example keys its maps by ref String, so nothing had written m["a"] = 1 or m[name] = 2 on a HashMap[String, i64] — and neither compiled, nor did a read with an owned String, nor any key of a HashMap[i64, …]: the brackets wrapped the key in index::at, which is for positions and made the i64 a usize. Now a key written is handed to the map as it is (a literal built into text there, a name handed over, a view refused with ADR-208’s .to_owned() sentence), and a key read is lent. A map keyed by views is unchanged.

Two more on the way. A literal assigned to a String — s = "b", ids[k] = "seven" — was NK1105; an assignment keeps what it is given, so the literal is built there as at every other such place (ADR-207 D1). And m[k] ?? "-" over a map of text did not compile below; it is a view of text now, with nothing copied.

NK2105: ADR-094 D2 decided long ago that a value handed to something that keeps it and then used again is refused in this language’s words; nothing did, and xs.push(name) then println(name) was rustc’s borrow of moved value. It is refused now at every place a value is given away — a kept argument, a key or value written into a container, a field, an element, a let rename, an assignment — with .clone() as the way out, and inside a loop or a lambda at the hand-over itself. Built on ADR-212’s machinery, and it found that machinery’s blind spot on the way: a branch that ends in return or throw takes its takings with it, which the http example’s refuse(connection, …) return, three times in a row, depends on. NK2702 learned the same.

[0.0.188] — 2026-09-25

A sequence says what it is as a whole: a range replays, a pipeline walks from its back end, and every taking is seen — ADR-212. Outside feedback the owner decided: the repairs first, and reversing a pipeline without a collect(); the two-ended cursor left out on purpose.

The repairs. Four programs a tester writes in the first hour went to rustc: a range kept in a name and walked (let r = 0..<3, for i in r — no method named iter), a named keys() or drain() in a for (the same), and a sequence taken by a let, inside a loop or inside a lambda and then used again (use of moved value). A range now has a type — a sequence that replays — and below it is nikaia_std::range::Span, a Copy value walked as a copy, so it can be walked twice, backwards and in steps. A for over a named sequence hands it over instead of writing .iter(). NK2702 now sees every read that takes a sequence, and a take inside a loop or lambda of a sequence from outside it — unless the loop gives it a new one or may leave. And it learned the arms of an if and a match: taking in the then and reading in the else was refused before, a correct program.

Three words after Seq[T]: ends (walkable from the back), sized (length known), replays. On a receiver they are a demand — io::lines().rev() is NK2703, in the program’s words — and on a result they pass through, with ends_by_length for the adapters Rust walks backwards only when it knows the length (take, zip, skip, step_by). Two words and not one, because chars().rev() has no length and filter(…).zip(…).rev() needs one.

The pipeline: map keeps its element type (the lambda’s body binds $U), and rev, zip, take, skip, step_by, a list’s iter(), windows and chunks are new — all the language below’s own adapters, so a chain is one loop and nothing is collected. A count is an i64 here and converted by the entry the call resolved to, so a program’s own method called take is untouched. Rust’s Range<i64> has no length, so (0..n).step_by(2).rev() would not have compiled below; Span has one for every integer type.

New example: examples/trend/src/main.nika, three answers read from the end of a list and a range walked twice. Found and left (ADR-212 §5): writing into a map keyed by an owned String, HashMap::insert, str::chars -> ?, two takes of one sequence within one statement.

[0.0.187] — 2026-09-25

What an error carries besides itself is one word — ADR-211, the owner’s objection to ADR-210 D3: every error paying for the special cases is not this language’s idea of cost.

It was worse than the list’s 24 bytes. A function that can fail returns a Result, and a Result is as large as its failure half whether it fails or not: Result<Int, Thrown<ConfigError>> was 96 bytes where the bare error costs 16 — the site as a two-word &str, a 48-byte Option<Backtrace> almost no process fills, and the list only an overlap fills. ADR-210 D3’s the success path never sees an envelope was wrong, and is marked so.

Now the site, the trace and the list are one tagged word: odd, the site alone, which is nearly every error and allocates nothing; even, a cold box with the trace and the list, made only where a process traces or something joins. The word is never zero, so a Result finds its niche there whatever the author’s type: for a field-less enum, a unit struct or one Int of payload, Result<Int, Thrown<E>> is 16 bytes — what the bare error costs. Site<E> went from 88 bytes to 8, the boxed channel’s envelope from 104 to 24. The sizes are asserted in tests; the unsafe that reads the word runs clean under Miri with strict provenance.

Changed below: the emitter writes a throw’s site as &"load" (a reference to the text, promoted like any constant) where it wrote "load".

Not done, and why (D3): the boxed channel is still two allocations per throw. One would need the error inside a generic envelope, which a downcast cannot find again — so either a failure channel type of this library’s own that every Rust-side ? converts into, or a deprecated method as a marker. Both cost more than an allocation on the failure path.

[0.0.186] — 2026-09-25

The joined failures are a diagnostic, not a value — ADR-210, the owner’s answer to open-decisions.md’s last question: option 3, against the page’s recommendation of an opaque Failures type. Nothing is built, which is the decision.

The secondary list exists because the language itself makes two failures happen at once — the branches of an overlap, or a cleanup failing while an error is already leaving — and a throw carries one; dropping the other would lose exactly what an operator needs in an outage. That is built and stays: the list travels with the failure, throw error keeps it, and the long form prints it. What is withdrawn is reading it in code (throw LoadFailed(error, error.secondary), ADR-115 D4’s example): nothing in the repository needed it, and a type introduced for one example is a construct whose only reader is the sentence that introduced it. If a program ever needs it, an opaque Failures can be added then without breaking anything.

D3 writes down why every envelope carries the field though only two things fill it: a failure travels through ? into a function that may join, and two envelope types would convert at every boundary; an empty list allocates nothing and only the failure path sees it.

Corrected on the way: Part I 7.1 and 8.1.2 said a handler reads error.secondary. No such name was ever built. open-decisions.md is empty; #199 loses the bullet that pointed at the question.

[0.0.185] — 2026-09-24

The tether is built: a buffer lives in the keep of whatever keeps its views, and the compiler decides which — ADR-209. The first of the README’s design risks, taken in one step as the owner asked: the programs that need it were invented first, the design was checked against them, and all of it was built.

What a program can do now

struct Setting {
    key: ref String,
    value: ref String,
}

fn load(path: String) -> Vec[Setting] throws {
    let text = fs::read_to_string(path, fs::Root::Anywhere)
    …                       // cut `text` into settings
    return settings         // views of `text`, which this function read
}

was refused (NK2303, or NK2302 blaming path, or rustc about a file nobody wrote). It compiles and runs, and nothing is written for it: no lifetime, no annotation, no copy.

How, and why this road

Eight programs, one per shape — a loader, a view handed two frames up across a pause, a loop over files, recursive includes into one map, a task, a cache that drops entries, the loader with no word above it, one line of a mapped file — said that ADR-008’s one representation (positions, three layouts, a buffer table) was one answer where the facts differ. So:

Found and fixed on the way

What is still refused, on purpose

NK2304: one buffer handed both to a task and out of the function (no one owner), and a container of structs holding views that drops entries in a loop.

In the tree

nikaia_std::tether (Keep, Held, and two unsafe functions with their contracts), contracts::keep (the plan and its fixpoint), the emitter’s keeps, tests/tether_keep.rs (the eight programs at both settings), four bytes.rs tests and two tethers.rs tests moved from the refusal to the tether. Part I 6.6, Part III, ADR-008/156/201’s status, #196 (the tether’s entry is closed) and the README follow.

[0.0.184] — 2026-09-24

A view of text is asked for a copy only where something keeps it — and the refusal says why — ADR-208. The owner’s point after 0.0.183: a language that hides ownership this thoroughly owes the reason at the one place it does not, or the reader asks why a compiler that knows exactly what to write does not write it.

A correct program that was refused

fn relay(city: ref String) { show(city) } for a show(s: String) that only reads asked for city.to_owned() — a copy nothing would keep, since the parameter is a &str below (ADR-207 D3). D1: a view handed to a String the callee only reads is lent as it is, for a function this compiler declares; a ref the source writes there is still NK1137.

The refusal that stays, explained

Where something keeps the text — a field, an annotated let, a return or a body’s last value, an argument the callee keeps — NK1102/NK1103/NK1104/ NK1106 now say whose text it is, what keeps it, and what a copy made on its own would cost (ADR-005 §3), in three cases:

error[NK1106]: `Reading.name` is `String`, and this is `ref String`
     = `city` is declared `ref String`: the text belongs to the caller, who still has it
     = `Reading` keeps its `name` after this line, so it needs text of its own
     = Nikaia copies text only where the program says so: a copy costs as much as the
       text is long, and one made on its own would run every time this line does, with
       nothing in the source to show it (ADR-005 §3)
     help: declare `city: String`, and the caller hands its text over instead of
       lending it - or write `city.to_owned()` to copy it here

A name bound to a literal gets the honest not yet: written in place the literal would be built on that line (ADR-207); through a name it is not, because that would change the type the name was declared with — and the help names the two spellings that work. Any other view says it points into text something else owns.

In the tree

a_view_kept in the checker, read by every keeping position; a binding remembers the literal it was bound to. Two new tests in tests/text_literals.rs — D1 run end to end, each explanation checked. Part III C.4’s example is the new message.

[0.0.183] — 2026-09-24

A text literal is a String where one is wanted — ADR-207. The first refusal a pre-alpha tester met, and the owner asked for it gone with the complexity in the compiler: clever rather than complicated, and not more expensive than it has to be.

What changed for a program

Person { name: "Ada" }, let s: String = "x", return "x" out of a -> String, greet("x"), a T? field given a literal, let xs: Vec[String] = ["a", "b"], and a match whose arms are literals where a String is handed back — all compile now. So do the mixed forms, because a literal takes its neighbours’ type: ["a", f"c{n}"] is a Vec[String], and if c { name } else { "anonymous" } over a name: String is a String.

Why this road and not ADR-107’s

ADR-107 D1 decided this and routed it through a new representation of text — a handle of three states on every text value. That costs every value a wider shape and a branch, to save an allocation in one position, and the state it adds is the one the tether analysis found nothing in the corpus needs. Inserting .to_string() everywhere is the other cheap answer and is wrong where the callee only reads. So the answer is per position, off two things the compiler already knows — what type the position wants, and whether the callee keeps the parameter (ADR-094):

ADR-005 §3 stands. A view of text the program has is still refused where it is kept, and NK1106/NK1102/NK1103/NK1104 now say how out of it — write .to_owned(). They used to say make it a String: the arm with the right sentence matched &str, which the checker stopped printing when the spelling became ref String.

A defect found on the way, and closed with it

keeps (ADR-094) counted a parameter as kept only behind a written return or an assignment. So fn same(name: String) -> String { name } — the shortest program that keeps anything — lowered to &String and failed in rustc with mismatched types about a file nobody wrote, and so did a parameter handed out through an if or match arm or through let s = name; return s. D4: the walk reads a body’s last expression as a return, descends through if, match and blocks, and follows a let’s second name. A let that is only read still keeps nothing. No committed ledger changed.

In the tree

What is still open

A name bound to a literal is a view — let s = "x" then Person { name: s } is refused with the .to_owned() help; making a binding follow a later use is inference across statements, and a larger question. A method’s arguments are not lent yet, so a literal there is constructed even where the method only reads.

[0.0.182] — 2026-09-24

A route is not refused by what its handler touches, and no report lists it either — ADR-206, the owner’s answer to open-decisions.md’s last remaining 0.0.174 question. Nothing is built, which is the decision, and no the former backlog file entry is created either: option 1 is the answer and not an unbuilt feature.

The question

Every function carries a touches column, closed over what its body calls. So the compiler already knows whether a handler reaches the filesystem, transitively — which no other language’s http.server knows, because no other one has the column. What to do with that was the question, and it named three options: refuse nothing, let a route declare what it may reach, or list it in nikaia --trust.

The answer, and where this page was wrong

D1 — no refusal. No reaching: at a route, no word for it, no code. A refusal on today’s granularity would mostly refuse programs that are right, and touches says file(path) read where the path is a parameter’s name and not a value.

D2 — and no listing either, which is the half the page recommended and got wrong. The owner’s reason decides it: in a real application almost every route reaches something, so a report whose rows are nearly all non-empty tells a reviewer nothing they can act on. It is the argument this project already makes about a gate — a gate that is a fifth false alarm teaches people to ignore it — in the other tool.

A listing is not free. The page called 3 the cheap direction because the data a later refusal would read has to be gathered anyway. What it did not count is the design: which touches are ordinary for a handler and therefore filtered, and which a person asks for on purpose — --extended, --including http, a name nobody has picked. That is a question with its own answer and not a 0.1.0 target. Option 1 costs nothing and stays extendable.

D3 names the distinction the page missed. ADR-108 D4’s listing works because it lists a way around a check: such a list is short by construction and prints none for a program with no exceptions. A list of what every handler touches is long by construction, and a report that is long by construction is read once. So a report earns its keep the way a refusal does — by what it leaves out.

And a sentence this project wrote at 0.0.178 is corrected

The question’s entry gained a paragraph claiming fs::Root’s arrival was option 3 arriving for the filesystem. It was not: one report lists exceptions and the other would list the ordinary case, which is the difference between them rather than a step from one to the other. This is the second time an answer has corrected how that page framed its own question (ADR-195 D5 was the first), and both are the page working: an entry written to be answerable is one whose framing can be argued with.

What a later record would need

ADR-206 §4 writes it down so it is not found again: the filter, the switch, the better granularity fs::Root already gives for one resource — and that a refusal stays available, because D1 says no to it on today’s granularity and nothing closes the door the root opened one resource at a time.

[0.0.181] — 2026-09-23

A bound reaches a caller inside the signature — ADR-205, the owner’s answer to open-decisions.md’s second question, in favour of that page’s recommendation. #162 closes and §1 is empty.

What was wrong

A call into a package’s generic function was not checked against its bound:

error: app/src/main.nika:6:5: the trait bound `Bare: Handler` is not satisfied
     = the trait `Handler` is not implemented for `Bare`
     = the trait `Handler` is implemented for `Static`

The position is right and the words are the backend’s, which is Part III C.1. Handler is written without the path the program must write, and Static is a name the program never mentions.

The check was four lines. What was missing was the key: the bound lived only in the AST of the unit that declared the function, and the ledger had no column for one — a signature wrote (h: $H) -> String and the : Handler was nowhere in it.

The decision

[fn."dispatch"]
signature = "[H: Handler](h: $H) -> String"

In front of the parameter list, and nowhere else. The signature already carries the type parameter as $H (ADR-074 D2), so a bound is the rest of that sentence; a second key that has to agree with the first is a second source of truth for one fact. [T] for a parameter with no bound is written nowhere, because the $T already says it exists.

Additive. The list stands before the (, so a signature written before this key existed parses unchanged — and no ledger in this tree changed a byte, because nothing in it publishes a generic function with a bound. That is what #162 meant by the first package to use it is the one that meets this.

And the trait is qualified on the way in

A package writes its own names bare and a consumer writes the path, because naming is per package (ADR-047 D1). Every prefix of the entry’s key is tried and the first spelling this program’s trait map knows wins. Without it the refusal never landed: the map held handler::Handler, the bound said Handler, and the check found no trait and passed the argument through. Where no spelling matches, the name stands as written and the fail-open line does what it always did (C.4).

What a reader gets

error[NK1164]: `handler::dispatch` asks for a `handler::Handler` here, and `Bare` is not one
     = `handler::dispatch`'s `H` is declared `[H: handler::Handler]`, so the type a caller
       picks has to answer for `handler::Handler`'s methods - and nothing in this program
       says `Bare` does (Part I, 4.7)
     help: write `impl handler::Handler for Bare { … }`, or pass a type that already has one

The path is in it and the help is a line the program can copy. The hash covers the bound, because it covered the signature already (ADR-100): a bound added or removed changes what a consumer compiles against, and now it changes the file too.

[0.0.180] — 2026-09-23

Whether a type compares is a question about its parts — ADR-204, and #157 closes the same day it opened. §1 holds one entry, and it is the one waiting on a decision.

What was wrong

error: binary operation `==` cannot be applied to type `P`
     = an implementation of `PartialEq` might be missing for `P`
     = consider annotating `P` with `#[derive(PartialEq)]`

on the author’s line, in the backend’s words about a file nobody wrote, with a help the source cannot take — Part III C.1 and C.2 at once. Every emitted type derived Debug and Clone and nothing else.

0.0.179 took the half that needed no decision: an enum whose variants hold nothing has no part that could fail to compare. This is the rest, and it subsumes that rule rather than sitting beside it — such a type has no parts, so the walk answers yes for it by the same sentence it answers for every other.

The walk

The one ADR-005 §1 Group B already asks for may this cross a thread, pointed at a different question:

Eq is the same walk with one primitive answering differently

NaN != NaN, so a float compares and is not an equivalence — and every type holding one differs with it, however far down. examples/json/src/main.nika is what said so: Json::Number(f64) made Json PartialEq, and a struct holding a Json was derived Eq beside it, which rustc refused about the generated file. A look at the type’s syntax would have missed that; the recursion does not.

A library type is never taken for an equivalence, because whether one is, is a second claim nobody has had a use for. So a struct holding a time::Duration compares and is not a map key yet — the under-approximation that cannot be wrong.

And a positional payload is a part

Nothing had recorded one. A Named variant’s fields were in the checker’s map under the qualified key; a Tuple variant’s were in no map at all, so Number(f64) looked like a variant holding nothing. The map that now holds them is what the Eq question reads.

NK1188

== on a type that does not compare is refused here, naming the type and what decides it, with a help a program can take: compare the parts that carry the answer, or give the type a method that says what equality means for it. There is no #[derive] to write, which is what made the old help a way out that cannot be taken.

The NK table gains it — and NK1187 too, which the compiler has emitted since 0.0.168 and the table never listed. A stale status note is a defect in its own right (README.md §1: a reader cannot tell a plan from a promise).

[0.0.179] — 2026-09-23

An enum’s cases are a column of the ledger — ADR-203, and with it ADR-018 D4’s Method, which is the last of that record’s data.

What was wrong

A [type."X"] entry carried a struct’s fields and nothing at all for an enum — an enum had no entry. So a consumer of a package could not see that the cases were Yes and No, and

match verdict::of(word) {
    verdict::Answer::Yes => { println("yes") }
    verdict::Answer::No  => { println("no") }
}

was NK1151, this match does not cover else. Two things wrong at once: a correct program refused (Part III C.4), and a way out that is the one ADR-146 D1 exists to prevent — an else makes a type gaining a variant silent in that program forever after.

The column

variants = ["Yes", "No"], one line per case as the source writes it: a bare name, a positional payload Name(T, U), or named fields Name { a: T }. A struct has fields and an enum has these, and neither has the other’s. Read from every ledger, with this unit’s own source winning.

A column and not a record of its own, because it is the same fact as fields for the other shape of type, in the same table. There was no second credible spelling — which is what makes it work rather than a question. How does a bound reach a caller across a package boundary? stays on open-decisions.md, because that one has two.

Two more the same program found

== on a unit-only enum. k == Kind::Post reached rustc as binary operation == cannot be applied to type Kind, with consider annotating Kind with #[derive(PartialEq)] as the help — the backend’s words about a file nobody wrote, and a way out the source cannot take. An enum whose variants hold nothing derives PartialEq and Eq now, which is a derivation rather than a caution: such a type has no part that could fail to compare. == on a struct and on a payload-carrying enum is #157, with the shape a fix takes and the reason a blanket derive is the wrong one — it would refuse the declaration of every type holding a lock.

match error { … } is a match over the type that arrived, where exactly one does. It was non-exhaustive patterns: IoError::NotText(_) not covered, about the generated file. The binding itself stays untyped, so this can only move a refusal and never add one: where the arms cover everything, nothing was said before and nothing is said now. Where several error types arrive the set is open and else is still the only answer (ADR-160 D4, untouched).

ADR-018 D4’s Method

pub enum Method { Get, Post }

in examples/http, and request.method() answers it. Two variants are the whole type, and that is std’s refusal rather than an omission: http1 answers 400 to any other method before a handler runs. method_text() is beside it for a log.

[0.0.178] — 2026-09-23

A path names its root at the call — ADR-108, whose §5 said nothing and now says D1 through D4. #189 closes.

The hole it shuts is path traversal: a name somebody else chose, joined under a directory, reaching the filesystem and leaving the directory through .. or a symlink. The record’s reasoning is why the check is in the call rather than a taint per value: a programmer sees a missing argument and not a taint, the check is std’s so checked and correctly checked are the same thing, and the way around it is one word that a report lists.

The root

let data = fs::map(name, fs::Root::Dir(store))              // name may not leave store
let text = fs::read_to_string("eins.txt", fs::Root::Anywhere)
fs::write(out, fs::Root::Dir(site), markup; append: true)

The check

By component on a resolved name, never as a string prefix (D3), because /data is a prefix of /data2 — which is a case in the test rather than a sentence in a comment. Both sides are canonicalised, so a symlink out of the root is the same answer as a .. out of it. A name whose tail does not exist yet is checked all the same: every component that exists is canonicalised and the rest applied on top, which is the half a plain canonicalize would have got wrong on exactly the call that most needs it — a write that creates.

What comes back is the name joined under the root and not the resolved one, which is D3’s never a rewritten name. sub/../ok.txt where sub does not exist resolves onto ok.txt; handing that to the operating system would serve a file that an open of the written name does not find. So the check answers inside, the operating system answers not found, and each says what it knows.

The refusal is io::IoError::Outside(what) — D3’s one case beside not found among the errors the entry already names. A case and not an error type of its own, so no program’s throws set grows a member, and the payload is what was asked for rather than where it landed: a message that printed the resolved name would tell whoever sent it what is on the machine.

The listing

nikaia --trust grows D4’s third place: every site that writes Anywhere, and every Dir("/"), which is the same thing in more characters — by file and line, because the security review of a program’s file access is that list and a list without line numbers is not one. A Dir the program worked out is not listed: the report is the list of ways around the check.

Which argument is the root comes from the ledger, not from a list of function names in the compiler: an entry’s signature names a parameter root. So the day fs::open or http::File is written, its sites are listed without a line changing there.

The corpus

Fifteen calls in examples/ and benches/, every one of them a command-line program whose path the operator typed, so every one of them honestly fs::Root::Anywhere — an operator’s own argument may point anywhere. The specification’s own blocks and examples/README.md are written the same way.

And one over-approximation the root found

A path in an argument is a constant, and the ordering analysis said it was a value read from somewhere else. That sentence is true of a field and of an index and not of a path: a path names an item — a unit variant, a constructor handed over as a value (ADR-140 D2), an associated constant — so there is nothing to read, nothing for an overlapped branch’s closure to capture, and nothing to carry to another thread.

Nothing wrote a path in an argument until every path call took a root, and then the over-approximation took the control pair of ADR-038 §4.5’s D7 experiment with it: two reads of two files, which have to overlap or that test proves nothing about a foreign call. So the narrowing arrives with the root rather than on its own, and overlap { } over three reads is what holds it there.

[0.0.177] — 2026-09-23

A view of the subject is handed back without a ref — ADR-202, and ADR-094 D1’s third position. #151 is closed, and §1 holds one entry.

What was wrong

pub struct Holder { text: String }

impl Holder {
    pub fn text(ref self) -> ref String {
        return self.text
    }
}

was two refusals at once — NK1131 self is borrowed here, so text cannot be handed back by value, and NK1104 this returns String, and the function declares ref String. Both are about one missing character, and neither named it.

And the one spelling that worked was the one the author should not write. return ref self.text compiled. So the state of the language was a reference the author writes is required in one position and refused in two others, which is what D1 exists to end — from the side nobody had looked at.

What the compiler writes now

The receiver is a position in borrows

A method whose result is a view and whose receiver is ref self records returns = "borrows(self)". It always belonged there — ADR-098’s column names the positions a result may point into, and a ref self is a view the caller gave. It was left out while nothing read the column across a receiver; the reader that does arrived at 0.0.176, and read no position at all for the accessor a program most often writes.

NK1131’s third way out, and only where it can be taken

The help names declare the result ref T and the view is what this line means where the field is handed back, and not where it is bound or passed: those have nowhere declared to point, so ADR-083 D2’s two ways out are still the whole answer there. Offering a third would be Part III C.2 from the other side.

What is open

The written ref is not refused, and one root still needs it (ADR-202 D2 and §4, #96). D1 writes the & where the place is inside a borrowed subject and not where it is inside a lent parameter — fn a(row: ref Row) -> ref String { return row.name } still wants the word — so the two are one piece of work: the inference reaches every root, and then the second spelling goes. D4 already stages its own refusals for this reason.

examples/http’s Request::body() is written, which is ADR-018 D4’s last accessor — the request’s whole surface is methods now.

[0.0.176] — 2026-09-23

A call says which of its arguments its result may point into, and the view analysis now reads that column. #152 is closed, and with it ADR-018 D4’s last two accessors: request.query(name) and request.header(name) are written on the request itself rather than reached through the head it holds.

What was wrong

views::analyse took no ledger. A view parameter handed to a call whose result is returned was treated as escaping through that result, so

pub fn query(ref self, name: ref String) -> ref String? {
    return self.head.query(name)
}

was NK2302 — and the program is correct: http1::Head::query is written returns = "borrows(self)", so its result points into the head and never into name. A correct program refused, which is Part III C.4. The way around it was not open either — declaring the parameter String refuses every caller’s literal, which is ADR-107’s text is one type, unbuilt.

What it reads

The two ledgers, at the call. returns = "borrows(…)" names the positions the result may point into (ADR-098), and an argument that is none of them does not reach the result through it. The receiver is one of those positions, and self is what names it.

By the whole key and not by the method name, which is the mistake worth recording: a wrapper keeps the name it wraps, so a lookup by name alone let Request::query find itself among the candidates — and its own column, the very thing being decided, answered for the callee’s. The receiver is typed from what the source wrote down: the subject, a field of the subject, or a parameter.

Everything unwritten reads as nobody said — no entry, an empty column, a receiver this walk cannot type — and the refusal stands there. That is ADR-010 D1’s polarity: an analysis that cannot see is not allowed to conclude that nothing happened. A local’s type is the checker’s business and this walk runs beside the checker rather than after it, so a call on one still refuses.

What is open

#151 — a method cannot hand back a view of a field it owns — is untouched by this: that one is about the & at a return, not about what a callee’s column says. examples/http’s body is still a pub field and still says so where the accessor would be.

[0.0.175] — 2026-09-23

A handler can ask its request — ADR-018 D4’s surface, as far as the language reaches, which #105 said none of it can be built before there is a server to bind to. There has been one since 0.0.166.

std::http1 keeps what the parse was throwing away

Two the first Nikaia program to hold a Head found

Both rustc about a generated file (Part III C.1), both in std:

A help that could not be taken

NK1131 said declare the result &str and write return &self.text. Both halves are impossible: &str stopped being a spelling at ADR-184 D4, and a & a program writes is NK1137 since ADR-094 D1 — and doing what it asked gets NK1131 and NK1104. That is Part III C.2, a way out that cannot be taken is not one. The message names ADR-083 D2’s two ways out now — .clone() and a self receiver — and says outright that a view is not a third.

And two it could not fix, each with its reproduction

Two numbers moved

Both block D4’s request.query(name) and request.header(name); both are reachable meanwhile through the head the request holds, which examples/http says in a comment where the accessors would be. crates/nikaia/tests/project.rs drives the query and the header over a real socket, asked in a case neither the client nor the handler wrote.

[0.0.174] — 2026-09-23

The questions go where questions go — a pass over the former backlog file against its own rule, citing a question is not asking it, which is a failure that file has now had three times and the third was written two versions ago.

What was wrong

the former backlog file’s head says it outright: an entry that names a question elsewhere is not finished until the link resolves, and it says the failure was found twice before, at 0.0.150, by following its own links. #162 made it a third time. It said the ledger has no column for a bound and that whether that is a new key or a widening of the signature language is ADR-106 D3’s table to extend, and no record does it — naming a decision and writing it nowhere.

Two questions, now asked

And two sentences that had stopped being true

Nothing in the compiler changed. open-decisions.md holds three questions, and every question the former backlog file names now resolves to one of them.

[0.0.173] — 2026-09-23

A field handed back out of a parameter keeps it — #156 closed, the package after the one that found it.

Four lines were enough

pub struct Answer { pub text: String }

pub fn say(answer: Answer) -> String {
    return answer.text
}

lowered to pub fn say(answer: &Answer) -> String { answer.text }, and rustc answered cannot move out of answer.text which is behind a shared reference about a file the author never opened — Part III C.1.

The answer is a widening, not a refusal

The two halves it has to get right

Where §1 stands

One entry: #162, a call into a dependency’s generic function not checked against its bound, which needs a ledger column no record decides. The five before it were each found by running something, and the last three by running what the package before them had just made possible — which is what this section’s method produces.

[0.0.172] — 2026-09-23

A dependency’s unit is checked in its own namespace — [#159](https://github.com/Nikaia-Language/Nikaia/issues/159) #159 closed, one package after the one that found it.

The defect

pub fn dispatch[H: Handler](h: H) in a package compiled on its own and was NK1126 — nothing says it has a method — the moment a program depended on it, without the program calling it. Every unit of a build is checked, and a dependency’s was checked against the program’s ledger, whose keys absorb had qualified: Handler had become handler::Handler, and the package’s own file writes the bare word, as its author must (ADR-046 D2 gives no import to write). A correct program refused, which is Part III C.4.

The fix, and the one that was wrong first

And two the fix revealed rather than made

Both were found by building what the package before this one had just made writable, and then by writing a fixture for it. §1 holds two entries and §2 holds 33.

[0.0.171] — 2026-09-23

A bound takes a path, and the ledger records traits and impls — ADR-106 D1, D3 and D4, which closes #193.

The path

The two tables

What it answers

ADR-078 §4 left a trait a package publishes as a question about modules, and that sentence outlived the record: #193 repeated it as nobody has decided until 0.0.170’s pass, and the doc comment on Ledger::traits carried it too. ADR-106 had decided it; what was missing was the build.

Three tests in crates/nikaia/tests/project.rs, each two packages, because inside one the module’s names are the package’s and a bound needs no path at all: the consumer’s own impl, the package’s impl, and a type that implements neither.

And one defect, found by building the shape from the other side

#159: a package’s own bound is refused when the package is a dependency. pub fn dispatch[H: Handler](h: H) in a package compiles on its own and is NK1126 — nothing says it has a method — when a program depends on it, without the program calling it. A dependency’s unit is checked against the program’s ledger, whose keys were qualified while it was absorbed, and the dependency’s own file writes the name unqualified as its author must.

Reproduced with this package’s change stashed, so it is pre-existing and not the path’s. The cheap fix is the wrong one: giving traits both spellings would make a program’s own [T: Handler] resolve to a dependency’s trait, which is a second spelling of a name ADR-046 D2 says must carry its path. What the case wants is a dependency’s unit checked in its own namespace, and that is a question about how the units are walked.

[0.0.170] — 2026-09-23

A pass over the former backlog file, closing what is answered and saying what is blocked — no compiler change, one documentation comment, and four entries that were telling a reader something that had stopped being true. docs/README.md §1 makes a stale status note a defect in its own right, because a reader cannot tell a plan from a promise.

Closed

Corrected

Where this leaves the list

§1 is empty and §2 holds 34 entries. Three closed in the last three packages — use std::<anything> (#146), the grammar action block (#202), and the described call’s threads (#195) — and this one is the pass that makes the rest say what is true.

[0.0.169] — 2026-09-23

A grammar’s action is the block after the pattern, and two borrowed names go — ADR-120, built whole, which closes #202.

The action

One place in a grammar where a space decides something

tag and digit1 are NK1187

The error corpus keeps its row and changes its question

C2 was rule A -> i32 = n:digit1 { n } — an action block whose arrow was forgotten, read as a repetition bound, answered with expected a digit. That input is now the form. The row asks the same reader question from the other side: it is the old arrow now, and what comes back names pattern { action }. docs/error-corpus.md carries both halves, because the finding that produced the lookahead is still the finding.

[0.0.168] — 2026-09-23

use std::<anything> is refused here and not by rustc — NK1186, which closes #146 and leaves that file’s §1 empty.

The defect

The list it is answered from is the join of two, and neither half alone is right

What a reader gets

§1 is empty, and that is a statement about what has been run

Every entry that section ever held was found by running something — the specification’s own programs, the corpus at both settings, a two-file project, a change made twice — and never by reading the code. So an empty §1 says what has been run, not what is correct, and the way to lengthen it is that method.

[0.0.167] — 2026-09-23

@borrowed is removed, and the word that allows a tether is @tethers — ADR-201, narrowing ADR-008 D6’s assertion half and leaving its inspection half standing.

Why the word was in the wrong place

What was removed

What replaces it

What a reader meets

[0.0.166] — 2026-09-23

The HTTP server, and nikaia serve is cut — ADR-194 D5 built and ADR-200 written, which together close #90’s order down to one step.

A Nikaia program answers HTTP over a socket

std::http1 — HTTP/1.1’s text half, and where the line runs

Two lowerings were wrong about a function-typed parameter

Both were rustc about a file nobody wrote (Part III C.1), and both were found by being the first program to write ADR-102 D1’s parameter for real.

nikaia serve is cut

ADR-200, narrowing ADR-194 D2 and D3’s operator half.

One handler and not a route table, which is a language limit named

Two numbers moved, and both say what moved them

The question that was deferred is open

[0.0.165] — 2026-09-22

A readiness wait is a registration, not a worker — ADR-199, which is #90’s second step and not what that line said it was.

What was wrong, and it was not speed

The measurement moved the design

ADR-009 D4 asks for a number before a shape is chosen. #90 called this step keep registrations:

  per wait
a poller built per wait, which is what the worker did 7.4 µs
one poller, add and delete around each wait 2.5 µs
a registration kept and re-armed 1.2 µs
the whole round trip through a worker 35 µs

The shape

Two things met on the way

[0.0.164] — 2026-09-22

A socket in std — ADR-198, building ADR-194 D1, and with it the step #90 calls the blocker.

A .nika program binds a socket and both ends talk

use std::net

fn main() throws {
    let listener = net::listen("127.0.0.1:0")
    let mut connection = listener.accept()
    let asked = connection.read()
    connection.write("pong")
}

The column that had nothing to fire on

touches gains socket

And a block of the specification started compiling

What is stated rather than hidden

[0.0.163] — 2026-09-22

The row a bound cannot answer — ADR-193 D4’s intra-crate call graph and use table, which is the last step of that record.

What it finds

# `across_a_thread_unchecked`: reaches `tokio::spawn` through `on_one_worker`.
#   A sink reached through a call says *this function threads something*,
#   never *this function threads your argument* (ADR-193 D4) - connecting
#   those is dataflow through a closure capture and is not built.
#   Does this put it on a thread?  -> threads = true | false

The use table

What the grammar grew

And a hole in the release step, walked into rather than found

The sinks

[0.0.162] — 2026-09-22

The describer proposes and never claims — ADR-193 D3 and D5 built, which is the half of that record a person reads rather than the compiler.

D5 — the promise a toolchain cannot check

D3 — a Send bound is evidence, and evidence is a sentence

# `across_a_thread`: the parameter `value` is bound `Send`.
#   Does this put it on a thread?  -> threads = true | false
#   Seen and not claimed (ADR-193 D3): a `Send` bound says the callee **may**
#   send it, which is usually `spawn` and is sometimes an API keeping a door open.
[fn."hyper_shim::across_a_thread"]

What it needed

[0.0.161] — 2026-09-22

A path is relative to the file it is written in (ADR-197) — and with the three experiments building again, threads gets measured, and both crossings are refused by this compiler.

The defect, and the record it needed

A second defect, found by getting two answers to one change

What that let us measure

[0.0.160] — 2026-09-22

threads is a column — ADR-193 D1 and D2 built, and with them the half of ADR-038 D7 that was left open.

The column

What it turns back on

The defect this found

[0.0.159] — 2026-09-22

A module is a file as often as it is a block — the last of the scraper’s three named limits that a parser can close, ADR-195 D3’s removes the class rather than the instance finished.

What a file is

What is left

[0.0.158] — 2026-09-22

The scanner is out, and nikaia describe reads a crate with a parser written in Nikaia — ADR-195 D3 built, on ADR-196 D1’s route.

What changed at the boundary

The measurement, as an assertion

an_item_that_is_only_text_is_not_described_and_a_re_export_is in crates/nikaia/tests/describing.rs. On one crate the scanner wrote entries for four functions that do not exist:

what it is where the scanner the grammar
spectre inside a block comment an entry nothing
phantom second line of a string literal an entry nothing
hidden inside a private mod an entry nothing at that name
buried private mod inside a pub mod an entry nothing
seen inside pub mod shown fremd::seen fremd::shown::seen
rescued pub use private::hidden as rescued nothing an entry

What the grammar grew for it

What is left of the third limit

[0.0.157] — 2026-09-22

The grammar moves into the toolchain, and the route is running — ADR-196 D1, no longer a decision but a thing a Rust program calls.

Where it went, and why it is not an example any more

pub use is an item, and the reason is C.4

Three things the lowered file taught, which is the point of the route

The defect this found

[0.0.156] — 2026-09-22

The grammar, and the route it travels — ADR-195 §5 step 1 written, and ADR-196 answering a question that record left open: how does a Nikaia-written piece reach the Rust program that calls it?

examples/rust-signatures.nika — a Rust file’s public surface, read by a Nikaia grammar

ADR-196 — a Nikaia piece joins a Rust program as an ordinary Cargo module

Two gates the new example tripped, and both were right to

What this leaves

[0.0.155] — 2026-09-22

nikaia describe is a Nikaia program — ADR-195, the owner’s answer, and the reason is not the one 0.0.154 led with. No code changes: the ruling and the order.

I put a measurement in front of a decision that does not rest on one

The decision

Why describe is the right first one

[0.0.154] — 2026-09-22

Two things are called a parser, and only one of them was decided — the owner’s differentiation, asked properly. No code changes.

The distinction, which is the whole of why this is a separate question

  the HTTP/1.1 parser describe’s Rust-signature parser
runs per connection, in a server’s hot path once per crate, when a person types a command
the compiler needs it to serve never — NK2504 names the command and a person runs it
decided Rust for the MVP (ADR-194 D5) open, and now asked

Why describe is a good self-hosting candidate, measured

What it would cost, against std’s actual surface

The recommendation, and the thing to do before taking it

[0.0.153] — 2026-09-22

Two servers, not one — ADR-194, the owner’s answer, and it dissolved a conflict this session had built. No code changes: the ruling and the order.

The choice was false, and that is the decision

The five decisions

Deferred on purpose, and written down so it is not mistaken for settled

And open-decisions.md is empty again

[0.0.152] — 2026-09-22

A package’s type could not be constructed through the package that declares it — a defect found by writing ADR-018’s own shape, fixed, and the whole chain now runs across a package boundary. The owner’s two MVP decisions are recorded with it.

The defect

And the MVP’s whole surface is measured, before any of it is built

crates/nikaia/tests/project.rs runs ADR-018’s chain across two packages — four things at once, because the point is that they compose:

The socket is the only thing missing.

Two more things that came out of writing it

The owner’s two decisions, recorded

[0.0.151] — 2026-09-22

The server’s runtime blocker has been built since ADR-121, and the microservice question is asked — the owner’s HTTP from the command line, like Python’s. No code changes.

#90 said the head of the list was blocked on the runtime. It is not.

And more of the ground is already there than the entry suggests

The question, in the shape that page asks for

Who decides what is on the network? Four options, and the recommendation is D with C kept, and never A:

Why D. ADR-038 D5 moved cleanup-deadline out of the manifest for exactly this reason: how long a program waits at exit is an operating property, and a build-time key cannot be tuned by the operator — who is not the person who compiled it. What is exposed to a network is an operating property in that same sense, and more sharply: the person who compiled a function and the person who decides it may be reached from outside are routinely not the same, and only the second knows what the network is.

Three smaller things it leaves, one of which no other language can do

[0.0.150] — 2026-09-22

Citing a question is not asking it — the list’s own cross-references followed, and four of them did not resolve. Found while answering is the tether next?; no code changes.

Two questions were named in the entry they block and never asked

And two were answered and not written back

#105 was stale about the language as well

Why this came out of a question about the tether

[0.0.149] — 2026-09-22

A description says whether it threads, and the describer proposes the answer — ADR-193, the owner’s A with D. The question leaves open-decisions.md and the work arrives in #124. No code changes: this is the ruling and the task, not the build.

What it decides

The task, in five steps each usable without the next

  1. the column, its parse and render, and NK2502 reading it;
  2. the signature scan and the note it writes, on the scraper as it stands — this already carries the safe half;
  3. syn and cargo metadata under it, which makes step 2 reliable rather than lucky;
  4. unsafe impl Send/Sync flagged — cheap, sound, independent of 3;
  5. the call graph and the use table, for the unchecked row.

And two pages got shorter

[0.0.148] — 2026-09-22

Step 2 said the right thing about two of three limits and named them in the wrong order — a correction to 0.0.147, and a better reason for the one that stays. No code changes.

The ordinals were swapped

And the macro limit has a better reason than syn

[0.0.147] — 2026-09-22

Option D gets its pipeline, checked against the repository’s own worked example — the owner’s five steps, read against examples/foreign-runtime/shim, which was written to study this exact hole and has both shapes in it side by side. No code changes.

The shim answers the question that was about to be argued

across_a_thread<T: Describe + Send + 'static>      → on_one_worker(move || value.describe())
across_a_thread_unchecked<T: Describe + 'static>   → on_one_worker(move || smuggled.describe())
                                                       // the bound is gone: `unsafe impl Send for Smuggled<T>`
on_one_worker<F: FnOnce() -> String + Send + 'static> → tokio::spawn(…)

Step by step

And a sixth step the list did not have

[0.0.146] — 2026-09-22

The threads question gains an option that helps the describer instead of interrogating them — the owner’s, and it caught a false premise in the question’s own option C. No code changes.

Option C said the describer could not see it, and that was wrong

Option D — A, and the describer proposes

But D2’s license is soundness, not a good hit rate

And D is what answers A’s cost rather than a second thing to build

[0.0.145] — 2026-09-22

An idea recorded, and five notes that were not in the index — no code changes.

unique-ownership.md: a door with no lock in it

The notes index had five holes in it

[0.0.144] — 2026-09-22

The body’s run-or-kept answer decides a code parameter’s shape — ADR-192, the owner’s option A, closing open-decisions.md’s question and narrowing ADR-122 D1.

Read at the right altitude, it is not a new rule

D1: the shape follows the body

D2: no column is added

D3: the two ends read it the same way

D4: what it traded away, and what answers for it

What moved and what did not

[0.0.143] — 2026-09-22

A decision written at the wrong altitude — the owner’s critique of open-decisions.md’s ADR-122 entry, checked against the tree and upheld. No code changes; the question does.

The question was phrased as a backend ticket

And the coherence argument it rested on is weaker than it looked

Three places where this language already answers yes, read off the tree rather than argued:

And a fourth, built and shipped: ADR-094 D1 — the callee’s body decides whether the caller’s argument gains a &, and the caller writes nothing. The body decides the caller’s lowering is not a rule this change would break; it is one the language already keeps.

Two places the critique overshot, and one it was missing

So the entry is rewritten and the recommendation is unchanged

[0.0.142] — 2026-09-22

?? joins two views, and a ?. takes one of a binding and not of a temporary — ADR-191, answering open-decisions.md’s question with the owner’s option A and closing all but one shape of ADR-113.

Building A in its literal form opened a new hole

So the question is asked of the root

NK1185: a fallback that owns what the left side views

The migration is the one line the recommendation predicted

What is left is a lowering, not a state

[0.0.141] — 2026-09-22

The tether is needed at an escape and nowhere else — ?. has no escape in it — ADR-190, correcting the reason ADR-189 D3 gave one version earlier.

The owner asked the right question

Measured: three of the four shapes are Borrowed

shape what happens state
the receiver is a local, the view stays in its scope compiles, runs Borrowed
the receiver is a temporary, the view is consumed in the same statement compiles, runs Borrowed
view ?? view — user?.name ?? "nobody", and a literal is a view compiles, runs, zero cost Borrowed
the view is bound past a temporary — let n = find(1)?.name temporary value dropped while borrowed an escape

What actually blocks it is one question about ??

The record is not rewritten

The method it cost

[0.0.140] — 2026-09-22

?. lends its receiver where that needs no representation, and the third case waits on the tether — ADR-189, building two thirds of ADR-113 and narrowing #192 to what is left.

What a program heard

Reading the record against the code found it is not one piece of work

The two thirds that need nothing

And the third is left alone rather than refused

Upkeep found while reading

[0.0.139] — 2026-09-22

The escape set is the language below’s, written down once and refused here — ADR-188, closing #197.

What a \ means was never written down, and what an unknown one was told was rustc’s

One table, two readers

NK1184, in the checker, naming the set

And it refuses nothing that existed

[0.0.138] — 2026-09-22

Rust has a stable async closure, and a premise under twelve sentences is false — ADR-187, raised by the owner.

The premise

The number, in the tree

row ns/call ×
plain closure (impl Fn) 0.31 1.0
boxed future (impl Fn(A) -> Pin<Box<dyn Future>>) — ADR-122 D1’s shape 11.99 38.3
async closure (impl AsyncFn(A) -> R) — the shape said not to exist 1.37 4.4
plain closure, twice (the control) 0.29 —

Where the conclusion survives, the reason is replaced and the conclusion is not

Where it rested on the premise alone, it is reopened and not rewritten

And the records keep their text

[0.0.137] — 2026-09-22

A parse keeps the text its record views — ADR-186, closing #140. §1 is empty: every defect that file carried is closed.

The column was not being withheld — it was answering the wrong way

And it needed no mechanism

One predicate, two columns

What it moves and what it does not

[0.0.136] — 2026-09-21

A view meets two more constructs that were never told — ADR-185, closing #139 and #153. One entry is left in §1, and it waits on a decision rather than on work.

Both messages told the reader to do something the language forbids

The first is a refusal and not a lowering

The second binds the tail’s value before the Ok

[0.0.135] — 2026-09-21

The living pages say ref — the upkeep ADR-184 owes, and docs/README.md §1’s own rule: a stale note is a defect, because a reader cannot tell a plan from a promise.

[0.0.134] — 2026-09-21

Array[T] is an array of any length, and & leaves the language — ADR-184 D3 and D4, finishing the record 0.0.133 began.

Array[T] is defined and not forbidden

& leaves, and the bracket form with it

A third printer of a written type, found the hard way

What the mechanical half cost, honestly

[0.0.133] — 2026-09-21

A view is spelled ref X, and text has one noun — ADR-184 D1 and D2, the owner’s answer to the two questions 0.0.131 put on open-decisions.md. That page is empty of spelling questions now.

What the measurement said, and what it was about

One rule, stated once

ref is reserved, and the corpus is not what decided that

What moved, and what did not

[0.0.132] — 2026-09-21

A path’s head is one of five things, and a name that is none of them is refused — ADR-183, closing #141 and with it the other half of NK1171.

What the reader used to get

The measurement is what made it answerable

The list, written down and asked in order

And a head the next file declares is a head

Two blocks of the specification stopped lowering, and both are the point

Where the two refusals divide

[0.0.131] — 2026-09-21

A for lends, and everything downstream of the binding has to know it — ADR-182, closing #138 and #147. Two defects with reproductions, and chasing them found three more — one of them a wrong answer nothing said out loud.

The two that were written down

A trait and not a *, for the reason index::at is one

And the read at a range answers a view

Four more, found by chasing those two

Two questions went to the owner

[0.0.130] — 2026-09-21

--comptime prints what was unrolled, and the unrolling reaches across the files of a package — ADR-181 D5, which is ADR-088 D6 and the last of that record’s six decisions this compiler can supply. Part II 10.3 is built, less T::variants.

The report

And that is what found the defect: an unrolling stopped at the file

What is still out

[0.0.129] — 2026-09-21

T::fields is a list the build walks, and the loop over it is unrolled per type — ADR-181, building ADR-088 D2, D4 and D5. Part II 10.3’s own describe compiles and runs, which is the first time anything in that section did.

What was specified, and which half was built

The loop is unrolled, and that is a derivation rather than a keyword

The body is checked once per turn, and the message says which turn

What is still out

[0.0.128] — 2026-09-21

A fixed table holds a declared type, as a view of one — ADR-180, answering the second of the two questions 0.0.125 put on open-decisions.md. That page is empty of build-time questions now.

The message was the wrong claim, and the feature was one line away

Copy is not derived, and that is the option not taken

And the table opened a second door onto an old hole

[0.0.127] — 2026-09-21

&[T] is a type of this language — ADR-179, the owner’s Option A on open-decisions.md’s question of whether a build-time value may cross as a view into what the build allocated. ADR-079 D1 has said since it was written that a Vec[T] arrives as a &[T]; four records later it does.

What was built instead, and where it ran out

So there are two writers for one spelling

The crossing is walked against the declaration

NK1179, because the two lines look identical

What this does not do

[0.0.126] — 2026-09-21

Tier-1 staging is withdrawn — ADR-178, answering open-decisions.md’s question of whether the box was still on the list. Nothing was built; a promise nobody intended to keep stopped being counted as one.

What the measurements said

So the box leaves the page

What is not withdrawn

[0.0.125] — 2026-09-21

What a build-time value is, widened — ADR-079 §5 — after the owner asked whether a Vec and an enum come across from a comptime, and then whether byte slices and compositions may. The answer was partly, and measuring turned the two questions into five gaps, four defects one construct over, and two absences that need a ruling.

Five shapes cross that did not

The enum is what makes the refusal a question about the value

Four defects one construct over, found by writing the programs

What is still out is one absence rather than five

[0.0.124] — 2026-09-21

A grammar runs while the program is built, by compiling the parser it generates — ADR-177, and Part II 10.2 A is a program.

The decision is what not to do

So the generated parser is compiled and run

Refused

Measured, and it is the finding worth keeping

Also

[0.0.123] — 2026-09-21

A file a build reads is named three times — ADR-072 entire, with ADR-116 D2’s asset("…") under it. The record said built: no because const has no syntax, so there is nothing to check yet; the order it implied has been walked, and the check finally had a const to hang on.

Added

The default is the whole point

Refused

The cache is right about it (D7)

And it adds no sandbox

[0.0.122] — 2026-09-21

from is an ordinary name — ADR-116 D1 and D3, and the drift that fell out of checking it.

Changed

And the reservation was paying for one sentence, which is still said

Fixed: the page and the table had drifted

[0.0.121] — 2026-09-21

with — a copy of a value with named fields changed — ADR-118, Part I 4.2. A word that had been reserved since 0.0.8 and had no rule.

Added

Refused

Fixed, one construct over

Measured: the corpus example moved

[0.0.120] — 2026-09-21

The two bounds that ask what a type is — ADR-088 D2 and D3, the first part of Part II 10.3 to be built.

Added

The hole this would have opened, closed in the same package

Where 10.3 stands now

[0.0.119] — 2026-09-21

A name beside a type this program declares — a defect ([#141](https://github.com/Nikaia-Language/Nikaia/issues/141) §1), found while reading what Part II 10.3 would need.

Fixed

Said

Measured, and it is what bounds the fix

[0.0.118] — 2026-09-21

A map the build can see — ADR-176, answering open-decisions.md’s what does a program write, for a map the build can see? with option B, and closing the last unbuilt part of ADR-079 §3.

Added

The measurement removed the feature it was taken to size

Refused, by name and at the line that wrote it

Said

Found by running it, and both would have shipped

Verified

[0.0.117] — 2026-09-21

The fixed map’s threshold is measured — ADR-079 D3 said how it is looked up is the compiler’s and named no number, and staging-candidates.md ends on the rule that says one is needed before any of it is built.

Measured

The trap, which is the most useful line in that file

Asked

[0.0.116] — 2026-09-21

A constant is an item, and an item is visible wherever its file is — which answers 0.0.115’s one deliberate limit and a defect that had been sitting beside it.

Fixed

Added

Changed

[0.0.115] — 2026-09-21

A comptime reaches across a file boundary — and the ledger needed nothing new, which is the part worth saying first.

The ledger was never the gap

Added

Not answered, deliberately

[0.0.114] — 2026-09-21

A sync method of this program’s own folds now — the wall the owner asked about two packages ago, and the reason for it was never sync.

Added

Fixed while building it

Changed

[0.0.113] — 2026-09-21

A build-time text is the value, not the spelling — 0.0.112’s refusal of .len() over text was a representation showing through, and the owner asked why.

Fixed

Added

Found

[0.0.112] — 2026-09-21

Text while the program is built — the thing NK1127’s own note had been calling out as missing since the evaluator gained a call, and the other half of ADR-079 D1.

Added

Decided by building it

Changed

[0.0.111] — 2026-09-21

A build-time table may be grown with push — which is ADR-079’s own title, growable going in, fixed coming out, and which 0.0.108 said was not possible.

Added

Fixed

[0.0.110] — 2026-09-20

Two of the three points the language is short of are not work, and the page said they were. No code changed; what changed is that the roadmap stops sending a reader to a design space that has been decided and to a direction the project examined and did not take.

Changed

Asked

[0.0.109] — 2026-09-20

Every refusal this compiler makes about a line of a .nika program names that line — ADR-171’s remaining eighteen sites, which that record’s §4 left open as a different walk.

Changed

Not changed, on purpose

Changed (the page)

[0.0.108] — 2026-09-20

A build-time value may be an array (ADR-175) — ADR-079 §3’s evaluator that can loop and push, which three records have been waiting on. The loop was built; what a loop had nowhere to put was a value.

Added

Fixed

Changed

[0.0.107] — 2026-09-20

Two roadmap boxes were describing a compiler from several records ago. No code changed here; what changed is that the page says what the tree does, and each claim was re-measured rather than remembered.

Changed

[0.0.106] — 2026-09-20

A bound is enforced where it is used — ADR-174, the second half of what a bound is for, and the one nobody was asking.

Added

Found

Changed

[0.0.105] — 2026-09-20

A field of a generic impl reaches the body as a type, which is the half of ADR-074 D1 that was written down and not held — and three refusals the language owes came back with it.

Fixed

Found

Changed

[0.0.104] — 2026-09-20

The README stops being a second copy, and the roadmap says how far along it is in numbers a reader can re-derive.

Changed

Added

And it is checked

[0.0.103] — 2026-09-20

What a parse fails with has a name — ParseError (ADR-173), the owner’s answer to the question 0.0.102 asked. And naming it made a miscompilation visible.

Added

Fixed

Found

Changed

[0.0.102] — 2026-09-20

The last ["?"] is asked about, and a miscount of mine is corrected — the second found by the first.

Corrected

Asked

[0.0.101] — 2026-09-20

A crate, a description, and a refusal — the chain three entries were waiting on, run from a program; and the order that said they were waiting, which had gone stale.

Measured

Fixed

[0.0.100] — 2026-09-20

An error that newly reaches a catch is named once (ADR-101) — NK2402, and the record said it could not be built.

Added

Measured

Stated rather than left as a gap

[0.0.99] — 2026-09-20

The describer reads fields (ADR-123 D2) — the one line three entries were waiting on, and the last thing in the reviewed description that a hand had written.

Added

Fixed

[0.0.98] — 2026-09-20

The hash rule holds (ADR-104 D5, on ADR-100 D3’s rule) — §5’s step 3, and the line the previous package left: the description records the crate source’s SHA-256 and nothing compares it.

Added

Fixed

Left open

[0.0.97] — 2026-09-20

nikaia describe <crate> writes the draft (ADR-104 D2, D3) — the command NK2504 has been naming since the refusal was built, and §5’s step 2.

Added

Measured

Left open

[0.0.96] — 2026-09-20

The eager walks of a pausing sequence, both halves of them (ADR-172 D5) — which 0.0.95 named as the next step and said needed two things rather than one.

Added

Left open

[0.0.95] — 2026-09-20

io::lines().count() counted the failures as lines, and every walk of a pausing sequence but the for is refused now (ADR-172 D5).

Fixed

Corrected

Left open

[0.0.94] — 2026-09-20

A for may iterate something whose step pauses (ADR-172) — the question docs/open-decisions.md carried, answered A by the owner and built.

Decided

Added

Measured

Left open

[0.0.93] — 2026-09-20

A description’s entries reach the analyses (ADR-104 D1) — the first sentence of the record, and the thing the file it asks for exists to do.

Fixed

Measured

Stated rather than left as a gap

[0.0.92] — 2026-09-20

A call that can reach a lock is refused under its own code — NK2503, which ADR-039 D6 decided and Part III C.6 wrote out. And the thing that stood in front of it turned out to be a decision nothing built.

Added

Fixed

Measured

[0.0.91] — 2026-09-20

Two questions asked, and six more refusals given their line.

Asked

Added

[0.0.90] — 2026-09-20

A refusal from the lowering names its line (ADR-171) — half of the roadmap’s Phase 2 the compiler’s own anyhow errors, which are still text without a position.

Fixed

Added

Scope, stated rather than left as a gap

[0.0.89] — 2026-09-20

An overlap keeps every failure (ADR-115 D1 and D2, built by ADR-170) — and docs/open-decisions.md has nothing open.

Added

Decided

The one case it cannot cover

What this leaves

[0.0.88] — 2026-09-20

A type says what reading it touches, and I/O under a lock is refused (ADR-169) — NK2201, the third of the three things Part II 12.2 forbids, and the only one that had no code.

Added

Fixed

The one place silence is not fail-closed

Free today

[0.0.87] — 2026-09-20

Two catalogued-and-unemitted codes measured rather than left as questions (#175). Docs only.

Measured

Fixed

Not changed

[0.0.86] — 2026-09-20

The re-entrancy check is a build option now (ADR-039 D8, built by ADR-168) — and measuring it first found the note describing it to be wrong about two of its three clauses.

Fixed

Added

What declining it reaches, and what it does not

Why it is offered at all

[0.0.85] — 2026-09-20

The shared types are the language’s, and a prelude name written with a module is refused (ADR-167) — Part I 1.3’s list seen from the two sides ADR-162 did not reach.

Fixed

How it was found

Also

What this leaves

[0.0.84] — 2026-09-20

Cargo’s target-info probe is given an empty standard input (ADR-166) — half of #201 #201, an entry that had been open for months and had already recorded its own best hypothesis as refuted. The hypothesis was right; the measurement that refuted it was measuring the wrong stream.

Fixed

Why it took months

Added

What this leaves

[0.0.83] — 2026-09-20

There is no postfix ?? (ADR-165), and the refusal is where a reader meets the three spellings that replace it. The owner’s answer to the one question docs/open-decisions.md had carried.

Decided

Added

What this leaves

[0.0.82] — 2026-09-20

A joining block has one outcome, and a handler on it binds what the branches threw (ADR-164) — which is ADR-115’s first step, and where its secondary list will live.

Fixed

Also

What this leaves

[0.0.81] — 2026-09-20

A block that joins on the executor pauses, and its branches travel in the function’s own channel (ADR-163) — two correct programs rustc refused, in files nobody wrote.

Fixed

Why neither was visible

Also

What this leaves

[0.0.80] — 2026-09-20

The roadmap is brought level with what is built, and its tail is put in the order its own last section already states.

Changed

Fixed

Not changed

[0.0.79] — 2026-09-20

Diagnosis and safety are the language’s: eprint, access_all and update_all join Part I 1.3’s list (ADR-162) — and the list gains the test that would have said so.

Fixed

Why each is on the list

Added

What this leaves

[0.0.78] — 2026-09-20

Reading a map through the brackets is a T? (ADR-114, built by ADR-161) — a key is data and may be absent, so there is nothing there is an answer rather than the end of the program.

Added

The five rustc messages this cost, and how each was found

None of them would have been found by reading the emitted Rust.

What said so

Changed

[0.0.77] — 2026-09-20

A set with two error types in it is a generated sum (ADR-160) — the last shape a throws set can have that had no channel.

Added

Fixed

What it closes, and what it leaves

[0.0.76] — 2026-09-20

A library’s error type is a channel too, and it travels bare (ADR-159).

Fixed

What was already built

Added

Why bare, and not the envelope

What this changes in the tree

[0.0.75] — 2026-09-20

std names what it throws, and the measurement is why the record is small (ADR-158).

What was measured

Added

Changed

What did not change

What stops waiting

[0.0.74] — 2026-09-20

The compile baseline records whether a block compiles, and no longer what rustc called the failure — which [0.0.72] fixed one half of and this one finishes.

Fixed

Why it is the baseline and not a pin

[0.0.73] — 2026-09-20

The failure channel is the error type, where the ledger names one (ADR-157) — and Part I 7.1’s own catch example becomes a program that runs.

Fixed

Added

What keeps the box, and why each does

Held to by running it

[0.0.72] — 2026-09-20

A baseline that recorded which error rustc reports first, and therefore disagreed with itself between two toolchains.

Fixed

Why it is the test and not the block

[0.0.71] — 2026-09-20

Bytes is the language’s, and the tether it needs is refused where a program would reach for it (ADR-156).

Added

Why the refusal is the answer and not a stub

Which way it may be wrong

Left open

[0.0.70] — 2026-09-20

The tether’s analysis, built alone (ADR-008 D2, D7, D6) — the state is solved, written down and read by nothing.

Added

Why alone, and in this order

What it found

Left open

[0.0.69] — 2026-09-20

The tether, measured — and the one part of it that was a defect rather than a project (ADR-008 D9).

Fixed

Written down

[0.0.68] — 2026-09-20

The second question goes where questions go: does a T? have a postfix unwrap?

Changed

The recommendation, and why it is a program rather than an argument

[0.0.67] — 2026-09-20

A grammar’s entry earns its columns (ADR-082) — #140, down from four missing columns to one, and the one that is left turns out to be a different question.

Fixed

Found by building it

Left open

[0.0.66] — 2026-09-20

The most negative i64 has a spelling (ADR-136) — the completeness item #135 carried, closed the smaller of the two ways it named.

Fixed

Found by building it

Changed

[0.0.65] — 2026-09-20

Bytes is a question, and it goes where questions go (docs/open-decisions.md) — with the measurement in front of it, which turned out to be larger than the name.

Found

Changed

Left open

[0.0.64] — 2026-09-19

The prelude’s type half (ADR-154 D3) — which is the decision the record is named for: HashMap is the first thing outside the list.

Added

Changed

Found by building it

Left open

[0.0.63] — 2026-09-19

The prelude is a written list, small and closed (ADR-154) — the function half of it, which is the half that had a mechanism waiting for it.

Added

Found by building it

Changed

Left open

[0.0.62] — 2026-09-19

A channel is std’s, and only bounded (ADR-149) — which takes the unspecified mark off Part II 12.5 and leaves ADR-141 D2 with nothing in that chapter still marked.

Added

Found by building it

Changed

Left open

[0.0.61] — 2026-09-19

select { … } keeps the first arm to finish, and a task handle has cancel() (ADR-148) — which takes the last unspecified mark off Part II 12.4 and completes the pair overlap was half of.

Added

Decided while building it

Found by building it

Changed

Left open

[0.0.60] — 2026-09-19

Three sweep failures that are not failures, measured once and written down.

Found

Changed

Left open

[0.0.59] — 2026-09-19

A span of time, and the call that waits one out (ADR-150) — which takes half of Part II 12.4’s unspecified mark off the page and leaves select the other half.

Added

Found by building it

Changed

Left open

[0.0.58] — 2026-09-19

What blocks the database driver, written down where the next reader looks (ADR-143) — because a measurement nobody recorded is a measurement somebody takes again.

Found

Changed

Left open

[0.0.57] — 2026-09-19

sqlite3 from end to end (ADR-147 §5 step 5) — which completes that record and finds three things four libc calls could not.

Added

Found by step 5

Fixed

[0.0.56] — 2026-09-19

A handle may be absent, and T? is how it says so (ADR-155) — the question ADR-147 D3 left open, answered and built in the same round.

Decided (ADR-155)

Found by building

Open

[0.0.55] — 2026-09-19

Text a C library hands back is a CStr that std copies (ADR-147 D4) — which completes that record’s five decisions.

Added (ADR-147 D4)

Open

[0.0.54] — 2026-09-19

A C library’s handle is an opaque type … released by … (ADR-147 D3).

Added (ADR-147 D3)

Open

[0.0.53] — 2026-09-19

the former backlog file holds only what is open.

Changed

Fixed

[0.0.52] — 2026-09-19

A length beside a buffer is checked at the call (ADR-147 D2), which closes the overrun 0.0.51 left open.

Added (ADR-147 D2)

Fixed

Open

[0.0.51] — 2026-09-19

The C boundary lends a view, which makes most of C callable (ADR-147 D1, D5).

Added (ADR-147 D1)

Decided rather than built (ADR-147 D5)

Open

[0.0.50] — 2026-09-19

std’s ledger carries prose about every entry it publishes, which completes ADR-139 §5 step 3.

Added (ADR-139 D2)

Open

[0.0.49] — 2026-09-19

The array literal’s answer descends with the literal, which closes the defect 0.0.48 filed against itself.

Fixed (ADR-152 D4)

[0.0.48] — 2026-09-19

A fixed-size array is Array[T, N] (ADR-152), which closes two records’ open doors with one type.

Added (ADR-152 D1, D2, D3, D4)

Open

[0.0.47] — 2026-09-19

use std::collections::HashMap is refused, which completes ADR-140: all five of its decisions are built.

Added (ADR-140 D5)

[0.0.46] — 2026-09-19

A doc comment is a language feature, and the ledger carries it (ADR-139).

Added (ADR-139 D1, D2, D3)

Changed (one thing in the ledger’s format)

[0.0.45] — 2026-09-19

throw, return, break and continue where an expression stands (ADR-138), and two defects the record did not see.

Added (ADR-138 D1, D2)

Fixed (two things the record did not see, and both are one rule)

[0.0.44] — 2026-09-19

The six match pattern shapes, which is the rest of ADR-137 and the line the record opened with: calc.nika matched step.0 because it could not match step.

Added (ADR-137 D1, D2, D3)

Fixed (two claims Part III’s rewrite carried back)

[0.0.43] — 2026-09-19

A task nobody joined was left unwoken about two runs in five. Four instances of one mistake, and the test that had been reporting it was reporting it three times too rarely.

Fixed (an answered operation was invisible in both counts)

Changed (the test that was finding it three times too rarely)

Fixed (one count)

[0.0.41] — 2026-09-19

The third part is rewritten to the language standard, and the editorial pass that began with STYLE.md is complete.

Changed (Part III follows STYLE.md)

[0.0.40] — 2026-09-19

The second of the three parts is rewritten to the language standard.

Changed (Part I follows STYLE.md)

[0.0.39] — 2026-09-19

The first of the three parts is rewritten to the language standard.

Changed (Part II follows STYLE.md)

[0.0.38] — 2026-09-19

The specification gets a language standard of its own, ahead of the editorial pass that applies it.

Added (the specification’s language standard)

a..b includes its end and a..<b does not, everywhere in the language — and every range in the tree was rewritten in the same change, because the old spelling keeps parsing.

Changed (ADR-137 D3, D4, D5, D6)

Found and filed, not fixed (a task nobody joined is left unwoken)

What is still open

[0.0.36] — 2026-09-19

1_000_000, 0xFF, 0b1010 and 0o17, and a number too wide for an i64 stops taking this compiler down.

Added (ADR-136)

Fixed (a number too wide for an i64 crashed the compiler)

[0.0.35] — 2026-09-19

The list literal, which language-review.md called the one piece of table stakes this language was missing.

Added (ADR-135)

Fixed (a specification block that said mut nowhere and needed it)

Changed (A4 in the error corpus)

[0.0.34] — 2026-09-19

The build-time evaluator gets a loop, which is #178’s second step.

Added (ADR-073 D5’s second stage, continued)

Changed (what the refusal says is not in the stage)

[0.0.33] — 2026-09-19

The build-time evaluator gets a call, which three records have been waiting on.

Added (ADR-073 D5’s second stage)

Fixed (a name bound to the wrong number)

[0.0.32] — 2026-09-19

Fixed (the specification’s own version number, twenty-four packages stale)

[0.0.31] — 2026-09-19

Eight doors the records had left open on purpose, taken in one sitting — each the way its entry recommended. docs/open-decisions.md is empty again.

Added (eight records, ADR-147 to ADR-154)

Changed (ADR-151: the one of the eight that was already built)

Changed (the pages)

[0.0.30] — 2026-09-19

The question the last record made answerable, answered the same day.

Added (ADR-146: a match covers every case)

[0.0.29] — 2026-09-19

One character back from a meaning it did not have.

Changed (ADR-145: a match’s catch-all arm is else)

[0.0.28] — 2026-09-19

ADR-140’s fourth migration, and the record’s own plan for it was wrong in the one way that mattered.

Changed (a type is constructed by its anonymous constructor, in std too)

Fixed (two things the migration found, both older than it)

[0.0.27] — 2026-09-19

The last question of the previous round is answered, and the doors the records left open on purpose are gathered on the open page with a recommendation each.

Decided (the order of the five big pieces: the HTTP server last)

Added (nine open decisions, each with a recommendation)

[0.0.26] — 2026-09-19

The second of the questions is answered, and answering it found two more holes than it had asked about.

Added (ADR-144: a name denotes one thing)

Noted (a flake written down instead of having its bound raised again)

[0.0.25] — 2026-09-19

The LINQ question leaves open-decisions.md, and the answer’s first draft is corrected on the way: the compiler checks no SQL, because it knows no dialect.

Decided (ADR-143: the driver checks the SQL at build time, std::db is the protocol)

[0.0.24] — 2026-09-19

The first of the three questions is answered, and answering it pays back what ADR-140 D3 had cost.

Added (ADR-142: a grammar’s action may not pause)

Fixed (the async ADR-140 D3 had spread through every parsing program)

[0.0.23] — 2026-09-19

Added (one open question, asked by marketing)

[0.0.22] — 2026-09-19

docs/open-decisions.md was empty for one round. Three questions went back on it, and all three were found by building rather than by reading.

Added (three open questions, and the defect behind the first)

[0.0.21] — 2026-09-19

ADR-140’s third migration, and the silence it made visible.

Changed (a grammar’s rule is reached with ::)

Fixed (the import walk read the old shape)

Fixed (a bound naming a trait nothing declares reached the backend)

And one silence that had been failing open

[0.0.20] — 2026-09-19

ADR-140’s second migration, and a test that had been holding a claim it could not check.

Changed (throws and sync stand after the result type)

Fixed (the test guarding that greedy rule was asking the wrong question)

[0.0.19] — 2026-09-19

The first of ADR-140’s five migrations, and the record that was waiting on it.

Changed (the struct literal is the brace form, and Name(field: value) goes)

Added (ADR-133’s call half, which D1 unblocked)

Fixed (a method call’s options were dropped, and it is older than either record)

[0.0.18] — 2026-09-19

Every question the notes page held is answered, in one round and each the way that page had recommended. Seven records, one roadmap paragraph, and the specification’s own slips corrected on the pages that write them.

Added (seven records, and nothing is open)

Fixed (ADR-141: the specification’s own nine, sorted into two kinds)

Changed (the specification states each rule, and keeps its examples)

[0.0.17] — 2026-09-19

Four words leave the reserved list, and the sentence each of them was paying for moves to the refusal every stray name already gets.

Changed (loop, const, macro and quote are names)

[0.0.16] — 2026-09-19

The ledger’s type language gets a word for what is produced step by step, and a measurement that one record made about itself turns out to be wrong.

Added (Seq[T] and Par[T], the ledger’s words for a produced sequence)

[0.0.15] — 2026-09-19

A character that had two readings, and the compiler’s answer was an accident of what the parser happened to accept.

Changed (_ is the ignore pattern, and it is no longer a name)

[0.0.14] — 2026-09-19

The last of 0.0.10’s three shapes, and the one every reader comes looking for.

Added (else if)

[0.0.13] — 2026-09-19

Two of 0.0.10’s three shapes, one built whole and one stopped by a premise that turned out to be wrong about the grammar — and two defects found on the way to it, both older than the record that names their shape.

Added (/* … */ is a comment, it nests, and it is not a doc comment)

Changed (the leading ; is gone from a signature, and the call meets a question)

[0.0.12] — 2026-09-19

The ledger column that could only say yes, and the two refusals that had spent two records waiting for something to refuse.

Changed (crosses says no as well as yes)

[0.0.11] — 2026-09-19

0.0.8 decided the ring’s park and left it unbuilt. This is the package that builds it, and the only piece of the decision it does not reach is the one that turned out to be a language question rather than a runtime one.

Changed (the ring’s park hears the bell, so standard input suspends)

[0.0.10] — 2026-09-19

Decided (three shapes every reader expects, and eight questions written down)

[0.0.9] — 2026-09-19

Decided (what the library boundary had left open)

[0.0.8] — 2026-09-19

Closes what had been unreleased since 0.0.7 — the integrity round (the Seen stamp, one update form, overlap keeping every failure, the exit status), four reserved words freed and with given its construct, the bare-metal target, the grammar’s vocabulary page, the ring’s park, crosses three-valued, extern and unsafe reserved with constructs — and ends with its last package: a library for other languages, and the ignore pattern.

Decided (a library for other languages, and the ignore pattern)

Decided (four of the owner’s five open questions)

Decided (a grammar’s vocabulary is one page, and an action is the block after the pattern)

Decided (a target without an operating system)

Decided (with is a copy of a value with named fields changed)

Decided (loop, const, macro and quote are names)

Decided (from is a name, and a file a build reads is asset("…"))

Decided (an overlap keeps every failure)

Decided (reading a map through the brackets is a T?)

Decided (?. reaches through a view)

Decided (an expired cleanup-deadline is a failure of the program)

Decided (what leaves a lock is stamped Seen[T])

Decided (an update block says mut, may run more than once, and the compiler picks the lock)

Decided (a trait method may pause unless it says sync)

Decided (a path names its root at the call)

Decided (text is one type, and &str is the assertion)

Decided (a bound takes a path, and the ledger records traits and impls)

Decided (the ledger says Seq[T] and Par[T])

Decided (a foreign crate is described before it is called)

Changed (the compiler writes the & at the call)

Changed (a function-typed parameter lowers by its type)

Added (extern and unsafe are words, and C is callable)

Added (a crate is described before it is called)

Added (a function type says what a handler may do)

Found (a hand-edited dependency ledger is repaired before it is read)

Changed (set(neu; after: seen) is the one door for a stamped value)

Changed (an expired cleanup-deadline is exit 70, said on the panic path)

Changed (what leaves a lock is stamped Seen[T])

Changed (a trait method may pause unless it says sync)

Added (NK2203: a lock taken while a lock is held)

Changed (an update block says mut, and the empty slot is gone)

Added (the ledger says which functions touch a lock)

Decided nothing yet (what wakes the executor when the work is on a worker thread)

Fixed (a recursive pausing method is boxed)

Added (a task is asked about what it binds, not only what it captures)

Fixed (a binding that is changed and does not say mut)

Added (mut on a parameter: in-place change is written in the declaration)

Fixed (a method that changes its subject is a column now)

Fixed (three holes this step walked into, each older than it)

Added (thirteen tests for the call half)

Changed (a grammar is entered by a call, and the silent entry choice is gone)

Decided (a package is found by version through Cargo, under nikaia_<name>)

Fixed (removing dsl … from … is a work order, not a question)

Decided (a parameter may be a function, and the type says what it may do)

Added (the keeps column, and what it says about the corpus)

Decided (an error that newly reaches a catch is named once)

Fixed (a consumer reads a dependency’s ledger, and the false NK1129 is gone)

Fixed (the inference graph is the package, not the file)

Decided (a consumer reads a dependency’s ledger)

Decided (the caller writes no &, and a review of the surface language)

Added (an http package, a program that reaches it, and what a package cannot yet take)

Added (the two refusals that come with the lock’s doors)

Added (a let takes one name, or a flat tuple of them)

Added (a comptime binding where an item stands)

Changed (examples/foreign-runtime/ explained the boundary with a rule that is gone)

Fixed (a type nothing declares is refused rather than lowered)

Fixed (a loop that cannot end needs no unreachable return)

Fixed (a trait a package publishes is implemented and then callable)

Changed (the sync defect is a unit boundary, not a package one)

Fixed (the superseded grammar entry, and three records whose decisions were not citable)

Fixed (a grammar fold’s init, step and merge are code)

Fixed (a catch over an expression that cannot fail is refused)

Fixed (a catch that ignores the error binds _error)

Fixed (six citations named an entry by its number and meant another one)

Added (the build-time evaluator is on the work list, as two entries)

Fixed (a ??’s fallback is one value, or an expression in brackets)

Changed (there is no macro system)

Fixed (three findings the change turned up in other people’s work)

Fixed (one statement, two calls, one wrap between them)

Added (a head holds every expression)

Added (&& and || in the head of an if, a while and a for)

Fixed (the unconditional loop is lowered to loop)

Added (break and continue)

Fixed (a field of a borrowed subject may not be handed out by value)

Changed (a grammar is entered by a call)

Fixed (a + over text is a call, and an expression gets a span to key it by)

Changed (the string-concatenation entry said two things that measurement contradicts)

Changed (the whole-workspace flake loses its hypothesis and gains a shape)

Fixed (an impl owes its trait, and a write through the brackets is not an index)

Changed (what crosses from build time to run time)

Changed (the whole-workspace test failure gets a mechanism)

Added (every program in the specification is compiled, by a test)

Added (a trait can be declared, and a type parameter can be bound by one)

Changed (the word is comptime)

Fixed (a name the language below reserves no longer reaches it unescaped)

Added (const is a declaration, inside a body)

Fixed (two records were wearing one number, and the checker now says so)

Fixed (the overlap report answered the question wrongly)

Added (the shipped ledger answers what it touches)

Changed (what a program may do at compile time)

Added (generics are built, and a type parameter means two things)

Fixed (an upkeep entry that claimed a reproduction it no longer has)

Added (const is reserved, and what it will mean is decided)

Changed (a compile-time read is named three times)

Added (break, continue and loop are reserved)

Changed (no unconditional loop keyword)

Changed (http leaves std)

Changed (sync means one thing, and touches gets the inference it was written for)

Fixed (the crossing lock cost 63.5 ns a door where it should cost 17)

Added (what a lock-free shape would buy, measured)

Added (a door over several locks, and Chapter 12’s transfer runs)

Fixed (a handle a task uses is duplicated, which it was not)

Changed (the pages catch up with the lock, and the cheap shape reaches yes)

Changed (the shared mutable type: one name, a constructor, one spelling)

Fixed (nine places still said the owner count is atomic at every setting)

Changed (a constant sum takes the first type that holds it, the way a constant does)

Fixed (a scratch manifest was committed by accident)

Decided (letting foreign code call in is a target, not a third value of the switch)

Changed (at one user thread every count is plain, and a Shared stays in)

Added (the site has a menu, and every example program is a page on it)

Fixed (three things that were wrong on the published site)

Changed (the README stops saying “single-threaded”, because the process is not)

Added (overlap { … }, and the compiler checks the claim)

Fixed (a package reached through two parents is one type, not two)

Added (the case folds, and a lesson about what a test may rest on)

Found (a generic function lowers without its type parameters)

Fixed (HashMap::get said a key is always there, and refused the program that asks whether it is)

Added (the ledger says what it can say: every known receiver now resolves)

Measured (where this compiler cannot work a type out, and what it would cost to change)

Added (a value whose type is not known is converted into the nullable slot)

Fixed (a member of a T? is refused where it is written, not where it runs)

Added (?. reaches a method, and ?? chains)

Found (two defects, neither caused by the work that found them)

Added (a lock shared by four tasks, on four threads, counts every increment)

Fixed (a lint that was red on main)

Added (a task runs on a thread of its own, at user_parallelism = yes)

Fixed (a build switch changed in the manifest rebuilds the program)

Removed (ADR-050 D1 and D7: statements run in the order they are written)

Fixed (a task that never finishes no longer hangs the program)

This one was introduced and fixed inside this release, and the two entries it had are one: the drain that makes a task nobody joins still run is what made a task that never finishes hold the program, and the bound is what it had been missing.

Documented (what overlap met rather than made)

Added (let big = 3000000000 compiles)

Changed (access reads, update writes, and no lambda is handed something mutable)

Changed (open-decisions.md lists what is open, and nothing else)

Changed (the specification’s status notes catch up with the execution model)

Changed (the roadmap and the open-decisions page catch up with the execution model)

Added (Locked[T] has a shape, and it is decided per value)

Added (spawn lowers, and a task interleaves on the one thread)

Added (NK2101: data a task took with it, used again afterwards)

Added (std suspends for real, and the executor is the only place a program parks)

Fixed (a finished file operation nobody had asked about yet lost its bytes)

Added (async fn where the ledger says a function can pause)

Fixed (a struct declared in one file of a package lost its lifetime in another)

Changed (a relayed message names no type the program did not write)

Added (the executor, and two tasks interleaving on one thread)

Added (ADR-055: the emitted Rust is async where a function can pause)

Added (a count goes in as an i64, and the compiler writes the conversion)

Fixed (an undeclared name is refused wherever it is written)

Changed (how a notes-page entry is cited)

Fixed (a warning the backend said twice)

Corrected (a defect entry that named the wrong cause)

Fixed (a result that borrows from nothing)

Added (the wrap reaches a call argument, and ?.m() says what the language has)

Fixed (every position that can declare a name called self)

Added (?., and the fourth place the Some(…) goes)

Added (T? is a type, null is a word)

Found (two defects the nullable work turned up)

Fixed (four stale claims, and the former backlog file’s upkeep section is empty)

Fixed (a clippy lint only CI could see)

Fixed (keywords are reserved words, and three silent miscompilations go with it)

Fixed (a sum of constants, and a division by a constant zero)

Added (use http as h, and the two import forms get sentences)

Fixed (a remedy in a type the specification does not offer)

Added (statement order and overlap { … } reach the specification)

Fixed (every abort names the Nikaia line)

Added (pub on a field means something, across a package boundary)

Added (a Nikaia package can be depended on, by path)

Fixed (a name this compiler substituted is put back in the message)

Removed (the automatic a, b, c)

Fixed (a program that imports nothing may still use a map)

Fixed (a program that imports nothing may still use a map)

Fixed (a program that imports nothing may still use a map)

Fixed (a refusal carries no backtrace, wherever in the compiler it is made)

Changed (a length is an i64, and the numeric surface is the one the page names)

Fixed (the compiled-std cache stops filling the disk)

Fixed (the explain modes reach a project, and two more shared counts stopped diverging)

Changed (a package is a directory, and its files are one namespace)

Added (two decisions get their records: what use does, and what a package is)

Fixed (a module can hand out types, and a shared field agrees across files)

Fixed (a word this language does not know, and what leaves the compiler when a program is wrong)

Changed (the crossing verdict takes the destination, so a lock may reach a task)

Added (a response may be bytes the program never allocated, and the criticism that asked for it was measured)

Fixed (the README’s code example is refused by this compiler)

Fixed (an overflow aborts at every build, and so does a conversion that does not fit)

Added (a handle on a shared value is duplicated, and what it costs is readable)

Added (Shared[T] is a type a program can write and run)

Fixed (a view keeps its type, and a transparent container is seen through)

Changed (named lambda arguments are the normal form)

Added (four themes on the documentation site, and Nikaia code that is coloured like Nikaia)

Fixed (the site was published without the theme it was configured to use)

Added (why an automatic clone is not a convenience)

Changed (Parts I and II follow ADR-039: one shared mutable type, four doors, and nesting refused)

Assessed (what a full answer to a stored view would have to track, and what a half-built one does)

Changed (where the destination already names a buffer, the view is lowered instead of refused)

Fixed (a view kept past its call is refused in Nikaia, not in the generated Rust)

Fixed (a trailing lambda can name its arguments)

Measured (0.0.9 — what an always-Mutex floor for Locked would cost against Part II 12.2)

Fixed (0.0.9 — a written call that can fail is reported, and the ledger stops publishing that it cannot)

Added (0.0.9 — ADR-037 D7 and D8: the count a particular Shared gets is inferred, and only ever downwards)

Decided (0.0.9 — ADR-037 D6: Shared is one atomic count, at both settings)

Fixed (0.0.9 - the specification now says which of its constructs are built)

Fixed (0.0.9 - two claims the withdrawal left standing)

Withdrawn (0.0.9 — the nightly toolchain, the Bridge-IR backend and the rustc_ast path)

Decided (0.0.9 - ADR-001 D5 and ADR-005 D9: Nikaia no longer requires the Rust nightly)

Found (0.0.9 - where the specification and the parser disagree, reported and not fixed)

Built (0.0.9 - ADR-002 D4: a project reaches std through a sysroot, and stops building the compiler twice)

Built (0.0.9 - ADR-004 and ADR-021 D9: a compiler without the bridge backend, on stable)

Decided (0.0.9 - four things that had been left to a later reader)

Fixed (0.0.9 - ADR-005 D7: the project build handed Cargo’s stderr to the terminal)

Fixed (0.0.9 - ADR-027: an asserted sync survived a call nothing could resolve)

Built (0.0.9 - ADR-021 D2: the lockfile records what Cargo resolved, and the bridge compiles at 2021)

Measured (0.0.9 - ADR-038 §4.3: a pair of operations costs nothing, and only on one of the two mechanisms)

Decided (0.0.9 - ADR-038: the runtime and the HTTP server are ours)

Fixed (0.0.9 - ADR-033: a catch handler’s effects were not counted)

Built (0.0.9 - ADR-033 §8.3 item 1: the ordering analysis sees more than one shape)

Built (0.0.9 - ADR-005 D8: the ledger’s byte-determinism is checked, not asserted)

Built (0.0.9 - ADR-037 D5 and ADR-033 D8: the switches live in the manifest)

Measured (0.0.9 - ADR-033 §8: what the overlap actually bought)

Decided and built (0.0.9 - ADR-033 D9: no allow_parallel, and the compiler says why instead)

Changed (0.0.8 - ADR-035: f"…" interpolates, "…" is text)

Built (0.0.9 - ADR-033’s first increment: two reads that meet on nothing)

Decided (0.0.9 - ADR-034: a handler that can return makes the next statement conditional)

Also found by building it

Decided, not built (0.0.9 - ADR-033: program order is a guarantee where it is observable)

Fixed (0.0.8 - ADR-032: a hole is code, and every analysis has to see it)

Decided and built (0.0.8 - ADR-031: a signature may name its receiver’s type arguments)

Added (0.0.8 - ADR-030: a program is more than one file)

Decided and built (0.0.8 - ADR-029: a higher-order function does what its lambda does)

Decided and built (0.0.8 - ADR-028: the type checker already knew)

Measured (0.0.8 - the compiler parses 20 % faster, and the survey was pointing the wrong way)

Measured and changed (0.0.8 - the first two Tier-1 candidates, and both intuitions were wrong)

Added (0.0.8 - Kap 5.1: the Subject ; Config protocol, G18)

Decided and built (0.0.8 - ADR-025: a loop can fail, and the rule was already written)

Added (0.0.8 - std can write a file)

Found (0.0.8 - two gaps, from trying to build the rest of std::fs)

Added (0.0.8 - ADR-024: the type checker)

Removed (0.0.8 - ADR-022: one lambda form, fn: is gone)

Measured (0.0.8 - the intern cache does not want sizing for 1BRC)

Added (0.0.8 - sync is checked)

Added (0.0.8 - ADR-020: the ledger, and a library brings its own)

Added (0.0.8 - std ships its contracts)

Added (0.0.8 - ADR-019: a program can read standard input)

Added (0.0.8 - a seventh running example, on a pipe)

Fixed (0.0.8 - a return inside an if stopped returning)

Changed (0.0.8 - the compiler parses 17% fewer instructions)

Added (0.0.8 - a sixth running example, with no grammar in it at all)

Added (0.0.8 - arithmetic that reads like arithmetic)

Fixed (0.0.8 - two that were silently wrong)

Added (0.0.8 - a fifth running example, and the three things it found)

Added (0.0.8 - char, which the language could not say)

Fixed (0.0.8 - a string could not hold a \u{…} escape)

Changed (0.0.8 - every error message shows the line it is about)

expected `}`; found unexpected token `temp` at line 3, column 5
   3 |     temp: i32
           ^^^^
note: also possible here: `,`, `//`
in struct_item

Surveyed (0.0.8 - where Tier-1 staging actually stands, and whether the compiler can choose)

Open (0.0.8 - ADR-026: what a build may read, and what may run while it reads)

Added (0.0.8 - the build cache exists now, and two backends stopped lying)

Decided (0.0.8 - ADR-021: the build cache is ours, and the lockfile is its key)

Decided (0.0.8 - ADR-018: the handler sees the request, because the lambda already could)

Decided (0.0.8 - ADR-017: escaping is the template’s contract)

Added (0.0.8 - enums and match, which Part I specified and Stage 0 could not hear)

Added (0.0.8 - a list can be put in an order, and the spec says so)

Fixed (0.0.8 - a lambda that reaches for a inside a string)

Fixed (0.0.8 - the last corpus row: all twenty-one say what a reader needs)

Changed (0.0.8 - twenty of twenty-one corpus rows say what a reader needs)

Added (0.0.8 - tuples, which calc.nika was writing a struct to stand in for)

Added (0.0.8 - a grammar rule may say what it is called)

Changed (0.0.8 - sixteen of twenty-one corpus rows now say what a reader needs)

Added (0.0.8 - a fourth running example, and the std call it needed)

Added (0.0.8 - two more examples, and every example run by cargo test)

Fixed (0.0.8 - two bugs the new examples found)

Documented (0.0.8 - what the new examples exposed)

Changed (0.0.8 - the backend ranks an expectation by whether the grammar required it)

Added (0.0.8 - the error corpus as a test, and a handoff)

Documented (0.0.8 - the error corpus)

Changed (0.0.8 - the grammar says whitespace once, not 203 times)

Documented (0.0.8 - findings for the parser backend)

Documented (0.0.8 - what the missing provenance analysis costs)

Added (0.0.8 - type arguments in a grammar)

Changed (0.0.8 - lazy diagnostics upstream, and 1BRC below hand-written Rust)

Added (0.0.8 - Nikaia in Nikaia’s std; the flagship keeps its claims)

Added (0.0.8 - Bootstrap Compiler: the flagship runs)

Added (0.0.8 - Bootstrap Compiler: diagnostics on the source line)

Added (0.0.8 - Bootstrap Compiler: the grammar lowering)

Changed (0.0.8 - Frames, corrected against winnow-grammar ADR 16)

Changed (0.0.8 - Frames, from the implementation)

Added (0.0.8 - Input Provenance)

Changed (0.0.8)

Added (0.0.8 - Parallel Parsing)

Changed (0.0.8)

Added (0.0.8 - Tethered Slices in User Structs)

Changed (0.0.8)

Added (0.0.7 - Grammar Protocol)

Changed (0.0.7)

Fixed (Toolchain / CI)

Changed (Toolchain)

Added (Standard Library)

Added (Examples)

Documentation

Added

Changed

Fixed

This site is open source. Improve this page.