Timers 1.5.0: the LTC capture tool installs itself; tiles warn in one line #27

Merged
michael merged 1 commits from claude/timers-capture-install into main 2026-09-27 10:08:30 -04:00
Owner

LTC capture tool installs as part of install/update

A catalog install ships source only (the binary is gitignored), and a catalog update deletes the module folder — any previously built ltc-capture with it. So every install or update left LTC dead until someone compiled the tool by hand. Worse, the existing fallback build was the blocking spawnSync that once froze the whole server (the bug spl-meter fixed in 0.4.0), and it only ran when a listener started — no tool meant no device list, and no device meant no listener would ever build it.

ensureCaptureBinary() (module scope in ltc-listener.js, following the spl-meter 0.4.2 pattern) now builds the tool in the background as part of coming up:

  • kicked the moment the LTC source starts (covers module update on a machine with LTC already configured) and by the admin device picker's /ltc/devices (covers first-time setup before any device is picked),
  • single-flight, never on the event loop, 5-minute give-up,
  • built to a temp name and renamed, so a killed or crashed build can't leave a truncated file that passes the exists() check,
  • a binary older than its source (a module update over an existing folder) keeps serving while the replacement builds behind it and swaps in atomically,
  • the listener that was waiting starts itself the moment the build lands.

Terse tile warnings

Dashboard-side warnings are now one line: "Not configured — see Admin", "Unreachable", "Capture tool building…", "Channel not on this device — see Admin" — and the client's source-label prefix is no longer repeated inside the message ("ProPresenter: Unreachable", not "ProPresenter: ProPresenter unreachable"). The full diagnostics — host:port and errno, the capture tool's own stderr, build instructions — moved to the admin health line and the server log, where the person who can act on them is looking. Listener updates carry both tiers (error short, detail full); each source's health() reports the detail while its emitted tile message stays short.

Verified

  • From the exact catalog-install state (source only, no binary): build kicks in the background, single-flight confirmed, onReady fires, and the built tool lists the dev Mac's audio devices.
  • touch ltc-capture.swift → "older than its source" rebuild runs while the old binary keeps serving (ready: true throughout), then swaps atomically.
  • /ltc/devices kicks the build when the tool is absent and answers with the real device list once it exists.
  • Live server (PP at a dead port, LTC on with no device): tile sources say Unreachable / Not configured — see Admin; /api/admin/state health says ProPresenter: Unreachable at 127.0.0.1:1 — fetch failed · LTC: LTC timecode is on but no audio device is selected — pick one in Admin · …. Screenshot-checked the tile: card note and warning strip both read clean.
  • ltc.selftest.js and phase.selftest.js still pass. Module 1.5.0, no shell changes.

🤖 Generated with Claude Code

## LTC capture tool installs as part of install/update A catalog install ships source only (the binary is gitignored), and a catalog **update deletes the module folder** — any previously built `ltc-capture` with it. So every install or update left LTC dead until someone compiled the tool by hand. Worse, the existing fallback build was the blocking `spawnSync` that once froze the whole server (the bug spl-meter fixed in 0.4.0), and it only ran when a listener started — no tool meant no device list, and no device meant no listener would ever build it. `ensureCaptureBinary()` (module scope in `ltc-listener.js`, following the spl-meter 0.4.2 pattern) now builds the tool in the background as part of coming up: - kicked the moment the LTC source starts (covers module update on a machine with LTC already configured) **and** by the admin device picker's `/ltc/devices` (covers first-time setup before any device is picked), - single-flight, never on the event loop, 5-minute give-up, - built to a temp name and renamed, so a killed or crashed build can't leave a truncated file that passes the exists() check, - a binary older than its source (a module update over an existing folder) keeps serving while the replacement builds behind it and swaps in atomically, - the listener that was waiting starts itself the moment the build lands. ## Terse tile warnings Dashboard-side warnings are now one line: **"Not configured — see Admin"**, **"Unreachable"**, **"Capture tool building…"**, **"Channel not on this device — see Admin"** — and the client's source-label prefix is no longer repeated inside the message ("ProPresenter: Unreachable", not "ProPresenter: ProPresenter unreachable"). The full diagnostics — host:port and errno, the capture tool's own stderr, build instructions — moved to the **admin health line** and the server log, where the person who can act on them is looking. Listener updates carry both tiers (`error` short, `detail` full); each source's `health()` reports the detail while its emitted tile message stays short. ## Verified - From the exact catalog-install state (source only, no binary): build kicks in the background, single-flight confirmed, `onReady` fires, and the built tool lists the dev Mac's audio devices. - `touch ltc-capture.swift` → "older than its source" rebuild runs while the old binary keeps serving (`ready: true` throughout), then swaps atomically. - `/ltc/devices` kicks the build when the tool is absent and answers with the real device list once it exists. - Live server (PP at a dead port, LTC on with no device): tile sources say `Unreachable` / `Not configured — see Admin`; `/api/admin/state` health says `ProPresenter: Unreachable at 127.0.0.1:1 — fetch failed · LTC: LTC timecode is on but no audio device is selected — pick one in Admin · …`. Screenshot-checked the tile: card note and warning strip both read clean. - `ltc.selftest.js` and `phase.selftest.js` still pass. Module 1.5.0, no shell changes. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
michael added 1 commit 2026-09-27 09:47:36 -04:00
A catalog install ships source only, and a catalog update deletes the
module folder — a previously built ltc-capture binary with it — so every
install or update left LTC dead until someone compiled the tool by hand.
The build also still used the blocking spawnSync that once froze the whole
server (the bug spl-meter fixed in 0.4.0), and only ran when a listener
started: no tool meant no device list, and no device meant no listener.

ensureCaptureBinary() (module scope in ltc-listener.js, the spl-meter
0.4.2 pattern) now builds the tool in the background as part of coming
up: kicked when the LTC source starts and by the admin device picker,
single-flight, built to a temp name and renamed so a killed build can't
leave a truncated binary, and rebuilt behind a still-serving binary when
a module update leaves the source newer. The waiting listener starts
itself the moment the build lands.

Tile-side warnings are now one line — 'Not configured — see Admin',
'Unreachable', 'Capture tool building…' — with the client's source-label
prefix no longer repeated. The full diagnostics (host:port, errno, the
capture tool's own words, build instructions) move to the admin health
line and the log, where the person who can act on them is looking.

Verified: build from the exact catalog-install state (source only) lands
and lists 12 real devices; touching the source triggers an atomic rebuild
while the old binary keeps serving; /ltc/devices kicks the build and
answers with the device list; live server shows terse source messages on
the tile and the detailed story in /api/admin/state health. ltc and phase
selftests still pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
michael merged commit 21519dd3da into main 2026-09-27 10:08:30 -04:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: michael/ProdDash#27