Repository navigation
Scope presenter BroadcastChannel traffic to one workbench session #151
Description
Activity
- addedbugSomething isn't workingSomething isn't workingarea:presenterPresenter controls, advance/hold/skip, pause/resumePresenter controls, advance/hold/skip, pause/resume
on May 23, 2026 🛠️ Picked up by /implement — driver Codex, branch
151-presenter-session-scope, 2026-05-24T03:33:05Z.- addedin-progressAn agent is actively working this issue via /implementAn agent is actively working this issue via /implement
on May 24, 2026 gc workflow phase recorded:
preflight(issue #151). Posted by the MCP server to enforce ordering between workflow steps (issue #794 MVP-2). Do not edit or delete — used by downstream tools to gate phase prerequisites.gc workflow phase recorded:
plan(issue #151). Posted by the MCP server to enforce ordering between workflow steps (issue #794 MVP-2). Do not edit or delete — used by downstream tools to gate phase prerequisites.Implementation Plan
Issue #151 is a requirement-free bug fix. The acceptance contract is the issue body.
- Modify
src/system/presenter/bridge.tsas the canonical transport boundary. Add an ephemeral presenter session id, a scoped channel-name helper, and asessionIdoption forcreatePresenterBridge(). KeepisPresenterCommand()as the inbound message validator and keep raw command payloads unchanged. - Update
src/main.tsto resolve one presenter session id for the workbench bootstrap and pass it tocreatePresenterBridge(). Do not persist the id, read storage, use env/config, or treat it as auth. - Update
src/system/presenter/prompter-window.tsso popout URLs carry the same presenter session scope through structuredURLSearchParams, alongside the existingmode=prompterhandling and same-origin/opener isolation. - Add failing tests first in
tests/system/presenter-bridge.test.tsproving same-session bridge instances communicate while different-session instances do not, and that malformed messages on scoped channels still failisPresenterCommand()validation. - Add failing tests in
tests/system/prompter-window-url.test.tsproving the prompter popout URL receives/preserves the presenter session scope while keeping hashes, same-origin checks, and opener isolation intact. - Add
changelog.d/151.fixed.md. Do not editCHANGELOG.mddirectly.
Security: the scope is a correlation boundary for same-origin transport, not a secret or authorization check. Maintainability: channel construction stays in the presenter bridge and popout propagation stays in the existing popout helper. Extensibility: future role- or presentation-specific scopes can parameterize the session id/channel helper without changing command validation. Whole-repo view: no runtime navigation grammar, presenter command schema, ADR, storage, config, or remote protocol changes are needed.
- Modify
gc_codex_review — cycle 1 of 1 (pre-push) on issue #151 (branch
151-presenter-session-scope)Core review
Verdict:
shipThis change is shaped correctly: it keeps the cross-window presenter bridge as the single synchronization seam, scopes the existing BroadcastChannel rather than introducing a parallel transport, and threads the session id through the prompter URL where the window boundary already exists. The validation and channel-name construction live with the bridge, while the prompter only carries the scope across same-origin popout creation. The obvious next variation, such as adding another presenter companion window, can reuse the same query-param/session contract without changing the controller or source-composition model.
No blocking findings.
Security review
Verdict:
shipThis change is shaped correctly: it narrows the presenter BroadcastChannel from a single same-origin bus to a per-workbench-session channel, keeps the existing controller/source seam, and propagates the scope through the prompter URL builder where the cross-window relationship is created. The cross-cutting concern is browser same-origin message isolation; the design leaves the obvious next variation, explicit externally supplied session IDs for tests or alternate launchers, available through the existing options surface without weakening default behavior.
No blocking findings.
gc_codex_review pre-push cycle 1 of 1 complete for issue #151 on branch '151-presenter-session-scope'. Posted by the MCP server to enforce the pre-push hard-cap-1 contract (issues #796, #804, #906). Do not edit or delete — used by the next
gc_codex_review(uncommitted) invocation to count cycles.Review decision record — codex cycle 1 (issue #151)
Reviewer: codex
Cycle: 1Architectural read:
Core reviewer: This change is shaped correctly: it keeps the cross-window presenter bridge as the single synchronization seam, scopes the existing BroadcastChannel rather than introducing a parallel transport, and threads the session id through the prompter URL where the window boundary already exists. The validation and channel-name construction live with the bridge, while the prompter only carries the scope across same-origin popout creation. The obvious next variation, such as adding another presenter companion window, can reuse the same query-param/session contract without changing the controller or source-composition model.
Security reviewer: This change is shaped correctly: it narrows the presenter BroadcastChannel from a single same-origin bus to a per-workbench-session channel, keeps the existing controller/source seam, and propagates the scope through the prompter URL builder where the cross-window relationship is created. The cross-cutting concern is browser same-origin message isolation; the design leaves the obvious next variation, explicit externally supplied session IDs for tests or alternate launchers, available through the existing options surface without weakening default behavior.
Blocking findings: 0 (clean run)
gc_test_quality_review cycle 1 of 1 — issue #151
Reviewer: test-quality (claude-sonnet-4-6 via gc_test_quality_review)
Branch:151-presenter-session-scope
Cycle: 1 / 1
Findings: 0 (clean run)gc_test_quality_review cycle 1 of 1 complete for issue #151 on branch '151-presenter-session-scope'. Posted by the MCP server to enforce the gc_test_quality_review hard-cap-1 contract (issue #884 follow-up, default lowered in #906). Do not edit or delete — used by the next
gc_test_quality_reviewinvocation to count cycles.Review decision record — test-quality cycle 1 (issue #151)
Reviewer: test-quality
Cycle: 1Architectural read:
Both test files fit the repo's vocabulary cleanly. The bridge tests are lightweight integration tests over real BroadcastChannel instances — exactly the right instrument for a module whose only job is multiplexing a browser broadcast primitive. The prompter-window tests stub only
window.open(the I/O boundary) and assert on the full constructed URL, giving tight regression coverage of the URL-builder logic without touching any framework internals. Session scoping, command validation, dispose/unsubscribe idempotency, the BroadcastChannel-absent fallback, and opener isolation are all exercised with real state assertions. No foreclosure of the obvious next variation (additional command kinds, additional presenter session shapes).Blocking findings: 0 (clean run)
- removedin-progressAn agent is actively working this issue via /implementAn agent is actively working this issue via /implement
on May 24, 2026 Final report — issue #151 complete
PR: #158
Plan: #151 (comment)Presenter bridge traffic now uses a scoped per-session channel, and prompter popout URLs carry that scope while retaining command shape validation and opener isolation.
Files changed
Added:
changelog.d/151.fixed.md
Modified:
src/main.tssrc/system/presenter/bridge.tssrc/system/presenter/index.tssrc/system/presenter/prompter-window.tstests/system/presenter-bridge.test.tstests/system/prompter-window-url.test.ts
Reviews
- codex: Cycle 1 clean, 0 findings remaining
- test-quality: Cycle 1 clean, 0 findings remaining
- sonarcloud: Quality gate passed after coverage tests; new coverage 95.9%
Traceability reconciliation
- IMPLEMENTS / TESTS / DOCUMENTS added: 0
- Links updated: 0
- Stale links removed: 0
Requirement-free issue; PR body records IMPLEMENTS and TESTS issue traceability.
Status
- CI: ✅ green
- SonarCloud: ✅ passed
- PR ready for user review and merge.
Review finding
Every workbench uses the same default presenter
BroadcastChannelname. That makes commands global to all Pulsar windows on the same origin instead of scoped to one presentation session.Evidence
src/system/presenter/bridge.ts:20definesDEFAULT_PRESENTER_CHANNEL = 'pulsar-presenter'.src/system/presenter/bridge.ts:51uses that default when callers do not supply a channel name.src/system/presenter/bridge.ts:58accepts any validPresenterCommandreceived on that same-origin channel.src/system/presenter/bridge.ts:83broadcasts local commands to the same shared channel.Impact
Two presentations running under the same origin can drive each other accidentally. Any same-origin page that knows the fixed channel name can also send valid presenter commands such as advance, back, or mute toggles. Same-origin is trusted in the current model, but this still creates surprising cross-talk for local workbench sessions.
Recommended fix
Add an explicit per-session scope to the presenter bridge, and ensure popout windows inherit that scope from the opener URL or bootstrap state.
Acceptance checks
isPresenterCommand()validation remains in place for message shape.