Docs / Developer / Renderer policy (shell vanilla, per-app freedom, chat surfaces re-evaluated at spec time)
Renderer policy (shell vanilla, per-app freedom, chat surfaces re-evaluated at spec time)
Status: accepted · 2026-07-05
Context owners: the shell (src/ui/), every app folder, the future Table Read and agent surfaces
Context
The shell and the current surfaces (Library, Workbench editor, Press, Settings, setup) are
hand-built vanilla TS + DOM: no framework, five runtime dependencies, one compiled exe
(ADR-001, ADR-003). The roadmap ALSO promises chat-shaped surfaces: the M4 Table Read
(a conversational interview engine) and the agent system (packages/agent, REPL, test chat),
with a Studio rendering surface later (M6). Chat runtimes are the classic React-shaped
problem (streaming state, live lists), and comparable ecosystem apps (RoleCall, Marinara
Engine) all render chat with React 19.
Two facts keep this from being a stack war:
- The Table Read spec locks its engine as UI-agnostic: "zero rendering code," one event
protocol driven identically by the CLI and the Studio app. The agent packages follow the
same rule (docs/02-ARCHITECTURE: the studio may not own private engine capabilities).
Renderer choice therefore never touches engine design. - The app contract (
manifest+mount(ctx)) is renderer-agnostic. An app folder may
bundle its own rendering library internally; the shell neither knows nor cares.
Decision
- The SHELL and the editor/shelf-class surfaces stay vanilla TS + DOM. Their state
complexity is form/card-shaped and does not justify a framework runtime. - Every app folder has RENDERER FREEDOM: bundling a framework inside one app is an add,
not a rewrite, and requires no shell change. - When the Table Read surface and the agent/chat surface reach implementation, their spec
MUST include a renderer assessment against these criteria, with React explicitly on the
table for that surface:- sustained streaming updates across many live regions at once,
- long virtualized lists (message history at scale),
- deep interactive state webs (many components reacting to shared live state),
- concrete component-ecosystem needs (e.g. an editor widget that would be vendored).
If two or more criteria hold, the default for THAT surface is React (Bun transpiles TSX
natively; the bundle bakes into the exe like any app). If not, vanilla, with the event
protocol keeping a later swap cheap.
- Engines never acquire a renderer dependency under any outcome.
Consequences
- No restart scenario exists: every layer built to date (converter, canonical model,
engines, shell, editors, tokens/CSS) serves both renderer outcomes unchanged. - The repo may eventually carry two rendering idioms, isolated per app folder. That cost
is accepted and bounded by folders-as-schema. - This ADR is the standing answer to "should Studios be React": the question is only ever
asked per-surface, with written criteria, at the moment the surface is specced.