The entire parallel CSS system (StyleValue type, style dictionary, keyword atom resolver, content-addressed class generation, runtime CSS injection, localStorage caching) was built but never adopted — the codebase already uses :class strings with defcomp components for all styling. Remove ~3,000 lines of unused infrastructure. Deleted: - cssx.sx spec module (317 lines) - style_dict.py (782 lines) and style_resolver.py (254 lines) - StyleValue type, defkeyframes special form, build-keyframes platform fn - Style dict JSON delivery (<script type="text/sx-styles">), cookies, localStorage - css/merge-styles primitives, inject-style-value, fnv1a-hash platform interface Simplified: - defstyle now binds any value (string, function) — no StyleValue type needed - render-attrs no longer special-cases :style StyleValue → class conversion - Boot sequence skips style dict init step Preserved: - tw.css parsing + CSS class delivery (SX-Css headers, <style id="sx-css">) - All component infrastructure (defcomp, caching, bundling, deps) - defstyle as a binding form for reusable class strings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4.0 KiB
SX Boundary Enforcement
Principle
SX is an uninterrupted island of pure evaluation. Host code (Python, JavaScript, Rust, etc.) interacts with it only through declared entry points. The specification enforces this — violations are errors, not style suggestions.
The Three Tiers
Tier 1: Pure Primitives
Declared in primitives.sx. Stateless, synchronous, no side effects. Available in every SX environment on every target.
Examples: +, str, map, get, concat, merge
Tier 2: I/O Primitives
Declared in boundary.sx. Async, side-effectful, require host context (request, config, services). Server-side only.
Examples: frag, query, current-user, csrf-token, request-arg
Tier 3: Page Helpers
Declared in boundary.sx with a :service scope. Registered per-app, return data that .sx components render. Server-side only.
Examples: highlight (sx), editor-data (blog), all-markets-data (market)
Boundary Types
Only these types may cross the host-SX boundary:
| Type | Python | JavaScript | Rust (future) |
|---|---|---|---|
| number | int, float |
number |
f64 |
| string | str |
string |
String |
| boolean | bool |
boolean |
bool |
| nil | NIL sentinel |
NIL sentinel |
SxValue::Nil |
| keyword | str (colon-prefixed) |
string |
String |
| list | list |
Array |
Vec<SxValue> |
| dict | dict |
Object / Map |
HashMap<String, SxValue> |
| sx-source | SxExpr wrapper |
string |
String |
NOT allowed: ORM models, datetime objects, request objects, raw callables, framework types. Convert at the edge before crossing.
Enforcement Mechanism
The bootstrappers (bootstrap_js.py, bootstrap_py.py, future bootstrap_rs.py, etc.) read boundary.sx and emit target-native validation:
-
Typed targets (Rust, Haskell, TypeScript): Boundary types become an enum/ADT/discriminated union. Registration functions have type signatures that reject non-SX values at compile time. You literally cannot register a primitive that returns a
datetime— it won't typecheck. -
Python + mypy: Boundary types become a
Protocol/Uniontype.validate_boundary_value()checks at runtime; mypy catches most violations statically. -
JavaScript: Runtime validation only.
registerPrimitive()checks the name against the declared set. Boundary type checking is runtime.
The Contract
-
Spec-first. Every primitive, I/O function, and page helper must be declared in
primitives.sxorboundary.sxbefore it can be registered. Undeclared registration = error. -
SX types only. Values crossing the boundary must be SX-typed. Host-native types (datetime, ORM models, request objects) must be converted to dicts/strings at the edge.
-
Data in, markup out. Python returns data (dicts, lists, strings).
.sxfiles compose markup. No SX source construction in Python — no f-strings, no string concatenation, noSxExpr(f"..."). -
Closed island. SX code can only call symbols in its env + declared primitives. There is no FFI, no
eval-python, no escape hatch from inside SX. -
Fail fast. Violations are runtime errors (startup crash), not warnings. For typed targets, they're compile errors.
Adding a New Primitive
- Add declaration to
primitives.sx(pure) orboundary.sx(I/O / page helper) - Implement in the target language's primitive file
- The bootstrapper-emitted validator will accept it on next rebuild/restart
- If you skip step 1, the app crashes on startup telling you exactly what's missing
File Map
shared/sx/ref/
primitives.sx — Pure primitive declarations
boundary.sx — I/O primitive + page helper + boundary type declarations
bootstrap_js.py — JS bootstrapper (reads both, emits validation)
bootstrap_py.py — Python bootstrapper (reads both, emits validation)
eval.sx — Evaluator spec (symbol resolution, env model)
parser.sx — Parser spec
render.sx — Renderer spec (shared registries)