
Not another AI chatbot. This is a battle-tested architecture guide extracted from Claude Cowork — turning your LLM agent from a conversational partner into a team member that delivers finished work. Updated with
coworkspacenaming conventions to avoid conflicts with existingworkspacesinfrastructure.
Rahul recently dropped a viral post on X — "How To Build a One-Person Company Using Claude Cowork" — racking up over 850K views. His core insight was devastatingly simple:
The average knowledge worker spends 60% of their day on work that doesn't require deep thought: formatting, organizing files, compiling reports, researching, SEO. The heavy lifting is the assembly, not the thinking.
Chat-based AI is useful, but it has a fundamental limitation: you're in the loop for every single turn. Every action requires your next prompt. That's collaboration — not delegation.
Claude Cowork changes the equation. Its core insight: wrap Claude Code's agentic engine in a GUI, and non-technical users can delegate entire tasks.
But if you're a developer, you don't need a "how to use Claude Cowork" tutorial. You need: how to implement the same architecture in your own Go backend.
This article is that implementation guide.
After deep-diving into Cowork's architecture, I extracted five patterns you can migrate to any backend:
Traditional Chat: user breaks down the problem → prompts step by step → AI responds step by step
Cowork mode: user describes the desired result → AI autonomously plans → executes → delivers
// Traditional Chat API
POST /api/chat { "message": "Organize my Downloads folder" }
// Cowork API (using coworkspace slug instead of opaque ID)
POST /api/cowork/tasks {
"outcome": "Organize Downloads folder by type and date. Extract receipts and generate an expense report.",
"coworkspace_slug": "downloads-organizer"
}
Every task has a corresponding folder. Files are the medium of exchange between human and agent. The agent reads, processes, and writes — the user returns to finished deliverables.
Why coworkspaces instead of workspaces? If your codebase already has a workspaces directory or concept, coworkspaces avoids namespace collisions entirely. Similarly, using slugs (e.g., downloads-organizer) instead of opaque IDs makes folders readable by both humans and agents — your filesystem becomes self-documenting.
/coworkspaces/{slug}/
├── .instructions.md ← Project instructions (claude.md equivalent)
├── .memory/ ← Agent memory (persists across sessions)
├── .plan.json ← Current task plan
├── files/ ← User files
├── output/ ← Agent-generated deliverables
└── .locks/ ← File locks (prevents race conditions)
The agent doesn't blindly start working. It generates a visible, reviewable plan:
{
"summary": "Organize Downloads and generate expense report",
"phases": [
{
"name": "Scan and Classify",
"subtasks": ["List all files", "Group by extension", "Create category folders"],
"parallel": false
},
{
"name": "Parallel Processing",
"subtasks": ["Process receipt PDFs", "Organize images", "Archive documents"],
"parallel": true
},
{
"name": "Generate Report",
"subtasks": ["OCR receipts", "Extract amounts and dates", "Generate Excel report"],
"parallel": false,
"depends_on": ["Parallel Processing"]
}
]
}
Users can review and modify the plan before execution begins. This is transparent autonomy.
Complex tasks aren't run by a single agent from start to finish. Based on parallel: true phases, the orchestrator spawns multiple sub-agents simultaneously:
Main Agent (Orchestrator)
├── Agent A: Process receipt PDFs
├── Agent B: Organize image files
├── Agent C: Archive document files
└── Agent D: Deduplicate and clean up
All done →
Main Agent synthesizes results → generates final deliverables
Each sub-agent gets its own context window and communicates through files and messages — not shared memory. This prevents context pollution and keeps each agent focused.
Cowork tasks can run for minutes or hours. State must be saved at every turn:
type TaskCheckpoint struct {
TaskID string `json:"task_id"`
Turn int `json:"turn"`
AgentStates []AgentSnapshot `json:"agent_states"`
PlanProgress map[string]float64 `json:"plan_progress"`
SavedAt time.Time `json:"saved_at"`
}
Server restart? Tasks resume from the last checkpoint. This is the fundamental difference between Chat and Cowork — Chat dies and restarts from zero; Cowork picks up where it left off.
The good news: your Go backend already has most of the building blocks:
| Existing Feature | Cowork Counterpart |
|---|---|
| Chat | Cowork Task (assign outcomes, not conversation) |
| Goals | Task Plan (visible to-do list with phases) |
| Files-based projects | Coworkspace (persistent folder per task) |
| Direct tasks (async) | Agent Loop (autonomous multi-step execution) |
| Schedules | Scheduled Cowork Tasks (cron + manual trigger) |
// 1. CoworkOrchestrator — full task lifecycle
type CoworkOrchestrator interface {
CreateTask(ctx, outcome, coworkspaceSlug, opts) (*CoworkTask, error)
StartTask(ctx, taskID) error
GeneratePlan(ctx, taskID) (*TaskPlan, error)
GetTaskProgress(ctx, taskID) (*TaskProgress, error)
}
// 2. AgentLoop — the autonomous execution engine
type AgentLoop interface {
ExecuteTurn(ctx, agentID) (*TurnResult, error)
ExecuteUntilDone(ctx, agentID, opts) error
}
// 3. SubAgentOrchestrator — parallel sub-agent management
type SubAgentOrchestrator interface {
FanOut(ctx, taskID, specs) ([]SubAgent, error)
ClaimTask(ctx, taskID, subtaskID, agentID) error
SynthesizeResults(ctx, taskID) (*SynthesisResult, error)
}
// 4. CoworkspaceManager — file-folder workspace management
type CoworkspaceManager interface {
CreateCoworkspace(ctx, name, opts) (*Coworkspace, error)
ReadFile(ctx, coworkspaceSlug, path) ([]byte, error)
WriteFile(ctx, coworkspaceSlug, path, content) error
}
The Agent Loop is the state machine that turns "I want X" into a series of LLM-powered actions:
LOOP:
1. Build Prompt (task + plan + current state + available tools)
2. Call LLM → receive Thought + Tool Calls
3. Approval Gate (high-risk operations require user confirmation)
4. Execute Tools (read, write, search, generate...)
5. Feed results back to agent
6. Check termination: done / wait_for_user / continue
7. Save Checkpoint
GOTO LOOP
The core Tool Registry:
var CoreCoworkTools = []Tool{
FileReadTool{}, // Read coworkspace files
FileWriteTool{}, // Write/create files
FileListTool{}, // List directory contents
WebSearchTool{}, // Web search
WebFetchTool{}, // Fetch URL content
ShellTool{}, // Execute in sandbox
SubAgentSpawnTool{}, // Launch sub-agent
SubAgentMessageTool{}, // Inter-agent communication
MemoryStoreTool{}, // Save to coworkspace memory
MemoryRetrieveTool{}, // Retrieve from coworkspace memory
TaskUpdateTool{}, // Update task plan
DeliverableCreateTool{}, // Create deliverables
}
-- Coworkspaces (slug-based instead of ID-based)
CREATE TABLE coworkspaces (
slug TEXT PRIMARY KEY, -- e.g., "downloads-organizer"
name TEXT NOT NULL,
root_path TEXT NOT NULL,
instructions TEXT,
project_id TEXT,
user_id TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
-- Cowork Tasks
CREATE TABLE cowork_tasks (
id TEXT PRIMARY KEY,
coworkspace_slug TEXT NOT NULL REFERENCES coworkspaces(slug),
outcome TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
plan_json JSONB,
permission_mode TEXT NOT NULL DEFAULT 'ask_before'
);
-- Task Phases
CREATE TABLE task_phases (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL REFERENCES cowork_tasks(id),
name TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
parallel BOOLEAN DEFAULT FALSE,
depends_on JSONB,
progress REAL DEFAULT 0.0
);
-- Sub-Agents
CREATE TABLE sub_agents (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL REFERENCES cowork_tasks(id),
name TEXT NOT NULL,
role TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'idle'
);
-- Agent Messages (inter-agent communication)
CREATE TABLE agent_messages (
id TEXT PRIMARY KEY,
from_agent TEXT NOT NULL,
to_agent TEXT NOT NULL,
type TEXT NOT NULL,
content TEXT NOT NULL
);
-- Task Checkpoints (for crash recovery)
CREATE TABLE task_checkpoints (
id TEXT PRIMARY KEY,
task_id TEXT NOT NULL REFERENCES cowork_tasks(id),
turn INTEGER NOT NULL,
state_json JSONB NOT NULL
);
-- Indexes
CREATE INDEX idx_cowork_tasks_coworkspace ON cowork_tasks(coworkspace_slug);
CREATE INDEX idx_cowork_tasks_status ON cowork_tasks(status);
CREATE INDEX idx_phases_task ON task_phases(task_id);
CREATE INDEX idx_agents_task ON sub_agents(task_id);
CREATE INDEX idx_checkpoints_task ON task_checkpoints(task_id, turn DESC);
No need to tear everything down. Cowork is additive to Chat, not a replacement:
CoworkspaceManager + file CRUD at /coworkspaces/{slug}/CoworkTask data model and API endpoints.instructions.md supportThroughout this process, your existing Chat API remains completely untouched. Users can use both modes simultaneously.
| Decision | Rationale |
|---|---|
| Coworkspace = file folder | Files are the most natural medium of exchange between human and agent |
| Slug-based routing | Human-readable paths (/coworkspaces/downloads-organizer/), no opaque IDs |
coworkspaces directory |
Avoids conflicts with existing workspaces naming in the codebase |
cowork_* / coworkspace_* prefix |
Clean namespace isolation for all tables, functions, and routes |
| Plan-first execution | Transparency + user can course-correct before work begins |
| Disposable sub-agents | Independent contexts, communicate via files — no context pollution |
| Checkpoint every turn | Crash recovery, long-running task resumability |
| Configurable approval | ask_before (safe) vs act_autonomously (fast); file deletion always requires approval |
| Chat stays unchanged | Cowork is addition, not substitution — use the right tool for the job |
Claude Cowork doesn't mean you need to replace your backend. It represents a shift in architectural thinking:
Your existing Go backend already has Chat, Schedules, Goals, Files, and Direct Tasks — these are the perfect foundation for the Cowork pattern. Add an Agent Loop, a Sub-Agent Orchestrator, and Coworkspace management, and your LLM agent transforms from a passive conversation partner into a team member that independently delivers finished work.