Three fixes:
1. Framework: render-dom-lake preserves SSR elements during hydration.
When client-side render-to-dom encounters a lake with an existing
DOM element (from SSR), it reuses that element instead of creating
a new one. This prevents the SSR HTML from being replaced with
unresolvable raw SX expressions (~tw calls).
2. Stepper: skip rebuild-preview on initial hydration. Uses a non-
reactive dict flag (not a signal) to avoid triggering the effect
twice. On first run, just initializes the DOM stack from the
existing SSR content by computing open-element depth from step
types and walking lastElementChild.
3. Stepper: rebuild-preview computes correct DOM stack after re-render.
Same depth computation + DOM walk approach. This fixes the bug where
do-step after do-back would append elements to the wrong parent
(e.g. "sx" span outside h1).
Also: increased code view font-size from 0.5rem to 0.85rem.
Playwright tests:
- lake never shows raw SX during hydration (mutation observer)
- back 6 + forward 6 keeps all 4 spans inside h1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs fixed:
1. Hydration flash: The effect's schedule-idle called rebuild-preview
on initial hydration, which cleared the SSR HTML and re-rendered
(flashing raw SX source or blank). Fix: skip rebuild-preview on
initial render — the SSR content is already correct. Only rebuild
when stepping.
2. Stepping structure: After do-back calls rebuild-preview, the DOM
stack was reset to just [container]. Subsequent do-step calls
appended elements to the wrong parent (e.g. "sx" span outside h1).
Fix: compute the correct stack depth by replaying open/close step
counts, then walk the rendered DOM tree that many levels deep via
lastElementChild to find the actual DOM nodes.
Proven by harness test: compute-depth returns 3 at step 10 (inside
div > h1 > span), 2 at step 8 (inside div > h1), 0 at step 16 (all
closed).
Playwright test: lake never shows raw SX (~tw, :tokens) during
hydration — now passes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: default step-idx was 9, but the expression has 16 steps.
At step 9, only "the joy" + empty emerald span renders. Changed default
to 16 so all four words display after hydration.
Reverted mutable-list changes — (list) already creates ListRef in the
OCaml kernel, so append! works correctly with plain (list).
Added spec/tests/test-stepper.sx (7 tests) proving the split-tag +
steps-to-preview pipeline works correctly at each step boundary.
Updated Playwright stepper.spec.js with four tests:
- no raw SX visible after hydration
- default view shows all four words
- all spans inside h1
- stepping forward renders styled text
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
JIT compiler:
- Fix jit_compile_lambda: resolve `compile` via symbol lookup in env
instead of embedding VmClosure in AST (CEK dispatches differently)
- Register eval-defcomp/eval-defisland/eval-defmacro runtime helpers
in browser kernel for bytecoded defcomp forms
- Disable broken .sxbc.json path (missing arity in nested code blocks),
use .sxbc text format only
- Mark JIT-failed closures as sentinel to stop retrying
CSSX in browser:
- Add cssx.sx symlink + cssx.sxbc to browser web stack
- Add flush-cssx! to orchestration.sx post-swap for SPA nav
- Add cssx.sx to compile-modules.js and mcp_tree.ml bytecode lists
SPA navigation:
- Fix double-fetch: check e.defaultPrevented in click delegation
(bind-event already handled the click)
- Fix layout destruction: change nav links from outerHTML to innerHTML
swap (outerHTML destroyed #main-panel when response lacked it)
- Guard JS popstate handler when SX engine is booted
- Rename sx-platform.js → sx-platform-2.js to bust immutable cache
Playwright tests:
- Add trackErrors() helper to all test specs
- Add SPA DOM comparison test (SPA nav vs fresh load)
- Add single-fetch + no-duplicate-elements test
- Improve MCP tool output: show failure details and error messages
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
boot-init now sets data-sx-ready on <html> and dispatches an sx:ready
CustomEvent after all islands are hydrated. Playwright tests use this
instead of networkidle + hard-coded sleeps (50+ seconds eliminated).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The OCaml `apply` primitive only handled NativeFn, causing swap! to
fail in the WASM browser when called with lambda arguments. Extended
to handle all callable types via _sx_call_fn/_sx_trampoline_fn.
Also fixes:
- Pre-existing build errors from int-interned env.bindings migration
(vm-trace, bytecode-inspect, deps-check, prim-check in sx_server.ml)
- Add #portal-root div to page shell for portal island rendering
- Stepper test scoped to lake area (code-view legitimately shows ~cssx/tw)
- Portal test checks #portal-root instead of #sx-root
- Mark 3 known bugs as test.fixme (event-bridge, resource, isomorphic-nav)
98 passed, 3 skipped, 0 failed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- web/orchestration.sx, web/signals.sx: dom-listen → dom-on (trampoline
wrapper that resolves TCO thunks from Lambda event handlers)
- .gitea/: CI workflow and Dockerfile for automated test runs
- tests/playwright/stepper.spec.js: stepper widget smoke test
- Remove stale artdag .pyc file
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>