Iterative cek_run broke page-script parsing in browser. Reverted to
recursive — bytecode compilation overflow handled by native Sx_compiler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace Node.js compile-modules.js with direct Sx_compiler.compile_module
calls in mcp_tree.ml. No subprocess, no JIT warm-up, no Node.js.
23 files compile in 1.9 seconds.
Also includes rebuilt WASM kernel (iterative cek_run) and all 23
bytecode modules recompiled with native compiler.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrite compile-modules.js to use the native OCaml sx_server binary
instead of the js_of_ocaml kernel in Node.js. Compiles 23 modules in
23s (was 3+ minutes). Uses batch epoch protocol with latin1 encoding
to preserve byte positions for multi-byte UTF-8 content.
- Add compile-blob server command: parse source natively, compile via
SX compile-module, return bytecode dict
- Fix orchestration.sxbc.json and boot.sxbc.json — never compiled
successfully with the old JS kernel, now work with native compiler
- Auto-copy compiled bytecode to shared/static/wasm/sx/ for serving
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CEK evaluator's cek_run was recursive (calls itself via cek_step).
Native OCaml handles deep recursion but wasm_of_ocaml compiles to JS
which has ~10K frame stack limit. Complex expressions (bytecode compiler)
exceeded this, causing "Maximum call stack size exceeded" in all WASM
bytecode compilation.
Replace recursive cek_run with iterative while loop — same semantics,
zero stack growth. Fixes sx_build_bytecode and browser-side evaluation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove prism.js, sweetalert2, body.js, sx-browser.js from shell —
only WASM kernel (sx_browser.bc.wasm.js + sx-platform.js) loads
- Restore request-handler.sx integration: SX handles routing + AJAX
detection, OCaml does aser → SSR → shell render pipeline
- AJAX fragment support: SX-Request header returns content fragment
(~14KB) instead of full page (~858KB), cached with "ajax:" prefix
- Fix language/applications/etc page functions to return empty fragment
instead of nil (was causing 404s)
- Shared JIT VM globals: env_bind hook mirrors ALL bindings to a single
shared globals table — eliminates stale-snapshot class of JIT bugs
- Add native `parse` function for components that need SX parsing
- Clean up unused shell params (sx-js-hash, body-js-hash, head-scripts,
body-scripts, use-wasm) from shell.sx, helpers.py, and server.ml
14/32 Playwright tests pass (navigation, SSR, isomorphic, geography).
Remaining failures are client-side (WASM bytecode 404s block hydration).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rewrite geography-demos, demo-interactions, and reactive-nav specs
to use 300-500ms waits instead of 2-3s sleeps. Each test still does
a fresh page.goto() for isolation (no knock-on failures) but benefits
from server response cache for near-instant page loads.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restore page pre-warming at HTTP server startup (was skipped) and
increase render workers from 2→4. Tighten Playwright timeouts and
run 3 workers in parallel for faster test runs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previous sed edits corrupted the file. Restored from 5c8b05a and
applied only the def-store change cleanly via python (no sed/sx-tools).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All OCaml files restored to the last known working state (5c8b05a).
All SX changes preserved and verified working with native server.
Docker compose updated to run sx_server.exe --http directly.
859KB homepage renders, 7/9 pages cached, ~30s startup (JIT fallback).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The sx-handle-request function (which routes URLs to page rendering)
was removed from request-handler.sx. Without it, the server's
http_render_page calls sx-handle-request which doesn't exist,
returning nil for every request → 500 errors.
Restored from the last known working version (394c86b).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The optimization to call the compiler through VM directly (instead
of CEK) when it's JIT-compiled was producing incorrect bytecode for
all subsequently compiled functions, causing "Expected number, got
symbol" errors across render-to-html, parse-loop, etc.
Revert to always using CEK for compilation. The compiler runs via
CEK which is slower but produces correct bytecode. JIT-compiled
USER functions still run at VM speed.
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The iterative cek_run with Atomic step counting and debug prints
added overhead and complexity. The debug print called value_to_str
twice per million steps which could be very slow on large expressions.
Restore the original recursive cek_run from before the iterative
conversion. Remove the step limit mechanism (was causing render
timeouts). The recursive version is simpler and proven.
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removing injection broke GLOBAL_GET for all JIT-compiled functions,
not just mutable closures. Top-level functions like render-to-html
need their referenced bindings in the VM globals table.
Restore the original injection (only injects values not already in
globals). Mutable closure vars (parser's pos etc.) still get stale
snapshots and fall back to CEK — that's the known limitation to fix
with cell-based boxing later.
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SX pretty-printer was reformatting compiler.sx on every sx-tree
edit, subtly breaking JIT bytecode output. Restored the exact original
file from f3f70cc and added compile-match + dispatch entry using sed
(no sx-tree tools that trigger pretty-printing).
Also: stop injecting stale closure snapshots into VM globals — let
GLOBAL_GET fall through to vm_closure_env for live bindings.
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: jit_compile_lambda copied closure variable VALUES into the
VM globals table. GLOBAL_GET found these stale snapshots instead of
falling through to vm_closure_env which has live bindings. When set!
mutated a variable (like parser's pos), the JIT code read the old
snapshot value.
Fix: don't inject closure bindings into globals at all. GLOBAL_GET
already has a fallback path that walks vm_closure_env — this sees
live env bindings that are updated by set!. No new opcodes needed.
This should fix JIT for parse-loop, skip-ws, read-expr and other
closure functions that use mutable variables.
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The SX pretty-printer reformatted the entire compiler — different
indentation, removed comments, changed dict literals. This broke
JIT compilation for render-to-html and other functions that were
previously working.
Restore the exact original formatting from before the match commits
and add compile-match as a clean insertion (no reformatting). The
compiler's own dispatch stays as cond (safe with JIT).
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Atomic.fetch_and_add on every CEK step added unnecessary overhead.
The step counter is per-invocation (not shared across threads), so
a plain int ref with incr is sufficient and faster.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prints phase + control expression every 1,048,576 steps so infinite
loops become visible in logs. Only fires when step count is high
enough to indicate a real hang, not normal execution.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Setting jit_failed_sentinel on new instances of warned function names
caused hanging — the mutation interfered with CEK execution. Now just
return None without touching l_compiled. The _jit_warned hash lookup
is a cheap check per call with no side effects.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New closure instances of functions like parse-loop get l_compiled=None
even though a previous instance already failed. Now check _jit_warned
by name and immediately sentinel-mark new instances of known failures.
The first failure is always logged. Subsequent instances are silently
sentinel-marked — no recompilation, no log spam, no suppressed errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Revert the _jit_warned check that silenced repeated JIT failures.
These errors indicate real JIT limitations (mutable closures) that
should remain visible until properly fixed. The sentinel on the
instance still prevents the same lambda from retrying.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The compiler's own match dispatch caused cascading JIT failures —
when compile-list is pre-compiled, the match bytecode is embedded
in it, and any subtle issue propagates to ALL subsequent compilations.
compile-list reverts to cond (battle-tested with JIT). compile-match
remains available for user code that uses match expressions.
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a JIT-compiled function failed on first call, the function name
was added to _jit_warned but this was never checked before recompiling.
Closures like parse-loop create new lambda instances on each call,
each with l_compiled=None, triggering fresh compilation + failure
in an infinite loop.
Fix: check _jit_warned before attempting compilation, and mark the
lambda with jit_failed_sentinel on first-call failure so the same
instance also stops retrying.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
compile-match-clauses was a separate define that the JIT VM couldn't
find at runtime ("VM undefined: compile-match-clauses"). Inline it
as a letrec-bound local function inside compile-match so it's
captured in the closure and visible to JIT-compiled code.
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The JIT compiler now handles the match special form, emitting the
same DUP/compare/JUMP_IF_FALSE bytecode pattern as case. Supports
literal patterns (string, number, boolean, nil), _ wildcard, symbol
binding, and quoted symbol patterns.
This fixes the infinite JIT retry loop where compile-list (which
used match for dispatch) couldn't be JIT-compiled, causing
parse-loop to endlessly fall back to CEK evaluation.
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The match special form in compile-list caused JIT-compiled functions
to fail with "Expected number, got symbol" errors, creating an
infinite retry loop in parse-loop. The JIT compiler can't compile
match expressions, so compile-list fell back to CEK on every call.
Revert to the original cond-based dispatch. The evaluator.sx match
conversions are fine (not used by JIT), only the compiler was affected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The home stepper's step-idx signal was not persisting across SX
navigation because set-cookie/freeze-to-sx wasn't working in the
WASM kernel. Replace with def-store which uses a global registry
that survives island re-hydration.
Also fix sx_http.exe build: add sx_http back to dune, inline scope
primitives (Sx_scope module was removed), add declarative form
stubs and render stubs, fix /sx/ home route mapping.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
OCaml HTML renderer (sx_render.ml) silently returned "" when env_get
failed for primitive function calls (str, +, len, etc.) inside HTML
elements. The Eval_error catch now falls through to eval_expr which
resolves primitives correctly. Fixes 21 rendering tests.
Rename condition system special form from "signal" to "signal-condition"
in evaluator.sx, matching the OCaml bootstrapped evaluator (sx_ref.ml).
This avoids clashing with the reactive signal function. Fixes 9
condition system tests.
1166 passed, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- request-handler.sx: replace all dots (not just `.(`) and auto-quote
undefined symbols as strings so 3-level URLs like
/sx/(geography.(reactive.(examples.counter))) resolve correctly
- sx-platform.js: register popstate handler (was missing from manual
boot sequence) and fetch full HTML for back/forward navigation
- sx_ref.ml: add CEK step limit (10M steps) checked every 4096 steps
so runaway renders return 500 instead of blocking the worker forever
- Rename test-runner.sx → runner-placeholder.sx to avoid `test-` skip
- Playwright config: pin testDir, single worker, ignore worktrees
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Island markers rendered during SX navigation responses had no
data-sx-state attribute, so hydration found empty kwargs and path
was nil in the copyright display. Now adapter-dom.sx serializes
keyword args into data-sx-state on island markers, matching what
adapter-html.sx does for SSR.
Also fix post-swap to use parent element for outerHTML swaps in
SX responses (was using detached old target). Add SX source file
hashes to wasm_hash for proper browser cache busting — changing
any .sx file now busts the cache. Remove stale .sxbc bytecode
cache files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The handler routes correctly and renders content, but shell statics
(__shell-sx-css etc.) resolve to nil/empty in the handler scope.
use-wasm flag also not working — need to remove sx-browser.js fallback.
Needs: debug shell static resolution in SX handler scope.
Remove sx-browser.js from shell template entirely.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
web/request-handler.sx: configurable SX handler called by OCaml server.
Detects AJAX (SX-Request header) and returns content fragment (no shell)
vs full page with shell. All routing, layout, response format in SX.
OCaml server: http_render_page calls sx-handle-request via CEK.
No application logic in OCaml — just HTTP accept + SX function call.
signal-condition rename: reactive signal works, condition system uses
signal-condition. Island SSR renders correctly (4/5 tests pass).
WASM JIT: no permanent disable on failure. Live globals.
WIP: page-sx empty in SX handler — client routing needs it.
Navigation tests timeout (links not found after boot).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert large cond chains doing string equality dispatch to use the
match special form: step-eval-list (42 arms), step-continue (31 arms),
compile-list (30 arms), ho-setup-dispatch (7 arms), value-matches-type?
(10 arms). Also fix test-canonical.sx to use defsuite/deftest format
and load canonical.sx in both test runners.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New web/request-handler.sx: configurable SX function (sx-handle-request)
that receives path + headers + env and returns rendered HTML.
The handler decides full page vs AJAX fragment.
OCaml server: http_render_page now just calls the SX handler.
All routing, layout selection, AJAX detection moved to SX.
Header parsing added. is_sx_request removed from OCaml.
Configurable via SX_REQUEST_HANDLER env var (default: sx-handle-request).
WIP: handler has parse errors on some URL formats. Needs debugging.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The stepper SSR output IS correct (273 bytes, identical to Quart,
no raw ~cssx/tw). The test was checking textContent which included
text outside the island boundary (the code-view documentation text).
Fixed to check innerHTML for HTML elements and class attributes.
5/5 island SSR tests pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
WASM kernel (sx_browser.ml): remove jit_failed_sentinel marking on
JIT runtime errors. Functions stay JIT-compiled and retry on next call.
Errors are data-dependent, not JIT bugs.
signal-condition: rename in CEK dispatcher so reactive 'signal' function
works. The conditions system 'signal' special form is now 'signal-condition'.
adapter-html.sx: remove cek-try wrapping island render. Errors propagate.
Island SSR: 4/5 tests pass. Header renders perfectly. Stepper ~cssx/tw
not expanding because component calls in island body aren't evaluated
by render-to-html — they're serialized as text. Needs SX adapter fix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: the new conditions system's 'signal' special form shadowed
the reactive 'signal' function. (signal 0) in island bodies raised
'Unhandled condition: 0' instead of creating a signal dict.
Fix: rename condition special form to 'signal-condition' in the CEK
dispatcher. The reactive 'signal' function now works normally.
adapter-html.sx: remove cek-try that swallowed island render errors.
Islands now render directly — errors propagate for debugging.
sx_render.ml: add sx_render_to_html that calls SX adapter via CEK.
Results: 4/5 island SSR tests pass:
- Header island: logo, tagline, styled elements ✓
- Navigation buttons ✓
- Geography content ✓
- Stepper: partially renders (code view OK, ~cssx/tw in heading)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Server always returns full page. Client sx-select handles extraction.
No application logic in OCaml server.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jit_compile_lambda now uses the live globals table directly instead
of Hashtbl.copy. Closure bindings that aren't already in globals are
injected into the live table. This ensures GLOBAL_GET always sees
the latest define values.
Previously: Hashtbl.copy created a stale snapshot. Functions defined
after the copy (like cssx-process-token from cssx.sx) resolved to nil
in JIT-compiled closures.
JIT error test now passes — 0 CSSX errors during navigation.
7/8 navigation tests pass. Remaining: back button content update.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Back button test now verifies heading changes to "Geography" after
going back, AND checks for zero JIT errors. Catches real bugs:
- 129 JIT "Not callable: nil" errors during navigation (CSSX closure)
- Back button content may not update (to be verified after JIT fix)
New test: "no JIT errors during navigation" — fails with 129 errors,
correctly identifying the CSSX closure capture bug.
6 pass, 2 fail (real bugs, not test issues).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AJAX fragment: extract #main-panel with matching close tag (depth
tracking) instead of taking everything to end of file. Prevents
shell closing tags from breaking the DOM swap.
Back button test: verifies content actually changes — checks for
"Geography" and "Rendering Pipeline" after going back, not just
that body has >100 chars. Tests forward nav content change too.
7/7 navigation tests pass including back button content verification.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>