Commit Graph

5 Commits

Author SHA1 Message Date
98ff7a350a host/tests: Phase 2 — trim Playwright to a boot smoke
The picker's per-behaviour browser tests are now SX engine tests
(web/tests/test-relate-picker.sx) + SX conformance (lib/host/tests/blog.sx), so
delete them from Playwright and keep only what needs a real boosted-SPA browser:

  spa-check.spec.js (3): WASM kernel boots + loads modules CONTENT-ADDRESSED
    (/sx/h/{hash} fetches, zero path-.sxbc fallback — new assertion) + marks
    ready; a boosted nav fragment-swaps #content (raw! HTML path); back/re-boost.
  relate-picker.spec.js (2): the bind-boost-form remove button; the picker
    re-binds its load trigger on content brought in by a boosted SPA nav.

Net: 11 browser tests -> 5. Both ephemeral-host suites verified green
(run-spa-check.sh 3/3, run-picker-check.sh 2/2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 17:56:07 +00:00
b9a24d5870 web: re-boost swapped content from the [sx-boost] ancestor (fixes back-then-click full reload)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 33s
After a fragment swap, process-elements(target) -> process-boosted(target) only
boosted [sx-boost] containers that are DESCENDANTS of the swap target. But the
swap target (#content) is nested UNDER the boost wrapper (<div sx-boost="#content">
<div id="content">), so re-boosting scoped to the target found nothing — the
swapped-in links never got bound. Only the initial document-wide boot boost
worked, so: home->sub worked (home links boosted at boot), but Back restored the
home content unboosted, and the next click did a full page reload. (Post-page
links were unboosted too; Back just exposed it.)

process-boosted now ALSO boosts from the nearest [sx-boost] ANCESTOR of root
(dom-closest), so any swap target inside a boost scope gets its links rebound.
is-processed? guards keep it idempotent.

spa-check: the back-button test now clicks AGAIN after Back and asserts it's a
SPA nav (no full reload) — would have caught this. .sxbc regenerated.

Verified: spa-check 4/4 (incl. click-after-back).
2026-06-29 13:41:50 +00:00
f5b6612ee1 web+host: fix raw! HTML dropped in client SX render (dom-parse-html returned a NodeList)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 41s
dom-parse-html returned body.childNodes — a NodeList, not a Node — so the client
SX render did appendChild(NodeList) and threw "Argument 1 does not implement
interface Node", silently dropping every raw! HTML block (e.g. a post's <article>
body). It surfaced only now because the blog renders fragments client-side
(text/sx) since this session; before, fragments were server HTML so sx-render
never ran on raw!. The error is caught/non-fatal, and the spa-check suite only
asserted the footer + URL behaviour, so it passed through a dropped post body.

- dom-parse-html now returns a DocumentFragment (moves the parsed nodes in): a
  real Node, appendChild-able as one unit, and queryable — which also fixes the
  already-broken hs-htmx callers that did (dom-query doc ...) / (dom-first-child
  doc) on what was a NodeList.
- spa-check: assert #content article is visible after a boosted nav, so a dropped
  post body fails the suite (closes the test gap).
- .sxbc regenerated; bundle dom.sx synced to canonical web/lib/dom.sx.

Verified: spa-check 4/4 (incl. the new article assertion).
2026-06-29 13:27:13 +00:00
689dae7d0c host+kernel: blog SPA boost works end-to-end on the WASM OCaml kernel (Playwright 4/4)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 43s
Clicking a blog link now fragment-swaps #content with URL push + working back
button, no full reload — the SX-htmx engine driving the same OCaml kernel the
server runs. Six bugs in the source-load + boost path, found by bisecting in
chromium, all fixed:

1. Import double-apply (sx_server.ml x2, sx_browser.ml): the import suspension
   handlers computed `key = library_name_key lib_spec` then called
   `library_loaded_p key` — but library_loaded_p applies library_name_key
   itself, so it ran sx_to_list on a string and crashed ("Expected list, got
   string"). Only unloaded libs suspend, so it only bit lazy imports. Pass the
   spec, not the key.

2. Unloaded-import crash (spec/evaluator.sx + sx_ref.ml library_exports): an
   import of a not-yet-loaded library returned nil exports, and bind-import-set
   did (keys nil) -> crash. Return an empty dict so the import is a graceful
   no-op (lazy symbol resolution covers real usage).

3. value_to_js missing Integer (sx_browser.ml): integers passed to host methods
   were mishandled, so dom-query-all's (host-call node-list "item" i) ignored i
   and returned node 0 for every index — every element aliased the first, so
   only one link ever boosted. Add the Integer -> JS number case.

4. browser-same-origin? rejected relative URLs (browser.sx x2): it only did
   (starts-with? url origin), so "/alpha/" was treated as cross-origin and
   should-boost-link? refused every relative link. Accept scheme-less,
   non-protocol-relative URLs.

5. dom-query-in undefined (orchestration.sx x2): the swap path called a function
   that exists nowhere; it's just dom-query with a container arg.

6. Lazy-deps never loaded under source fallback (sx-platform.js): lazy symbol
   resolution only fires on the VM GLOBAL_GET path, but source-loaded swap
   callbacks run on the CEK and raise instead of lazy-loading, so the post-swap
   hs-boot-subtree!/htmx-boot-subtree! were undefined and aborted URL push.
   Preload the manifest's lazy-deps.

Verified: native host conformance 271/271; lib/host/playwright/spa-check 4/4
(boot, boost, fragment swap + URL push, back button) in real chromium against an
ephemeral durable host server.
2026-06-29 11:09:11 +00:00
dbcbc39ebe host: blog SPA scaffolding (WASM kernel) — server side complete, boost blocked on bundle rebuild
Turn the blog into a SPA using the SX-htmx engine (web/engine.sx) booting the
WASM OCaml kernel (same evaluator as the server) in-browser, with sx-boost
fragment-swapping every link into #content.

Server side DONE + verified:
- lib/host/static.sx: GET /static/** serves shared/static via the file-read
  primitive (ctype by ext, traversal-guarded, 404 on missing). Wired into
  serve.sh (module + route group). Tested: kernel JS + .wasm binary-exact.
- host/blog--page is now the SPA shell: full page = WASM boot scripts +
  sx-boost=#content wrapper + #content; on SX-Request:true returns ONLY the
  inner content fragment for the engine to swap. All 13 handlers thread req.
- docker-compose mounts ./shared/static.
- lib/host/playwright/spa-check.{spec.js,run-spa-check.sh}: boot/boost/swap/back.

Client side: the WASM kernel BOOTS (SxKernel object, data-sx-ready=true, web
stack loads). BLOCKER: the bundled .sxbc throw 'VM: unknown opcode 0' vs this
worktree's kernel -> .sx source fallback -> boot.sx source fails 'Expected
list, got string' -> process-boosted never binds links (boosted 0/N). Fix =
rebuild a consistent WASM bundle (recompile .sxbc against the kernel via
scripts/sx-build-all.sh); the browser wasm target isn't built here yet. See
plans/host-spa.md. Live NOT redeployed (stays on pre-SPA process).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 20:53:06 +00:00