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
+18 -2
View File
@@ -1,5 +1,5 @@
import axios from 'axios';
import { OntimeReport, ShowRun, ShowRunSummary } from 'ontime-types';
import { OntimeReport, OpenRun, ShowRun, ShowRunSummary } from 'ontime-types';
import { ontimeQueryClient } from '../../common/queryClient';
import { REPORT, apiEntryUrl } from './constants';
@@ -45,7 +45,23 @@ export async function fetchRun(id: string, options?: RequestOptions): Promise<Sh
}
/**
* HTTP request to close the run in progress, writing it to history immediately
* HTTP request to fetch the run being recorded
* @returns null when no run is in progress
*/
export async function fetchOpenRun(options?: RequestOptions): Promise<OpenRun | null> {
try {
const res = await axios.get(`${reportUrl}/runs/open`, { signal: options?.signal });
return res.data;
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 404) {
return null;
}
throw error;
}
}
/**
* HTTP request to finish the run in progress, which is what writes it to history
*/
export async function finishRun(): Promise<void> {
await axios.post(`${reportUrl}/runs/finish`);