Skip to content

Architecture

Three kinds of process. The window draws and never waits. A supervisor owns everything slow. Plugins and services run in supervised worker processes and reach the outside world only through calls the supervisor is willing to answer.

┌──────────────────────────────────────────────────────────────┐
│ UI process — Qt. Draws. Never blocks.                        │
│   shell · workflow view · episode editor · command palette   │
└───────────────┬──────────────────────────────▲───────────────┘
                │ asks                         │ progress, results
┌───────────────▼──────────────────────────────┴───────────────┐
│ Supervisor — owns every long-running thing                   │
│   task registry · capability gate · show store (one writer)  │
└───────────────────────────┬──────────────────────────────────┘
                            │ every call a plugin makes
┌───────────────────────────▼──────────────────────────────────┐
│ Workers — one per plugin, one per job. Killable.             │
│   RSS import · transcription · audio & mixdown · model calls │
└──────────────────────────────────────────────────────────────┘

Components

Component Lives in Responsibility
Shell UI Window, sidebar, header bar, navigation
Workflow view UI Draws activities and their states; opens steps
Episode editor UI Tracks, chapters, transcript; one widget tree, keyboard-first
Command registry UI Every action by id; the palette and menus are views of it
Task registry Supervisor Starts, reports, cancels; the Tasks screen is its mirror
Capability gate Supervisor Answers a plugin's calls only where manifest and user agree
Show store Supervisor The only writer to the show file
Workflow engine Supervisor Activities, workflows, completion; pure logic, no Qt
Plugin host Worker One process per active plugin; dies alone
Services Worker Transcription, audio, model calls — reached like any capability

Why it cannot hang

Four rules, enforced by the structure rather than by everyone's discipline:

  1. The UI process contains no plugin code and makes no network call.
  2. Anything that might take longer than a frame is a task: it has an id, reports progress and can be cancelled. The Tasks screen is the task registry drawn.
  3. The show file has exactly one writer, so the window never waits on a lock.
  4. A worker that stops answering is killed and reported. Its plugin is marked unhealthy; the window does not notice.

The one exception, named. The editor's waveform drawing and the player's audio callback run in the UI process because they must. They touch no file and no network — they read decoded samples a worker prepared. Everything else slow is outside.

The boundary is a protocol

Workers speak to the supervisor over a line-delimited message protocol on a pipe, not by sharing Python objects. That costs a serialisation step and buys three things: a plugin crash cannot corrupt the application's memory, a plugin can be killed at any instant, and a plugin need not be written in Python.