Server & API Module
apps/server/server/ — HTTP server, apps/server/trpc/ — tRPC procedures, apps/server/coordination/ — merge queue.
HTTP Server
Framework: Native node:http (no Express/Fastify)
Default: 127.0.0.1:3847
PID file: ~/.cw/server.pid
Routes
| Route |
Method |
Purpose |
/health |
GET |
Health check ({ status, uptime, processCount }) |
/status |
GET |
Full server status with process list |
/trpc/* |
POST |
All tRPC procedure calls |
Lifecycle
CoordinationServer.start() — checks PID file, creates HTTP server, emits server:started
CoordinationServer.stop() — emits server:stopped, closes server, removes PID file
GracefulShutdown handles SIGTERM/SIGINT/SIGHUP with 10s timeout
tRPC Adapter
trpc-adapter.ts converts node:http IncomingMessage/ServerResponse to fetch Request/Response for tRPC. Subscriptions stream via ReadableStream bodies (SSE).
tRPC Context
All procedures share a context with optional dependencies:
Each procedure uses require*Repository(ctx) helpers that throw TRPCError(INTERNAL_SERVER_ERROR) if a dependency is missing.
Procedure Reference
System
| Procedure |
Type |
Description |
| health |
query |
Health check with uptime |
| status |
query |
Server status with process list |
| systemHealthCheck |
query |
Account, agent, project health |
Agents
| Procedure |
Type |
Description |
| spawnAgent |
mutation |
Spawn new agent (taskId, prompt, provider, mode) |
| stopAgent |
mutation |
Stop agent by name or ID |
| deleteAgent |
mutation |
Delete agent and clean up worktree |
| dismissAgent |
mutation |
Dismiss agent (set userDismissedAt) |
| resumeAgent |
mutation |
Resume with answers |
| listAgents |
query |
All agents |
| getAgent |
query |
Single agent by name or ID; also returns taskName, initiativeName, exitCode |
| getAgentResult |
query |
Execution result |
| getAgentQuestions |
query |
Pending questions |
| getAgentOutput |
query |
Timestamped log chunks from DB ({ content, createdAt }[]) |
| getTaskAgent |
query |
Most recent agent assigned to a task (by taskId) |
| getAgentInputFiles |
query |
Files written to agent's .cw/input/ dir (text only, sorted, 500 KB cap) |
| getAgentPrompt |
query |
Assembled prompt — reads from DB (agents.prompt) first; falls back to .cw/agent-logs/<name>/PROMPT.md for pre-persistence agents (1 MB cap) |
| getActiveRefineAgent |
query |
Active refine agent for initiative |
| getActiveConflictAgent |
query |
Active conflict resolution agent for initiative (name starts with conflict-) |
| listWaitingAgents |
query |
Agents waiting for input — returns AgentWithContext[] enriched with taskName, phaseName, initiativeName, taskDescription via SQL LEFT JOINs |
| listForRadar |
query |
Radar page: per-agent metrics (questionsCount, messagesCount, subagentsCount, compactionsCount) with time/status/mode/initiative filters |
| getCompactionEvents |
query |
Compaction events for one agent: {agentId} → {timestamp, sessionNumber}[] (cap 200) |
| getSubagentSpawns |
query |
Subagent spawn events for one agent: {agentId} → {timestamp, description, promptPreview, fullPrompt}[] (cap 200) |
| getQuestionsAsked |
query |
AskUserQuestion tool calls for one agent: {agentId} → {timestamp, questions[]}[] (cap 200) |
| onAgentOutput |
subscription |
Live raw JSONL output stream via EventBus |
Tasks
| Procedure |
Type |
Description |
| listTasks |
query |
Child tasks of parent |
| getTask |
query |
Single task |
| updateTaskStatus |
mutation |
Change task status |
| updateTask |
mutation |
Update task fields (name, description) |
| createInitiativeTask |
mutation |
Create task on initiative |
| createPhaseTask |
mutation |
Create task on phase |
| listInitiativeTasks |
query |
All tasks for initiative |
| listPhaseTasks |
query |
All tasks for phase |
| deleteTask |
mutation |
Delete a task by ID |
| listPhaseTaskDependencies |
query |
All task dependency edges for tasks in a phase |
| listInitiativeTaskDependencies |
query |
All task dependency edges for tasks in an initiative |
Initiatives
| Procedure |
Type |
Description |
| createInitiative |
mutation |
Create with optional branch/projectIds/description, auto-creates root page (seeded with description); if description provided, auto-spawns refine agent |
| listInitiatives |
query |
Filter by status and/or projectId; returns activity (state, activePhase, phase counts) computed from phases |
| getInitiative |
query |
With projects array |
| updateInitiative |
mutation |
Name, status |
| deleteInitiative |
mutation |
Cascade delete initiative and all children |
| updateInitiativeConfig |
mutation |
executionMode, branch, qualityReview |
| getInitiativeReviewDiff |
query |
Full diff of initiative branch vs project default branch |
| getInitiativeReviewCommits |
query |
Commits on initiative branch not on default branch |
| getInitiativeCommitDiff |
query |
Single commit diff for initiative review |
| approveInitiativeReview |
mutation |
Approve initiative review: {initiativeId, strategy: 'push_branch' | 'merge_and_push'} |
| requestInitiativeChanges |
mutation |
Request changes on initiative: {initiativeId, summary} → creates review task in Finalization phase, resets initiative to active |
| checkInitiativeMergeability |
query |
Dry-run merge check: {initiativeId} → {mergeable, conflictFiles[], targetBranch} |
| spawnConflictResolutionAgent |
mutation |
Spawn agent to resolve merge conflicts: {initiativeId, provider?} → auto-dismisses stale conflict agents, creates merge task |
Phases
| Procedure |
Type |
Description |
| createPhase |
mutation |
Create in initiative |
| listPhases |
query |
By initiative |
| getPhase |
query |
Single phase |
| updatePhase |
mutation |
Name, content, status |
| approvePhase |
mutation |
Validate and approve |
| deletePhase |
mutation |
Cascade delete |
| createPhasesFromPlan |
mutation |
Bulk create from agent output |
| createPhaseDependency |
mutation |
Add dependency edge |
| removePhaseDependency |
mutation |
Remove dependency edge |
| listInitiativePhaseDependencies |
query |
All dependency edges |
| getPhaseDependencies |
query |
What this phase depends on |
| getPhaseDependents |
query |
What depends on this phase |
| getPhaseReviewDiff |
query |
File-level metadata for pending_review phase: {phaseName, sourceBranch, targetBranch, files: FileStatEntry[], totalAdditions, totalDeletions} — no hunk content. Results are cached in-memory by phaseId:headHash (TTL: REVIEW_DIFF_CACHE_TTL_MS, default 5 min). Cache is invalidated when a task merges into the phase branch. |
| getFileDiff |
query |
Per-file unified diff on demand: {phaseId, filePath, projectId?} → {binary: boolean, rawDiff: string}; filePath must be URL-encoded; binary files return {binary: true, rawDiff: ''} |
| getPhaseReviewCommits |
query |
List commits between initiative and phase branch |
| getCommitDiff |
query |
Diff for a single commit (by hash) in a phase |
| approvePhaseReview |
mutation |
Approve and merge phase branch |
| requestPhaseChanges |
mutation |
Request changes: creates revision task from unresolved threaded comments (with [comment:ID] tags and reply threads), resets phase to in_progress |
| listReviewComments |
query |
List review comments by phaseId (flat list including replies, frontend groups by parentCommentId) |
| createReviewComment |
mutation |
Create inline review comment on diff |
| resolveReviewComment |
mutation |
Mark review comment as resolved |
| unresolveReviewComment |
mutation |
Mark review comment as unresolved |
| replyToReviewComment |
mutation |
Create a threaded reply to an existing review comment (copies parent's phaseId/filePath/lineNumber) |
Phase Dispatch
| Procedure |
Type |
Description |
| queuePhase |
mutation |
Queue approved phase |
| queueAllPhases |
mutation |
Queue all approved phases for initiative |
| dispatchNextPhase |
mutation |
Start next ready phase |
| getPhaseQueueState |
query |
Queue state |
| createChildTasks |
mutation |
Create tasks from detail parent |
Architect (High-Level Agent Spawning)
| Procedure |
Type |
Description |
| spawnArchitectDiscuss |
mutation |
Discussion agent |
| spawnArchitectPlan |
mutation |
Plan agent (generates phases). Passes initiative context (phases, execution tasks only, pages) |
| spawnArchitectRefine |
mutation |
Refine agent (generates proposals) |
| spawnArchitectDetail |
mutation |
Detail agent (generates tasks). Passes initiative context (phases, execution tasks only, pages) |
Dispatch
| Procedure |
Type |
Description |
| queueTask |
mutation |
Add task to dispatch queue |
| dispatchNext |
mutation |
Dispatch next ready task |
| getQueueState |
query |
Queue state |
| completeTask |
mutation |
Complete task |
Coordination (Merge Queue)
| Procedure |
Type |
Description |
| queueMerge |
mutation |
Queue task for merge |
| processMerges |
mutation |
Process merge queue |
| getMergeQueueStatus |
query |
Queue state |
| getNextMergeable |
query |
Next ready-to-merge task |
Projects
| Procedure |
Type |
Description |
| registerProject |
mutation |
Clone git repo, create record. Validates defaultBranch exists in repo |
| listProjects |
query |
All projects |
| getProject |
query |
Single project |
| updateProject |
mutation |
Update project settings (defaultBranch). Validates branch exists in repo |
| deleteProject |
mutation |
Delete clone and record |
| getInitiativeProjects |
query |
Projects for initiative |
| updateInitiativeProjects |
mutation |
Sync junction table |
| syncProject |
mutation |
git fetch + ff-only merge of defaultBranch, updates lastFetchedAt |
| syncAllProjects |
mutation |
Sync all registered projects |
| getProjectSyncStatus |
query |
Returns { ahead, behind, lastFetchedAt } for a project |
Pages
| Procedure |
Type |
Description |
| getRootPage |
query |
Auto-creates if missing |
| getPage |
query |
Single page |
| getPageUpdatedAtMap |
query |
Bulk updatedAt check |
| listPages |
query |
By initiative |
| listChildPages |
query |
By parent page |
| createPage |
mutation |
Create, emit page:created |
| updatePage |
mutation |
Title/content/sortOrder, emit page:updated |
| deletePage |
mutation |
Delete, emit page:deleted |
Accounts
| Procedure |
Type |
Description |
| listAccounts |
query |
All accounts |
| addAccount |
mutation |
Create account |
| removeAccount |
mutation |
Delete account |
| refreshAccounts |
mutation |
Clear expired exhaustion |
| updateAccountAuth |
mutation |
Update credentials |
| markAccountExhausted |
mutation |
Set exhaustion timer |
| listProviderNames |
query |
Available provider names |
| addAccountByToken |
mutation |
Upsert account from OAuth token; returns { upserted, account } |
Proposals
| Procedure |
Type |
Description |
| listProposals |
query |
By agent or initiative |
| acceptProposal |
mutation |
Apply side effects, auto-dismiss agent |
| dismissProposal |
mutation |
Dismiss, auto-dismiss agent |
| acceptAllProposals |
mutation |
Bulk accept with error collection |
| dismissAllProposals |
mutation |
Bulk dismiss |
Subscriptions (SSE)
| Procedure |
Type |
Events |
| onEvent |
subscription |
All event types |
| onAgentUpdate |
subscription |
agent:* events (7 types, excludes agent:output) |
| onTaskUpdate |
subscription |
task:* + phase:* events (8 types) |
| onPageUpdate |
subscription |
page:created/updated/deleted |
| onPreviewUpdate |
subscription |
preview:building/ready/stopped/failed |
| onConversationUpdate |
subscription |
conversation:created/answered |
Subscriptions use eventBusIterable() — queue-based async generator, max 1000 events, 30s heartbeat. agent:output is excluded from all general subscriptions (it's high-frequency streaming data); use the dedicated onAgentOutput subscription instead.
Coordination Module
apps/server/coordination/ manages merge queue:
- CoordinationManager port:
queueMerge, getNextMergeable, processMerges, handleConflict, getQueueState
- DefaultCoordinationManager adapter: in-memory queue, dependency-ordered processing
- ConflictResolutionService: creates resolution tasks for merge conflicts
- Merge flow: queue → check deps → merge via WorktreeManager → handle conflicts
- Events:
merge:queued, merge:started, merge:completed, merge:conflicted
Preview Procedures
Docker-based preview deployments. No database table — Docker is the source of truth.
| Procedure |
Type |
Description |
startPreview |
mutation |
Start preview: {initiativeId, phaseId?, projectId, branch} → PreviewStatus |
stopPreview |
mutation |
Stop preview: {previewId} |
listPreviews |
query |
List active previews: {initiativeId?} → PreviewStatus[] |
getPreviewStatus |
query |
Get preview status: {previewId} → PreviewStatus |
Context dependency: requirePreviewManager(ctx) — requires PreviewManager from container.
Conversation Procedures
Inter-agent communication for parallel agents.
| Procedure |
Type |
Description |
createConversation |
mutation |
Ask a question: {fromAgentId, toAgentId?, phaseId?, taskId?, question} → Conversation |
getPendingConversations |
query |
Poll for incoming questions: {agentId} → Conversation[] |
getConversation |
query |
Get conversation by ID: {id} → Conversation |
answerConversation |
mutation |
Answer a conversation: {id, answer} → Conversation |
getByFromAgent |
query |
Radar drilldown: all conversations sent by agent: {agentId} → {id, timestamp, toAgentName, toAgentId, question, answer, status, taskId, phaseId}[] (cap 200) |
Target resolution: toAgentId → direct; taskId → find running agent by task; phaseId → find running agent by any task in phase.
Context dependency: requireConversationRepository(ctx), requireAgentManager(ctx).
Chat Session Procedures
Persistent chat loop for iterative phase/task refinement via agent.
| Procedure |
Type |
Description |
sendChatMessage |
mutation |
Send message: {targetType, targetId, initiativeId, message, provider?} → {sessionId, agentId, action} |
getChatSession |
query |
Get active session with messages: {targetType, targetId} → ChatSession | null |
closeChatSession |
mutation |
Close session and dismiss agent: {sessionId} → {success} |
sendChatMessage finds or creates an active session, stores the user message, then either resumes the existing agent (if waiting_for_input) or spawns a fresh one with full chat history + initiative context. Agent runs in 'chat' mode and signals "questions" after applying changes, staying alive for the next message.
Context dependency: requireChatSessionRepository(ctx), requireAgentManager(ctx), requireInitiativeRepository(ctx), requireTaskRepository(ctx).
Headquarters Procedures
Composite dashboard query aggregating all action items that require user intervention.
| Procedure |
Type |
Description |
getHeadquartersDashboard |
query |
Returns 6 typed arrays of action items (no input required) |
Return Shape
Each array is sorted ascending by timestamp (oldest-first). All timestamps are ISO 8601 strings. lastMessage is truncated to 160 chars and is null when no messages exist or the message repository is not wired.
Context dependency: requireInitiativeRepository(ctx), requirePhaseRepository(ctx), requireAgentManager(ctx). Task/message repos are accessed via optional ctx fields for blockedPhases.lastMessage.