# S-expressions as Microservice Wire Format

**The strongest near-term application: structured inter-service communication, even when final output is still rendered HTML.**

---

## The Current Problem

Rose-ash services communicate two ways, both lossy:

### `fetch_data()` — returns dicts

The receiver has to know what keys to expect. There's no schema, no composability. It's just "here's a bag of stuff, good luck."

### `fetch_fragment()` — returns HTML strings

The receiving service can't inspect, filter, or transform them. It just jams the string into a template. If events returns a calendar nav fragment, blog can't reorder the items or filter by date. It's take-it-or-leave-it.

---

## Sexp Between Services

Sexp gives you **structured data that's also renderable**. The relations service could return:

```scheme
(nav :class "container-nav"
  (relation :type "page->calendar" :label "Saturday Market" :href "/events/saturday/")
  (relation :type "page->market" :label "Craft Stalls" :href "/markets/crafts/"))
```

The receiving service can:

- **Render it straight to HTML** — same as today's fragments
- **Filter it** — `exclude: page->calendar`
- **Reorder, group, or transform it** — tree operations, not string manipulation
- **Pass it through to the client as-is** — if they speak sexp
- **Cache it by content hash** — deterministic, structural equality

All without parsing HTML or knowing the internal structure of the sending service. The tree *is* the API response *and* the renderable output.

---

## One Format, Two Purposes

Today there's a split between "data endpoints" (`fetch_data` → dicts) and "fragment endpoints" (`fetch_fragment` → HTML strings). They serve different needs:

| Need | Current | With Sexp |
|---|---|---|
| Structured data for logic | `fetch_data()` → dict | Same sexp tree |
| Renderable HTML for templates | `fetch_fragment()` → HTML string | Same sexp tree, rendered at the boundary |
| Filtering/transforming cross-service output | Not possible (HTML is opaque) | Tree operations on sexp |
| Caching | Key-based (URL + params) | Content-addressed (hash the tree) |
| Schema/validation | None (hope the keys are right) | Tree structure is self-describing |

The unification eliminates an entire class of bugs — where `fetch_data` and `fetch_fragment` for the same resource return subtly inconsistent results because they're maintained as separate code paths.

---

## Nothing Changes for the User

The final output to the browser is still plain HTML. The improvement is entirely in how services talk to each other — less brittle, more composable, zero extra infrastructure. Sexp is evaluated to HTML at the outermost boundary (the route handler), exactly as it is today with the sexp template engine.

---

## Migration Path

This can be adopted incrementally, one service boundary at a time:

1. **Relations service already returns sexp** — `container-nav` fragment is rendered from sexp templates
2. **Next**: have fragment endpoints return raw sexp instead of pre-rendered HTML, let the caller render
3. **Then**: unify `fetch_data` and `fetch_fragment` into a single `fetch_sexp` that returns structured trees
4. **Finally**: callers that want data extract it from the tree; callers that want HTML render the tree

Each step is backwards-compatible. Services can serve both HTML fragments and sexp simultaneously during transition.
