Skip to content

[codex] add v2 end-to-end QA loop#4544

Open
Kitenite wants to merge 4 commits into
mainfrom
animated-shroud
Open

[codex] add v2 end-to-end QA loop#4544
Kitenite wants to merge 4 commits into
mainfrom
animated-shroud

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented May 14, 2026

Summary

Adds a v2-only end-to-end QA loop plan covering the user journeys documented in Superset docs and represented in the desktop, host-service, CLI, and MCP implementation surfaces.

The plan includes:

  • repository/project onboarding, workspace create/import/delete/recovery, panes/tabs, terminal, files, diff/git, setup/teardown/run scripts, presets, ports/browser, chat/agents, tasks/automations, CLI/MCP, and settings/auth/hosts
  • happy paths and edge cases for each journey
  • implementation owners and runnable command sets
  • an execution log from the first automated pass
  • one recorded QA harness finding for renderer stress fixture seeding

Validation

  • bun run lint
  • bun --cwd packages/host-service test src/trpc/router/workspace-creation src/trpc/router/project/utils src/trpc/router/git/v2-diff-surfaces.integration.test.ts test/integration/workspace-create-delete.integration.test.ts test/integration/workspace-cleanup.integration.test.ts test/integration/git.integration.test.ts test/integration/bug-hunt-3.integration.test.ts
  • bun --cwd packages/panes test
  • bun --cwd apps/desktop test src/shared/workspace-run-definition.test.ts src/shared/context src/renderer/routes/_authenticated/_dashboard/utils/workspace-navigation.test.ts 'src/renderer/routes/_authenticated/_dashboard/v2-workspace'
  • bun --cwd apps/desktop test src/renderer/lib/terminal src/main/terminal-host src/main/lib/host-service-coordinator.test.ts src/main/lib/terminal-escape-filter.test.ts
  • bun --cwd packages/host-service test src/trpc/router/terminal src/trpc/router/attachments src/trpc/router/settings src/trpc/router/config src/trpc/router/notifications
  • bun test packages/cli/src/lib/resolve-auth.test.ts
  • bun --cwd packages/mcp test
  • bun --cwd packages/mcp-v2 typecheck

Summary by cubic

Adds a v2 end-to-end QA loop plan and fixes regressions found during the run: sanitize git env for desktop and host-service, batch terminal resize work to keep the UI responsive, and clamp terminal WebSocket close reasons to spec.

  • New Features

    • Added plans/v2-end-to-end-qa-loop.md with scope, test data, and a journey matrix (onboarding, workspaces, panes/tabs, terminal, files/diff/git, setup/run/presets, ports/browser, chat/agents, tasks/automations, settings/auth/hosts).
    • Included runnable command sets for apps/desktop, packages/host-service, packages/panes, packages/cli, packages/mcp, and packages/mcp-v2.
    • Logged the first automated pass and a finding: renderer stress fixture seeding requires an initialized tanstack-db.sqlite.
  • Bug Fixes

    • Strip PAGER, GIT_PAGER, GH_PAGER, and LESS from app-owned git environments in both apps/desktop and packages/host-service to prevent simple-git failures.
    • Batch terminal resize/fit across animation frames with a per-frame cap to reduce layout thrash under heavy pane churn.
    • Truncate terminal WebSocket close reasons to the 123-byte protocol limit to avoid close errors.

Written for commit e1a9776. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Removed problematic pager environment variables to improve git and terminal compatibility
    • Fixed WebSocket connection close handling to prevent protocol violations
  • New Features

    • Optimized terminal resize performance through batched, frame-based updates to reduce redundant work during rapid layout changes
  • Tests

    • Added coverage for environment sanitization, host service configuration, and WebSocket close reason handling
  • Documentation

    • Added end-to-end QA loop documentation covering key user workflows and test scenarios

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

📝 Walkthrough

Walkthrough

This pull request adds three independent safety and performance improvements: Git pager environment sanitization to prevent simple-git conflicts, WebSocket close reason truncation to prevent protocol violations, and terminal resize batching to reduce layout thrashing. It also documents a comprehensive V2 end-to-end QA test plan with execution results and findings.

Changes

Git Pager Environment Sanitization

Layer / File(s) Summary
Shell environment pager-stripping utility
apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.ts, apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.test.ts
Exports SIMPLE_GIT_UNSAFE_ENV_KEYS constant and stripUnsafeSimpleGitEnv helper to remove PAGER, GIT_PAGER, GH_PAGER, and LESS from environment objects. Tests verify all four pager variables are stripped while PATH is preserved.
Host-service coordinator environment sanitization
apps/desktop/src/main/lib/host-service-coordinator.ts, apps/desktop/src/main/lib/host-service-coordinator.test.ts
Imports and applies stripUnsafeSimpleGitEnv to the host-service child process environment. Test verifies pager variables are removed from buildEnv output while HOST_SERVICE_PORT and HOST_SERVICE_SECRET are correctly set.

WebSocket Close Reason Truncation

Layer / File(s) Summary
Safe close reason truncation utility
packages/host-service/src/terminal/terminal.ts, packages/host-service/src/terminal/terminal.test.ts
Adds toSafeWebSocketCloseReason to truncate reason strings to 123 UTF-8 bytes, appending ... when truncated. Centralizes close calls via closeTerminalSocket helper. Tests verify short reasons pass unchanged and long UTF-8 strings are safely truncated with suffix.
Apply safe close across terminal lifecycle
packages/host-service/src/terminal/terminal.ts
Routes PTY daemon disconnect, session disposal, and WebSocket attach error closes through closeTerminalSocket to prevent close-reason protocol violations in all teardown paths.

Terminal Resize Measurement Batching

Layer / File(s) Summary
Resize queue infrastructure
apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts
Introduces MAX_RESIZE_MEASURES_PER_FRAME constant, global RAF/timeout bookkeeping, and a Map to batch terminal resize measurements. Implements scheduler functions to enqueue flushes, manage per-runtime callbacks, and flush measurements in a bounded loop.
Integrate batched resizing into terminal lifecycle
apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts
Routes resize operations through the new queuing system: defers measurement in createResizeScheduler, schedules post-attach measurement in attachToContainer, cancels queued work in detachFromContainer/disposeRuntime, and schedules measurement when appearance updates on an attached runtime.

V2 End-to-End QA Test Plan

Layer / File(s) Summary
QA plan structure and journeys
plans/v2-end-to-end-qa-loop.md
Comprehensive QA documentation including scope, test data model, 12-journey workflow matrix (repository onboarding, workspace management, terminal sessions, diff/git, browser/ports, chat/agents, tasks, settings) with happy paths and edge cases, execution phases, targeted test commands, stress-run environment setup, execution log from 2026-05-14 with pass/fail results, and findings on harness gaps and performance signals.

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • superset-sh/superset#4499: Modifies apps/desktop/src/main/lib/host-service-coordinator.ts environment construction for spawned host-service children; overlaps with this PR's stripUnsafeSimpleGitEnv integration in the same code path.

Poem

🐰 With pager-safe vars and truncated reasons too,
Batched resize frames make terminals smooth and true,
WebSocket messages no longer overflow,
Git ops thrive where pagers fear to go,
And tests ensure quality end-to-end flow! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: adding a v2 end-to-end QA loop documentation plan.
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.
Description check ✅ Passed The PR description follows the template structure with all required sections populated: Summary, Related Issues (implied through QA testing), Type of Change, Testing, and Additional Notes. The description comprehensively covers the changes with clear objectives.

✏️ 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 animated-shroud

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.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 14, 2026

🚀 Preview Deployment

🔗 Preview Links

Service Status Link
Neon Database (Neon) View Branch
Vercel API (Vercel) Open Preview
Vercel Web (Vercel) Open Preview
Vercel Marketing (Vercel) Open Preview
Vercel Admin (Vercel) Open Preview
Vercel Docs (Vercel) Open Preview

Preview updates automatically with new commits

@Kitenite Kitenite marked this pull request as ready for review May 14, 2026 22:49
@capy-ai
Copy link
Copy Markdown

capy-ai Bot commented May 14, 2026

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/host-service/src/terminal/terminal.test.ts (1)

4-18: ⚡ Quick win

Consider adding edge case tests for comprehensive coverage.

The current tests cover the main paths effectively. Adding tests for edge cases would further improve confidence:

  • Empty string input
  • Reason exactly at 123-byte boundary
  • Multi-byte UTF-8 characters near the truncation boundary
  • String that becomes empty after truncation (e.g., first character is very close to limit)
🧪 Suggested additional test cases
 	test("truncates long UTF-8 close reasons to the WebSocket byte limit", () => {
 		const reason = `Terminal session "${"session-".repeat(40)}🔥" not found; create it before connecting.`;
 		const safeReason = toSafeWebSocketCloseReason(reason);
 
 		expect(Buffer.byteLength(safeReason, "utf8")).toBeLessThanOrEqual(123);
 		expect(safeReason.endsWith("...")).toBe(true);
 	});
+
+	test("handles empty string", () => {
+		expect(toSafeWebSocketCloseReason("")).toBe("");
+	});
+
+	test("handles reason at exact byte limit", () => {
+		// "a" is 1 byte, create a 123-byte string
+		const reason = "a".repeat(123);
+		expect(toSafeWebSocketCloseReason(reason)).toBe(reason);
+	});
+
+	test("truncates multi-byte UTF-8 at boundary correctly", () => {
+		// Create string with 4-byte emoji characters near the limit
+		const reason = "🔥".repeat(50); // 200 bytes total
+		const safeReason = toSafeWebSocketCloseReason(reason);
+		
+		expect(Buffer.byteLength(safeReason, "utf8")).toBeLessThanOrEqual(123);
+		expect(safeReason.endsWith("...")).toBe(true);
+		// Verify it's still valid UTF-8 (no partial characters)
+		expect(() => Buffer.from(safeReason, "utf8")).not.toThrow();
+	});
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/host-service/src/terminal/terminal.test.ts` around lines 4 - 18, Add
unit tests for toSafeWebSocketCloseReason to cover edge cases: (1) an empty
string input should return an empty string; (2) a reason whose UTF-8 byte length
is exactly 123 bytes should be preserved unchanged; (3) a reason with multi-byte
UTF-8 characters placed so truncation falls inside a multi-byte sequence should
still produce a valid UTF-8 string and end with "..." when truncated; and (4) a
very short limit case where truncation would remove all characters (e.g., single
multi-byte character exceeding 123 bytes) should return "..." or the function's
defined safe output. Target the test file that contains
toSafeWebSocketCloseReason and add these cases alongside the existing tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts`:
- Around line 181-201: The loop in flushQueuedResizes must isolate per-runtime
failures so one thrown error doesn't abort processing and leave entries
stranded; for each iteration (using runtimesWithQueuedResize, runtime, onResize,
measureAndResize, and MAX_RESIZE_MEASURES_PER_FRAME) ensure you remove the
runtime from runtimesWithQueuedResize up front (or in a finally), then wrap the
call to measureAndResize() and the subsequent onResize?.() in a try/catch that
catches/logs the error and continues to the next runtime; preserve the processed
count behavior and still call scheduleResizeFlush() if items remain so remaining
runtimes are retried.

---

Nitpick comments:
In `@packages/host-service/src/terminal/terminal.test.ts`:
- Around line 4-18: Add unit tests for toSafeWebSocketCloseReason to cover edge
cases: (1) an empty string input should return an empty string; (2) a reason
whose UTF-8 byte length is exactly 123 bytes should be preserved unchanged; (3)
a reason with multi-byte UTF-8 characters placed so truncation falls inside a
multi-byte sequence should still produce a valid UTF-8 string and end with "..."
when truncated; and (4) a very short limit case where truncation would remove
all characters (e.g., single multi-byte character exceeding 123 bytes) should
return "..." or the function's defined safe output. Target the test file that
contains toSafeWebSocketCloseReason and add these cases alongside the existing
tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 45666193-7f70-4282-b5e6-cbe9d3fb7048

📥 Commits

Reviewing files that changed from the base of the PR and between f95b8f4 and 45dee8e.

📒 Files selected for processing (8)
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.test.ts
  • apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.ts
  • apps/desktop/src/main/lib/host-service-coordinator.test.ts
  • apps/desktop/src/main/lib/host-service-coordinator.ts
  • apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts
  • packages/host-service/src/terminal/terminal.test.ts
  • packages/host-service/src/terminal/terminal.ts
  • plans/v2-end-to-end-qa-loop.md

Comment on lines +181 to +201
function flushQueuedResizes() {
resizeFlushRafId = null;
if (resizeFlushTimeoutId !== null) {
clearTimeout(resizeFlushTimeoutId);
resizeFlushTimeoutId = null;
}

let processed = 0;
for (const [runtime, onResize] of Array.from(runtimesWithQueuedResize)) {
runtimesWithQueuedResize.delete(runtime);
if (!runtime.container) continue;
const changed = measureAndResize(runtime);
if (changed) onResize?.();
processed += 1;
if (processed >= MAX_RESIZE_MEASURES_PER_FRAME) break;
}

if (runtimesWithQueuedResize.size > 0) {
scheduleResizeFlush();
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Isolate per-runtime measurement errors in the flush loop.

If measureAndResize throws for any one runtime (e.g., fitAddon.fit() failing on a partially torn-down terminal), the for loop aborts. Since resizeFlushRafId/resizeFlushTimeoutId are already cleared at the top of flushQueuedResizes, the remaining entries stay in runtimesWithQueuedResize with no scheduled flush — they will only be processed if another scheduleMeasureAndResize happens to be called later, and they continue to hold strong references in the meantime.

Wrap each iteration so one bad runtime can't strand the rest.

🛡️ Proposed fix
 	let processed = 0;
 	for (const [runtime, onResize] of Array.from(runtimesWithQueuedResize)) {
 		runtimesWithQueuedResize.delete(runtime);
 		if (!runtime.container) continue;
-		const changed = measureAndResize(runtime);
-		if (changed) onResize?.();
+		try {
+			const changed = measureAndResize(runtime);
+			if (changed) onResize?.();
+		} catch (err) {
+			console.error("terminal-runtime: measureAndResize failed", err);
+		}
 		processed += 1;
 		if (processed >= MAX_RESIZE_MEASURES_PER_FRAME) break;
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function flushQueuedResizes() {
resizeFlushRafId = null;
if (resizeFlushTimeoutId !== null) {
clearTimeout(resizeFlushTimeoutId);
resizeFlushTimeoutId = null;
}
let processed = 0;
for (const [runtime, onResize] of Array.from(runtimesWithQueuedResize)) {
runtimesWithQueuedResize.delete(runtime);
if (!runtime.container) continue;
const changed = measureAndResize(runtime);
if (changed) onResize?.();
processed += 1;
if (processed >= MAX_RESIZE_MEASURES_PER_FRAME) break;
}
if (runtimesWithQueuedResize.size > 0) {
scheduleResizeFlush();
}
}
function flushQueuedResizes() {
resizeFlushRafId = null;
if (resizeFlushTimeoutId !== null) {
clearTimeout(resizeFlushTimeoutId);
resizeFlushTimeoutId = null;
}
let processed = 0;
for (const [runtime, onResize] of Array.from(runtimesWithQueuedResize)) {
runtimesWithQueuedResize.delete(runtime);
if (!runtime.container) continue;
try {
const changed = measureAndResize(runtime);
if (changed) onResize?.();
} catch (err) {
console.error("terminal-runtime: measureAndResize failed", err);
}
processed += 1;
if (processed >= MAX_RESIZE_MEASURES_PER_FRAME) break;
}
if (runtimesWithQueuedResize.size > 0) {
scheduleResizeFlush();
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts` around lines 181
- 201, The loop in flushQueuedResizes must isolate per-runtime failures so one
thrown error doesn't abort processing and leave entries stranded; for each
iteration (using runtimesWithQueuedResize, runtime, onResize, measureAndResize,
and MAX_RESIZE_MEASURES_PER_FRAME) ensure you remove the runtime from
runtimesWithQueuedResize up front (or in a finally), then wrap the call to
measureAndResize() and the subsequent onResize?.() in a try/catch that
catches/logs the error and continues to the next runtime; preserve the processed
count behavior and still call scheduleResizeFlush() if items remain so remaining
runtimes are retried.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 14, 2026

Greptile Summary

This PR fixes three real bugs surfaced by an expanded renderer stress run and adds the QA plan that found them. All three fixes ship with targeted regression tests.

  • Pager env stripping (shell-env.ts, host-service-coordinator.ts): Extracts stripUnsafeSimpleGitEnv and applies it to both the desktop main-process and host-service child-process environments, covering PAGER, GIT_PAGER, GH_PAGER, and LESS to prevent GitPluginError from git \u2265 2.50 / simple-git.
  • WebSocket close reason truncation (terminal.ts): Wraps all socket.close call sites with toSafeWebSocketCloseReason, clamping close reasons to the 123-byte RFC 6455 limit to prevent RangeError during terminal attach cleanup.
  • Batched terminal resize (terminal-runtime.ts): Replaces synchronous measureAndResize calls with a RAF-coalesced queue (MAX_RESIZE_MEASURES_PER_FRAME = 8) to prevent the renderer heartbeat from stalling under high tab/pane churn.

Confidence Score: 4/5

Safe to merge; all three bug fixes are well-targeted and covered by new regression tests, with one minor batch-scheduling edge case worth a follow-up.

The git env stripping and WebSocket close truncation are clean and correct. The batched resize queue is a sound architectural improvement, but the per-frame slot counter increments for hidden terminals even though they exit measureAndResize immediately without doing any layout work — in a high-terminal-count font-change scenario this could delay visible terminals by extra frames.

apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts — the flushQueuedResizes batch counter, and apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.ts — the missing JSDoc on the newly exported function.

Important Files Changed

Filename Overview
apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts Introduces a module-level RAF-batched resize queue to coalesce rapid terminal fit/visibility reads; hidden terminals consume per-frame batch slots even though measureAndResize returns early for them.
apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.ts Extracts pager-stripping logic into exported stripUnsafeSimpleGitEnv; extends coverage to GH_PAGER and LESS; the prior explanatory comment about git ≥ 2.50 was not preserved on the new export.
packages/host-service/src/terminal/terminal.ts Adds toSafeWebSocketCloseReason to clamp close reason strings to the 123-byte RFC 6455 limit; wraps all four socket.close call sites; correctly handles multi-byte UTF-8.
apps/desktop/src/main/lib/host-service-coordinator.ts Calls stripUnsafeSimpleGitEnv on the child-process environment for host-service spawn, ensuring pager vars are stripped consistently across both git call sites.
packages/host-service/src/terminal/terminal.test.ts New test file covering short-reason passthrough and UTF-8 multi-byte truncation for toSafeWebSocketCloseReason.
apps/desktop/src/main/lib/host-service-coordinator.test.ts Adds a test suite verifying that buildEnv strips all four pager env vars while preserving HOST_SERVICE_PORT and HOST_SERVICE_SECRET.
apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.test.ts Adds a test for getProcessEnvWithShellPath confirming all four pager vars are stripped while PATH is preserved.
plans/v2-end-to-end-qa-loop.md New QA plan mapping 12 user journeys with happy paths, edge cases, implementation owners, runnable command sets, and a first-pass execution log including three bugs found and fixed.
Prompt To Fix All With AI
Fix the following 2 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 2
apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts:188-196
**Hidden terminals consume per-frame batch slots unnecessarily**

`measureAndResize` returns `false` immediately for any container that fails `hostIsVisible` (zero `clientWidth`/`clientHeight`), meaning it never calls `fitAddon.fit()`. The `processed` counter still increments for these no-op entries, however, so they compete for the `MAX_RESIZE_MEASURES_PER_FRAME = 8` budget alongside terminals that actually need a layout read. In a font-change scenario with many background-tab terminals queued (e.g. the 128-terminal stress fixture), visible terminals could be pushed into later frames unnecessarily. Adding a `hostIsVisible` guard before `processed += 1` (or before calling `measureAndResize`) would restrict slot consumption to the expensive `fit()` path.

### Issue 2 of 2
apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.ts:205-211
The prior comment block explaining the git ≥ 2.50 / `simple-git` "allowUnsafePager" restriction was removed when this function was extracted. Since `stripUnsafeSimpleGitEnv` is now exported and called from a second callsite (`host-service-coordinator.ts`), future contributors won't know why `GH_PAGER` and `LESS` are treated as unsafe without digging into git history. A brief JSDoc preserves that context.

```suggestion
/**
 * Strips env vars that cause simple-git / git ≥ 2.50 to throw
 * `GitPluginError: Use of "PAGER" is not permitted without enabling
 * allowUnsafePager` on non-interactive callers.  Programmatic git
 * invocations have piped stdout so git skips paging entirely; the
 * stripped keys are safe to remove from app-owned git environments.
 */
export function stripUnsafeSimpleGitEnv(
	env: Record<string, string | undefined>,
): void {
	for (const key of SIMPLE_GIT_UNSAFE_ENV_KEYS) {
		delete env[key];
	}
}
```

Reviews (1): Last reviewed commit: "fix(desktop): address v2 QA regressions" | Re-trigger Greptile

Comment on lines +188 to +196
let processed = 0;
for (const [runtime, onResize] of Array.from(runtimesWithQueuedResize)) {
runtimesWithQueuedResize.delete(runtime);
if (!runtime.container) continue;
const changed = measureAndResize(runtime);
if (changed) onResize?.();
processed += 1;
if (processed >= MAX_RESIZE_MEASURES_PER_FRAME) break;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Hidden terminals consume per-frame batch slots unnecessarily

measureAndResize returns false immediately for any container that fails hostIsVisible (zero clientWidth/clientHeight), meaning it never calls fitAddon.fit(). The processed counter still increments for these no-op entries, however, so they compete for the MAX_RESIZE_MEASURES_PER_FRAME = 8 budget alongside terminals that actually need a layout read. In a font-change scenario with many background-tab terminals queued (e.g. the 128-terminal stress fixture), visible terminals could be pushed into later frames unnecessarily. Adding a hostIsVisible guard before processed += 1 (or before calling measureAndResize) would restrict slot consumption to the expensive fit() path.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/renderer/lib/terminal/terminal-runtime.ts
Line: 188-196

Comment:
**Hidden terminals consume per-frame batch slots unnecessarily**

`measureAndResize` returns `false` immediately for any container that fails `hostIsVisible` (zero `clientWidth`/`clientHeight`), meaning it never calls `fitAddon.fit()`. The `processed` counter still increments for these no-op entries, however, so they compete for the `MAX_RESIZE_MEASURES_PER_FRAME = 8` budget alongside terminals that actually need a layout read. In a font-change scenario with many background-tab terminals queued (e.g. the 128-terminal stress fixture), visible terminals could be pushed into later frames unnecessarily. Adding a `hostIsVisible` guard before `processed += 1` (or before calling `measureAndResize`) would restrict slot consumption to the expensive `fit()` path.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +205 to +211
export function stripUnsafeSimpleGitEnv(
env: Record<string, string | undefined>,
): void {
for (const key of SIMPLE_GIT_UNSAFE_ENV_KEYS) {
delete env[key];
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 The prior comment block explaining the git ≥ 2.50 / simple-git "allowUnsafePager" restriction was removed when this function was extracted. Since stripUnsafeSimpleGitEnv is now exported and called from a second callsite (host-service-coordinator.ts), future contributors won't know why GH_PAGER and LESS are treated as unsafe without digging into git history. A brief JSDoc preserves that context.

Suggested change
export function stripUnsafeSimpleGitEnv(
env: Record<string, string | undefined>,
): void {
for (const key of SIMPLE_GIT_UNSAFE_ENV_KEYS) {
delete env[key];
}
}
/**
* Strips env vars that cause simple-git / git 2.50 to throw
* `GitPluginError: Use of "PAGER" is not permitted without enabling
* allowUnsafePager` on non-interactive callers. Programmatic git
* invocations have piped stdout so git skips paging entirely; the
* stripped keys are safe to remove from app-owned git environments.
*/
export function stripUnsafeSimpleGitEnv(
env: Record<string, string | undefined>,
): void {
for (const key of SIMPLE_GIT_UNSAFE_ENV_KEYS) {
delete env[key];
}
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/desktop/src/lib/trpc/routers/workspaces/utils/shell-env.ts
Line: 205-211

Comment:
The prior comment block explaining the git ≥ 2.50 / `simple-git` "allowUnsafePager" restriction was removed when this function was extracted. Since `stripUnsafeSimpleGitEnv` is now exported and called from a second callsite (`host-service-coordinator.ts`), future contributors won't know why `GH_PAGER` and `LESS` are treated as unsafe without digging into git history. A brief JSDoc preserves that context.

```suggestion
/**
 * Strips env vars that cause simple-git / git ≥ 2.50 to throw
 * `GitPluginError: Use of "PAGER" is not permitted without enabling
 * allowUnsafePager` on non-interactive callers.  Programmatic git
 * invocations have piped stdout so git skips paging entirely; the
 * stripped keys are safe to remove from app-owned git environments.
 */
export function stripUnsafeSimpleGitEnv(
	env: Record<string, string | undefined>,
): void {
	for (const key of SIMPLE_GIT_UNSAFE_ENV_KEYS) {
		delete env[key];
	}
}
```

How can I resolve this? If you propose a fix, please make it concise.

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