Running log of what moved and where. Ten lines per entry max.
For the procedure to follow when porting a new feature, see REFACTOR.md.
- Moved:
TaskService+TaskCreationSaga(the canonical renderer-service-fetching-domain-data + multi-step-orchestration forbidden pattern, ~610L) →@posthog/ui/features/task-detail/{taskService,taskCreationSaga}. apps task-detail feature is now fully ported. - Registered: NEW
TASK_CREATION_PORT(taskCreationPort.ts) aggregating workspace/folders/environment/git host I/O + getAuthenticatedClient + getTaskDirectory + getWorkspace. appsTrpcTaskCreationPortadapter bound indi/container.ts. AddeddisconnectFromTasktosessionServiceBridge. - Data: orchestration (saga steps + rollback) is host-agnostic in ui; the port is dumb transport; TaskService stays a thin injectable wrapper updating ui stores.
- Cleaned: deleted apps
task-detail/service/service.ts+sagas/task/task-creation.ts; repointed di/container + task-service-bridge to the ui TaskService; migrated the 490L saga test → ui (port mock replaces trpc/getSessionService vi.mocks). - Validation: apps tsc 0; ui my-files 0; biome 0 noRestrictedImports; ui task-detail+bridge vitest 29/29 (saga 7/7); renderer
vite build✓ (whole app bundles). ui-task-detail → needs_validation (live create-task GUI smoke remains).
- Moved:
taskViewedApi+pinnedTasksApi(the last imperative host helpers in apps sidebar) →@posthog/ui/features/sidebar/taskMetaApi.tsvia module-settersetTaskMetaApi(parse/unpin/isPinned logic in ui; rawtrpc.workspace.*host calls injected, wired indesktop-services.ts). - Repointed: sessions/service/service.ts, archive-task-bridge, task-mutation-bridge, + 2 sessions test mocks → ui taskMetaApi.
git rmapps useTaskViewed.ts + usePinnedTasks.ts (0 consumers). - Data: per-task pins/timestamps truth stays host (trpc.workspace); taskMetaApi is dumb transport for non-React callers (React reads go through SIDEBAR_TASK_META_CLIENT hooks).
- Bridge: useSidebarData.ts / useTaskPrStatus.ts pure re-export shims remain (cosmetic; one is mid concurrent-delete). panels/index.ts dead barrel (0 consumers).
- Validation: apps tsc 0; ui taskMetaApi clean; biome 0 noRestrictedImports; ui sidebar vitest 41/41. GUI smoke blocked by exogenous ui-inbox InboxView build breakage. ui-sidebar → needs_validation.
- Moved:
SettingsDialog(container),settings/sections/SignalSourcesSettings,inbox/components/DataSourceSetup(576L) →@posthog/ui. apps settings feature is now 100% re-export shims. - Registered: NEW
LINEAR_INTEGRATION_CLIENTport +LinearIntegrationClientiface (integrations/ports.ts);TrpcLinearIntegrationClientadapter bound indesktop-services.ts(DataSourceSetup's lone trpc calllinearIntegration.startFlow). GitHubRepoPicker + useAuthenticatedClient were already in ui (false blockers). - Data: settings persistence stays host (SETTINGS_*_PORT + settingsStore); SettingsDialog is pure UI reading the ported sections.
- Bridge: apps re-export shims at
@features/settings/components/SettingsDialog,.../sections/SignalSourcesSettings,@features/inbox/components/DataSourceSetup(consumers App.tsx/MainLayout + inbox unchanged). - Validation: apps tsc 0; ui my-files 0; biome 0 noRestrictedImports; ui inbox+settings+integrations vitest 89/89; renderer
vite build -c vite.renderer.config.mts✓. ui-settings → needs_validation (only live GUI smoke remains).
- Moved:
settings/sections/{SlackSettings,SignalSlackNotificationsSettings}→@posthog/ui/features/settings/sections/. Last real settings sections except the inbox-gated SignalSources. - Registered: NEW
SLACK_INTEGRATION_CLIENTport +SlackIntegrationClientiface in@posthog/ui/features/integrations/ports.ts(mirrors GITHUB_INTEGRATION_CLIENT: startFlow/consumePendingCallback/onCallback/onFlowTimedOut). PorteduseSlackConnect+useSlackIntegrationCallback→@posthog/ui/features/integrations/(off@renderer/trpc→ useService + ui auth store). Desktop adapterTrpcSlackIntegrationClientbound to SLACK_INTEGRATION_CLIENT indesktop-services.ts. - Data: Slack integration list/connection truth stays in the host slackIntegration router (react-query cache projection); the port is dumb transport.
- Cleaned: deleted dead apps
integrations/hooks/{useSlackConnect,useSlackIntegrationCallback}.ts(0 consumers after the move). Inbox hooks (useSignalSourceManager/useSlackChannels) were already in ui = false blockers. - Bridge: apps
settings/.../sections/{SlackSettings,SignalSlackNotificationsSettings}.tsxre-export shims (consumers SettingsDialog + SignalSourcesSettings unchanged). - Validation: ui typecheck (my files 0; exogenous task-detail/sessions red), apps typecheck 0, biome 0 noRestrictedImports, ui integrations+settings vitest 11/11, renderer
vite build -c vite.renderer.config.mts✓. - Remaining ui-settings: SignalSourcesSettings + SettingsDialog gated ONLY on inbox's DataSourceSetup → ui (ui-inbox in_progress owns it).
- Moved:
editor/prompt-builder(→@posthog/sharedfor path),setup/{buildDiscoveredTaskPrompt,categoryConfig,SetupScanFeed},connectivity/connectivityToast,tasks/taskKeys→@posthog/ui. All pure / ui-only deps. - Dedup:
skill-buttons/promptsapps copy (divergent near-dup, 4 live consumers) → shim re-exporting the canonical ui twin (single source of truth). Deleted deadintegrations/integrationStore(0 refs). - Bridge: apps re-export shims at old paths where consumers are hot (App/SessionView/SuggestedTasksPanel/SuggestedTaskCard/sessions/task-creation); cold single-consumers repointed directly.
- Validation: full typecheck 19/19; ui mcp-apps 39/39 + billing spendAnalysis 21/21; biome clean.
- Moved:
mcp-app-theme.ts(pure) +mcp-app-csp.ts(ext-apps type only) + tests →@posthog/ui/features/mcp-apps/utils/(alongside the already-ported host-utils). - Bridge: none — single consumer
useAppBridgerepointed to the ui path. - Validation: ui typecheck 0; mcp-apps/utils tests 39/39; biome clean.
- Moved:
spendAnalysisFormat.ts+spendAnalysisPrompt.ts(+test, 21) →@posthog/ui/features/billing/. Pure display/markdown helpers, no trpc/store/host coupling. - Cleaned: spendAnalysisPrompt's type import now reads
@posthog/api-client/spend-analysisdirectly (the appstypes/spend-analysis.tswas only re-exporting that). - Data: SpendAnalysisResponse owned by
@posthog/api-client; these are pure projections of it. - Bridge: none — single cold consumer
TokenSpendAnalysisBannerrepointed directly. Deferredbilling/utils.ts(blocked on@mainllm-gatewayUsageOutputtype). - Validation: full typecheck 19/19; spendAnalysisPrompt 21/21; biome clean.
- Moved:
handleExternalAppAction→@posthog/ui/features/external-apps/handleExternalAppAction.ts;focusToast.tsx→@posthog/ui/features/focus/. The recurring "hot host util" that blocked code-editor/panels/task-detail/sessions from importing it. - Registered:
EXTERNAL_APPS_CLIENTport extended withopenInApp/copyPath; new module-levelsetExternalAppsClient(cloudFileReader pattern) for the non-React caller, wired at boot indesktop-servicesfrom the DI singleton. - Data: source of truth is the desktop adapter behind
EXTERNAL_APPS_CLIENT; toasts/auto-focus are derived effects. - Bridge: apps
@utils/handleExternalAppAction.tsxre-export shim (8 consumers unchanged). Retire when code-editor/panels/task-detail import the package path directly. - Validation: full typecheck 19/19; ui external-apps 6/6 (3 new); biome clean. GUI smoke pending.
- Moved:
DiffSettingsMenu,DiffSourceSelector,DraftCommentAnnotation,ReviewToolbar,constants.ts,hooks/useCommentState.ts→@posthog/ui/features/code-review(consume only ui stores/primitives +@pierre/diffs+ lucide). - Registered: added
lucide-react ^1.7.0to@posthog/uideps (ReviewToolbar icons; forward-compat for remaining code-review components). - Bridge: app re-export shims at all 6 old paths; coupled siblings (ReviewShell/ReviewPage/ReviewRows) import them via the shims unchanged.
- Note: the bulk of code-review (diff rendering + comment hooks) is blocked — it needs
trpc.gitdiffs (the git-interaction cache-coherence unit) + the unportedtask-detailhub. - Validation: ui typecheck 0 + code-review 27/27; apps web/main 0 non-exogenous; apps ReviewShell.test 4/4; biome clean.
- Moved: pure
resolveCloudPrUrl(PR-url derivation, zero trpc) + test →@posthog/ui/features/git-interaction/cloudPrUrl.ts(Task ←@posthog/shared/domain-types, AgentSession ← ui sessionStore). - Bridge: apps
useCloudPrUrl.tsre-exports it; the hook stays in apps (depends on unporteduseTasks). Consumers (useCloudRunState/useTaskPrUrl) unchanged. - Note: the rest of the git-interaction data layer is ONE coherent tRPC-react cache unit (usePrActions optimistic writes share read hooks' keys; gitCacheKeys/updateGitCache keys are shared with ChangesPanel et al.) — must move together behind GIT_INTERACTION_CLIENT, not piecemeal. See slice notes.
- Validation: ui typecheck 0; ui git-interaction 63/63; apps web touched-files clean (3 exogenous message-editor errors); biome clean.
- Moved:
prActionTypeenum/PrActionType→@posthog/shared/git-domain(zod-backed, barrel-exported);git-interaction/utils/prStatus.tsx→@posthog/ui/features/git-interaction/utils/(pure PR-status presentation). - Cleaned: removes the
@main/services/git/schemasimport that previously blocked portingprStatus. main schemas re-export the shared type (drop-in); ws-server keeps its own enum (zod v4-vs-v3 isolation). - Bridge: app re-export shims at
@features/git-interaction/utils/prStatus; consumers (TaskActionsMenu/PRBadgeLink/usePrActions) unchanged. - Validation: shared+ui+apps(main+web)+ws-server typecheck 0; ui git-interaction 56/56; biome clean.
- Moved:
agentVersion.ts(+test) →@posthog/ui/utils/agentVersion(pure semver gate; addedsemver/@types/semverto ui);getFilePath.ts→@posthog/ui/utils/getFilePathbehindsetFilePathResolver. - Registered:
setFilePathResolverwired indesktop-servicesto Electronwindow.electronUtils.getPathForFile(the only host-specific bit; stays in apps). - Bridge: app re-export shims at both
@utils/*paths — consumers (useAgentVersion, message-editor/persistFile) unchanged. - Validation: ui typecheck 0; apps/code web tsc 0; agentVersion 11/11; persistFile 12/12; biome clean.
- Moved: the create-PR saga orchestration
apps/code/.../git/service.ts createPr→GitPrService.createPr(input, host, onProgress). The already-portedCreatePrSagais now constructed+run inside core; apps no longer imports it. - Registered: new
CreatePrHost/CreatePrInput/CreatePrResultinpackages/core/src/git-pr/ports.ts;GitPrLoggernow extendsSagaLogger. Host ops passed per-call (no DI cycle). - Data: source of truth is core
GitPrService; appsGitService.createPris a thin transport bridge (builds the host adapter, emitsGitServiceEvent.CreatePrProgress). - Bridge: apps
GitService.createPr+ git router forward unchanged;createPrViaGh(gh CLI = host syscall) stays host-side behind the port. Retire when renderer consumes workspace-client. - Validation: core typecheck 0 + purity gate 0; core git-pr.test 7/7 (3 new createPr); apps main tsc 0; apps git service.test 27/27. GUI PR-creation smoke not run.
- Moved:
sounds(+13 .mp3 assets),browser,dialog,clearStorage→@posthog/ui/utilsvia the module-setter pattern (setMessageBoxHost,setStorageDataCleaner, existingopenExternalUrl/setCloudFileReader).soundseliminated the redundantCOMPLETION_SOUND_PORT. - Registered: desktop-services wires the setters to trpc (
os.showMessageBox,folders.clearAllData,os.openExternal,fs.readFileAsBase64). - Bridge: app re-export shims at all
@utils/*paths — consumers unchanged. - Validation: ui + apps typecheck clean; notifications 12/12; biome clean.
- Moved:
overlay(+test),promptContent(+test),urls(+test),posthogLinks→@posthog/ui/utils;useBlurOnEscape→@posthog/ui/hooks; deleted deadobject.ts. - Cleaned:
urls/posthogLinksread region/projectId from the ui auth store (useAuthStore.getState()) instead of appgetCachedAuthState— no port needed.overlay(DOM) unblockeduseBlurOnEscape. - Bridge: app re-export shims at all old
@utils/*/@hooks/*paths — consumers unchanged. - Validation: ui + apps typecheck clean; overlay/promptContent/urls tests 23/23; biome clean.
- Moved:
features/sessions/utils/cloudArtifacts.ts(409L) +features/editor/utils/cloud-prompt.ts(230L) →packages/ui(sessions/editor). Deps →@posthog/shared/@posthog/api-client/@posthog/ui. - Registered: new
cloudFileReader.tsmodule-level host setter (setCloudFileReader) wired at boot indesktop-services.ts; replaces the per-filetrpcClient.fs.readFileAsBase64call. - Bridge: app re-export shims at both old paths (sessions service / task-creation saga / useTaskCreation unchanged). cloud-prompt.test (16) moved to ui, mock repointed, node:url removed.
- Validation: ui + apps typecheck clean; cloud-prompt.test 16/16; biome clean.
- Moved:
sections/GeneralSettings(largest settings section, 559 LOC) →packages/ui; sleep pref behind newSETTINGS_GENERAL_PORT, sound viaCOMPLETION_SOUND_PORT,getPostHogUrlinlined via@posthog/shared. - Registered:
RendererSettingsGeneralClient(sleep.getEnabled/setEnabled) bound indesktop-services.ts; app shim left. - Validation: ui + apps/code typecheck clean for settings; biome clean; settings tests 11/11.
- Moved:
sections/UpdatesSettings→packages/ui/src/features/settings/sections/behind a newSETTINGS_UPDATES_CLIENTport (ports.ts); rewrote off@renderer/trpctouseService+ per-feature client. - Registered: desktop adapter
RendererSettingsUpdatesClient(wrapsos.getAppVersion/updates.check/updates.onStatus) bound indesktop-services.ts. - Note: confirms the per-feature client-port pattern (no generic main-trpc-react client possible — app router type can't cross into ui). Template for the remaining trpc-coupled sections.
- Validation: ui + apps/code typecheck clean for settings; biome clean.
- Moved:
SettingRow,SettingsOptionSelect,ModalInlineComboboxContent(pure) +sections/TerminalSettings,sections/PersonalizationSettings→packages/ui/src/features/settings/. Imports repointed (analytics →@posthog/ui/workbench/analytics,ANALYTICS_EVENTS→@posthog/shared, useDebounce → ui). - Bridge: app re-export shims at
@features/settings/components/*keep all consumers (7 SettingRow sections, SettingsDialog, Signal* sections) unchanged. - Cleaned: shrinks the settings feature in apps/code; SettingRow now a shared ui presentational primitive.
- Deferred: sections using
@renderer/trpc(Updates/Permissions/Workspaces/ClaudeCode), auth/seat (Account), integrations (GitHub/Slack), host utils (General/Advanced) — all gated on a packages/ui main-trpc-react port. - Validation: ui + apps/code typecheck clean for settings; biome clean.
- Moved:
apps/code/.../setup/services/setupRunService.ts(656 LOC forbidden renderer orchestration) →packages/ui/src/features/setup/setupRunService.tsas an@injectable()Inversify UI service.prompts.ts→packages/ui/src/features/setup/prompts.ts. - Registered:
SETUP_RUN_PORT(packages/ui/.../setup/ports.ts) — host capability port (auth/task-API/agent/enrichment/env/analytics, intent-based). Service injects it +WORKBENCH_LOGGER; writes to the ported setupStore. - Bridge:
apps/code/.../platform-adapters/setup-run-port.ts(RendererSetupRunPort) wraps trpcClient + authed PostHog client + analytics + dev flag; bound in desktop-services.ts.RENDERER_TOKENS.SetupRunServicenow binds the package class. - Data: SetupRunService owns the flow; SETUP_RUN_PORT owns host I/O; setupStore holds UI state.
- Cleaned: removes the canonical "Renderer Service Fetching Domain Data" forbidden pattern (no trpc/Electron/analytics/import.meta.env in the package).
- Validation: setupRunService.test 6 + suggestions.test 8 = 14/14; ui + apps/code typecheck clean for setup (other red exogenous); biome clean. Live discovery smoke not run.
- Moved:
apps/code/.../components/ErrorBoundary.tsx→packages/ui/src/primitives/ErrorBoundary.tsx, made host-agnostic (dropped@utils/analytics+@utils/logger; addedonError(error,{componentStack,suppressed})prop). - Bridge:
apps/code/.../components/ErrorBoundary.tsxis now a thin wrapper supplyingonError→captureException+logger.scope; re-exportsErrorBoundaryProps. Consumers (App.tsx, task-detail/TaskLogsPanel) unchanged. - Data: telemetry/logging decision stays in the host wrapper; the primitive only signals via callback.
- Cleaned: removes apps/code analytics/logger coupling from a shared primitive.
- Validation: ui + apps/code typecheck clean for ErrorBoundary; ErrorBoundary.test 10/10 (kept in apps/code as wrapper+primitive integration test — packages/ui lacks @testing-library); biome clean.
- Moved: pure enricher suggestion builders (buildStaleFlagSuggestion/buildSdkHealthSuggestion/buildPosthogSetupSuggestion + StaleFlagPayload)
apps/code/.../setup/services/setupRunService.ts→packages/ui/src/features/setup/suggestions.ts(+ suggestions.test.ts, 8 tests). - Cleaned: deleted byte-duplicate stale
apps/code/.../setup/types.ts+apps/code/.../setup/stores/setupStore.ts(canonical lives in@posthog/ui/features/setup/{types,setupStore}; app copies had zero external consumers) — removes a duplicated-truth violation. - Data: source of truth is
@posthog/ui/features/setup/types.ts(DiscoveredTask + buildTaskDiscoverySchema). - Bridge: none. Behavior-preserving; SetupRunService imports builders from the package.
- Remaining (ui-onboarding parent): SetupRunService orchestration (runDiscovery/runEnricher) still in renderer → move to core/main behind agent/enrichment/task-run/auth ports; delete onboarding stale dups.
- Validation: @posthog/ui + apps/code typecheck clean for setup (other red exogenous); suggestions.test 8/8; biome clean.
- Moved: skill-listing host fs ops →
packages/workspace-server/src/services/skills/skill-discovery.ts(findSkillDirs, getMarketplaceInstallPaths, readSkillMetadataFromDir) +parse-skill-frontmatter.ts; createdSkillsService.listSkills()(skills.ts) injecting POSTHOG_PLUGIN_SERVICE + FOLDERS_SERVICE, with zodschemas.tsas boundary source of truth. - Registered:
skillsModulebinds SKILLS_SERVICE; loaded in apps/codecontainer.tsafter posthogPluginModule (shares the bound plugin/folders singletons + single SQLite conn). - Cleaned:
routers/skills.tscollapsed to a one-line forward to SKILLS_SERVICE.listSkills() — removed the "router with no backing service + inline logic + container.get" forbidden pattern. Splitagent/discover-plugins.ts: SDK-coupleddiscoverExternalPluginsstays in apps/code (agent slice; @anthropic-ai/claude-agent-sdk not a ws-server dep) and imports the shared helpers from ws-server. Deleted apps/codeskill-schemas.ts+parse-skill-frontmatter.ts. - Data: source of truth is ws-server
skills/schemas.tsskillInfo zod; SkillInfo/SkillSource neutral types in @posthog/shared. - Bridge: none new. MAIN_TOKENS.PosthogPluginService alias remains for the unrelated old posthog-plugin service.
- Validation: ws-server typecheck clean; ws-server skill-discovery.test.ts 5/5; apps/code agent discover-plugins.test.ts 21/21 (behavior preserved); biome clean. apps/code typecheck red is exogenous (concurrent MAIN_TOKENS-alias removal). UI move (SkillsView/SkillDetailPanel/skill-buttons) blocked on a packages/ui main-trpc client port + ui-code-editor/ui-task-detail/ui-shell + sessions.
2026-05-30 — OAuth + integrations + McpCallback + Notification retirements (6 more MAIN_TOKENS removed)
- Retired MAIN_TOKENS.OAuthService: already package-canonical (
.toService(OAUTH_SERVICE)); repointed the 3 consumers (index bootstrap, oauth router, authOAuthFlowPortAdapter@inject) to OAUTH_SERVICE, deleted bridge + token. - Ported the integration services off
MAIN_TOKENS → .to(class)to package-canonical identifiers: added GITHUB_INTEGRATION_SERVICE / LINEAR_INTEGRATION_SERVICE / SLACK_INTEGRATION_SERVICE topackages/core/src/integrations/identifiers.ts, bound the core classes to them, repointed consumers (github/linear/slack routers + index), removed the 3 tokens. No bridge needed (all consumers host-level). - Retired MAIN_TOKENS.McpCallbackService: repointed the mcp-callback router to the existing MCP_CALLBACK_SERVICE, deleted the
.toServicebridge + token. - Ported NotificationService to a package identifier: added NOTIFICATION_SERVICE to
packages/core/src/notification/identifiers.ts, bound the core class to it, repointed consumers (notification router + index), removed the token. - 15 MAIN_TOKENS service tokens retired this session. Validation: core + apps/code typecheck 0 errors; core notification test 8/8; full
pnpm typecheck19/19. - Completed the integrations registration module: added
packages/core/src/integrations/integrations.module.ts(binds GITHUB/LINEAR/SLACK_INTEGRATION_SERVICE, singleton) per REFACTOR.md "Registration Modules"; apps/code container nowcontainer.load(integrationsModule)+ binds only the host logger ports, instead of three inline.to(class)binds. Lets a future web/mobile host load integrations without app-local wiring. core + my apps/code files: 0 errors.
- Repointed the host-side consumers of the 4 remaining bridges to package identifiers: llm-gateway/cloud-task/suspension/mcp-apps routers + menu.ts (McpApps) + index.ts (Suspension) now
container.get(<PACKAGE_ID>). Each bridge now has exactly ONE consumer left (the off-limits tangle inject: Git/Handoff/Workspace/Agent) — annotated in container.ts so the final retirement is a one-liner. - Validation campaign: ran package test suites. core 210 passing; ws-server pass except the better-sqlite3 DB round-trip (Electron-ABI NODE_MODULE_VERSION 145 vs 137 — environmental, not code). Promoted 16 needs_validation slices to passing with per-slice evidence: connectivity, environments, folders, archive, suspension, usage-monitor, cloud-task, enrichment, fs-capability, local-logs-capability, llm-gateway, notifications, os, github-integration, slack-integration, linear-integration.
- Authored 9 new test suites (83 tests): core llm-gateway (prompt/usage/invalidate + timeout), oauth (refreshToken status->errorCode, cancelFlow, deep-link refocus), task-link (path/run-id/queue/focus), notification (click-navigate + dock badge lifecycle), integrations github + slack (startFlow url/timeout, callback parsing incl non-numeric ids, queue/consume, timeout-cancel) + linear (authorize url + error wrap); ws-server os (showMessageBox mapping, dialog-port pickers, getClaudePermissions parse), workspace-metadata (togglePin/markViewed/markActivity-clamp + projections — annotates the in_progress workspace slice); shared backoff (getBackoffDelay exponential + cap, sleepWithBackoff timing) + regions (getCloudUrlFromRegion, getOauthClientIdFromRegion distinct-per-region, formatRegionBadge) + errors (auth/rate-limit/fatal-session classification incl rate-limit-precedence) + xml (escape/unescape round-trip). 13 suites total (~96 tests), all green; shared 277/277, core 210+, ws-server pass (modulo DB-ABI). auth slice annotated: oauth is test-backed, blocked only on agent coupling.
- Mid-turn convergence with concurrent agents: adapted oauth.test.ts to a newly-added 7th constructor param (CRYPTO_SERVICE / @posthog/platform/crypto port another agent extracted); rode out transient updates.ts / updates.test.ts churn without touching their slice.
- NOTE: src/updates/updates.test.ts has 1 red ("disabled/unsupported platform") from another agent's in-flight updates refactor (static props DISABLE_ENV_FLAG/SUPPORTED_PLATFORMS) — exogenous, not from this work; left untouched.
- Moved:
apps/code/src/main/db/**→packages/workspace-server/src/db/**(drizzleschema,DatabaseService, 8 repositories +.mock,test-helpers, migrations). Newdb/identifiers.ts(DATABASE_SERVICE) +db/db.module.ts. - Registered:
databaseModulebound in maindi/container.ts(container.load);DatabaseServiceinjects platformSTORAGE_PATHS_SERVICE; repos injectDATABASE_SERVICE. - Data: source of truth is the on-disk SQLite (
posthog-code.db); repositories are the typed sync access layer (unchanged — kept in-process, not cross-process). - Cleaned: dropped main logger +
MAIN_TOKENS/@sharedcoupling from db (inlinedCloudRegion,SuspensionReason, package-localnormalize-path). Fixed apps/codevitest.configto reuserendererAliases(@posthog/*workspace aliases). - Bridge:
MAIN_TOKENS.DatabaseService→DATABASE_SERVICE, and the 8MAIN_TOKENS.*Repositorybindings (now → package classes) remain (PORT NOTE in container.ts) so the 19 consumers are unchanged; only their db type-import paths were repointed. Build:copy-drizzle-migrationssource +drizzle.configrepointed to the package; runtime read path unchanged. - Validation:
pnpm typecheck19/19;pnpm --filter code test124 files / 1527 pass (incl. real-SQLite archive integration);pnpm dev:codeboots clean (migrations copied, in-process DB init, live tRPC IPC, no errors). Unblocks the persistence-coupled core tier (folders/workspace/archive/suspension/handoff/agent/auth).
- Moved: auth, sleep, agent services now inject
POWER_MANAGER_SERVICE(@posthog/platform/power-manager) instead ofMAIN_TOKENS.PowerManager. - Cleaned: removed the
MAIN_TOKENS.PowerManageralias (container.ts) + token (tokens.ts) + sleep's unused MAIN_TOKENS import. ElectronPowerManager adapter unchanged (dumb onResume/preventSleep). Sleep-blocking decisions remain in SleepService. - Validation: my files typecheck clean (unrelated git.ts errors are concurrent git-read WIP); biome clean. GUI smoke pending.
- Moved:
decodePlanBase64+parseGitHubIssueUrl(were private inapps/code/src/main/services/new-task-link/service.ts) →packages/shared/src/deep-links.ts(+GitHubIssueReftype), exported from the shared barrel. new-task-link now imports them from@posthog/shared. - Data: pure host-agnostic parsing utilities; no state. (Slice said
core, but zero-dep pure utils belong inshared.) - Bridge: none. Host wiring (Electron protocol registration via IAppLifecycle, IMainWindow focus, event emit/queue) intentionally stays in the apps/code link services.
- Remaining (slice in_progress): move
getDeeplinkProtocol+NewTaskLinkPayload/NewTaskSharedParamsto @posthog/shared (repoint ~10 importers); extract deep-link URL-decomposition + task/inbox path parsers. - Validation: shared build + typecheck;
deep-links.test.ts8/8; apps/code typecheck clean for deep-links files.
- Moved: 4 main consumers (os.ts router, handoff, context-menu, folders) now inject
DIALOG_SERVICE(@posthog/platform/dialog) instead ofMAIN_TOKENS.Dialog. - Cleaned: removed the
MAIN_TOKENS.Dialog.toServicealias (container.ts) + token (tokens.ts). ElectronDialog adapter unchanged (thin wrapper). - Remaining: os.ts (396-line serviceless router) -> backing-service split (acceptance #2) overlaps os/misc-host-capabilities; deferred. GUI smoke (file picker + message box) pending.
- Validation: dialog edits typecheck clean (unrelated git.ts WorkspaceClient error is the concurrent git-read agent's WIP); biome clean.
- Moved: sole main consumer
external-apps/service.tsnow injectsCLIPBOARD_SERVICE(@posthog/platform/clipboard) instead ofMAIN_TOKENS.Clipboard. - Cleaned: removed the
MAIN_TOKENS.Clipboard.toService(CLIPBOARD_SERVICE)alias (container.ts) and theMAIN_TOKENS.Clipboardtoken (tokens.ts). ElectronClipboard adapter unchanged (already a dumb writeText wrapper). - Note: renderer copy uses
navigator.clipboarddirectly (host-appropriate DOM API), not trpcClient — no clipboard misuse to migrate. Image copy/paste path is os.ts saveClipboardImage (separate slice). - Validation: apps/code(node) typecheck; platform-identifiers test 4/4. GUI smoke (copy text/image) pending.
- Moved: gating from
apps/code/src/renderer/utils/notifications.ts->packages/ui/src/features/notifications/TaskNotificationService(stopReason + focus/active-task + settings gating, title truncation). New platform contractpackages/platform/src/notifications.ts(INotifications: notify/showUnreadIndicator/requestAttention,NOTIFICATIONS_SERVICE). New renderer adapterapps/code/src/renderer/platform-adapters/notifications.ts(dumb trpcClient.notification wrapper). - Registered:
notificationsUiModule(binds TaskNotificationService) loaded indesktop-contributions.ts;NOTIFICATIONS_SERVICE+ the settings/active-view/sound UI ports bound indesktop-services.ts. - Data: source of truth for "should notify" is the gating in TaskNotificationService, computed from injected facts (settings snapshot, document focus, active task id). No persisted/duplicated state.
- Bridge:
apps/code/src/renderer/utils/notifications.tsfree functions now delegate to TaskNotificationService via the renderer container (PORT NOTE). Retire when the sessions service usesuseServicedirectly. Main NotificationService/router/electron-notifier unchanged. - Cleaned: platform interface is host-neutral (showUnreadIndicator/requestAttention, not dockBadge/bounceDock — adapter maps to the existing trpc procedure names).
- Validation: platform typecheck+build; apps/code web typecheck 0 errors; 12 TaskNotificationService unit tests pass. GUI smoke not yet run.
2026-05-29 — ui-primitives (dependency-clean leaf primitives → packages/ui/src/primitives) — in_progress (partial)
- Moved:
components/ui/{Tooltip,Button,Badge,KeyHint,PanelMessage,StepList,SafeImagePreview},components/{List,Divider,DotsCircleSpinner,DotPatternBackground,CodeBlock},components/ui/combobox/{Combobox,Combobox.css,useComboboxFilter},hooks/{useDebounce,useDebouncedValue,useInView,useImagePanAndZoom},utils/{toast,confetti}→packages/ui/src/primitives/**. - Registered: none (pure presentational primitives; no DI module). Importers across
apps/code/srcrewritten to@posthog/ui/primitives/*(short +@renderer/*+ relative forms all covered). - Data: no state; these are stateless visual/util primitives.
- Cleaned: packages/ui gained deps
@posthog/shared,@radix-ui/react-tooltip,@radix-ui/react-icons,cmdk,canvas-confetti,sonner(+@types/canvas-confetti). - Bridge: colocated tests/stories (CodeBlock/useDebounce/useImagePanAndZoom tests, combobox test+story) stay in apps/code pointing at
@posthog/uipaths until packages/ui gets vitest/storybook infra. - Deferred/not-primitives: FileIcon (host asset glob), RelativeTimestamp/action-selector/useBlurOnEscape/syntax-highlight/HighlightedCode (blocked on renderer-shared-utils + code-editor slices); HeaderRow/HedgehogMode/ZenHedgehog/focusToast/useAutoFocusOnTyping/TreeDirectoryRow are feature-coupled (belong to feature slices, not primitives).
- Validation:
pnpm typecheck19/19 green.
2026-05-29 — fs-capability (workspace-server owns fs syscalls; main is a WorkspaceClient bridge) — needs_validation
- Moved: all 8 fs methods (listRepoFiles+30s cache, readRepoFile(s), readRepoFile(s)Bounded, readAbsoluteFile, readFileAsBase64, writeRepoFile)
apps/code/src/main/services/fs/service.ts->packages/workspace-server/src/services/fs/service.ts(joins existing listDirectory). fs schemas ->packages/workspace-server/src/services/fs/schemas.ts(source of truth); deleted the main copies. - Registered: 8 one-line
fs.*procedures inpackages/workspace-server/src/trpc.ts. MainMAIN_TOKENS.FsServicenow bound inindex.tsviatoConstantValue(new FsService(workspaceClient))(bridge), removed fromdi/container.ts. - Data: source of truth is workspace-server FsService; the list cache (TTL + write-self-invalidation) lives there; renderer react-query cache is the user-facing projection (invalidated by useFileWatcher).
- Cleaned: fs no longer injects FileWatcherBridge — the watcher coupling only fed the server cache, now reconciled via TTL + renderer-side invalidation. Removes one of the 4 FileWatcherBridge-retirement consumers (remaining: archive, suspension, workspace).
- Bridge:
apps/code/src/main/services/fs/service.ts(PORT NOTE) until AgentService reads/writes via workspace-client directly. - Validation: ws-server typecheck + fs service.test.ts 6/6 (incl. tmp-dir round-trip + path-traversal guard); apps/code typecheck clean for all fs files. Boot smoke deferred (shared tree red from concurrent ui-primitives move).
- Moved:
apps/code/src/main/services/connectivity/service.tspolling/HTTP-reachability/backoff ->packages/workspace-server/src/services/connectivity/{service,schemas,service.test}.ts. Newconnectivity.{getStatus,checkNow,onStatusChange}procedures in wstrpc.ts(one-line forwards), bound in wsdi/{tokens,container}.ts. - Data: source of truth is the live network-reachability poll in the single ws-server ConnectivityService;
isOnlineis its derived state. The main bridge caches the latest value so AuthService can read it synchronously. - Bridge:
apps/code/src/main/services/connectivity/service.tsis now aWorkspaceClientbridge (extends TypedEventEmitter; subscribes to wsonStatusChange, re-emitsStatusChange, answersgetStatus()from cache). Bound inindex.tsafterwsServer.start(), beforeinitializeServices()(AuthService consumer). Main connectivity router + renderer connectivityStore/toast unchanged. - Bridge retirement: delete when AuthService + renderer consume
workspaceClient.connectivitydirectly. - Cleaned: dropped main-process logger from the capability; polling timer is
unref'd; emit-on-change-only preserved. - Validation: ws-server + apps/code(node) typecheck; 11 unit tests pass. GUI smoke not yet run.
- Moved:
apps/code/src/main/services/local-logs/service.tslogic →packages/workspace-server/src/services/local-logs/{service,schemas,service.test}.ts. NewlocalLogs.{read,write}procedures inpackages/workspace-server/src/trpc.ts(one-line forwards), bound in wsdi/{tokens,container}.ts. - Data: source of truth is the on-disk NDJSON at
~/.posthog-code/sessions/<taskRunId>/logs.ndjson; the single-flight latest-wins write coalescing (pertaskRunId) now lives in the one workspace-server instance, so all writers (renderer vialogsrouter, future main callers) funnel through it. - Bridge:
apps/code/src/main/services/local-logs/service.tsis now a thinLocalLogsServiceoverWorkspaceClient.localLogs, bound inindex.tsafterwsServer.start()(mirrors FocusService/FileWatcherBridge).logs.tsrouter and the renderer sessions service are unchanged (stilltrpcClient.logs.{readLocalLogs,writeLocalLogs}). - Bridge retirement: delete the main bridge +
logsrouter local-log procedures when the renderer sessions service consumesworkspaceClient.localLogsdirectly. - Cleaned: dropped the main-process logger dependency from the capability (ws services don't log; failures still degrade to null/no-op as before).
- Known debt:
DATA_DIR(".posthog-code") is duplicated in the ws service, apps/codeshared/constants.ts, and handoffseedLocalLogs(raw fs). Consolidate into@posthog/sharedonce the di-foundation lockfile churn settles. handoff still writes the same NDJSON via raw fs (pre-existing) — should adopt the capability later. - Validation: ws-server + ws-client + apps/code(node) typecheck; 11 unit tests pass (vitest, ws-server root). GUI smoke (logs stream/render) not yet run.
- Moved:
packages/ui/src/workbench/{contribution.ts,service-context.tsx}→packages/di/src/{contribution.ts,react.tsx}(git mv).startWorkbenchContributions→startWorkbench. - New package
@posthog/di: ownsWORKBENCH_CONTRIBUTION+WorkbenchContribution+startWorkbench(container),useService/ServiceProvider(React boundary hook — see REFACTOR.md "React Access to Services": component-boundary only, never a service-locator), and a host-agnosticWorkbenchLogger/WORKBENCH_LOGGERport. - Registered:
fileWatcherUiModule(ContainerModule) bindsFileWatcherContributionas aWORKBENCH_CONTRIBUTION.apps/codedesktop-contributions.tscontainer.loads it;desktop-services.tsbindsWORKBENCH_LOGGERto the renderer electron-log scope;main.tsxcallsstartWorkbench(container)before render. - Data: source of truth is
packages/difor the workbench DI primitives; no persisted/derived state. - Cleaned: renderer Vite resolves
@posthog/di/*via a new alias invite.shared.mts(consistent with every other workspace package, which the repo aliases tosrc/$1rather than node_modulesexports).packages/ui/tsconfig.jsongainedexperimentalDecorators+emitDecoratorMetadata(first@injectablein ui; mirrors workspace-server). - Bridge: none.
- Validation:
pnpm typecheck(19 tasks);@posthog/distartWorkbenchunit test;pnpm --filter code test(1588) afterbuild:deps;pnpm dev:codeboots to a rendered window with live tRPC IPC and zero resolution/boot errors.
2026-05-29 — platform-identifiers (package-owned DI symbols + MAIN_TOKENS bridge) — needs_validation
- Added:
export const <CAP>_SERVICE = Symbol.for("posthog.platform.<cap>")to all 15packages/platform/src/*.tsinterface files. Each platform capability now owns its Inversify identifier beside its interface (no new identifiers added toapps/code/src/main/di/tokens.ts). - Registered:
apps/code/src/main/di/container.tsbinds each Electron adapter to its package-owned identifier (bind(CLIPBOARD_SERVICE).to(ElectronClipboard), …) and aliases the legacyMAIN_TOKENS.<Platform>entries viabind(MAIN_TOKENS.Clipboard).toService(CLIPBOARD_SERVICE). Same singleton, single source of truth. - Data: source of truth is the platform identifier binding;
MAIN_TOKENS.*platform entries are projections (aliases). Interfaces audited host-neutral (no electron/macos/dock/taskbar/tray/safeStorage terms); platform imports nothing internal. - Bridge: the 15
MAIN_TOKENS.<Platform>toServicealiases remain (PORT NOTE in container.ts). Retire each once its consumers inject the@posthog/platformidentifier directly — done per feature slice (clipboard/dialog/secure-storage/notifications/updater/power-manager/context-menu capability slices). - Validation:
@posthog/platformbuild + typecheck green;apps/codetypecheck (node+web) green;apps/code/src/main/di/platform-identifiers.test.ts4/4 (identifiers unique/namespaced; toService alias === platform singleton). Boot smoke deferred — boot path concurrently owned by in-progress di-foundation in this shared worktree.
- Moved:
apps/code/src/main/services/file-watcher/deleted entirely. Orchestration (debounce, bulk threshold, git event filtering, git-dir resolution) lives inpackages/workspace-server/src/services/watcher/service.tsasWatcherService.watchRepo(). New tRPC subscription procedurefileWatcher.watchemits the processedFileWatcherEventdiscriminated union. Rawwatcher.watchstill available for unprocessed events. - Nothing for file-watcher lives in
packages/core/. The "orchestration" we thought belonged in core (debounce, bulk threshold, git filtering) turned out to be source-smoothing — properties of the event source, not domain logic. Source-smoothing belongs with the source. Core is for business state machines, retries, cross-feature coordination — none of which file-watcher has. - New transport (still applies):
workspace-clientusessplitLinkoverhttpSubscriptionLink(SSE) for subscriptions +httpBatchLinkfor queries/mutations. SSE auth via?secret=query param since EventSource can't send headers. - Renderer hook (
packages/ui/src/features/file-watcher/useFileWatcher.ts) is a 5-lineuseSubscription(trpc.fileWatcher.watch.subscriptionOptions(...))wrapper. NouseEffect, nofor-await, no orchestration state — pure react-query idiom. Caller passes a singleonEventcallback and switches onevent.kind. - Main bridge:
apps/code/src/main/services/file-watcher/bridge.tsis a smallFileWatcherBridgeclass (~40 lines) that subscribes tofileWatcher.watchvia workspace-client and re-emits viaTypedEventEmitterfor the four legacy in-process consumers (fs,archive,suspension,workspace). Bound atMAIN_TOKENS.FileWatcherServiceviacontainer.bind(...).toConstantValue(new FileWatcherBridge(workspaceClient))inindex.tsafterworkspaceServer.start(). - Bridge retirement: delete
FileWatcherBridge, its router, and the renderer'sstart/stopmutation calls when fs, archive, suspension, workspace migrate. Those consumers will then useuseFileWatcherdirectly (renderer) or subscribe via workspace-client (background work in workspace-server or main). - Cleaned:
WatcherRegistryServicedep dropped (itsisShutdowncheck is unnecessary — subscriptions die naturally when workspace-server child or main process exits). Schemas split out oftrpc.tsinto per-serviceschemas.ts. Router is now strict one-liners. - Left as-is: two parallel watcher pipelines per repo (the bridge + the renderer each subscribe to workspace-server); workspace-server doesn't dedupe parcel watchers.
FsServicein main still owns its file-cache invalidation.WatcherRegistryServicestill used by focus + app-lifecycle. - New import paths:
import { useFileWatcher } from "@posthog/ui/features/file-watcher/useFileWatcher". For main consumers needing kind constants:import { FileWatcherEventKind } from "@posthog/workspace-server/services/watcher/schemas". Bridge class:apps/code/src/main/services/file-watcher/bridge.ts.
- Moved:
apps/code/src/renderer/api/{fetcher,generated,generated.augment,fetcher.test}.ts→packages/api-client/src/.generated.augment.d.ts→.ts(side-effect import fromindex.tsso apps/code's tsc picks up the module augmentation through the package's exports). - Cleaned:
__APP_VERSION__Vite global removed from fetcher — now anappVersionfield onApiFetcherConfig. Renderer wrapper passes the global at construction. - Left as-is: the 2929-line
posthogClient.tsgod-class. Tagged with aPORT NOTE— gets sliced intopackages/core/<feature>/service.tsper feature, following REFACTOR.md "Coexistence and bridges". - New import path:
@posthog/api-client(was@renderer/api/{fetcher,generated}). Also updatedscripts/update-openapi-client.tsto write into the new package.
- Moved host operations out of Electron main:
apps/code/src/main/services/focus/sync-service.tsdeleted; git/worktree/watch logic now lives inpackages/workspace-server/src/services/focus/{service,sync-service}.tsbehind one-linefocus.*procedures inpackages/workspace-server/src/trpc.ts. - Moved orchestration out of the renderer:
apps/code/src/renderer/stores/sagas/focusSagas.tsdeleted; multi-step enable/disable/restore flow now lives inpackages/core/src/focus/service.tsasFocusController, with dependencies injected as a pure interface. - Renderer stays thin:
apps/code/src/renderer/stores/focusStore.tsis now UI state plus one controller call per action. It adapts existing tRPC calls into the core dependency interface and no longer owns the flow graph. - Main is a bridge, not the source of truth for focus logic:
apps/code/src/main/services/focus/service.tsnow persists the local session snapshot for Electron restarts, forwards mutations/queries to workspace-server throughWorkspaceClient, and re-emits focus events to legacy main-router subscribers. - Bridge retirement: delete the main
FocusServiceshim and move persisted focus-session storage out of Electron once session restore/event subscribers can read directly from workspace-server (or the eventual shared persistence layer). At that point the mainfocusrouter can disappear with the bridge. - Left as-is: restore still re-saves the validated session before starting workspace-server watchers so the server-side in-memory session map is repopulated after app restart. That is intentional coexistence glue, not the final architecture.
- Moved:
apps/code/src/main/services/git/getDiffStats→packages/workspace-server/src/services/git/service.ts+packages/ui/src/features/diff-stats/ - New:
@posthog/workspace-server,@posthog/workspace-client,@posthog/uipackages. Workspace-server runs as a child process spawned by Electron (ELECTRON_RUN_AS_NODE=1). - Cleaned: PSK comparison now uses
timingSafeEqual.DiffStatsschema is the source of truth (z.infer), not the type. Connection query invalidates on child exit via a tRPC subscription. - Left as-is:
useTaskDiffSummaryStatsstill has 4 modes (local/branch/PR/cloud). Collapses once the relay protocol exists. - New import paths:
useDiffStats(repoPath)from@posthog/ui/features/diff-stats/useDiffStats(wastrpc.git.getDiffStats).DiffStatsBadgefrom@posthog/ui/features/diff-stats/DiffStatsBadge.
- Moved:
apps/code/src/main/services/environment/{service,schemas,service.test}.ts->packages/workspace-server/src/services/environment/. fs-based TOML environment CRUD is a host capability. - Registered: ws-server
TOKENS.EnvironmentService+environmenttRPC router (list/get/create/update/delete, zod in/out). Added vitest to workspace-server (test script + config + smol-toml dep). - Moved:
apps/code/src/renderer/features/environments/components/EnvironmentSelector.tsx->packages/ui/src/features/environments/+ newuseEnvironmentshook (workspace-client). Cross-feature settings reach-in replaced by anonCreateEnvironmentprop wired in TaskInput. - Data: source of truth is the per-repo
.posthog-code/environments/*.tomlfiles, read/written by ws-server EnvironmentService;Environmentzod schema is the contract. Renderer holds no env truth (react-query cache). - Bridge:
apps/code/src/main/services/environment/service.tsnow forwards to workspace-client (binding inindex.ts); mainenvironmentrouter +environment/schemas.tsremain until the settings/task-detail renderer consumers move to workspace-client. - Deferred:
session-env/loader.ts(agent bash env + CLAUDE_CONFIG_DIR) stays in main. - Validation: ws-server typecheck + 21 environment tests; packages/ui typecheck; apps/code 0 new typecheck errors. App smoke pending.
- Split:
git-core->git-read/git-worktree/git-mutate/git-prsub-slices (git-core marked blocked/superseded). - Moved: read-only git ops into
packages/workspace-server/src/services/git/(thin wrappers over@posthog/git/queries) behind a one-linegittRPC router (zod in/out). - Registered:
MAIN_TOKENS.WorkspaceClient(the workspace-client bound inindex.tsafterworkspaceServer.start()). - Bridge:
apps/code/src/main/trpc/routers/git.tsread procedures forward to ws-server via workspace-client. MainGitServiceretains read methods for in-process callers (WorkspaceService/HandoffService); retire with git-mutate/git-worktree + ui-git-interaction. - Data: read git state computed by
@posthog/git/queriesin ws-server; no new persisted state. Reads are lockless; the per-repo write lock stays with git-mutate. - Validation: ws-server typecheck; apps/code 0 new errors on git surface; env tests 21/21. App smoke pending.
- Moved:
apps/code/src/renderer/features/provisioning/{stores/provisioningStore,components/ProvisioningView}->packages/ui/src/features/provisioning/{store,ProvisioningView}. Output processing (stripAnsi/processOutput) moved from the view into the store. - Registered:
provisioningUiModule(WORKBENCH_CONTRIBUTION -> ProvisioningContribution);PROVISIONING_OUTPUT_PORThost port; desktopTrpcProvisioningOutputServiceadapter bound in desktop-services; module loaded in desktop-contributions. - Cleaned: removed component-level
useSubscription(forbidden) — contribution subscribes once and writes the store; view is pure. Added zustand to @posthog/ui (first store in the package). - Data: source of truth is the main ProvisioningService relay (fed by WorkspaceService.emitOutput); the ui store is a subscription-fed cache (activeTasks Set + output lines per taskId).
- Bridge: main ProvisioningService + provisioning router remain (WorkspaceService is the producer) until the workspace slice migrates.
- Validation: packages/ui typecheck; apps/code typecheck fully green; saga test 7/7. App smoke pending.
- Moved:
WorkspaceMode->@posthog/shared(packages/shared/src/workspace.ts);HandoffLocalGitState+GitHandoffCheckpoint(origin@posthog/git/handoff) ->@posthog/shared(packages/shared/src/git-handoff.ts). - Registered:
@posthog/sharedindex barrel exportsWorkspaceMode,HandoffLocalGitState,GitHandoffCheckpoint. - Data: source of truth for these host-neutral domain types is now
@posthog/shared;@posthog/git,@posthog/agent,@posthog/workspace-server, and apps/code consume/re-export from it.packages/coremay now import them without violating import rules (core may not import@posthog/agentor@posthog/workspace-server). - Cleaned: removed apps/code handoff schema reach-in to ws-server db repository for
WorkspaceMode; removed@posthog/agent->@posthog/git/handoffdependency for the two handoff data types. - Bridge:
@posthog/git/handoffand@posthog/workspace-server/.../workspace-repositoryre-export the relocated types for existing consumers; retire when all consumers import from@posthog/shared. - Bridge: PostHogAPIClient contract + Task/resume domain types NOT yet relocated -> tracked as slice
agent-domain-types. - Validation: typecheck clean across shared/git/agent/workspace-server/core/apps/code (node+web); git handoff 158/158.
- Decision (recorded): domain SQLite persistence lives in
packages/workspace-server(Node-only host capability; travels with the future cloud sandbox). The move itself landed under thepersistence-repositoriesslice. - Added:
packages/workspace-server/src/db/repositories/repositories.test.ts— the only real-SQLite repository round-trip test (RepositoryRepository CRUD + repository→workspace→worktree FK chain), using the sanctionedcreateTestDb()+ stub-DatabaseService pattern. The archive integration test mocks repositories, so this fills the genuine round-trip gap. - Data: drizzle table schema is the single source of truth for DB row shapes (
$inferSelect/$inferInsert). Repositories are in-process, not a serialization boundary — no parallel zod on repo contracts (would duplicate truth). Zod lives at the tRPC boundary in consumer feature slices. - Bridge:
MAIN_TOKENS.*Repository+MAIN_TOKENS.DatabaseServicealiases remain in apps/code container.ts (PORT NOTE) until consumers injectDATABASE_SERVICE/package repositories directly. - Validation: ws-server typecheck clean with the test added; no Electron imports (grep). Round-trip test EXECUTION gated on node-ABI better-sqlite3 — local snapshot has Electron-ABI (NODE_MODULE_VERSION 145) so plain-node vitest can't load it; runs green in CI / after
pnpm install. Rebuilding locally was declined (would break the shared Electron app other agents smoke-test).
- Moved:
apps/code/src/renderer/features/auth/utils/userInitials.ts->packages/ui/src/features/auth/userInitials.ts(pure projection, with test) - Registered: added vitest runner to
@posthog/ui(vitest.config.ts + test script); first tests in the package - Data: source of truth is the user record;
getUserInitialsis a pure derived projection (UserLike -> initials) - Consumers:
SettingsDialog,AccountSettingsimport from@posthog/ui/features/auth/userInitials - Bridge: none (clean move; old path deleted)
- Validation:
pnpm --filter @posthog/ui test(28 passed),@posthog/ui typecheckclean - Note:
authslice split into auth-utils/auth-core/auth-callback-server/auth-ui; only auth-utils landed
- Moved: PostHog Task DTOs (
Task,TaskRun,TaskRunArtifact,ArtifactType,TaskRunStatus,TaskRunEnvironment,PostHogAPIConfig)@posthog/agent/types->@posthog/shared(packages/shared/src/task.ts). - Registered:
@posthog/sharedindex barrel exports the Task DTOs;@posthog/agent/typesre-exports them so all existing consumers keep working. - Data: source of truth for the host-neutral PostHog Task model is now
@posthog/shared;packages/coremay import it without importing@posthog/agent(forbidden by import rules). - Bridge:
@posthog/agent/typesre-export remains for existing consumers; retire when they import from@posthog/shared. - Bridge: PostHogAPIClient method contract (interface in
@posthog/api-client) + resume DATA types (ResumeState,ConversationTurn) NOT yet relocated — remain inagent-domain-types(needs new dep edges). - Validation: typecheck clean across shared/agent/workspace-server/ui/core; apps/code residual errors are an unrelated concurrent process-tracking move.
- Moved:
apps/code/src/renderer/features/auth/stores/authUiStateStore.ts->packages/ui/src/features/auth/authUiStateStore.ts(thin UI store) - Moved:
apps/code/src/shared/types/regions.ts->packages/shared/src/regions.ts(host-agnostic region types) - Registered:
CloudRegion/RegionLabel/REGION_LABELS/formatRegionBadgeon the@posthog/sharedbarrel - Data: auth form UI state (mode/invite/region) owned by the thin store; region constants are pure data in shared
- Bridge:
apps/code/src/shared/types/regions.tsre-exports@posthog/shareduntil all 13 importers move - Validation: ui + apps/code typecheck both 0 errors; ui tests 28 passed
- Moved:
apps/code/src/main/services/process-tracking/service.ts->packages/workspace-server/src/services/process-tracking/process-tracking.ts;apps/code/src/main/utils/process-utils.ts->packages/workspace-server/src/services/process-tracking/process-utils.ts - Registered:
processTrackingModule(bindsPROCESS_TRACKING_SERVICE); zod boundary schemas in packageschemas.ts - Data: source of truth is the in-memory live-PID registry owned by ProcessTrackingService (model
TrackedProcess);ProcessSnapshot/DiscoveredProcessare derived projections - Cleaned: dropped app-logger coupling (ws-server no-logger convention); router uses package zod schemas, inline z.enum removed
- Decision: IN-PROCESS KEEP — bound in main (not the ws-server child) so the 6 synchronous consumers (shell/agent/workspace/archive/suspension/app-lifecycle) are unchanged. Same pattern as the SQLite DB layer.
- Bridge:
MAIN_TOKENS.ProcessTrackingServicetoService(PROCESS_TRACKING_SERVICE) in apps/code container;apps/code/src/main/utils/process-utils.tsre-export shim. Retire when consumers inject the package identifier; re-bind to the ws-server child when shell+agent move there. - Validation: ws-server typecheck + 37 unit tests;
pnpm typecheck19/19;pnpm --filter code test122 files/1474;pnpm dev:codeclean boot
- Moved: worktree/auto-suspend settings reads off direct
settingsStoreimport ->@posthog/platform/workspace-settings(IWorkspaceSettings/WORKSPACE_SETTINGS_SERVICE). - Registered:
ElectronWorkspaceSettingsadapter bound toWORKSPACE_SETTINGS_SERVICEinapps/code/src/main/di/container.ts. - Data: source of truth stays the apps/code electron-store
settingsStore; the adapter wraps it; legacy worktree-dir default migration stays in the adapter (apps/code). - Cleaned:
FoldersServiceinjects the port instead of importingsettingsStorefree functions (first consumer). - Bridge:
settingsStorefree functions remain for the other consumers (archive, suspension, workspace, focus shim, shell, os router, worktree-helpers) until their slices migrate to the port. - Validation: platform + apps/code (node+web) typecheck 0 errors; folders service.test.ts 23/23.
- Moved:
apps/code/src/shared/utils/{urls,backoff,repo}.ts->packages/shared/src/* - Registered:
getCloudUrlFromRegion,getBackoffDelay/sleepWithBackoff/BackoffOptions,normalizeRepoKeyon the@posthog/sharedbarrel - Data: pure host-agnostic primitives;
@posthog/sharedis now the single source - Bridge:
apps/code/src/shared/utils/{urls,backoff,repo}.tsre-export@posthog/shareduntil importers move - Validation: @posthog/shared + @posthog/code typecheck both 0 errors
- Added: package-owned repository identifiers in
packages/workspace-server/src/db/identifiers.ts(REPOSITORY/WORKSPACE/WORKTREE/ARCHIVE/SUSPENSION/AUTH_SESSION/AUTH_PREFERENCE/DEFAULT_ADDITIONAL_DIRECTORY) +db/repositories.module.tsbinding each class. - Changed:
apps/code/src/main/di/container.tsloadsrepositoriesModule;MAIN_TOKENS.*Repositoryare now.toService()bridges over the package symbols (was.to(Class)). - Why: the repo classes had moved to the package but their DI identifiers were still apps/code-local, so no package service could inject a repository. This unblocks folders/archive/suspension/workspace.
- Validation: full
pnpm typecheck19/19 green at the time of this change.
- Moved:
apps/code/src/main/services/folders/{service,service.test,schemas}.ts->packages/workspace-server/src/services/folders/{folders,folders.test,schemas}.ts+ newfolders.module.ts,identifiers.ts,ports.ts. - Registered:
foldersModule(binds FOLDERS_SERVICE); hosted in apps/code's container (shares the single SQLite connection — not ws-server tRPC). - Data: source of truth is the SQLite repositories (injected via package identifiers); worktree base path via
WORKSPACE_SETTINGS_SERVICE.getWorktreeLocation()(reused the platform capability, no duplicate port).normalizeRepoKeyinlined. - Cleaned: router/skills repointed to package imports;
apps/code/.../folders/schemas.tsreduced to a type-only re-export for renderer type consumers (no ws-server runtime pulled into the renderer bundle). - Bridge:
MAIN_TOKENS.FoldersService -> FOLDERS_SERVICE;FOLDERS_LOGGERbound tologger.scope("folders-service"). Retire MAIN_TOKENS.FoldersService once consumers inject FOLDERS_SERVICE. - Validation: ws-server typecheck clean;
folders.test.ts23/23 in the new home; apps/code typecheck has zero folders-related errors (remaining apps/code/core red is exogenous: concurrent handoff/agent-types + context-menu migrations). App smoke pending (tree can't fully build while those are red).
- Cleaned: retired 4
MAIN_TOKENS.*platform-alias bridges (FileIcon, AppMeta, BundledResources, ImageProcessor); 5 consumers (external-apps, agent, updates, posthog-plugin, os.ts) now inject the package-owned@posthog/platformsymbols directly. - Registered: removed the
.toServicealiases fromdi/container.tsand the token defs fromdi/tokens.ts. - Bridge:
UrlLauncher/StoragePaths/MainWindowaliases remain until their consumers migrate; os.ts still a service-less router pending carve. - Validation: apps/code node typecheck clean in scope; behavior-preserving.
- Moved:
apps/code/src/main/services/context-menu/{service,schemas,types}.ts->packages/core/src/context-menu/{context-menu,schemas,types}.ts - Registered:
contextMenuCoreModule(bindsCONTEXT_MENU_CONTROLLER); new core portCONTEXT_MENU_EXTERNAL_APPS_PORT - Foundation: bootstrapped core DI — added @posthog/platform + inversify + reflect-metadata to packages/core; added decorator tsconfig flags; updated core charter/description to match REFACTOR.md (host-agnostic business layer with Inversify DI over platform interfaces)
- Data: source of truth is menu content decided by the core ContextMenuService consuming platform CONTEXT_MENU_SERVICE/DIALOG_SERVICE interfaces; ElectronContextMenu adapter only renders the native menu
- Cleaned: retired MAIN_TOKENS.ContextMenu platform alias + Platform.ContextMenu token (core service injects CONTEXT_MENU_SERVICE directly); inverted external-apps coupling behind a core port
- Bridge:
CONTEXT_MENU_EXTERNAL_APPS_PORTtoService(MAIN_TOKENS.ExternalAppsService) until external-apps migrates to a package service - Validation: core typecheck;
pnpm typecheck19/19;pnpm --filter code test120/1450;pnpm dev:codeclean boot
- Moved:
apps/code/src/main/services/archive/{service,service.integration.test,schemas}.ts->packages/workspace-server/src/services/archive/{archive,archive.integration.test,schemas}.ts+archive.module.ts,identifiers.ts,ports.ts. - Registered:
archiveModule(binds ARCHIVE_SERVICE); hosted in apps/code container (single SQLite conn, not ws-server tRPC). - Ports: ARCHIVE_SESSION_CANCELLER (AgentService.cancelSessionsByTaskId) + ARCHIVE_FILE_WATCHER (FileWatcherBridge.stopWatching), bound via container.toDynamicValue lazy ctx.get; ARCHIVE_LOGGER -> logger.scope("archive"); worktree location via WORKSPACE_SETTINGS_SERVICE; repos via package identifiers; PROCESS_TRACKING_SERVICE.
- Data: archivedTaskSchema moved into the package;
apps/code/src/shared/types/archive.ts-> type-only re-export (renderer type consumers unchanged, no ws-server runtime in renderer bundle). - Bridge:
MAIN_TOKENS.ArchiveService -> ARCHIVE_SERVICE. Retire once consumers inject ARCHIVE_SERVICE. - Validation: ws-server typecheck clean; archive.integration.test.ts 23/23 (real git); apps/code zero archive errors (remaining red is exogenous analytics migration). App smoke pending.
- Moved: 401-line service-less
trpc/routers/os.tsbusiness logic -> NEWapps/code/src/main/services/os/service.ts(OsService) +os/schemas.ts. - Registered:
MAIN_TOKENS.OsServicebound toOsServiceindi/container.ts;osRouternow one-line forwards. - Data: OsService constructor-injects DIALOG/URL_LAUNCHER/APP_META/IMAGE_PROCESSOR/WORKSPACE_SETTINGS platform capabilities; owns fs/clipboard/image host ops. Stays in apps/code main (wires Electron platform adapters).
- Cleaned: removed service-less router, inline router business logic, and business-logic container.get from the router; getWorktreeLocation now reads WORKSPACE_SETTINGS_SERVICE.
- Validation: apps/code node+web typecheck 0 errors; behavior-preserving.
- Moved:
apps/code/src/main/services/suspension/{service,service.test,schemas}.ts->packages/workspace-server/src/services/suspension/{suspension,suspension.test,schemas}.ts+suspension.module.ts,identifiers.ts,ports.ts. - Registered:
suspensionModule(binds SUSPENSION_SERVICE); hosted in apps/code container (single SQLite conn). Ports SUSPENSION_SESSION_CANCELLER + SUSPENSION_FILE_WATCHER via toDynamicValue; SUSPENSION_LOGGER -> logger.scope("suspension"); all auto-suspend/worktree settings via WORKSPACE_SETTINGS_SERVICE; repos via package identifiers; PROCESS_TRACKING_SERVICE. Local TypedEventEmitter (no external event consumers). - Data: suspendedTaskSchema/suspensionReasonSchema/suspensionSettingsSchema moved to the package;
apps/code/src/shared/types/suspension.ts-> type-only re-export. - Carve-out: sleep service (OS power) intentionally not bundled — separate concern, follow-up.
- Bridge:
MAIN_TOKENS.SuspensionService -> SUSPENSION_SERVICE; type-imports repointed in index.ts/app-lifecycle/workspace/router. - Validation: ws-server typecheck clean; suspension.test.ts 11/11; apps/code zero suspension errors (remaining red exogenous: @utils/path,@utils/time renderer-utils migration). App smoke pending.
- Cleaned: retired the MainWindow MAIN_TOKENS alias; 10 consumers inject MAIN_WINDOW_SERVICE directly. With this, all 7 in-scope platform aliases (FileIcon/AppMeta/BundledResources/ImageProcessor/StoragePaths/UrlLauncher/MainWindow) are retired and os.ts is carved into OsService.
- Bridge: AppLifecycle/Updater/Notifier MAIN_TOKENS aliases remain (owned by app-lifecycle/updater/notifications slices).
- Validation: apps/code node+web typecheck 0 errors; behavior-preserving.
- Moved: usageBucketSchema/usageOutput + UsageBucket/UsageOutput types from
apps/code/src/main/services/llm-gateway/schemas.ts->packages/core/src/usage/schemas.ts. - llm-gateway/schemas.ts now value+type re-exports from
@posthog/core/usage/schemas— llm-gateway router, usage-monitor, and the 4 renderer billing consumers are unchanged. - Why: usage-monitor is core orchestration and core may not import apps/code; this gives the shared usage domain type a package home core can consume. (If llm-gateway later moves to ws-server, the schema can move to @posthog/shared.)
- Validation: @posthog/core typecheck clean; apps/code zero usage/llm-gateway/billing errors.
- Cleaned: removed the last 3 MAIN_TOKENS platform aliases (AppLifecycle/Updater/Notifier) and the PORT NOTE bridge block. The entire MAIN_TOKENS.* -> @posthog/platform alias bridge is gone; all consumers inject package-owned platform identifiers directly.
- Validation: apps/code node+web typecheck 0 errors.
- Moved:
LinearIntegrationService+ integration flow schemasapps/code/.../linear-integration->packages/core/src/integrations/{linear.ts,schemas.ts}. - Registered: container binds
MAIN_TOKENS.LinearIntegrationServiceto the core class; router forwards. - Bridge:
apps/code/.../integration-flow-schemas.tsre-exports the core schemas (github/slack consume via it until they migrate). - Validation: core integrations + apps/code node+web typecheck 0 errors.
- Moved: 3 duplicate node:events-based TypedEventEmitter copies (apps/code main util + ws-server connectivity/focus) -> ONE browser-safe impl in
packages/shared/src/typed-event-emitter.ts - Registered: exported
TypedEventEmitterfrom the @posthog/shared barrel; added @posthog/shared dep to @posthog/workspace-server - Data: source of truth is the single shared emitter; per-service typed event maps are projections over it
- Cleaned: removed node:events coupling from the subscription backbone so packages/core (and future web/mobile hosts) can consume it; full EventEmitter API + buffered toIterable(event,{signal})
- Bridge:
apps/code/src/main/utils/typed-event-emitter.tsre-exports from @posthog/shared so the 24 main services + ~20 tRPC subscription routers stay unchanged — retire by repointing them to @posthog/shared - Validation: shared unit test 13/13; pnpm typecheck 19/19; apps/code tests 1395; pnpm dev:code full boot with live subscription layer, zero emitter errors
- Added:
@posthog/platform/deep-link(IDeepLinkRegistry/DEEP_LINK_SERVICE/DeepLinkHandler).DeepLinkServiceimplements it; 7 feature consumers inject the port instead of the concrete service. - Data: deep-link handler registry is now a host-neutral port; apps/code provides the impl; host-boot protocol registration + URL dispatch stay on the concrete service.
- Validation: apps/code node+web typecheck 0 errors.
- Moved:
apps/code/src/main/services/usage-monitor/{service,service.test,schemas}.ts->packages/core/src/usage/{usage-monitor,usage-monitor.test,monitor-schemas}.ts+ schemas.ts (usage types), ports.ts, identifiers.ts, usage-monitor.module.ts. - Registered:
usageMonitorModule(binds USAGE_MONITOR_SERVICE); hosted in apps/code container. Ports: USAGE_GATEWAY (LlmGatewayService.fetchUsage), USAGE_ACTIVITY_MONITOR (AgentService LlmActivity + hasActiveSessions) via toDynamicValue; USAGE_THRESHOLD_STORE + USAGE_LOGGER via toConstantValue. Local TypedEventEmitter (router subscriptions over toIterable). - Data: usage schema (usageBucketSchema/usageOutput) lives in @posthog/core/usage/schemas; llm-gateway/schemas.ts re-exports. usage-monitor/store.ts (electron-store) retained in apps/code, wrapped by the THRESHOLD_STORE adapter.
- Bridge:
MAIN_TOKENS.UsageMonitorService -> USAGE_MONITOR_SERVICE; router repointed to core. - Validation: full
pnpm typecheck19/19 green; usage-monitor.test 12/12 in core.
- Moved:
GitHubIntegrationService+SlackIntegrationService->packages/core/src/integrations/{github.ts,slack.ts}(+identifiers.tswithIntegrationLoggerand per-provider logger tokens). - Registered: container binds
MAIN_TOKENS.{GitHub,Slack}IntegrationServiceto the core classes and the*_INTEGRATION_LOGGERtokens tologger.scope(...); routers/index repoint to core. - Data: services inject DEEP_LINK/URL_LAUNCHER/MAIN_WINDOW platform ports + an injected logger; flow schemas + region utils + TypedEventEmitter from core/shared. All 3 integration services (linear/github/slack) now in
packages/core. - Bridge: apps/code
integration-flow-schemas.tsstill re-exports core schemas; sharedfeatures/integrationsUI not yet moved to packages/ui. - Validation: apps/code node+web typecheck 0 errors.
- Moved: apps/code/src/main/services/updates/{service,schemas,test}.ts -> packages/core/src/updates/{updates,schemas,updates.test}.ts
- Registered: updatesCoreModule (UPDATES_SERVICE); new UPDATE_LIFECYCLE_PORT + UPDATES_LOGGER
- Data: source of truth is the UpdatesService state machine (idle/checking/downloading/ready/installing/error) over platform UPDATER/APP_LIFECYCLE/APP_META/MAIN_WINDOW interfaces; updateStore is a subscription projection
- Cleaned: extends @posthog/shared TypedEventEmitter (no node:events); inverted the update-quit handoff behind UPDATE_LIFECYCLE_PORT; logger via injected SagaLogger; isDevBuild->appMeta.isProduction; added vitest to packages/core
- Bridge: MAIN_TOKENS.UpdatesService toService(UPDATES_SERVICE) + UPDATE_LIFECYCLE_PORT toService(MAIN_TOKENS.AppLifecycleService) until menu/index/router migrate
- Validation: core tests 66; pnpm typecheck 19/19; apps/code tests 1329; dev:code boot clean
- Moved:
apps/code/src/main/services/auth/service.ts(AuthService) ->packages/core/src/auth/auth.ts - Registered: AUTH_PREFERENCE/SESSION/OAUTH_FLOW/CONNECTIVITY/TOKEN_CIPHER ports (packages/core/src/auth/ports.ts); auth.module.ts; WORKBENCH_LOGGER bound in main
- Data: AuthService owns session/refresh truth; ws-server drizzle rows mapped to core domain records (AuthSessionRecord/AuthPreferenceRecord) in desktop adapters
- Cleaned: removed the forbidden ws-server/electron coupling from the auth business logic; OAuth host flow behind OAUTH_FLOW_PORT (OAuthService stays the Electron adapter)
- Bridge:
apps/code/src/main/services/auth/service.tsre-exports@posthog/core/auth/authuntil consumers import it directly - Validation: full typecheck 19/19; apps/code 1292 tests; core auth 18 tests
- Moved:
apps/code/src/main/services/enrichment/{service,detectPosthogInstallState.test,findStaleFlagSuggestions.test}.ts->packages/core/src/enrichment/{enrichment,detectPosthogInstallState.test,findStaleFlagSuggestions.test}.ts+ ports.ts, identifiers.ts, enrichment.module.ts. - Registered:
enrichmentModule(binds ENRICHMENT_SERVICE); hosted in apps/code container. Ports: ENRICHMENT_AUTH (AuthService), ENRICHMENT_FILE_READER (node fs + @posthog/git listFilesContainingText), ENRICHMENT_LOGGER. core consumes @posthog/enricher directly (added to core deps; @posthog/git devDep for tests). - Cleaned: core stays fs/git-free behind the file-reader port; auth behind a minimal port shape.
- Bridge:
MAIN_TOKENS.EnrichmentService -> ENRICHMENT_SERVICE; router repointed to @posthog/core/enrichment. - Validation: core typecheck clean; 19/19 enrichment tests in core (real git + tree-sitter + fetch mocks); apps/code zero enrichment errors.
- Moved:
TaskLinkService/InboxLinkService/NewTaskLinkService->packages/core/src/links/*(+identifiers.tsLinkLogger + per-service logger tokens). Tests moved too (39 pass). - Registered: container binds
MAIN_TOKENS.{Task,Inbox,NewTask}LinkServiceto the core classes + the logger tokens tologger.scope(...); index/deep-link-router/notification repoint to core. - Data: services inject DEEP_LINK + MAIN_WINDOW platform ports + injected logger; TypedEventEmitter + deep-link utils from shared. No AuthService coupling.
- Validation: core links 39 tests; apps/code node+web 0 errors.
- Moved:
apps/code/src/main/services/mcp-apps/service.ts->packages/core/src/mcp-apps/mcp-apps.ts;apps/code/src/shared/types/mcp-apps.ts->packages/core/src/mcp-apps/schemas.ts(+ identifiers.ts, ports.ts, mcp-apps.module.ts). - Registered:
mcpAppsModule(binds MCP_APPS_SERVICE); hosted in apps/code container. Injects URL_LAUNCHER_SERVICE + MCP_APPS_LOGGER; local TypedEventEmitter. Added @modelcontextprotocol/sdk + ext-apps to core deps. - Cleaned: apps/code @shared/types/mcp-apps ->
export *re-export from core (renderer + router unchanged); menu.ts + agent type-imports repointed. - Bridge:
MAIN_TOKENS.McpAppsService -> MCP_APPS_SERVICE. - Validation: core typecheck clean; apps/code zero mcp errors (remaining red exogenous: posthog-plugin migration). App smoke pending.
- Moved: apps/code/src/main/services/posthog-plugin/* + utils/extract-zip.ts -> packages/workspace-server/src/services/posthog-plugin/*
- Registered: posthogPluginModule (POSTHOG_PLUGIN_SERVICE); POSTHOG_PLUGIN_LOGGER; added fflate dep
- Data: source of truth is the runtime plugin/skills dirs under appDataPath; PosthogPluginService orchestrates download+overlay+codex-sync via UpdateSkillsSaga
- Cleaned: extends @posthog/shared TypedEventEmitter; captureException via platform ANALYTICS_SERVICE; isDevBuild->appMeta.isProduction; logger via injected SagaLogger
- Bridge: MAIN_TOKENS.PosthogPluginService toService(POSTHOG_PLUGIN_SERVICE) until index/skills/agent inject directly
- Validation: ws-server typecheck + 27 tests; apps/code+core typecheck 0; dev:code boot 'Saga completed successfully'
- Moved:
apps/code/src/main/services/external-apps/{service,schemas,types}.ts->packages/workspace-server/src/services/external-apps/{external-apps,schemas,types}.ts+ identifiers.ts, ports.ts, external-apps.module.ts. - Registered:
externalAppsModule(binds EXTERNAL_APPS_SERVICE); hosted in apps/code container. Injects CLIPBOARD_SERVICE + FILE_ICON_SERVICE + EXTERNAL_APPS_STORE port (electron-store bound in apps/code). Dropped getPrefsStore() (unused) + STORAGE_PATHS (only fed the store). DetectedApplication/ExternalAppType from ./schemas (no @shared barrel dep). - Bridge:
MAIN_TOKENS.ExternalAppsService -> EXTERNAL_APPS_SERVICE(CONTEXT_MENU_EXTERNAL_APPS_PORT resolves through it); router + index.ts repointed. - Validation: full
pnpm typecheck19/19 green.
- Moved:
apps/code/src/main/services/llm-gateway/{service,schemas}.ts->packages/core/src/llm-gateway/{llm-gateway,schemas}.ts+ ports.ts, identifiers.ts, llm-gateway.module.ts. - Registered:
llmGatewayModule; hosted in apps/code container. Ports keep core @posthog/agent-free: LLM_GATEWAY_AUTH (AuthService getValidAccessToken+authenticatedFetch), LLM_GATEWAY_ENDPOINTS (apps/code supplies @posthog/agent URL helpers + DEFAULT_GATEWAY_MODEL), LLM_GATEWAY_LOGGER. - Cleaned: apps/code llm-gateway/schemas.ts ->
export *re-export from core (renderer billing type consumers unchanged); git/service + router repointed. - Bridge:
MAIN_TOKENS.LlmGatewayService -> LLM_GATEWAY_SERVICE. - Validation: core typecheck clean; apps/code zero llm-gateway errors (remaining red exogenous: GitFileStatus shared migration).
- Moved: the dev HTTP callback server from
apps/code/src/main/services/oauth/service.ts->packages/workspace-server/src/services/oauth-callback/oauth-callback.ts(OAuthCallbackServer.waitForCode owns http.Server/listen/connections/timeout/HTML; cancel via AbortSignal). - Registered:
oauthCallbackModule(binds OAUTH_CALLBACK_SERVER); loaded in apps/code container. - Refactored: OAuthService (stays in apps/code) injects OAUTH_CALLBACK_SERVER; waitForHttpCallback delegates; pendingFlow uses an AbortController; getCallbackHtml/cleanupHttpServer/node:http removed. Deep-link prod path + PKCE + token exchange unchanged.
- Validation: full
pnpm typecheck19/19 green.
- Moved: dev HTTP callback server from
apps/code/src/main/services/mcp-callback/service.ts->packages/workspace-server/src/services/mcp-callback/mcp-callback-server.ts(McpCallbackServer.waitForCallback -> URLSearchParams; owns http.Server/timeout/connections/HTML; cancel via AbortSignal;successWhenpredicate picks success/error HTML). - Registered:
mcpCallbackModule(MCP_CALLBACK_SERVER); loaded in apps/code container. - Refactored: McpCallbackService (apps/code) injects MCP_CALLBACK_SERVER, delegates; pendingCallback uses AbortController; getCallbackHtml/cleanupHttpServer/node:http removed. Deep-link prod path + events unchanged.
- Validation: full
pnpm typecheck19/19 green. Same pattern as auth-callback-server.
- Moved:
apps/code/src/main/services/os/{service,schemas}.ts->packages/workspace-server/src/services/os/{os,schemas}.ts+ identifiers, os.module.ts. - Registered:
osModule(OS_SERVICE); hosted in apps/code container. Injects only platform services (DIALOG/URL_LAUNCHER/APP_META/IMAGE_PROCESSOR/WORKSPACE_SETTINGS) + node fs/os/path + @posthog/shared image utils. - Bridge:
MAIN_TOKENS.OsService -> OS_SERVICE; os router repointed (service + schemas). - Validation: full
pnpm typecheck19/19 green.
- Moved:
apps/code/src/main/services/cloud-task/*->packages/core/src/cloud-task/{cloud-task,schemas,cloud-task-types,sse-parser}.ts+ ports/identifiers/module + tests. - Registered:
cloudTaskModule; hosted in apps/code container. CLOUD_TASK_AUTH port (AuthService.authenticatedFetch) + CLOUD_TASK_LOGGER. @posthog/shared TypedEventEmitter + StoredLogEntry/TaskRunStatus. SseEventParser logger decoupled (onWarn callback). - Data: CloudTask* update types kept as a core copy (cloud-task-types.ts) pending the concurrent shared-domain-types relocation landing in the @posthog/shared index barrel.
- Bridge:
MAIN_TOKENS.CloudTaskService -> CLOUD_TASK_SERVICE; router + handoff repointed. - Validation: full
pnpm typecheck19/19 green; cloud-task.test 22/22 + sse-parser 3/3 in core.
- Moved:
apps/code/src/main/services/shell/{service,schemas}.ts->packages/workspace-server/src/services/shell/{shell,schemas}.ts+ identifiers/ports/module. pty = ws-server host concern. - Registered:
shellModule(SHELL_SERVICE); hosted in apps/code container. Injects PROCESS_TRACKING + repos + WORKSPACE_SETTINGS (inlined deriveWorktreePath) + SHELL_LOGGER. @posthog/shared TypedEventEmitter + ws-server buildWorkspaceEnv. Added node-pty to ws-server deps. - Bridge:
MAIN_TOKENS.ShellService -> SHELL_SERVICE; shell + agent routers repointed. - Validation: ws-server + core + apps/code typecheck clean (ui red is exogenous).
- Moved:
apps/code/src/main/services/ui/{service,schemas}.ts->packages/core/src/ui/{ui,schemas}.ts+ identifiers/ports/module. UI command event relay (menu->renderer) over @posthog/shared TypedEventEmitter; UI_AUTH port (test-only token invalidation). - Bridge:
MAIN_TOKENS.UIService -> UI_SERVICE; menu.ts + ui router repointed. - Validation: full
pnpm typecheck19/19 green.
- Moved:
apps/code/src/main/services/oauth/{service,schemas}.ts->packages/core/src/oauth/{oauth,schemas}.ts+ identifiers/ports/module. PKCE flow orchestration. - Registered:
oauthModule; hosted in apps/code container. Platform deps (DEEP_LINK/URL_LAUNCHER/MAIN_WINDOW) + OAUTH_CALLBACK port (-> ws-server OAuthCallbackServer) + OAUTH_ENV {isDev} + OAUTH_LOGGER. oauth constants/backoff/urls from @posthog/shared. - Bridge:
MAIN_TOKENS.OAuthService -> OAUTH_SERVICE; router/index/port-adapters repointed. - Validation: full
pnpm typecheck19/19 green.
- Retired MAIN_TOKENS.{OsService, FoldersService, ArchiveService, UsageMonitorService, EnrichmentService, UIService} — consumers (routers + menu.ts) now inject the package identifiers (OS_SERVICE, FOLDERS_SERVICE, ARCHIVE_SERVICE, USAGE_MONITOR_SERVICE, ENRICHMENT_SERVICE, UI_SERVICE) directly; the
.toServicebridges + MAIN_TOKENS tokens deleted. The documented final migration step for these ported services. - Remaining MAIN_TOKENS service bridges (LlmGateway, CloudTask, Suspension, McpApps) stay until their cross-service injectors in the agent/workspace/handoff tangle migrate.
- Validation: full
pnpm typecheck19/19 green.
- Retired MAIN_TOKENS.{ShellService, AuthProxyService, McpProxyService}. Consumers were routers/adapters, NOT the tangle classes: shell + agent routers (container.get) -> SHELL_SERVICE; agent/auth-adapter (@inject) -> AUTH_PROXY_SERVICE + MCP_PROXY_SERVICE.
.toServicebridges + MAIN_TOKENS tokens deleted; _AUTH/_LOGGER port bindings + ws-server modules kept. - 9 bridges retired total this session. Validation: apps/code typecheck clean.
- handoff/AgentService are blocked on @posthog/agent coupling (runtime resumeFromLog + agent type signatures). DECISION: do NOT make @posthog/agent a core dependency (would break core's host-agnostic web/mobile purpose; the SDK is Node/process-coupled), and do NOT touch the @posthog/agent package now.
- Consequence: handoff + AgentService stay in apps/code (desktop host services, not core slices) until a later agent-package split extracts pure types/utils to @posthog/shared and injects the runtime via ports.
- Moved:
apps/code/src/renderer/features/terminal/*(TerminalManager 514LOC, terminalStore, resolveTerminalFontFamily, Terminal/ShellTerminal/ActionTerminal components) ->packages/ui/features/terminal/. - Registered:
ShellClientport (packages/ui/features/terminal/shellClient.ts, incl. onData/onExit subscription methods) + apps/codeshellClientAdapterwrapping trpcClient.shell.* + os.openExternal, registered at boot in main.tsx. - Cleaned: components now subscribe via the imperative port in useEffect (no trpcReact); service/store use getShellClient(); logger/platform via @posthog/ui ports; xterm added to ui deps.
- Bridge: none — fully ported. Shell output subscriptions flow through the ShellClient port.
- Validation: apps web 0, node 0; ui terminal test 7/7; full ui sweep 157.
- Moved: @utils/{session,promptContent}, features/sessions/{hooks/useSession,stores/sessionStore} -> packages/ui/features/sessions/* (path/session-events types via @posthog/shared; PermissionRequest/UserMessageAttachment via ui session types; ACP via ui dep).
- Cleaned: removed apps/code @utils/session + @utils/promptContent + the sessions hooks/stores dirs. sessionStore was unblocked by relocating its util chain bottom-up.
- Bridge: sessions COMPONENTS (SessionView etc.) remain in apps/code (trpcReact); convert via the imperative-port + useEffect pattern next.
- Validation: apps web 0, node 0; ui 186 tests.
- Moved: branch create/checkout, stage/unstage, discard, sync-status (+fetch throttle = source smoothing), push/pull/publish/sync + a mutate-variant getStateSnapshot from
apps/code/src/main/services/git/service.tsintopackages/workspace-server/src/services/git/service.ts. Added the matching zod schemas to the packageschemas.ts. - Registered: 11 one-line
git.*procedures inpackages/workspace-server/src/trpc.ts. Maingitrouter procedures now FORWARD to ws-server viaWorkspaceClient(extends the git-read PORT NOTE). MainGitServicekeeps the methods for in-process callers (WorkspaceService/HandoffService/createPr). - Data: source of truth for these ops is
@posthog/git(sagas/queries) running in the ws-server child; GitStateSnapshot is a derived aggregate (changedFiles+diffStats+syncStatus+latestCommit). PR status excluded from the mutate snapshot (never requested by this group). - Deferred:
commit(needs AgentService session-env — main process),cloneRepository+onCloneProgress(progress streaming). All gh/PR ops → git-pr. - Bridge retirement: delete the main forwarding when renderer git-interaction consumes
workspaceClient.git.*directly (ui-git-interaction slice). - Validation: ws-server typecheck clean; apps/code git router/service 0 errors (remaining apps/code red exogenous); ws-server tests 243/248 (5 = known better-sqlite3 Electron-ABI DB test). App smoke pending.
- Moved:
apps/code/src/main/services/workspace/service.ts->packages/workspace-server/src/services/workspace/workspace.ts;schemas.ts-> same package dir.apps/code/.../workspace/schemas.tsis now a re-export shim (14 rendererimport typeconsumers + workspace router). Deleted dead duplicateworkspaceEnv.ts(canonical:packages/workspace-server/src/workspace-env.ts). - Registered:
workspaceModule(bindsWORKSPACE_SERVICE); ports.ts + identifiers.ts. Full constructor injection. - Data: source of truth is the WORKSPACE/WORKTREE/REPOSITORY repos (ws-server); derived projections are Workspace/WorkspaceInfo/WorktreeInfo computed per call (git branch via repo-fs-query), activeRepoStore (UI), workspace UI.
- Cleaned: removed the last
MAIN_TOKENSproperty-injection in WorkspaceService. Cross-layer deps now narrow ports:WORKSPACE_AGENT(cancelSessionsByTaskId + onAgentFileActivity),WORKSPACE_FILE_WATCHER(stopWatching + onGitStateChanged),WORKSPACE_FOCUS(onBranchRenamed),WORKSPACE_PROVISIONING(emitOutput),WORKSPACE_LOGGER; settings via WORKSPACE_SETTINGS_SERVICE, analytics via ANALYTICS_SERVICE. ws-server never imports core (provisioning is a port) or apps/code. - Bridge:
MAIN_TOKENS.WorkspaceService -> WORKSPACE_SERVICE(toService) for the workspace router + GitService + index.ts initBranchWatcher. Retire once those inject WORKSPACE_SERVICE. schemas shim retires when renderer workspace types move to @posthog/shared / workspace-client. - Validation: ws-server typecheck clean;
biome lint packages/workspace-server/src/services/workspace0 noRestrictedImports; newworkspace.test.ts7/7. apps/code typecheck has 0 workspace-attributable errors.
- Moved:
apps/code/src/main/services/agent/{service.ts,auth-adapter.ts,discover-plugins.ts,schemas.ts}->packages/workspace-server/src/services/agent/{agent.ts,auth-adapter.ts,discover-plugins.ts,schemas.ts} - Registered:
agentModule(bindsAGENT_SERVICE,AGENT_AUTH_ADAPTER); 5 inversion ports (AGENT_SLEEP_COORDINATOR,AGENT_MCP_APPS,AGENT_REPO_FILES,AGENT_AUTH,AGENT_LOGGER) bound in apps/code container - Data: source of truth is
packages/agentframework; ws-serverAgentServiceowns session lifecycle; projection = session messages in sessions UI - Cleaned: agent SDK host integration now lives in a package, not apps/code; core/host deps inverted into narrow ports (no more direct McpApps/Sleep/Auth/Fs coupling in the moved service); ws-server moved to zod v4
- Bridge:
MAIN_TOKENS.AgentService+MAIN_TOKENS.AgentAuthAdapter(toServicealiases) remain until handoff/git/router/usage-monitor injectAGENT_SERVICEdirectly - Validation:
@posthog/workspace-server typecheck0; agent unit tests 44/44;biome lintagent dir 0 noRestrictedImports. Live-app smoke deferred (concurrent MAIN_TOKENS slice breaks apps/code build)
- Moved 18 pure gh-CLI methods (gh status/auth, PR status/url/open/details, PR+branch file diffs + toUnifiedDiffPatch, review comments + resolve/reply/update, PR template, commit conventions, GitHub ref search/issue/PR) from
apps/code/src/main/services/git/service.tsintopackages/workspace-server/src/services/git/service.ts, with matching zod schemas. 18 one-linegit.*procedures in wstrpc.ts; maingitrouter procedures forward viaWorkspaceClient(extends the git-read/git-mutate PORT NOTE). Main GitService keeps the methods for in-process callers (createPr). - Data: source of truth is the
ghCLI /@posthog/gitrunning in the ws-server child; no new persisted state. Dropped the module logger from moved error paths (degrade to null/[] as before). - Deferred (coupled to main-process services, cannot run in the ws-server child): getTaskPrStatus (WorkspaceService), createPr/createPrViaGh (AgentService session-env + WorkspaceService linkBranch + commit), generateCommitMessage/generatePrTitleAndBody (LlmGateway.prompt) — need GIT_WORKSPACE_PORT/GIT_AGENT_ENV_PORT/GIT_LLM_PORT (git-pr-coupled follow-up).
- Bridge retirement: delete the main forwarding when renderer git-interaction consumes
workspaceClient.git.*directly (ui-git-interaction slice). - Validation: ws-server typecheck GREEN; apps/code git router/service 0 errors; biome clean; ws-server tests 294/299 (5 = known better-sqlite3 Electron-ABI DB test). App smoke pending.
- Removed: apps/code dead settings-store duplicates after the canonical port to packages/ui/src/features/settings —
features/settings/stores/{settingsStore,settingsDialogStore}.{ts,test.ts}andrenderer/stores/settingsStore.{ts,test.ts}(the old trpc-based sendMessagesWith store, superseded by the merged packages/ui settingsStore). - Repointed:
features/auth/stores/authStore.ts->@posthog/ui/features/settings/settingsDialogStore(last straggler). - Data: canonical UI settings state lives in
@posthog/ui/features/settings/{settingsStore,settingsDialogStore}(20 + 14 importers).apps/code/src/main/services/settingsStore.tsis a separate main-process store (worktree location) and stays. - Bridge: none. Remaining ui-settings work: move the feature components (components/sections/*) + SETTINGS_SERVICE interface.
- Validation: packages/ui settings tests 11/11; apps/code 0 fallout from the deletions (typecheck down to 1 exogenous error).
- Moved: host-agnostic git-interaction layer apps/code -> packages/ui/src/features/git-interaction (types, utils/{branchNameValidation,deriveBranchName,diffStats,errorPrompts,fileKey,gitStatusUtils,partitionByStaged}, state/{gitInteractionLogic,gitInteractionStore} + tests). ~20 consumers repointed to @posthog/ui; old copies deleted.
- New shared: packages/shared/src/git-naming.ts (BRANCH_PREFIX), barrel-exported; apps/code @shared/constants re-exports it (single source).
- Data: gitInteractionStore is a thin UI store (zustand + electronStorage via @posthog/ui/workbench/rendererStorage); gitInteractionLogic is pure menu-action logic.
- Deferred (blocked on git-pr-coupled transport): prStatus.tsx (@main PrActionType), trpc-coupled utils (branchCreation/getSuggestedBranchName/gitCacheKeys/updateGitCache), hooks (useGitQueries etc.), components (BranchSelector/CreatePrDialog/etc.) — they consume trpc.git.* via renderer->main and need workspace-client + the coupled ops ported.
- Validation: @posthog/shared+ui+apps/code typecheck clean; 56 ui tests pass; apps/code 2 remaining errors are exogenous.
- Moved: 14 permission components + types
apps/code/src/renderer/components/permissions->packages/ui/src/features/permissions;components/action-selector/*->packages/ui/src/primitives/action-selector(completes the ui ActionSelector facade);mcp-app-host-utils->ui/features/mcp-apps/utils;posthog-exec-display->ui/features/posthog-mcp/utils - Registered: ui deps +=
@posthog/agent,@modelcontextprotocol/ext-apps,@modelcontextprotocol/sdk - Cleaned: fixed the dangling
ui/primitives/ActionSelectorre-export (3 ui errors); UI permission rendering no longer lives in apps/code - Bridge: apps shims
components/{ActionSelector,permissions/PermissionSelector,permissions/PlanContent}.tsx,mcp-apps/utils/mcp-app-host-utils.ts,posthog-mcp/utils/posthog-exec-display.ts— retire as sessions/mcp-apps consumers import@posthog/uidirectly - Validation: ui typecheck moved files clean (total 12->9); apps/code my files clean; biome 0 noRestrictedImports
- Moved: SerializedEnrichment/SerializedFlag/SerializedEvent (+ nested) + FlagType + StalenessReason from
packages/enricher/src/{serialize,types}.ts->packages/shared/src/enrichment.ts(zero-dep, renderer-safe) - Registered: shared barrel
export * from "./enrichment"; enricher +=@posthog/shareddep and re-exports the types from serialize.ts/types.ts (single source of truth; apps/code + ws-server keep importing from@posthog/enricher) - Data: source of truth is
@posthog/shared/enrichment; the enricher scan (ws-server) produces them, the renderer (ui code-editor) renders them - Cleaned: ui code-editor enrichment files (postHogEnrichment, enrichmentPopoverStore) now import from
@posthog/sharedinstead of the layer-restricted@posthog/enricher(biome noRestrictedImports satisfied) - Validation: shared+enricher dists rebuilt; ws-server typecheck 0; apps enricher/code-editor clean; ui biome 0 noRestrictedImports
- Moved: pure logic (mcpFilters/mcpToolBulk/statusBadge), presentational components (ToolPolicyToggle/ToolRow/AddCustomServerForm/ServerCard/McpInstalledRail/MarketplaceView/icons), and 36 service-logo assets -> packages/ui/src/features/mcp-servers + packages/ui/src/assets/services. Added *.png to packages/ui/src/assets.d.ts.
- Data: types/client via @posthog/api-client/posthog-client (ui already depends on api-client). No state owned by the moved layer (pure + presentational).
- Deferred: useMcpServers/useMcpInstallationTools hooks + McpServersView/ServerDetailView views — use main-router useTRPC subscriptions + trpcClient.mcpCallback; need an MCP_OAUTH port + ui->main subscription bridge.
- Validation: ui + apps/code typecheck clean; 16 ui mcp-servers tests pass; apps/code 1 exogenous error.
- Moved: commit-message generation orchestration from the 2049-LOC apps
GitService-> newpackages/core/src/git-pr/(GitPrService) — pure, host-agnostic, unit-testable - Registered:
gitPrModule(binds GIT_PR_SERVICE); ports GIT_DIFF_SOURCE (git CLI reads — core can't import @posthog/git) + GIT_PR_LOGGER, bound in apps container; LLM via core LLM_GATEWAY_SERVICE - Data: prompt-building + LLM call now testable in isolation; git diffs flow through a port
- Cleaned: business logic out of the apps GitService bridge; GitService.generateCommitMessage is now a 3-line delegate (router + CreatePrSaga unchanged)
- Bridge: GitService delegates to GIT_PR_SERVICE (injected); retire once router/saga call GIT_PR_SERVICE directly
- Validation: core typecheck 0 + biome 0 noRestrictedImports (purity gate) + 2 core tests; git service.test 27/27; ws-server 0
- Moved: PR title/body generation -> GitPrService (core). Widened GIT_DIFF_SOURCE port (default/current branch, diff-against-remote, commits-between-branches, PR template, fetch-if-stale).
- Cleaned: GitService no longer depends on the LLM gateway at all (removed the injection) — both commit-message and PR-description generation now live in core; GitService is a thin delegate for them.
- Validation: core 0 + 4 git-pr tests + purity gate; git service.test 27/27
- Moved: CreatePrSaga -> packages/core/src/git-pr/create-pr-saga.ts. Used lightweight structural dep types (no git-schema-graph relocation); @posthog/git getHeadSha + operation-manager soft-reset became deps (getHeadSha + resetSoft).
- Result: ALL git-pr orchestration (generateCommitMessage + generatePrTitleAndBody + CreatePrSaga) now in @posthog/core/git-pr, pure + unit-tested (7 tests). Host GitService.createPr is integration-only (builds the core saga + SSE progress + session env).
- Validation: core 0 + 7 git-pr tests + purity gate; git service.test 27/27; apps non-mcp-servers 0
- Moved:
ActionTabIcon->packages/ui/features/actions/ActionTabIcon.tsx(appsfeatures/actionsdir now fully removed;actionStorewas already in ui).FilePicker->packages/ui/features/command/FilePicker.tsx. - Registered: extended the
ShellClientport (@posthog/ui/features/terminal/shellClient) withdestroy(); hostshellClientAdapterforwards totrpcClient.shell.destroy. ActionTabIcon's only host call now flows through the port — no@renderer/trpc/clientleft in the moved code. - Data: no owned state moved (ActionTabIcon reads
actionStore; FilePicker readspanelLayoutStore+useRepoFiles— all already in ui). - Cleaned: removed the last app-local consumer references (
panels/usePanelLayoutHooks,task-detail/TaskDetail). - Bridge: none added.
command/CommandKeyHints.tsxstays as an app shim only because the still-app-residentCommandMenuimports it. - Validation: ui + apps/code typecheck 0; ui command(6)/repo-files/terminal(7) tests green; biome clean.
- Moved:
apps/code/src/renderer/features/panels/components/{Panel,PanelGroup,PanelResizeHandle,GroupNodeRenderer,PanelDropZones,PanelTree}.tsx+hooks/{useDragDropHandlers,usePanelKeyboardShortcuts}.ts->packages/ui/src/features/panels/{components,hooks}/ - Registered: none (presentational layout primitives over the already-ported panel stores)
- Data: source of truth is
panelLayoutStore(already in ui); these are pure projections - Cleaned: relativized self-name imports;
usePanelKeyboardShortcutskeyboard-shortcuts ->../../command/keyboard-shortcuts; added @dnd-kit/react + react-resizable-panels + react-hotkeys-hook to packages/ui - Bridge: apps
PanelLayoutcontent cluster (PanelLayout/LeafNodeRenderer/TabbedPanel/PanelTab/DraggableTab/usePanelLayoutHooks) stays until ui-task-detail (TabContentRenderer port), a PANEL_CONTEXT_MENU client port, and handleExternalAppAction/workspaceApi are resolved - Validation:
pnpm typecheck19/19;@posthog/ui508/508; biome check+lint clean
- Moved:
apps/code/src/renderer/hooks/useAutoFocusOnTyping.ts->packages/ui/src/features/message-editor/useAutoFocusOnTyping.ts(pure DOM hook; dep only EditorHandle from message-editor types); orphaned colocated testsuseDebounce.test.ts+useImagePanAndZoom.test.tsx->packages/ui/src/primitives/hooks/beside their already-migrated impls - Registered: none (presentational hook + test relocation; no token/contribution)
- Cleaned:
useAutoFocusOnTypingself-name import -> relative./types; repointed 2 consumers (SessionView, TaskInput) to the package path and deleted the app copy (no shim); added jsdom PointerEvent polyfill topackages/ui/src/test/setup.ts(mirrors apps test setup) so pointer-drag hook tests carrypointerId - Bridge: none. Remaining renderer/hooks entries are thin re-export shims or feature-gated (useTask*DeepLink/useTaskContextMenu -> deep-links/task; useRepositoryDirectory -> workspace; useFileWatcher deliberatelyNotSliced)
- Validation:
@posthog/uitypecheck 0 + full vitest 52 files/565 tests green;pnpm --filter code typecheck0 slice-attributable errors (2 exogenous inbox errors from a concurrent move); biome format clean
- Moved: inbox pure utils (filterReports, suggestedReviewerFilters, inboxSort, inboxConstants, build{Discuss,CreatePr}ReportPrompt, pendingInboxOpenMethod) + 8 pure presentational/store leaves ->
packages/ui/features/inbox/{utils,components/utils,components/detail,stores}. - Data: no owned domain state moved; types now sourced from
@posthog/shared/domain-types+@posthog/shared/analytics-events.inboxSignalsSidebarStoreis a thincreateSidebarStoreUI store. - Cleaned: removed app-alias coupling (
@shared/*,@utils/logger) from the moved code; all consumers import from@posthog/ui. - Bridge: none.
inbox/utils/resolveDefaultModel.tsstays in app (trpcClient); knotted views/hooks remain pending navigationStore/auth/trpc ports. - Validation: ui + apps/code typecheck 0; ui inbox tests 73/73; biome clean.
- Moved:
apps/code/src/renderer/features/message-editor/{commands,suggestions/getSuggestions,tiptap/*,components/IssueRow,components/SuggestionStatus}->packages/ui/src/features/message-editor/ - Registered:
MessageEditorHostmodule-setter port (ports.ts); desktop adapterplatform-adapters/message-editor-host.tsset viasetMessageEditorHostin desktop-services - Data: suggestions derived from host (
searchGithubRefs/fetchRepoFiles); prompt encoding already in@posthog/sharedcloud-prompt - Cleaned: removed direct
trpcClient/queryClient/@hooks/useRepoFilescoupling from the suggestion engine + node views; relativized self-name imports - Bridge: attachment subsystem + editor shell (persistFile, AttachmentsBar/IssuePicker/AttachmentMenu, PromptInput, useTiptapEditor) stay in apps until MessageEditorHost gains the os/git attachment methods
- Validation:
pnpm typecheck19/19;@posthog/ui572/572; biome check+lint clean
- Moved:
apps/code/src/renderer/features/code-editor/{hooks/useFileEnrichment.ts,components/EnrichmentPopover.tsx}->packages/ui/src/features/code-editor/{hooks,components}/ - Registered: NEW
ENRICHMENT_CLIENTport (packages/ui/src/features/code-editor/ports.ts) bound toTrpcEnrichmentClient(apps/code/src/renderer/platform-adapters/enrichment-client.ts) indesktop-services.ts - Data: source of truth is the workspace-server EnrichmentService (
enrichment.enrichFile); ui consumes it via the typed client port + TanStack Query, gated onuseAuthStateValue(ui auth store) - Cleaned: ui enrichment UI no longer imports
@renderer/trpc,@posthog/enricher(now@posthog/shared), or@features/auth; openExternal goes through the existing@posthog/ui/workbench/openExternalhost port - Bridge: none for the moved files. code-editor tier-2 (CodeEditorPanel/useCodeMirror/useCloudFileContent/CodeMirrorEditor) remains in apps until a contextMenu client port + workspace/sidebar/task-detail hooks land
- Validation:
@posthog/uitypecheck 0 + full vitest 55 files/580 tests;pnpm --filter code typecheck0 slice-attributable errors (3 exogenous message-editor errors from a concurrent move); biome format clean
- Moved: analytics types, AdapterIndicator, ModeSelector, PromptHistoryDialog, tiptap/useDraftSync -> packages/ui/features/message-editor.
- Registered: extended MessageEditorHost port (saveClipboardImage/Text/File, downscaleImageFile); desktop adapter forwards to trpcClient.os.*. The non-React persistFile module consumes the port (no @renderer import).
- Data: no owned state moved; PromptHistoryDialog analytics via @posthog/shared/analytics-events + @posthog/ui/workbench/analytics.
- Validation: full typecheck 19/19; ui message-editor tests 62/62; biome clean.
- Moved: groupTasks util (repository grouping; deps now @posthog/shared) + SidebarItem base + nav item leaves (Skills/McpServers/CommandCenter/Search/Home/SidebarKbdHint) + SidebarTrigger + DraggableFolder -> packages/ui/features/sidebar.
- Data: groupTasks is pure (Task[] -> grouped); items are props-driven (no store/trpc reach-ins).
- Validation: full typecheck 19/19; ui sidebar tests 41/41; biome clean.
- Moved:
persistFile,useTiptapEditor,AttachmentsBar,IssuePicker,AttachmentMenu(+test),PromptInput,message-editor.css->packages/ui/src/features/message-editor/ - Registered:
MessageEditorHostnow 13 methods (git refs/gh-status, os clipboard/attachments/data-url, fs read, repo files, dir picker); desktop adapterplatform-adapters/message-editor-host.ts - Cleaned: attachment components converted from
useTRPC().queryOptionstouseQuerymanual keys over the host; removed alltrpcClient/queryClient/@renderercoupling - Bridge: only
PromptInput.stories.tsx(storybook) +README.mdremain in apps (host-appropriate) - Validation:
pnpm typecheck19/19;@posthog/ui612/612; message-editor 64/64; biome clean
- Moved:
apps/code/src/renderer/features/git-interaction/{utils/gitCacheKeys.ts,hooks/useGitQueries.ts}->packages/ui/src/features/git-interaction/{gitCacheKeys.ts,useGitQueries.ts} - Registered: host-set
setQueryClient(@posthog/ui/workbench/queryClient) +setGitCacheKeyProvider(@posthog/ui/features/git-interaction/gitCacheProvider) + DI bindingGIT_QUERY_CLIENT->TrpcGitQueryClient, all wired indesktop-services.ts - Data: git read data source of truth is the host git router (forwards to workspace-server); cache keys are host-supplied (the real tRPC keys) so packages/ui invalidation stays byte-coherent with the host's read queries
- Cleaned: git read hooks + cache invalidation no longer import
@renderer/trpc/@utils/queryClient; they go throughGIT_QUERY_CLIENT(data) + the host-set key/queryClient providers - Bridge: apps shims at the old
utils/gitCacheKeys.ts+hooks/useGitQueries.tspaths re-export from@posthog/ui(≈14 consumers unchanged); git result types (GitSyncStatus/GitRepoInfo/etc.) are declared in uiports.tsuntil git-domain-types-to-shared relocates them. Git WRITE ops + createPr-progress subscription + components still in apps. - Validation:
@posthog/uitypecheck 0 + vitest 58 files/612 tests;pnpm --filter codetypecheck 0; useBranchMismatchDialog/BranchSelector/ReviewShell tests green
- Moved:
apps/code/src/renderer/stores/navigationStore.ts->packages/ui/src/features/navigation/store.ts(+taskBinder.ts,store.test.ts) - Registered:
setNavigationTaskBinder(NavigationTaskBinder port) +setActiveTaskContextHandler(analytics) wired inapps/code/src/renderer/desktop-services.ts/utils/analytics.ts - Data: source of truth is the navigation store's
view+history;canGoBack/canGoForwardare derived - Cleaned: removed store-owned multi-step flow + cross-store reach-in from
navigateToTask(workspace/folder auto-registration now a host adapter behindNavigationTaskBinder) - Bridge:
apps/code/src/renderer/stores/navigationStore.tsre-export shim remains (33 consumers);platform-adapters/navigation-task-binder.tsholds host orchestration until it moves to a main/core service emitting events - Validation:
@posthog/uitypecheck 0 + 639 ui tests (navigation 16/16);codetypecheck 0; biome clean. Live Electron smoke pending.
- Moved:
apps/code/src/renderer/features/code-editor/hooks/useCodeMirror.ts->packages/ui/src/features/code-editor/hooks/useCodeMirror.ts - Moved:
apps/code/src/renderer/features/code-editor/components/CodeMirrorEditor.tsx->packages/ui/src/features/code-editor/components/CodeMirrorEditor.tsx - Registered: consumes existing
FILE_CONTEXT_MENU_CLIENT+WORKSPACE_CLIENT(no new tokens; both already bound in desktop-services.ts) - Cleaned: useCodeMirror dropped trpcClient/workspaceApi/handleExternalAppAction direct imports (host-agnostic via useService); CodeMirrorEditor SerializedEnrichment now from @posthog/shared (was @posthog/enricher, layer violation)
- Bridge: none (apps CodeEditorPanel repointed to @posthog/ui; no shim left)
- Validation: pnpm typecheck 19/19; biome lint 0 noRestrictedImports on packages/ui/src/features/code-editor
- Moved:
apps/code/src/renderer/features/git-interaction/hooks/useGitInteraction.ts->packages/ui/src/features/git-interaction/useGitInteraction.ts - Moved:
.../hooks/usePrActions.ts->packages/ui/src/features/git-interaction/usePrActions.ts - Moved:
.../utils/{updateGitCache,branchCreation,getSuggestedBranchName}.ts(+branchCreation.test) ->packages/ui/src/features/git-interaction/utils/ - Registered:
GIT_WRITE_CLIENT(packages/ui/.../git-interaction/ports.ts) bound toTrpcGitWriteClient(apps/code/.../platform-adapters/git-write-client.ts) in desktop-services; addedWorkspaceClient.linkBranch - Data: source of truth is the host git service (workspace-server); write mutations return
GitStateSnapshotprojections that update the read caches via the host-registeredgitQueryKeyprovider (coherent by construction) - Cleaned: removed trpcClient/electron/auth-service-locator coupling from the orchestration hub; os.openExternal -> openExternalUrl port, auth -> useOptionalAuthenticatedClient, workspace.linkBranch -> WORKSPACE_CLIENT
- Bridge: apps re-export shims at all old hook/util paths (12 consumers) remain until those consumers import from @posthog/ui directly; branchCreation shim supplies the writeClient via container.get at the app boundary
- Validation:
pnpm typecheck19/19;@posthog/ui639/639; apps BranchSelector.test 5/5; biome lint/check clean
- Moved:
apps/code/.../task-detail/utils/cloudToolChanges.ts(+test) ->packages/ui/src/features/task-detail/utils/ - Moved:
apps/code/.../task-detail/components/{ActionPanel,ExternalAppsOpener}.tsx->packages/ui/src/features/task-detail/components/ - Cleaned: @shared/types->@posthog/shared/domain-types; @shared/types/session-events->@posthog/shared; handleExternalAppAction/keyboard-shortcuts/useExternalApps/ActionTerminal -> @posthog/ui relative
- Bridge: none (all consumers repointed to @posthog/ui; no shims)
- Validation: pnpm typecheck 19/19; ui cloudToolChanges 15/15; biome 0 noRestrictedImports
- Moved: pure helpers/hooks/types/sub-components out of
apps/.../code-review/components/ReviewShell.tsx-> NEWpackages/ui/src/features/code-review/reviewShellParts.tsx - Kept in apps: the
ReviewShellcomponent (host-only ChangesPanel + pierre Vite worker + virtua) - Bridge: apps
ReviewShell.tsxexport *-re-exports the parts (retire when ReviewPage/CloudReviewPage/cluster import from @posthog/ui directly) - Validation: pnpm typecheck 19/19; ui code-review 27/27; ReviewShell.test 4/4; biome 0 noRestrictedImports
- Moved:
useCloudEventSummary/useCloudRunState/useCloudChangedFiles->packages/ui/src/features/task-detail/hooks/;useTaskDiffSummaryStats->packages/ui/src/features/code-review/hooks/ - Split: tasks READ hooks (
useTasks/useTaskSummaries/useSlackTasks) ->packages/ui/src/features/tasks/useTasks.ts; mutation hooks remain inapps/code(host-coupled) - Data: tasks list = api-client read (useAuthenticatedQuery); cloud changed-files derived from session events + PR/branch git queries
- Bridge: apps re-export shims at all moved hook paths; tasks mutations +
getSessionService.updateSessionTaskTitlecoupling remain until a sessions-title-sync port lands - Validation: ui+code typecheck 0; ui tests 113/113; biome clean
- Moved:
apps/code/.../code-editor/components/CodeEditorPanel.tsx+.../hooks/useCloudFileContent.ts->packages/ui/src/features/code-editor/ - Registered: NEW
FILE_CONTENT_CLIENT(packages/ui/.../code-editor/ports.ts: readRepoFile/readAbsoluteFile/readFileAsBase64) bound toTrpcFileContentClient(apps/code/.../platform-adapters/file-content-client.ts) in desktop-services - Added:
packages/ui/.../code-editor/hooks/useFileContent.ts(useRepoFileContent/useAbsoluteFileContent/useFileAsBase64) — useService(FILE_CONTENT_CLIENT) + useQuery keyed via the host-registeredfsQueryKeyprovider, so keys stay byte-coherent with the host's other fs reads - Data: source of truth is workspace-server fs (file contents); panel is read-only, cloud reads derive from session tool-call events (useCloudFileContent)
- Cleaned: dropped
useTRPC/trpcClient.os.openExternalfrom the panel (fs.* -> port hooks; openExternal ->openExternalUrl);@features/*+@shared/types-> relative/@posthog/shared - Drained
editorfeature too: repointeduseTaskCreation(buildCloudTaskDescription) +sagas/task/task-creation(buildPromptBlocks) ->@posthog/ui/features/editorand deleted the re-export shims.apps/code/.../features/{code-editor,editor}are now empty. - Bridge: none (sole panel consumer TabContentRenderer repointed directly; no shims left)
- Validation:
pnpm typecheck19/19;@posthog/ui706/706; biome lint 0 noRestrictedImports on code-editor. Live Electron GUI smoke deferred (shared-tree WIP).
- Moved:
apps/code/src/renderer/assets/images/hedgehogs/{builder-hog-03,explorer-hog,happy-hog}.png->packages/ui/src/assets/hedgehogs/(+ newpackages/ui/src/assets/hedgehogs.tsURL manifest) - Moved:
apps/code/src/renderer/assets/logo.tsx->packages/ui/src/primitives/Logo.tsx(pure SVG, zero deps) - Moved:
WelcomeScreen.tsx->packages/ui/src/features/onboarding/components/;createFirstTaskTour.ts->packages/ui/src/features/tour/tours/ - Data: assets are static URLs; manifest re-exports them by name (cross-package raw
.pngimport is not resolvable via the@posthog/uiexports map, a.tsmanifest is — mirrors the sounds-asset precedent) - Cleaned: 14 hedgehog import sites + Logo + 3 moved-file consumers repointed to
@posthog/ui; no shims left - Bridge: none
- Validation:
pnpm typecheck19/19;@posthog/uivitest 67 files / 706 tests; biome check clean. Live Electron smoke deferred (shared-tree WIP). - Remaining: onboarding/setup components still gated on auth/integrations/projects/folder-picker/analytics(
track) ports (GitHubConnectPanel is the keystone).
- Moved:
apps/code/src/renderer/components/SpaceSwitcher.tsx->packages/ui/src/workbench/SpaceSwitcher.tsx - Cleaned: deps repointed to
@posthog/ui/@posthog/shared; sole consumer MainLayout repointed; no shim - Validation:
@posthog/uitypecheck 0 + 706 tests; biome clean
- Moved:
apps/code/.../git-interaction/hooks/useFixWithAgent.ts->packages/ui/src/features/git-interaction/useFixWithAgent.ts - Moved:
apps/code/.../git-interaction/components/CreatePrDialog.tsx->packages/ui/src/features/git-interaction/components/CreatePrDialog.tsx - Cleaned: useFixWithAgent now consumes ui paths only (useSession/sendPromptToAgent/navigation store/errorPrompts); CreatePrDialog's last app-local dep (GitInteractionDialogs shim) is now a relative import; self-imports relativized
- Bridge: none. Consumers repointed directly — CreatePrDialog's
useFixWithAgentimport, TaskActionsMenu + CreatePrDialog.stories (stories stay in apps/code; storybook is app-only) now import CreatePrDialog from@posthog/ui - Gated: useCloudPrUrl/useTaskPrUrl/TaskActionsMenu chain blocked on tasks reconciliation (apps useTasks vs ui useTasks are distinct impls with different query keys); useTaskPrUrl additionally needs trpc.git.getPrStatus -> GIT_QUERY_CLIENT.getPrStatus + gitQueryKey
- Validation:
pnpm typecheck19/19;@posthog/uigit-interaction 6 files/71 tests; biome lint 0 noRestrictedImports
- Moved: inbox
{utils/ReportImplementationPrLink, utils/ReportCardContent, detail/MultiSelectStack, list/ReportListRow, list/ReportListPane}->packages/ui/src/features/inbox/components/* - Cleaned:
usePrDetails->ui git-interaction;SignalReport->@posthog/shared/domain-types; apps consumers (ReportDetailPane, InboxSignalsTab) repointed; no shims - Validation:
@posthog/uitypecheck 0 + 710 tests; biome clean
- Moved:
apps/code/.../code-review/components/{ReviewShell,ReviewPage,CloudReviewPage}.tsx+hooks/useDiffStatsToggle.ts->packages/ui/src/features/code-review/(apps/code/features/code-review now all shims + one host-bindings file) - Registered:
reviewHost.ts(setReviewDiffWorkerFactory / setReviewExpandedSidebarRenderer); wired by appsreviewHostBindings.tsx(side-effect import inmain.tsx) - Data: untracked-file prefetch source of truth is the host fs via
REVIEW_FILE_CLIENT(new batchreadRepoFilesBounded); cache keys derived from host-setfsQueryKeyso prefetch stays coherent withuseReadRepoFileBounded - Cleaned: ReviewShell no longer imports task-detail ChangesPanel or the Vite worker URL directly — both injected by the host
- Bridge:
apps/.../code-review/reviewHostBindings.tsxsupplies the pierre worker (host/bundler) + ChangesPanel sidebar slot; sidebar half retires when task-detail's ChangesPanel lands inpackages/ui. Component/hook shims retire when consumers import@posthog/uidirectly. - Validation:
pnpm typecheck19/19; ui code-review vitest 710 pass; biome clean. Live review-pane smoke pending (no headless Electron).
- Moved: sessions
{PlanStatusBar, ContextUsageIndicator, ContextBreakdownPopover(+test), utils/contextColors}->packages/ui/src/features/sessions/* - Cleaned: contextColors ->
@posthog/ui/features/sessions/contextColors; consumers SessionView/SessionFooter/PlanStatusBar.stories repointed; no shims - Validation:
@posthog/uitypecheck 0 + moved test 3/3 + 719 ui tests; biome clean
- Moved:
useCloudPrUrl.ts,useTaskPrUrl.ts,components/TaskActionsMenu.tsx,components/BranchSelector.tsx(+test) ->packages/ui/src/features/git-interaction/ - Registered: added
checkoutBranchtoGIT_WRITE_CLIENTport +TrpcGitWriteClientadapter - Cleaned: useTaskPrUrl + BranchSelector dropped
useTRPC; git reads now go throughuseService(GIT_QUERY_CLIENT)+gitQueryKeyprovider, branch checkout throughuseService(GIT_WRITE_CLIENT)(cache coherence preserved via the host-registered key provider) - Data: corrected a false gate — apps
useTasksalready re-exports the ui read hooks, so the PR-url chain was never tasks-divergent - Bridge: apps shims at
useCloudPrUrl/useTaskPrUrl(CommandCenter consumers); TaskActionsMenu/BranchSelector consumers (HeaderRow/TaskInput) repointed directly, no shim - Remaining:
CloudGitInteractionHeader(sessions-gated) is the only real app-side file left in the feature - Validation:
pnpm typecheck19/19;@posthog/uigit-interaction 7 files/76 tests; biome lint 0 noRestrictedImports
- Moved:
InviteCodeStep.tsx,SelectRepoStep.tsx,hooks/useProjectsWithIntegrations.ts->packages/ui/src/features/onboarding/ - Data: extracted
DetectedRepointerface ->packages/ui/src/features/onboarding/types.ts(appsuseOnboardingFlowre-exports it; unblocks SelectRepoStep without porting the still-trpc-coupled hook) - Bridge: apps shims for the 3 moved files (consumers OnboardingFlow + GitHubConnectPanel unchanged); retire when those consumers land in ui
- Validation:
@posthog/ui typecheck0; biome clean
- Moved:
apps/code/src/renderer/features/sidebar/components/TaskListView.tsx->packages/ui/src/features/sidebar/components/TaskListView.tsx - Moved:
apps/code/src/renderer/features/sidebar/components/Sidebar.tsx->packages/ui/src/features/sidebar/components/Sidebar.tsx - Data: source of truth is
useSidebarData(already in ui); TaskListView is fully props-driven projection - Cleaned: TaskListView imports repointed to package paths (useFolders/useWorkspace/useMeQuery/navigation + @posthog/shared utils); Sidebar uses
@posthog/ui/primitives/ResizableSidebar - Bridge: apps
features/sidebar/components/index.tsxbarrel re-exportsSidebarfrom ui; SidebarMenu imports TaskListView from ui directly (no shim) - Validation:
pnpm --filter @posthog/ui typecheck,pnpm --filter code typecheck, ui sidebar vitest 41/41, biome clean
- Moved:
apps/code/src/renderer/features/setup/components/DiscoveredTaskDetailDialog.tsx->packages/ui/src/features/setup/DiscoveredTaskDetailDialog.tsx - Moved:
apps/code/src/renderer/features/setup/hooks/useSetupDiscovery.ts->packages/ui/src/features/setup/useSetupDiscovery.ts - Registered:
setupUiModule(packages/ui/src/features/setup/setup.module.ts, bindsSetupRunServicesingleton) loaded indesktop-contributions.ts - Cleaned:
useSetupDiscoverynow resolvesSetupRunServiceviauseServiceinstead of renderer-containerget(RENDERER_TOKENS.SetupRunService); removed the deadRENDERER_TOKENS.SetupRunServicetoken +di/container.tsbinding - Bridge: app shims at
features/setup/components/DiscoveredTaskDetailDialog.tsx(consumer task-detail/SuggestedTasksPanel) andfeatures/setup/hooks/useSetupDiscovery.ts(consumer MainLayout) — retire when those consumers move to packages - Validation:
pnpm typecheck19/19; ui setup vitest 14/14; biome lint clean
- Moved: clone subscription + auto-dismiss timer out of
packages/ui/.../clone/cloneStore.tsintoclone.contribution.ts(bootWORKBENCH_CONTRIBUTION);startCloneorchestration ->clone/cloneActions.ts - Registered:
cloneUiModule(clone.module.ts) inapps/code/src/renderer/desktop-contributions.ts - Data: source of truth is the host clone lifecycle (main git service
CloneProgressevents);cloneStore.operationsis a pure projection;isCloning/getCloneForRepoare derived - Cleaned: removed store-owned module subscription, domain-cleanup
setTimeout, and in-store orchestration (3 AGENTS.md forbidden patterns) - Note:
startClonecurrently has no callers (clone-progress feature is dead) — patterns removed + capability preserved, not deleted - Validation:
@posthog/ui typecheck0;cloneStore.test7/7; biome clean
- Moved:
apps/.../features/auth/hooks/useOrgRole.ts->packages/ui/src/features/auth/useOrgRole.ts - Moved:
apps/.../features/integrations/hooks/useGitHubIntegrationCallback.ts+useGithubUserConnect.ts->packages/ui/src/features/integrations/ - Moved:
apps/.../features/onboarding/components/GitHubConnectPanel.tsx+ConnectGitHubStep.tsx->packages/ui/src/features/onboarding/components/ - Registered:
GITHUB_INTEGRATION_CLIENTport (packages/ui/src/features/integrations/ports.ts) + desktop adapterplatform-adapters/github-integration-client.tsbound indesktop-services.ts - Data: source of truth is the host GitHub integration service (callbacks/pending-callback/flow events); ui consumes via the port + RQ cache invalidation
- Cleaned: subscriptions go through
client.onCallback/onFlowTimedOut(useService) instead ofuseTRPC().githubIntegration.*;trpc.os.openExternal->openExternalUrl;IS_DEV->import.meta.env.DEV - Bridge: apps shims for
useOrgRole+useGithubUserConnectre-export from ui (consumers in App/settings/inbox/task-detail unchanged); retire when those features land - Validation: ui typecheck 0; full ui vitest 736/736; biome clean
- Moved:
apps/code/src/renderer/features/billing/utils.ts(+test) ->packages/ui/src/features/billing/utils.ts - Cleaned:
UsageOutputtype import moved from@main/services/llm-gateway/schemas->@posthog/core/llm-gateway/schemas(removes a main->renderer cross-process type coupling; ui->core is an allowed edge) - Bridge: app shim at
features/billing/utils.ts— retire when SidebarUsageBar/UsageLimitModal/billing subscriptions/PlanUsageSettings move to packages - Validation: ui typecheck 0; ui billing utils vitest 11/11; biome clean
- Moved:
apps/code/src/renderer/features/inbox/components/{InboxEmptyStates,SignalSourceToggles}.tsx,components/detail/SignalCard.tsx,components/list/{SuggestedReviewerFilterMenu,SignalsToolbar}.tsx,hooks/{useInboxBulkActions,useSignalSourceManager}.ts->packages/ui/src/features/inbox/...;components/ui/RelativeTimestamp.tsx->packages/ui/src/primitives/;assets/images/mail-hog.png->packages/ui/src/assets/images/ - Data: inbox report truth owned by api-client query cache key
["inbox","signal-reports"](ui read hooks); useInboxBulkActions invalidates the same key — single source preserved across the move - Cleaned: dropped false auth coupling (all auth read-path already in
@posthog/ui/features/auth);@renderer/api/posthogClient(shim) repointed to@posthog/api-client/posthog-client - Bridge: apps shims at
features/inbox/components/SignalSourceToggles.tsx,features/inbox/hooks/{useInboxBulkActions,useSignalSourceManager}.ts,components/ui/RelativeTimestamp.tsxremain until settings (SignalSourcesSettings/SignalSlackNotificationsSettings) consumers import from@posthog/uidirectly - Validation:
pnpm --filter @posthog/ui typecheck(0); ui inbox vitest 76 tests; biome clean;pnpm --filter code typecheck(0 in inbox paths)
- Moved:
buildCloudDefaultConfigOptions/extractLatestConfigOptionsFromEntries->packages/ui/.../sessions/cloudSessionConfig.ts;hasSessionPromptEvent/isAbsoluteFolderPath/promptReferencesAbsoluteFolder->packages/ui/.../sessions/session.ts - Cleaned: renderer
service/service.tsno longer defines these; imports them from ui; dropped unused@posthog/agent/execution-modeimport - Bridge:
isTurnCompleteEventstays inservice/service.ts(needs@posthog/agentroot barrel, forbidden in ui; no browser-safe acp-extensions subpath) - Validation: ui typecheck 0; ui tests 41/41; apps/code typecheck 0; biome 0 noRestrictedImports
- Moved:
apps/code/src/renderer/components/TreeDirectoryRow.tsx->packages/ui/src/primitives/;features/task-detail/components/{CloudGithubMissingNotice,ChangesTreeView}.tsx->packages/ui/src/features/task-detail/components/ - Cleaned: dropped false auth/integrations coupling (auth read-path + github-connect already in
@posthog/ui);@components/TreeDirectoryRow->@posthog/ui/primitives/TreeDirectoryRow,@shared/types->@posthog/shared/domain-types - Bridge: apps shim
components/TreeDirectoryRow.tsxremains until ChangesPanel/FileTreePanel move to packages/ui - Validation:
@posthog/uitypecheck (0);codetypecheck (0); ui task-detail vitest 20 tests; biome clean
- Added:
@posthog/agent/acp-extensionsbrowser-safe subpath export (pure ACP notification consts +isNotification); tsup entry + package.json exports, dist rebuilt - Moved:
isTurnCompleteEvent->packages/ui/.../sessions/session.ts;cloudRunIdleTracker.ts->packages/ui/.../sessions/(git mv, +test 9/9) - Cleaned: renderer
service/service.tsimports both from@posthog/ui; agent-root barrel no longer needed for these - Enabler: ui code may now import
isNotification/POSTHOG_NOTIFICATIONSfrom@posthog/agent/acp-extensionsinstead of the forbidden root barrel - Validation: agent build OK; ui session 29/29 + cloudRunIdleTracker 9/9; service typecheck clean; biome clean
- Moved:
apps/.../features/onboarding/components/InstallCliStep.tsx->packages/ui/src/features/onboarding/components/ - Registered:
getGitStatusadded toGIT_QUERY_CLIENTport +git-query-client.tsadapter - Cleaned: InstallCliStep off
useTRPC->useService(GIT_QUERY_CLIENT)+gitQueryKey/gitPathFilterfor cache-coherent reads/invalidation;trpc.os.openExternal->openExternalUrl - Bridge: none (OnboardingFlow repointed; sole consumer)
- Validation: ui git-interaction vitest 76/76; ui+apps typecheck clean in touched paths; biome clean
- Moved:
apps/code/.../features/billing/hooks/{useUsage,useFreeUsage}.ts->packages/ui/src/features/billing/ - Registered:
UsageClientport (packages/ui/src/features/billing/usageClient.ts) + desktop adapterRendererUsageClient(platform-adapters/usage-client.ts), wired viasetUsageClientin desktop-services - Data:
useUsageowns the usageMonitor.getLatest cache solely -> ui-owned query key["billing","usage","latest"](no host key provider needed); onUsageUpdated subscription writes that key - Cleaned: removed renderer trpc coupling (
useTRPC/useSubscription) from the usage read path;UsageOutputfrom@posthog/core/usage/schemas - Bridge: app shims at both hook paths — retire when PlanUsageSettings + SidebarUsageBar move to packages
- Validation: pnpm typecheck 19/19; ui billing vitest 53/53; biome clean
- Moved:
apps/code/src/renderer/features/sidebar/components/ProjectSwitcher.tsx->packages/ui/src/features/sidebar/components/ - Cleaned: replaced
trpcClient.os.openExternalwith theopenExternalUrlplatform port; dropped false auth/projects/command coupling (all already in@posthog/ui) - Bridge: none (sole consumer SidebarContent repointed directly)
- Validation:
@posthog/uitypecheck (0);codetypecheck (0); ui sidebar vitest 41 tests; biome clean
- Moved: feature-flag constants ->
packages/shared/src/flags.ts;SidebarUsageBar.tsx->packages/ui/src/features/billing/ - Cleaned: flag strings now host-agnostic in @posthog/shared;
apps/code/src/shared/constants.tsre-exports them (additive shim); SidebarUsageBar fully on ui/shared imports - Bridge: app shim at
features/billing/components/SidebarUsageBar.tsx— retire when SidebarContent moves - Validation: shared+ui+code typecheck 0 in touched paths; ui billing vitest 53/53; biome clean
- Moved:
apps/.../hooks/useNewTaskDeepLink.ts->packages/ui/src/features/deep-links/useNewTaskDeepLink.ts - Registered: new
DEEP_LINK_CLIENTport (features/deep-links/ports.ts) +deep-link-client.tsadapter bound indesktop-services.ts;getGithubIssueadded toGIT_QUERY_CLIENTport + adapter - Cleaned: tRPC
useSubscription->useEffect+client.onNewTaskAction;trpcClient.deepLink/git->useServiceports - Bridge: apps shim
@hooks/useNewTaskDeepLinkre-exports from ui (MainLayout unchanged) - Validation: ui git-interaction vitest 76/76; ui+apps typecheck clean in touched paths; biome clean
- Moved: reconcile decision (
classifyCloudLogGap) + request coalescing (mergeCloudLogGapRequests) ->packages/ui/.../sessions/cloudLogGap.ts - Cleaned:
service.tsreconcileCloudLogGapOnce now delegates the decision to the pure module and shares onecommitReconciledCloudEventswrite path; removed 3 local interfaces + the merge method - Validation: cloudLogGap 9/9; existing service reconcile tests pass (behavior preserved); typecheck 0 in touched paths; biome clean
- Moved: App.tsx inline
registerBillingSubscriptions->packages/ui/src/features/billing/billing.contribution.ts(BillingContribution); registered viabilling.module.ts(WORKBENCH_CONTRIBUTION) loaded in desktop-contributions; deleted appsbilling/subscriptions.ts - Moved:
UsageLimitModal.tsx-> ui (os.openExternal -> openExternalUrl port) - Registered:
onThresholdCrossedadded to UsageClient port + RendererUsageClient adapter - Cleaned: App.tsx no longer registers the billing subscription inline (ui-shell acceptance #1)
- Validation: pnpm typecheck 19/19; ui billing vitest 53/53; biome clean
- Moved:
initializeUpdateStore->UpdatesContribution(updates.module.ts);initializeConnectivityStore+initializeConnectivityToast->ConnectivityContribution(connectivity.module.ts); both WORKBENCH_CONTRIBUTION, loaded in desktop-contributions - Cleaned: App.tsx no longer registers update/connectivity init inline (acceptance #1)
- Validation: ui+code typecheck 0 (my paths); updates+connectivity vitest 7/7; biome clean
- Moved:
ProjectSelectStep.tsx->packages/ui/.../onboarding/components(imports repointed to ui/shared; apps shim left) - Added:
useAuthStateFetched()to@posthog/ui/features/auth/store - Note:
OnboardingFlowstays — host-coupled viaFullScreenLayout+UpdateBanner+IS_DEV(no shared subpath); needs a banner-slot decision - Validation: ui+app typecheck 0 in touched paths; biome 0 noRestrictedImports
- Attempted HedgehogMode.tsx -> packages/ui/workbench; reverted: ui biome noRestrictedImports forbids
@posthog/hedgehog-mode(DOM/canvas lib, "ui must run in any JS environment"). Needs a host-injected game factory port to port; stays app-local for now.
- Added:
PANEL_CONTEXT_MENU_CLIENTplatform-style port (packages/ui/src/features/panels/panelContextMenuClient.ts) +TrpcPanelContextMenuClientadapter (apps/code/.../platform-adapters/panel-context-menu-client.ts), bound indesktop-services.ts - Moved:
DraggableTab,PanelTab,TabbedPanel->packages/ui/src/features/panels/components/ - Cleaned: replaced direct
trpcClient.contextMenu.show{Tab,Split}ContextMenu+workspaceApi/handleExternalAppActionwith the port (adapter handles external-app host-side, returns close-family choice) - Bridge: none for these components (sole consumer
LeafNodeRendererrepointed) - Validation:
@posthog/uitypecheck (0); ui panels vitest 42 tests;codetypecheck (0 in panels paths); biome clean
- Moved:
CloudInitializingView,DiffStatsChip,SessionFooter,GitActionResult,UnifiedModelSelector(apps sessions/components) ->packages/ui/src/features/sessions/components/;zen.png->packages/ui/src/assets/images/ - Cleaned: GitActionResult off
useTRPC->useService(GIT_QUERY_CLIENT)+gitQueryKey;trpc.os.openExternal->openExternalUrl - Deduped:
VirtualizedList(apps dead twin/shim removed; 3 consumers repointed direct to ui) - Bridge: none (all consumers repointed)
- Validation: ui sessions vitest 78/78; ui+apps typecheck clean in touched paths; biome clean
- Moved:
HedgehogMode.tsx->packages/ui/src/workbench/; newHedgehogModeHostport + desktopRendererHedgehogModeHostadapter (owns@posthog/hedgehog-mode), wired viasetHedgehogModeHostin desktop-services - Cleaned: ui no longer references the DOM/canvas hedgehog lib (noRestrictedImports honored); game details live in the adapter, state decision in ui
- Bridge: app shim at
components/HedgehogMode.tsx— retire when MainLayout moves - Validation: ui+code typecheck 0; ui biome lint clean
- Moved:
OnboardingFlow.tsx->packages/ui/src/features/onboarding/components/(steps + hooks + store already in ui) - Cleaned: dropped
@components/FullScreenLayout/@features/auth/@hooks/@stores/@utilscouplings (all ui);IS_DEVinlined asimport.meta.env.DEV; deleted 4 dead apps shims - Bridge:
OnboardingHogTip.tsxremains in apps only because authInviteCodeScreenstill consumes it (retire with the auth slice) - Validation:
@posthog/uitypecheck (0);codetypecheck (0 in onboarding/App; 14 exogenous tasks/useTasks errors); biome clean
- Moved:
SkillButtonsMenu.tsx->packages/ui/src/features/skill-buttons/components/(deps all ui/shared; sendPromptToAgent via existing agentPromptSender port) - Bridge: app shim at old path (consumers HeaderRow + stories) — retire when HeaderRow moves
- Validation: ui+code typecheck 0; ui skill-buttons vitest 6/6; biome clean
- Added:
SESSION_TASK_BRIDGEport (@posthog/ui/features/sessions/sessionTaskBridge.ts) + apps adapter (sessionTaskBridgeAdapter.ts, wired inmain.tsx) - Moved:
useUpdateTask+useRenameTask->@posthog/ui/features/tasks/useTaskMutations.ts(coupling togetSessionService().updateSessionTaskTitlenow via the bridge); test moved + repointed - Bridge: apps
useTasks.tsre-exports both from ui; retire when all consumers import the package directly - Data: source of truth is the renderer SessionService (host); the bridge is a narrow injected port
- Validation: ui+app typecheck 0 in touched paths; useTaskMutations.test 4/4; biome clean
- Moved:
useAppBridge.ts->packages/ui/src/features/mcp-apps/hooks/(McpUiResource type from @posthog/core/mcp-apps/schemas; ext-apps already a ui dep) - Bridge: app shim at old path (consumer McpAppHost) — retire when McpAppHost moves
- Validation: ui+code typecheck 0; ui mcp-apps vitest green; biome clean
- Moved:
SkillsView.tsx+SkillDetailPanel.tsx->packages/ui/src/features/skills/(SkillCard + skillsSidebarStore already ui) - Registered:
SKILLS_CLIENTport (@posthog/ui/features/skills/ports.ts) +useSkills()hook; desktop adapterRendererSkillsClient(platform-adapters/skills-client.ts) bound indesktop-services.ts - Data: source of truth is ws-server
SkillsService(skills.list); SkillInfo/SkillSource neutral types in@posthog/shared. SKILL.md body read reusesFILE_CONTENT_CLIENT(useAbsoluteFileContent) for fs-cache coherence; frontmatter stripped client-side - Cleaned: removed
@renderer/trpc/useTRPCfrom the skills UI; skills feature now host-agnostic in ui - Bridge: app shims at
features/skills/components/SkillsView(consumer MainLayout) + SkillCard + skillsSidebarStore — retire when MainLayout/consumers import the package directly - Validation: ui typecheck 0; useSkills.test 1/1; biome lint 0 noRestrictedImports. Live GUI smoke deferred (exogenous tree red from concurrent handoff/archive agents)
- Moved:
apps/code/src/renderer/features/tasks/hooks/useArchiveTask.ts->packages/ui/src/features/archive/useArchiveTask.ts(old path is a re-export shim) - Registered:
ArchiveTaskBridge(packages/ui archive) + host implplatform-adapters/archive-task-bridge.ts, side-effect imported inmain.tsx; extendedarchiveCacheProviderwith list + pathFilter keys - Data: source of truth is ws-server archive service; ui holds optimistic cache writes.
ArchivedTaskdomain type added to@posthog/shared(ws-server zod schema stays the boundary validator) - Cleaned: removed the last
@renderer/*couplings from the archive flow (workspaceApi/pinnedTasksApi/trpcClient.archive now behind the bridge) - Bridge:
apps/code/.../platform-adapters/archive-task-bridge.ts+ appsuseArchiveTask.tsshim remain until SidebarMenu/useTaskContextMenu import the package path and useDeleteTask moves behind ports - Validation: pnpm typecheck 19/19; ui useArchiveTask.test.ts 2/2; renderer vite build
- Moved:
apps/code/src/main/services/handoff/handoff-saga.ts+handoff-to-cloud-saga.ts->packages/core/src/handoff/(+types.tsowningHandoffStep/HandoffBaseDeps/saga input types) - Cleaned: core imports only
@posthog/shared— agent runtime (resumeFromLog/formatConversationForResume) and theapiClientcalls are injected viaHandoffSagaDeps; checkpoint typed as sharedGitHandoffCheckpoint(no generics);apiClientremoved from the saga - Data: source of truth is the cloud run log (rebuilt via injected
fetchResumeState); derived projections are the handoff context summary + checkpointApplied flag - Bridge:
HandoffService(apps/code) stays as the saga deps-provider (focus pattern), supplying agent/git/fs host ops; retire its raw fs/git when a workspace-server handoff-host capability lands - Validation:
@posthog/coretypecheck + 16/16 core saga tests; apps main tsc 0 errors; apps handoff service test 6/6; biome lint core clean
- Moved: 7 settings sections
apps/code/src/renderer/features/settings/components/sections/{PermissionsSettings,ClaudeCodeSettings,AdvancedSettings,ShortcutsSettings,AccountSettings,GitHubSettings,GitHubIntegrationSection}.tsx->packages/ui/src/features/settings/sections/(old paths areexport *re-export shims) - Registered: new
SETTINGS_PERMISSIONS_PORT(packages/ui/.../settings/ports.ts) + desktop adapterapps/code/.../platform-adapters/settings-permissions-client.tswrappingtrpc.os.getClaudePermissions, bound indesktop-services.ts - Data: Permissions reads allow/deny tool lists from the host (source of truth = host Claude settings.json) via the port; other sections consume already-ported ui stores/hooks (settingsStore, auth store/useCurrentUser/useAuthMutations, billing useSeat, integrations useGithubUserConnect/useIntegrations)
- Cleaned: Account/GitHub/GitHubIntegration were FALSE BLOCKERS — their auth/integrations deps already lived in @posthog/ui + @posthog/api-client + @posthog/shared; only import paths changed
- Bridge: app
export *shims at all 7 sections/* paths remain until SettingsDialog imports the package paths directly (SettingsDialog still apps-side, gated on the remaining inbox/billing/folders/tasks-coupled sections) - Validation: ui+apps typecheck 0;
vite build -c vite.renderer.config.mts✓ (runtime bundle, validates the new port binding); ui settings vitest 11/11; biome clean
- Moved:
useCreateTask/useDeleteTaskfrom appsfeatures/tasks/hooks/useTasks.ts->packages/ui/src/features/tasks/useTaskCrudMutations.ts(apps useTasks.ts is now a pure re-export shim for all 5 task hooks) - Registered:
TaskMutationBridge(packages/ui tasks) + host implplatform-adapters/task-mutation-bridge.ts, side-effect imported inmain.tsx - Data: source of truth is the PostHog API task CRUD; ui holds the optimistic task-list cache (taskKeys)
- Cleaned: removed the last
@renderer/*couplings (workspaceApi.get/delete, contextMenu.confirmDeleteTask, pinnedTasksApi.unpin) from the task CRUD hooks - Milestone: the entire
apps/.../features/tasks/hooks/layer is now @posthog/ui shims — the tasks-mutation-hooks keystone (cited as the blocker for sidebar/inbox/task-detail/command) is retired - Bridge: apps task-mutation-bridge.ts + useTasks.ts shim remain until consumers import package paths directly
- Validation: pnpm typecheck 19/19; ui useTaskCrudMutations.test.tsx 2/2; renderer vite build
- Moved:
useSuspendTask/useRestoreTaskappsfeatures/suspension/hooks->packages/ui/src/features/suspension(apps paths are re-export shims) - Registered: extended
SUSPENSION_CLIENT(suspend/restore) + newSuspensionCacheKeyProvider(host adaptersuspension-cache-keys.ts, wired in desktop-services) - Data: source of truth is ws-server suspension service; ui holds the optimistic suspended-id set + drives git working-tree/branch cache invalidation
- Cleaned: removed
@renderer/trpc+workspaceApi+ apps gitCacheKeys couplings from the suspension write hooks - Bridge: apps suspension hook shims remain until consumers (TaskLogsPanel, useTaskContextMenu) import the package paths directly
- Validation: pnpm typecheck 19/19; ui useSuspendTask.test.tsx 2/2; renderer vite build
- Moved:
apps/code/.../sidebar/components/{SidebarMenu,SidebarContent,MainSidebar}.tsx+apps/code/.../hooks/useTaskContextMenu.ts->packages/ui/src/features/{sidebar/components,tasks}/(old paths are re-export shims) - Registered:
TASK_CONTEXT_MENU_CLIENT(packages/ui/.../tasks/taskContextMenuClient.ts) + desktop adapterapps/.../platform-adapters/task-context-menu-client.tswrappingtrpcClient.contextMenu.show{Task,BulkTask}ContextMenu, bound indesktop-services.ts. AddedBulkTaskContextMenuResultexport to@posthog/core/context-menu/schemas - Data: the native context menu is host transport (port returns the chosen action); the ui
useTaskContextMenuorchestrates the business actions (rename/pin/suspend/archive/delete) via the already-ported ui task/suspension/archive hooks; workspace lookup viaWORKSPACE_CLIENT.getAll; external-app via uihandleExternalAppAction - Cleaned: deleted dead app suspension duplicates
useSuspendTask.ts/useRestoreTask.ts(byte-equivalent to the ui versions behind WORKSPACE_CLIENT+SUSPENSION_CLIENT); repointedTaskLogsPanelto@posthog/ui/features/suspension - Bridge: app
export *shims at the 4 ported paths remain until SidebarContent/MainSidebar consumers + command-center import package paths directly - Validation: @posthog/ui + @posthog/core typecheck 0 (my files); ui sidebar+suspension+tasks vitest 49/49; biome clean. Live bundle smoke deferred (concurrent environments-settings move left the renderer bundle red — exogenous)
- Moved: workspace mutation hooks (useCreate/Delete/EnsureWorkspace) ->
@posthog/ui/features/workspace/useWorkspaceMutations;useBranchMismatchDialog(+test) ->@posthog/ui/features/workspace - Registered: WORKSPACE_CLIENT port +create/+delete (TrpcWorkspaceClient adapter); NEW host-set worktrees cache-key provider (
workspaceCacheProvider+workspace-cache-keysadapter, wired in desktop-services); branch-mismatch checkout via existing GIT_WRITE_CLIENT - Data: source of truth is ws-server WorkspaceService; WORKSPACE_QUERY_KEY (ui-owned) + listGitWorktrees (host-keyed via provider) invalidated coherently on mutate
- Cleaned: removed @renderer/trpc/useTRPC from the workspace UI; only the imperative
workspaceApi(apps host glue for adapters) stays apps-side - Bridge: apps shims at
features/workspace/hooks/useWorkspace(re-exports ui hooks + workspaceApi) +useBranchMismatchDialog(consumer TaskLogsPanel) — retire when task-detail consumers move - Validation: ui workspace vitest 21/21; ui+apps typecheck 0 workspace-attributable; biome 0 restricted imports; renderer vite build ✓
- Moved: inbox
useDiscussReport/useCreatePrReportapps ->packages/ui/features/inbox/hooks(apps paths re-export shims) - Registered:
TaskServiceBridge(@posthog/ui/features/tasks/taskServiceBridge, createTask/openTask/resolveDefaultModel) + host implplatform-adapters/task-service-bridge.ts(wraps renderer TaskService), wired in main.tsx - Data:
TaskCreationInput/TaskCreationOutputrelocated to@posthog/shared/task-creation-domain(Task = domain-types Task); renderer TaskCreationSaga re-exports them - Cleaned: inbox direct-create hooks no longer depend on the renderer TaskService (keystone #1) — they call the bridge
- Bridge: apps task-service-bridge.ts + inbox hook shims remain until the TaskCreationSaga itself lands in core
- Validation: pnpm typecheck 19/19; ui useDiscussReport.test.tsx 2/2; renderer vite build
- Moved:
apps/code/.../features/sessions/components/{buildConversationItems.ts, mergeConversationItems.ts, session-update/{SessionUpdateView,ToolCallBlock,SubagentToolView}.tsx}+utils/extractSearchableText.ts(~1210L) ->packages/ui/src/features/sessions/(old paths areexport *shims). Colocated tests git-mv'd to ui. - Registered:
mcpToolBlockSlot.ts(set/getMcpToolBlock) in ui; hostapps/.../features/sessions/mcpToolBlockHost.tsregisters the appMcpToolBlockat boot (side-effect import in main.tsx).ToolCallBlockrenders the slot, falling back toToolCallViewwhen unset. - Data: the conversation model (
ConversationItem/RenderItem) and its update-rendering are now host-agnostic ui; the live-agentSessionService(3848L, host connections) is untouched and still owns event ingestion - Cleaned:
@posthog/agentroot import -> browser-safe/acp-extensionssubpath (ui biome rule);@shared/types/session-events->@posthog/shared - Bridge:
McpToolBlockstays in apps (iframe MCP-app host +mcpAppstrpc) behind the slot; appexport *shims remain untilConversationView/SessionViewconsume the package paths directly - Validation: ui+apps typecheck 0 (my files); ui sessions vitest 99/99 (+21 moved); biome clean. Bundle smoke deferred (concurrent task-detail FileTreePanel move left the renderer red — exogenous)
- Moved: settings worktrees subtree (WorktreeSize/Row/GroupSection/WorktreesSettings) + WorkspacesSettings ->
@posthog/ui/features/settings/sections; useSuspensionSettings ->@posthog/ui/features/suspension - Registered: WORKSPACE_CLIENT +getWorktreeSize/listGitWorktrees/deleteWorktree/confirmDeleteWorktree + worktreesQueryKey provider; SUSPENSION_CLIENT +getSettings/updateSettings; NEW SETTINGS_WORKSPACES_PORT (+ RendererSettingsWorkspacesClient adapter, bound in desktop-services)
- Data: worktrees read keyed by host-provided worktreesQueryKey (coherent with worktreesFilter invalidation); default-directories list on a ui-owned key (sole react-query consumer)
- Bridge: apps shims at WorktreesSettings + WorkspacesSettings (consumer SettingsDialog) — retire when SettingsDialog moves
- Validation: ui workspace+suspension+settings vitest 34/34; ui+apps typecheck 0; biome 0 restricted imports; renderer vite build ✓
- Moved: App.tsx inline workspace.onError/onPromoted/onBranchChanged/onLinkedBranchChanged listeners ->
@posthog/ui/features/workspace/workspace-events.contribution(started by startWorkbench via workspaceUiModule) - Registered: WORKSPACE_CLIENT +onError/onPromoted/onBranchChanged/onLinkedBranchChanged (TrpcWorkspaceClient adapter); workspace.module.ts binds WORKBENCH_CONTRIBUTION
- Data: host workspace events invalidate WORKSPACE_QUERY_KEY (shared key) so all workspace readers stay in sync; promote/error surface toasts
- Validation: contribution test 4/4; ui+apps typecheck 0; renderer vite build ✓ 13.4s
- Registered:
SESSION_SERVICEbridge (@posthog/ui/features/sessions/sessionServiceBridge, 13 methods: sendPrompt/config x2/permission x2/cancel/clear/reset/handoffToCloud/retryCloudTaskWatch/retryUnhealthy/shell-exec x2) + host implplatform-adapters/session-service-bridge.tsdelegating togetSessionService(), wired in main.tsx - Added:
ShellClient.execute()(one-shottrpcClient.shell.execute) to the existing terminal ShellClient port - Moved:
ModelSelector+useSessionCallbacks->@posthog/ui/features/sessions/*(apps paths re-export shims) - Cleaned: 2 more
getSessionService()UI consumers decoupled from the renderer service; this is the keystone-#1 (SessionService) contract the prior notes flagged as the unblock - Bridge: apps session-service-bridge.ts + shims remain until SessionService is dismantled into core/ws-server
- Validation: ui sessions vitest 14 files / 112 tests; typecheck + biome clean on touched paths
- Moved:
App.tsxinlinefocus.onBranchRenamed/focus.onForeignBranchCheckout/agent.onAgentFileActivitysubscriptions ->FocusEventsContribution+AgentEventsContribution(packages/ui/features/{focus,agent}) - Registered:
FOCUS_EVENTS_CLIENT+AGENT_EVENTS_CLIENTports; desktop adapters bound in desktop-services;focusUiModule/agentUiModulein desktop-contributions - Cleaned: App.tsx no longer registers any workspace/focus/agent subscriptions inline (all three clusters now WORKBENCH_CONTRIBUTIONs); orphaned imports removed
- Validation: ui + apps typecheck clean in touched files; biome lint 0 noRestrictedImports
- Moved: inline router logic in
apps/code/.../trpc/routers/secure-store.ts(encrypt/decrypt + electron-store + try/catch) -> newapps/code/.../services/secure-store/{service.ts,schemas.ts}SecureStoreService - Registered:
MAIN_TOKENS.SecureStoreService(.to(SecureStoreService)) +MAIN_TOKENS.SecureStoreBackend(.toConstantValue(rendererStore)); router now one-line zod-validated forwards - Data: encrypted-at-rest KV store; values machine-key encrypted before touching the backend (never plaintext at rest). SecureStoreBackend is a minimal has/get/set/delete/clear interface so the service is Electron-free and unit-testable
- Cleaned: removed the "tRPC router with no backing service" + "inline business logic in router" forbidden patterns for secure-store
- Validation: apps typecheck 0; service.test.ts 5/5 (node, real crypto + fake backend); biome clean
- Moved: getCloudPrAuthorshipMode/getCloudRunSource/getCloudRuntimeOptions out of the renderer SessionService ->
@posthog/ui/features/sessions/cloudRunOptions(pure derivations; +test 7/7) - Data: cloud-run-source / pr-authorship-mode / runtime options derived from host run-state + session config; service keeps the I/O
- Validation: ui sessions vitest 119/119; ui+apps typecheck 0; renderer vite build ✓ 13.5s
- Added: neutral
diffWorkerHost(@posthog/ui/workbench/diffWorkerHost) for the pierre diff Vite worker; reviewHostBindings registers it alongside the review-specific one - Moved:
useConversationSearch,ConversationView(361L),SessionView(716L) ->@posthog/ui/features/sessions/*(apps paths are re-export shims) - Cleaned: ConversationView's
?worker&urlhost coupling now flows through the worker host; SessionView's 5 SessionService calls through the SESSION_SERVICE bridge - Bridge: apps shims remain until the stateful SessionService is dismantled; useSessionConnection still needs
loadLogsOnly/watchCloudTaskadded to the bridge - Validation: ui sessions vitest 15 files / 119 tests; typecheck + biome clean on touched paths
- Moved: direct repository access in
apps/code/.../trpc/routers/additional-directories.ts-> newpackages/workspace-server/src/services/additional-directories/AdditionalDirectoriesService - Registered:
ADDITIONAL_DIRECTORIES_SERVICEidentifier +additionalDirectoriesModule; loaded in the apps container (shares the boundWORKSPACE_REPOSITORY+DEFAULT_ADDITIONAL_DIRECTORY_REPOSITORY) - Data: the service injects both repos via their ws-server identifiers and owns default (per-device) + per-task additional directories; router is one-line zod-validated forwards
- Cleaned: removed the "router bypasses service to repository" forbidden pattern for additional-directories
- Validation: ws-server typecheck 0 + additional-directories.test.ts 2/2 (fake repos, plain node); apps typecheck 0 (my files); biome clean
2026-06-02 - task-detail leaves -> ui (TaskPendingView / SuggestedTasksPanel / WorkspaceSetupPrompt)
- Moved: TaskPendingView, SuggestedTasksPanel, WorkspaceSetupPrompt ->
@posthog/ui/features/task-detail/components - Registered: WorkspaceSetupPrompt consumes existing FOLDERS_CLIENT.addFolder + GIT_QUERY_CLIENT.detectRepo + useEnsureWorkspace (no new ports)
- Bridge: apps shims at old paths (consumers MainLayout/TaskInput/TaskLogsPanel) — retire when those move
- Validation: ui task-detail vitest 20/20; ui+apps typecheck 0; renderer vite build ✓ 14s
- Moved: useCommandCenterData/useAutofillCommandCenter/useAvailableTasks (hooks) + TaskSelector/CommandCenterPRButton (components) ->
@posthog/ui/features/command-center - Data: all consume existing ui hooks/stores (tasks/workspaces/archive/sessions/commandCenterStore) + git-interaction PR hooks; no new ports
- Bridge: apps shims at old paths (consumers CommandCenterGrid/View/Panel) — retire when the Panel/Toolbar keystone tier moves
- Validation: ui command-center vitest 6/6; ui+apps typecheck 0; renderer vite build ✓ 13.4s
- Extended
SESSION_SERVICEbridge: +connectToTask/loadLogsOnly/watchCloudTask/recordActivity (+ ConnectParams type) - Moved:
useSessionConnection->@posthog/ui/features/sessions/hooks(apps shim) - Decoupled:
CommandCenterToolbarcancelPrompt -> bridge (stays in apps/command-center) - MILESTONE: no renderer UI calls
getSessionService(); the SessionService is reachable from ui only through the bridges. Remaining direct callers are the bridge adapters, the singleton + tests, and apps-layer orchestration (task-creation saga, localHandoffService, GlobalEventHandlers, desktop-services) - Validation: ui sessions vitest 15 files / 119 tests; typecheck + biome clean on touched paths
- Moved: TaskLogsPanel, TabContentRenderer (task-detail) + usePanelLayoutHooks, PanelLayout, LeafNodeRenderer (panels) -> @posthog/ui
- Cleaned: apps/panels reduced to index.ts re-export; PanelLayout/usePanelLayoutHooks no longer in apps (ui-panels acceptance)
- Bridge: apps shims at TaskLogsPanel/TabContentRenderer (consumers in task-detail) — retire when TaskDetail moves
- Validation: ui panels+task-detail vitest 62/62; ui+apps typecheck 0; renderer vite build ✓ 13.8s
- Moved: inline router logic in
apps/code/.../trpc/routers/encryption.ts(isAvailable + base64 + passthrough fallback + error handling) -> newapps/code/.../services/encryption/service.tsEncryptionService - Registered:
MAIN_TOKENS.EncryptionService(.to(EncryptionService), injects platformSECURE_STORAGE_SERVICE); router is one-line zod forwards - Cleaned: removed "tRPC router with inline business logic" forbidden pattern for encryption
- Validation: service.test.ts 3/3 (fake ISecureStorage); apps typecheck 0 (my files); biome clean
- Moved:
useSpendAnalysis,TokenSpendAnalysisBanner(393L),PlanUsageSettings(509L) ->@posthog/ui(apps paths are re-export shims) - Cleaned: imperative
getAuthenticatedClient->useOptionalAuthenticatedClient;UsageBucketsourced from@posthog/core/usage/schemas(ui may import core) instead of@main/* - Validation: ui billing vitest 4 files / 53 tests; typecheck + biome clean on touched paths
- Moved: TaskDetail.tsx (main task-detail screen) -> @posthog/ui/features/task-detail/components; apps @hooks/useFileWatcher orchestration -> @posthog/ui/features/file-watcher/useRepoFileWatcher
- Registered: NEW FILE_WATCHER_CONTROL port (start/stop) + TrpcFileWatcherControl adapter (desktop-services); fs-read invalidation via fsQueryKey provider
- Cleaned: deleted obsolete apps @hooks/useFileWatcher (host trpc now behind the port)
- Bridge: apps TaskDetail shim (consumer MainLayout) — retire when MainLayout moves
- Validation: ui task-detail+file-watcher vitest 20/20; ui+apps typecheck 0; renderer vite build ✓ 13.25s
- Moved:
apps/code/src/renderer/features/command-center/components/CommandCenterSessionView.tsx->packages/ui/src/features/command-center/components/CommandCenterSessionView.tsx - Data: pure UI; renders ui SessionView driven by useSessionViewState/useSessionConnection/useSessionCallbacks (all ui)
- Bridge: apps path is a re-export shim; remains until CommandCenterPanel/Grid/View land (gated on task-detail
TaskInput) - Validation:
@posthog/ui+@posthog/codetypecheck 0; biome clean
- Moved: session domain model
apps/@posthog/uisessionStore types ->@posthog/shared/src/sessions.ts(AgentSession,Adapter,QueuedMessage,OptimisticItem,PermissionRequest,SessionStatus, config-option helpers); uisessionStore.ts/sessionLogTypes.tsre-export from shared. - Moved: pure connect-orchestration decisions out of the renderer
SessionService.doConnect->@posthog/core/sessions/connectRouting.ts(routeLocalConnect,computeAutoRetryFinalState). - Data: source of truth for the session model is now
@posthog/shared;@posthog/uisessionStore is the single runtime store (the divergent appsstores/sessionStore.tsduplicate was deleted). - Cleaned: removed the apps↔ui sessionStore divergence (one model, one store); core now consumes the model directly, enabling future core-owned session orchestration.
- Bridge:
apps/.../platform-adapters/session-service-bridge.ts(SESSION_SERVICE) remains the seam until the statefulSessionServicebody is split into a core SessionService + ws-server host I/O. - Validation:
pnpm typecheck19/19; renderervite build; core 8/8; ui session 39/39; apps service.test 101/103 (2 pre-existing exogenous cloud-file-reader fails). - Known:
@posthog/sharedhas two divergentTaskinterfaces (task.tsvsdomain-types.ts) — needs a dedicated reconcile slice.
- Moved:
apps/.../task-detail/components/TaskInput.tsx+hooks/{usePreviewConfig,useTaskCreation}.ts->packages/ui/src/features/task-detail/ - Moved:
apps/.../command-center/components/{CommandCenterPanel,CommandCenterGrid,CommandCenterView}.tsx->packages/ui/src/features/command-center/components/;useAutofillCommandCenter.test.ts-> ui - Registered:
PREVIEW_CONFIG_CLIENT(packages/ui/features/task-detail/previewConfigClient.ts) +TrpcPreviewConfigClientadapter bound in desktop-services; addedFOLDERS_CLIENT.getMostRecentlyAccessedRepository,WORKSPACE_CLIENT.getWorktreeFileUsage - Data: task creation routes through
getTaskServiceBridge()(keystone-#1 bridge) instead ofget(RENDERER_TOKENS.TaskService); preview config + skills + recent-repo + worktree-usage via per-feature client ports - Cleaned: removed 6 dead apps command-center shims; apps command-center dir now empty (fully ui-resident)
- Bridge: apps
task-detail/components/TaskInput.tsxis a re-export shim (consumers MainLayout + the now-ui CommandCenterPanel). Retire when MainLayout's task-input view moves (ui-shell/ui-task-detail). - Validation: @posthog/ui typecheck 0; @posthog/code typecheck 0 in these paths (tree red is exogenous: concurrent sessions/settings/inbox agents); renderer vite build ✓; ui command-center + task-detail vitest green; biome clean
- Moved:
apps/code/.../features/git-interaction/components/CloudGitInteractionHeader.tsx->packages/ui/src/features/git-interaction/components/(last real app file) - Registered:
LocalHandoffBridge(packages/ui/src/features/sessions/localHandoffBridge.ts); host wiressetLocalHandoffBridge(getLocalHandoffService())inapps/code/.../platform-adapters/session-service-bridge.ts - Cleaned: retired all 14 git-interaction re-export shims (components/hooks/utils); repointed last consumers (
HeaderRow,focusClientAdapter,GitInteractionDialogs.stories) to@posthog/ui. apps/code git-interaction now holds only the 2 app-only*.stories.tsx. - Data: source of truth is the git capability in workspace-server (via GIT_QUERY_CLIENT/GIT_WRITE_CLIENT ports); UI is a pure projection
- Bridge:
LocalHandoffBridge(apps->ui) remains untilLocalHandoffService(trpc.folders/os + getSessionService) moves to core/ws-server — a sessions-slice concern - Validation: apps web+node tsc 0; @posthog/ui typecheck 0; ui git-interaction vitest 76/76; renderer
vite build✓. Remaining gate: live-GUI stage/commit/switch smoke (needs running Electron; not headless-runnable here)
- Moved:
apps/.../features/inbox/{components/InboxView,InboxSignalsTab,InboxSetupPane,InboxSourcesDialog, components/detail/ReportDetailPane,ReportTaskLogs, hooks/useInboxDeepLink,useInboxDeepLinkListSync, stores/inboxCloudTaskStore}->packages/ui/src/features/inbox/ - Registered:
DEEP_LINK_CLIENT.getPendingReportLink+onOpenReport(port + deep-link adapter) - Data: inbox report reads via ported ui hooks; cloud-task creation + default-model via
getTaskServiceBridge()(createTask/resolveDefaultModel); deep links viaDEEP_LINK_CLIENT; folders recent-repo viaFOLDERS_CLIENT - Cleaned: fixed the SignalSourcesSettings module-not-found red (repointed inbox setup/sources to
@posthog/ui/features/settings/sections) - Bridge: apps
inbox/components/InboxView.tsx+inbox/hooks/useInboxDeepLink.tsare re-export shims (consumer MainLayout). Host-stays (by design):inbox/utils/resolveDefaultModel.ts(task-service-bridge impl),inbox/devtools/inboxDemoConsole.ts(dev console). - Validation: @posthog/ui typecheck 0; @posthog/code typecheck 0 inbox errors; renderer vite build OK; ui inbox vitest 78/78
- Moved: the entire ~3650-line renderer
SessionService->@posthog/core/sessions/sessionService.ts, behind an injected host-agnosticSessionServiceDeps(tRPC port, store port, helper ports, auth/notifier/analytics/toast/log/queryClient/persistedConfig). - Adapter:
apps/.../sessions/service/service.tsis now a thin desktop host adapter (buildSessionServiceDeps()+getSessionService()), wiringtrpcClient+@posthog/uistores + host helpers; re-exportsSessionService+ConnectParams. - Data: orchestration is now host-agnostic core; host I/O is injected via ports (tRPC -> main process, stores ->
@posthog/ui).Task/ConnectParamsuse@posthog/shared/domain-types(the app's live Task shape). - Cleaned: the canonical "renderer service owns all the orchestration" forbidden pattern is removed — the renderer no longer contains the SessionService logic, only the singleton + deps wiring.
- Bridge:
platform-adapters/session-service-bridge.ts(SESSION_SERVICE) +sessionTaskBridgeAdapter.tsremain in apps by design (host wiring);getSessionService()singleton stays in the adapter. - Validation:
@posthog/core+ apps typecheck 0 (my paths);service.test.ts101/103 (identical pre/post-move; 2 exogenous cloud-file-reader fails); biome clean. Live agent-turn smoke pending (can't run headless) -> sliceneeds_validation.
- Moved:
apps/.../components/{HeaderRow,MainLayout}.tsx->packages/ui/src/workbench/ - Registered:
WORKSPACE_CLIENT.reconcileCloudWorkspaces(port + adapter); hostAnalyticsBootContribution+InboxDemoDevContribution(apps/.../contributions/app-boot.contributions.ts) bound in desktop-contributions - Data: MainLayout cloud-reconcile via WORKSPACE_CLIENT; analytics-init + dev-inbox-console now WORKBENCH_CONTRIBUTIONs (App.tsx has zero inline initializers)
- Cleaned: lifted GlobalEventHandlers (host glue) out of MainLayout to the App root; deleted dead HeaderRow apps shim
- Bridge/host-stays (correct end-state): App.tsx (auth-gate root), GlobalEventHandlers, Providers, main.tsx, ErrorBoundary wrapper. apps MainLayout.tsx is a shim (consumer App).
- Outstanding: live Electron boot smoke; acceptance #4 (TanStack route contributions) doesn't match the navigationStore view-switch routing model — flagged for re-scoping.
- Validation: @posthog/ui typecheck 0; @posthog/code typecheck 0 (modulo exogenous service.ts); renderer vite build OK; biome clean
- Moved:
apps/.../sidebar/hooks/useTaskPrStatus.test.ts->packages/ui/src/features/sidebar/useTaskPrStatus.test.ts(mock repointed @renderer/trpc -> @posthog/di/react useService) - Deleted:
apps/.../features/panels/index.ts(dead re-export barrel, zero consumers) - Result: apps features/{sidebar,right-sidebar,panels} have zero real files; all ui-resident
- Validation: ui + apps typecheck 0; ui useTaskPrStatus test 8/8; renderer vite build OK; biome clean
- Moved: nothing new; this collapses three module-setter bridges now that consumers resolve real services via DI.
- Registered: consumers use
useService(SESSION_SERVICE|TASK_SERVICE|PREVIEW_CONFIG_CLIENT)(React) andresolveService(SESSION_SERVICE)(imperative) directly; no bridge indirection. - Cleaned:
- Deleted bridges
packages/ui/.../sessions/sessionServiceBridge.ts,sessions/sessionTaskBridge.ts,tasks/taskServiceBridge.tsand thesessionServiceBridge.test.ts(it only covered the deleted setX/getX singleton — 2 tests removed, no real coverage lost). - Deleted apps adapters
platform-adapters/task-service-bridge.tsandfeatures/sessions/sessionTaskBridgeAdapter.ts(sole purpose was setX wiring); removed their boot imports fromrenderer/main.tsx. - Stripped the
sessionServiceBridgeobject +setSessionServiceBridgewiring (and now-unused imports) fromplatform-adapters/session-service-bridge.ts; the file now only registersLocalHandoffBridge(that bridge stays). - Repointed 5 affected test files off the dead bridge
vi.mocks onto@posthog/di/reactuseService/@posthog/di/containerresolveServicemocks (useTaskMutations, useChatTitleGenerator, useTaskDeepLink, useArchiveTask, useDiscussReport) plus taskCreationSaga.
- Deleted bridges
- Bridges left intact by design:
getArchiveTaskBridge,getTaskMutationBridge,getLocalHandoffBridge. - Validation:
@posthog/core/@posthog/ui/@posthog/codetypecheck 0; ui vitestsessions tasks task-detail inbox archive command-center910/910 (was 891 pass / 21 fail before repointing the test mocks); biome clean on all touched files (broader tree has unrelated exogenous errors from other in-flight agents).
- Moved: nothing new;
LocalHandoffServicealready lives inpackages/ui/.../sessions/localHandoffService.tsbound toLOCAL_HANDOFF_SERVICEin the renderer DI container. - Repointed:
CloudGitInteractionHeader.tsxnow resolves the service viauseService<LocalHandoffService>(LOCAL_HANDOFF_SERVICE)instead ofgetLocalHandoffBridge(); all five method calls (start/resumePending/openConfirm/cancelPendingFlow/hideDirtyTree) are identical on the ported service. - Cleaned:
- Deleted bridge
packages/ui/.../sessions/localHandoffBridge.ts(setX/getX module-setter, sole consumer was CloudGitInteractionHeader). - Deleted the unported apps service
apps/.../features/sessions/service/localHandoffService.ts(superseded by the ui service +getLocalHandoffHostport). - Deleted apps adapter
platform-adapters/session-service-bridge.ts(its sole remaining job was the LocalHandoffBridge setX wiring) and removed its boot import fromrenderer/main.tsx.
- Deleted bridge
- No bridge tests existed for localHandoff; nothing to repoint.
- Validation:
@posthog/core/@posthog/ui/@posthog/codetypecheck 0 (my paths); biome clean on touched files; ui vitestsessions git-interactionreported below.
- Moved: nothing new; consumers
useDeleteTask/useCreateTask(tasks/useTaskCrudMutations.ts) andarchiveTaskImperative/useArchiveTask(archive/useArchiveTask.ts) already resolve real DI:useService<WorkspaceClient>(WORKSPACE_CLIENT)/resolveService(WORKSPACE_CLIENT),useHostTRPCClient().contextMenu.confirmDeleteTask,resolveService(ARCHIVE_CLIENT).archive,resolveService(SESSION_SERVICE).disconnectFromTask, andpinnedTasksApi. - Cleaned:
- Deleted bridges
packages/ui/.../tasks/taskMutationBridge.ts,packages/ui/.../archive/archiveTaskBridge.ts(setX/getX module-setters with no remaininggetXconsumers). - Deleted apps adapters
platform-adapters/task-mutation-bridge.ts,platform-adapters/archive-task-bridge.ts(sole purpose was setX wiring); removed their two side-effect boot imports fromrenderer/main.tsx. - Repointed
tasks/useTaskCrudMutations.test.tsxandarchive/useArchiveTask.test.tsoff the dead bridgevi.mocks onto the real ports:@posthog/di/reactuseService,@posthog/di/containerresolveService(routed by token),@posthog/host-router/reactuseHostTRPCClient, and@posthog/ui/features/sidebar/taskMetaApipinnedTasksApi. Coverage preserved (confirm/decline delete paths; optimistic-add + rollback/re-pin archive paths).
- Deleted bridges
- Left intact by design: apps imperative
workspaceApi(features/workspace/hooks/useWorkspace.ts) — not a bridge, has 4 other apps-internal consumers. - Validation: see structured report.
- Context: every real consumer of these ports already calls
useHostTRPC/useHostTRPCClientagainst the host-router; only the dead port interface + adapter + DI binding remained. Retired the scaffolding. - Retired ports/tokens:
ARCHIVE_CLIENT,AUTH_CLIENT,FILE_CONTEXT_MENU_CLIENT,PANEL_CONTEXT_MENU_CLIENT,SETTINGS_GENERAL_PORT,TASK_CONTEXT_MENU_CLIENT. - Deleted (ui):
features/archive/ports.ts,features/archive/archiveCacheProvider.ts,features/sessions/fileContextMenuClient.ts,features/panels/panelContextMenuClient.ts,features/tasks/taskContextMenuClient.ts. - Deleted (apps):
platform-adapters/{archive-client,archive-cache-keys,file-context-menu-client,panel-context-menu-client,task-context-menu-client,settings-general-client}.ts.auth-client.tswas already removed (token no longer existed;auth/ports.tskeeps onlyAUTH_SIDE_EFFECTS). - Edited (not deleted):
features/settings/ports.ts— strippedSettingsGeneralPort/SETTINGS_GENERAL_PORTonly;SettingsWorkspacesPort/SETTINGS_WORKSPACES_PORTstay (blocked port, still bound). - Repointed:
archive/useArchiveTask.ts: replaced thearchiveCacheProviderhost-set cache-key indirection with auseArchiveCacheKeys()hook that derives the real keys offuseHostTRPC()(trpc.archive.{archivedTaskIds,list}.queryKey(),trpc.archive.pathFilter().queryKey);archiveTaskImperative/archiveTasksImperativenow take anArchiveCacheKeysparam.SidebarMenu.tsxderives keys via the hook and threads them into the twoarchiveTasksImperativecalls.sessions/components/useFileContextMenu.ts: inlined theOpenFileContextMenuInputtype (its sole non-adapter consumer) so the deadfileContextMenuClient.tscould be deleted.apps/.../features/auth/hooks/authQueries.ts: dropped a stale PORT NOTE referencing the retiredAUTH_CLIENT.
- Cleaned (apps
renderer/desktop-services.ts): removed the 5 port imports, 5 adapter imports, thesetArchiveCacheKeys(...)boot call, and the 5 container bindings. - Tests: rewrote
archive/useArchiveTask.test.ts— dropped the./ports(ARCHIVE_CLIENT) +archiveCacheProvidermocks; now mocks@posthog/host-router/clientHOST_TRPC_CLIENT(routed throughresolveServiceto{ archive: { archive: { mutate } } }), passesArchiveCacheKeysas a param, and assertsmutate({ taskId }). Coverage preserved (optimistic add + rollback/re-pin). This test was already red in-tree (consumer had moved toHOST_TRPC_CLIENTbut the test still mocked the old token); now green. - Validation:
@posthog/uitypecheck 0;@posthog/codetypecheck 0; biome clean on all touched files;@posthog/uitest 910/910.
- Context: every real consumer already calls
useHostTRPC/useHostTRPCClientagainst the host-router; only the dead port interface + adapter + DI binding remained. Retired the scaffolding. - Retired tokens:
AGENT_EVENTS_CLIENT,FILE_CONTENT_CLIENT,FOCUS_EVENTS_CLIENT,GIT_QUERY_CLIENT,GIT_WRITE_CLIENT,GITHUB_INTEGRATION_CLIENT,LINEAR_INTEGRATION_CLIENT,PREVIEW_CONFIG_CLIENT,REPO_FILES_CLIENT,REVIEW_FILE_CLIENT,SETTINGS_WORKSPACES_PORT,SIDEBAR_TASK_META_CLIENT,SLACK_INTEGRATION_CLIENT,WORKSPACE_CLIENT. - Deleted whole (ui port files, all tokens retired):
features/agent/agentEventsClient.ts,features/focus/focusEventsClient.ts,features/code-editor/ports.ts,features/code-review/ports.ts,features/repo-files/ports.ts,features/task-detail/previewConfigClient.ts,features/integrations/ports.ts(github+slack+linear trio),features/settings/ports.ts(sole token SETTINGS_WORKSPACES_PORT),features/workspace/workspaceCacheProvider.ts(its only live consumer WorktreesSettings now derives keys offuseHostTRPC().workspace.listGitWorktrees.queryKey/queryFilter). - Stripped (ui port files with shared survivors):
features/git-interaction/ports.ts— removedGitQueryClient/GitWriteClient+GIT_QUERY_CLIENT/GIT_WRITE_CLIENT+ now-unusedGithubRef/PrActionType/PrReviewThread/GitBusyStateimports; kept all git domain types (GitStateSnapshot/CreatePrStep/CommitResult/...PrDetails) still consumed across the git tier.features/sidebar/ports.ts— removedSidebarTaskMetaClient+SIDEBAR_TASK_META_CLIENT; keptSidebarPrState/TaskPrStatus/RawTaskTimestamp.features/workspace/ports.ts— removedWorkspaceClient/WORKSPACE_CLIENT+ adapter-only typesCreateWorkspaceInput/GitWorktreeEntry/WorkspaceWarning; keptWORKSPACE_QUERY_KEY. - Deleted (apps adapters):
platform-adapters/{agent-events-client,focus-events-client,file-content-client,review-file-client,repo-files-client,preview-config-client,github-integration-client,slack-integration-client,linear-integration-client,settings-workspaces-client,sidebar-task-meta-client,workspace-client,git-query-client,git-write-client,workspace-cache-keys}.ts. - Repointed (ui):
git-interaction/utils/branchCreation.ts+useGitInteraction.ts+task-detail/components/TaskInput.tsxdropped theGitWriteClienttype (branchCreation now takes a local structuralBranchCreator; TaskInput memoizes its inline createBranch client).WorktreesSettings.tsxderives the worktrees query key/filter offuseHostTRPC(). - Cleaned (apps
renderer/desktop-services.ts): removed all 14 port imports + 14 adapter imports + thesetWorkspaceCacheKeyProvider(...)boot call + the 14 container bindings (keptsetGitCacheKeyProvider+ the git-cache-keys adapter — shared git working-tree/branch invalidation infra still consumed by the git/code-review/file-watcher tiers — andsetTaskMetaApi, taskMetaApi survives). Dropped stale PORT NOTE comments inapps/.../features/workspace/hooks/useWorkspace.tsandfeatures/sidebar/taskMetaApi.ts. - Tests: repointed 7 test files off the retired-port
vi.mock("@posthog/di/react")/workspaceCacheProvidermocks ontouseHostTRPC/useHostTRPCClientmocks: useTaskPrStatus, BranchSelector, useBranchMismatchDialog, branchCreation, useWorkspaceMutations, useSuspendTask, useDiscussReport, plus workspace-events.contribution (host-router subscription shape). Coverage preserved; these were already red in-tree (consumers had migrated). Full ui suite 96 files / 910 tests pass (was 879 pass / 31 fail). - Validation:
@posthog/host-router/@posthog/core/@posthog/ui/@posthog/codetypecheck 0; biome clean on all touched files;@posthog/uitest 910/910.
- Context: the handoff orchestration sagas already live in
@posthog/core/handoff, but the appsHandoffServicedeps-provider still performed raw host syscalls (node:fson~/.posthog-code/sessions/<runId>/logs.ndjson+@posthog/gitstash/reset sagas). Moved those into workspace-server so the deps-provider is pure wiring. - FS -> ws-server
LocalLogsService(packages/workspace-server/src/services/local-logs/service.ts): addedseedLocalLogs/countLocalLogEntries/deleteLocalLogCache, reusing its existinggetLocalLogPathso the NDJSON path is owned in one place. Exposed aslocalLogs.seed(mutation) /localLogs.count(query) /localLogs.delete(mutation) intrpc.ts. Main thin-client (apps/code/src/main/services/local-logs/service.ts) gained 3 delegating methods; its PORT NOTE's "handoff stops writing the NDJSON via raw fs" retirement clause is now satisfied. - GIT -> ws-server
GitService(packages/workspace-server/src/services/git/service.ts): addedreadHandoffLocalGitState(wraps@posthog/git/handoff) +cleanupAfterCloudHandoff(StashPushSaga + ResetToDefaultBranchSaga, returns{stashed,switched,defaultBranch}). Exposed asgit.readHandoffLocalGitState(query) +git.cleanupAfterCloudHandoff(mutation).handoffLocalGitStateSchemamirrored locally in ws gitschemas.ts(ws zod v4; nullable strings; structurally assignable toAgentTypes.HandoffLocalGitState). HandoffService: now injectsMAIN_TOKENS.LocalLogsService+MAIN_TOKENS.WorkspaceClientand delegates; dropped allnode:fs/node:os/node:pathimports and the@posthog/gitruntime imports (readHandoffLocalGitState/StashPushSaga/ResetToDefaultBranchSaga) — only a type-onlyGitHandoffBranchDivergenceimport remains. The cloud-logfetchstays in the provider (network, not a host syscall); only the fs write/read/rm moved.- Core:
HandoffToCloudSagaDeps.countLocalLogEntrieschangednumber -> Promise<number>(now an async tRPC call); saga awaits it; test mocks updated tomockResolvedValue. - Validation: full
pnpm typecheck21/21; ws-serverlocal-logs17/17 (added seed/count/delete tests);@posthog/corehandoff 16/16; appshandoff/service.test6/6 (constructor +2 args;localGitStatenow driven via theworkspaceClient.gitmock); core purity gatebiome lint packages/core/src/handoff0 noRestrictedImports; biome check clean. ws-server consumed from src (no dist build). NOT run: live end-to-end handoff GUI smoke (real cloud run + GitHub auth + Electron; env-gated headless) — the sole remaining gate beforehandoffflips to passing.
2026-06-03 - opus-handoff-syscalls - HandoffService fully out of apps/code (core + workspace-server)
- Follow-up to the host-syscalls move: the entire HandoffService is now gone from
apps/code. Orchestration lives in core, host I/O in workspace-server, the port contract in shared, and the desktop keeps only a thin transport adapter + DI wiring. - core
@posthog/core/handoff/handoff.ts(HandoffService): preflight/execute/preflightToCloud/executeToCloud +extractHandoffErrorCode+ both saga constructions +closeCloudRun(viaCLOUD_TASK_SERVICE). Injects a singleHANDOFF_HOSTport (from shared) +CLOUD_TASK_SERVICE+HANDOFF_LOGGER.handoff.modulebindsHANDOFF_SERVICE. Schemas moved to@posthog/core/handoff/schemas(handoffLocalGitStateSchemadefined locally instead of importing@posthog/agent/server/schemas). Purity gate clean. - workspace-server
services/handoff/service.ts(HandoffHostService implements HandoffHost): owns ALL the host business logic — agent api client construction,HandoffCheckpointTrackercapture/apply,resumeFromLog,formatConversationForResume, theGIT_CHECKPOINTnotification append, workspace/repository repo orchestration (attachWorkspaceToFolderrevert,updateWorkspaceMode), and the diverged-branch confirmation dialog. Injects wsAgentService/AgentAuthAdapter+WORKSPACE_REPOSITORY/REPOSITORY_REPOSITORY+ platformDIALOG_SERVICE/APP_LIFECYCLE_SERVICE+ two narrow gateways (HANDOFF_GIT_GATEWAY/HANDOFF_LOG_GATEWAY) for the child-process git/log syscalls. - shared
handoff-host.ts: theHandoffHostport contract (+HandoffApiContext/HandoffChangedFile/HandoffReconnectParams/HandoffResumeStateResult), so core and workspace-server reference it without importing each other. - apps/code keeps only:
services/handoff/git-gateway.ts(TrpcHandoffGitGateway— a ~50-line desktop tRPC adapter overworkspaceClient.git),HANDOFF_LOG_GATEWAYbound to the existing local-logs thin client, the one-line handoff router (now imports core schemas + injectsHANDOFF_SERVICE), and the DI bindings. Deletedservices/handoff/{service,schemas,service.test}.ts. RetiredMAIN_TOKENS.HandoffService. No agent runtime, checkpoint, saga, or orchestration code remains in apps/code. - Validation: full
pnpm typecheck21/21;@posthog/corehandoff 22/22; ws-server handoff host 8/8 + local-logs 17/17; core purity gate 0 noRestrictedImports; biome clean; shared dist rebuilt. NOT run: live end-to-end handoff GUI smoke (env-gated).