Skip to content

Code-Review-Umsetzung + Engineer-Bugfixes (v0.2.7) - #1

Merged
Swizzjack merged 10 commits into
mainfrom
codereview
Jun 14, 2026
Merged

Swizzjack merged 10 commits into
mainfrom
codereview

Conversation

@Swizzjack

Copy link
Copy Markdown
Owner

Bridge correctness (reader.rs from_le_bytes, ZIP slip protection, WS handler survives closed channels, non-blocking engineer installs, BRIDGE_KEEP_ALIVE fix), Perf (boxed ServerMessages, throttled download progress, widgets via React.memo → 4 Hz instead of 60 Hz), refactors (sector_tracker step_car, Rule-Trait &mut self, ESLint 0 errors, uplot removed), CI workflow.

Plus engineer bug fixes: Lap time latch, SessionLapsRemainEx, Live Gap with lap wrap, StablePositionTracker.

Swizzjack and others added 10 commits June 11, 2026 19:02
…ineer task

- reader: decode frame values via from_le_bytes instead of casting &[u8]
  to &[T] (unaligned reads were UB; Vec<u8> guarantees no alignment)
- piper install: zip-slip protection via enclosed_name(), throttle
  download progress broadcasts to 250ms intervals
- ws handler: a closed watch/broadcast channel disables that branch
  instead of killing the connection (fixes reconnect loop on non-Windows
  and removes the need for the update task's pending() keep-alive hack)
- engineer task: piper/voice installs run in their own task instead of
  blocking the select loop; state backlog collapses to the newest
  snapshot so rules never fire on stale data in bursts
- tts engine: drop dead keep-alive fields (piper_process was never
  spawned persistently); document per-request spawn trade-off
- flags: per-car blue/debris bits corrected to official irsdk_defines.h
  values (0x0020/0x0040; previous 0x0400/0x0800 are greenHeld/tenToGo)
  — needs a live-session sanity check
- protocol: box Telemetry/SessionInfo variants (enum was ~720B by value)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… allocations

- standings: drop the incident-source diagnostic block (built ~64
  format! strings per 4 Hz tick guarded by two global mutexes)
- snapshot: tire var names as literal arrays instead of per-frame
  format!() — the 60 Hz build path is now allocation-free per lookup
- delete events.rs (todo!() stub, never called) and broadcast.rs
  (placeholder enum), drop unused Config::{telemetry_hz,standings_hz}
- scope #![allow(dead_code)] to non-Windows so the shipped Windows
  build keeps real dead-code analysis
- dedupe track_key derivation (track_recorder + track_map)
- clippy: is_some_and/sort_by_key/needless-ref fixes, ?-operator

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…le trait

- sector_tracker: extract step_car() — update() and the unit tests now
  drive the identical production code path (the test helper used to
  mirror ~80 lines of logic that could silently diverge)
- Rule trait: evaluate(&mut self) — rules are owned exclusively by the
  dispatcher inside one task, so the AtomicU32/AtomicBool interior-
  mutability workaround in the temp-change rules becomes plain fields;
  Sync bound dropped
- engineer state: wire last_sector_deltas from the standings sector data
  (current lap vs personal best) — SectorDeltaRule could never fire
  before because the array was hardcoded to [None; 3]

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- registry: memoize all widgets at the single render boundary — App
  re-renders per telemetry frame, but standings/track-map/session-info
  widgets only consume 4-15 Hz snapshots; with stable prop identities
  memo() skips them entirely
- App: useMemo'd WidgetData object, useCallback'd onDeleteTrackMap;
  engineer-settings updates compute outside the setState updater
  (StrictMode runs updaters twice — side effects were doubled)
- Dashboard: memoize the GridLayout layout array (was rebuilt per frame)
- extract sofFromRatings() — the SoF formula lived in both Standings
  and SoF; translate leftover German UI string

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- useFuelTracker/useLapHistory: frame processing moves into effects;
  refs are bookkeeping only, render-visible results live in useState and
  commit only on real events (lap completed, session change, pit exit) —
  no per-frame renders, React-Compiler-compatible
- TrackMap: settings/interp/data refs are now written in effects, not
  during render
- Wind: previous-render angle unwrapping via the documented guarded
  setState-in-render pattern instead of ref mutation
- SettingsPanel: voice list derived from props + optimistic overrides
  (replaces the prop→state mirror effect)
- TrackMapSettings: confirm-delete armed state derived from hasMap
- eslint: disable react-refresh/only-export-components (HMR granularity
  only; widgets co-locate settings constants by design); fix no-empty/
  unused-vars
- drop unused uplot dependency

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- the scoped dead-code allow exposed real dead code on the shipped
  target: RADIO_CHECK_PHRASE (frontend has its own copy), var_index(),
  EngineerStateTx alias, unused CommandExt import (tokio's Command has
  creation_flags natively), never-constructed ServerMessage variants
  SdkStatus/Disconnected (removed from protocol + frontend cases)
- placeholder surfaces (EngineerState/FlagState/DamageState fields,
  PitState::InBox, forecast minutes, LapCompletion) get documented
  targeted allows instead of a crate-wide one
- var_header: string slicing now uses the IRSDK_MAX_* layout constants
- CI: single sequential workflow — cargo test (exports gitignored
  shared/ ts-rs bindings) → clippy -D warnings → npm ci/lint/build

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…down signal

With BRIDGE_KEEP_ALIVE=1 run_watcher returns immediately, dropping the
oneshot sender. serve() treated the resulting RecvError like a shutdown
request and exited at startup. An Err from shutdown_rx now means 'no
watcher → never shut down'. Verified with a live server: three WS
connect/disconnect cycles, HTTP stays up throughout.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…itions

Four related bugs, all caused by taking iRacing telemetry at face value:

- Lap times announced one lap late: iRacing publishes LapLastLapTime
  ~1-2 s after the Lap counter increments, so latching the value at the
  increment tick recorded the previous lap's time. The aggregator now
  arms a pending latch on the increment and records once the value
  actually changes (10 s timeout covers identical consecutive laps).
  Lap-gated pace rules key off the new lap_just_completed flag instead
  of comparing lap counters, so they evaluate with the fresh lap time.
  Joining mid-session no longer records a stale lap as personal best.

- Final-lap call shifted by one lap: SessionLapsRemain is deprecated
  and off by one; prefer SessionLapsRemainEx with fallback.

- Gap callouts repeated the start/finish-line gap: CarIdxF2Time only
  refreshes at the line during races. Standings now carry CarIdxEstTime
  (backend-only, not serialized) and live_gap() derives the current gap
  with lap-wrap correction; F2Time remains as fallback. The trend
  ("closing"/"pulling away") now compares against the gap at the last
  announcement instead of the 100 ms previous tick, which always read
  "holding steady".

- Wrong position announced (P14 while running P15): live position
  flickers during side-by-side running and TTS adds seconds of latency.
  Position changes are now debounced for 3 s before announcing, and the
  tracker resets outside the Racing phase so formation-lap shuffles and
  session changes stay silent.

Adds unit tests for the lap-time latch and live-gap math.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Swizzjack
Swizzjack merged commit a877831 into main Jun 14, 2026
1 check passed
@Swizzjack
Swizzjack deleted the codereview branch June 14, 2026 05:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant