Skip to content

fix: forward pane terminal bells - #2498

Merged
ogulcancelik merged 1 commit into
masterfrom
akbash/2453-forward-pane-bel
Aug 7, 2026
Merged

fix: forward pane terminal bells#2498
ogulcancelik merged 1 commit into
masterfrom
akbash/2453-forward-pane-bel

Conversation

@akbash-bot

Copy link
Copy Markdown
Collaborator

Summary

  • register libghostty's BEL callback and preserve live pane bell counts without treating OSC terminators as bells
  • forward pane bells through the runtime to the foreground client, with matching monolithic behavior
  • bump the client/server protocol to 20 and update release docs and protocol fixtures

Checks

  • focused parser, spawned PTY reader, foreground routing, raw writer, and wire round-trip tests
  • raw PTY reproduction: the same printf '\a' interaction emitted 0 BEL bytes before and exactly 1 after
  • just ci 'all() - test(live_server_holds_one_pty_master_fd_per_pane)' (3254 tests passed), just windows-lint, and 97 maintenance tests
  • unfiltered just check reached an environment-only existing harness limitation: the isolated external CARGO_TARGET_DIR prevents that one handoff test from recognizing its successfully started replacement process under checkout target/debug

refs #2453

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: deef99f7-8b9e-4e1a-b304-c92554f041f0

📥 Commits

Reviewing files that changed from the base of the PR and between 5b60dd2 and 392fc8a.

📒 Files selected for processing (3)
  • docs/next/CHANGELOG.md
  • src/ghostty/mod.rs
  • src/pane/terminal.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/next/CHANGELOG.md
  • src/ghostty/mod.rs
  • src/pane/terminal.rs

📝 Walkthrough

Walkthrough

The change adds pane BEL counting and forwarding. PTY readers publish bell events, the server sends bell counts to the foreground client, and clients write BEL characters to the outer terminal. The wire protocol and related tests advance to version 20.

Changes

Terminal bell forwarding

Layer / File(s) Summary
Pane bell capture
src/events.rs, src/ghostty/mod.rs, src/pane/terminal.rs, src/pane.rs
Ghostty counts BEL callbacks. Terminal processing returns live bell counts. PTY readers publish aggregated AppEvent::TerminalBell events. Tests cover history filtering, live output, clipboard output, and PTY delivery.
Bell event and protocol routing
src/app/actions.rs, src/app/api.rs, src/protocol/wire.rs, src/server/headless.rs, docs/next/api/herdr-api.schema.json, tests/*, docs/next/CHANGELOG.md
Bell events bypass rendering, use protocol version 20, and are delivered only to the foreground client. Tests cover serialization, routing, and protocol version updates.
Outer terminal bell output
src/terminal_effects.rs, src/client/mod.rs, src/app/api.rs, src/main.rs
Shared output code writes and flushes the requested BEL count. Application and client handlers log output failures without stopping event processing.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PaneTerminal
  participant AppEventQueue
  participant HeadlessServer
  participant Client
  participant OuterTerminal
  PaneTerminal->>AppEventQueue: publish TerminalBell count
  AppEventQueue->>HeadlessServer: deliver AppEvent::TerminalBell
  HeadlessServer->>Client: send ServerMessage::TerminalBell count
  Client->>OuterTerminal: write terminal bells
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: forwarding terminal bells emitted by panes.
Description check ✅ Passed The description directly explains the terminal bell forwarding changes, protocol update, tests, and validation results.
Docstring Coverage ✅ Passed Docstring coverage is 89.66% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch akbash/2453-forward-pane-bel

Comment @coderabbitai help to get the list of available commands.

@kangal-bot kangal-bot added the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Aug 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/terminal_effects.rs (1)

5-13: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Clamp terminal bell counts before forwarding.

bell_count can reach u16::MAX, and write_terminal_bells emits one BEL byte per count. Clamp the count before creating AppEvent::TerminalBell to prevent a single PTY read from sending 65,535 BEL bytes to the outer terminal.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b40ced45-9dce-4e9a-aa8f-8e30b5871ef3

📥 Commits

Reviewing files that changed from the base of the PR and between 50ddc06 and 5b60dd2.

📒 Files selected for processing (16)
  • docs/next/CHANGELOG.md
  • docs/next/api/herdr-api.schema.json
  • src/app/actions.rs
  • src/app/api.rs
  • src/client/mod.rs
  • src/events.rs
  • src/ghostty/mod.rs
  • src/main.rs
  • src/pane.rs
  • src/pane/terminal.rs
  • src/protocol/wire.rs
  • src/server/headless.rs
  • src/terminal_effects.rs
  • tests/api_ping.rs
  • tests/cli/sessions.rs
  • tests/support/mod.rs

@akbash-bot
akbash-bot force-pushed the akbash/2453-forward-pane-bel branch from 5b60dd2 to 392fc8a Compare August 7, 2026 20:54
@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds end-to-end forwarding of libghostty BEL callbacks from pane PTY parsing to the monolithic host terminal or the headless server's foreground client, and bumps the client/server protocol to 20.

  • Registers and drains libghostty bell callbacks while excluding restored history and OSC terminators.
  • Adds pane events and a wire message for foreground-client bell delivery.
  • Adds exact-count terminal output and focused parser, PTY, routing, and wire tests.
  • Updates protocol fixtures, API schema, and release documentation.

Confidence Score: 4/5

The PR should not merge until terminal bells are retained or retried when the bounded application-event queue is temporarily full.

Both PTY reader paths route bells through try_send, so queue pressure can discard the newly promised terminal side effect even though parsing and downstream forwarding are otherwise correctly wired.

Files Needing Attention: src/pane.rs

Important Files Changed

Filename Overview
src/ghostty/mod.rs Registers a saturating BEL callback counter and exposes a take-and-reset accessor.
src/pane/terminal.rs Drains stale callback state before live PTY parsing and returns the resulting live bell count.
src/pane.rs Publishes parsed bell counts from both PTY reader paths, but drops them whenever the bounded event queue is full.
src/server/headless.rs Intercepts terminal-bell events and forwards their counts only to the foreground client.
src/client/mod.rs Emits received bell counts to the attached client's stdout serially with other server messages.
src/protocol/wire.rs Appends the TerminalBell server message and bumps the incompatible wire protocol to version 20.
src/app/api.rs Handles monolithic bell events as non-rendering host-terminal side effects.
src/terminal_effects.rs Writes and flushes the exact requested number of BEL bytes in bounded chunks.

Sequence Diagram

sequenceDiagram
  participant PTY as Pane PTY
  participant Ghostty as libghostty
  participant Runtime as PaneRuntime
  participant Server as App / HeadlessServer
  participant Client as Foreground client
  participant Host as Outer terminal
  PTY->>Ghostty: Output containing BEL
  Ghostty->>Runtime: Parsed bell callback count
  Runtime->>Server: AppEvent::TerminalBell
  alt Monolithic mode
    Server->>Host: Write count × BEL
  else Headless mode
    Server->>Client: ServerMessage::TerminalBell
    Client->>Host: Write count × BEL
  end
Loading

Reviews (1): Last reviewed commit: "fix: forward pane terminal bells" | Re-trigger Greptile

Comment thread src/pane.rs
if count == 0 {
return;
}
if let Err(err) = events.try_send(AppEvent::TerminalBell { pane_id, count }) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Bell events drop under pressure

When the bounded application-event channel is full, try_send discards the parsed terminal bell event, causing the outer terminal to miss the pane's audible or visual bell.

Knowledge Base Used:

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m keeping this best-effort on the existing bounded application-event channel. Blocking the PTY reader when the UI loop is under pressure can stall terminal output or deadlock against pane work; adding a reliable side-effect queue would be a broader transport-policy change than BEL forwarding.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a sound tradeoff. PTY reader stalls would degrade the core terminal experience in a much more visible way than an occasional missed bell — and bells are inherently best-effort signals. The existing channel back-pressure semantics already encode that priority correctly.

The one low-cost improvement worth considering: log the drop so it's observable without adding any blocking:

Suggested change
if let Err(err) = events.try_send(AppEvent::TerminalBell { pane_id, count }) {
if let Err(err) = events.try_send(AppEvent::TerminalBell { pane_id, count }) {
tracing::debug!(pane_id = ?pane_id, count, ?err, "bell event dropped (channel full)");

That way the behavior stays non-blocking but the drop isn't completely silent during debugging. Otherwise the approach is justified.

@akbash-bot

Copy link
Copy Markdown
Collaborator Author

On CodeRabbit review 4886460553: I’m keeping the exact count. Production PTY reads are bounded to 8 KiB, so one parsed read cannot reach u16::MAX; output is allocation-free and chunked, while clamping would discard pane-emitted BELs.

@ogulcancelik
ogulcancelik merged commit 6f31149 into master Aug 7, 2026
7 checks passed
@kangal-bot kangal-bot removed the ai-review Trigger automated AI reviews for pull requests admitted by the PR gate label Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants