feat(phase 4.2): switcher latency baseline on prod OBS host — 32× headroom #29
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/phase-4.2-latency-baseline"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes the G4.2 SLO gate — the dress-rehearsal latency-acceptance check that the plan was treating as a partial / unverified gate.
Measured on the real Windows OBS host (bridge) at 2026-05-21T23:59Z:
30/30 samples, 0 timeouts. SLO p95 ≤ 2000 ms warm: PASS — 32× headroom.
Methodology
Three priority methods were spelled out in
docs/plugin-contract.mdafter the original Phase 0.5.2 baseline was found to be measuring WebSocket RTT instead of real switch latency. This PR uses the third (and turns out to be the only viable) one: screenshot-hash polling.Why event-driven detection (priority 1, the original hope) was rejected:
scripts/discoverSwitcherEvents.mjssubscribed to ~50 event types and captured zero events across a 6 s window after a known-good file write.WSPRlogging enabled) shows zeroop: 5messages in the request/response stream during a switch.ss_largeis an input (source_switcherkind), not a scene — soGetSceneItemList/SceneItemEnableStateChangeddon't apply at all.settings.current_indexdoes not update at runtime.Conclusion: the plugin switches silently. Screenshot-hash polling is the only ground-truth signal.
Interpretation
The plugin's
current_source_file_interval: 1000(ms) is not the actual poll cadence — true latency is ~60 ms. That value must throttle some other internal operation (likely a file-stat coalescing window).The 50 ms screenshot poll interval is now the limiting factor in the measurement, not the plugin. True switch latency could be as low as 10–20 ms.
Files
scripts/measureSwitcherLatencyV2.mjs— the real measurement script (plain Node ESM, runs on bridge without tsx)scripts/measureSwitcherLatencyV2.package.json— npm-install template to ship alongside the scriptscripts/{discoverSwitcherEvents,probeSwitcherMechanism,dumpInputState}.mjs— diagnostics kept in-repo as the audit trail for the methodology decisiondocs/plugin-contract.md— appended Phase 4.2 baseline section with results, interpretation, caveats, and re-run instructionsdocs/phase42-latency-baseline-win.json— full sample dataSafety
large.txtwas saved at startup and restored on exit; all 30 measurements alternated between two non-original plugin source values (jellyfish_palpatine_stream↔jellyfish_dellgate_stream). No production state was left mutated.Test plan
tsc --noEmitcleannpm run audit:sqlite-openscleanCloses the G4.2 SLO gate. Measurement target was p95 ≤ 2000 ms warm; real measurement on bridge (Windows production OBS host) shows: p50=62 p95=63 p99=374 min=60 max=374 mean=72 Samples: 30/30 (0 timeouts) SLO PASS — 32× headroom The plugin's documented current_source_file_interval=1000 (ms) is not the actual poll cadence — true switch latency is ~60 ms, so that value must throttle some other internal operation (likely a file-stat coalescing window). Methodology: screenshot-hash polling (priority 3 in plugin-contract.md) turned out to be the only ground-truth signal available. The event-driven approach (priority 1) was investigated and rejected --- the plugin emits ZERO observable OBS-WebSocket events for switches, verified two ways: - discoverSwitcherEvents.mjs subscribed to ~50 event types, captured 0 - OBS debug-WS log (with WSPR logging enabled) shows 0 op:5 messages in the request/response stream during a switch. Also, ss_large is an *input* (custom obs source kind = source_switcher), not a scene — so GetSceneItemList / SceneItemEnableStateChanged don't apply at all. settings.current_index does not update at runtime either. Files: - scripts/measureSwitcherLatencyV2.mjs — the real measurement (ESM, plain Node, runs on bridge without tsx) - scripts/measureSwitcherLatencyV2.package.json — npm-install template to ship alongside the script onto bridge - scripts/{discoverSwitcherEvents,probeSwitcherMechanism,dumpInputState}.mjs — diagnostics kept in-repo as the audit trail for the methodology decision - docs/plugin-contract.md — appended Phase 4.2 baseline section with results, interpretation, caveats, and re-run instructions - docs/phase42-latency-baseline-win.json — full sample data Safety contract held: large.txt was saved at startup and restored on exit; all 30 measurements alternated between two non-original plugin source values. 220/220 jest tests pass; type-check + audit:sqlite-opens clean.