refactor(desktop): move Workbar ownership below AppShell - #4692
Conversation
hqhq1025
left a comment
There was a problem hiding this comment.
Reviewed exact head 14f0a65516ea0ad5e32a03fd1c62f24adf148d93 against base/current main 74a20f60c9a7bb6947e2c428f07ca2e83fad92a1.
I found no actionable P0-P3 issues in the reviewed scope.
The change moves useWorkbarController into the feature-owned provider (apps/desktop/src/renderer/features/workbar/ui/workbar-provider.tsx:70), exposes stable imperative shell commands plus the hidden-session external-store projection (apps/desktop/src/renderer/features/workbar/controller/workbar-shell-bridge.ts:51), and keeps the Session rail subscribed through useSyncExternalStore (apps/desktop/src/renderer/features/session-navigation/controller/use-session-navigation-reads.ts:73). I traced the active-session transition, Terminal cleanup, Side Chat cleanup, bridge publish/disconnect, titlebar/host context reads, AppShell command call sites, and E2E fixture path.
Local validation passed: focused Workbar/Session Navigation tests (25/25), Desktop full test suite under Node 24.18.1 (2062/2062), Desktop typecheck, production Desktop build, renderer architecture checks (71/71), AppShell hook ratchet, lint, format check, ASF headers, and git diff --check. The merge tree is clean and current main equals the PR base.
Hosted CI has not executed: the CI run is terminal action_required with no jobs, and only the label check is green. I also did not run an interactive Electron/macOS smoke test. This is a refactor, so the final design and merge decision remains with a human reviewer.
Automated review notice: This review was produced with AI assistance and does not replace independent human review.
|
Thanks—moving ownership out of AppShell while keeping a single Workbar controller is a useful direction. #4789 has now merged, so please rebase onto current main and adapt the provider/bridge to the new TabList and open/close menu. Keep the retired Tasks face, drag-reordering, and preview paths removed. Then validate the focused Workbar behavior and refresh CI; the earlier checks don't establish compatibility with the new shell. AI-assisted review with Codex. |
14f0a65 to
d2e59fc
Compare
|
Rebased onto current Validation on signed, GitHub-verified head
The branch is mergeable again; hosted CI is awaiting repository-side execution. |
hqhq1025
left a comment
There was a problem hiding this comment.
Reviewed exact head d2e59fc2dddedf32c8403d6ae6c486bdc05f7751 against base 2310035a38c1db9f210252a03ce2c741e86ff878 and current main 42fa4d070504e672173c46dd75f7fcabb88789f5.
The change moves useWorkbarController into a feature-owned provider, publishes stable shell commands plus an equality-selected hidden-session store through a per-shell bridge, and moves the host/titlebar reads to narrow contexts. I checked controller/resource lifecycles, bridge publication/disconnect behavior, Session rail updates, shell call sites, E2E fixture behavior, Storybook seams, and the architecture boundary.
No P0-P2 issue was found. One P3 architecture-guard gap is noted inline. Local verification passed: build:test; 28 focused Workbar/Session Navigation tests; Desktop 2336/2336; Desktop typecheck; renderer production build; renderer architecture 101/101; AppShell hook ratchet; Biome lint/format; ASF headers; and git diff --check. A StrictMode production-component probe also confirmed bridge command publication, hidden-session propagation to an ancestor external-store reader, and reset on unmount.
This head is not merge-ready: GitHub reports CONFLICTING / DIRTY against current main, with conflicts in app-shell.tsx, renderer-architecture.json, and the Astryx inventory. The hosted test check is green for this head, but the conflict resolution will require a new exact-head review. Interactive Electron/macOS behavior was not independently exercised.
Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.
819c8b4 to
663abf3
Compare
|
Rebased onto current Three files conflicted:
Verification, in the order the workspace requires (
@Astro-Han this is up to date with the post-#4789 surface again and ready for another look whenever you have time. |
Astro-Han
left a comment
There was a problem hiding this comment.
Thanks for rebasing this. I reviewed 663abf3 and the ownership move looks sound: there is still one Workbar controller, with stable shell commands and a narrow hidden-session projection. A real Electron probe with the production provider/controller confirmed that toggling and opening a tool do not re-render the shell; hidden-session updates and unmount cleanup also worked under StrictMode. I found one P2 below.
The current required CI run also fails in Desktop Knip on the two unused type exports in features/workbar/testing.ts: WorkbarControllerCommands and WorkbarControllerSelectors. Please remove those exports and refresh the checks alongside the small fix below. This does not need another architecture rewrite.
Review assisted by Codex, with source tracing and focused real Electron probes; no installed-Desktop end-to-end resource test was run.
| shellObscured: boolean; | ||
| modelChoices: readonly ChatModelChoice[]; | ||
| reportError(title: string, description: string, sessionId: string): void; | ||
| reportError(sessionId: string, title: string, description?: string): void; |
There was a problem hiding this comment.
[P2] Migrate the Terminal Stop error call to the new argument order
Could you also reorder the reportError call in closeTabs (lines 439–443)? The signature now takes (sessionId, title, description), and WorkbarProvider receives AppShell’s showSessionError directly, but the terminal.stop rejection still passes (stopFailed, localizedError, ownerSessionId). In a real Electron provider/controller probe, rejecting Stop for session a produced ["Could not stop terminal", "Could not stop terminal", "a"]. The toast therefore displays the session ID as its description and associates diagnostics with the error title instead of the real session. The two other call sites were migrated correctly. Please move ownerSessionId to the first argument here and assert the captured error arguments in the existing failed-close/retry test.
中文
停止 Terminal 失败是正常故障路径。接口已改成 sessionId、title、description,但 closeTabs 中这一处仍用旧顺序。真实 Electron 探针确认提示描述变成会话 ID,诊断关联的 sessionId 变成错误标题。把 ownerSessionId 移到第一个参数,并在已有失败关闭/重试测试中补错误参数断言即可。
There was a problem hiding this comment.
Thanks, this was a real bug on the old branch. By the time I rebuilt on main, #5374/#5352 had replaced reportError with a toastApi input, and the closeTabs Stop rejection now calls toastApi.error(stopFailed, message, undefined, { sessionId: ownerSessionId }). So the argument-order hazard is gone at the source rather than patched.
I still took your second ask: the existing failed-close/retry test now passes a recording toastApi and asserts that every Stop failure is reported with sessionId: 'a', and that the Session id never appears as the title or description (8e93a897d).
One side observation from that test, not changed here: pressing Close twice while the first Stop is in flight coalesces into one Stop request (TerminalCloseIntents), but each close attaches its own .catch, so one failed Stop shows two identical toasts. Happy to open a separate issue if that's worth fixing.
9e38345 to
8e93a89
Compare
|
@Astro-Han thanks for the Electron probe and the review. Sorry this sat. By the time I got back to it, Your two asks
What changed in the design: it now follows the shape #5505/#5509 settled on. Verification: Desktop |
hqhq1025
left a comment
There was a problem hiding this comment.
Reviewed exact head 8e93a897df8ba287b3c8caea69d549e0b29a8221 (22 files). The Workbar controller now has one owner in WorkbarProvider (apps/desktop/src/renderer/features/workbar/ui/workbar-provider.tsx:34-56); WorkbarShellRoot and its bridge expose stable commands and only the shell-visible readiness, collapse, and hidden-Session state (workbar-shell-root.tsx:26-47, workbar-shell-bridge.ts:62-114). AppShell passes inputs instead of owning the controller (app-shell.tsx:2096-2119), while the host/titlebar read the provider model. The prior Terminal Stop error argument-order issue no longer applies to this base: the failure path passes { sessionId: ownerSessionId } to toastApi.error (use-workbar-controller.ts:661-669), and the updated test checks that Session ownership is not displayed as the error text (workbar-controller.test.ts:688-744). The old unused testing exports are narrowed in features/workbar/testing.ts:59-67. I found no substantiated P0–P3 issue in the inspected paths.
Node 24 clean install and build:test pass; 45 focused Workbar tests, 112 architecture-checker tests and the architecture check, knip --workspace apps/desktop, surface inventory, diff check, and merge-tree against current main pass. The PR is currently mergeable, but GitHub reports no checks for this head, so this is not a merge-readiness or human design approval. I did not run a packaged Electron or cross-platform interactive Workbar smoke; please validate those behaviors and the current-head CI before merging.
Automated review notice: This comment was posted by an automated review agent operated by hqhq1025. It is not an independent human review and does not replace one.
WorkbarProvider becomes the sole owner of useWorkbarController, registered in controllerOwners, and WorkbarShellRoot hands AppShellContent a narrow projection beside the Task Entry, Overlays and Session Collaboration roots. The shell body calls no Workbar hook. The shell keeps stable command delegates plus the three values it renders from: hidden companion Session ids, right-panel collapse and readiness. The bridge replaces that state only when one of them changes, so tab switches, panel topology and resize drags re-render the provider and the host model's readers instead of the shell. The Workbar width reaches the frame and the titlebar through an inherited custom property. Storybook renders environment-free WorkbarHostView and WorkbarTitlebarActionsView. Refs apache#4582. Generated-by: Claude Code Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8e93a89 to
cca7908
Compare
|
Thanks @hqhq1025. Re-run on the new head against current As you noted, GitHub shows no checks for this head; the workflow runs are waiting for maintainer approval. @Astro-Han, could you approve the CI run when you have a moment? |
|
CI approved and running! |
Astro-Han
left a comment
There was a problem hiding this comment.
Re-checked at this head. cca79088 is a rebase of the previously reviewed 8e93a897 onto current main: git range-diff between the two shows the PR's own change is identical, apart from the regenerated totals line in docs/astryx-surface-file-inventory.md (301 → 302 files), which follows from main. The earlier review of 8e93a897 therefore still applies: Workbar controller ownership moves into WorkbarProvider, AppShell passes inputs, the Terminal Stop error path passes the session as options rather than as the message, and no P0–P3 issue was found. The branch merges cleanly; current-head CI is still running.
Automated review notice: This review was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.
Astro-Han
left a comment
There was a problem hiding this comment.
Approved at @Astro-Han's explicit request: a behaviour-preserving refactor with no findings across several automated review rounds; this head is a rebase of the reviewed one. Merge follows once required CI passes.
Summary
useWorkbarControllerownership fromAppShellContentinto a feature-ownedWorkbarProvider, registered incontrollerOwnersas its only caller.WorkbarShellRootbesideTaskEntryRoot/OverlaysRoot/SessionCollaborationDialogRoot. It owns a per-shell bridge and handsAppShellContenta projection, so the shell body calls no Workbar hook (AppShell gate: 34 → 33 hooks, 56 → 55 call sites).rightCollapsed(WorkHub dock) andready(WorkHub navigation). The bridge replaces that state only when one of those values changes. Tab switches, panel topology and resize drags re-render the provider and the host model's readers, not the shell.WorkbarHostandWorkbarTitlebarActionsread the host model from the provider's context. The Workbar width reaches the frame and titlebar through an inherited--maka-session-workbar-width, soappShellFrameStyleno longer carries it. Storybook renders the environment-freeWorkbarHostView/WorkbarTitlebarActionsView.AppShellContentbuilt before; it is now passed to the provider.Refs #4582
Verification
Rebuilt on
main(re-rebased ontoc171eacd0on 2026-09-27; the original branch was 231 commits behind; the controller had moved totoastApi, WorkHub and Work Board inputs in the meantime).npm run build:test, Desktop typecheck (including stories),build:renderertest:dist: 2933/2933workbar-provider-scope,workbar-boundary,workbar-controller,app-shell-frame-style): 45/45. The new provider-scope test drives the realWorkbarShellRoot→WorkbarProvidercomposition and asserts that a tool switch re-renders the host but not the shell, while hidden-fork and collapse changes re-render it exactly once.check:renderer-architecture -- --base origin/main --strict-basepasses; renderer architecture fixtures 112/112npm run lint,npm run format:check, Desktop/UI Knip, ASF headers,git diff --checkAI use
Select exactly one:
Tool(s) and scope: Claude Code rebuilt this branch on current
main(implementation, tests, documentation and local validation). The first revision was assisted by OpenAI Codex. The commit carries theGenerated-by: Claude Codetrailer.Checklist
Does this PR entail a change in behavior?
🤖 Generated with Claude Code