vm-global-get checked vm.globals before closure-env, while vm-global-set
wrote to closure-env first. This asymmetry meant set! mutations to mutable
closure variables (e.g. parser position counters) were invisible to sibling
closures reading via JIT — they saw stale snapshots in the globals table.
Reversed vm-global-get lookup order: closure env → globals → primitives,
matching vm-global-set. Also enabled JIT in the MCP harness (compiler.sx
loading, env_bind hook for live globals sync, jit_try_call hook) so
sx_harness_eval exercises the same code path as the server.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bytecode compiler now emits OP_PERFORM for (import ...) and compiles
(define-library ...) bodies. The VM stores the import request in
globals["__io_request"] and stops the run loop — no exceptions needed.
vm-execute-module returns a suspension dict, vm-resume-module continues.
Browser: sx_browser.ml detects suspension dicts from execute_module and
returns JS {suspended, op, request, resume} objects. The sx-platform.js
while loop handles cascading suspensions via handleImportSuspension.
13 modules load via .sxbc bytecode in 226ms (manifest-driven), both
islands hydrate, all handlers wired. 2650/2650 tests pass including
6 new vm-import-suspension tests.
Also: consolidated sx-platform-2.js → sx-platform.js, fixed
vm-execute-module missing code-from-value call, fixed bootstrap.py
protocol registry transpiler issues.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds opcode 112 (OP_PERFORM / IO suspension) to lib/vm.sx's vm-step
dispatch. The native sx_vm.ml already has this opcode — this brings
the SX spec into alignment.
Full VM transpilation deferred to Step 6 (define-record-type): the
dict-based state in vm.sx maps to get_val/sx_dict_set_b calls which
are ~5x slower than native record/array access in the hot loop.
define-record-type will let vm.sx use typed records that the
transpiler maps to OCaml records natively.
2598/2598 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SX-level VM couldn't find for-each/map/etc. because they're CEK
special forms, not primitives in the JS host. Added vm-resolve-ho-form
which creates wrapper functions that dispatch callbacks through
vm-call-external (handling VmClosure callbacks correctly).
Also fixed vm-call dispatch order: Lambda/Component checked before
generic callable? to avoid calling SX Lambdas as JS functions.
JS full: 1585/1585 (was 1582/3 failed). All 3 pre-existing failures fixed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Registering map/for-each/reduce as PRIMITIVES caused the compiler to
emit CALL_PRIM for them everywhere. The OCaml VM's call-primitive
can't invoke VM closures, causing "Undefined symbol: resource" crashes.
Revert vm.sx to original CALL_PRIM handler. Remove map/for-each/reduce
from JS PRIMITIVES so compiler emits OP_CALL instead (handled by
vm-call which dispatches correctly).
3 JS VM tests remain failing (VM closure interop) but production is stable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Handle for-each/map/reduce in VM's CALL_PRIM handler instead of JS
primitives. VM closures are called via vm-call-closure (fresh VM,
shared upvalue cells). Native callables dispatch directly.
Named let doesn't work inside vm-step context — use explicit define
+ recursion for reduce loop.
JS tests: 1585/1585 (0 failures)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
spec/ now contains only the language definition (5 files):
evaluator.sx, parser.sx, primitives.sx, render.sx, special-forms.sx
lib/ contains code written IN the language (8 files):
stdlib.sx, types.sx, freeze.sx, content.sx,
bytecode.sx, compiler.sx, vm.sx, callcc.sx
Test files follow source: spec/tests/ for core language tests,
lib/tests/ for library tests (continuations, freeze, types, vm).
Updated all consumers:
- JS/Python/OCaml bootstrappers: added lib/ to source search paths
- OCaml bridge: spec_dir for parser/render, lib_dir for compiler/freeze
- JS test runner: scans spec/tests/ (always) + lib/tests/ (--full)
- OCaml test runner: scans spec/tests/, lib tests via explicit request
- Docker dev mounts: added ./lib:/app/lib:ro
Tests: 1041 JS standard, 1322 JS full, 1101 OCaml — all pass
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>