Skip to content

Persist token stats and runtime across Symphony restarts #22

@corylanou

Description

@corylanou

Symphony's token usage stats (input/output/total per Codex session, plus the codex_totals aggregate) currently live only in the running orchestrator's in-memory state. When Symphony restarts — which happens often during dogfooding — all token history resets to zero. There's no way to see cumulative cost over a day, week, or month, only "since boot."

What to build

Persist token stats across restarts and surface them in the dashboard with at least:

  • Cumulative totals across all Symphony runs (lifetime input/output/total tokens, total Codex runtime seconds).
  • Per-issue token history: each completed Codex session writes a record (issue id + identifier, started_at, completed_at, turns, input/output/total tokens, runtime_seconds, final state, model used).
  • Per-restart aggregate: each Symphony boot writes a "run" record (started_at, stopped_at, restart reason if known, peak concurrent agents, sessions launched, tokens used during that run). Useful for spotting expensive restarts.

Storage

A small embedded SQLite database under ~/.local/share/symphony/stats.db (or whatever the workspace root sibling is) is enough. Schema sketch:

CREATE TABLE symphony_runs(id INTEGER PRIMARY KEY, started_at TEXT, stopped_at TEXT, …);
CREATE TABLE codex_sessions(id INTEGER PRIMARY KEY, run_id INTEGER, issue_id TEXT, identifier TEXT, started_at TEXT, completed_at TEXT, turns INTEGER, input_tokens INTEGER, output_tokens INTEGER, total_tokens INTEGER, runtime_seconds INTEGER, final_state TEXT, model TEXT);

Append-only writes; no migrations across schema changes — just version the DB file and write a new one.

Dashboard additions

  • New "Lifetime totals" card on the hero metrics row (tokens + runtime + sessions across all runs).
  • New "Recent sessions" section listing the last 20 completed Codex sessions with their token + runtime numbers.
  • Existing "Runtime" metric shows the active run; add a tooltip or sub-line "this run / all time" so the cumulative number is visible.

Acceptance

  • Tokens and runtime persist across Ctrl-C + restart.
  • Dashboard shows cumulative totals alongside current-run totals.
  • The stats DB is portable: deleting it resets history without breaking Symphony.
  • Existing /api/v1/state endpoint stays backward-compatible; new lifetime data is exposed on a new endpoint or as an additive field.
  • 100% coverage gate still passes; the SQLite layer goes through a small testable interface module (the raw connection stays in ignore_modules like other HTTP/external shells).

Why now

Surfaced during dogfooding when the user restarted Symphony to pick up new workflow + favicon changes and noticed the token totals on the dashboard went back to zero. Hard to reason about spend without persistence.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions