Skip to content

Commit 78c20c8

Browse files
committed
feat(runtime): ADR-0171 — merge the rt kernel ns into clojure.core; internals to cljw.internal
Smell-audited: 3: the ADR-0171 full-merge (DA-fork Alt 2, committed 4018266) — data-layer fix over display normalization per F-011/F-013; retired 3 ADs as parity; the rehome-idempotency leak and the two stragglers (diff fixture, RegisterError test) were caught by the gate and fixed before commit. Zig builtins + bootstrap macros now intern with home ns = clojure.core (the mainline shape): `(resolve '+)` → `#'clojure.core/+`, doc/meta :ns, eldoc/lookup/completion ns fields, callable printing (`#<clojure.core/+>`), and `(ns-publics 'clojure.core)` all match clj at the data layer. The `__`-prefixed kernel helpers (48 + 9 dash-named ones renamed `__*`) re-home to the new `cljw.internal` ns via Env.rehomeInternals; macro expansions and the bundled .clj reference them qualified. The `rt` ns is deleted: Env.init, the per-ns rt refer in both backends, the resolveQualified clojure.core refers-exception, and syntax-quote's per-site rt→clojure.core rename all dissolve. Reader `@x` now lowers to clojure.core/deref (= clj); `or` emissions qualify to clojure.core/or. The re-find rebind hazard is closed by making the builtin cljw.internal/__re-find (core.clj's wrapper owns the public name). serialize VERSION 5→6 (pre-merge archives bake "rt" var_refs). AD ledger: AD-011 (ns-publics omission), AD-038 (rt/deref reader form), AD-049 (spec s/form rt/ qualification) retired as PARITY with pins flipped; AD-025 text refreshed; new AD-053 pins cljw.internal's existence + rt's absence + the no-leak invariant. D-562 records the user-directed arc (completion parity → AD inventory → release tag).
1 parent 4018266 commit 78c20c8

56 files changed

Lines changed: 495 additions & 490 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.dev/accepted_divergences.yaml‎

Lines changed: 24 additions & 65 deletions
Large diffs are not rendered by default.

‎.dev/debt.yaml‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,27 @@
2424
# md-table-align no longer pads it to ~800 KB of whitespace.
2525

2626
active:
27+
- id: "D-562"
28+
status: |-
29+
open (2026-07-15, USER-DIRECTED landing point for the current arc).
30+
Full inventory of .dev/accepted_divergences.yaml (49 rows): for EACH
31+
AD decide (a) KEEP — inherent to cljw's nature (no-JVM identity
32+
hashes, tz/locale-free edge target, NaN-box representation …); or
33+
(b) CONVERGE — historically-motivated, now retirable: un-accept per
34+
accepted_divergences.md (remove row + flip pin to parity + fix), or
35+
IMPLEMENT the missing feature that blocks parity. Work the checklist
36+
row by row (record per-AD verdict inline here or in a
37+
private/notes checklist as the loop proceeds so autonomy survives
38+
compaction). Precedent this cycle: ADR-0171 retired AD-011/038/049
39+
as parity (rt merge) — the same class of "old structural excuse
40+
dissolved" is what this sweep hunts. ORDER within the arc (user):
41+
(1) finish the CIDER completion parity unit (incl. java-interop
42+
class/static completion without require, good design), (2) this AD
43+
inventory + convergence work, (3) THEN cut a release tag — the
44+
arc's landing point.
45+
category: "clj-parity"
46+
barrier: "none — user-directed; ordered after the completion-parity unit"
47+
last_reviewed: "2026-07-15"
2748
- id: "D-561"
2849
status: |-
2950
open (2026-07-15, Character/* completion cycle). `Character/getName` is
@@ -4546,7 +4567,7 @@ discharged:
45464567
skips `.var_ref` + `.ns` (Env-lifetime, never GC-allocated — also closed a
45474568
latent `.var_ref` hole). Corpus `namespace` (12), e2e phase15_namespace
45484569
(11), unit test for the membrane. AD-010 (`#object[Namespace …]` print) +
4549-
AD-011 (clojure.core ns-publics omits rt-referred prims). Deferred: D-231
4570+
AD-011 (retired 2026-07-15: ADR-0171 made ns-publics parity). Deferred: D-231
45504571
(Var-as-IFn), `remove-ns` (tombstone/lifetime), `*out*` (separate). Unblocks
45514572
D-227 (clojure.test) + D-158 (real-lib). Cross-ref ADR-0083 / D-227 / F-004.
45524573
ORIGINAL: now (structural, surfaced 2026-06-03 by D-227 design probe) — no Namespace value type / *ns* / ns-reflection.

‎.dev/handover.md‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,19 @@
4444
now advertises `versions.clojurewasm` (babashka-precedent CIDER-banner
4545
key; CIDER upstream patch draft + init.el advice:
4646
`private/notes/cider-clojurewasm-banner-patch.md`).
47-
- **NEXT (user-directed 2026-07-15)**: CIDER completion parity — kill the
48-
`(rt)` ns leak (show clojure.core), add kind annotations (<s>/<m>/<v>),
49-
class + static-member candidates. Design directive: mechanically
50-
extract the canonical surface FROM mainline clj into a data file
51-
(EDN; F-013 definition-derived), cljw consumes it via a fixed
52-
convention/layer — parity audit falls out as a by-product.
47+
- **ADR-0171 LANDED (2026-07-15)**: the `rt` kernel ns is GONE — Zig
48+
builtins + bootstrap macros intern into clojure.core (home ns matches
49+
mainline: `(resolve '+)` → `#'clojure.core/+`; ns-publics parity);
50+
`__`-internals live in the new `cljw.internal` ns (macro expansions
51+
call them qualified). Retired AD-011/038/049 as PARITY (pins flipped);
52+
new AD-053 pins cljw.internal's existence. serialize VERSION 6.
53+
- **ARC (user-directed 2026-07-15, the landing point)**: (1) CIDER
54+
completion parity — kind annotations, class + static-member candidates
55+
(java-interop completion without require), keywords, special forms,
56+
sort; oracle harness `scripts/completion_oracle.py` + fixtures landed
57+
(red baseline 5/18). (2) AD full inventory + convergence (**D-562**).
58+
(3) THEN cut a release tag. Cron re-fire 2026-07-16 03:02 JST is set
59+
(session-only).
5360
- **First task on resume**: self-select from the live `active:` list,
5461
easiest-first (D-523's architecture/wasm-demo residual + D-522 drain 3
5562
landed 2026-07-14; D-430 is DISCHARGED — the prior pointer here was

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ private/
4040
.clj-kondo/
4141
.lsp/
4242
test/conformance/verified_projects/*/.cpcache/
43+
__pycache__/

‎src/eval/analyzer/analyzer.zig‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -705,13 +705,13 @@ fn analyzeSymbol(
705705
// analysis (which blocks a lib whose CORE is pure but a peripheral fn
706706
// touches such a class — e.g. integrant's `(clojure.lang.RT/baseLoader)`
707707
// in its optional hierarchy loader), rewrite it to a loud runtime
708-
// `(rt/__unsupported-host-ref "ns/name")`. The enclosing fn DEFINES, the
708+
// `(cljw.internal/__unsupported-host-ref "ns/name")`. The enclosing fn DEFINES, the
709709
// namespace LOADS, and the ref errors only if actually evaluated. Covers
710710
// both call-head and value positions (both reach analyzeSymbol). AD-008.
711711
if (isDeferredHostNs(ns_name)) {
712712
const full = try std.fmt.allocPrint(arena, "{s}/{s}", .{ ns_name, sym.name });
713713
const items = try arena.alloc(Form, 2);
714-
items[0] = .{ .data = .{ .symbol = .{ .ns = "rt", .name = "__unsupported-host-ref" } }, .location = form.location };
714+
items[0] = .{ .data = .{ .symbol = .{ .ns = "cljw.internal", .name = "__unsupported-host-ref" } }, .location = form.location };
715715
items[1] = .{ .data = .{ .string = full }, .location = form.location };
716716
const call_form: Form = .{ .data = .{ .list = items }, .location = form.location };
717717
return analyze(arena, env.rt, env, scope, call_form, macro_table);

‎src/eval/analyzer/syntax_quote.zig‎

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ fn qualifySym(env: *Env, s: SymbolRef, loc: SourceLocation) Form {
7878
if (BARE_SYMS.has(s.name) or hasDot(s.name) or (s.name.len > 0 and (s.name[0] == '.' or s.name[0] == '%')))
7979
return .{ .data = .{ .symbol = s }, .location = loc };
8080
const cur = env.current_ns orelse return .{ .data = .{ .symbol = s }, .location = loc };
81-
var home = if (cur.resolve(s.name)) |v|
81+
const home = if (cur.resolve(s.name)) |v|
8282
v.ns.name
8383
else if (s.name.len > 0 and s.name[0] >= 'A' and s.name[0] <= 'Z')
8484
// An unresolved capitalized symbol is a class name (`Throwable`,
@@ -88,11 +88,6 @@ fn qualifySym(env: *Env, s: SymbolRef, loc: SourceLocation) Form {
8888
return .{ .data = .{ .symbol = s }, .location = loc }
8989
else
9090
cur.name;
91-
// cljw's `rt` ns is the implementation home of what clj calls `clojure.core`
92-
// (primitives are interned in `rt` + referred everywhere; `clojure.core/+`
93-
// also resolves). Present them as `clojure.core/…` so a syntax-quoted core
94-
// symbol matches clj (`` `+ `` → clojure.core/+, not rt/+).
95-
if (std.mem.eql(u8, home, "rt")) home = "clojure.core";
9691
return .{ .data = .{ .symbol = .{ .ns = home, .name = s.name } }, .location = loc };
9792
}
9893

‎src/eval/backend/tree_walk.zig‎

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -557,32 +557,23 @@ fn evalInNs(env: *Env, n: node_mod.InNsNode) !Value {
557557

558558
/// `(ns foo)` / `(ns foo (:refer-clojure))`. ADR-0035 D1 + D9
559559
/// second amendment. When `:refer-clojure` is in effect (default
560-
/// true), the cw v1 widened semantic refers BOTH `rt` AND
561-
/// `clojure.core` into the entering ns (divergence from JVM which
562-
/// has no rt ns). When `refer_clojure = false`, the ns switch is
563-
/// naked — same shape as `(in-ns 'foo)`. The widening makes the
564-
/// refer mechanism grep-traceable from the `.clj` head: every
565-
/// user-visible refer of rt + clojure.core comes from a
566-
/// `(:refer-clojure)` directive (boot-time fan-out for user/
567-
/// remains in bootstrap.zig + primitive.zig + macro_transforms.zig).
560+
/// true), `clojure.core`'s publics are referred into the entering ns
561+
/// (the mainline shape; Zig builtins live there per ADR-0171). When
562+
/// `refer_clojure = false`, the ns switch is naked — same shape as
563+
/// `(in-ns 'foo)`. Boot-time fan-out for user/ remains in
564+
/// bootstrap.zig + primitive.zig + macro_transforms.zig.
568565
fn evalNs(rt: *Runtime, env: *Env, n: node_mod.NsNode) !Value {
569566
env.setCurrentNs(try env.findOrCreateNs(n.name));
570567
// `(ns ^{…} name {:attr …} "doc" …)` → ns meta: attr merge first, then
571568
// the docstring (an explicit doc wins over an attr :doc) — D-239/D-554.
572569
if (!n.attr_meta.isNil()) try meta_mod.mergeNsMeta(rt, env.current_ns.?, n.attr_meta);
573570
if (n.doc) |d| try meta_mod.setNsDoc(rt, env.current_ns.?, d);
574571
if (n.refer_clojure) {
575-
// Row 14.7 (D-098): filters apply to both rt/ and clojure.core
576-
// (the two auto-refer sources). `rt/` is cw-side primitives;
577-
// its `+` / `-` / etc. are what `:exclude [+]` shadows.
578-
if (env.findNs("rt")) |rt_ns| {
579-
try env.referAllWithFilter(rt_ns, env.current_ns.?, n.refer_clojure_exclude, n.refer_clojure_only);
580-
}
572+
// Row 14.7 (D-098): the `:exclude`/`:only` filter applies to
573+
// clojure.core — the single auto-refer source (Zig builtins
574+
// intern there directly per ADR-0171).
581575
if (env.findNs("clojure.core")) |cc_ns| {
582-
// ADR-0035 D9 revision: clojure.core OVERRIDES rt on collision —
583-
// the public layer's redefinition (e.g. matcher-arity re-find)
584-
// wins over the internal primitive.
585-
try env.referAllOverriding(cc_ns, env.current_ns.?, n.refer_clojure_exclude, n.refer_clojure_only);
576+
try env.referAllWithFilter(cc_ns, env.current_ns.?, n.refer_clojure_exclude, n.refer_clojure_only);
586577
}
587578
}
588579
// Row 14.7 (D-098): walk ns-level (:require [...]) libspecs. Each

‎src/eval/backend/vm.zig‎

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,19 +1207,16 @@ inline fn stepOnce(
12071207
// ADR-0035 D9 second amendment + ADR-0036 dual-
12081208
// backend parity contract. Mirror of post-T3
12091209
// tree_walk::evalNs when `refer_clojure = true`.
1210-
// op_in_ns logic + referAll(rt) + referAll(clojure.core).
1210+
// op_in_ns logic + referAll(clojure.core) — the single
1211+
// core surface per ADR-0171.
12111212
if (instr.operand >= chunk.constants.len)
12121213
return raiseInternal("vm: op_ns_with_refer_clojure constant index out of range");
12131214
const name_val = chunk.constants[instr.operand];
12141215
if (!name_val.isString())
12151216
return raiseInternal("vm: op_ns_with_refer_clojure constant is not a String");
12161217
env.setCurrentNs(try env.findOrCreateNs(string_mod.asString(name_val)));
1217-
if (env.findNs("rt")) |rt_ns| {
1218-
try env.referAll(rt_ns, env.current_ns.?);
1219-
}
12201218
if (env.findNs("clojure.core")) |clojure_core_ns| {
1221-
// ADR-0035 D9 revision: clojure.core overrides rt on collision.
1222-
try env.referAllOverriding(clojure_core_ns, env.current_ns.?, &.{}, null);
1219+
try env.referAll(clojure_core_ns, env.current_ns.?);
12231220
}
12241221
if (sp >= stack.len)
12251222
return raiseInternal("vm: operand stack overflow");
@@ -1229,7 +1226,7 @@ inline fn stepOnce(
12291226
.op_ns_with_filter => {
12301227
// D-098: mirror of tree_walk::evalNs's refer-clojure branch
12311228
// with the `:exclude`/`:only` filter. Enter the ns, apply the
1232-
// docstring meta (D-239 sibling), then refer rt + clojure.core
1229+
// docstring meta (D-239 sibling), then refer clojure.core
12331230
// through referAllWithFilter (skipped when the ns form had no
12341231
// refer-clojure step — the entry's flag).
12351232
if (instr.operand >= chunk.ns_filters.len)
@@ -1244,12 +1241,8 @@ inline fn stepOnce(
12441241
}
12451242
if (f.doc) |d| try meta_mod.setNsDoc(rt, env.current_ns.?, d);
12461243
if (f.refer_clojure) {
1247-
if (env.findNs("rt")) |rt_ns| {
1248-
try env.referAllWithFilter(rt_ns, env.current_ns.?, f.exclude, f.only);
1249-
}
12501244
if (env.findNs("clojure.core")) |clojure_core_ns| {
1251-
// ADR-0035 D9 revision: clojure.core overrides rt on collision.
1252-
try env.referAllOverriding(clojure_core_ns, env.current_ns.?, f.exclude, f.only);
1245+
try env.referAllWithFilter(clojure_core_ns, env.current_ns.?, f.exclude, f.only);
12531246
}
12541247
}
12551248
if (sp >= stack.len)

‎src/eval/bytecode/serialize.zig‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const tree_walk = @import("../backend/tree_walk.zig");
7575
const Function = tree_walk.Function;
7676

7777
pub const MAGIC: [4]u8 = .{ 'C', 'L', 'J', 'W' };
78-
pub const VERSION: u16 = 5; // v5: NsFilterEntry attr_const (D-554); v4: doc + refer_clojure
78+
pub const VERSION: u16 = 6; // v6: rt ns merged into clojure.core (ADR-0171) — pre-v6 archives bake "rt" var_refs that no longer resolve; v5: NsFilterEntry attr_const (D-554)
7979

8080
pub const SerializeError = error{
8181
OutOfMemory,

‎src/eval/reader.zig‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ pub const Reader = struct {
610610
return Form{ .data = .{ .list = items }, .location = loc };
611611
}
612612

613-
/// `@x` reader macro → `(rt/deref x)` (mirrors `readQuote`). The `deref`
613+
/// `@x` reader macro → `(clojure.core/deref x)` (mirrors `readQuote`). The `deref`
614614
/// symbol is NS-QUALIFIED so a local `deref` binding cannot capture it:
615615
/// `(let [deref f] @a)` derefs `a`, not calls `f` (clj hygiene, which uses
616616
/// `clojure.core/deref`). cljw qualifies to the `rt` primitive ns — the
@@ -627,7 +627,7 @@ pub const Reader = struct {
627627
return error_catalog.raise(.eof_unexpected, loc, .{});
628628

629629
const items = self.allocator.alloc(Form, 2) catch return error.OutOfMemory;
630-
items[0] = Form{ .data = .{ .symbol = .{ .ns = "rt", .name = "deref" } }, .location = loc };
630+
items[0] = Form{ .data = .{ .symbol = .{ .ns = "clojure.core", .name = "deref" } }, .location = loc };
631631
items[1] = inner;
632632
return Form{ .data = .{ .list = items }, .location = loc };
633633
}

0 commit comments

Comments
 (0)