본문으로 건너뛰기

구조

ADR-0003: Workspace/UI capabilities go through a request queue serviced by the frame loop

이 페이지는 영어입니다. 하나의 언어로 유지된 사양은 하나의 내용만 말합니다.

Status: accepted (2026-10-09)

Context#

Scripts need to read the workspace (charts, symbols, timeframes) and to change it. The state lives in App on the UI thread; egui state is not Sync and must not be touched from a worker. TTT already applies UI-originated actions inside App::ui and services each window's shared state once per frame.

Decision#

  • A host operation whose contract says execution: ui_thread is turned by the bridge into a UiRequest { op, args, reply } pushed on a channel; the worker then waits for the reply, checking cancellation and a timeout.
  • App::ui calls t0_host::Bridge::service_ui(&mut port) once per frame; the app implements the narrow UiPort trait (list charts, active chart, set a chart's symbol/timeframe). The bridge never sees App.
  • The worker asks for a repaint when it enqueues a request, because the terminal does not repaint while idle.
  • Components are addressed by semantic handles (ChartRef = { epoch, id }), never by pointers or widget instances. The epoch is chosen per terminal process; a handle from another process, or for a chart that has since closed, fails with a typed error that says which.

Alternatives#

  • Lock App state from the worker -- requires Arc<Mutex> around state the whole UI reads every frame; contention and deadlock risk in the hottest path of the application.
  • Expose the palette Command enum directly -- internal and unversioned; binding scripts to it would freeze it.

Consequences#

  • A UI operation costs at most one frame of latency plus the wait for the next repaint (normally milliseconds).
  • If the window is not being drawn (minimised and not repainting), UI requests time out with E-HOST-UI-TIMEOUT instead of hanging.

Amendment (2026-10-10): charts and panels#

Programs open, inspect and focus charts and the application's panels through the same queue (workspace 0.1.2). The port does what the terminal's own controls do and nothing else: open_chart uses the one function the terminal's "new chart" uses (terminal_app::t0::spawn_chart); open_panel opens only kinds from the terminal's own registry of panels a person can open (terminal_ui::openable_panels, the Workspace window's list). Panel handles are pane identities, stale once the pane closes. No program reaches a widget, an id it invented, or a private type.