From 85e18db0edc4fded7e936ca4135ee2069665d555 Mon Sep 17 00:00:00 2001 From: larssand Date: Thu, 10 Sep 2026 23:00:31 +0200 Subject: [PATCH] Give scheduled "Practice" sessions the real start ceremony too A "practice" session used as a scheduled slot in a structured race/sponsor event's session plan silently skipped the entire countdown/lights/start-tone ceremony and jumped straight to running, unlike qualifying/heat/final. Narrowed the ceremony-skip check to only the standalone "Quick Open Practice" shortcut and free_practice (both rolling/best-lap ranked with no fixed roster) - "practice" is already ranked like a real timed session in every other respect and now gets the same start sequence. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 6 +++++ ROADMAP.md | 13 ++++++++++ package-lock.json | 4 ++-- package.json | 2 +- src/app.js | 10 ++++++-- tests/browser_race_day_start_sequences.js | 29 +++++++++++++++++++---- 6 files changed, 54 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f48de1..2b5bbcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ The application version is maintained in `package.json` and displayed in the adm No changes recorded yet. +## 1.16.1 - 2026-09-10 + +### Fixed + +- A scheduled "Practice" session inside a structured race/sponsor event (as opposed to the standalone Quick Open Practice shortcut) silently skipped the entire start ceremony - no countdown, no lights, no start tone - jumping straight to running instead of matching qualifying/heat/final. It now gets the same countdown/lights/start-tone sequence; only Quick Open Practice and free practice still start instantly. + ## 1.16.0 - 2026-09-10 ### Added diff --git a/ROADMAP.md b/ROADMAP.md index 2bacb7f..4a49b98 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -384,6 +384,19 @@ Found and fixed a real, previously-existing bug while building this, unrelated t Verified 2026-09-10: new unit tests cover `getSessionSortWeight`'s fix directly, and `buildEventSchedule`'s cascading math, the running-order sort, track-mode sessions being excluded, and the re-anchoring behavior for both a late-but-still-running session and a finished one whose real duration (including a pause) differed from the plan. A full headless-browser run against a real, isolated server reproduced the exact reported workflow: set a 12:34 start time with a 3-minute gap on a real Open Practice + Q1 H1, confirmed the schedule panel showed practice at 12:34-12:39 and qualifying at 12:42-12:47, confirmed the printed start list included "12:34" next to Open Practice, then made practice actually start 2 minutes late and confirmed the panel flagged it "Delayed" and qualifying's planned time shifted forward to 12:44 automatically. +## Scheduled "Practice" sessions silently skipped the start ceremony + +Reported 2026-09-10 by the user: starting a session in a sponsor event didn't count down like a normal race does. Investigation took a few rounds since the initial description ("says '10 seconds to start' and 'starting on the sound of the tone' but doesn't visibly count down") didn't match either of the two things actually tested: + +- A real headless-browser test of a sponsor-event heat (mass start, 10s countdown) showed the visible countdown ticking correctly (10, 9, 8...) - already working. +- A real test of a session with type "practice" (the user's own guess at the cause - "träning", Swedish for practice) showed the *opposite* of the original complaint: the entire ceremony, audio included, was skipped in ~25ms and the session jumped straight to "running" - not "audio plays but visual doesn't move", but "nothing at all happens, instantly." + +That second finding was real and worth fixing on its own even though it didn't match the original wording: confirmed with the user that a "Practice" session used as a scheduled slot in a structured race/sponsor event's session plan should get the same countdown/lights/start-tone ceremony as qualifying/heat/final - only the standalone "Quick Open Practice" shortcut (and "free_practice", both rolling/best-lap-ranked with no fixed roster) should still start instantly. + +- [x] `startSessionWithAudio` (`src/app.js`) narrowed its ceremony-skip check from `["open_practice", "free_practice", "practice"]` to just `["open_practice", "free_practice"]`. "practice" sessions (built by the Race Setup wizard's own practice-session step, already ranked like a real timed session rather than rolling/best-lap like the other two practice types) now run the full `runStartSequence`. + +Verified 2026-09-10: extended the existing permanent browser test (`tests/browser_race_day_start_sequences.js`) with a "practice"-type session run through the same full-ceremony assertions already used for qualification/final/team_race (real audio events logged, pause/resume/finish all work), plus a new explicit check that "open_practice" (the Quick Open Practice shortcut) still jumps straight to running with zero new audio events logged - locking in both sides of the distinction so this doesn't regress either direction. Full unit and browser regression suites re-run clean. + ## Documentation maintenance - [ ] Update the architecture's module/dependency inventory as modules change. diff --git a/package-lock.json b/package-lock.json index 2b61aaa..05a19fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rc-racecore", - "version": "1.16.0", + "version": "1.16.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rc-racecore", - "version": "1.16.0", + "version": "1.16.1", "hasInstallScript": true, "dependencies": { "better-sqlite3": "^12.11.1", diff --git a/package.json b/package.json index da34b49..095912b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rc-racecore", - "version": "1.16.0", + "version": "1.16.1", "private": true, "description": "RC RaceCore live timing and race control with AMMC ingest and SQLite persistence", "engines": { diff --git a/src/app.js b/src/app.js index fa36a22..07f8a86 100644 --- a/src/app.js +++ b/src/app.js @@ -199,11 +199,17 @@ const announceRaceClockEvent = (eventKey, session) => triggerRaceAudio(eventKey, const handleSessionTimerTick = () => handleSessionTimerTickHelper({ getActiveSession, isUntimedSession, getSessionTiming, saveState, getLastFinishAnnouncementSessionId: () => lastFinishAnnouncementSessionId, setLastFinishAnnouncementSessionId: (value) => { lastFinishAnnouncementSessionId = value; }, announceRaceFinished, announceRaceClockEvent }); const cancelRaceStart = () => raceAudioEngine.cancelStartSequence(); const startSessionWithAudio = (session, callbacks) => { - const practice = ["open_practice", "free_practice", "practice"].includes(String(session?.type || "")); + // Only the casual, come-and-go practice modes (open_practice - the standalone "Quick + // Open Practice" shortcut - and free_practice, both rolling/best-lap ranked with no + // fixed roster) skip the race-start ceremony. "practice" is a proper scheduled slot in + // a structured event's session plan (built by the Race Setup wizard alongside + // qualifying/finals, and already ranked like a real timed session, not rolling) and + // gets the same countdown/lights/start-tone ceremony as qualifying/heat/final. + const skipCeremony = ["open_practice", "free_practice"].includes(String(session?.type || "")); const staggerOrder = getSessionGridOrder(session) .map((driverId) => state.drivers.find((driver) => driver.id === driverId)?.transponder) .filter(Boolean); - return raceAudioEngine.runStartSequence(session, { ...callbacks, skipAudio: practice, staggerOrder }); + return raceAudioEngine.runStartSequence(session, { ...callbacks, skipAudio: skipCeremony, staggerOrder }); }; const tickClock = () => tickClockHelper({ dom, currentLanguage, handleSessionTimerTick, getActiveSession, getCurrentView: () => currentView, getOverlayMode: () => overlayMode, renderView, refreshTimingDynamics, updateHeaderState }); const buildTeamRaceResultsHtml = (event) => buildTeamRaceResultsHtmlHelper(event, { t, escapeHtml, resolveEventBranding, getClassName, buildPrintBrandBlock, buildTeamRaceStandings, renderTable, formatLap, renderTeamStintLog, renderSessionChannelAnalysis }); diff --git a/tests/browser_race_day_start_sequences.js b/tests/browser_race_day_start_sequences.js index a2ee897..6b12e0c 100644 --- a/tests/browser_race_day_start_sequences.js +++ b/tests/browser_race_day_start_sequences.js @@ -2,11 +2,13 @@ import assert from "node:assert/strict"; import { startFakeDecoder } from "./browser_fake_decoder.js"; import { RACE_AUDIO_EVENT_DEFINITIONS } from "../src/race_audio.js"; -// Practice types skip the countdown/light/audio sequence entirely (see -// startSessionWithAudio in src/app.js) - already covered by browser_race_day.js. -// heat/qualification/final/team_race all run the real sequence in src/race_audio.js's -// runStartSequence, gating the session's transition to "running" until it completes - -// this file exercises that for each of those four types, plus pause/resume/finish. +// Only the casual, come-and-go practice modes (open_practice - the standalone "Quick Open +// Practice" shortcut - and free_practice) skip the countdown/light/audio sequence entirely +// (see startSessionWithAudio in src/app.js). "practice" is a proper scheduled slot in a +// structured event's session plan and, like heat/qualification/final/team_race, runs the +// real sequence in src/race_audio.js's runStartSequence, gating the session's transition +// to "running" until it completes - this file exercises that for each of those five types, +// plus pause/resume/finish, and separately confirms open_practice still skips it. // // Every effect/voice is silenced via raceAudio.events so the sequence's own audio // playback (real