Issue #125: Stage 1: main’s pipeline (read, check, lower) runs Nikaia end to end

Open and answer this issue on GitHub.

selfhosting, note · opened 2026-09-30 · updated 2026-10-25 · 7 comments

ADR-250 D5, road in ADR-294. Reached when read, check and lower run Nikaia from end to end, with Rust left only as adapters to crates (winnow, clap, toml, the file system).

Where it stands (0.0.426): 29.1 % of the toolchain is Nikaia — 17 297 lines in 59 files under crates/nikaia-std/src/tools/*.nika, against 42 178 lines of Rust in crates/nikaia{,-logic}/src (scripts/self_hosting.py).

Moved so far (see scripts/self_hosting.py for the list, by version): the syntax tree, the constant fold, the checker’s types and what it asks of them, the ledger (read, written, published), describe, the trait check, throws/locks/touch and their walks, views, text tiers, buffers (contracts::keep), the checker’s words, type and call helpers, NK1116, written-back expressions, the build-time integer (integers.nika) and value (build_values.nika).

Still Rust, largest first:

Module Lines
check 23 802
emit 14 597
contracts 4 926
parser 4 377
project.rs 3 519
bounds.rs 2 723
prove.rs 2 553
nikaia-logic 2 184
build_time.rs (the evaluator’s walk) 1 318
modules.rs 1 105
diagnostics 871
the rest (main, sysroot, text_tiers, grammar_run, manifest, describe, adapters) ~4 000

How a move goes: one package per version, leaves first; a defect the move finds is fixed in the compiler in the same package (ADR-294 D3), never bent around in the .nika. Where the Rust reached for a wider number (i128) out of convenience, the move takes the better alternative instead (ADR-315’s checked_ names, integers.nika).


Generated by Claude Code

Comments

keywan-ghadami, 2026-10-04

Where it stands (0.0.434): 30.1 % of the toolchain is Nikaia: 18 087 lines under crates/nikaia-std/src/tools/*.nika, against 41 907 lines of Rust in crates/nikaia{,-logic}/src (scripts/self_hosting.py).

Moved since 0.0.426: the basic bounds level (bounds_basic.nika, 0.0.428/0.0.429) and what a loop’s body does (bounds_body.nika, 0.0.431).

Next, as their own issues: #435 (the rest of bounds.rs: Shape, then the interval walk), then #436 (prove.rs).


Generated by Claude Code

keywan-ghadami, 2026-10-10

Handover, 2026-10-10 (end of a long session)

Share: 26.3 % at the start of the day, 42.7 % now (scripts/self_hosting.py, 31 301 Nikaia lines). develop is green since 0.0.773 (clippy was red from the first move until then; CLAUDE.md now makes it a gate before every push). main follows through promote.

Moved today (the commits say what): contracts (trust, declared, ledger_items, tether report), diagnostics render, parser (numbers, parse errors, tree building, interpreter, parsed tree), views, the whole nikaia-logic crate (deleted, logic_*.nika), cexport (+ node.rs, deleted), the build_time evaluator, grammar_run/comptime_run drivers, bounds matched/ensured, proofs book, and about 2 850 lines of check/mod.rs into 18 check_*.nika files (the Rust Checker holds a CheckState as self.s).

Decided today: ADR-339 (the grammar language gets span(p), here, same_line; /// is a rule; no state, no extern rule) - build list on #557. ADR-094 D7 (NK2108, a part taken out of a mut parameter is given back) - built, #576. ADR-340 (every Expr node carries a parser-given id) - build list on #558, steps 2 to 4 pushed (0.0.777 to 0.0.779), see its last comment for what is left.

Compiler defects found by the moves and filed under this issue: #559 to #576 are fixed or no longer reproduce; open: #562 and #573 (decision), #578 (bool bound in a nested match lowers as &bool, ready).

What to do next, in this order

  1. #558: finish ADR-340 (the remaining as *const MatchPattern / Stmt sites, Checked’s statement-byte + shape keys, then drop the node_of callbacks). It decides whether patterns and statements need an id.
  2. check/mod.rs, the rest: the takes cluster (taken, first_read_after, hands_over_path, the sequence-walk reports; about 3 000 lines) and then the recursive core (value_of, stmt, call, call_on, arguments) in one step. The Checker state is a Nikaia struct (CheckState); about 15 more fields still sit in the Rust struct (the i128 fields, the OnceCell, Parsed).
  3. emit/mod.rs (16 291 lines): the same way, after check. emit is almost all &self, so its leaves go first.
  4. #557: the compiler’s own grammar (parser/mod.rs, 3 185 lines) once span(p), here, same_line are built.
  5. Smaller leftovers: comptime_run.rs (needs a Nikaia view of Parsed), bounds_report.rs, the IO parts of assets.rs, libraries.rs, proofs.rs, project.rs (4.2k), modules.rs, manifest.rs, sysroot.rs, main.rs.

Honest scale: by the spike on #558 the walks and the emitter are about 77 % of check + emit; reaching 90 % of the toolchain needs all of the above, including project.rs and the grammar. Nothing in the language blocks it any more; what is left is volume, builds (10 to 30 minutes each on the shared machines) and the disk (it ran out three times).

How to work is in CLAUDE.md, section Moving Rust into Nikaia. The short form: one agent per large module, never two on one file, leaves first, clippy before every push, delete target/ of a worktree you do not use.


Generated by Claude Code

keywan-ghadami, 2026-10-11

#580 is the decided build order for NK2302 precision (ADR-283 D25-D26, 0.0.836). A moved (ref self, world) method may hand back a view of world (or of self) once it is built, so no clone is needed. Until then, the .clone() workaround stays.


Generated by Claude Code

keywan-ghadami, 2026-10-11

One lowered HashMap type for the tools (lowering defect D6) is decided in #581 (ADR-010 D9): one hasher per class of maps, decided after the whole package is read, and a type parameter at the package boundary, so a Rust adapter names a type that does not move (Names = keyed by default, Names<nikaia_std::hash::Fast> where it vouches for its keys). Until #581 is built, tools/*.nika keep BTreeMap/BTreeSet.


Generated by Claude Code

keywan-ghadami, 2026-10-24

Can two builders run at once? Assessment of 2026-10-24 (architect; nothing started).

Two independent lanes are left.

What blocks it, sorted into real and not real:

Order:

  1. U-d2 (running).
  2. #791 (lowering).
  3. #667 (owner’s order: before U-d3).
  4. Then two lanes, if the owner moves to stage 2: C-7 U-d3 to U-d6, and E10/E11.

Before stage 2, CLAUDE.md needs a worktree-builder rule: own branch, own CARGO_TARGET_DIR, rebase by the supervisor. It replaces today’s “no separate worktrees” rule.


Generated by Claude Code

keywan-ghadami, 2026-10-24

Selective lower-std F.nika… as a way to run two builders in one checkout: assessed 2026-10-24, not filed as an issue.

Probe. I copied the sysroot (NIKAIA_SYSROOT) and gave one tools file a type error. lower-std then fails as a whole (NK1104 in assets.nika) and writes no file at all. So today another builder’s half-edited .nika blocks every builder’s lowering.

Why a file list cannot isolate the builders.

The cost goal is already #667’s. The lowering cache key includes the compiler binary’s bytes, and the lowering runs in the binary. So every rebuild misses all files: about 181 G instructions instead of 1.3 G. Per-file keys on the file plus the package interface, then step 4, remove that miss. In a worktree, the path compiled into the binary (env!("CARGO_MANIFEST_DIR")) is a second reason every key misses.

Recommendation.


Generated by Claude Code

keywan-ghadami, 2026-10-25

End point, owner’s direction (2026-10-25): when self-hosting is finished, the compiler becomes a Nikaia package of its own. Its nikaia.toml names tools/*.nika as its sources, it is built by nikaia build, and the compiler tests (today tests/compiler) become that package’s tests/. Nothing is built for this before the end of self-hosting; until then the Rust workspace and tests/compiler stay as they are.


Generated by Claude Code

Back to Open Issues