fix: guard TUI render during session transitions to prevent freeze (#1658)

The progress widget's render() synchronously accesses sessionManager
state via cmdCtx. When newSession() is in-flight, this can block the
TUI input loop, freezing the terminal. Guard render() to return the
last cached frame while a session switch is in progress.

Closes #1653

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
TÂCHES 2026-03-20 15:29:34 -06:00 committed by GitHub
parent cc2c887948
commit dfe7715245
2 changed files with 12 additions and 1 deletions

View file

@ -390,6 +390,8 @@ export interface WidgetStateAccessors {
getCmdCtx(): ExtensionCommandContext | null;
getBasePath(): string;
isVerbose(): boolean;
/** True while newSession() is in-flight — render must not access session state. */
isSessionSwitching(): boolean;
}
export function updateProgressWidget(
@ -460,6 +462,14 @@ export function updateProgressWidget(
render(width: number): string[] {
if (cachedLines && cachedWidth === width) return cachedLines;
// While newSession() is in-flight, session state is mid-mutation.
// Accessing cmdCtx.sessionManager or cmdCtx.getContextUsage() can
// block the render loop and freeze the TUI. Return the last cached
// frame (or an empty frame on first render) until the switch settles.
if (accessors.isSessionSwitching()) {
return cachedLines ?? [];
}
const ui = makeUI(theme, width);
const lines: string[] = [];
const pad = INDENT.base;

View file

@ -195,7 +195,7 @@ import {
postUnitPostVerification,
} from "./auto-post-unit.js";
import { bootstrapAutoSession, type BootstrapDeps } from "./auto-start.js";
import { autoLoop, resolveAgentEnd, type LoopDeps } from "./auto-loop.js";
import { autoLoop, resolveAgentEnd, isSessionSwitchInFlight, type LoopDeps } from "./auto-loop.js";
import {
WorktreeResolver,
type WorktreeResolverDeps,
@ -1129,6 +1129,7 @@ const widgetStateAccessors: WidgetStateAccessors = {
getCmdCtx: () => s.cmdCtx,
getBasePath: () => s.basePath,
isVerbose: () => s.verbose,
isSessionSwitching: isSessionSwitchInFlight,
};
// ─── Preconditions ────────────────────────────────────────────────────────────