Files
rose-ash/shared/sx/ref/BOUNDARY.md
giles a8bfff9e0b Remove CSSX style dictionary infrastructure — styling is just components
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>
2026-03-08 00:00:23 +00:00

85 lines
4.0 KiB
Markdown

# 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`/`Union` type. `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
1. **Spec-first.** Every primitive, I/O function, and page helper must be declared in `primitives.sx` or `boundary.sx` before it can be registered. Undeclared registration = error.
2. **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.
3. **Data in, markup out.** Python returns data (dicts, lists, strings). `.sx` files compose markup. No SX source construction in Python — no f-strings, no string concatenation, no `SxExpr(f"...")`.
4. **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.
5. **Fail fast.** Violations are runtime errors (startup crash), not warnings. For typed targets, they're compile errors.
## Adding a New Primitive
1. Add declaration to `primitives.sx` (pure) or `boundary.sx` (I/O / page helper)
2. Implement in the target language's primitive file
3. The bootstrapper-emitted validator will accept it on next rebuild/restart
4. 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)
```