Commit graph

6 commits

Author SHA1 Message Date
Yogthos
b1486d58a0 ci: fix the one red test on CI runners; document the gate + porting gotchas
nrepl-test was CI's only failure: the server subprocess ran main.janet from
source, paying the full compile-mode init, which outran the 5s connect poll
on slow runners (locally it always won the race). The test now prefers
build/jolt — its ctx is baked at build time, so it accepts in ~20ms and CI
builds it anyway — falls back to source, polls up to 60s under a 90s
watchdog, and dumps the server's stderr when startup fails so the next CI
failure is diagnosable.

CLAUDE.md's placeholder sections become real: build/test commands with the
run-the-gate-with-a-real-exit-code protocol (a piped gate once shipped masked
spec failures), the seed/overlay/tier architecture sketch, and the porting
gotchas that have each bitten at least once (leaf verification, stub-breaks-
self-recursion, tier macro ordering, ref-get vs get on attached-ops wrappers,
:jolt/type map keys, expander-called fns, canonical-port policy) — previously
only in local bd memories.
2026-06-10 16:59:12 -04:00
Yogthos
0e3584884f core: AOT context image — init-cached recovers the bootstrap cost across processes
init in compile mode is ~2.4 s (tier loading, analyzer self-compile, macro
recompilation), paid by every process that builds a ctx from source — each
jpm-test file, embedders, workers. init-cached marshals the built ctx to a
disk image (same root-env dicts as snapshot/fork) and later processes
unmarshal it in ~5 ms, any process: nothing from the baking process is
needed at load.

The cache key fingerprints the embedded .clj stdlib (which covers jolt-core:
analyzer, IR, core tiers), the .janet seed sources next to the module, the
janet version, the init opts, and the env knobs that shape a ctx (JOLT_PATH/
MUTABLE/AOT_CORE/FEATURES) — any change rebuilds. Corrupt or non-ctx images
fall back to a rebuild (unmarshal of garbage can 'succeed' with a scalar, so
the shape is checked, not just the throw). Writes are atomic (tmp + rename)
so racing cold starts never publish a torn image. JOLT_NO_IMAGE_CACHE=1
opts out; JOLT_IMAGE_CACHE_DIR overrides the location (default TMPDIR).

Test consumers switch to init-cached (harness, suite-worker, conformance,
the behavioral unit/integration tests); tests that validate the bootstrap
itself (bootstrap-fixpoint, staged-bootstrap, aot round-trip, direct-linking)
and the deps tests (tmp-dir :paths would fragment the key) keep real init.
Full jpm test: 2:46 -> 1:58 (~29%). New ctx-image-test covers cold/warm,
cross-process load (subprocess runs defn/redef/macros/protocols/multimethods
off the baked image), per-opts keying, and corrupt-image fallback.
2026-06-10 13:57:37 -04:00
Yogthos
d00c3cc117 fix: pr-str/str of a var, and nREPL namespace switching
- core: pr-render/str-render now render a Jolt var as #'ns/name instead of
  falling through to the generic table printer, which recursed into the var's
  cyclic :meta/:ns refs and looped forever. Adds name-of/var-display helpers.
- jolt.nrepl: capture the post-eval namespace *after* running the form — jolt
  evaluates map-literal values right-to-left, so {:val (eval form) :ns (the-ns)}
  read the ns before in-ns ran, pinning every session to 'user'. Now in-ns/ns
  switches persist across evals. render-value dropped: pr-str handles vars.
- specs: var printing (strings-spec) and nREPL in-ns/ns-persistence + explicit
  :ns override (nrepl-test).
2026-06-05 22:05:45 -04:00
Yogthos
5b66cfaa97 test: cover the janet interop bridge and nREPL var rendering
- host-interop-spec: add an 'interop / janet bridge' suite — janet/<name> and
  janet.<module>/<name> resolution, the value-representation boundary (a Jolt
  vector crosses as a Janet table), explicit-only (unprefixed module not
  exposed), and unknown-symbol errors.
- nrepl-test: assert a def's value renders as #'ns/name (pr-str loops on a
  var's cyclic ns refs, so jolt.nrepl renders vars itself).
2026-06-05 21:43:15 -04:00
Yogthos
dd96b1900a test(nrepl): add watchdog + explicit exit so the integration test can't hang CI 2026-06-05 21:26:32 -04:00
Yogthos
8cbc695f99 feat(nrepl): nREPL server + client in Clojure on a Janet interop bridge
Add a general Janet interop bridge and an nREPL implemented on top of it.

Interop bridge (evaluator):
- A qualified symbol whose namespace is `janet` or `janet.<module>` resolves
  against Janet's environment: `janet/<name>` -> root binding (janet/slurp),
  `janet.<module>/<name>` -> module binding (janet.net/server, janet.os/clock).
  The explicit `janet` segment marks every crossing into host code (where
  Clojure semantics, e.g. collection representation, no longer hold). This makes
  the whole Janet stdlib — networking included — reachable from Clojure.

jolt.nrepl (Clojure, src/jolt/jolt/nrepl.clj):
- bencode codec (encode + streaming decode), ported from nrepl.bencode.
- server: accept loop via janet.net/accept + janet.ev/call (Janet's built-in
  handler arity-checks Jolt closures, so we drive accept ourselves); ops clone/
  describe/eval/load-file/close/ls-sessions/interrupt/eldoc following
  babashka.nrepl response shapes. eval captures *out* by rebinding Janet's :out,
  reports ns, streams out, and isolates the eval namespace (current-ns is global
  ctx state) restoring it afterward. Vars are rendered as #'ns/name (pr-str
  loops on a var's cyclic ns refs).
- client: connect / request / client-eval / client-clone / client-close.

CLI: `jolt nrepl [port]` starts the server and writes .nrepl-port; the Clojure
source is embedded at build time so the binary is self-contained from any cwd.

Tests: test/spec/nrepl-spec.janet (bencode), test/integration/nrepl-test.janet
(server+client over a real TCP/bencode wire, server in a subprocess).
2026-06-05 21:25:23 -04:00