Lesson

When Designs Collapse

The hive workspace pattern looked great on paper — dedicated workspace, nested splits, workspace naming. Reality deleted 88 lines.

Sometimes you build something elaborate, document it thoroughly, and then delete the whole thing in one commit. This is the story of the 🐝 hive workspace — a beautiful design that reality rejected.

The hive workspace was supposed to be the crown jewel of worker orchestration. All agents would live together in a dedicated workspace, neatly arranged in a grid. The orchestrator would spawn them there, name the workspace meaningfully, and you could tab over to watch your entire fleet at work. It took 88 lines of careful cmux CLI sequencing to implement.

It lasted three commits.

The elaborate design

The original spawn_pi created workers like this:

First worker: Create a dedicated workspace for the hive.

// 1. Create new workspace
const result = cmux("new-workspace", "--cwd", cwd);
const _hiveWorkspaceRef = result.match(/workspace:\d+/)?.[0];

// 2. Name it meaningfully  
cmuxSafe("rename-workspace", "--workspace", _hiveWorkspaceRef, "🐝 hive");

// 3. Select it (--workspace flag doesn't work for CLI-created workspaces)
cmux("select-workspace", "--workspace", _hiveWorkspaceRef);

// 4. List the default surfaces (non-writable)
const defaultSurfaces = cmux("list-pane-surfaces", "--workspace", _hiveWorkspaceRef);
const defaultSf = defaultSurfaces.match(/surface:\d+/);

// 5. Create a real terminal pane
const paneResult = cmux("new-pane", "--type", "terminal", "--direction", "right");
const surfaceRef = paneResult.match(/surface:\d+/)?.[0];

// 6. Close the non-writable default surface
if (defaultSf) cmuxSafe("close-surface", "--surface", defaultSf[0]);

// 7. Resize the worker pane
if (pnMatch) cmuxSafe("resize-pane", "--pane", pnMatch[0], "-L", "--amount", "40");

Subsequent workers: Split into the existing hive with alternating directions.

// 1. Select the hive workspace again
cmux("select-workspace", "--workspace", _hiveWorkspaceRef);

// 2. Alternate split directions for grid layout
const splitDir = _hiveWorkspaceSurfaceCount % 2 === 0 ? "right" : "down";
const paneResult = cmux("new-pane", "--type", "terminal", "--direction", splitDir);

// 3. Resize the new worker
if (pnMatch) cmuxSafe("resize-pane", "--pane", pnMatch[0], "-L", "--amount", "40");

Orchestrator focus preservation: Remember where you came from, switch back when done.

// Save orchestrator's workspace before spawning
let orchestratorWorkspace: string | null = null;
try {
  const id = cmux("identify");
  const info = JSON.parse(id);
  orchestratorWorkspace = info.caller?.workspace_ref || null;
} catch {}

// ... spawn worker in hive ...

// Switch back to orchestrator workspace
if (orchestratorWorkspace && orchestratorWorkspace !== workspaceRef) {
  cmuxSafe("select-workspace", "--workspace", orchestratorWorkspace);
}

Seven sequential cmux calls to spawn the first worker. Three more for each subsequent worker. State tracking for the hive workspace reference and surface count. Orchestrator workspace save-and-restore dance.

It was thoroughly documented across three files and five breadcrumbs. It looked great on paper.

The five platform quirks that killed it

Reality fought back with a coordinated assault:

1. Non-writable default surfaces cmux new-workspace creates a default surface that isn't a terminal — you can't type in it, can't start pi in it. You have to create a --type terminal pane and then close the default. This doubled the surface management complexity.

2. Unreliable workspace flags The --workspace flag on new-pane doesn't work for CLI-created workspaces. You have to select-workspace first, then create the pane. This broke the clean "spawn into workspace X" pattern the design assumed.

3. Silent rename failures rename-workspace with an invalid workspace reference silently renames the wrong workspace. Sometimes it renamed the orchestrator's workspace to "🐝 hive", leaving the worker in an unnamed workspace. Debugging this required parsing tree output to find what got renamed.

4. Focus state fragility The orchestrator's workspace save/restore via cmux identify was fragile. Sometimes the workspace reference changed between save and restore. Sometimes the orchestrator lost its place entirely. The focus preservation became more complex than the spawning itself.

5. Reduced visibility Workers in a separate workspace were LESS visible, not more. You had to actively switch tabs to see them. The worker pane appearing right next to the orchestrator (same workspace) was actually more useful for monitoring.

Each quirk got its own workaround. Each workaround added more sequential cmux calls. Each call was another failure point. The complexity accumulated until the system was more fragile than the problem it solved.

The replacement

Commit e85e94b deleted 88 lines and replaced them with this:

// Split a new terminal pane in the current workspace
const paneResult = cmux("new-pane", "--type", "terminal", "--direction", direction);
const sfMatch = paneResult.match(/surface:\d+/);
const pnMatch = paneResult.match(/pane:\d+/);
surfaceRef = sfMatch ? sfMatch[0] : "";

// Resize worker pane to ~1/3
if (pnMatch) cmuxSafe("resize-pane", "--pane", pnMatch[0], "-L", "--amount", "40");

Two cmux calls. Workers split into the current workspace. No workspace switching, no state tracking, no focus preservation dance. The worker appears right next to the orchestrator where you can see it immediately.

The principle

This isn't just about cmux quirks. It's about a universal pattern in systems design:

  1. You design for the ideal — Workers neatly arranged in their own space, named meaningfully, grid layout for easy scanning.

  2. Platform reality fights back — Non-writable surfaces, unreliable flags, silent failures, fragile state.

  3. Workarounds accumulate — Each platform quirk gets its own fix. Seven calls become eight, then nine. Complexity compounds.

  4. The simplest approach wins — Delete everything. Do the obvious thing. Split a pane where you are.

  5. Documentation debt drags you down — The more you document a design, the harder it becomes to delete. The hive workspace was explained in architecture.md, captured in build-log.md, and had five breadcrumb entries before removal. Investment creates inertia.

The multi-agent lesson

Multi-agent systems multiply platform interactions. Every sequential cmux call is a potential failure point. Seven calls mean seven things can break. Two calls mean two things can break.

When you're orchestrating agents across workspaces, panes, and processes, elaborate coordination designs are especially vulnerable. The more moving parts you add, the more ways reality can surprise you.

The best orchestration pattern is often the simplest one that works. Not the most elegant, not the most architecturally pure — the one that has the fewest places to fail.

Sometimes the right answer is to delete 88 lines and split a pane where you are.