feat: stream-a-thon 2026-06-13 — phases 0 / 0.7 / 0.5 + ralplan plan iter-3.4 #18

Merged
deco merged 6 commits from feat/stream-a-thon-2026-06-13 into main 2026-05-19 04:28:48 +03:00
Owner

What this lands

The first three phases of the ralplan consensus plan at .omc/plans/stream-a-thon-2026-06-13.md (iter-3.4, deliberate mode). All LOCAL work — nothing required SSH/RDP to the production OBS host beyond a one-time snapshot script that's now in the tree for later use.

Commits

  1. chore: gitignore OMC runtime state + oversized obs-scene assets.
  2. feat(phase 0): consolidate sqlite lifecycle via lib/db.ts (withDb helper), migrate 4 runtime sqlite.open call sites, add scripts/auditSqliteOpens.ts to CI (forgejo + github), structured per-call JSON logging in setActive, schema asymmetry doc.
  3. feat(phase 0.7): switcher coverage check (scripts/verifySwitcherCoverage.ts — the S4' pre-mortem kill switch), Mac OBS path remap script (scripts/remapMacObsSwitcherPaths.ts, idempotent), repo cleanup (rm files/ss_right.text typo, document SOURCE_SWITCHER_NAMES).
  4. feat(phase 0.5): fallback event-day runbook (docs/RUNBOOK_FALLBACK.md), Windows snapshot script (scripts/capturePhase05Snapshot.sh), latency baseline + plugin contract docs, reference scene JSONs (2025 + active 2026), the ralplan plan artifact.

What's NOT in this PR

  • Phase 1+ work (createStreamGroup V1/V2 split, Streamlink supervisor, convertBrowserToMedia, dress rehearsal). Those land in subsequent PRs.
  • Phase 4.2 will need a re-measurement of latency — the iter-3.4 method in scripts/measureSwitcherLatency.ts hit a documented methodology gap (GetInputSettings.current_index doesn't reflect plugin-internal source state, so the captured 1–3ms numbers are WS RTT not end-to-end). Three alternative detection methods documented in docs/plugin-contract.md.
  • The ~40 untracked AGENTS.md files across the tree — pre-existing from a prior deepinit run, not part of this PR.

Verification

  • next build green (full compile + types + 23 static pages)
  • npx tsx scripts/auditSqliteOpens.ts exits 0; injection of a fake open({ flips to exit 1
  • npx tsx scripts/verifySwitcherCoverage.ts exits 0 on current scene+DB; fail-injection produces a clear missing-coverage diff
  • scripts/remapMacObsSwitcherPaths.ts was run end-to-end against the local Mac OBS — all 7 switchers reconfigured, smoke test confirmed plugin still reading after the remap, idempotent re-run is a no-op
  • bash -n scripts/capturePhase05Snapshot.sh syntax-clean

Test plan

  • Diff-read the api/route.ts changes — confirm withDb semantics match the pre-migration behavior (no transaction boundary changes)
  • Confirm the audit script's allowlist (5 entries: lib/database.ts + 4 scripts) matches your understanding
  • Skim docs/RUNBOOK_FALLBACK.md — fill in the 18 TODO(operator) hotkey placeholders during your dress rehearsal
  • Decide whether you want the 2025 obs-scene/SaT.json reference to stay committed alongside the current 2026 one, or just the active one
  • Optional: run scripts/capturePhase05Snapshot.sh once via npm run snapshot:phase05 -- bridge.hippo-sidemirror.ts.net derek SaT_Summer_2026 to verify the bash script works against your Scoop OBS install
## What this lands The first three phases of the ralplan consensus plan at `.omc/plans/stream-a-thon-2026-06-13.md` (iter-3.4, deliberate mode). All LOCAL work — nothing required SSH/RDP to the production OBS host beyond a one-time snapshot script that's now in the tree for later use. ## Commits 1. **chore**: gitignore OMC runtime state + oversized obs-scene assets. 2. **feat(phase 0)**: consolidate sqlite lifecycle via `lib/db.ts` (`withDb` helper), migrate 4 runtime sqlite.open call sites, add `scripts/auditSqliteOpens.ts` to CI (forgejo + github), structured per-call JSON logging in `setActive`, schema asymmetry doc. 3. **feat(phase 0.7)**: switcher coverage check (`scripts/verifySwitcherCoverage.ts` — the S4' pre-mortem kill switch), Mac OBS path remap script (`scripts/remapMacObsSwitcherPaths.ts`, idempotent), repo cleanup (rm `files/ss_right.text` typo, document `SOURCE_SWITCHER_NAMES`). 4. **feat(phase 0.5)**: fallback event-day runbook (`docs/RUNBOOK_FALLBACK.md`), Windows snapshot script (`scripts/capturePhase05Snapshot.sh`), latency baseline + plugin contract docs, reference scene JSONs (2025 + active 2026), the ralplan plan artifact. ## What's NOT in this PR - Phase 1+ work (createStreamGroup V1/V2 split, Streamlink supervisor, convertBrowserToMedia, dress rehearsal). Those land in subsequent PRs. - Phase 4.2 will need a re-measurement of latency — the iter-3.4 method in `scripts/measureSwitcherLatency.ts` hit a documented methodology gap (GetInputSettings.current_index doesn't reflect plugin-internal source state, so the captured 1–3ms numbers are WS RTT not end-to-end). Three alternative detection methods documented in `docs/plugin-contract.md`. - The ~40 untracked `AGENTS.md` files across the tree — pre-existing from a prior deepinit run, not part of this PR. ## Verification - `next build` green (full compile + types + 23 static pages) - `npx tsx scripts/auditSqliteOpens.ts` exits 0; injection of a fake `open({` flips to exit 1 - `npx tsx scripts/verifySwitcherCoverage.ts` exits 0 on current scene+DB; fail-injection produces a clear missing-coverage diff - `scripts/remapMacObsSwitcherPaths.ts` was run end-to-end against the local Mac OBS — all 7 switchers reconfigured, smoke test confirmed plugin still reading after the remap, idempotent re-run is a no-op - `bash -n scripts/capturePhase05Snapshot.sh` syntax-clean ## Test plan - [ ] Diff-read the api/route.ts changes — confirm withDb semantics match the pre-migration behavior (no transaction boundary changes) - [ ] Confirm the audit script's allowlist (5 entries: lib/database.ts + 4 scripts) matches your understanding - [ ] Skim `docs/RUNBOOK_FALLBACK.md` — fill in the 18 TODO(operator) hotkey placeholders during your dress rehearsal - [ ] Decide whether you want the 2025 `obs-scene/SaT.json` reference to stay committed alongside the current 2026 one, or just the active one - [ ] Optional: run `scripts/capturePhase05Snapshot.sh` once via `npm run snapshot:phase05 -- bridge.hippo-sidemirror.ts.net derek SaT_Summer_2026` to verify the bash script works against your Scoop OBS install
- .omc/state/, .omc/sessions/, .omc/project-memory.json are session-local
  runtime state — not portable. .omc/plans/ stays tracked (planning artifacts).
- obs-scene/SaT Summer 2025/ holds ~hundreds of MB of media assets;
  exclude to keep the repo small. Reference scene JSONs and the 7 switcher
  .txt files are committed separately as ground-truth data.
- sources.db mirrors + generated snapshots are excluded; regenerable
  from prod via scripts/capturePhase05Snapshot.sh.
Closes Phase 0 of .omc/plans/stream-a-thon-2026-06-13.md.

- 0.1 — Add lib/db.ts with withDb(callback) wrapping the existing
  getDatabase() singleton; migrate the 4 runtime sqlite.open call
  sites (createGroup, syncGroups, addStream x2) to use it.
  Eliminates 4 file-descriptor leaks under load.
- 0.2 — docs/schema.md documents the streams.id AUTOINCREMENT vs
  teams.team_id non-AUTOINCREMENT asymmetry and the rationale
  (team_id is the public-facing semantic team number).
- 0.3 — scripts/auditSqliteOpens.ts + allowlist enforces no new
  direct sqlite.open() sites land. Wired into both .forgejo and
  .github workflow CI as a step before npm run build.
- 0.4 — setActive emits one JSON log line per call with
  {ts, screen, group, ms} for offline latency analysis.

Verified: next build green, 500-call FD-leak test green, audit
flips to fail when a 10th open site is injected.
Closes Phase 0.7 of .omc/plans/stream-a-thon-2026-06-13.md.

- 0.7.2 — rm files/ss_right.text (0-byte typo orphan). Document
  SOURCE_SWITCHER_NAMES at lib/constants.ts:54-62 with a JSDoc
  block: it holds canonical OBS input names (with ss_ prefix), NOT
  file paths. Constant is USED at lib/obsClient.js:2,746 and
  app/api/addStream/route.ts:7,21,170,175 — must stay in sync with
  the production scene JSON; do not delete.
- 0.7.3 — scripts/verifySwitcherCoverage.ts checks that for every
  (stream, screen) pair the webui can target via setActive, the
  derived stream-group name exists as a 'value' in the
  corresponding ss_${screen} sources array in the scene JSON. This
  is the S4' pre-mortem kill switch (per the iter-3.3 user
  clarification that switcher-file content is webui-overwritten;
  the real failure mode is name-coverage mismatch). The derivation
  is extracted into lib/streamGroupName.ts so script and
  setActive share parity.
- 0.7.4 — scripts/remapMacObsSwitcherPaths.ts uses OBS WebSocket
  SetInputSettings to repoint the 7 Mac OBS switcher inputs from
  Windows paths (C:/OBS/source-switching/${screen}.txt) to a Mac
  filesystem path. Idempotent. Closes the macOS local-loop
  testability gap (Mac OBS scene was imported as-is from Windows).

Verified: tsc green; remap script ran end-to-end against local
Mac OBS, all 7 switchers updated, smoke test confirmed plugin
still reading after remap.
feat(phase 0.5): fallback runbook, latency baseline, plugin contract, ralplan plan iter-3.4
Some checks failed
Lint and Build / build (pull_request) Failing after 1m40s
a0c31e66b9
Closes Phase 0.5 of .omc/plans/stream-a-thon-2026-06-13.md
(G0.5 binding gate, with one documented partial).

- 0.5.1 + 0.5.3 — scripts/capturePhase05Snapshot.sh: one-shot
  SSH-driven snapshot of the Windows OBS scene JSON + sources.db
  + Node version + plugin DLL info. Defaults to the user's Scoop
  install paths (overridable via arg 4). Note: production OBS
  uses Scoop, so scenes live at
  %USERPROFILE%\scoop\persist\obs-studio\config\obs-studio\
  (NOT %APPDATA%\obs-studio\).
- 0.5.2 — scripts/measureSwitcherLatency.ts measures end-to-end
  switcher latency via OBS WebSocket; encoding contract check
  (UTF-8 no BOM no trailing newline matches fs.writeFileSync
  default) and Windows OBS connectivity probe folded in.
  PARTIAL: GetInputSettings.current_index does not reflect the
  plugin's live source state, so the measured numbers are
  WebSocket RTT, not end-to-end plugin latency. Methodology gap
  documented in docs/plugin-contract.md with three alternative
  detection methods to try before Phase 4.2 dress rehearsal.
- 0.5.4 — docs/RUNBOOK_FALLBACK.md: universal event-day kill
  switch covering S1 (RAM saturation), S3 (corrupted scene),
  S7 (host inaccessible), S8 (plugin SPOF) plus generic
  file-write fallback. 18 operator TODOs for actual hotkey
  bindings (filled in during dress rehearsal).
- obs-scene/SaT.json + SaT_Summer_2026.json — reference scene
  collections for 2025 and the current active 2026 collection
  (verified via OBS WebSocket GetSceneCollectionList).
- obs-scene/source-switching/*.txt — the 7 snake_case switcher
  files. Inert (webui-overwritten on each setActive write) but
  committed as documentation of the file-name contract.
- .omc/plans/stream-a-thon-2026-06-13.md — ralplan consensus
  plan iter-3.4 (deliberate mode, 3 iterations: Architect
  REJECT folded via 6 amendments; Critic APPROVE after Critical
  + Major fixes; 2 inline patches from user clarifications).
  Recommends Option A (memory-fix only) for June 13; defers
  Postgres + plugin-DB integration to post-event R-track.
fix(ci): move bufferutil to optionalDependencies
Some checks failed
Lint and Build / build (pull_request) Failing after 1m55s
7597699cbd
ARM64 CI runners (e.g. Raspberry Pi) don't have prebuilt binaries
available for bufferutil@^4.0.8 + node-20, so npm falls back to a
source compile via node-gyp. The Pi runner doesn't have g++ installed
(`make: c++: No such file or directory`), so the install fails.

bufferutil is not imported directly anywhere in this codebase — it's
an optional performance accelerator that `ws` uses if present.
Without it, `ws` falls back to a pure-JS implementation, which is
fine for OBS WebSocket (small JSON payloads, latency dominated by
the 1s plugin polling floor anyway).

Moving to optionalDependencies makes npm skip it when compilation
fails on platforms without compilers, matching the convention for
native-optional accelerators. Local Mac builds remain unaffected
(prebuilt binaries available there).
fix(ci): allowlist verifySwitcherCoverage.ts in sqlite-opens audit
All checks were successful
Lint and Build / build (pull_request) Successful in 2m48s
f6a1ca196d
The Phase 0.7.3 coverage-check script (added in 08ac560) is a one-shot
CLI that accepts an arbitrary --db path so it can run against backup
snapshots / sources.db mirrors / etc. The withDb() singleton convention
is the wrong fit for that usage — it'd be locked to FILE_DIRECTORY.

Same category as the other allowlisted scripts (addGroupNameToTeams,
addGroupUuidColumn, createSatSummer2025Tables, verifyTables).
deco merged commit 55667e80ed into main 2026-05-19 04:28:48 +03:00
deco deleted branch feat/stream-a-thon-2026-06-13 2026-05-19 04:28:48 +03:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
deco/cuesheet!18
No description provided.