You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
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.py and services/workspace_resolver.py — the orchestrator wraps the call sequence, not the implementations
Problem
PR #31's service extraction successfully decomposed the monolithic
api/workspaces.pyfrom ~1,400 LOC to a thin 162-line Flask routing layer that delegates toservices/. 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–107Each 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 intodetermine_project_for_conversation()at each call site. Any change to the ceremony order, error handling, or a new required map (e.g., addingcollect_invalid_workspace_idsorinfer_invalid_workspace_aliases— already used inworkspace_listing.pybut not in the other 3) must be replicated across all 4 sites. Extracting this into a singleresolve_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
resolve_workspace_context()orchestrator function returning a typed context objectImplementation Notes
services/workspace_db.pyandservices/workspace_resolver.py— the orchestrator wraps the call sequence, not the implementations@dataclasscontext object (e.g.,WorkspaceContext) holdingworkspace_entries,composer_id_to_ws,project_name_map,workspace_path_map,project_layouts_map,bubble_mapapi/export_api.py) uses a subset (noproject_layouts_map); orchestrator should handle optional maps via flag or lazy loadingReferences
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)