feat(report): persistent show run history

Turns the reporter from a live-only, single-run curiosity into a
persistent, per-project, per-rundown history of runs.

- Extend OntimeEventReport with a schedule snapshot (scheduledStart,
  scheduledDuration) taken when an event starts, plus playCount. Reports
  are now a record that survives later rundown edits, rather than a live
  join against the current rundown.
- Add a run lifecycle: a run opens on the first event start and closes on
  a full stop, archiving the previous run to history so the next start
  begins fresh.
- Persist runs to a per-project sidecar file (services/report-service),
  patterned on the existing restore-service, so a crash or restart no
  longer loses the whole show's record. Runs are scoped by rundownId for
  multi-rundown projects, cascade-deleted with their rundown or project,
  renamed alongside a project rename, and deliberately not copied when a
  project is duplicated.
- Extract the over/under/on-time variance and run summary maths shared by
  the rundown chip, the report settings panel, and the server into
  ontime-utils (getEventVariance, getRunSummary, countPlannedEvents).
- Extend the report API with run history endpoints (list, get, latest,
  rename, delete) while keeping GET /report's existing shape so Companion
  and HTTP automations are unaffected.
- Replace the settings report table with a run browser: a list of runs
  with a rundown filter, inline rename, delete, and a detail view with
  per-run summary stats and CSV export.
- Add a third, muted state to the rundown event chip: an event with
  nothing in the current run yet previews how it went last time.

Fixes a real bug found while testing the new store: emptyStore() was a
shared object, so its runs array leaked mutations across project loads.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nr3FbLbM8gB8Jm771YgTV
This commit is contained in:
Claude
2026-08-08 11:08:28 +00:00
parent fb559ed9da
commit 3c2875a956
32 changed files with 2144 additions and 172 deletions
@@ -1,8 +1,58 @@
import type { MaybeNumber } from '../../utils/utils.type.js';
import type { EntryId } from './OntimeEntry.js';
export type OntimeEventReport = {
startedAt: MaybeNumber;
endedAt: MaybeNumber;
/**
* Snapshot of the schedule taken when the event ran.
* Keeping a copy is what makes a report a record: editing the rundown
* afterwards no longer rewrites history.
*/
scheduledStart: number;
scheduledDuration: number;
/** how many times the event was started within this run, >1 means it was re-run */
playCount: number;
};
export type OntimeReport = Record<string, OntimeEventReport>;
export type OntimeReport = Record<EntryId, OntimeEventReport>;
export type RunSummary = {
/** events which produced a report entry */
eventsRun: number;
/** playable events in the rundown at the time the summary was made */
eventsPlanned: number;
scheduledDuration: number;
actualDuration: number;
/** actualDuration - scheduledDuration, signed */
drift: number;
eventsOver: number;
eventsUnder: number;
eventsOnTime: number;
/** largest single overrun, answers "what blew the schedule" */
worstOverrun: { id: EntryId; delta: number } | null;
};
export type ShowRun = {
id: string;
rundownId: string;
/**
* Denormalised so a run stays readable after its rundown
* is renamed or deleted.
*/
rundownTitle: string;
/** user editable, defaults to a formatted timestamp */
label: string;
startedAt: number;
endedAt: MaybeNumber;
report: OntimeReport;
summary: RunSummary;
};
/** A run without its per event data, for list views */
export type ShowRunSummary = Omit<ShowRun, 'report'>;
/** Contents of a project's report sidecar file */
export type ProjectReports = {
runs: ShowRun[];
};
+8 -1
View File
@@ -24,7 +24,14 @@ export { TimerType } from './definitions/TimerType.type.js';
export type { Day, Duration, Instant, TimeOfDay } from './definitions/core/Temporal.js';
// ---> Report
export type { OntimeReport, OntimeEventReport } from './definitions/core/Report.type.js';
export type {
OntimeReport,
OntimeEventReport,
ProjectReports,
RunSummary,
ShowRun,
ShowRunSummary,
} from './definitions/core/Report.type.js';
// ---> Automations
export { ontimeActionKeyValues } from './definitions/core/Automation.type.js';