Skip to content

HUD Architecture

This page explains how the HUD is structured internally. Read it before debugging a panel or adding a new one.

Data Flow

graph TD
    DB[(raise.db)] --> DM["HUDDataManager\n(data.py)"]
    DM -->|"missions, active_mission,\nmissions_detail"| MP[MissionsPanel]
    DM -->|"active_session, session_state,\nmission, signals, pipeline_runs"| SP[SessionPanel]
    DM -->|pipeline_runs| PP[PipelinePanel]
    DM -->|signals| EP[EventsPanel]
    DM -->|"session_index, skill_events, kpis"| IP[InsightsPanel]
    TIMER["Poll Timers\n(app.py)"] -->|"3s / 5s / 30s"| DM

raise.db is the only data source. No panel reads the database directly.

Module Responsibilities

Module Responsibility Does NOT
app.py Composes the TUI, owns keybindings, schedules poll timers Read from DB directly
data.py Single gateway to raise.db — all queries through HUDDataManager Import Textual
panels/missions.py Navigable mission list (left) + coordinator detail pane (right) Query DB
panels/session.py Active session state, mission progress bar, HITL gates Query DB
panels/pipeline.py Sortable DataTable of pipeline runs with phase chain Query DB
panels/events.py Signal console with filter bar and status line Query DB
panels/insights.py Delivery KPIs (velocity, cycle time, flow efficiency) and token health Query DB

Poll Cycle

Three independent intervals are set in app.py:79-81:

Interval Method What it refreshes Why
3 s _poll_signals New lifecycle signals Signals are bursty; Events tab needs near-real-time updates
5 s _poll_fast Active session, missions, pipeline runs Frequent enough to feel live without saturating SQLite
30 s _poll_slow Session index, KPIs, token health Expensive aggregations; trend data tolerates higher latency

action_refresh (key r) triggers all three immediately on demand.

Panel Contract

Every panel exposes a single update_data(**kwargs) method. HUDDataManager pushes data to each panel; the panel owns all rendering logic. Panels never pull data themselves.

Panel update_data keyword args
MissionsPanel missions: list[Mission], active_mission: Mission \| None, missions_detail: dict \| None
SessionPanel active_session: dict \| None, session_state: SessionState \| None, mission: Mission \| None, signals: list[dict], pipeline_runs: list[dict], last_closed_session: dict \| None
PipelinePanel pipeline_runs: list[dict]
EventsPanel signals: list[dict]
InsightsPanel session_index: list[dict], skill_events: list[dict], kpis: dict \| None

When adding a new panel, implement update_data(**kwargs) and register the call in app.py's _poll_fast or _poll_slow method depending on the data's refresh requirements.

What's Next

  • Extension Guide — step-by-step: Hello World panel → data-driven panel → poll-driven panel (coming in a future release)