refactor(tray): extract App-owned surface lifetimes - #1085
Conversation
|
Codex review: needs maintainer review before merge. Reviewed August 13, 2026, 2:02 AM ET / 06:02 UTC. ClawSweeper reviewWhat this changesThis PR moves native tray and companion-window creation, reuse, theming, callbacks, and shutdown from App into dedicated App-owned services. Merge readinessKeep open. Current main still keeps the tray and companion-window lifetime code in App, while this collaborator PR is dirty against main and needs conflict resolution plus exact-current-head proof before merge. Likely related people: bkudiess, high confidence from the two preceding merged tray layers. Priority: P2 Review scores
Verification
How this fits togetherThe WinUI tray app coordinates native notification-area UI and companion windows with gateway, settings, and local-MCP services. App receives user and MCP actions, then routes them to the tray and window owners. flowchart LR
Actions[Tray and MCP actions] --> App[Application root]
App --> Tray[Tray surface owner]
App --> Windows[Window surface owner]
Tray --> Menu[Tray icon and menu]
Windows --> Surfaces[Hub chat setup windows]
App --> Runtime[Connection settings and node services]
Decision needed
Why: The branch is not mergeable against current main, and resolving the overlap determines who verifies the combined lifecycle behavior. Before merge
Agent review detailsSecurityNone. Review metrics
Merge-risk optionsMaintainer options:
Technical reviewBest possible solution: Rebase or reconstruct the A2 extraction on current main, preserve the merged A0/A1 ownership boundaries, then validate real tray, window reuse, routing, and shutdown behavior from the resulting exact head. Do we have a high-confidence way to reproduce the issue? Not applicable as a bug report. The remaining question is whether the refactor preserves existing behavior after current-main conflict resolution. Is this the best way to solve the issue? Unclear. The requested ownership direction matches the architecture ledger, but the current dirty branch and non-current-head proof prevent confirming this exact implementation as the best landing path. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against bc518f7bd158. LabelsLabel changes:
Label justifications:
EvidenceWhat I checked:
Likely related people:
Rank-up movesOptional improvements that raise the rating; they are not merge blockers.
Rating scale
Overall follows the weaker of proof and patch quality. Workflow
HistoryReview history (22 earlier review cycles; latest 8 shown)
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
7d5f245 to
378197f
Compare
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: ed0ab729-86e9-48b1-a0e4-57eeadaa99c3
378197f to
6501249
Compare
Current-head UI proofCaptured from the isolated Dev-identity app at commit Connection HubSetup windowThe Setup deep link was invoked twice and UI Automation confirmed that exactly one Setup window remained, demonstrating window reuse. Permissions HubThe existing Hub window navigated from Connection to Permissions. The local device name was blurred before upload, and all unredacted screenshot files were deleted. |
|
ClawSweeper status: review started. I am starting a fresh review of this pull request: refactor(tray): extract App-owned surface lifetimes This is item 1/1 in the current shard. Shard 0/1. This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking. Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted. |



Moves concrete tray and non-tray window lifetimes out of
App.xaml.csinto two focused App-owned services. This is layer 3 of the App architecture stack and depends on #1082 and #1075.What changed
TrayControlleras the authoritative owner of tray icon, tray menu, live toggle/control references, event subscriptions, popup coordination, status/tooltip application, and dispose-once behavior.WindowManageras the authoritative owner of concrete keep-alive, Hub, Chat, connection-status, and Setup window creation/reuse/focus/theme/close lifetime plus App-level Canvas routing.TrayMenuPresenter/TrayMenuRenderer/ConnectionTogglePresenter; TrayController does not interpret menu semantics.TrayMenuWindowownership of HWND, DPI/monitor positioning, focus/light-dismiss, keyboard navigation, cascade, and measurement mechanics.NodeServiceownership of the actual Canvas window.Ownership transfer
App.xaml.csconcrete tray/window fields, construction, subscriptions, show/hide/focus/theme/close, and disposal mechanics.ITrayController/TrayControllerandIWindowManager/WindowManager.Validation
Audited at exact head
7d5f24587a67c7af704d0b90c18c3f7e8dc148c2:./build.ps1: all 5 projects passedOpenClaw.Shared.Tests: 3,415 passed, 32 skipped, 0 failedOpenClaw.Tray.Tests: 2,205 passed, 0 skipped, 0 failedwin-x64): 19/19 passedHosted exact-head checks
CodeQL analyses from advanced configurations cannot be processed when the default setup is enabled. This is a default/advanced CodeQL configuration conflict, not an A2 build or source failure.Build and Testworkflow has noworkflow_dispatchentry and its pull-request trigger targetsmain/master, so this feature-based stack layer cannot safely manufacture that hosted check. Maintainer retargeting or merge-queue execution is still required for that signal.Real behavior proof
Current-head isolated runtime proof showed:
app.statusandapp.menureturned the expected disconnected/idle surface state.canvas.presentandcanvas.hidesucceeded; unpaired Canvas visibly routed to Connection while real Canvas ownership remained in NodeService.openclaw://menuforwarded successfully and created the tray-menu window.Current-head Permissions visual proof
This image was captured from the isolated Dev app at exact PR head
7d5f24587a67c7af704d0b90c18c3f7e8dc148c2after MCPapp.navigatevisibly opened Permissions. Privacy crop/redaction only; UI state unchanged. Unrelated desktop content was cropped away and the machine identifier was covered with a solid opaque mask. The original screenshot was not published.810DD5042F3F8808B4AA039F9406D651885179E934FE4A463AFADC75EFB3BE1F1690F62072B97300767016DAFE4D6AB34CB37374308EE81F95A6150D4EFD5835The raw PNG and manifest URLs returned HTTP 200. The downloaded PNG was 1234x804 and matched the local sanitized derivative byte-for-byte.
Reproduction:
Confirm the isolated Companion window visibly routes to Permissions and retains the expected disconnected/local-MCP surface state.
Warning
Tray/menu media: Not verified / blocked. The only privacy-safe crop removed the tray menu, so no image is published or claimed as tray/menu proof. Runtime transcript, Win32/source contracts, presentation matrices, accessibility tests, and clean shutdown logs cover those paths, but current-head public tray/menu media remains unavailable.
No success is claimed for a paired Canvas dependency that was unavailable in the isolated MCP-only host.
Review
CleanupCompleted. The close path now always awaits cleanup and has a regression guard.ShowHubChatAndStartVoicewas rejected after tracing synchronous WinUIFrame.Navigate/Navigatedbehavior; Sonnet independently reached no-regression confidence of 80% for that specific path.Deferred work
Activation routing, settings-change coordination, startup/bootstrap ownership, and final shutdown orchestration remain in App for the next stack layer. Pairing workflow and actual Canvas lifetime remain with their existing owners.