# 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)
```
