Commit Graph

148 Commits

Author SHA1 Message Date
fb30351be2 Post-10d: JIT measurement infrastructure + compiler fixes
Measurement:
- JIT hit/miss/skip counters in sx_runtime.ml (jit_try_call)
- VM instruction counter enabled in run loop
- jit-enable, vm-counters, vm-counters-reset epoch commands
- Test runner --jit flag for opt-in JIT measurement
- Results (132 tests): 5.8% VM hit, 56% evaluator self-calls, 38% anon

Fixes:
- Move compile-provide, compile-scope, compile-guard,
  compile-guard-clauses inside define-library begin block
  (were orphaned outside, causing "Undefined symbol" JIT failures)
- Add deref primitive (signal unwrap with tracking)
- Add deref compiler dispatch
- Fix compile-expr for scope forms to handle non-keyword args

CEK pruning assessment: evaluator self-calls (56%) can't be pruned —
the CEK must evaluate itself. Real pruning requires self-hosting
compiler (Phase 2+). The VM correctly handles user code that
JIT-compiles. 2776/2776 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 14:32:48 +00:00
b8f389ac9b Import hook: verify library registered, re-entry guard
- _import_hook verifies library_loaded_p AFTER load_library_file
  to catch cases where the file loads but define-library doesn't register
- Re-entry guard (_loading_libs) prevents infinite retry loops
- cek_run import patch deferred — retry approach infinite-loops because
  cek_step_loop re-enters deeply nested eval contexts. Root cause:
  eval_expr → cek_run → cek_step_loop processes the ENTIRE remaining
  kont chain after import resolution, which includes rendering code that
  triggers MORE eval_expr calls. Needs architectural solution (step-level
  suspension handling, not run-level).

Server runs with 4 harmless IO suspension errors. These don't affect
functionality — symbols load via the global env.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 01:16:03 +00:00
244c669334 Revert cek_run import patch — caused infinite CEK loop on server
The cek_run import handling (resume after hook loads library) caused
cek_step_loop to infinite-loop during aser page rendering. Root cause
not yet identified — the resumed CEK state never reaches terminal.

Reverted to original cek_run that throws "IO suspension in non-IO
context". The 4 server startup errors are harmless (files load
partially, all needed symbols available via other paths).

Import hook re-entry guard and debug logging retained for future work.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 00:59:45 +00:00
6e216038ba Fix import resolution: correct library paths + hook type mismatch
Root causes of server [http-load] errors:
1. _import_hook passed pre-computed string key to library_loaded_p
   which calls library_name_key(string) → sx_to_list(string) → crash.
   Fix: pass original list spec, not the string key.
2. resolve_library_path didn't check web/lib/ for (sx dom), (sx browser),
   (web boot-helpers). These libraries use namespace prefixes that don't
   match their file locations.

Server startup errors: 190 → 0.
2683/2684 tests pass (1 known: define-library import clause — spec gap).
New test file: spec/tests/test-import-bind.sx

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 23:44:22 +00:00
5ac1ca9756 Fix server import suspension, dist sync, JIT errors
- cek_run patched to handle import suspensions via _import_hook.
  define-library (import ...) now resolves cleanly on the server.
  IO suspension errors: 190 → 0. JIT failures: ~50 → 0.
- _import_hook wired in sx_server.ml to load .sx files on demand.
- compile-modules.js syncs source .sx files to dist/sx/ before
  compiling — eliminates stale bytecode from out-of-date copies.
- WASM binary rebuilt with all fixes.
- 2658/2658 tests pass (8 new — previously failing import tests).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 22:52:41 +00:00
e46cdf3d4d Wire transpiled VM as active execute_module — 2644 tests pass
The transpiled VM (sx_vm_ref.ml, from lib/vm.sx) is now the ACTIVE
bytecode execution engine. sx_server.ml and sx_browser.ml call
Sx_vm_ref.execute_module instead of Sx_vm.execute_module.

Results:
- OCaml tests: 2644 passed, 0 failed
- WASM tests: 32 passed, 0 failed
- Browser: zero errors, zero warnings, islands hydrate
- Server: pages render, JIT compiles, all routes work

The VM logic now lives in ONE place: lib/vm.sx (SX).
OCaml gets it via transpilation (bootstrap_vm.py).
JS/browser gets it via bytecode compilation (compile-modules.js).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 13:34:11 +00:00
7b4c918773 Recompile all 26 .sxbc with define-library wrappers + fix eval/JIT
All 26 browser modules recompiled with define-library/import forms.
Compilation works without vm-compile-adapter (JIT pre-compilation
hangs with library wrappers in some JIT paths — skipped for now,
CEK compilation is ~34s total).

Key fixes:
- eval command: import-aware loop that handles define-library/import
  locally without touching the Python bridge pipe (avoids deadlock)
- compile-modules.js: skip vm-compile-adapter, bump timeout

2621/2621 OCaml tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 00:08:00 +00:00
ac772ac357 IO-aware eval for server dispatch + compile-modules timeout bump
sx_server.ml: the "eval" command now uses cek_run_with_io instead of
raw eval_expr. This handles import suspensions during eval-blob
(needed for .sx files with define-library/import wrappers).

compile-modules.js: timeout bumped 5min → 10min for sxbc compilation
with define-library overhead.

2608/2608 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 23:31:41 +00:00
2d7dd7d582 Step 5 piece 6: migrate 23 .sx files to define-library/import
Wraps all core .sx files in R7RS define-library with explicit export
lists, plus (import ...) at end for backward-compatible global re-export.

Libraries registered:
  (sx bytecode)      — 83 opcode constants
  (sx render)        — 15 tag registries + render helpers
  (sx signals)       — 23 reactive signal primitives
  (sx r7rs)          — 21 R7RS aliases
  (sx compiler)      — 42 compiler functions
  (sx vm)            — 32 VM functions
  (sx freeze)        — 9 freeze/thaw functions
  (sx content)       — 6 content store functions
  (sx callcc)        — 1 call/cc wrapper
  (sx highlight)     — 13 syntax highlighting functions
  (sx stdlib)        — 47 stdlib functions
  (sx swap)          — 13 swap algebra functions
  (sx render-trace)  — 8 render trace functions
  (sx harness)       — 21 test harness functions
  (sx canonical)     — 12 canonical serialization functions
  (web adapter-html) — 13 HTML renderer functions
  (web adapter-sx)   — 13 SX wire format functions
  (web engine)       — 33 hypermedia engine functions
  (web request-handler) — 4 request handling functions
  (web page-helpers) — 12 page helper functions
  (web router)       — 36 routing functions
  (web deps)         — 19 dependency analysis functions
  (web orchestration) — 59 page orchestration functions

Key changes:
- define-library now inherits parent env (env-extend env instead of
  env-extend make-env) so library bodies can access platform primitives
- sx_server.ml: added resolve_library_path + load_library_file for
  import resolution (maps library specs to file paths)
- cek_run_with_io: handles "import" locally instead of sending to
  Python bridge

2608/2608 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:48:54 +00:00
5f72801901 Step 3: IO registry — spec-level defio + io contract dispatch
Promotes defio from native OCaml special form to spec-level CEK
evaluator feature. The IO registry is now the contract layer between
evaluator and platform.

Evaluator additions (spec/evaluator.sx):
- *io-registry* mutable dict global (like *library-registry*)
- io-register!, io-registered?, io-lookup, io-names accessors
- defio-parse-kwargs! recursive keyword parser
- sf-defio processes (defio "name" :category :data :params (...) ...)
- "defio" dispatch in step-eval-list
- step-sf-io: the contract function — validates against registry,
  then delegates to perform for IO suspension
- "io" dispatch in step-eval-list

Native OCaml defio handlers removed from:
- sx_server.ml (~20 lines)
- sx_browser.ml (~20 lines)
- run_tests.ml (~18 lines)
All replaced with __io-registry alias to spec's *io-registry*.

IO accessor functions bound in run_tests.ml env so tests can
call io-registered?, io-lookup, io-names.

10 new tests (spec/tests/test-io-registry.sx):
- defio populates registry
- io-lookup returns spec with name/category/returns/doc
- io-registered?/io-names work correctly
- kwargs parsing (batchable, cacheable, params)
- io contract rejects unregistered ops
- io contract passes validation for registered ops

2608/2608 tests passing (+10 new).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:18:04 +00:00
19e7a6ee2d Step 5.5 phases 1-2: merge sx_scope into sx_primitives, 4-child define support
Phase 1: Absorb sx_scope.ml (180 lines) into sx_primitives.ml. Scope stacks,
cookies, and trace infrastructure now live alongside other primitives. All 20
scope primitive registrations moved. References updated in sx_server.ml and
sx_browser.ml. sx_scope.ml deleted.

Phase 2: Transpiler handles (define name :effects (...) (fn ...)) forms.
ml-emit-define and ml-emit-define-body detect keyword at position 2 and use
(last expr) instead. Unblocks transpilation of spec/render.sx and
web/adapter-html.sx which use 4-child defines extensively.

2598/2598 tests passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 19:43:26 +00:00
1dd4c87d64 Step 5: CEK IO suspension + R7RS modules (define-library/import)
Third CEK phase "io-suspended": perform suspends evaluation, host
resolves IO, cek-resume feeds result back. VM OP_PERFORM (opcode 112)
enables JIT-compiled functions to suspend. VM→CEK→suspend chain
propagates suspension across the JIT/CEK boundary via pending_cek.

R7RS define-library creates isolated environments with export control.
import checks the library registry and suspends for unknown libraries,
enabling lazy on-demand loading. Import qualifiers: only, prefix.

Server-side cek_run_with_io handles suspension by dispatching IO
requests to the Python bridge and resuming. guard composes cleanly
with perform for structured error recovery across IO boundaries.

2598/2598 tests (30 new: 15 core suspension, 3 JIT, 1 cross-boundary,
9 modules, 2 error handling). Zero regressions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 18:55:43 +00:00
36acb56a3a Quiet noisy JIT compilation logs: only log slow (>500ms) or failed compiles
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 17:08:24 +00:00
bd8d62cd9a Zero bootstrap patches: all 11 moved to spec or runtime
- make-raise-guard-frame: was never defined in spec — added it
- *last-error-kont*: set at error origination (host-error calls), not
  wrapped around every cek-run step. Zero overhead on normal path.
- JIT: jit-try-call runtime function called from spec. Platform
  registers hook via _jit_try_call_fn ref. No bootstrap patching.
- bootstrap.py compile_spec_to_ml() now returns transpiled output
  with zero post-processing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 15:17:13 +00:00
ede05c26f5 IO registry: defio declares platform suspension points
Core SX has zero IO — platforms extend __io-registry via (defio name
:category :data/:code/:effect ...). The server web platform declares 44
operations in web/io.sx. batchable_helpers now derived from registry
(:batchable true) instead of hardcoded list. Startup validation warns if
bound IO ops lack registry entries. Browser gets empty registry, ready
for step 5 (IO suspension).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 23:21:48 +00:00
17b6c872f2 Server cleanup: extract app-specific config into SX
Phase 1 Step 2 of architecture roadmap. The OCaml HTTP server is now
generic — all sx_docs-specific values (layout components, path prefix,
title, warmup paths, handler prefixes, CSS/JS, client libs) move into
sx/sx/app-config.sx as a __app-config dict. Server reads config at
startup with hardcoded defaults as fallback, so it works with no config,
partial config, or full config.

Removed: 9 demo data stubs, stepper cookie cache logic, page-functions.sx
directory heuristic. Added: 29-test server config test suite covering
standard, custom, no-config, and minimal-config scenarios.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:00:32 +00:00
869f49bc01 Merge sx-tools: test coverage + bug fixes + Playwright fixes
- 7 new test files (~268 tests): stdlib, adapter-html, adapter-dom,
  boot-helpers, page-helpers, layout, tw-layout
- Fix component-pure? transitive scan, render-target crash on unknown
  components, &rest param binding (String vs Symbol), swap! extra args
- Fix 5 Playwright marshes tests: timing + test logic
- 2522/2522 OCaml tests, 173/173 Playwright tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

# Conflicts:
#	shared/static/wasm/sx/orchestration.sxbc
#	shared/static/wasm/sx_browser.bc.js
#	shared/static/wasm/sx_browser.bc.wasm.js
#	sx/sx/not-found.sx
#	tests/playwright/isomorphic.spec.js
2026-04-02 18:59:45 +00:00
6d5c410d68 Uncommitted sx-tools changes: WASM bundles, Playwright specs, engine fixes
WASM browser bundles rebuilt with latest kernel. Playwright test specs
updated (helpers, navigation, handler-responses, hypermedia-handlers,
isomorphic, SPA navigation). Engine/boot/orchestration SX files updated.
Handler examples and not-found page refreshed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:58:38 +00:00
a64b693a09 Remove old CSSX system — ~tw is the sole CSS engine
Phase 1 Step 1 of the architecture roadmap. The old cssx.sx
(cssx-resolve, cssx-process-token, cssx-template, old tw function)
is superseded by the ~tw component system in tw.sx.

- Delete shared/sx/templates/cssx.sx
- Remove cssx.sx from all load lists (sx_server.ml, run_tests.ml,
  mcp_tree.ml, compile-modules.js, bundle.sh, sx-build-all.sh)
- Replace (tw "tokens") inline style calls with (~tw :tokens "tokens")
  in layouts.sx and not-found.sx
- Remove _css-hash / init-css-tracking / SX-Css header plumbing
  (dead code — ~tw/flush + flush-collected-styles handle CSS now)
- Remove sx-css-classes param and meta tag from shell template
- Update stale data-cssx references to data-sx-css in tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:18:07 +00:00
dea1879e27 Fix vm_globals: prevent SX definitions from overwriting native primitives
The env_bind hook was copying SX-defined functions (e.g. has-key? from
stdlib.sx) into vm_globals, shadowing the native primitives seeded there.
CALL_PRIM then called the SX version which broke with wrong arg types.

Fix: env_bind hook skips names that are registered primitives. Native
implementations are authoritative for CALL_PRIM dispatch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:10:34 +00:00
92a59eba9d Fix vm_globals seeding: run after make_server_env to avoid HO wrapper conflict
Move primitive seeding to end of make_server_env() so ho_via_cek
wrappers (map, filter, etc.) are already in vm_globals. The seeding
only adds primitives NOT already present, preserving wrappers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 11:03:17 +00:00
c430ef8110 Unify CALL_PRIM dispatch: vm_globals as single source of truth
Seed all primitives into vm_globals as NativeFn values at init.
CALL_PRIM now looks up vm.globals only (not the separate primitives
table). This means OP_DEFINE and registerNative naturally override
primitives — browser.sx's (define set-cookie ...) now takes effect.

The primitives Hashtbl remains for the compiler's primitive? predicate
but has no runtime dispatch role.

Tests: 2435 pass / 64 fail (pre-existing), vs 1718/771 baseline.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 10:53:09 +00:00
e44a689783 Stepper cookie persistence: SSR + client-side save/restore
- Parse Cookie header in OCaml HTTP server for get-cookie primitive
- Stepper saves step-idx to cookie via host-set! FFI on click
- Stepper restores from cookie: get-cookie on server, host-get FFI on client
- Cache key includes stepper cookie value to avoid stale SSR
- registerNative: also update Sx_primitives table for CALL_PRIM dispatch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 10:28:22 +00:00
0019f8e56a Proper handler dispatch: nested slugs, method lookup, param binding
Rewrites the OCaml handler dispatch to handle all handler types:

1. Nested slug extraction: (api.(editrow.1)) → slug "editrow", param "1"
2. Method-based handler lookup: tries exact slug, then suffixes like
   -form (GET), -save/-submit (POST), -put (PUT)
3. Path param injection: extracts <sx:param_name> from handler path
   template, converts underscores to hyphens, injects into query string
4. Param binding: reads handler (&key) params via request-arg/request-form
   and binds them in env before aser evaluation
5. Handles both List and ListRef param storage (CEK may mutate)
6. Response status support: set-response-status handled natively

Fixes: delete-row, edit-row, tabs, editrow-cancel, and all parameterized
handlers. 17/19 handlers now return 200 (flaky=503 intentional, slow=500
IO bridge timeout).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 21:52:59 +00:00
f66195ce18 Fix delete-row handler: nested slug extraction in OCaml dispatch
The slug extractor after (api. scanned for the first ) but for nested
URLs like (api.(delete.1)) it got "(delete.1" instead of "delete".
Now handles nested parens: extracts handler name and injects path
params into query string. Also strengthened the Playwright test to
accept confirm dialogs and assert strict row count decrease.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:41:32 +00:00
bdb54d5919 Handler dispatch in OCaml: look up handler dict, aser body directly
Skip the SX api function entirely. The OCaml handler interception
looks up handler:ex-{slug} in the env, extracts the body, and calls
aser directly with the full global env. This avoids the double-eval
problem where eval-expr evaluates HTML tags as function calls and
then tries to call the result.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 14:42:02 +00:00
a0f4ff02a1 Fix defhandler: native special form in make_server_env
defhandler was only available via web-forms.sx which had load
dependencies that failed. Now registered as a native special form
in make_server_env, works in both coroutine and HTTP modes.

Key fix: custom special forms receive [List args; Env eval_env],
not flat args. The handler is now bound in the eval env, not the
make_server_env closure env.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 14:09:14 +00:00
775ab301f6 Fix debug endpoint: use raw_path to preserve query string
The query string was stripped from path for routing but the debug
endpoint needs it to parse ?expr= and ?name= params.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 13:50:17 +00:00
86c67e5955 Auto-restart HTTP server when binary changes
Checks binary mtime every 10 requests. If the binary has been
rebuilt, closes the listen socket and exec's itself. No more
stale server after builds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 13:45:07 +00:00
4ea43e3659 Handler endpoints bypass page routing, return raw fragments
Paths containing (api.) are intercepted before page routing and
dispatched directly to the api function. The handler result is
rendered to SX wire format and returned without layout wrapping.

This fixes the issue where handler URLs went through page routing,
causing the handler result to be passed as a slug to the page
function, and the response to be wrapped in the full page layout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 09:50:16 +00:00
461fae269b Request primitives, POST support, handler dispatch
OCaml server:
- Accept POST/PUT/PATCH/DELETE for /sx/ paths (was GET-only)
- Parse request body, query string, set per-request context
- Add 16 request primitives: now, state-get/set!, request-form/arg,
  request-json, request-header(s), request-method/body, into, etc.
- URL-encoded body parser for form submissions

Handler dispatch (sx/sx/handlers/dispatch.sx):
- `api` function routes URL paths like (api "click") to handler:ex-click
- `call-handler` checks HTTP method, binds params, evaluates body
- Handlers defined via defhandler in handlers/examples.sx now reachable

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 00:54:12 +00:00
9bd03bc26b Load web/page-helpers.sx in HTTP server core files
build-reference-data, build-attr-detail, etc. were undefined because
page-helpers.sx wasn't in the explicit core_files list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 23:38:35 +00:00
833415b170 Error pages render within layout instead of bare nil/404
Route errors and missing pages now show a styled error message inside
the normal layout (header, nav still work) instead of bare "nil" text
or a raw "Not Found" page. AJAX errors return renderable SX error
fragments instead of "nil" strings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 23:20:50 +00:00
28273eb740 Fix component-source SSR override, add SX island tests
component-source from data/helpers.sx was overriding the native
OCaml version. The SX version calls env-get with wrong arity (1 arg
vs required 2), producing empty source. Re-bind the native version
in SSR overrides after file loading.

Note: source code still not visible because highlight function
returns empty — separate issue in the aser rendering pipeline.

Also adds:
- spec/tests/test-reactive-islands.sx — 22 SX-native tests for all
  14 reactive island demos (render + signal logic + DOM)
- tests/node/run-sx-tests.js — Node runner for SX test files
- tests/node/test-reactive-islands.js — 39 Node/happy-dom tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:44:11 +00:00
200e5d5e47 Add .sxbc MIME type to OCaml HTTP server static file handler
Stops 404s for bytecoded module files — the server now serves .sxbc
files with the correct content type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:00:44 +00:00
e2b29fb9f3 Fix effect SSR: rebind after file load instead of guarding spec
Revert (when (client?) ...) guard in signals.sx — it broke JS tests
since client? is false in Node.js too.

Instead, rebind effect and register-in-scope as no-ops in sx_server.ml
AFTER all .sx files load. The SX definition from signals.sx is replaced
only in the OCaml SSR context. JS tests and WASM browser keep the real
effect implementation.

Remove redundant browser primitive stubs from sx_primitives.ml — only
resource SSR stub needed (effect override moved to server setup).

JS tests: 1582/1585 (3 VM closure interop remain)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 10:55:04 +00:00
128dbe1b25 Live demo islands with highlighted source: SSR-safe effect, native component-source
- signals.sx: guard effect body with (when (client?) ...) so effects
  are no-op during SSR — only 2 stubs needed (effect, register-in-scope)
- sx_primitives.ml: add resource SSR stub (returns signal {loading: true}),
  remove 27 unnecessary browser primitive stubs
- sx_server.ml: native component-source that looks up Component/Island
  from env and pretty-prints the definition (replaces broken Python helper)
- reactive-islands/index.sx: Examples section with all 15 live demos
  inline + highlighted source via component-source
- reactive-islands/demo.sx: replace 14 hardcoded highlight strings with
  (component-source "~name") calls for always-current source

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 10:45:22 +00:00
465ce1abcb Page helpers in pure SX: 3 OCaml primitives + SX data + SX helpers
Add pretty-print, read-file, env-list-typed primitives to OCaml kernel.
Convert Python reference data (attrs, headers, events, primitives) to SX
data files. Implement page helpers (component-source, handler-source,
read-spec-file, reference-data, etc.) as pure SX functions.

The helper dispatcher in HTTP mode looks up named functions in the env
and calls them directly, replacing the Python IO bridge path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 07:42:59 +00:00
9ce8659f74 Fix signal-add-sub! losing subscribers after remove, fix build pipeline
signal-add-sub! used (append! subscribers f) which returns a new list
for immutable List but discards the result — after signal-remove-sub!
replaces the subscribers list via dict-set!, re-adding subscribers
silently fails. Counter island only worked once (0→1 then stuck).

Fix: use (dict-set! s "subscribers" (append ...)) to explicitly update
the dict field, matching signal-remove-sub!'s pattern.

Build pipeline fixes:
- sx-build-all.sh now bundles spec→dist and recompiles .sxbc bytecode
- compile-modules.js syncs .sx source files alongside .sxbc to wasm/sx/
- Per-file cache busting: wasm, platform JS, and sxbc each get own hash
- bundle.sh adds cssx.sx to dist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 07:36:36 +00:00
5b55b75a9a AJAX on main thread, fix double-push in click delegation
- AJAX requests (SX-Request: true) now render on main thread instead
  of queueing behind slow full-page renders in worker pool
- Remove pushState from click handler — handle-history does it after
  swap succeeds, preventing double-push that triggered popstate handler

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 18:06:22 +00:00
80931e4972 Fix JIT closure isolation, SX wire format, server diagnostics
Root cause: _env_bind_hook mirrored ALL env_bind calls (including
lambda parameter bindings) to the shared VM globals table. Factory
functions like make-page-fn that return closures capturing different
values for the same param names (default-name, prefix, suffix) would
have the last call's values overwrite all previous closures' captured
state in globals. OP_GLOBAL_GET reads globals first, so all closures
returned the last factory call's values.

Fix: only sync root-env bindings (parent=None) to VM globals. Lambda
parameter bindings stay in their local env, found via vm_closure_env
fallback in OP_GLOBAL_GET.

Also in this commit:
- OP_CLOSURE propagates parent vm_closure_env to child closures
- Remove JIT globals injection (closure vars found via env chain)
- sx_server.ml: SX-Request header → returns text/sx (aser only)
- sx_server.ml: diagnostic endpoint GET /sx/_debug/{env,eval,route}
- sx_server.ml: page helper stubs for deep page rendering
- sx_server.ml: skip client-libs/ dir (browser-only definitions)
- adapter-html.sx: unknown components → HTML comment (not error)
- sx-platform.js: .sxbc fallback loader for bytecode modules
- Delete sx_http.ml (standalone HTTP server, unused)
- Delete stale .sxbc.json files (arity=0 bug, replaced by .sxbc)
- 7 new closure isolation tests in test-closure-isolation.sx
- mcp_tree.ml: emit arity + upvalue-count in .sxbc.json output

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 17:28:47 +00:00
a24efc1a00 Bootstrap SX bytecode compiler to native OCaml
Transpile lib/compiler.sx → hosts/ocaml/lib/sx_compiler.ml (42 functions).
The bytecode compiler now runs as native OCaml instead of interpreted SX,
eliminating the 24s JIT warm-up for compiler functions.

- bootstrap_compiler.py: transpiler script (like bootstrap.py for evaluator)
- sx_compiler.ml: 39KB native compiler (compile, compile-module, etc.)
- Bind compile/compile-module as native functions in setup_core_operations
- Add mutable_list to sx_runtime.ml (used by compiler pool)
- Add native parse function (wraps Sx_parser.parse_all)
- compile-match delegated via ref (uses letrec, transpiler can't handle)
- Compile all 23 bytecode modules successfully (was 0/23 due to WASM overflow)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 10:30:53 +00:00
1985c648eb Native bytecode compiler: 8x faster, compile-blob command
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>
2026-03-30 09:49:28 +00:00
671d19c978 SX request handler + AJAX nav + shared JIT globals + shell cleanup
- 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>
2026-03-30 03:03:57 +00:00
6845ced98f Enable pre-warm cache + faster Playwright config
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>
2026-03-30 00:00:46 +00:00
501934f9c6 Skip pre-warm — start listening immediately, render on demand
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 22:49:08 +00:00
702074eaa9 Add _jit_warned check to prevent parse-loop infinite recompilation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 22:38:54 +00:00
07f5d03ac1 Restore OCaml to 5c8b05a + Docker native server entrypoint
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>
2026-03-29 22:35:40 +00:00
a38b5a9b44 Restore all OCaml + request-handler to working state (aa4c911)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 22:11:17 +00:00
85f72af74b Restore recursive cek_run, remove step counter/limit
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>
2026-03-29 21:29:36 +00:00