# UI/kernel split completion — branch `ui-ux-split-completion` (2026-09-13)

The egui shell is a consumer of `View` (`docs/shell-on-kernel-playbook.md`).
This branch closes the seams a second front end still needs, resolved against
what the user decided on 2026-09-13. The target front end is a Rust one
(dioxus or similar), not React, so the interface doc's wasm and JSON edges
are deferred; web support stays with the egui build meanwhile.

## Decisions

- **E1 No `Tool` crosses the call; chaining stays.** Every state-carrying
  hand-off between tools (a stamp that opens its rename editor, a route that
  mints a pin and arms its rename, drag-to-move out of select, the Tab cycle
  over a block's fields) is `Transition::SwitchTool(pre-seeded Tool)` produced by
  a tool's `widget` and installed inside the same `kernel()` call. None of
  it serializes and none of it changes. What did carry a `Tool` outward was
  three `CommandSet` entries: the band's arm (already `CommandId::Arm(ToolName)`
  by another road), the overlay's add-route-label (a `RouteId` is all the
  state), and the editor commands (Rename, RenameType, Retype, RenameTag,
  EditText — re-derived from the selection at dispatch, as their constructors
  already take only the drawing and a target). So the wire form of an action
  is the serializable subset; the tool-internal transition keeps the `Tool`.
- **E2 Assets cross by hash, once, with their format.** `Paint::Image` names
  an `AssetHash`. The bytes ride out in the hand-off the first frame that
  draws a hash the session has not yet sent, as the `Asset` itself — the
  enum is the format (PNG or SVG), which both sides must know: the kernel to
  measure and export, the front end to decode. A frame may draw several new
  hashes, so the hand-off slot is a list. The egui `ImageRegistry` is that
  cache, keyed by hash, fed from the hand-off and nothing else.
- **E3 The text editor is the front end's; only the text comes back.**
  Reverses §4.2 and D12. The kernel asks for an editor — field id, screen
  rect, angle, font, alignment, the text as it stands, multiline, character
  limit, whether Tab cycles, a hint — and the front end runs a visible editor
  of its own over the picture. It answers with the edited text on commit, a
  cancel, or Tab with the text (commit, then advance the cycle). The kernel
  paints no draft, caret or selection, holds no cursor, and hears no
  keystroke. Layout may differ between the editor and the committed text
  (a rotated route label is edited upright; a text box wraps where the
  editor wraps until it commits) — accepted, because the kernel rotates
  and wraps on commit anyway. Consequently §6 (text layout in the core,
  `Paint::Glyphs`) is retired: the kernel measures committed text through
  the `TextLayout` the front end supplies, exports through `EpaintLayout`,
  and D15 stands.
- **E4 No wasm facade.** Web support stays in egui until the split is done;
  a Rust front end needs no `wasm-bindgen` edge. §7g's facade and §7h's OPFS
  store are deferred, not planned.
- **E5 `Errand` is `Effect`.** The thing a pure update returns for the host
  to perform is an effect in the Elm/Redux vocabulary; `Act::Effect(Effect)`,
  `Effect::named`, and the prose follow. The contract is unchanged: the
  registry names the effect and its target, the front end performs it and
  answers with the ordinary command the flow ends in.
- **E6 The UI holds the theme and tells the engine its palette by
  command.** The chrome is the front end's own and draws from its own
  theme; the drawing is painted in the palette the session was last told,
  `Action::SetPalette(Palette)`, sent as an ordinary event when the
  appearance changes and on the first frame. It is not an edit — no rev,
  no undo step — so a read-only session and one viewing a past rev take
  it. `View` still carries resolved colours (`Ground`, `EditColors`), and
  nothing in the kernel reads a palette from anywhere else. The role table
  and the canvas font sizes stay the engine's own, embedded; the dev
  editors that author them live tell the session through
  `Session::retune`, a host fact with no palette in it. The font family is
  the `TextLayout` the call is given (D15). Supersedes D16.
- **E7 The glass lag (D13) is retired as a non-issue.** It is invisible at
  frame rate under egui and does not exist for a front end that owns its
  layers.
- **E8 `serde` on everything that crosses the call**, last, over the shapes
  E1–E3 leave: `Event`, `View`, and every type they carry, down to the
  geometry leaves. Round-trip tests prove it.

## Order

1. E5 rename, E4/E7 in the docs.
2. E3 the editor and E2 the assets (independent seams; E3 first if serial).
3. E1 and E8 together: narrow the wire, then derive.
4. E6 the palette as a command, once decided.

Done 2026-09-13.
