Problem
After #88 (lazy-load + summary endpoints), two correctness issues and one performance gap remain on large Cursor installs:
- Count mismatch — The project list page shows a different
conversationCount than the number of tabs on the workspace details page for the same project.
- Noisy logs — Loading workspace summaries logs warnings for
composerData:empty-state-draft when the global KV row has a NULL payload (the JSON object must be str, bytes or bytearray, not NoneType).
- Slow first paint — On machines with many
workspaceStorage folders, project list and sidebar summary still take ~50s because composer ownership was rebuilt sequentially from local state.vscdb files, and some paths still over-fetched global composerData.
Expected behavior
- Project card
conversationCount matches GET /api/workspaces/<id>/tabs?summary=1 tab count for the same project.
- Cursor UI placeholders (e.g.
empty-state-draft) are skipped without decode warnings.
- Project list and sidebar summary use workspace-scoped I/O with cached composer registry; first conversation auto-loads after sidebar render (~2s is acceptable).
Root cause (count mismatch)
Project list briefly counted conversations from local allComposers without applying the same global filters as the summary path (non-empty fullConversationHeadersOnly, composer validation, exclusion rules including model names).
Acceptance criteria
Problem
After #88 (lazy-load + summary endpoints), two correctness issues and one performance gap remain on large Cursor installs:
conversationCountthan the number of tabs on the workspace details page for the same project.composerData:empty-state-draftwhen the global KV row has aNULLpayload (the JSON object must be str, bytes or bytearray, not NoneType).workspaceStoragefolders, project list and sidebar summary still take ~50s because composer ownership was rebuilt sequentially from localstate.vscdbfiles, and some paths still over-fetched globalcomposerData.Expected behavior
conversationCountmatchesGET /api/workspaces/<id>/tabs?summary=1tab count for the same project.empty-state-draft) are skipped without decode warnings.Root cause (count mismatch)
Project list briefly counted conversations from local
allComposerswithout applying the same global filters as the summary path (non-emptyfullConversationHeadersOnly, composer validation, exclusion rules including model names).Acceptance criteria
conversationCounton project cards equals summary tab count for the same workspacecomposerDataplaceholders?tab=query param