refactor(desktop): move the AppShell command and project actions' Desktop calls to their owners - #5937
Conversation
…ostics feature The Error Boundary, About and the command palette still called window.maka.diagnostics.copyReport from legacy renderer files after apache#5892 moved the root's diagnostics into features/diagnostics. Each now receives one fixed-surface command from the feature's services instead: - the Error Boundary takes copyRendererCrashReport from an optional RendererCrashReportConsumer; without Desktop composition it still renders its fallback and copies its own browser report; - About takes copyManualReport from ManualDiagnosticReportConsumer; - AppShell takes the same command through that consumer and passes it to the palette in its command options. The Desktop adapter fixes the manual and renderer_crash surfaces and forwards the same fields as before. DiagnosticReportToastProvider now reads its action's words from the shared shell catalog itself, so AppShell no longer reads shell copy to hand them over. No renderer file outside platform/desktop references the diagnostics bridge any more; the ledger drops six bridge paths, and re-adding any of them is new bridgePaths debt. Refs apache#4582 Generated-by: Claude Code
createAppShellProjectActions built twelve project actions for AppShell, but nine of them (add, select, select-no-project, prepare, prepare-default, relink, rename, archive, restore) had no production caller: the rail and the new-task flows have gone through taskEntry.commands since apache#5527. Delete them with their helpers, the project-picker state and refs that only addProject used, and the onProjectSelected callback that only those actions fired. The two live actions move to Task Entry, whose README already names it the Task Entry / Workspace owner: - TaskEntryServices gains folders.openProjectFolder(sessionId?) and openWorkspaceFolder(). The Desktop adapter opens a task's folder through the task and anything else on the default Runtime Host, and names the task or Host profile a refusal or failure belongs to. - The controller exposes openProjectFolder / openWorkspaceFolder commands and reports failures in the same shell copy, targets and workspace-unavailable notice as before. TaskEntryError may now carry a sessionId instead of a profileId. - AppShell hands taskEntry.commands to the titlebar and the palette. refreshProjects stays with the workspace projection in use-project-context.ts, inlined on runOnDefaultRuntimeHost. app-shell-project-actions.ts, its workspace-projection ownership entry and the now unused openPathActionErrorMessage are removed. Refs apache#4582 Generated-by: Claude Code
…lays port Five command-palette rows still called the Desktop bridge from app-shell-command-actions.ts: testing a connection, making it the default, testing the network proxy, opening the local memory file and saving the conversation to a file. They now reach Desktop through the palette owner, features/overlays: - OverlaysServices gains a palette port (OverlayPaletteActions) whose Desktop adapter makes the same bridge calls with the same arguments, including the slug form of connections.test/setDefault, which is a different IPC channel from Connection Settings' identity form. - The overlays projection hands the port to AppShell as paletteActions, and AppShell passes it in the command options. The rows keep their default-Host resolution, toasts and failure copy. app-shell-command-actions.ts no longer references window.maka; together with the diagnostics report, its six bridge paths are gone. The overlays entry-surface pin now lists OverlayPaletteActions for that file. Refs apache#4582 Generated-by: Claude Code
Astro-Han
left a comment
There was a problem hiding this comment.
Reviewed 02a1ea720fa1ec82e3e243e2711ed40bfe9469a8 (49 files, +1319/−850, 3 commits).
No P0–P3 findings, at the shape-and-gate depth this card was scoped to.
The ownership move is a removal from the shell. app-shell.tsx goes to +14/−28 (net −14): the command and project actions' Desktop access leaves the shell rather than being copied beside it. The added files are the expected shape for that — owner tests plus an options fixture, with the command-palette Desktop actions getting their own suite.
The ledger only tightens. apps/desktop/renderer-architecture.json moves +11/−61 (net −50), so the AppShell budget shrinks by fifty lines with nothing added back. check-app-shell-hooks.mjs is unchanged, which is consistent rather than a gap: its allowances only need editing when a hook actually leaves the shell, and the absent hook-count change means none of its quotas grew.
Gate on this head: test and label are green; the three commits each carry a Generated-by: trailer and there is no Grok involvement.
What I did not judge
- The internal correctness of the relocated command/project logic beyond the accounting; this card was scoped to shape and gate because a second lineage is doing the per-file pass.
- No Electron run.
I did not approve, request changes, or merge.
Automated review notice: This comment 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.
Independent review of 02a1ea720fa1ec82e3e243e2711ed40bfe9469a8 (base c7fa6bb6a, two commits behind current main 887924e14; it merges cleanly onto main).
Verdict: behaviour-preserving. No P0 or P1 found. One cross-PR knip break shared with #5935 (P3, coordination) and two further P3s are below.
What I checked against main:
- Palette rows.
create-overlays-services.tsmakes exactly the old calls:connections.test(slug, undefined, host),connections.setDefault(slug, host),settings.testNetworkProxy(undefined, host),memory.openFile(host),sessions.saveConversationToFile(input).runOnDefaultRuntimeHost, the toasts and the failure copy stay inapp-shell-command-actions.ts.paletteActionsisservices.palette, so its identity never changes and it is read throughoptionsRef. - Diagnostics. About, ⌘⇧D/palette and the Error Boundary send the same
copyReportpayloads:surface: 'manual'with an optionaltarget, andsurface: 'renderer_crash'with title and details. The Error Boundary is still a class boundary. The new function wrapper sits outside it and only reads the optional services, so a missing provider still leads to the browser-report fallback.DiagnosticReportToastProviderreadsuseUiLocale()under the sameLocaleProviderthat AppShell resolveduiLocalefor, so the labels are unchanged. - Project actions. I confirmed on
mainthatrelinkProject,renameProject,archiveProjectandrestoreProjectare only destructured atapp-shell.tsx:879-881. The rail usestaskEntry.commands.*(app-shell.tsx:967-970).onProjectSelectedwas fired only fromselectProject,selectNoProjectandprepareProject, which are deleted with it. The picker refs were read only by the deletedaddProjectand the bootstrap cleanup, which is removed together with them.openProjectFolder(ownerActiveId)keeps the oldsessionIdsource (useAppShellProjectContext({ sessionId: ownerActiveId })). The adapter keeps the session-vs-default-Host branching and the diagnostic targets, andfolderOpenFailurereproduces the workspace-unavailable branch and the copy, as theopen-path.tshelpers resolved it.refreshProjectshas the same body, and its only consumer is the bootstrap's effect-event handler, so losing the stable identity is harmless.
Local run on this head (build:test): about-settings-page, command-palette-desktop-actions, diagnostics-owner, expected-error-presentation, overlays-*, task-entry-* and use-project-context pass (100/100). CI is green.
P3 (coordination): knip breaks with #5935. (Graded P3: neither PR is wrong on its own; this is a merge-order coordination item, like the protocol-epoch collisions we track across PRs.) This PR deletes app-shell-project-actions.ts, the last importer of showSessionWorkspaceUnavailableToast from the legacy src/renderer/session-workspace-errors.ts once #5935 has moved the chat, revision and turn actions onto its own copy. On main + #5935 + this PR, npx knip --workspace apps/desktop reports Unused files (1) apps/desktop/src/renderer/session-workspace-errors.ts, so whichever of the two lands second fails CI's knip step. Fix in the second PR to land: delete the legacy file and its ledger entry. Better still, #5935 could move the helper into application/contracts/session-workspace-errors.ts.
P3: unstable handler. openProjectFolder (app-shell.tsx:883) is now a fresh closure on every AppShell render and is passed straight to the titlebar's onOpenFolder. The old useStableActions facade kept it stable. The palette reads it through optionsRef, so only the titlebar sees a new prop each commit. useCallback over [taskEntry.commands, ownerActiveId] would restore the stable identity.
P3: second copy of the open-path helpers. folderOpenFailure re-implements openPathActionLabel and openPathFailureCopy from open-path.ts, including the reason in failures fallback (folder-open-failure.ts:39-46). The output matches today, but there are now two copies to keep in sync. Consider moving those two helpers into an application contract and importing them.
Cross-PR / merge order (pairwise git merge-tree against 887924e14):
- #5936: conflicts in
renderer-architecture.jsonand exactly threeapp-shell.tsxhunks (@@ -204,-240,-249). Both PRs wrapAppShellContentin a render-prop root and add one prop. - #5935: conflicts in the ledger and
docs/astryx-surface-file-inventory.md, plus the knip break above. - #5934: no textual conflict. On main + #5934 + this PR, #5934's checker fails with exactly one line:
appShell root uses ManualDiagnosticReportConsumer from features/diagnostics/index.ts, which rootSymbolUses does not record. That is a new export, so--writeadmits it.
This is an automated review by Claude (Anthropic), run on behalf of the maintainer. It is not an approval. Please verify the findings before acting on them.
…ebar opener stable Review follow-ups for apache#5937: - openPathFailureCopy and openPathActionLabel move into the shell copy catalog beside the strings they read. Task Entry's folderOpenFailure now calls them instead of its own copy of the lookup, and open-path.ts re-exports them for Settings and the artifact pane. An application contract cannot hold them: the application zone may not import copy catalogs. The renderer's unused OpenPathKey / OpenPathFailureReason aliases go with the move. - AppShell's openProjectFolder is a useCallback over taskEntry.commands and the owner session, so the titlebar no longer receives a new onOpenFolder on every commit. Refs apache#4582 Generated-by: Claude Code
Astro-Han
left a comment
There was a problem hiding this comment.
Reviewed 50988296abd130ec8d38914847a154aa204a3d3b (51 files, +1344/−874, 4 commits).
Both P3s from my previous pass are fixed, verified in the code.
- The titlebar's folder action has its identity back. My finding was that a stable
useStableActionsfacade method had become a fresh closure every render, and that although the command palette reads it throughoptionsRef(where it is harmless), it also goes straight into the titlebar'sonOpenFolder. At this headopenProjectFolderis auseCallbackover[taskEntry.commands, ownerActiveId]and the titlebar consumes that same value, so the prop's identity is stable between commits again. - The duplicated open-path helpers are merged. I flagged that
folder-open-failure.tsre-implementedopenPathActionLabelandopenPathFailureCopyfromopen-path.ts, fallback included. They now have a single definition inlocales/shell-copy.ts, next to the strings they read;folder-open-failure.tsimports and calls them instead of restating the logic, and other consumers still reach them throughopen-path, which re-exports them. So the output is the same and there is now one place to change.
Gate on this head: test and label are green; mergeable is true against the base branch, so the PR can land on its own. Every commit carries a Generated-by: trailer with no Grok involvement.
What I could not judge
- I did not run the suites; both fixes are verified by reading the callback and its dependency list, the single helper definition, its consumers and the re-export.
- Whether this PR still conflicts with its sibling on
app-shell.tsx— the three overlapping regions I measured last round are in the file this change edits, so the overlap should be re-measured at merge time rather than assumed resolved.
I did not approve, request changes, or merge.
Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.
Conflict only in the generated renderer-architecture.json: taken from main's copy, the workspace-projection ownership entry removed again (its only path, app-shell-project-actions.ts, is deleted on this branch), and regenerated. Generated-by: Claude Code
Astro-Han
left a comment
There was a problem hiding this comment.
Reviewed eb057b0747c34426e33861f6ec5ec606c52cbec3.
The rebase is faithful, so my previous review's conclusions carry over unchanged. A git range-diff between the old and new ranges marks all five commits of this PR as = — byte-identical — the only differences being commits from main. The tree difference against my previously reviewed head was that base advancement alone. The gate is green on the new head.
Merge order and remaining conflicts (measured): against #5934 the overlap is the ledger plus features/overlays/index.ts and testing.ts; against #5935 it is the ledger, app-shell.tsx and both inventory documents; against #5936 it is the ledger, app-shell-effects.ts and app-shell.tsx. All four PRs share one base, so the #5934 → #5935 → #5936 → #5937 order is still required and this one should land last, with its app-shell.tsx resolution reviewed rather than trusted.
What I could not judge
- Verified by
range-diffand file sets; I did not run the suites or perform the merges. - The moved logic was not re-audited, since the rebase left it untouched.
I did not approve, request changes, or merge.
Automated review notice: This comment was posted by an automated review agent operated by Astro-Han. It is not an independent human review and does not replace one.
Brings in apache#5934 and apache#5935. Conflicts were only in generated files: - renderer-architecture.json: taken from main's copy, the workspace-projection ownership entry removed again, and regenerated. The new rootSymbolUses records one admitted appShell use, features/diagnostics: ManualDiagnosticReportConsumer (new export). - docs/astryx-surface-file-inventory.md: regenerated from main's copy. With apache#5935 on main, the legacy src/renderer/session-workspace-errors.ts lost its last importer (the deleted app-shell-project-actions.ts), so it is removed here; knip reports no unused files. Generated-by: Claude Code
Brings in apache#5936. - app-shell.tsx: apache#5936 wraps AppShellContent in OnboardingProjectionRoot where this branch wraps it in ManualDiagnosticReportConsumer. Both are kept, the onboarding root outside, and AppShellContent takes both the onboarding projection and copyManualDiagnosticReport. - renderer-architecture.json: taken from main's copy, the workspace-projection ownership entry removed again, and regenerated. - README retained-root table: the useAppShellProjectContext row now describes the projection it still provides (project mutations and the folder commands are Task Entry's); the transitional-exports table lists ManualDiagnosticReportConsumer. The hook gate matches the merged tree unchanged (25 hooks, 30 call sites). Generated-by: Claude Code
After #5934–#5937, `npm run check:renderer-architecture -- --report` still listed two retained-root rows scheduled for M5. M5's fourth item, "remove migrated legacy exceptions, public raw-state exports and redundant props/helpers", and its fifth, "document every retained root projection/lifecycle with its actual consumer, owner and allowed capability", were also open. This PR closes all three, in five commits: | Commit | What changes | Who loses what | |---|---|---| | `61cad0bcc` | `OnboardingConnectionSeed`, a render-null watch beside the onboarding authority, seeds the default Host's connection projection from each accepted snapshot, or asks it to refresh when onboarding cannot be read. | `AppShellContent` loses its last `useEffect` and the M5 row for it. | | `e485fa592` | `useAppShellProjectContext` stops returning the default Host's project list, the Local Host's projects and the raw selected id. It also drops the Local Host project subscription (`projects.getLocalSnapshot`, `subscribeLocalChanges`), which nothing has read since #5937 moved project mutations to Task Entry. Its row moves from "M5" to "application lifecycle" (see below). | `use-project-context.ts` loses 2 bridge paths and 1 effect. | | `eae997042` | Props nobody reads are removed (details below). A type-level test pins the trimmed contracts. | AppShell stops computing 3 chat-model values and importing `ProviderLogo`. | Generated-by: Claude Opus 5.5
Summary
Refs #4582 — M5, the AppShell-family action helpers outside M3.
This takes the command-palette and project helpers, plus the legacy diagnostics reports, off the Desktop bridge. Each goes to the owner that already holds that area. The AppShell family drops from 43 → 24 bridge references and 6 → 5 action factories. The 24 that remain are the chat, revision, stop and turn actions (M3), the effects, session-list and
app-shell.tsxpaths (separate slices), and the exempt E2E fixture. There is one commit per owner.1. Diagnostics (
features/diagnostics)Three legacy callers still called
window.maka.diagnostics.copyReportafter #5892. Each now gets one command, with its report surface fixed by the adapter:error-boundary.tsx)window.maka?.diagnostics(4 references): the whole namespace, any surfacecopyRendererCrashReport({ title, details })from an optionalRendererCrashReportConsumersettings/about-settings-page.tsx)window.maka.diagnostics.copyReportcopyManualReport()fromManualDiagnosticReportConsumerdiag:copy-diagnosticsand ⌘⇧Dwindow.maka.diagnostics.copyReportcopyManualDiagnosticReport(target)in the command options; AppShell reads it through the same consumerDiagnosticReportToastProviderlabelsprop and the outerAppShell's shell-copy readlocales/shell-copy.tshookCalls-ratcheted. About already reads App Update the same way.createServicesContextgainsuseOptionalServices(5 lines).app-shell.tsx, which has no token headroom. Reading the feature inapp-shell-overlays.tsxwould have cost +43 tokens, and a hook inuseAppShellCommandswould have been newhookCallsdebt.2. Project actions (
features/task-entry)createAppShellProjectActionsbuilt 12 actions. Nine had no production caller:app-shell.tsx:878-881but never used;taskEntry.commandssince fix(desktop): scope project groups by runtime host #5527.This PR deletes the nine, together with:
addProjectused, including their unmount cleanup inapp-shell-effects.ts;onProjectSelected, which only those actions fired, so itsrefreshProjectSkills()call never ran in production.The two live folder openers move to Task Entry, whose README already titles it "Task Entry / Workspace":
TaskEntryServices.folders.openProjectFolder(sessionId?)andopenWorkspaceFolder().opened,refusedorfailed, naming the task or Host profile the result belongs to.openProjectFolder/openWorkspaceFoldercommands and reports failures with the same shell copy, diagnostic targets and workspace-unavailable notice as before.TaskEntryErrormay now carry asessionIdin place of aprofileId.taskEntry.commandsto the titlebar and the palette.refreshProjectsstays with the workspace projection inuse-project-context.ts, inlined onrunOnDefaultRuntimeHost. That hook has no new bridge paths, and it losesuseStableActions, twouseRefcalls and oneuseState.Also removed:
app-shell-project-actions.ts, itsworkspace-projectionownership entry, and the now unusedopenPathActionErrorMessage. The open-path classification test now covers Task Entry'sfolderOpenFailure.3. Command palette rows (
features/overlays)Five palette rows called the bridge: test a connection, make it the default, test the network proxy, open the local memory file, and save the conversation to a file. They now go through the palette's owner:
OverlaysServicesgainspalette: OverlayPaletteActions. The Desktop adapter makes the same bridge calls with the same arguments.connections:testBySlugIPC channel. That is a different channel from Connection Settings' identity-basedtest/setDefault, so this port does not duplicate that one.paletteActions, at a cost of about 6 tokens because AppShell already holds that projection. The rows keep their default-Host resolution, toasts and failure copy.OverlayPaletteActionsforapp-shell-command-actions.ts.Inventory deltas (generated ledger,
c7fa6bb6a→ this branch)app-shell.tsxapp-shell.tsx/ command actions / effects /app-shell-copy.tsUnchanged:
controllerOwners(13; no controller was created or moved);featurePrivateModules(13);app-shell.tsximports (79).app-shell.tsxitself goes from 2,019 to 2,005 lines.What enforces it
window.maka.*call back into the command actions, the Error Boundary or About is newbridgePathsdebt. For the three diagnostics calls this was checked by re-adding them against this branch's ledger, which produced 7 violations. Recreating a project-action factory in the family is a newactionFactoriesentry, which the ratchet also rejects.index.ts, so it shows up in review.Overlap
app-shell-command-actions.ts. Its base1e80e3b88has the same version of this file, so line numbers compare directly:as unknown as AppShellCommandListOptions, so the new required fields do not break that test's types.git merge-tree.--strict-baseagainst its head and admits one newappShellroot symbol use,features/diagnostics: ManualDiagnosticReportConsumer, as a new public export. No retained-root hook rows change, because no hook leaves AppShell here.app-shell.tsx: both PRs wrapAppShellContentin a render-prop consumer (OnboardingProjectionRootthere,ManualDiagnosticReportConsumerhere) and add one prop each. Whichever lands second nests both consumers and keeps both props.app-shell-effects.ts; refactor(desktop): retire AppShell's direct Desktop bridge (R2 M5) #5936 edits that file in other hunks.Verification
clean:main+build:test+test:dist), against 3,143 onc7fa6bb6a. 22 tests were added and the 3 legacy project-action tests deleted; two of those only covered deleted actions, and the third moved to the Task Entry adapter.sessionIdtarget, the workspace-unavailable branch, and the adapter's task path);typecheck(all workspaces),lint,format:check;apps/desktop,packages/ui),check:asf-headers,windows:inventory;check:locale-hygiene --base c7fa6bb6a,check:app-shell-hooks;check:renderer-architecture --base c7fa6bb6a --strict-base, which also passes against the current main1a66e4d5e;build:rename-focus.spec.ts,settings.spec.ts(2) andzh-tw-locale.spec.tspass (4/4). Throwaway local scripts, not committed pere2e/AGENTS.md, also checked the real IPC path:Maka Desktop diagnostic report … Surface: manualto the system clipboard;AI use
Select exactly one:
Tool(s) and scope: Claude Code (Claude Opus 5.5) implemented these ownership moves, their tests and documentation, ran the validation and drafted this description, all under human direction and review. Each commit carries
Generated-by: Claude Code.Checklist
Does this PR entail a change in behavior?