# Host blog → SPA via the SX-htmx engine (WASM OCaml kernel) Turn the blog (lib/host/blog.sx) into a single-page app using the in-repo SX hypermedia engine (web/engine.sx — "our htmx"): boot the **WASM OCaml kernel** (the same evaluator the server runs) in the browser, and `sx-boost` every link/form into a fragment swap into `#content` — no full reloads, history kept, graceful degradation to plain server-rendered pages with no JS. ## Status **DONE — server side (verified, all green):** - `lib/host/static.sx` — `GET /static/**` serves files under `shared/static` via the `file-read` primitive (content-type by extension, path-traversal guarded, 404 on missing). Mounted in serve.sh + the route list. Tested: kernel JS 200 + correct ctype + exact bytes; `.wasm` binary-exact with `application/wasm`; traversal/missing → 404. - `lib/host/blog.sx` `host/blog--page` is now the SPA shell: full page = WASM boot scripts (`/static/wasm/sx_browser.bc.wasm.js` + `sx-platform.js`) + a `sx-boost="#content"` wrapper div + `#content`. On the `SX-Request: true` header (a boosted nav) it returns ONLY the inner content (fragment) so the engine swaps it into `#content`. All 13 page handlers thread `req`. Tested: full page carries scripts+boost+#content; `SX-Request` returns the bare fragment. - `docker-compose.dev-sx-host.yml` mounts `./shared/static` so the live container can serve the kernel. - `lib/host/playwright/spa-check.spec.js` + `run-spa-check.sh` — browser check (boot, boost, fragment swap, back button). **DONE — client side, partial:** - The WASM kernel BOOTS in a headless browser: `globalThis.SxKernel` is an object, `` is set, the web-stack modules load. - Fixed: this worktree's `shared/static/wasm/sx_browser.bc.wasm.assets/` was missing 5 of 11 `.wasm` units (`sx-`, `unix-`, `re-`, `start-`, `dune__exe__Sx_browser-`); copied the complete set from the main worktree. **BLOCKER — boost does not activate (`boosted links: 0 / N`):** - The bundled `.sxbc` bytecode throws `VM: unknown opcode 0` against this worktree's `sx_browser.bc.wasm.js` kernel, so sx-platform.js falls back to `.sx` source for every web-stack module. Source fallback works for all modules EXCEPT `boot.sx`, which then fails with `Expected list, got string` — so the boot sequence that wires `process-elements → process-boosted` doesn't complete and no link gets `_sxBoundboost`. - Root cause: the `.sxbc` in `shared/static/wasm/sx/` are out of sync with the WASM kernel (sx.rose-ash.com avoids this because its Docker image ships a consistent bundle and it navigates via client-router page-routes, not boost). ## Rebuild attempt (2026-06-28) — FAILED, reverted Tried it: `dune build browser/sx_browser.bc.wasm.js` succeeded (with many `integer-overflow` warnings — "generated code might be incorrect"), and `node hosts/ocaml/browser/compile-modules.js shared/static/wasm` recompiled all 35 `.sxbc` cleanly. But the freshly-built kernel **crashes on init** in the browser: `Fatal error: exception Invalid_argument("Char.chr")` — so `SxKernel` never initialises (worse than before). The integer-overflow truncation during wasm codegen is the likely culprit (a SHA/char constant). Reverted `shared/static/wasm/` to the main-worktree bundle (which boots cleanly — verified SxKernel + data-sx-ready). So a naive in-worktree rebuild is NOT the fix; the wasm build itself needs investigating (wasm_of_ocaml version? the merged sx-vm-extensions/resolver changes interacting with codegen?). ## Next step — rebuild a consistent WASM bundle `scripts/sx-build-all.sh` does: build the browser wasm target → sync web `.sx` into `hosts/ocaml/browser/dist/sx/` → `node hosts/ocaml/browser/compile-modules.js` (recompiles `.sxbc` via the native sx_server binary) → copy into `shared/static/wasm/`. The browser wasm target is NOT built in this worktree (`hosts/ocaml/_build/default/browser/` is empty), so this needs the `wasm_of_ocaml` toolchain set up first. Once the `.sxbc` match the kernel, the bytecode path loads (no source fallback), `boot.sx` runs, and `process-boosted` binds the links — then the SPA Playwright check should pass. Alternatively: build the browser kernel in the main worktree (which has the pipeline) and copy a consistent `sx_browser.bc.wasm.js` + assets + `.sxbc` set into this worktree's `shared/static/wasm/`. ## Deploy note The live container is NOT redeployed with the SPA shell yet — it keeps running the pre-SPA `blog.sx` in memory (the native host doesn't hot-reload). Don't recreate the container until the bundle is consistent and the SPA Playwright check is green, to avoid shipping a kernel that boots but doesn't boost. (Even if it is recreated, pages degrade gracefully: links still do normal full-page nav.)