Skip to content

Fix remote mode worktree operations and close session render crash#226

Merged
PureWeen merged 5 commits intomainfrom
fix/remote-mode-worktree-close-dialog
Feb 26, 2026
Merged

Fix remote mode worktree operations and close session render crash#226
PureWeen merged 5 commits intomainfrom
fix/remote-mode-worktree-close-dialog

Conversation

@PureWeen
Copy link
Copy Markdown
Owner

Summary

Fixes remote mode (mobile) worktree operations from PR #205 and a desktop Blazor render crash when closing sessions with worktrees.

Remote Mode Fixes

  • Atomic worktree+session creation: Single create_session_with_worktree bridge command replaces multi-step orchestration that caused race conditions
  • DeleteGroup remote mode: Routes worktree removal through bridge client
  • Organization sync: push_organization message keeps mobile in sync after multi-agent team creation
  • Initial connect optimization: Only sends history for the active session (was sending all 30+ sessions)
  • Stale broadcast guard: _recentlyClosedRemoteSessions prevents SyncRemoteSessions from re-adding closed sessions
  • Branch deletion: deleteBranch field plumbed through RemoveWorktreePayload

Desktop Close Session Crash Fix

  • Root cause: The close confirmation dialog was portaled to document.body via JS to escape overflow:hidden on sidebar ancestors. This broke Blazor's index-based DOM tracking — when the SessionListItem component was removed during session close, Blazor tried to removeChild on nodes that were no longer in its DOM tree, causing render batch corruption (Received unexpected acknowledgement for render batch N).
  • Fix: Replaced the Blazor-rendered dialog with a pure JS dialog (window.showCloseSessionDialog) that creates/destroys DOM elements entirely outside Blazor's tree. The JS function returns a Promise with the user's choices. Blazor never tracks these elements, so no desync occurs.

Tests

  • 4 new tests for remote mode close session behavior
  • All 1419 tests pass

Remote mode (mobile) fixes:
- Add atomic create_session_with_worktree bridge command
- Fix DeleteGroup to use bridge for worktree removal in remote mode
- Fix CreateGroupFromPresetAsync to use bridge for worktree creation
- Add push_organization bridge message for multi-agent team sync
- Only send history for active session on initial bridge connect
- Add _recentlyClosedRemoteSessions guard against stale broadcasts
- Plumb deleteBranch through RemoveWorktreePayload

Desktop close session crash fix:
- Replace Blazor-rendered close dialog with JS-rendered dialog
- Root cause: portaling Blazor elements to document.body broke DOM
  index tracking, causing render batch corruption on component removal
- JS dialog (window.showCloseSessionDialog) creates/destroys DOM
  elements outside Blazor's tree, avoiding desync entirely

Includes 4 new tests for remote mode close session behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PureWeen PureWeen force-pushed the fix/remote-mode-worktree-close-dialog branch from 3226bea to 06ef716 Compare February 26, 2026 16:40
Code review fixes:
- Fix JS showCloseSessionDialog to use PascalCase property names
  matching C# CloseDialogResult record (Confirmed, DeleteWorktree,
  DeleteBranch). Blazor JSON deserialization is case-sensitive.
- Wrap Organization.Sessions.Add in InvokeOnUI() in
  CreateSessionWithWorktreeAsync (was called from background thread)
- Wrap Organization assignment in InvokeOnUI() in WsBridgeServer
  PushOrganization handler (was called from WebSocket thread)
- Change InvokeOnUI from private to internal for WsBridgeServer access

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PureWeen PureWeen force-pushed the fix/remote-mode-worktree-close-dialog branch from 06ef716 to a908849 Compare February 26, 2026 16:41
PureWeen and others added 3 commits February 26, 2026 10:42
Agents should always add new commits on top, never amend, unless
explicitly asked. This preserves history for reviewers and other agents.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add InvokeOnUIAsync() for awaitable UI thread marshaling
- WsBridgeServer CreateSessionWithWorktree: await UI thread completion
  before broadcasting state, ensuring clients receive consistent data
- WsBridgeServer PushOrganization: await UI thread before broadcasting
- Mobile OnOrganizationStateReceived: move Organization assignment
  inside InvokeOnUI so sidebar sees the new session immediately

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address review findings:
- PushOrganization: move SaveOrganization/FlushSaveOrganization inside
  InvokeOnUIAsync callback so serialization of Organization.Sessions
  happens on the UI thread, preventing races with UI-thread mutations
- CreateSessionWithWorktree: marshal entire CreateSessionWithWorktreeAsync
  call onto UI thread via InvokeOnUIAsync(Func<Task>) since the non-remote
  path calls ReconcileOrganization which mutates Organization.Sessions
- Add InvokeOnUIAsync(Func<Task>) overload for marshaling async operations
  to the UI thread while preserving async/await yield behavior

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PureWeen PureWeen merged commit 0e40ea3 into main Feb 26, 2026
@PureWeen PureWeen deleted the fix/remote-mode-worktree-close-dialog branch February 26, 2026 17:31
PureWeen added a commit that referenced this pull request Feb 26, 2026
Previous fix removed the JS DOM portal but kept the dialog in Blazor's
render tree. The overlay showed but the dialog was clipped/invisible
due to ancestor overflow constraints that position:fixed alone can't
escape in WebView with scoped CSS.

Adopt the approach from PR #226: render the close-confirmation dialog
entirely via JS (window.showCloseSessionDialog), creating DOM elements
imperatively and appending to document.body. Blazor never tracks these
elements, so no render batch desync on open/close/delete.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit that referenced this pull request Feb 26, 2026
Previous fix removed the JS DOM portal but kept the dialog in Blazor's
render tree. The overlay showed but the dialog was clipped/invisible
due to ancestor overflow constraints that position:fixed alone can't
escape in WebView with scoped CSS.

Adopt the approach from PR #226: render the close-confirmation dialog
entirely via JS (window.showCloseSessionDialog), creating DOM elements
imperatively and appending to document.body. Blazor never tracks these
elements, so no render batch desync on open/close/delete.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit that referenced this pull request Feb 26, 2026
Previous fix removed the JS DOM portal but kept the dialog in Blazor's
render tree. The overlay showed but the dialog was clipped/invisible
due to ancestor overflow constraints that position:fixed alone can't
escape in WebView with scoped CSS.

Adopt the approach from PR #226: render the close-confirmation dialog
entirely via JS (window.showCloseSessionDialog), creating DOM elements
imperatively and appending to document.body. Blazor never tracks these
elements, so no render batch desync on open/close/delete.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit that referenced this pull request Feb 27, 2026
Previous fix removed the JS DOM portal but kept the dialog in Blazor's
render tree. The overlay showed but the dialog was clipped/invisible
due to ancestor overflow constraints that position:fixed alone can't
escape in WebView with scoped CSS.

Adopt the approach from PR #226: render the close-confirmation dialog
entirely via JS (window.showCloseSessionDialog), creating DOM elements
imperatively and appending to document.body. Blazor never tracks these
elements, so no render batch desync on open/close/delete.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen added a commit that referenced this pull request Feb 27, 2026
Previous fix removed the JS DOM portal but kept the dialog in Blazor's
render tree. The overlay showed but the dialog was clipped/invisible
due to ancestor overflow constraints that position:fixed alone can't
escape in WebView with scoped CSS.

Adopt the approach from PR #226: render the close-confirmation dialog
entirely via JS (window.showCloseSessionDialog), creating DOM elements
imperatively and appending to document.body. Blazor never tracks these
elements, so no render batch desync on open/close/delete.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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