fix(desktop): local-first project icons
Problem
The v2 project icon picker (IconUploadField) called the cloud v2Project.uploadIcon/resetIconToGitHub/removeIcon mutations, which are keyed by v2Projects.id. But v2 projects are host-local (useHostProjects fan-out over client.project.list) — their id has no cloud v2Projects row except for legacy cloud-created projects. So setting an icon failed with "Project not found" on every local-first project (the common case), and even on the projects where it "worked" the uploaded icon was never displayed (the v2 UI derived the icon from repoOwner). That's the #5843 regression.
CDP-confirmed before the fix: upload → Project not found on a local-first project; success only on a legacy cloud-linked project.
Fix — move icons to the local-first model
Project icons now work the same way name, branch prefix, and worktree location already do: host-service local storage.
Host-service
- Add an
iconcolumn to the localprojectstable (migration0012_dear_morph.sql, generated via drizzle-kit). - New
project.setIconmutation (mirrorssetBranchPrefix), writing throughupdateLocalProjectso it bumpsupdatedAtand broadcastsproject:changed. Input validated as an image data-URI, capped at 256KB. iconthreaded throughproject.list/get,toProjectSnapshot, and theProjectSnapshotevent type.
Renderer
useHostProjectsmerge layer carriesicon(local-host-wins).V2ProjectSettingsresolves the icon as custom → GitHub avatar → placeholder, so an uploaded icon shows immediately in both the header and the picker.IconUploadFieldrewritten to call the host clientproject.setIcon, downscale the chosen image to a 128×128 PNG data-URI client-side (no cloud blob), and offer Upload image… / Reset to default.
Cloud teardown
- Deleted the orphaned cloud
uploadIcon/resetIconToGitHub/removeIconprocs + their tests. The renderer now has zerov2Project.*calls. (Thev2Projects.iconUrlcolumn is left in place; dropping it belongs to the broader teardown — seeplans/remove-cloud-project-model.md.)
Verification
typecheckclean (host-service, trpc, desktop);biomeclean.- Tests: host-service integration 9 pass (2 new
setIcontests, mutation-verified to fail without the fix); trpcv2-project18 pass;V2ProjectSettings1 pass. - CDP end-to-end on the local-first
supersetproject that returned "Project not found" before: upload → icon persists asdata:image/pngand displays; Reset to default → reverts to the GitHub avatar. No more "Project not found".
Context
This supersedes the original cloud-based wiring on this branch. It's Phase A of a broader effort to stop the local-first desktop app from querying the cloud for app data (chat, tasks, automations, secrets) — scoped in plans/remove-cloud-project-model.md.
Closes #5843