# 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` | | dict | `dict` | `Object` / `Map` | `HashMap` | | 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) ```