Skip to content

fix(desktop): remove terminal backpressure warning#2996

Merged
Kitenite merged 5 commits into
mainfrom
fix/terminal-backpressure-combined
Mar 30, 2026
Merged

fix(desktop): remove terminal backpressure warning#2996
Kitenite merged 5 commits into
mainfrom
fix/terminal-backpressure-combined

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Mar 29, 2026

Summary

Remove the terminal-host backpressure warning while keeping the existing socket backpressure handling unchanged.

What Changed

  • delete the console.warn emitted when a client socket returns false from write()
  • keep the existing pause/resume-on-drain flow intact

Why

The warning was noisy and no longer worth carrying forward as a dedicated change. This leaves the existing behavior in place without logging repeated backpressure messages.

Impact

  • no terminal output delivery semantics change
  • daemon logs no longer emit this backpressure warning

Validation

bun test apps/desktop/src/main/terminal-host/session.test.ts

Summary by CodeRabbit

  • Refactor
    • Removed unnecessary console warnings from terminal session handling to reduce log noise while maintaining all backpressure functionality.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 29, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e08daf16-1139-460c-93e2-c7c6f0269d15

📥 Commits

Reviewing files that changed from the base of the PR and between bfeaa39 and a4617b3.

📒 Files selected for processing (1)
  • apps/desktop/src/main/terminal-host/session.ts

📝 Walkthrough

Walkthrough

Removed a warning log emitted when a client socket's write returned false in the terminal host session; backpressure handling via pause/resume and drain events remains unchanged.

Changes

Cohort / File(s) Summary
Terminal host session
apps/desktop/src/main/terminal-host/session.ts
Removed console.warn() that ran when socket.write() returned false. Existing backpressure handling (handleClientBackpressure(), pause/resume/drain) remains in place.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Possibly related PRs

Poem

🐰 A hush where warnings used to play,
The pipes still breathe, they find their way.
No shout, just rhythm, steady beat—
Quiet code with paws so neat. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(desktop): rate-limit terminal backpressure warnings' clearly and specifically describes the main change—implementing rate-limiting for backpressure warnings in the desktop terminal host.
Description check ✅ Passed The description covers the key sections: summary of changes, what changed, why the change was made, impact, and validation instructions. However, it does not follow the provided template structure with explicit sections for 'Related Issues', 'Type of Change', or 'Testing' with checkboxes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 fix/terminal-backpressure-combined

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
apps/desktop/src/main/terminal-host/session.test.ts (3)

296-305: Incomplete AttachedClient structure injected.

The test directly injects into attachedClients but only includes { socket }. The actual AttachedClient interface requires attachedAt and attachToken fields. While this works for the current broadcastEvent() implementation (which only uses socket), it may cause issues if future code accesses other fields.

♻️ Suggested improvement for test robustness
 		// Directly inject the socket as an attached client
 		(
 			session as unknown as {
 				attachedClients: Map<
 					import("node:net").Socket,
-					{ socket: import("node:net").Socket }
+					{ socket: import("node:net").Socket; attachedAt: number; attachToken: symbol }
 				>;
 			}
-		).attachedClients.set(fakeSocket, { socket: fakeSocket });
+		).attachedClients.set(fakeSocket, {
+			socket: fakeSocket,
+			attachedAt: Date.now(),
+			attachToken: Symbol("test-attach"),
+		});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/terminal-host/session.test.ts` around lines 296 - 305,
The test injects an incomplete AttachedClient into session.attachedClients — it
only sets { socket } but the real AttachedClient type also includes attachedAt
and attachToken; update the injected value in the test for the Map used by
attachedClients (referencing attachedClients, AttachedClient, and
broadcastEvent()) to include valid attachedAt (e.g., a Date or timestamp) and
attachToken (e.g., a test token string) so the object matches the real interface
and prevents future breaks if other fields are accessed.

367-382: Test primarily verifies skip behavior, not rate-limiting.

Since writeFn always returns false, the socket becomes backpressured on the first write. Subsequent broadcasts skip the socket entirely (due to the clientSocketsWaitingForDrain check), so warnBackpressure() is only called once. The test passes, but it doesn't directly exercise the rate-limiting logic—only the skip logic. The next test ("includes suppressed count") properly tests rate-limiting by draining between broadcasts.

Consider updating the test name or comment to clarify what behavior it actually validates:

-	it("emits only one backpressure warning within the rate-limit window", () => {
+	it("emits only one backpressure warning when socket stays backpressured", () => {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/terminal-host/session.test.ts` around lines 367 - 382,
The test currently uses createSessionWithSocket(() => false) so writeFn always
returns false, causing the socket to be marked in clientSocketsWaitingForDrain
and subsequent broadcasts are skipped—so the test actually verifies the skip
behavior rather than rate-limiting; rename the test (or update its
description/comment) from "emits only one backpressure warning within the
rate-limit window" to something like "emits one backpressure warning when socket
is marked waiting-for-drain (skip behavior)" and/or adjust the setup to exercise
rate-limiting by using a writeFn that allows multiple writes/drains (e.g., a
function that returns false once then true or toggles) so warnBackpressure and
rate-limit logic are exercised; refer to createSessionWithSocket, writeFn,
clientSocketsWaitingForDrain, warnBackpressure, and broadcast when making the
change.

237-244: Consider saving and restoring the original console.warn.

The current approach replaces console.warn without saving the original. While mock() in Bun returns a spy, it doesn't inherently support mockRestore() to restore the original function. If tests run in sequence, this could affect subsequent tests.

♻️ Suggested improvement
 describe("Terminal Host Session backpressure", () => {
 	let warnSpy: ReturnType<typeof mock>;
+	let originalWarn: typeof console.warn;

 	beforeEach(() => {
+		originalWarn = console.warn;
 		warnSpy = mock();
 		console.warn = warnSpy;
 	});

 	afterEach(() => {
-		warnSpy.mockRestore?.();
+		console.warn = originalWarn;
 	});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/terminal-host/session.test.ts` around lines 237 - 244,
The tests overwrite console.warn without saving the original, which can leak
state between tests; update the beforeEach/afterEach setup to save the original
console.warn into a variable (e.g., originalWarn) before assigning warnSpy =
mock() and restoring originalWarn in afterEach instead of calling
warnSpy.mockRestore; reference the existing beforeEach, afterEach, warnSpy,
console.warn and mock() to locate and change the setup so the real console.warn
is preserved and reliably restored after each test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/desktop/src/main/terminal-host/session.test.ts`:
- Around line 296-305: The test injects an incomplete AttachedClient into
session.attachedClients — it only sets { socket } but the real AttachedClient
type also includes attachedAt and attachToken; update the injected value in the
test for the Map used by attachedClients (referencing attachedClients,
AttachedClient, and broadcastEvent()) to include valid attachedAt (e.g., a Date
or timestamp) and attachToken (e.g., a test token string) so the object matches
the real interface and prevents future breaks if other fields are accessed.
- Around line 367-382: The test currently uses createSessionWithSocket(() =>
false) so writeFn always returns false, causing the socket to be marked in
clientSocketsWaitingForDrain and subsequent broadcasts are skipped—so the test
actually verifies the skip behavior rather than rate-limiting; rename the test
(or update its description/comment) from "emits only one backpressure warning
within the rate-limit window" to something like "emits one backpressure warning
when socket is marked waiting-for-drain (skip behavior)" and/or adjust the setup
to exercise rate-limiting by using a writeFn that allows multiple writes/drains
(e.g., a function that returns false once then true or toggles) so
warnBackpressure and rate-limit logic are exercised; refer to
createSessionWithSocket, writeFn, clientSocketsWaitingForDrain,
warnBackpressure, and broadcast when making the change.
- Around line 237-244: The tests overwrite console.warn without saving the
original, which can leak state between tests; update the beforeEach/afterEach
setup to save the original console.warn into a variable (e.g., originalWarn)
before assigning warnSpy = mock() and restoring originalWarn in afterEach
instead of calling warnSpy.mockRestore; reference the existing beforeEach,
afterEach, warnSpy, console.warn and mock() to locate and change the setup so
the real console.warn is preserved and reliably restored after each test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7bd58080-b539-4c40-b791-6e5768fa4d7b

📥 Commits

Reviewing files that changed from the base of the PR and between 8dc5c38 and e377392.

📒 Files selected for processing (2)
  • apps/desktop/src/main/terminal-host/session.test.ts
  • apps/desktop/src/main/terminal-host/session.ts

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/desktop/src/main/terminal-host/session.test.ts">

<violation number="1" location="apps/desktop/src/main/terminal-host/session.test.ts:238">
P2: `console.warn` is globally overwritten in `beforeEach` but not actually restored, causing cross-test global state leakage. Use a method spy (`mock.method`) or explicitly restore the original `console.warn`.</violation>
</file>

<file name="apps/desktop/src/main/terminal-host/session.ts">

<violation number="1" location="apps/desktop/src/main/terminal-host/session.ts:1081">
P1: Skipping backpressured sockets for every event type can drop critical `exit`/`error` events. Restrict this skip to `data` events so lifecycle/error notifications are still delivered.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread apps/desktop/src/main/terminal-host/session.ts Outdated
Comment thread apps/desktop/src/main/terminal-host/session.test.ts Outdated
…limit warnings

Combines the fixes from #2969 and #2962 into a single changeset:

1. Skip writes to backpressured sockets (#2969): When a client socket
   signals backpressure (write returns false), subsequent broadcastEvent
   calls skip that socket entirely instead of growing Node's internal
   write buffer without bound. The terminal emulator still processes all
   data so snapshot state stays consistent — the next TUI repaint after
   drain naturally resyncs the display.

2. Rate-limit backpressure warnings (#2962): Replace unbounded
   console.warn on every backpressure event with a rate-limited
   warnBackpressure() method. Only one warning is emitted per 5-second
   window; subsequent occurrences are counted and reported in the next
   warning (e.g. '247 similar warnings suppressed'). Under sustained
   high-output commands, a single pane could previously generate 400k+
   identical warnings flooding the daemon log.

Tests cover: writes skipped during backpressure, writes resume after
drain, warning rate-limiting within the 5s window, and suppressed
count reporting after the window elapses.

Closes #2969
Closes #2962

Co-Authored-By: Mastra Code (anthropic/claude-opus-4-6) <noreply@mastra.ai>
@Kitenite Kitenite force-pushed the fix/terminal-backpressure-combined branch from e377392 to d98affb Compare March 29, 2026 22:39
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/desktop/src/main/terminal-host/session.ts (1)

136-137: Consider resetting backpressure warning state in resetProcessState().

The backpressureWarnLastAt and backpressureWarnSuppressed fields are not reset when the process state is reset (lines 984-1013). If a session's subprocess exits and respawns, stale warning state from the previous subprocess could affect warning behavior for the new one.

This is minor since the 5-second window is short and the state would naturally expire, but for consistency with other state fields it may be worth resetting.

♻️ Proposed fix
 private resetProcessState(): void {
   this.subprocess = null;
   this.subprocessReady = false;
   this.subprocessDecoder = null;
+  this.backpressureWarnLastAt = 0;
+  this.backpressureWarnSuppressed = 0;
   const shellName = this.shell.split("/").pop() || this.shell;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/terminal-host/session.ts` around lines 136 - 137, Reset
the backpressure warning state inside resetProcessState(): clear the fields
backpressureWarnLastAt and backpressureWarnSuppressed when resetting process
state so stale values from a previous subprocess don't affect a respawned one;
locate resetProcessState() in session.ts and add assignments to set
backpressureWarnLastAt = 0 and backpressureWarnSuppressed = 0 alongside the
other state resets.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/desktop/src/main/terminal-host/session.ts`:
- Around line 136-137: Reset the backpressure warning state inside
resetProcessState(): clear the fields backpressureWarnLastAt and
backpressureWarnSuppressed when resetting process state so stale values from a
previous subprocess don't affect a respawned one; locate resetProcessState() in
session.ts and add assignments to set backpressureWarnLastAt = 0 and
backpressureWarnSuppressed = 0 alongside the other state resets.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f40f3f30-ed6a-4ce0-b2f7-115d7e6339e8

📥 Commits

Reviewing files that changed from the base of the PR and between e377392 and d98affb.

📒 Files selected for processing (2)
  • apps/desktop/src/main/terminal-host/session.test.ts
  • apps/desktop/src/main/terminal-host/session.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/main/terminal-host/session.test.ts

@Kitenite Kitenite closed this Mar 29, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 29, 2026

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

@Kitenite Kitenite reopened this Mar 30, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/desktop/src/main/terminal-host/session.test.ts (2)

267-339: Cover the mixed healthy/backpressured-client case.

createSessionWithSocket() hardcodes a single attached socket, so this suite would still pass if broadcastEvent() accidentally stopped broadcasting after the first backpressured client. Since the fix is supposed to skip only the affected socket, add a two-client test where one socket is waiting for drain and another keeps receiving frames.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/terminal-host/session.test.ts` around lines 267 - 339,
create a new test using createSessionWithSocket that attaches two FakeSocket
instances to the session (use the same attachedClients injection pattern) where
one socket's writeFn returns false to simulate backpressure and the other
returns true so it keeps receiving frames; call (session as ...).broadcastEvent
/ broadcast repeatedly and assert the non-backpressured socket receives frames
while the backpressured socket is skipped, then emit a 'drain' on the
backpressured FakeSocket and assert it resumes receiving frames; reference
createSessionWithSocket, FakeSocket, attachedClients, broadcastEvent and
broadcast when locating where to add the test.

441-444: Prefer advancing time through a seam instead of mutating Session internals.

Overwriting backpressureWarnLastAt makes this assertion depend on the current implementation of the rate limiter rather than the 5-second contract. Injecting a clock or stubbing Date.now() would keep the test resilient to internal refactors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/terminal-host/session.test.ts` around lines 441 - 444,
The test directly mutates the Session internal field backpressureWarnLastAt;
instead, replace that mutation with a time seam (e.g., stub Date.now() or use a
fake timer) or use an injected clock on the Session so the test can advance time
by >5 seconds without touching internals; update the failing test in
session.test.ts to call the seam (or advance the fake clock) to simulate
Date.now() being 5+ seconds earlier and assert behavior, referencing the Session
instance and the backpressure rate-limit contract rather than the private
backpressureWarnLastAt field.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/desktop/src/main/terminal-host/session.test.ts`:
- Around line 267-339: create a new test using createSessionWithSocket that
attaches two FakeSocket instances to the session (use the same attachedClients
injection pattern) where one socket's writeFn returns false to simulate
backpressure and the other returns true so it keeps receiving frames; call
(session as ...).broadcastEvent / broadcast repeatedly and assert the
non-backpressured socket receives frames while the backpressured socket is
skipped, then emit a 'drain' on the backpressured FakeSocket and assert it
resumes receiving frames; reference createSessionWithSocket, FakeSocket,
attachedClients, broadcastEvent and broadcast when locating where to add the
test.
- Around line 441-444: The test directly mutates the Session internal field
backpressureWarnLastAt; instead, replace that mutation with a time seam (e.g.,
stub Date.now() or use a fake timer) or use an injected clock on the Session so
the test can advance time by >5 seconds without touching internals; update the
failing test in session.test.ts to call the seam (or advance the fake clock) to
simulate Date.now() being 5+ seconds earlier and assert behavior, referencing
the Session instance and the backpressure rate-limit contract rather than the
private backpressureWarnLastAt field.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a80c4e90-66ae-4b87-a86b-b5d2c421caa4

📥 Commits

Reviewing files that changed from the base of the PR and between d98affb and 69ed33a.

📒 Files selected for processing (2)
  • apps/desktop/src/main/terminal-host/session.test.ts
  • apps/desktop/src/main/terminal-host/session.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/main/terminal-host/session.ts

@Kitenite Kitenite changed the title fix(desktop): skip writes to backpressured terminal sockets and rate-limit warnings fix(desktop): rate-limit terminal backpressure warnings Mar 30, 2026
@Kitenite
Copy link
Copy Markdown
Collaborator Author

Kitenite commented Mar 30, 2026

Updated summary for the final branch state:

  • remove the terminal backpressure console.warn
  • keep the existing pause/resume-on-drain handling unchanged
  • no terminal output delivery semantics change
  • no additional test changes are part of the final diff

Why this changed:

The earlier branch went through a few narrower versions while backing out the lossy streaming idea. The final PR is now intentionally minimal: remove the noisy backpressure warning and leave the rest of the behavior alone.

Validation:

bun test apps/desktop/src/main/terminal-host/session.test.ts

@Kitenite Kitenite changed the title fix(desktop): rate-limit terminal backpressure warnings fix(desktop): remove terminal backpressure warning Mar 30, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/desktop/src/main/terminal-host/session.test.ts (1)

15-22: Assert the stdout pause/resume side effect here.

The production path is valuable because it pauses subprocess.stdout on backpressure and resumes it on drain, but this suite only checks clientSocketsWaitingForDrain. A regression that stopped calling pause()/resume() would still pass, because Line 396 keeps writing even while backpressured.

🔧 Tighten the fake and assert the contract
 class FakeStdout extends EventEmitter {
+	paused = false;
+	pauseCalls = 0;
+	resumeCalls = 0;
+
 	pause() {
+		this.paused = true;
+		this.pauseCalls++;
 		return this;
 	}
 	resume() {
+		this.paused = false;
+		this.resumeCalls++;
 		return this;
 	}
 }

Then return child from createSessionWithSocket() and assert child.stdout.pauseCalls === 1 after the first backpressured write and child.stdout.resumeCalls === 1 after drain.

Also applies to: 267-339, 365-400

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/desktop/src/main/terminal-host/session.test.ts` around lines 15 - 22,
The test must verify that the session pauses and resumes subprocess stdout on
backpressure: enhance FakeStdout to track pause/resume call counts (e.g., add
pauseCalls and resumeCalls incremented in pause() and resume()), have
createSessionWithSocket return the mocked child process, then in the test assert
that after the first backpressured write child.stdout.pauseCalls === 1 and after
emitting 'drain' child.stdout.resumeCalls === 1; also add similar assertions in
the other test blocks mentioned (around lines 267-339 and 365-400) to ensure
pause/resume behavior is covered alongside clientSocketsWaitingForDrain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/desktop/src/main/terminal-host/session.test.ts`:
- Around line 15-22: The test must verify that the session pauses and resumes
subprocess stdout on backpressure: enhance FakeStdout to track pause/resume call
counts (e.g., add pauseCalls and resumeCalls incremented in pause() and
resume()), have createSessionWithSocket return the mocked child process, then in
the test assert that after the first backpressured write child.stdout.pauseCalls
=== 1 and after emitting 'drain' child.stdout.resumeCalls === 1; also add
similar assertions in the other test blocks mentioned (around lines 267-339 and
365-400) to ensure pause/resume behavior is covered alongside
clientSocketsWaitingForDrain.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0578043f-07e5-4715-ac7b-325205fb1704

📥 Commits

Reviewing files that changed from the base of the PR and between 69ed33a and bfeaa39.

📒 Files selected for processing (2)
  • apps/desktop/src/main/terminal-host/session.test.ts
  • apps/desktop/src/main/terminal-host/session.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/desktop/src/main/terminal-host/session.ts

@Kitenite Kitenite merged commit 8ff299c into main Mar 30, 2026
6 checks passed
@Kitenite Kitenite deleted the fix/terminal-backpressure-combined branch March 30, 2026 03:18
siarhei-belavus pushed a commit to siarhei-belavus/localset that referenced this pull request Mar 30, 2026
* fix(desktop): skip writes to backpressured terminal sockets and rate-limit warnings

Combines the fixes from superset-sh#2969 and superset-sh#2962 into a single changeset:

1. Skip writes to backpressured sockets (superset-sh#2969): When a client socket
   signals backpressure (write returns false), subsequent broadcastEvent
   calls skip that socket entirely instead of growing Node's internal
   write buffer without bound. The terminal emulator still processes all
   data so snapshot state stays consistent — the next TUI repaint after
   drain naturally resyncs the display.

2. Rate-limit backpressure warnings (superset-sh#2962): Replace unbounded
   console.warn on every backpressure event with a rate-limited
   warnBackpressure() method. Only one warning is emitted per 5-second
   window; subsequent occurrences are counted and reported in the next
   warning (e.g. '247 similar warnings suppressed'). Under sustained
   high-output commands, a single pane could previously generate 400k+
   identical warnings flooding the daemon log.

Tests cover: writes skipped during backpressure, writes resume after
drain, warning rate-limiting within the 5s window, and suppressed
count reporting after the window elapses.

Closes superset-sh#2969
Closes superset-sh#2962

Co-Authored-By: Mastra Code (anthropic/claude-opus-4-6) <noreply@mastra.ai>

* fix(desktop): preserve terminal lifecycle events under backpressure

* fix(desktop): preserve terminal output under backpressure

* test(desktop): trim terminal host session coverage

* fix(desktop): remove terminal backpressure warning

---------

Co-authored-by: Mastra Code (anthropic/claude-opus-4-6) <noreply@mastra.ai>
(cherry picked from commit 8ff299c)
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.

1 participant