Problem
determine_project_for_conversation is the highest schema-drift-risk function in cppa-cursor-browser. Located in services/workspace_resolver.py (line 227), it takes 9 parameters and implements a multi-stage fallback chain: definitive per-workspace mapping → projectLayouts path matching → newlyCreatedFiles URI matching → codeBlockData file-path matching → bubble relevantFiles / attachedFileCodeChunksUris / context.fileSelections matching → last-resort path-segment substring matching. When Cursor changes its internal SQLite schema — and it has already migrated from workspace-specific to global storage — a renamed key silently returns None rather than raising an error, and the fallback chain silently degrades through progressively less reliable resolution stages with no diagnostic signal. The codebase has 34 test files with 344 test cases and CI running pytest, mypy, and gitleaks, but determine_project_for_conversation has only 1 direct test (tests/test_workspace_assignment_fallback.py — testing the invalid-workspace-alias bypass path). None of the 6 fallback stages (projectLayouts, newlyCreatedFiles, codeBlockData, bubble headers, path-segment matching, or the None terminal) have dedicated coverage, meaning schema drift in any stage is undetectable until it surfaces as silent misassignment in production.
Acceptance Criteria
Implementation Notes
References
services/workspace_resolver.py:227 — function definition (9 params, ~145 LOC)
tests/test_workspace_assignment_fallback.py — existing 1-test coverage
Problem
determine_project_for_conversationis the highest schema-drift-risk function in cppa-cursor-browser. Located inservices/workspace_resolver.py(line 227), it takes 9 parameters and implements a multi-stage fallback chain: definitive per-workspace mapping →projectLayoutspath matching →newlyCreatedFilesURI matching →codeBlockDatafile-path matching → bubblerelevantFiles/attachedFileCodeChunksUris/context.fileSelectionsmatching → last-resort path-segment substring matching. When Cursor changes its internal SQLite schema — and it has already migrated from workspace-specific to global storage — a renamed key silently returnsNonerather than raising an error, and the fallback chain silently degrades through progressively less reliable resolution stages with no diagnostic signal. The codebase has 34 test files with 344 test cases and CI running pytest, mypy, and gitleaks, butdetermine_project_for_conversationhas only 1 direct test (tests/test_workspace_assignment_fallback.py— testing the invalid-workspace-alias bypass path). None of the 6 fallback stages (projectLayouts,newlyCreatedFiles,codeBlockData, bubble headers, path-segment matching, or theNoneterminal) have dedicated coverage, meaning schema drift in any stage is undetectable until it surfaces as silent misassignment in production.Acceptance Criteria
Implementation Notes
services/workspace_resolver.py(post-PR refactor(api): split api/workspaces.py 1,407 → 142 LOC into services/ (closes #25) #31 split)tests/test_workspace_assignment_fallback.py— extend from thereReferences
services/workspace_resolver.py:227— function definition (9 params, ~145 LOC)tests/test_workspace_assignment_fallback.py— existing 1-test coverage