Skip to content

πŸ€– Simplify state: derive projects from workspace metadataΒ #298

@ammar-agent

Description

@ammar-agent

Problem

Currently we maintain two sources of truth for workspace lists:

  1. workspaceMetadata Map - updated via metadata events from backend
  2. projects Map - manually reloaded when workspaces change

This requires careful coordination to keep them in sync. Recent bug: forked workspace didn't appear in sidebar until we added special "detect new workspace" logic.

Proposed Solution

Derive projects from workspace metadata. All information in ProjectConfig exists in WorkspaceMetadata:

function deriveProjectsFromMetadata(
  workspaceMetadata: Map<string, FrontendWorkspaceMetadata>
): Map<string, ProjectConfig> {
  const projects = new Map<string, ProjectConfig>();
  
  for (const metadata of workspaceMetadata.values()) {
    if (!projects.has(metadata.projectPath)) {
      projects.set(metadata.projectPath, {
        path: metadata.projectPath,
        workspaces: [],
      });
    }
    
    projects.get(metadata.projectPath)!.workspaces.push({
      path: metadata.namedWorkspacePath,
      id: metadata.id,
      name: metadata.name,
      createdAt: metadata.createdAt,
    });
  }
  
  return projects;
}

Benefits

  • βœ… Single source of truth (workspaceMetadata)
  • βœ… Projects always in sync (derived on-demand)
  • βœ… Eliminates entire class of synchronization bugs
  • βœ… ~155 LoC reduction (remove useProjectManagement, manual reloading)
  • βœ… Simpler mental model

Implementation

Phase 1: Add derivation

  1. Create src/utils/deriveProjects.ts
  2. Use in App.tsx: const projects = useMemo(() => deriveProjectsFromMetadata(workspaceMetadata), [workspaceMetadata])
  3. Verify UI works identically

Phase 2: Remove dead code

  1. Delete useProjectManagement hook
  2. Remove onProjectsUpdate callback machinery
  3. Remove manual project reloading from create/remove/rename
  4. Remove "detect new workspace" logic (lines 58-75 in useWorkspaceManagement.ts)

Phase 3: Update tests

  • Update tests to use derivation
  • Document pattern

Unresolved: Empty Projects

Current behavior: Config can have projects with no workspaces, shown in sidebar
New behavior: Projects only appear when they have β‰₯1 workspace

Question: Is this acceptable?

Arguments for yes:

  • Empty projects have no actions, just take space
  • Creating first workspace brings project back
  • Cleaner UX (only show active projects)

Arguments for no:

  • User adds project, expects to see it immediately
  • Breaks expectation that "added = visible"

Recommendation: Try it, see if users complain. Empty projects are edge case.

Alternative: Keep Project Registration

If empty projects must be visible:

  • Keep project list in config as projects: ['/path/to/project'] (just paths)
  • Derive workspaces from metadata
  • Show project if in list OR has workspaces
  • Smaller win (~80 LoC instead of ~155 LoC) but still valuable

Estimated Effort

1-2 hours, mostly deleting code. Low risk (type system catches issues).

Related

  • Current workaround: useWorkspaceManagement detects new workspaces and triggers reload (lines 58-75)
  • This issue proposes eliminating the root cause instead of patching symptoms

Generated with cmux

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions