Skip to content

Workspace determination ceremony extraction #91

@bradjin8

Description

@bradjin8

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:

  1. services/workspace_tabs.py (605 LOC) — lines 102–106
  2. services/workspace_listing.py (307 LOC) — lines 56–61
  3. scripts/export.py (537 LOC) — lines 206–210
  4. api/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.py and services/workspace_resolver.py — the orchestrator wraps the call sequence, not the implementations
  • Pattern: @dataclass context object (e.g., WorkspaceContext) holding workspace_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 (no project_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 1
  • services/workspace_listing.py:56–61 — ceremony instance 2
  • scripts/export.py:206–210 — ceremony instance 3
  • api/export_api.py:105–107 — ceremony instance 4 (partial)

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions