refactor(report): a run becomes a report only when finished

Progress is kept in memory while a show runs and written once, as a
complete report, when the operator finishes the run. The stored history
therefore never contains a partial run.

- Nothing writes on the cue path. triggerReportEntry only updates memory,
  so the sidecar is untouched between the first event and finish.
- Removes the write coalescing added for the previous model: with a
  single write per run there is nothing to debounce, so the timer, the
  dirty flag, the flush export and the shutdown hook all go, and app.ts
  returns to its previous state.
- ShowRun.endedAt is no longer nullable. A stored run is finished by
  definition, so recovery of runs left open by a crash is gone along with
  the concept, and the run list drops its ongoing state.
- Adds GET /report/runs/open for the editor indicator. /runs now means
  finished reports only, which is also the contract Cloud sees.
- A run whose rundown was deleted before it finished now reports the
  events that actually ran as its planned count, rather than zero.

Known gap: RestorePoint carries playback state only, so a crash mid show
loses the in progress report. The stored history is unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019nr3FbLbM8gB8Jm771YgTV
This commit is contained in:
Claude
2026-08-08 21:35:52 +00:00
parent faca1a1c76
commit b21385ec4a
13 changed files with 152 additions and 388 deletions
@@ -49,10 +49,11 @@ export type ShowRun = {
*/
startedAt: number;
/**
* Time of day the last event in the run finished, or null while the run is
* open. Only ever compared against the per event times in the same run.
* Wall clock instant the run was finished.
* Always set: a run only becomes a report once it has been finished, so
* there is no such thing as a stored run still in progress.
*/
endedAt: MaybeNumber;
endedAt: number;
report: OntimeReport;
summary: RunSummary;
};
@@ -60,6 +61,13 @@ export type ShowRun = {
/** A run without its per event data, for list views */
export type ShowRunSummary = Omit<ShowRun, 'report'>;
/**
* The run currently being recorded.
* Lives in memory only until it is finished, so it carries no report data
* and has no end.
*/
export type OpenRun = Omit<ShowRun, 'report' | 'summary' | 'endedAt'>;
/** Contents of a project's report sidecar file */
export type ProjectReports = {
runs: ShowRun[];
+1
View File
@@ -27,6 +27,7 @@ export type { Day, Duration, Instant, TimeOfDay } from './definitions/core/Tempo
export type {
OntimeReport,
OntimeEventReport,
OpenRun,
ProjectReports,
RunSummary,
ShowRun,