Repository navigation
Workspace determination ceremony extraction #91
Description
Problem
PR #31's service extraction successfully decomposed the monolithic api/workspaces.py from ~1,400 LOC to a thin 162-line Flask routing layer that delegates to services/. However, the extraction moved the workspace-determination ceremony into the service layer without consolidating it — the same 6-function setup sequence is now duplicated verbatim across 4 consumer sites:
services/workspace_tabs.py(605 LOC) — lines 102–106services/workspace_listing.py(307 LOC) — lines 56–61scripts/export.py(537 LOC) — lines 206–210api/export_api.py(235 LOC) — lines 105–107
Each site independently calls: collect_workspace_entries() → build_composer_id_to_workspace_id() → create_project_name_to_workspace_id_map() → create_workspace_path_to_id_map() → load_project_layouts_map() → load_bubble_map(). The results feed into determine_project_for_conversation() at each call site. Any change to the ceremony order, error handling, or a new required map (e.g., adding collect_invalid_workspace_ids or infer_invalid_workspace_aliases — already used in workspace_listing.py but not in the other 3) must be replicated across all 4 sites. Extracting this into a single resolve_workspace_context() orchestrator that returns a context object with all 6 precomputed maps would reduce the 4-site maintenance burden to one and make the ceremony's contract explicit.
Acceptance Criteria
- Single
resolve_workspace_context()orchestrator function returning a typed context object - All 4 call sites refactored to use the shared function
- Behavior unchanged (same test results before and after)
- Context object is well-typed with clear fields for each map
Implementation Notes
- The 6-function ceremony calls are already factored into individual functions in
services/workspace_db.pyandservices/workspace_resolver.py— the orchestrator wraps the call sequence, not the implementations - Pattern:
@dataclasscontext object (e.g.,WorkspaceContext) holdingworkspace_entries,composer_id_to_ws,project_name_map,workspace_path_map,project_layouts_map,bubble_map - The 4th consumer (
api/export_api.py) uses a subset (noproject_layouts_map); orchestrator should handle optional maps via flag or lazy loading - Brad already extracted services in PR refactor(api): split api/workspaces.py 1,407 → 142 LOC into services/ (closes #25) #31; this is the next decomposition step
References
services/workspace_tabs.py:102–106— ceremony instance 1services/workspace_listing.py:56–61— ceremony instance 2scripts/export.py:206–210— ceremony instance 3api/export_api.py:105–107— ceremony instance 4 (partial)
Activity
refactor(services): centralize workspace determination in resolve_wor…