agent: builder model: opus max_turns: 120 budget_usd: 30 —
Score: self-hosting lane (docs/priorities.md, S 2): ~350 Rust lines replaced; C ~10 M (E12-1 moved ~470 lines in one Opus run, 654866b). The row and its risks are in docs/selfhosting-plan.md, C-7 rest and E12 (read it first; its line ranges are old, the ones below are re-grepped at 6920907).
crates/nikaia/src/emit/mod.rs: template_hole_tables (:1470, called :1275), fold_callees (:1521, called :1176), literal_expressions_bound (:1727), hole_as_read (:1761), interpolated_holes (:1780), literal_expressions (:1798), template_holes (:1840), built_at_build_time (:873, called :1262) -> new crates/nikaia-std/src/tools/emit_holes.nika.emit/host.rs names_read (:269) goes; check/host.rs literal_holes (:218) and holes_read (:355) call the Nikaia functions.EmitHolesHost in emit_holes.nika, implemented in Rust for Parsed (so the one-line adapter and its twelve callers, which hold only &Parsed, keep working), with four methods: hole(site, text) -> an EmitHole (the Expr or its error text), cached_holes, cached_kept_calls, and wrapped_hole(at: u32, hole: ref Expr) -> Expr? (the hole with parsed.hole_wraps[at] applied through text_tiers::wrap_hole; null where nothing is wrapped, and the Nikaia side then uses the hole itself). Its only caller of hole_as_read (check/host.rs:361) takes .into_owned(), so a copy there loses no node address. The pure parts (interpolated_holes, the template’s sites and texts via dsl_name, is_deferred, template::split, template_holes_bound) need no host. On the emit side names_read in Nikaia reads EmitState.template_holes, EmitState.hole_wraps and the existing EmitHost.wrap_hole: EmitHost gains nothing. The parser’s caches (parser/mod.rs:439-446) stay Rust.crate::emit::literal_expressions keeps a one-line Rust adapter: twelve Rust callers pass it as a closure (contracts/sync.rs, keep.rs, keeps.rs, locks.rs, sharing.rs, throws.rs, touch.rs, prove.rs, foreign.rs, fold_bound.rs); it goes when they move.The table names R-6 or a walker probe. contracts/sync.rs::visit_expr (:305) is already Nikaia’s walk (tools/check_visit.nika: check_visit_children, check_visit_has_holes) plus literal_expressions for the holes. So the Nikaia side walks with check_visit, not emit_walk::visited_in_*; then it sees the same nodes by construction.
lower-std byte-identical, so a missed node shows as a lowered diff, not a test failure. Run lower-std after each step and compare..nika needs one lower-std with the old binary first, and a row in scripts/self_hosting.py (docs/builder-facts.md). A gap that blocks is a compiler fix, its own commit with a test (CLAUDE.md Fix, do not work around); if it cannot be fixed in this run, Result: partial with the probe under Found:.Part of #125)interpolated_holes, template_holes, hole_as_read, literal_expressions, literal_expressions_bound into emit_holes.nika, with the host hole; the Rust adapter stays; names_read goes, check/host.rs calls Nikaia.template_hole_tables, fold_callees, built_at_build_time into emit_holes.nika, with cached_holes, cached_kept_calls.Both built; lower-std clean and byte-identical apart from the moved code, generated diff committed; nikaia test tests/compiler --both-settings; it filters template, interpolat, fold, build_time, sync, views_kept, and the guard set of .claude/agents/builder.md. Report the Rust lines removed (python3 scripts/self_hosting.py) under Tests:. Changelog: none unless a message text changes.
A host method beyond the four named is needed, or an error in another agent’s file.
Read docs/builder-facts.md.
docs/builder-facts.md first. develop in the main checkout; git status at start: other agents’ uncommitted files are not yours, never touch or stage them.git add <files> only; commit after each step; git push origin develop fast-forward, no rebase (rejected: report Pushed: rejected, go on committing).cargo fmt, cargo clippy --all-targets -- -D warnings, scripts/pre-push-check.sh, and the gate of .claude/agents/builder.md (#852).CHANGELOG.md, badges; no new NK code. New tests in Nikaia only; never the full suite. No foreground sleep, no polling; scripts in files.NIKAIA_CACHE_LIMIT=2G; at most two clones with target/ in the machine; delete your own clone’s target/ and target/release/nikaia-scratch at the end; report GB freed./home/user/Nikaia/docs/briefs/e12-2.md.report.md)Fields of docs/architect.md, Briefs and reports: Result: done|noop|partial|blocked (the bare word alone on that line: Result: done, never a summary after it, which cuts the batch, b869a 2026-10-27; Owner: is a question for the owner or none, never a role), Files, Commit, Fixes, Pushed, Tests, Gate (green|red), Deviation, Found, Needs, Changelog, Owner, Disk freed. none where nothing, and then the bare word: Deviation: none (a note) cuts the batch (batch.nika compares exactly); a note goes into the commit message. Found: is only for a defect outside your change (it cuts the batch); how you solved your own step goes into the commit message. Deviation: is only a step of the brief built otherwise than written or not built (a choice the brief left to you, such as keep what X needs and say so, is no deviation: it goes into the commit message); how you verified (e.g. a test not run against the unfixed compiler) goes under Tests:. A noop says why under Tests: (what was probed), not under Deviation:. Final answer, one line: done <sha>, report <path> (or BLOCKED: ... / PUSH REJECTED ...).
Attribution: Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01GFeMqxRbtUMiGVyDMxsvEq