Repository navigation
Project list and workspace details show different conversation counts; workspace load still slow on large installs #95
Description
Problem
After #88 (lazy-load + summary endpoints), two correctness issues and one performance gap remained on large Cursor installs:
- Count mismatch — The project list page showed a different
conversationCountthan the number of tabs on the workspace details page for the same project. - Noisy logs — Loading workspace summaries logged warnings for
composerData:empty-state-draftwhen the global KV row had aNULLpayload (the JSON object must be str, bytes or bytearray, not NoneType). - Slow first paint — On machines with many
workspaceStoragefolders, project list and sidebar summary took ~50s because composer ownership was rebuilt sequentially from localstate.vscdbfiles, and some paths still over-fetched globalcomposerData.
Expected behavior
- Project card
conversationCountmatchesGET /api/workspaces/<id>/tabs?summary=1tab 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).
A secondary regression dropped conversations during assembly: get_project_from_file_path was accidentally removed while refactoring workspace_resolver.py, causing NameError and parse/assembly warnings on large installs.
Resolution (branch fix/workspace-list-load-and-count)
Shared composer scan
- Extracted
services/workspace_composer_scan.pyso list and summary paths share the same parse, exclusion, and workspace-assignment logic.
Count alignment
list_workspace_projectsnow applies the same filters aslist_workspace_tab_summaries(headers, validation, exclusion rules, assignment).
Performance
- Parallelized
build_composer_id_to_workspace_idviaThreadPoolExecutor. - Bulk
load_project_layouts_mapinstead of per-composer N+1 queries. - Reused mtime-keyed disk cache (
summary_cache.py) for project list and tab summaries.
Correctness
- Null/placeholder
composerDatarows skipped without warnings. - Restored
get_project_from_file_pathinworkspace_resolver.py.
UI
- Workspace page auto-selects the first tab when no
?tab=query param is present. - Deep links from search (
?tab=<composerId>) fetch the requested conversation directly instead of falling back to the first sidebar item.
Tests
tests/test_workspace_list_count_alignment.py— count paritytests/test_project_path_boundary.py— path helper / assignment regression- Updated parse-warning and API tests
Acceptance criteria
-
conversationCounton project cards equals summary tab count for the same workspace - No warnings for null/empty
composerDataplaceholders - Parallel local registry build + scoped global fetches for list/summary paths
- Regression tests for count alignment, workspace scope, and null payload handling
- Workspace UI auto-selects first tab when no
?tab=query param
Out of scope (tracked separately)
Search performance and indexing work landed on the same branch but is not part of this issue:
- 30-day default search window +
all_historyopt-in - Local FTS search index (
search_index.sqlite) with background refresh - Search bubble-scan optimization and deep-link workspace assignment fixes
Those should be referenced in the PR and/or a dedicated search issue.
Verify
- Restart
python app.py. - Home page: project counts match sidebar totals per project.
- No “could not be fully assembled” banner from placeholder composers.
- Open a workspace without
?tab=— first conversation loads automatically. pytest tests/test_workspace_list_count_alignment.py tests/test_api_workspaces.py
Activity