core: Stage 3 turn 2a — close the implicit Janet root-env leak

resolve-sym's last resort silently resolved any unknown Clojure symbol
against Janet's root environment — leaking Janet builtins with JANET
semantics into Clojure code: (type 1) was Janet's :number, (gensym) returned
Janet symbols (the long-documented (symbol (str (gensym))) macro landmine
existed BECAUSE of this), compare/slurp/int?/any? likewise. The explicit
janet/ prefix is the deliberate interop channel; the implicit fallback is
gone — an unresolved symbol is an error.

What the leak was masking, now proper interned vars:
- gensym: jolt's own (already existed, never interned) — returns real jolt
  symbols; the macro landmine is dead
- compare: full Clojure total order (nil-first, numbers, strings, keywords,
  symbols by ns/name, booleans, chars, uuid/inst, vectors by length then
  elementwise; cross-type throws)
- type: :type metadata override, deftype/record tag as symbol, else a
  taxonomy keyword (host-classified)
- int?: core-integer? — which had a latent bug the leak hid: (integer?
  ##Inf) was true (floor of inf is inf); NaN/infinities now excluded
- any?: constantly true (Clojure 1.9; SCI's namespaces.cljc needs it)
- jolt.interop/janet-type now uses the explicit (janet/type x) channel
- evaluator-test uses init (a bare make-ctx resolved EVERYTHING via the leak)

Suite 4470 -> 4532+ pass / 86-87 clean (proper compare unlocks the sort
files); baselines raised. Conformance 326x3 (+5 rows), +22 predicate spec
rows, stdlib battery green, all specs+unit. Coverage dashboard now counts
previously-leak-resolvable names honestly (missing-portable 19 -> 27).
This commit is contained in:
Yogthos 2026-06-10 12:43:08 -04:00
parent d0c605ac9d
commit c7b0ad9d84
9 changed files with 150 additions and 37 deletions

View file

@ -157,3 +157,30 @@
["not-empty full" "[1]" "(not-empty [1])"]
["not-empty empty" "nil" "(not-empty [])"]
["not-empty string" "nil" "(not-empty \"\")"])
# Stage 3 turn 2a: the implicit Janet root-env fallback is GONE — these are now
# proper interned clojure.core vars with Clojure semantics (compare's total
# order, meta-aware type, any?, gensym returning jolt symbols).
(defspec "predicates / compare, type, any? (stage 3)"
["compare =" "0" "(compare 1 1)"]
["compare <" "-1" "(compare 1 2)"]
["compare nil first" "-1" "(compare nil 1)"]
["compare nil nil" "0" "(compare nil nil)"]
["compare strings" "-1" "(compare \"a\" \"b\")"]
["compare keywords" "-1" "(compare :a :b)"]
["compare symbols" "-1" "(compare (quote a) (quote b))"]
["compare bools" "-1" "(compare false true)"]
["compare vec length" "-1" "(compare [1 2] [1 2 3])"]
["compare vec elems" "-1" "(compare [1 2] [1 3])"]
["compare cross-type throws" :throws "(compare 1 \"a\")"]
["sort with compare" "[nil 1 3]" "(sort compare [3 nil 1])"]
["type meta override" ":custom" "(type (with-meta [1] {:type :custom}))"]
["type of record" "true" "(do (defrecord TyR [a]) (= (symbol (str (type (->TyR 1)))) (type (->TyR 1))))"]
["any? value" "true" "(any? 5)"]
["any? nil" "true" "(any? nil)"]
["gensym is symbol" "true" "(symbol? (gensym))"]
["gensym prefix" "true" "(do (require (quote [clojure.string :as s])) (s/starts-with? (str (gensym \"p_\")) \"p_\"))"]
["gensym distinct" "false" "(= (gensym) (gensym))"]
["int? Inf false" "false" "(int? ##Inf)"]
["integer? Inf false" "false" "(integer? ##Inf)"]
["integer? NaN false" "false" "(integer? ##NaN)"])