# §6 · The execution-side model

> **Status: DRAFT.** The side vocabulary and one inference are probed. The *correctness* of inference at runtime,
> and what crosses the wire, are **UNVERIFIED here by construction** — see §6.6.

This is the language's most distinctive rule, and the one §11.1 and §11.2 both depend on.

## 6.1 One unit, more than one runtime

A single compilation unit produces code that runs in a browser and code that runs on a server. There is no wire
protocol to declare, no endpoint to route, and no data-transfer type to keep in step, because there is no boundary
in the source for those things to sit on.

**Normative.** A program MUST NOT be required to state where a member runs. An implementation MUST determine it.

## 6.2 The three sides

⚠ **The model is three-valued, not two.** A reader who assumes "client or server" will mispredict the third and
most interesting case.

| side | meaning |
|---|---|
| **Server** | the authority for data **reads**, security, and secrets — the server interpreter runs it |
| **Client** | dialogs, local UI, interaction — the client runtime runs it |
| **Either** | pure compute **and local data mutation** (`new`, assignment) — runs wherever the cursor already is, and **may span the boundary** |

*Source: `ExecutionSide`, `OsySharpExpressionEnums.cs:539`.*

⭐ **`Either` is where the model earns itself.** A pure helper does not need a home, so it does not get one — it runs
on whichever side called it, and the same function called from a render slot and from a server function is not two
functions. Local mutation is `Either` for the same reason: creating a row or assigning a field acts on the unit of
work the cursor already holds, so it is meaningful on both sides and identical on both.

## 6.3 Inference

The side follows from **what the code touches**, not from what it is called or where it is written.

- an **entity read** materialises rows under the declared row filter (§8) → **Server**
- a **render** expression paints and performs no I/O → **Client**
- **pure compute** and local mutation → **Either**

⚑ **Probe (established, and run by CI).** A `live var` holding an entity read is reported `side=server`:

```osy probe=accepts
using Osysharp.Ui;
entity Order { [MaxLength(40)] string Ref; int Total; }
[Page("/")] [AllowAnonymous] component Board() {
  live var orders = Order.Where(o => o.Total > 0);
  render { foreach (var o in orders) { Text(o.Ref); } }
}
```

`osy model --json` reports, for that component member:

```
component Board
   members: orders  side=server
```

## 6.4 Asking the compiler

**Normative.** An implementation MUST make the resolved side inspectable. A rule the author cannot observe is
folklore.

⚠ **Two different answers, and they are easy to confuse:**

| you have | `osy model --json` gives you |
|---|---|
| a **component member** | a `side` — `server` / `client` / `either` |
| a **top-level function** | an **`effects`** record — `readsData`, `writesData`, `pure`, `durability`, plus the entities read/created/modified — and its `transitive` closure |

⚑ **Probed.** A top-level function has **no `side` field**; its keys are `allowAnonymous · effects · file ·
isSystem · name · parameters · returnNullable · returns · scopedName · transitive · visibility`. The effects record
is the answer for a function — `OpenOrders` reports `reads: [Order], readsData: true, pure: false`.

*Informative.* The distinction is not an oversight. A component member is bound to one side because it is part of a
rendered tree; a top-level function is frequently `Either` and takes its side from the caller, so "what does it
touch" is the useful question and "where does it run" is not always answerable in isolation.

## 6.5 Forcing the side

`[Server]` and `[Client]` exist. They force a member onto a side.

**Reaching for one is almost always the wrong instinct.** They were de-advertised across **718 mentions in 174
files** on 2026-08-27, and removed from `osy init`'s scaffold — the first Osy# a downloader reads — precisely
because their presence in examples taught readers to write them. The scaffold's explanatory comment was also
false: it claimed a plain function runs on the server and a render expression cannot call one; measured, a plain
pure top-level function *is* callable from a render.

**Normative.** An implementation MUST NOT require these attributes for ordinary programs, and diagnostics MUST NOT
suggest adding one as the remedy for an inference the author has not been shown.

## 6.6 What this section does NOT establish

**UNVERIFIED, deliberately.** `osy validate` — the instrument behind every probe in this document — establishes
what the compiler **accepts** and what it **reports**. It cannot establish:

- that an inferred side is **correct at runtime** (that the server half really runs on the server);
- **what crosses the wire** at a hand-off, and in which direction;
- that a value which cannot cross is refused rather than silently dropped.

Those are runtime semantics and need a test class, per §1.5. `ExecutionSidesHandlerTests` is the closest existing
guard and is named here so the gap is addressable rather than merely admitted. **A future revision of this section
MUST cite tests for the three claims above, or drop them.**

---

**Next:** §9 Durability semantics — the other half of the model §11 depends on. **Drafted.**
