Frame-based dynamic scope: 870/870 — all tests passing
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 13m34s

provide/context and scope/emit!/emitted now use CEK continuation
frames instead of an imperative global stack. Scope state is part
of the continuation — captured by shift, restored by k invocation.

New frame types:
- ProvideFrame: holds name + value, consumed when body completes
- ScopeAccFrame: holds name + mutable emitted list

New CEK special forms:
- context: walks kont for nearest ProvideFrame, returns value
- emit!: walks kont for nearest ScopeAccFrame, appends to emitted
- emitted: walks kont for nearest ScopeAccFrame, returns list

Kont walkers: kont-find-provide, kont-find-scope-acc

This fixes the last 2 test failures:
- provide survives resume: scope captured by shift, restored by k
- scope and emit across shift: accumulator preserved in continuation

JS Full: 870/870 (100%)
JS Standard: 747/747 (100%)
Python: 679/679 (100%)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 14:40:14 +00:00
parent 719da7914e
commit 9f32c8cf0d
3 changed files with 293 additions and 12 deletions

View File

@@ -491,6 +491,78 @@
(td :class "px-3 py-2 text-stone-600" "Full compiled pipeline. JIT compilation, cached WASM functions, near-native rendering speed."))))))
;; -----------------------------------------------------------------------
;; WASM responses — htmx with compiled components
;; -----------------------------------------------------------------------
(~docs/section :title "WASM Responses" :id "wasm-responses"
(p "The server doesn't have to send HTML fragments for HTMX-style swaps. "
"It can send compiled WASM functions that produce DOM directly, "
"or \u2014 when the client already has the components cached \u2014 "
"just the data as gzipped bytecode.")
(h4 :class "font-semibold mt-4 mb-2" "Three response tiers")
(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" "Client cache state")
(th :class "px-3 py-2 font-medium text-stone-600" "Server sends")
(th :class "px-3 py-2 font-medium text-stone-600" "Client does")))
(tbody
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Nothing cached")
(td :class "px-3 py-2 text-stone-700" "HTML (fallback)")
(td :class "px-3 py-2 text-stone-600" "Parse HTML, morph DOM"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Compiler cached")
(td :class "px-3 py-2 text-stone-700" "SX source")
(td :class "px-3 py-2 text-stone-600" "Compile, execute, morph DOM"))
(tr :class "border-b border-stone-100"
(td :class "px-3 py-2 text-stone-700" "Components cached")
(td :class "px-3 py-2 text-stone-700" "Bytecode (data only)")
(td :class "px-3 py-2 text-stone-600" "Call cached WASM function with args"))
(tr
(td :class "px-3 py-2 text-stone-700" "Components cached + gzip")
(td :class "px-3 py-2 text-stone-700" "Gzipped bytecode")
(td :class "px-3 py-2 text-stone-600" "Decompress, call cached function")))))
(h4 :class "font-semibold mt-4 mb-2" "Why bytecode is tiny")
(p "The bytecode doesn't repeat what the client already knows. "
"Component CID references are 4-byte pointers to already-cached compiled functions. "
"Tag names, class strings, attribute keys \u2014 all baked into the compiled component. "
"The only payload is the data that differs between instances.")
(~docs/code :code (highlight ";; HTML response: ~450 bytes\n<div class=\"card\"><h2>Hello</h2><p class=\"text-stone-600\">World</p></div>\n\n;; SX source response: ~60 bytes\n(~card :title \"Hello\" :body \"World\")\n\n;; Bytecode response: ~18 bytes\n[CALL_CID bafyrei..card] [STR \"Hello\"] [STR \"World\"]\n\n;; Gzipped bytecode: ~12 bytes" "text"))
(p "For a list of 50 cards:")
(~docs/code :code (highlight "HTML: 50\u00d7 <div class=\"card\"><h2>...</h2>...</div> ~22 KB\nSX source: 50\u00d7 (~card :title \"...\" :body \"...\") ~3 KB\nBytecode: [CALL_CID] + 50\u00d7 [STR, STR] ~800 bytes\nGzipped bytecode: ~400 bytes" "text"))
(p "The markup structure is in the compiled component. "
"The class strings are in the compiled component. "
"The only thing on the wire is the data that differs between instances. "
"Gzip crushes the repetitive framing to almost nothing.")
(h4 :class "font-semibold mt-4 mb-2" "Content negotiation")
(p "The client advertises what it has via request headers:")
(~docs/code :code (highlight ";; Client tells server what's cached\nAccept: application/sx-bytecode, text/sx, text/html\nX-Sx-Cached-Components: bafyrei..card, bafyrei..header, bafyrei..nav\n\n;; Server picks the smallest response:\n;; - Client has ~card cached? Send bytecode (data only)\n;; - Client has compiler but not ~card? Send SX source\n;; - Client has nothing? Send HTML" "text"))
(p "The server and client negotiate the optimal response format automatically. "
"First visit is HTML (full progressive enhancement). "
"Subsequent navigations are gzipped bytecode \u2014 "
"just the data, a few hundred bytes, instant render via cached compiled components.")
(h4 :class "font-semibold mt-4 mb-2" "The server becomes a data API")
(p "At the bytecode tier, the server is no longer rendering views. "
"It's serving data \u2014 structured arguments for compiled client-side functions. "
"The rendering logic is cached on the client as compiled WASM. "
"This inverts the traditional server-rendered model without sacrificing "
"progressive enhancement \u2014 the HTML fallback is always available."))
;; -----------------------------------------------------------------------
;; How this changes existing plans
;; -----------------------------------------------------------------------