Fix empty code blocks: rename ~docs/code param, fix batched IO dispatch
Two bugs caused code blocks to render empty across the site: 1. ~docs/code component had parameter named `code` which collided with the HTML <code> tag name. Renamed to `src` and updated all 57 callers. Added font-mono class for explicit monospace. 2. Batched IO dispatch in ocaml_bridge.py only skipped one leading number (batch ID) but the format has two (epoch + ID): (io-request EPOCH ID "name" args...). Changed to skip all leading numbers so the string name is correctly found. This fixes highlight and other batchable helpers returning empty results. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,7 +50,7 @@
|
||||
"But the crucial move — the one that makes this a genuine Hegelian synthesis rather than a mere juxtaposition — is " (strong "the morph") ". When the server sends new content and the client merges it into the existing DOM, hydrated islands are " (em "preserved") ". The server updates the lake. The islands keep their state. The server's new representation flows around the islands like water around rocks. The client's interiority survives the server's authority.")
|
||||
(p :class "text-stone-600"
|
||||
"This is " (em "Aufhebung") " in its precise meaning: cancellation, preservation, and elevation. The thesis (server authority) is " (em "cancelled") " — the server no longer has total control over the page. It is " (em "preserved") " — the server still renders the document, still determines structure, still delivers representations. It is " (em "elevated") " — the server now renders " (em "around") " reactive islands, acknowledging their autonomy. Simultaneously, the antithesis (client autonomy) is cancelled (the client no longer controls the whole page), preserved (islands keep their state), and elevated (client state now coexists with server-driven updates).")
|
||||
(~docs/code :code (highlight ";; The island: reactive state coexists with server lakes.\n;; Lakes are server-morphable slots — the water within the island.\n\n(defisland ~essays/hegelian-synthesis/header ()\n (let ((families (list \"violet\" \"rose\" \"blue\" \"emerald\"))\n (idx (signal 0))\n (current (computed (fn ()\n (nth families (mod (deref idx) (len families)))))))\n (a :href \"/\" :sx-get \"/\" :sx-target \"#main-panel\"\n ;; Lake: server can update the logo\n (lake :id \"logo\"\n (span :style (cssx ...) \"(<sx>)\"))\n ;; Reactive: signal-bound, NOT in a lake\n (span :style (cssx (:text (colour (deref current) 500)))\n :on-click (fn (e) (swap! idx inc))\n \"reactive\")\n ;; Lake: server can update the copyright\n (lake :id \"copyright\"\n (p \"© 2026\")))))\n\n;; Click: colour changes (client state)\n;; Server sends new page — morph enters the island\n;; Lakes update from server content\n;; Reactive span keeps its colour — state survives" "lisp"))
|
||||
(~docs/code :src (highlight ";; The island: reactive state coexists with server lakes.\n;; Lakes are server-morphable slots — the water within the island.\n\n(defisland ~essays/hegelian-synthesis/header ()\n (let ((families (list \"violet\" \"rose\" \"blue\" \"emerald\"))\n (idx (signal 0))\n (current (computed (fn ()\n (nth families (mod (deref idx) (len families)))))))\n (a :href \"/\" :sx-get \"/\" :sx-target \"#main-panel\"\n ;; Lake: server can update the logo\n (lake :id \"logo\"\n (span :style (cssx ...) \"(<sx>)\"))\n ;; Reactive: signal-bound, NOT in a lake\n (span :style (cssx (:text (colour (deref current) 500)))\n :on-click (fn (e) (swap! idx inc))\n \"reactive\")\n ;; Lake: server can update the copyright\n (lake :id \"copyright\"\n (p \"© 2026\")))))\n\n;; Click: colour changes (client state)\n;; Server sends new page — morph enters the island\n;; Lakes update from server content\n;; Reactive span keeps its colour — state survives" "lisp"))
|
||||
(p :class "text-stone-600"
|
||||
"The " (code "lake") " tag is the key. Inside the island, " (code "(lake :id \"logo\" ...)") " marks a region as server territory — the server can update its content during a morph. The reactive " (code "span") " with its signal-bound style is " (em "not") " in a lake — it is island territory, untouchable by the morph. The morph enters the island, finds the lakes, updates them from the server's new representation, and " (em "flows around") " the reactive nodes like water around rocks.")
|
||||
(p :class "text-stone-600"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"The SX specification is written in SX. This sounds like the same trick as HTML — the spec in its own format — but it is categorically different.")
|
||||
(p :class "text-stone-600"
|
||||
(code "eval.sx") " defines the SX evaluator as s-expressions. Not as documentation " (em "about") " the evaluator. As the evaluator " (em "itself") ". A bootstrapper reads " (code "eval.sx") " and produces a working evaluator — in JavaScript, in Python, in any target language. " (code "parser.sx") " defines the SX parser as s-expressions. A bootstrapper reads it and produces a working parser. " (code "render.sx") " defines the renderer. " (code "primitives.sx") " defines the primitive operations.")
|
||||
(~docs/code :code (highlight ";; From eval.sx — the evaluator defining itself:\n\n(define eval-expr\n (fn (expr env mode)\n (cond\n ((number? expr) expr)\n ((string? expr) expr)\n ((boolean? expr) expr)\n ((nil? expr) expr)\n ((symbol? expr) (resolve-symbol expr env))\n ((keyword? expr) (keyword-name expr))\n ((dict? expr) (eval-dict expr env mode))\n ((list? expr)\n (let ((head (first expr)))\n (if (symbol? head)\n (let ((name (symbol-name head)))\n (if (special-form? name)\n (eval-special-form name (rest expr) env mode)\n (eval-call expr env mode)))\n (eval-call expr env mode)))))))" "lisp"))
|
||||
(~docs/code :src (highlight ";; From eval.sx — the evaluator defining itself:\n\n(define eval-expr\n (fn (expr env mode)\n (cond\n ((number? expr) expr)\n ((string? expr) expr)\n ((boolean? expr) expr)\n ((nil? expr) expr)\n ((symbol? expr) (resolve-symbol expr env))\n ((keyword? expr) (keyword-name expr))\n ((dict? expr) (eval-dict expr env mode))\n ((list? expr)\n (let ((head (first expr)))\n (if (symbol? head)\n (let ((name (symbol-name head)))\n (if (special-form? name)\n (eval-special-form name (rest expr) env mode)\n (eval-call expr env mode)))\n (eval-call expr env mode)))))))" "lisp"))
|
||||
(p :class "text-stone-600"
|
||||
"This is not documentation. It is a " (em "program that defines the rules of its own interpretation") ". The evaluator that processes SX expressions is itself an SX expression. The spec does not merely " (em "describe") " how SX works — it " (em "is") " how SX works. Give this file to a bootstrapper, and out comes a functioning evaluator. The specification is executable. The definition " (em "is") " the implementation.")
|
||||
(p :class "text-stone-600"
|
||||
@@ -67,7 +67,7 @@
|
||||
"Every practical advantage of SX over JSON and HTML for hypermedia flows from this single property.")
|
||||
(p :class "text-stone-600"
|
||||
(strong "Progressive discovery") " works because controls are not metadata interpreted by convention — they are expressions evaluated by the same evaluator that processes content. The server renders conditional controls using the language itself:")
|
||||
(~docs/code :code (highlight "(when can-cancel\n (button :sx-post \"/orders/4281/cancel\"\n :sx-confirm \"Cancel this order?\"\n \"Cancel order\"))" "lisp"))
|
||||
(~docs/code :src (highlight "(when can-cancel\n (button :sx-post \"/orders/4281/cancel\"\n :sx-confirm \"Cancel this order?\"\n \"Cancel order\"))" "lisp"))
|
||||
(p :class "text-stone-600"
|
||||
"The " (code "when") " is not a convention layered on data. It is a special form defined in " (code "eval.sx") ". The server evaluates it. The client sees only the controls that survive evaluation. The state machine is authored in the language, not in metadata " (em "about") " the language.")
|
||||
(p :class "text-stone-600"
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"JSON is data notation. It has objects, arrays, strings, numbers, booleans, and null. It parses in one pass. It validates structurally. It is ubiquitous.")
|
||||
(p :class "text-stone-600"
|
||||
"JSON is not homoiconic because it has no concept of evaluation. It is " (em "inert") " data. To make JSON a programming language, you must invent a convention for representing code — and every such convention reinvents s-expressions with worse ergonomics:")
|
||||
(~docs/code :code (highlight ";; JSON \"code\" (actual example from various JSON-based DSLs)\n{\"if\": [{\">\": [\"$.count\", 0]},\n {\"map\": [\"$.items\", {\"fn\": [\"item\", {\"get\": [\"item\", \"name\"]}]}]},\n {\"literal\": \"No items\"}]}\n\n;; The same thing in s-expressions\n(if (> count 0)\n (map (fn (item) (get item \"name\")) items)\n \"No items\")" "lisp"))
|
||||
(~docs/code :src (highlight ";; JSON \"code\" (actual example from various JSON-based DSLs)\n{\"if\": [{\">\": [\"$.count\", 0]},\n {\"map\": [\"$.items\", {\"fn\": [\"item\", {\"get\": [\"item\", \"name\"]}]}]},\n {\"literal\": \"No items\"}]}\n\n;; The same thing in s-expressions\n(if (> count 0)\n (map (fn (item) (get item \"name\")) items)\n \"No items\")" "lisp"))
|
||||
(p :class "text-stone-600"
|
||||
"The JSON version is an s-expression encoded in JSON's syntax — lists-of-lists with a head element that determines semantics. It has strictly more punctuation (colons, commas, braces, brackets, quotes around keys) and strictly less readability. Every JSON-based DSL that reaches sufficient complexity converges on this pattern and then wishes it had just used s-expressions."))
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
(p :class "text-stone-600"
|
||||
"A " (code "defcomp") " definition is a Form:")
|
||||
|
||||
(~docs/code :code
|
||||
(~docs/code :src
|
||||
(str "(defcomp ~card (&key title subtitle &rest children)\n"
|
||||
" (div :class \"rounded-lg shadow-sm p-4\"\n"
|
||||
" (h2 :class \"font-bold\" title)\n"
|
||||
@@ -134,7 +134,7 @@
|
||||
"The SX evaluator does something structurally similar. "
|
||||
"When the browser receives SX wire format \u2014 ")
|
||||
|
||||
(~docs/code :code
|
||||
(~docs/code :src
|
||||
"(~card :title \"Plato\" :subtitle \"428 BC\")")
|
||||
|
||||
(p :class "text-stone-600"
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
(~docs/section :title "What SX does differently" :id "sx-approach"
|
||||
(p :class "text-stone-600"
|
||||
"An SX component is a single expression that contains its structure, its style (as keyword-resolved CSS classes), and its behavior (event bindings, conditionals, data flow). Nothing is in a separate file unless it genuinely represents a separate concern.")
|
||||
(~docs/code :code "(defcomp ~essays/separation-of-concerns/product-card (&key product on-add)
|
||||
(~docs/code :src "(defcomp ~essays/separation-of-concerns/product-card (&key product on-add)
|
||||
(div :class \"rounded-lg border border-stone-200 p-4 hover:shadow-md transition-shadow\"
|
||||
(img :src (get product \"image\") :alt (get product \"name\")
|
||||
:class \"w-full h-48 object-cover rounded\")
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
(~docs/section :title "Boundary types" :id "types"
|
||||
(p :class "text-stone-600"
|
||||
"Only these types may cross the host-SX boundary:")
|
||||
(~docs/code :code (highlight "(define-boundary-types\n (list \"number\" \"string\" \"boolean\" \"nil\" \"keyword\"\n \"list\" \"dict\" \"sx-source\" \"style-value\"))" "lisp"))
|
||||
(~docs/code :src (highlight "(define-boundary-types\n (list \"number\" \"string\" \"boolean\" \"nil\" \"keyword\"\n \"list\" \"dict\" \"sx-source\" \"style-value\"))" "lisp"))
|
||||
(p :class "text-stone-600"
|
||||
"No Python " (code :class "text-violet-700 text-sm" "datetime") " objects. No ORM models. No Quart request objects. If a host function returns a " (code :class "text-violet-700 text-sm" "datetime") ", it must convert to an ISO string before crossing. If it returns a database row, it must convert to a plain dict. The boundary validation checks this recursively — lists and dicts have their elements checked too.")
|
||||
(p :class "text-stone-600"
|
||||
@@ -58,8 +58,8 @@
|
||||
"One enforcement that is not automated but equally important: " (strong "SX source code must not be constructed as Python strings") ". S-expressions belong in " (code :class "text-violet-700 text-sm" ".sx") " files. Python belongs in " (code :class "text-violet-700 text-sm" ".py") " files. If you see a Python f-string that builds " (code :class "text-violet-700 text-sm" "(div :class ...)") ", that is a boundary violation.")
|
||||
(p :class "text-stone-600"
|
||||
"The correct pattern: Python returns " (strong "data") " (dicts, lists, strings). " (code :class "text-violet-700 text-sm" ".sx") " files receive data via keyword args and compose the markup. The only exception is " (code :class "text-violet-700 text-sm" "SxExpr") " wrappers for pre-rendered fragments — and those should be built with " (code :class "text-violet-700 text-sm" "sx_call()") " or " (code :class "text-violet-700 text-sm" "_sx_fragment()") ", never with f-strings.")
|
||||
(~docs/code :code (highlight ";; CORRECT: .sx file composes markup from data\n(defcomp ~essays/server-architecture/my-page (&key items)\n (div :class \"space-y-4\"\n (map (fn (item)\n (div :class \"border rounded p-3\"\n (h3 (get item \"title\"))\n (p (get item \"desc\"))))\n items)))" "lisp"))
|
||||
(~docs/code :code (highlight "# CORRECT: Python returns data\ndef _my_page_data():\n return {\"items\": [{\"title\": \"A\", \"desc\": \"B\"}]}\n\n# WRONG: Python builds SX source\ndef _my_page_data():\n return SxExpr(f'(div (h3 \"{title}\"))') # NO" "python")))
|
||||
(~docs/code :src (highlight ";; CORRECT: .sx file composes markup from data\n(defcomp ~essays/server-architecture/my-page (&key items)\n (div :class \"space-y-4\"\n (map (fn (item)\n (div :class \"border rounded p-3\"\n (h3 (get item \"title\"))\n (p (get item \"desc\"))))\n items)))" "lisp"))
|
||||
(~docs/code :src (highlight "# CORRECT: Python returns data\ndef _my_page_data():\n return {\"items\": [{\"title\": \"A\", \"desc\": \"B\"}]}\n\n# WRONG: Python builds SX source\ndef _my_page_data():\n return SxExpr(f'(div (h3 \"{title}\"))') # NO" "python")))
|
||||
|
||||
(~docs/section :title "Why this matters for multiple languages" :id "languages"
|
||||
(p :class "text-stone-600"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"A typical web project requires the AI to context-switch between HTML (angle brackets, void elements, boolean attributes), CSS (selectors, properties, at-rules, a completely different syntax from HTML), JavaScript (statements, expressions, classes, closures, async/await), and whatever templating language glues them together (Jinja delimiters, ERB tags, JSX interpolation). Each is a separate grammar. Each has edge cases. Each interacts with the others in ways that are hard to predict.")
|
||||
(p :class "text-stone-600"
|
||||
"In SX, structure, style, logic, and data are all s-expressions:")
|
||||
(~docs/code :code (highlight ";; Structure\n(div :class \"card\" (h2 title) (p body))\n\n;; Style\n(cssx card-style\n :bg white :rounded-lg :shadow-md :p 6)\n\n;; Logic\n(if (> (length items) 0)\n (map render-item items)\n (p \"No items found.\"))\n\n;; Data\n{:name \"Alice\" :role \"admin\" :active true}\n\n;; Component definition\n(defcomp ~essays/sx-and-ai/user-card (&key user)\n (div :class \"card\"\n (h2 (get user \"name\"))\n (span :class \"badge\" (get user \"role\"))))" "lisp"))
|
||||
(~docs/code :src (highlight ";; Structure\n(div :class \"card\" (h2 title) (p body))\n\n;; Style\n(cssx card-style\n :bg white :rounded-lg :shadow-md :p 6)\n\n;; Logic\n(if (> (length items) 0)\n (map render-item items)\n (p \"No items found.\"))\n\n;; Data\n{:name \"Alice\" :role \"admin\" :active true}\n\n;; Component definition\n(defcomp ~essays/sx-and-ai/user-card (&key user)\n (div :class \"card\"\n (h2 (get user \"name\"))\n (span :class \"badge\" (get user \"role\"))))" "lisp"))
|
||||
(p :class "text-stone-600"
|
||||
"The AI learns one syntax and applies it everywhere. The mental model does not fragment across subsystems. A " (code "div") " and an " (code "if") " and a " (code "defcomp") " are all lists. The model that generates one can generate all three, because they are the same thing."))
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"A React component's interface is spread across prop types (or TypeScript interfaces), JSDoc comments, Storybook stories, and whatever documentation someone wrote. An AI reading a component must synthesize information from multiple sources to understand what it accepts and what it produces.")
|
||||
(p :class "text-stone-600"
|
||||
"An SX component declares everything in one expression:")
|
||||
(~docs/code :code (highlight "(defcomp ~essays/sx-and-ai/product-card (&key title price image &rest children)\n (div :class \"rounded border p-4\"\n (img :src image :alt title)\n (h3 :class \"font-bold\" title)\n (span :class \"text-lg\" (format-price price))\n children))" "lisp"))
|
||||
(~docs/code :src (highlight "(defcomp ~essays/sx-and-ai/product-card (&key title price image &rest children)\n (div :class \"rounded border p-4\"\n (img :src image :alt title)\n (h3 :class \"font-bold\" title)\n (span :class \"text-lg\" (format-price price))\n children))" "lisp"))
|
||||
(p :class "text-stone-600"
|
||||
"The AI reads this and knows: it takes " (code "title") ", " (code "price") ", and " (code "image") " as keyword arguments, and " (code "children") " as rest arguments. It knows the output structure — a " (code "div") " with an image, heading, price, and slot for children. It knows this because the definition " (em "is") " the documentation. There is no separate spec to consult, no type file to find, no ambiguity about which props are required.")
|
||||
(p :class "text-stone-600"
|
||||
@@ -54,8 +54,8 @@
|
||||
"LLMs operate on tokens. Every token costs compute, latency, and money. The information density of a representation — how much semantics per token — directly affects how much an AI can see, generate, and reason about within its context window and output budget.")
|
||||
(p :class "text-stone-600"
|
||||
"Compare equivalent UI definitions:")
|
||||
(~docs/code :code (highlight ";; SX: 42 tokens\n(div :class \"card p-4\"\n (h2 :class \"font-bold\" title)\n (p body)\n (when footer\n (div :class \"mt-4 border-t pt-2\" footer)))" "lisp"))
|
||||
(~docs/code :code (highlight "// React/JSX: ~75 tokens\n<div className=\"card p-4\">\n <h2 className=\"font-bold\">{title}</h2>\n <p>{body}</p>\n {footer && (\n <div className=\"mt-4 border-t pt-2\">{footer}</div>\n )}\n</div>" "python"))
|
||||
(~docs/code :src (highlight ";; SX: 42 tokens\n(div :class \"card p-4\"\n (h2 :class \"font-bold\" title)\n (p body)\n (when footer\n (div :class \"mt-4 border-t pt-2\" footer)))" "lisp"))
|
||||
(~docs/code :src (highlight "// React/JSX: ~75 tokens\n<div className=\"card p-4\">\n <h2 className=\"font-bold\">{title}</h2>\n <p>{body}</p>\n {footer && (\n <div className=\"mt-4 border-t pt-2\">{footer}</div>\n )}\n</div>" "python"))
|
||||
(p :class "text-stone-600"
|
||||
"The SX version is roughly 40% fewer tokens for equivalent semantics. No closing tags. No curly-brace interpolation. No " (code "className") " vs " (code "class") " distinction. Every token carries meaning. Over an entire application — dozens of components, hundreds of expressions — this compounds into significantly more code visible per context window and significantly less output the model must generate."))
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"The hardest thing for AI to get right in conventional frameworks is composition — how pieces fit together. React has rules about hooks. Vue has template vs script vs style sections. Angular has modules, declarations, and dependency injection. Each framework's composition model is a set of conventions the AI must learn and apply correctly.")
|
||||
(p :class "text-stone-600"
|
||||
"S-expressions compose by nesting. A list inside a list is a composition. There are no rules beyond this:")
|
||||
(~docs/code :code (highlight ";; Compose components by nesting — that's it\n(~page-layout :title \"Dashboard\"\n (~sidebar\n (~nav-menu :items menu-items))\n (~main-content\n (map ~essays/sx-and-ai/user-card users)\n (~pagination :page current-page :total total-pages)))" "lisp"))
|
||||
(~docs/code :src (highlight ";; Compose components by nesting — that's it\n(~page-layout :title \"Dashboard\"\n (~sidebar\n (~nav-menu :items menu-items))\n (~main-content\n (map ~essays/sx-and-ai/user-card users)\n (~pagination :page current-page :total total-pages)))" "lisp"))
|
||||
(p :class "text-stone-600"
|
||||
"No imports to manage. No registration steps. No render props, higher-order components, or composition APIs. The AI generates a nested structure and it works, because nesting is the only composition mechanism. This eliminates an entire class of errors that plague AI-generated code in conventional frameworks — the kind where each piece works in isolation but the assembly is wrong."))
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"SX has a peculiar architecture. At its centre sits a specification — a set of s-expression files that define the language. Not a description of the language. Not documentation " (em "about") " the language. The specification " (em "is") " the language. It is simultaneously a formal definition and executable code. You can read it as a document or run it as a program. It does not describe how to build an SX evaluator; it " (em "is") " an SX evaluator, expressed in the language it defines.")
|
||||
(p :class "text-stone-600"
|
||||
"This is the nucleus. Everything else radiates outward from it.")
|
||||
(~docs/code :code (highlight ";; The spec defines eval-expr\n;; eval-expr evaluates the spec\n;; The spec is an artifact that makes itself\n\n(define eval-expr\n (fn (expr env)\n (cond\n (number? expr) expr\n (string? expr) expr\n (symbol? expr) (env-get env (symbol-name expr))\n (list? expr) (eval-list expr env)\n :else expr)))" "lisp"))
|
||||
(~docs/code :src (highlight ";; The spec defines eval-expr\n;; eval-expr evaluates the spec\n;; The spec is an artifact that makes itself\n\n(define eval-expr\n (fn (expr env)\n (cond\n (number? expr) expr\n (string? expr) expr\n (symbol? expr) (env-get env (symbol-name expr))\n (list? expr) (eval-list expr env)\n :else expr)))" "lisp"))
|
||||
(p :class "text-stone-600"
|
||||
"From this nucleus, concentric rings unfurl:"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user