fix(session): coalesce resize storms, gate size pushes to the owning pane - #374
Merged
Conversation
…pane (#373) Production logs showed ~50 cols-gate ring purges in 90s of launch layout churn, wiping the resume transcripts the correctly-sized forks had just painted. Three fixes, purge semantics unchanged: - Trailing-debounce cols changes for clears-on-resize runtimes in SessionManager::resize (175ms quiescence): one settled ioctl per storm, purge only when the settled cols differ from the ring's; a round-trip storm skips the purge and forces the restoring repaint via the rows nudge. The settle runs as one critical section under the state lock, aborting on killed/resuming/handle-gone, so it can never target a child spawned after the storm. Rows-only and shell resizes stay synchronous; update_last_size stays prompt; a failed ioctl still cannot purge. - Settle-gate transitional panes: unplaced terminal wrappers are held at 0x0 until pane geometry sync places them from a real rect, so a shrink-wrapped 80x24 canvas can no longer be measured and pushed as a ~78x23 garbage fit. - Single-writer per session: sizePushVerdict routes every backend size push; only the visible owning pane writes, hidden-pool and background mounts refit locally without writing, and the owner-only viewport clear pairs 1:1 with pushes the settle will repaint. Extends the #372 instrumentation in runner.log (coalesced counts, round-trip settles, suppressed pushes, pane placement), corrects the resize-frequency assumption in impl 0024 decision 5, and records the decisions in docs/impls/0039-resize-storm-coalescing.md.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #373.
The v0.4.7
[launch-dims]instrumentation proved the launch-resume corruption was not a fork-width bug: forks landed at caller-supplied real widths, and what destroyed the panes was ~50 cols-gate ring purges in 90 seconds of post-launch layout churn — each purge dropping the resume transcript claude had just repainted. Purge semantics themselves stay (impl 0020 / #306): given genuinely stale-width bytes, purging remains correct. This PR removes the churn that fed it.The three defects → three fixes
SessionState::pending_resize; every further push folds in and extends the deadline; a per-storm settle thread applies one final ioctl after 175ms quiescence and purges only when the settled cols differ from the cols the ring holds. A drag that ends where it began costs zero purges and exactly one restoring repaint (rows-nudge, since a same-sizeTIOCSWINSZis a kernel no-op and the owner pane cleared its viewport during the drag). Rows-only resizes stay synchronous — preserving the activation dance's SIGWINCH nudge — as do shells.update_last_sizestays prompt, and a failed ioctl still cannot purge. The settle runs as one critical section under the session state lock, aborting onkilled/resuming/handle-gone:killsetskilledunder that lock beforeruntime.stopbegins andresumesetsresumingbeforeruntime.spawncan overwrite the id→PTY mapping, so a stale settle can never physically resize a replacement child.createPaneGeometry(moved tosrc/lib/paneGeometry.ts) holds unplaced wrappers at 0×0 until they're positioned from a real pane-body rect — an unplaced absolute wrapper otherwise shrink-wraps xterm's default 80×24 canvas into a measurable, meaningless box. Zero size keeps the pane unmeasurable, so the existing rect guards hold every fit and push.sizePushVerdictgates every backend size push: only the visible owning pane writes; hidden-pool wrappers and background tabs refit locally without writing; activation hands ownership over. Single-window case only — bug: two windows on the same chat — terminal size state desyncs across primary flips (stale lastPushed dedupe, dual-primary push race, wrong-cols replay) #313's multi-window protocol is out of scope.Instrumentation extends #372 in the same
runner.log:cols-gate purge … (N coalesced),cols-gate settle … round-trip, purge skipped, repaint nudged, and frontend[resize-gate]suppression/placement lines. Docs: correction note on impl 0024 decision 5, full decision record indocs/impls/0039-resize-storm-coalescing.md.Tests
sizePushVerdict(owner pushes, transitional/non-owner push nothing),shouldClearViewportBeforePush(clears pair only with real pushes),createPaneGeometryplacement gate.Checks
cargo test --workspace562 passed / 0 failed ·pnpm exec tsc --noEmitclean ·pnpm run lint0 problems ·pnpm test258 passed. Peer-reviewed via Runner crew: two must-fix rounds (round-trip blank viewport, settle/lifecycle race) resolved; final review clean.Manual smoke (post-merge)
pane-placedbeforefirst-fit, coalesced settle lines, no transient-width purges; scroll history intact.🤖 Generated with Claude Code