Skip to content

Scope presenter BroadcastChannel traffic to one workbench session #151

Description

@Brad-Edwards

Review finding

Every workbench uses the same default presenter BroadcastChannel name. 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:20 defines DEFAULT_PRESENTER_CHANNEL = 'pulsar-presenter'.
  • src/system/presenter/bridge.ts:51 uses that default when callers do not supply a channel name.
  • src/system/presenter/bridge.ts:58 accepts any valid PresenterCommand received on that same-origin channel.
  • src/system/presenter/bridge.ts:83 broadcasts 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

  • A per-workbench nonce or session id is included in the channel name or message envelope.
  • The prompter popout receives the same session scope as the opener.
  • isPresenterCommand() validation remains in place for message shape.
  • Tests prove two bridge instances with different session scopes do not receive each other's commands, while windows in the same session do.

Activity

  1. added
    bugSomething isn't working
    area:presenterPresenter controls, advance/hold/skip, pause/resume
    on May 23, 2026
  2. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    🛠️ Picked up by /implement — driver Codex, branch 151-presenter-session-scope, 2026-05-24T03:33:05Z.

  3. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    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.

  4. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    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.ts as the canonical transport boundary. Add an ephemeral presenter session id, a scoped channel-name helper, and a sessionId option for createPresenterBridge(). Keep isPresenterCommand() as the inbound message validator and keep raw command payloads unchanged.
    • Update src/main.ts to resolve one presenter session id for the workbench bootstrap and pass it to createPresenterBridge(). Do not persist the id, read storage, use env/config, or treat it as auth.
    • Update src/system/presenter/prompter-window.ts so popout URLs carry the same presenter session scope through structured URLSearchParams, alongside the existing mode=prompter handling and same-origin/opener isolation.
    • Add failing tests first in tests/system/presenter-bridge.test.ts proving same-session bridge instances communicate while different-session instances do not, and that malformed messages on scoped channels still fail isPresenterCommand() validation.
    • Add failing tests in tests/system/prompter-window-url.test.ts proving 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 edit CHANGELOG.md directly.

    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.

  5. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    gc_codex_review — cycle 1 of 1 (pre-push) on issue #151 (branch 151-presenter-session-scope)

    Core review

    Verdict: ship

    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.

    No blocking findings.

    Security review

    Verdict: ship

    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.

    No blocking findings.

  6. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    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.

  7. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    Review decision record — codex cycle 1 (issue #151)

    Reviewer: codex
    Cycle: 1

    Architectural 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)

  8. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    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)

  9. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    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_review invocation to count cycles.

  10. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    Review decision record — test-quality cycle 1 (issue #151)

    Reviewer: test-quality
    Cycle: 1

    Architectural 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)

  11. removed
    in-progressAn agent is actively working this issue via /implement
    on May 24, 2026
  12. Brad-Edwards commented on May 24, 2026

    @Brad-Edwards
    ContributorAuthor

    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.ts
    • src/system/presenter/bridge.ts
    • src/system/presenter/index.ts
    • src/system/presenter/prompter-window.ts
    • tests/system/presenter-bridge.test.ts
    • tests/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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:presenterPresenter controls, advance/hold/skip, pause/resumebugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions