Fix JIT compiler, CSSX browser support, double-fetch, SPA layout

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>
This commit is contained in:
2026-03-30 20:48:43 +00:00
parent 5b55b75a9a
commit d81a518732
37 changed files with 688 additions and 405 deletions

View File

@@ -606,6 +606,7 @@ let rec handle_tool name args =
"render.sx"; "core-signals.sx"; "signals.sx"; "deps.sx"; "router.sx";
"page-helpers.sx"; "freeze.sx"; "bytecode.sx"; "compiler.sx"; "vm.sx";
"dom.sx"; "browser.sx"; "adapter-html.sx"; "adapter-sx.sx"; "adapter-dom.sx";
"cssx.sx";
"boot-helpers.sx"; "hypersx.sx"; "harness.sx"; "harness-reactive.sx";
"harness-web.sx"; "engine.sx"; "orchestration.sx"; "boot.sx";
] in
@@ -1225,18 +1226,41 @@ let rec handle_tool name args =
(try while true do lines := input_line ic :: !lines done with End_of_file -> ());
ignore (Unix.close_process_in ic);
let all_lines = List.rev !lines in
let fails = List.filter (fun l -> let t = String.trim l in
String.length t > 1 && (t.[0] = '\xE2' (**) || (String.length t > 4 && String.sub t 0 4 = "FAIL"))) all_lines in
(* Count passed/failed/skipped from the summary line *)
let summary = List.find_opt (fun l ->
try let _ = Str.search_forward (Str.regexp "passed\\|failed") l 0 in true
with Not_found -> false) (List.rev all_lines) in
let result = match summary with
| Some s ->
if fails = [] then s
else s ^ "\n\nFailures:\n" ^ String.concat "\n" fails
| None ->
let last_n = List.filteri (fun i _ -> i >= List.length all_lines - 10) all_lines in
String.concat "\n" last_n
(* Extract test names that failed *)
let fail_names = List.filter_map (fun l ->
let t = String.trim l in
if String.length t > 2 then
try
let _ = Str.search_forward (Str.regexp " .* ") t 0 in
Some (" " ^ t)
with Not_found -> None
else None) all_lines in
(* Extract error messages (lines starting with Error:) *)
let errors = List.filter_map (fun l ->
let t = String.trim l in
if String.length t > 6 then
try
let _ = Str.search_forward (Str.regexp "expect.*\\(received\\)\\|Expected\\|Received\\|Error:.*expect") t 0 in
Some (" " ^ t)
with Not_found -> None
else None) all_lines in
let total = List.length fail_names + (match summary with
| Some s -> (try let _ = Str.search_forward (Str.regexp "\\([0-9]+\\) passed") s 0 in
int_of_string (Str.matched_group 1 s) with _ -> 0)
| None -> 0) in
let summary_str = match summary with Some s -> String.trim s | None -> "no summary" in
let result =
if fail_names = [] then
Printf.sprintf "%s (%d total)" summary_str total
else
Printf.sprintf "%s (%d total)\n\nFailed:\n%s\n\nErrors:\n%s"
summary_str total
(String.concat "\n" fail_names)
(String.concat "\n" (List.filteri (fun i _ -> i < 10) errors))
in
text_result result
end else begin