Phase 1: Create directory structure for spec/hosts/web/sx

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 02:09:29 +00:00
parent f8a8e1eeb0
commit 8ed8134d66
6 changed files with 774 additions and 0 deletions

View File

@@ -0,0 +1,333 @@
;; ---------------------------------------------------------------------------
;; Isolated Evaluator — Shared platform layer, isolated JS, Rust WASM
;; ---------------------------------------------------------------------------
(defcomp ~plans/isolated-evaluator/plan-isolated-evaluator-content ()
(~docs/page :title "Isolated Evaluator"
(~docs/section :title "Context" :id "context"
(p "The SX spec is already split into three layers:")
(ul :class "list-disc list-inside space-y-1 mt-2"
(li (code "spec/") " \u2014 Core language (19 files): evaluator, parser, primitives, CEK, types, continuations. Host-independent.")
(li (code "web/") " \u2014 Web framework (20 files): signals, adapters, engine, orchestration, boot, router, deps. Built on core spec.")
(li (code "sx/") " \u2014 Application (sx-docs website). Built on web framework."))
(p "Bootstrappers search " (code "spec/ \u2192 web/ \u2192 shared/sx/ref/") " for " (code ".sx") " files. The separation is clean.")
(p "This plan takes the next step: " (strong "isolate the evaluator from the real world") ". The JS evaluator should run in the same sandbox as Rust/WASM \u2014 unable to touch DOM, fetch, timers, or storage directly. Both evaluators call into a shared " (code "sx-platform.js") " for all browser access.")
(p "This also involves sorting out the JavaScript: eliminating hand-coded JS that duplicates specced " (code ".sx") " logic, and moving web framework " (code ".sx") " from compiled-into-evaluator to runtime-evaluated."))
(~docs/section :title "Existing Architecture" :id "existing"
(h4 :class "font-semibold mt-4 mb-2" "Three-layer spec split (DONE)")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "Layer")
(th :class "px-3 py-2 font-medium text-stone-600" "Directory")
(th :class "px-3 py-2 font-medium text-stone-600" "Files")
(th :class "px-3 py-2 font-medium text-stone-600" "Content")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Core spec")
(td :class "px-3 py-2 text-stone-700" (code "spec/"))
(td :class "px-3 py-2 text-stone-700" "19")
(td :class "px-3 py-2 text-stone-600" "eval, parser, primitives, render, types, CEK, continuations, boundary-core"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Web framework")
(td :class "px-3 py-2 text-stone-700" (code "web/"))
(td :class "px-3 py-2 text-stone-700" "20")
(td :class "px-3 py-2 text-stone-600" "adapters, signals, engine, orchestration, boot, router, deps, forms, boundary-web"))
(tr
(td :class "px-3 py-2 text-stone-700" "Application")
(td :class "px-3 py-2 text-stone-700" (code "sx/"))
(td :class "px-3 py-2 text-stone-700" "\u2014")
(td :class "px-3 py-2 text-stone-600" "sx-docs website, page components, content")))))
(h4 :class "font-semibold mt-4 mb-2" "Rust/WASM evaluator (DONE)")
(p (code "sx-rust/") " has a working parser + evaluator + render-to-html in WASM: 9,823 lines generated Rust, 75 real primitives, 154 stubs, 92 tests passing. Currently pure computation \u2014 no DOM interaction.")
(h4 :class "font-semibold mt-4 mb-2" "What needs to change")
(p "Currently " (strong "everything") " (core + web framework) gets compiled into one monolithic " (code "sx-browser.js") ". The web framework " (code ".sx") " files (signals, engine, orchestration, boot, etc.) are baked into the evaluator output by the bootstrapper. They should instead be " (strong "evaluated at runtime") " by the core evaluator, like any other " (code ".sx") " code.")
(p "The JavaScript platform primitives (DOM, fetch, timers, storage) are also inlined into the bootstrapped output. They need to be extracted into a standalone " (code "sx-platform.js") " module that both JS and WASM evaluators share."))
;; -----------------------------------------------------------------------
;; What's core vs web (bootstrapped vs runtime)
;; -----------------------------------------------------------------------
(~docs/section :title "Bootstrapped vs Runtime-Evaluated" :id "bootstrap-vs-runtime"
(p "The key question: what MUST be compiled into the evaluator vs what can be loaded as " (code ".sx") " at runtime?")
(h4 :class "font-semibold mt-4 mb-2" "Must be bootstrapped (core spec)")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "File")
(th :class "px-3 py-2 font-medium text-stone-600" "Dir")
(th :class "px-3 py-2 font-medium text-stone-600" "Why")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "eval.sx"))
(td :class "px-3 py-2 text-stone-700" "spec/")
(td :class "px-3 py-2 text-stone-600" "IS the language \u2014 can't evaluate without it"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "parser.sx"))
(td :class "px-3 py-2 text-stone-700" "spec/")
(td :class "px-3 py-2 text-stone-600" "Can't read .sx source without a parser"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "primitives.sx"))
(td :class "px-3 py-2 text-stone-700" "spec/")
(td :class "px-3 py-2 text-stone-600" "80+ built-in pure functions \u2014 must be native"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "render.sx"))
(td :class "px-3 py-2 text-stone-700" "spec/")
(td :class "px-3 py-2 text-stone-600" "HTML_TAGS registry, parse-element-args"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "adapter-html.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "render-to-html \u2014 co-recursive with eval-expr"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "adapter-sx.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "aser (wire format) \u2014 co-recursive with eval-expr"))
(tr
(td :class "px-3 py-2 text-stone-700" (code "adapter-dom.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "render-to-dom \u2014 co-recursive with eval-expr")))))
(h4 :class "font-semibold mt-4 mb-2" "Runtime-evaluated (web framework)")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "File")
(th :class "px-3 py-2 font-medium text-stone-600" "Dir")
(th :class "px-3 py-2 font-medium text-stone-600" "Why it can be runtime")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "signals.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "Pure computation \u2014 dicts with markers, no new types"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "engine.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "Pure logic \u2014 trigger parsing, swap specs, morph"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "orchestration.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "Event binding + fetch \u2014 calls platform primitives"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "boot.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "Page lifecycle \u2014 calls platform primitives"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "router.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "URL pattern matching \u2014 pure computation"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "deps.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "Component dependency analysis \u2014 pure AST walking"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "page-helpers.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "Data transformation helpers"))
(tr
(td :class "px-3 py-2 text-stone-700" (code "forms.sx"))
(td :class "px-3 py-2 text-stone-700" "web/")
(td :class "px-3 py-2 text-stone-600" "Server-only definition forms")))))
(h4 :class "font-semibold mt-4 mb-2" "adapter-dom.sx reactive split")
(p (code "adapter-dom.sx") " contains reactive-aware code (" (code "reactive-text") ", " (code "reactive-attr") ", " (code "render-dom-island") ", " (code "render-dom-lake") ") interleaved with core DOM rendering. These call " (code "signal?") " and " (code "deref") " from " (code "signals.sx") " via environment lookup \u2014 no compile-time dependency. Option: split reactive DOM functions into " (code "adapter-dom-reactive.sx") " (web/ layer), keeping base " (code "adapter-dom.sx") " purely about elements/text/fragments/components.")
(h4 :class "font-semibold mt-4 mb-2" "Hand-coded JS to clean up")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li (code "CONTINUATIONS_JS") " in " (code "platform_js.py") " \u2014 hand-coded shift/reset. Should use specced " (code "continuations.sx") " or be eliminated if continuations are application-level.")
(li (code "ASYNC_IO_JS") " in " (code "platform_js.py") " \u2014 hand-coded async rendering dispatch. Already replaced by " (code "adapter-async.sx") " for Python. JS version should also be bootstrapped or eliminated.")
(li "Various wrapper functions in " (code "PLATFORM_BOOT_JS") " that duplicate logic from " (code "boot.sx") ".")))
;; -----------------------------------------------------------------------
;; Phase 1: Extract sx-platform.js
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 1: Extract sx-platform.js" :id "phase-1"
(p (strong "Goal:") " All real-world-touching JavaScript lives in one standalone module. The evaluator never directly accesses " (code "document") ", " (code "window") ", " (code "fetch") ", " (code "localStorage") ", " (code "history") ", etc.")
(h4 :class "font-semibold mt-4 mb-2" "Architecture")
(~docs/code :code (highlight " \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 sx-platform.js \u2502 \u2190 DOM, fetch, timers, storage\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 \u2502\n \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n \u2502 sx-evaluator.js \u2502 \u2502 sx-wasm-shim.js \u2502\n \u2502 (isolated JS) \u2502 \u2502 (WASM instance \u2502\n \u2502 \u2502 \u2502 + handle table) \u2502\n \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518" "text"))
(h4 :class "font-semibold mt-4 mb-2" "What moves into sx-platform.js")
(p "Extracted from " (code "platform_js.py") " string constants:")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "Category")
(th :class "px-3 py-2 font-medium text-stone-600" "Source")
(th :class "px-3 py-2 font-medium text-stone-600" "~Functions")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "DOM primitives")
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_DOM_JS"))
(td :class "px-3 py-2 text-stone-600" "~50 (createElement, setAttribute, appendChild...)"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Engine platform")
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_ENGINE_PURE_JS"))
(td :class "px-3 py-2 text-stone-600" "~6 (locationHref, pushState, nowMs...)"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Orchestration platform")
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_ORCHESTRATION_JS"))
(td :class "px-3 py-2 text-stone-600" "~80 (fetch, abort, timers, SSE, scroll, media...)"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Boot platform")
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_BOOT_JS"))
(td :class "px-3 py-2 text-stone-600" "~20 (mount target, localStorage, cookies, logging...)"))
(tr
(td :class "px-3 py-2 text-stone-700" "Parser helpers")
(td :class "px-3 py-2 text-stone-700" (code "PLATFORM_PARSER_JS"))
(td :class "px-3 py-2 text-stone-600" "~4 (isIdentStart, parseNumber...)")))))
(h4 :class "font-semibold mt-4 mb-2" "Isolation rule")
(p "After extraction, searching " (code "sx-evaluator.js") " for " (code "document") ", " (code "window") ", " (code "fetch") ", " (code "localStorage") ", " (code "history") ", " (code "setTimeout") ", " (code "console") " should find " (strong "zero") " direct references.")
(h4 :class "font-semibold mt-4 mb-2" "The callSxFunction bridge")
(p "Platform code (event listeners, timers) needs to invoke SX lambdas. The evaluator provides a single " (code "callSxFunction(fn, args) \u2192 result") " bridge to the platform at registration time. This is the ONE evaluator-to-platform callback.")
(h4 :class "font-semibold mt-4 mb-2" "Implementation")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li "Modify " (code "platform_js.py") " to emit platform functions as a separate output section")
(li "Create " (code "sx-platform.js") " as an IIFE that sets " (code "globalThis.SxPlatform = {...}"))
(li "The evaluator IIFE reads " (code "globalThis.SxPlatform") " at init, registers each function as a PRIMITIVE")
(li "Clean up " (code "CONTINUATIONS_JS") " and " (code "ASYNC_IO_JS") " \u2014 eliminate or bootstrap")
(li "Test that existing pages work identically")))
;; -----------------------------------------------------------------------
;; Phase 2: Isolate the JS Evaluator
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 2: Isolate the JS Evaluator" :id "phase-2"
(p (strong "Goal:") " " (code "sx-evaluator.js") " contains ONLY core spec + render adapters. Web framework " (code ".sx") " is evaluated at runtime.")
(h4 :class "font-semibold mt-4 mb-2" "Core-only build")
(p "The bootstrapper already supports selecting which modules to compile. A core-only build:")
(~docs/code :code (highlight "# In run_js_sx.py \u2014 core-only build\ncompile_ref_to_js(\n adapters=[\"parser\", \"html\", \"sx\", \"dom\"], # core spec + adapters\n modules=None, # no signals, engine, orchestration, boot\n extensions=None, # no continuations\n spec_modules=None # no deps, router, cek, frames, page-helpers\n)" "python"))
(h4 :class "font-semibold mt-4 mb-2" "Web framework loading")
(p "Web framework " (code ".sx") " files ship as " (code "<script type=\"text/sx-lib\">") " blocks. The platform boot shim evaluates them before component scripts:")
(~docs/code :code (highlight "<script src=\"/static/scripts/sx-platform.js\"></script>\n<script src=\"/static/scripts/sx-evaluator.js\"></script>\n<script type=\"text/sx-lib\">\n ;; concatenated web/ framework: signals, deps, router,\n ;; engine, orchestration, boot\n</script>\n<script type=\"text/sx\" data-components>\n ;; page component definitions\n</script>" "html"))
(h4 :class "font-semibold mt-4 mb-2" "Boot chicken-and-egg")
(p (code "boot.sx") " orchestrates the boot sequence but is itself web framework code. Solution: thin native boot shim (~30 lines) in " (code "sx-platform.js") ":")
(~docs/code :code (highlight "SxPlatform.boot = function(evaluator) {\n // 1. Evaluate web framework .sx libraries\n var libs = document.querySelectorAll('script[type=\"text/sx-lib\"]');\n for (var i = 0; i < libs.length; i++) {\n evaluator.evalSource(libs[i].textContent);\n }\n // 2. Call boot-init (defined in boot.sx)\n evaluator.callFunction('boot-init');\n};" "javascript"))
(h4 :class "font-semibold mt-4 mb-2" "Performance")
(p "Parsing + evaluating ~5,000 lines of web framework " (code ".sx") " at startup takes ~10\u201350ms. After " (code "define") ", functions are Lambda objects dispatched identically to compiled functions. " (strong "Zero ongoing performance difference.")))
;; -----------------------------------------------------------------------
;; Phase 3: Wire Up Rust/WASM
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 3: Wire Up Rust/WASM" :id "phase-3"
(p (strong "Goal:") " Rust evaluator calls " (code "sx-platform.js") " via wasm-bindgen imports. Handle table bridges DOM references.")
(h4 :class "font-semibold mt-4 mb-2" "Handle table (JS-side)")
(~docs/code :code (highlight "// In sx-wasm-shim.js\nconst handles = [null]; // index 0 = null handle\nfunction allocHandle(obj) { handles.push(obj); return handles.length - 1; }\nfunction getHandle(id) { return handles[id]; }\nfunction freeHandle(id) { handles[id] = null; }" "javascript"))
(p "DOM nodes are JS objects. The handle table maps " (code "u32") " IDs to JS objects. Rust stores " (code "Value::Handle(u32)") " and passes the " (code "u32") " to imported JS functions.")
(h4 :class "font-semibold mt-4 mb-2" "Value::Handle in Rust")
(~docs/code :code (highlight "// In platform.rs\npub enum Value {\n // ... existing variants ...\n Handle(u32), // opaque reference to JS-side object\n}" "rust"))
(h4 :class "font-semibold mt-4 mb-2" "WASM imports from platform")
(~docs/code :code (highlight "#[wasm_bindgen(module = \"/sx-platform-wasm.js\")]\nextern \"C\" {\n fn platform_create_element(tag: &str) -> u32;\n fn platform_create_text_node(text: &str) -> u32;\n fn platform_set_attr(handle: u32, name: &str, value: &str);\n fn platform_append_child(parent: u32, child: u32);\n fn platform_add_event_listener(handle: u32, event: &str, callback_id: u32);\n // ... ~50 DOM primitives\n}" "rust"))
(h4 :class "font-semibold mt-4 mb-2" "Callback table for events")
(p "When Rust creates an event handler (a Lambda), it stores it in a callback table and gets a " (code "u32") " ID. JS " (code "addEventListener") " wraps it: when the event fires, JS calls into WASM with the callback ID. Rust looks up the Lambda and evaluates it.")
(h4 :class "font-semibold mt-4 mb-2" "sx-wasm-shim.js")
(p "Thin glue (~100 lines):")
(ul :class "list-disc list-inside space-y-1 mt-2"
(li "Instantiate WASM module")
(li "Wire handle table")
(li "Delegate all platform calls to " (code "sx-platform.js"))
(li "Provide " (code "invoke_callback") " \u2192 Rust for event dispatch")))
;; -----------------------------------------------------------------------
;; Phase 4: Web Framework Loading
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 4: Web Framework Loading" :id "phase-4"
(p (strong "Goal:") " Both JS and WASM evaluators load the same web framework " (code ".sx") " files at runtime.")
(h4 :class "font-semibold mt-4 mb-2" "Boot sequence (identical for both evaluators)")
(ol :class "list-decimal list-inside space-y-2 mt-2"
(li "Load " (code "sx-platform.js") " + evaluator (" (code "sx-evaluator.js") " or " (code "sx-wasm-shim.js") ")")
(li "Platform registers primitives with evaluator")
(li "Platform boot shim evaluates " (code "<script type=\"text/sx-lib\">") " blocks")
(li "Dependency order: signals \u2192 deps \u2192 frames \u2192 router \u2192 page-helpers \u2192 engine \u2192 orchestration \u2192 boot")
(li (code "boot-init") " called \u2014 processes component scripts, hydrates, initializes engine")
(li "Page is interactive"))
(h4 :class "font-semibold mt-4 mb-2" "Library dependency order")
(~docs/code :code (highlight "signals.sx \u2190 no SX deps (uses only core primitives)\ndeps.sx \u2190 no SX deps\nframes.sx \u2190 no SX deps\nrouter.sx \u2190 no SX deps\npage-helpers.sx \u2190 no SX deps\nengine.sx \u2190 uses render.sx (core), adapter-dom.sx (core)\norchestration.sx \u2190 depends on engine.sx\nboot.sx \u2190 depends on orchestration.sx" "text")))
;; -----------------------------------------------------------------------
;; Phase 5: Verification + Rollout
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 5: Verification + Rollout" :id "phase-5"
(h4 :class "font-semibold mt-4 mb-2" "Shadow comparison")
(p "Run both JS and WASM evaluators on same input, compare outputs:")
(ol :class "list-decimal list-inside space-y-1 mt-2"
(li (strong "Parse") " \u2014 same AST (already testable with current WASM exports)")
(li (strong "Eval") " \u2014 same values (already testable)")
(li (strong "Render") " \u2014 same DOM structure (requires Phase 3)"))
(h4 :class "font-semibold mt-4 mb-2" "Feature flag")
(p "Server sets " (code "data-sx-runtime=\"wasm\"") " or " (code "\"js\"") " on root element. Boot shim loads appropriate evaluator. Progressive enhancement: try WASM, fall back to JS.")
(h4 :class "font-semibold mt-4 mb-2" "Test matrix")
(p "All " (code "test-*.sx") " files from both " (code "spec/") " and " (code "web/") " run on BOTH evaluators."))
;; -----------------------------------------------------------------------
;; Principles
;; -----------------------------------------------------------------------
(~docs/section :title "Principles" :id "principles"
(ul :class "list-disc list-inside space-y-2"
(li (strong "Core is what defines the language.") " Parser, evaluator, primitives, render modes. If you can't run SX without it, it's core (" (code "spec/") "). If you can write it " (em "in") " SX, it's web framework (" (code "web/") ").")
(li (strong "Web framework runs ON the evaluator.") " Signals, engine, orchestration, boot, router, deps \u2014 these are SX programs. They're evaluated at runtime, not compiled into the evaluator.")
(li (strong "Isolation is the boundary.") " The evaluator can't touch the real world. Platform primitives are the only bridge. JS and WASM evaluators have identical isolation.")
(li (strong "Shared platform, not duplicated.") " One implementation of every browser primitive in " (code "sx-platform.js") ". Both evaluators use it. Fix a bug once, both get the fix.")
(li (strong "Handle table is the WASM boundary.") " Rust holds " (code "u32") " IDs. JavaScript holds real objects. Swap the handle table for a different host and the Rust code doesn't change.")
(li (strong "Progressive, not all-or-nothing.") " WASM is an enhancement. JS remains the fallback. Feature-flagged per page. Gradual and reversible.")))
;; -----------------------------------------------------------------------
;; Interactions
;; -----------------------------------------------------------------------
(~docs/section :title "Interaction with Other Plans" :id "interactions"
(ul :class "list-disc list-inside space-y-2"
(li (strong (a :href "/sx/(etc.(plan.rust-wasm-host))" "Rust/WASM Host")) " \u2014 this plan supersedes it. That plan didn't distinguish core from web framework or propose evaluator isolation. The shared platform layer is the key architectural difference.")
(li (strong (a :href "/sx/(etc.(plan.wasm-bytecode-vm))" "WASM Bytecode VM")) " \u2014 complementary. This plan bootstraps the tree-walking evaluator. A future bytecode VM compiles SX to bytecodes. The platform layer and handle table are shared.")
(li (strong (a :href "/sx/(etc.(plan.runtime-slicing))" "Runtime Slicing")) " \u2014 simplified. With web framework as runtime-evaluated " (code ".sx") ", slicing becomes: ship core + only the framework files you need. L0 pages skip signals/engine entirely.")
(li (strong (a :href "/sx/(etc.(plan.reactive-runtime))" "Reactive Runtime")) " \u2014 signals are web framework code, confirming they layer on top of core without special treatment.")
(li (strong (a :href "/sx/(etc.(plan.foundations))" "Foundations")) " \u2014 the core/web split is the same principle: small kernel, everything else built on top.")))
;; -----------------------------------------------------------------------
;; Outcome
;; -----------------------------------------------------------------------
(~docs/section :title "Outcome" :id "outcome"
(p "After completion:")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li "Core evaluator is ~3,200 lines of bootstrapped spec (" (code "spec/") " + adapters from " (code "web/") ").")
(li "JS evaluator is isolated \u2014 can't touch the real world, same sandbox as WASM.")
(li "Shared " (code "sx-platform.js") " provides all browser primitives to both evaluators.")
(li "Rust/WASM evaluator runs in the browser with full DOM rendering via handle table.")
(li "Web framework " (code ".sx") " files (signals, engine, orchestration, boot) are runtime-evaluated by whichever evaluator is active.")
(li "Hand-coded JS (" (code "CONTINUATIONS_JS") ", " (code "ASYNC_IO_JS") ") eliminated or bootstrapped from spec.")
(li "The architecture proof is complete: one spec, isolated evaluator, shared platform, deployment-time target selection.")))))

View File

@@ -0,0 +1,263 @@
;; ---------------------------------------------------------------------------
;; Rust/WASM Host — Bootstrap the SX spec to Rust, compile to WASM
;; ---------------------------------------------------------------------------
(defcomp ~plans/rust-wasm-host/plan-rust-wasm-host-content ()
(~docs/page :title "Rust/WASM Host"
(~docs/section :title "Context" :id "context"
(p "The SX host architecture says: spec it in " (code ".sx") ", bootstrap to every target. We've now done it for Rust.")
(p "The Rust bootstrapper (" (code "bootstrap_rs.py") ") reads all 20 " (code ".sx") " spec files and emits a complete Rust crate — " (strong "9,781 lines") " of Rust that compiles with zero errors. The test suite has " (strong "92 tests passing") " across parser, evaluator, primitives, and rendering.")
(p "This is distinct from the " (a :href "/sx/(etc.(plan.wasm-bytecode-vm))" "WASM Bytecode VM") " plan. That plan designs a custom bytecode format and VM. This plan bootstraps the " (strong "same tree-walking evaluator") " directly to Rust/WASM from the same " (code ".sx") " specs — no new bytecode, no new VM, just another host.")
(h4 :class "font-semibold mt-4 mb-2" "What exists today")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "Artifact")
(th :class "px-3 py-2 font-medium text-stone-600" "Lines")
(th :class "px-3 py-2 font-medium text-stone-600" "Status")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "bootstrap_rs.py"))
(td :class "px-3 py-2 text-stone-700" "—")
(td :class "px-3 py-2 text-stone-600" "Rust bootstrapper, reads all 20 .sx spec files"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "sx_ref.rs"))
(td :class "px-3 py-2 text-stone-700" "9,781")
(td :class "px-3 py-2 text-stone-600" "Generated Rust — compiles clean, 0 errors"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "platform.rs"))
(td :class "px-3 py-2 text-stone-700" "—")
(td :class "px-3 py-2 text-stone-600" "Rust platform interface (type constructors, env ops)"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" (code "test_parser.rs"))
(td :class "px-3 py-2 text-stone-700" "—")
(td :class "px-3 py-2 text-stone-600" "92 tests passing (parser, eval, primitives, render)"))
(tr
(td :class "px-3 py-2 text-stone-700" "Primitives")
(td :class "px-3 py-2 text-stone-700" "—")
(td :class "px-3 py-2 text-stone-600" "75 real implementations, 154 stubs"))))))
;; -----------------------------------------------------------------------
;; Architecture
;; -----------------------------------------------------------------------
(~docs/section :title "Architecture" :id "architecture"
(p "The key architectural insight: " (strong "factor the browser primitives into a shared platform layer") " that both the JS evaluator and the WASM module consume.")
(h4 :class "font-semibold mt-4 mb-2" "Shared platform layer")
(p (code "platform_js.py") " already contains all DOM, browser, fetch, timer, and storage implementations — bootstrapped from " (code "boundary.sx") ". These are pure JavaScript functions that call browser APIs. They don't depend on the evaluator.")
(p "Extract them into a standalone " (code "sx-platform.js") " module. Both " (code "sx-browser.js") " (the current JS evaluator) and the new " (code "sx-wasm-shim.js") " import from the same platform module:")
(~docs/code :code (highlight " ┌─────────────────┐\n │ sx-platform.js │ ← DOM, fetch, timers, storage\n └────────┬────────┘\n │\n ┌──────────────┼──────────────┐\n │ │\n ┌─────────┴─────────┐ ┌────────┴────────┐\n │ sx-browser.js │ │ sx-wasm-shim.js │\n │ (JS tree-walker) │ │ (WASM instance │\n │ │ │ + handle table) │\n └────────────────────┘ └─────────────────┘" "text"))
(p "One codebase for all browser primitives. Bug fixes apply to both targets. The evaluator is the only thing that changes — JS tree-walker vs Rust/WASM tree-walker.")
(h4 :class "font-semibold mt-4 mb-2" "Opaque handle table")
(p "Rust/WASM can't hold DOM node references directly. Instead, Rust values use " (code "Value::Handle(u32)") " — an opaque integer that indexes into a JavaScript-side handle table:")
(~docs/code :code (highlight "// JS side (in sx-wasm-shim.js)\nconst handles = []; // handle_id → DOM node\n\nfunction allocHandle(node) {\n const id = handles.length;\n handles.push(node);\n return id;\n}\n\nfunction getHandle(id) { return handles[id]; }\nfunction freeHandle(id) { handles[id] = null; }" "javascript"))
(~docs/code :code (highlight "// Rust side\n#[derive(Clone, Debug)]\nenum Value {\n Nil,\n Bool(bool),\n Number(f64),\n Str(String),\n Symbol(String),\n Keyword(String),\n List(Vec<Value>),\n Dict(Vec<(Value, Value)>),\n Lambda(Rc<Closure>),\n Handle(u32), // ← opaque DOM node reference\n}" "rust"))
(p "When Rust calls a DOM primitive (e.g. " (code "createElement") "), it gets back a " (code "Handle(id)") ". When it passes that handle to " (code "appendChild") ", the JS shim looks up the real node. Rust never sees a DOM node — only integer IDs.")
(h4 :class "font-semibold mt-4 mb-2" "The JS shim is thin")
(p "The WASM shim's job is minimal:")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li "Instantiate the WASM module")
(li "Wire up the handle table")
(li "Delegate all browser primitives to " (code "sx-platform.js"))
(li "Provide " (code "#[wasm_bindgen]") " imports for the DOM primitives that Rust calls"))
(p "Everything complex — event dispatch, morph engine, routing, SSE — lives in the shared platform layer. The shim is glue."))
;; -----------------------------------------------------------------------
;; Stubs breakdown
;; -----------------------------------------------------------------------
(~docs/section :title "Current Stub Breakdown" :id "stubs"
(p "Of the 154 stubbed primitives, most fall into a few categories that map directly to implementation phases:")
(div :class "overflow-x-auto rounded border border-stone-200 mb-4"
(table :class "w-full text-left text-sm"
(thead (tr :class "border-b border-stone-200 bg-stone-100"
(th :class "px-3 py-2 font-medium text-stone-600" "Category")
(th :class "px-3 py-2 font-medium text-stone-600" "Count")
(th :class "px-3 py-2 font-medium text-stone-600" "Examples")
(th :class "px-3 py-2 font-medium text-stone-600" "Phase")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "DOM creation & attrs")
(td :class "px-3 py-2 text-stone-700" "~30")
(td :class "px-3 py-2 text-stone-600" "createElement, setAttribute, appendChild")
(td :class "px-3 py-2 text-stone-600" "Phase 2"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Events & callbacks")
(td :class "px-3 py-2 text-stone-700" "~20")
(td :class "px-3 py-2 text-stone-600" "addEventListener, setTimeout, requestAnimationFrame")
(td :class "px-3 py-2 text-stone-600" "Phase 3"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Fetch & network")
(td :class "px-3 py-2 text-stone-700" "~15")
(td :class "px-3 py-2 text-stone-600" "fetch, XMLHttpRequest, SSE")
(td :class "px-3 py-2 text-stone-600" "Phase 3"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Browser APIs")
(td :class "px-3 py-2 text-stone-700" "~25")
(td :class "px-3 py-2 text-stone-600" "history, location, localStorage, console")
(td :class "px-3 py-2 text-stone-600" "Phase 5"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Morph engine")
(td :class "px-3 py-2 text-stone-700" "~15")
(td :class "px-3 py-2 text-stone-600" "morph, sync-attrs, reconcile")
(td :class "px-3 py-2 text-stone-600" "Phase 3"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Signals & reactivity")
(td :class "px-3 py-2 text-stone-700" "~20")
(td :class "px-3 py-2 text-stone-600" "signal, deref, effect, computed, batch")
(td :class "px-3 py-2 text-stone-600" "Phase 6"))
(tr
(td :class "px-3 py-2 text-stone-700" "Component lifecycle")
(td :class "px-3 py-2 text-stone-700" "~29")
(td :class "px-3 py-2 text-stone-600" "boot, hydrate, register-component")
(td :class "px-3 py-2 text-stone-600" "Phase 4"))))))
;; -----------------------------------------------------------------------
;; Phase 1: WASM build + parse/eval
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 1: WASM Build + Parse/Eval" :id "phase-1"
(p "Get the existing Rust code compiling to WASM and running parse/eval in the browser.")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li "Add " (code "wasm-bindgen") " and " (code "wasm-pack") " to the Rust crate")
(li "Export " (code "#[wasm_bindgen]") " functions: " (code "parse(src) -> JsValue") " and " (code "eval(src) -> JsValue"))
(li "All 75 real primitives work — arithmetic, string ops, list ops, dict ops, comparisons")
(li "Test page: load WASM module, parse SX source, eval expressions, display results")
(li "Benchmark: parse/eval speed vs JS evaluator on the same expressions"))
(p (strong "Milestone:") " SX expressions evaluate identically in JS and WASM. No DOM, no rendering — just computation."))
;; -----------------------------------------------------------------------
;; Phase 2: DOM rendering via handle table
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 2: DOM Rendering via Handle Table" :id "phase-2"
(p "Implement the shared platform layer and handle table. Rust can create and manipulate DOM nodes.")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li "Extract " (code "sx-platform.js") " from " (code "platform_js.py") " output — all DOM primitives as standalone functions")
(li "Implement " (code "sx-wasm-shim.js") " — WASM instantiation + handle table + platform imports")
(li "Wire " (code "Value::Handle(u32)") " through the Rust evaluator for DOM node references")
(li "Implement ~30 DOM stubs: " (code "createElement") ", " (code "setAttribute") ", " (code "appendChild") ", " (code "createTextNode") ", " (code "removeChild"))
(li (code "render-to-dom") " works through WASM — evaluates component tree, produces real DOM nodes via handle table"))
(p (strong "Milestone:") " A component renders to identical DOM whether evaluated by JS or WASM."))
;; -----------------------------------------------------------------------
;; Phase 3: Events + fetch + morph
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 3: Events + Fetch + Morph" :id "phase-3"
(p "The hardest phase — callbacks cross the WASM/JS boundary. Event handlers are Rust closures that JS must be able to invoke.")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li (strong "Callback table") " — mirror of the handle table but for functions. Rust registers a closure, gets back an ID. JS calls the ID when the event fires. Rust looks up and invokes the closure.")
(li (strong "Event listeners") " — " (code "addEventListener") " stores the callback ID on the element handle. JS dispatches events to the WASM callback table.")
(li (strong "Fetch") " — Rust initiates fetch via JS import, JS calls " (code "sx-platform.js") " " (code "fetch") ", returns result to Rust via callback.")
(li (strong "Morph engine") " — DOM diffing/patching runs in Rust. Morph calls produce a sequence of DOM mutations via handle operations. " (code "sync-attrs") " and " (code "morph-children") " work through the handle table."))
(p (strong "Milestone:") " Interactive pages work — click handlers, form submissions, HTMX-style requests, morph updates. This is the " (strong "MVP") " — a page could ship with the WASM evaluator and function correctly."))
;; -----------------------------------------------------------------------
;; Phase 4: Boot + hydration + components
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 4: Boot + Hydration + Components" :id "phase-4"
(p "Full page lifecycle — the WASM module replaces " (code "sx-browser.js") " for a complete page.")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li (strong "Boot sequence") " — WASM module loads, reads " (code "data-components") " script tags, registers component definitions, processes " (code "data-sx-page") " content")
(li (strong "Component registration") " — " (code "defcomp") " and " (code "defisland") " evaluated in Rust, stored in the WASM-side environment")
(li (strong "Hydration") " — server-rendered HTML matched against component tree, event handlers attached, islands activated")
(li (strong "CSSX") " — style computation runs in Rust (all CSSX primitives are already in the 75 real implementations)"))
(p (strong "Milestone:") " A full SX page boots and hydrates under WASM. Component definitions, CSSX styles, page content all evaluated by Rust."))
;; -----------------------------------------------------------------------
;; Phase 5: Routing + streaming + SSE
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 5: Routing + Streaming + SSE" :id "phase-5"
(p "Client-side navigation and real-time updates.")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li (strong "Client routing") " — " (code "navigate-to") ", " (code "popstate") " handling, URL matching from " (code "defpage") " routes")
(li (strong "History API") " — " (code "pushState") " / " (code "replaceState") " via JS imports")
(li (strong "SSE") " — server-sent events for live updates, morph on incoming HTML/SX")
(li (strong "Streaming responses") " — progressive rendering of large page content"))
(p (strong "Milestone:") " Full SPA navigation works under WASM. Pages load, navigate, and receive live updates."))
;; -----------------------------------------------------------------------
;; Phase 6: Signals + reactive islands
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 6: Signals + Reactive Islands" :id "phase-6"
(p "The most architecturally interesting phase — closures-as-values must work across the WASM boundary.")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li (strong "Signal primitives") " — " (code "signal") ", " (code "deref") ", " (code "reset!") ", " (code "swap!") ", " (code "computed") ", " (code "effect") ", " (code "batch") " all implemented in Rust")
(li (strong "Reactive DOM updates") " — signal changes trigger DOM mutations via handle table operations. No full re-render — fine-grained updates.")
(li (strong "Island scoping") " — " (code "with-island-scope") " manages signal lifecycle. Dispose island = drop all signals, effects, and handles.")
(li (strong "Computed chains") " — dependency graph tracks which signals feed which computed values. Topological update order."))
(p "The closure challenge: " (code "effect") " and " (code "computed") " take closures that capture reactive context. In Rust, these are " (code "Rc<dyn Fn(...)>") " values that the signal graph holds. The " (code "with-island-scope") " arena pattern handles cleanup — drop the arena, drop all closures.")
(p (strong "Milestone:") " Reactive islands work under WASM. Signals, computed values, effects, fine-grained DOM updates — all in Rust."))
;; -----------------------------------------------------------------------
;; Phase 7: Full parity + gradual rollout
;; -----------------------------------------------------------------------
(~docs/section :title "Phase 7: Full Parity + Gradual Rollout" :id "phase-7"
(p "Shadow-compare and feature-flagged rollout.")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li (strong "Shadow compare") " — run both JS and WASM evaluators in parallel on every page render. Assert identical DOM output. Log divergences. Same principle as async eval convergence.")
(li (strong "Feature flag") " — server sets " (code "data-sx-runtime=\"wasm\"") " or " (code "\"js\"") " on the page. Boot script loads the corresponding evaluator. Flag can be per-page, per-user, or global.")
(li (strong "Progressive enhancement") " — try WASM first, fall back to JS if WASM instantiation fails. Ship both " (code "sx-browser.js") " and " (code "sx-wasm.wasm") ".")
(li (strong "Gradual rollout") " — start with simple pages (documentation, static content). Move to interactive pages. Finally, reactive islands. Each phase validates correctness before advancing."))
(p (strong "Milestone:") " Full parity. Any SX page renders identically under JS or WASM. The runtime is a deployment choice, not an architectural one."))
;; -----------------------------------------------------------------------
;; Shared platform layer
;; -----------------------------------------------------------------------
(~docs/section :title "Shared Platform Layer" :id "shared-platform"
(p "The architectural heart of this plan. " (code "platform_js.py") " already generates all browser primitive implementations — DOM manipulation, fetch wrappers, timer management, storage access, history API, SSE handling. Currently these are inlined into " (code "sx-browser.js") ".")
(p "The extraction:")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li (strong "Step 1") " — refactor " (code "platform_js.py") " to emit a standalone " (code "sx-platform.js") " module (ES module exports)")
(li (strong "Step 2") " — " (code "sx-browser.js") " imports from " (code "sx-platform.js") " instead of containing the implementations inline")
(li (strong "Step 3") " — " (code "sx-wasm-shim.js") " imports from the same " (code "sx-platform.js") " and wires the functions as WASM imports"))
(p "Result: one implementation of every browser primitive, shared by both evaluator targets. Fix a bug in " (code "sx-platform.js") " and both JS and WASM evaluators get the fix.")
(~docs/code :code (highlight "// sx-platform.js (extracted from platform_js.py output)\nexport function createElement(tag) {\n return document.createElement(tag);\n}\nexport function setAttribute(el, key, val) {\n el.setAttribute(key, val);\n}\nexport function appendChild(parent, child) {\n parent.appendChild(child);\n}\nexport function addEventListener(el, event, callback) {\n el.addEventListener(event, callback);\n}\n// ... ~150 more browser primitives\n\n// sx-browser.js\nimport * as platform from './sx-platform.js';\n// Uses platform functions directly — evaluator is JS\n\n// sx-wasm-shim.js\nimport * as platform from './sx-platform.js';\n// Wraps platform functions for WASM import — evaluator is Rust" "javascript")))
;; -----------------------------------------------------------------------
;; Interaction with other plans
;; -----------------------------------------------------------------------
(~docs/section :title "Interaction with Other Plans" :id "interactions"
(ul :class "list-disc list-inside space-y-2"
(li (strong "WASM Bytecode VM") " — complementary, not competing. This plan bootstraps the tree-walking evaluator to Rust/WASM. The bytecode VM plan compiles SX to a custom bytecode format and runs it in a dispatch loop. Tree-walking comes first (it's working now). Bytecode VM is a future optimisation on top of the Rust host.")
(li (strong "Runtime Slicing") " — the WASM module can be tiered. L0 pages need no WASM at all. L1 pages need a minimal WASM module (just parse + eval, no DOM). L2+ pages need the full module with DOM and signals. Tree-shake unused primitives per tier.")
(li (strong "Content-Addressed Components") " — deterministic Rust compilation means the same " (code ".sx") " source always produces the same WASM binary. CID-addressable WASM modules. Fetch the evaluator itself by content hash.")
(li (strong "Async Eval Convergence") " — must complete first. The spec must be the single source of truth before we add another compilation target. The Rust bootstrapper reads the same spec files that the Python and JS bootstrappers read.")))
;; -----------------------------------------------------------------------
;; Principles
;; -----------------------------------------------------------------------
(~docs/section :title "Principles" :id "principles"
(ul :class "list-disc list-inside space-y-2"
(li (strong "Same spec, another host.") " The Rust target is not special — it's the same architecture as Python and JavaScript. " (code "bootstrap_rs.py") " reads the same " (code ".sx") " files and emits Rust instead of Python or JS. The spec doesn't know which host runs it.")
(li (strong "Platform primitives stay in JavaScript.") " DOM, fetch, timers, storage — these are browser APIs. Rust doesn't reimplement them. It calls them through the shared platform layer via the handle table.")
(li (strong "Shared platform, not duplicated platform.") " The key win over a pure-WASM approach. Browser primitives exist once in " (code "sx-platform.js") ". Both evaluators use them. No divergence, no duplicate bugs.")
(li (strong "Progressive, not all-or-nothing.") " WASM is an enhancement. JS remains the fallback. Pages can opt in per-page. The server decides which runtime to ship. Rollout is gradual and reversible.")
(li (strong "Handle table is the boundary.") " Rust holds integer IDs. JavaScript holds real objects. The handle table is the only bridge. This keeps the WASM module platform-independent — swap the handle table implementation for a different host (Node, Deno, native webview) and the Rust code doesn't change.")))
;; -----------------------------------------------------------------------
;; Outcome
;; -----------------------------------------------------------------------
(~docs/section :title "Outcome" :id "outcome"
(p "After completion:")
(ul :class "list-disc list-inside space-y-2 mt-2"
(li "SX bootstraps to four hosts: JavaScript, Python, Rust (native), Rust (WASM)")
(li "Browser evaluation runs at near-native speed via WASM tree-walking")
(li "All browser primitives shared between JS and WASM evaluators — zero duplication")
(li "Gradual rollout: feature flag per-page, shadow-compare for correctness, progressive enhancement for compatibility")
(li "The architecture proof is complete: one spec, every host, deployment-time target selection")
(li "Future bytecode VM plan builds on the Rust host — the platform layer and handle table are already in place")))))