Changelog¶
A consolidated timeline of releases across the four RaceLink repositories. Currently a stub — entries should be added as releases ship. Each entry should follow the template at the bottom of this file.
Source of truth. Each repository maintains its own GitHub releases page; this changelog is a curated cross-repo summary. When the cross-repo summary and a repository's release notes disagree, the repository's release notes win.
2026-05-04 — Sidebar group rows: live counts + flash¶
- RaceLink_Host (WebUI) — the sidebar's group list now shows
M / Nper row — devices currently online out of total devices in the group — with a hover tooltip explaining what "online" means in this context (replied to the last status query or sent an unsolicitedIDENTIFY_REPLYrecently). The number is computed client-side fromstate.devices'sonlineflag in a single pass; falls back to the server-sidedevice_countwhen the device list hasn't loaded yet on first render. - RaceLink_Host (WebUI) — group rows now flash the same
way the device-table rows do when any of their devices receives
data. Driven by the per-group max
last_seen_tssnapshot, with the same first-render-doesn't-flash semantics so a fresh page load doesn't strobe the sidebar. CSS@keyframes rl-row-flashis reused; the new rule is scoped to.rl-groups li. - Wire protocol — unchanged (UI-only change).
Notes:
loadDevices()now callsrenderGroups()alongsiderenderTable()so the sidebar tracks SSE refreshes the same way the device table does — no extra API calls.
2026-05-03 — Groups target picker: search dialog¶
- RaceLink_Host (WebUI) — the inline checkbox grid for the unified target picker's Groups mode is replaced by a compact summary chip + a modal selection dialog. The summary shows the selected groups in small text together with the total group count and total device count across the selection, so the operator can scan an action without opening the picker. Edit groups… opens a dialog with a search field (filters by name or id), a scrollable result list, and three batch buttons that act on the currently- visible hits: Select all hits, Deselect all hits, Invert hits. Designed for fleets with many groups where the previous flat checkbox row became unwieldy. The save- time broadcast-collapse hint moved into the dialog footer.
- Wire protocol — unchanged (UI-only change).
Notes:
- No on-disk migration required — scene format unchanged.
- No estimator / runner behaviour change; the dialog edits the
same
target.kind = "groups"shape the planner consumes.
2026-05-02 — Estimator ↔ runner structural sync¶
- RaceLink_Host — extracted a new pure module
racelink/services/dispatch_planner.pythat is now the single source of truth for "what packets would the runner emit for this action". Both the cost estimator and the scene runner consumeplan_action_dispatch(action, …) → ActionDispatchPlan{ops: List[WireOp], …}; the runner iteratesplan.opsand dispatches each via a small_dispatch_opadapter, the estimator iterates the same plan and sumsbody_bytesper op. Per-kind logic that used to live in two parallel implementations (_resolve_target/_resolve_offset_group_child_target/_send_with_fanout/_merge_flags_into_params/_lookup_rl_preset/_dispatch_offset_group_childon the runner;_target_packet_multiplier/_estimate_offset_group_cost/_materialize_rl_preset_params/_estimate_control_body_lenon the estimator) is now in one place. New parity test suite (tests/test_dispatch_parity.py) runs every action shape through (planner, estimator, runner-with-recording-stubs) and asserts identical packet counts + per-op senders + per-op addressing — any future drift is caught at CI time. - RaceLink_Host — bug fix: the API's
_known_group_ids_from_ctx()had a stray.controllerindirection that silently returned[], closing the optimizer's Strategy-C gate and making the cost badge under-report by reaching for Strategy B (per-group EXPLICIT) where the runtime actually emitted Strategy C (broadcast formula + sparse NONE overrides). Reproducer scene from the bug report — 7-of-10 sparse linearoffset_groupwith one broadcast child — pre-fix reported 8 packets / 121 B; post-fix correctly reports 5 packets matching the wire. - Wire protocol — unchanged.
WireOpwas extended with optionalsenderanddetailfields (additive, default- valued, back-compat). - Sync body sizing — incidental fix surfaced by the
unification: the estimator was sizing OPC_SYNC with
flags=0(4-byte legacy form). The runner has always senttrigger_armed=True(5-byte form). The planner now sizes withSYNC_FLAG_TRIGGER_ARMEDso the cost badge matches the wire.
Notes:
- No on-disk migration required — scene format unchanged.
- Operator-visible behaviour change: the cost badge in the scene editor is now accurate for sparse-subset offset_group containers. No other UI changes.
2026-05-01 — Broadcast / target-picker unification¶
- Docs — new Broadcast Ruleset page (full per-opcode rules across Host / Gateway / WLED) and a Roadmap page recording the two future-feature commitments (capability-agnostic broadcast addressing, group-agnostic re-identification). Glossary, scene-format, operator-guide, webui-guide, RH-plugin operator-setup, opcodes, and contributing all updated to the unified vocabulary.
- RaceLink_Host — unified
targetshape across every action:{kind: "broadcast"} | {kind: "groups", value: [...]} | {kind: "device", value: "<MAC>"}. The pre-unification shapes (scope, singulargroup, standalonegroupsfield onoffset_group) are migrated on read. Save-time canonicalisation collapses "every known group selected" →broadcastso the runtime / cost-estimator pair agrees on optimizer Strategy A. Scene-editor exposes the unified three-radio picker (Broadcast / Groups / Device) everywhere, replacing the previous mix of "Group/Device" radios + "All groups" checkbox + multi-select + "Scope (broadcast)" radio. Tests cover the migration shims and thedevice.groupId-pinned single-device emission rule. - Wire protocol —
PROTO_VER_MAJOR/MINORunchanged; this is a host + UI change only.
Notes:
- No on-disk migration step required — old persisted scenes load as-is and are rewritten on next save.
- Operator-visible behaviour change: top-level effect actions now offer a Broadcast option that wasn't there before. Selecting every known group in the Groups picker shows a hint that it will save as Broadcast (so a future-added group is also hit) — see operator-guide §"The target picker".
2026-04-30 — Documentation consolidation¶
- New: consolidated
RaceLink_Docscollection (this set). - No code or wire-protocol changes.
Unreleased / in progress¶
- (placeholder)
Template for new entries¶
## YYYY-MM-DD — <release name or component>
* **<Component>** vX.Y.Z — <one-line summary>
* <bullet of what changed>
* <bullet of what changed>
* **Wire protocol** — `PROTO_VER_MAJOR/MINOR = X.Y` (no change / +N)
Notes:
* <any cross-component coordination required>
* <any breaking change or migration step the operator must take>
Useful queries¶
GitHub releases per repository (manual links):
- https://github.com/PSi86/RaceLink_Host/releases
- https://github.com/PSi86/RaceLink_Gateway/releases
- https://github.com/PSi86/RaceLink_WLED/releases
- https://github.com/PSi86/RaceLink_RH-plugin/releases
The wire-protocol version pair lives in racelink_proto.h:
A drift in any of the three byte-identical copies fails
tests/test_proto_header_drift.py.