Files
Codewalkers/docs/database.md

9.7 KiB

Database Module

apps/server/db/ — SQLite database via better-sqlite3 + Drizzle ORM with hexagonal architecture.

Architecture

  • Schema: apps/server/db/schema.ts — all tables, columns, relations
  • Ports (interfaces): apps/server/db/repositories/*.ts — 13 repository interfaces
  • Adapters (implementations): apps/server/db/repositories/drizzle/*.ts — 13 Drizzle adapters
  • Barrel exports: apps/server/db/index.ts re-exports everything

All adapters use nanoid() for IDs, auto-manage timestamps, and use Drizzle's .returning() for atomic reads after writes.

Tables

initiatives

Column Type Notes
id text PK nanoid
name text NOT NULL
status text enum 'active' | 'pending_review' | 'completed' | 'archived', default 'active'
mergeRequiresApproval integer/boolean default true
branch text nullable auto-generated initiative branch (e.g., 'cw/user-auth')
createdAt, updatedAt integer/timestamp

phases

Column Type Notes
id text PK
initiativeId text FK → initiatives (cascade)
name text NOT NULL
content text nullable Tiptap JSON
status text enum 'pending' | 'approved' | 'in_progress' | 'completed' | 'blocked'
createdAt, updatedAt integer/timestamp

phase_dependencies

phaseId FK → phases, dependsOnPhaseId FK → phases. Both cascade delete.

tasks

Column Type Notes
id text PK
phaseId text nullable FK → phases (cascade)
initiativeId text nullable FK → initiatives (cascade)
parentTaskId text nullable self-ref FK (cascade) decomposition hierarchy
name text NOT NULL
description text nullable
type text enum 'auto' | 'checkpoint:human-verify' | 'checkpoint:decision' | 'checkpoint:human-action'
category text enum 'execute' | 'research' | 'discuss' | 'plan' | 'detail' | 'refine' | 'verify' | 'merge' | 'review'
priority text enum 'low' | 'medium' | 'high'
status text enum 'pending_approval' | 'pending' | 'in_progress' | 'completed' | 'blocked'
requiresApproval integer/boolean nullable null = inherit from initiative
order integer default 0
summary text nullable Agent result summary — propagated to dependent tasks as context
createdAt, updatedAt integer/timestamp

task_dependencies

taskId FK → tasks, dependsOnTaskId FK → tasks. Both cascade delete.

agents

Column Type Notes
id text PK
name text NOT NULL UNIQUE human-readable alias (adjective-animal)
taskId text nullable FK → tasks (set null)
initiativeId text nullable FK → initiatives (set null)
sessionId text nullable CLI session ID for resume
worktreeId text NOT NULL path to agent's git worktree
provider text NOT NULL default 'claude'
accountId text nullable FK → accounts (set null)
status text enum 'idle' | 'running' | 'waiting_for_input' | 'stopped' | 'crashed'
mode text enum 'execute' | 'discuss' | 'plan' | 'detail' | 'refine'
pid integer nullable OS process ID
exitCode integer nullable
outputFilePath text nullable
result text nullable JSON
pendingQuestions text nullable JSON
userDismissedAt integer/timestamp nullable
createdAt, updatedAt integer/timestamp

accounts

Column Type Notes
id text PK
email text NOT NULL
provider text NOT NULL default 'claude'
configJson text nullable serialized .claude.json
credentials text nullable serialized .credentials.json
isExhausted integer/boolean default false
exhaustedUntil integer/timestamp nullable
lastUsedAt integer/timestamp nullable round-robin scheduling
sortOrder integer
createdAt, updatedAt integer/timestamp

proposals

Column Type Notes
id text PK
agentId text FK → agents (cascade)
initiativeId text FK → initiatives (cascade)
targetType text enum 'page' | 'phase' | 'task'
targetId text nullable existing entity ID, null for creates
title, summary, content text markdown body
metadata text nullable JSON
status text enum 'pending' | 'accepted' | 'dismissed'
sortOrder integer
createdAt, updatedAt integer/timestamp

pages

Column Type Notes
id text PK
initiativeId text FK → initiatives (cascade)
parentPageId text nullable self-ref FK (cascade) root page has NULL
title text NOT NULL
content text nullable Tiptap JSON
sortOrder integer
createdAt, updatedAt integer/timestamp

projects

Column Type Notes
id text PK
name text NOT NULL UNIQUE
url text NOT NULL UNIQUE git repo URL
defaultBranch text NOT NULL default 'main'
lastFetchedAt integer/timestamp nullable, updated by ProjectSyncManager
createdAt, updatedAt integer/timestamp

initiative_projects (junction)

initiativeId + projectId with unique index. Both FK cascade.

messages

Self-referencing (parentMessageId) for threading. Sender/recipient types: 'agent' | 'user'.

agent_log_chunks

Column Type Notes
id text PK
agentId text NOT NULL NO FK — survives agent deletion
agentName text NOT NULL snapshot for display
sessionNumber integer spawn=1, resume=prev+1
content text NOT NULL raw JSONL chunk
createdAt integer/timestamp

Index on agentId for fast queries.

conversations

Inter-agent communication records.

Column Type Notes
id text PK
fromAgentId text NOT NULL FK→agents ON DELETE CASCADE
toAgentId text NOT NULL FK→agents ON DELETE CASCADE
initiativeId text FK→initiatives ON DELETE SET NULL
phaseId text FK→phases ON DELETE SET NULL
taskId text FK→tasks ON DELETE SET NULL
question text NOT NULL
answer text nullable until answered
status text enum pending, answered
createdAt integer/timestamp
updatedAt integer/timestamp

Indexes: (toAgentId, status) for listen polling, (fromAgentId).

chat_sessions

Persistent chat sessions for iterative refinement of phases/tasks.

Column Type Notes
id text PK nanoid
targetType text enum 'phase' | 'task'
targetId text NOT NULL phase or task ID
initiativeId text FK → initiatives (cascade)
agentId text FK → agents (set null) linked agent
status text enum 'active' | 'closed', default 'active'
createdAt, updatedAt integer/timestamp

Indexes: (targetType, targetId), (initiativeId).

chat_messages

Messages within a chat session.

Column Type Notes
id text PK nanoid
chatSessionId text FK → chat_sessions (cascade)
role text enum 'user' | 'assistant' | 'system'
content text NOT NULL
changeSetId text FK → change_sets (set null) links assistant messages to applied changes
createdAt integer/timestamp

Index: (chatSessionId).

review_comments

Inline review comments on phase diffs, persisted across page reloads.

Column Type Notes
id text PK nanoid
phaseId text FK → phases (cascade) scopes comment to phase
filePath text NOT NULL file in diff
lineNumber integer NOT NULL line number (new-side or old-side for deletions)
lineType text enum 'added' | 'removed' | 'context'
body text NOT NULL comment text
author text NOT NULL default 'you'
resolved integer/boolean default false
createdAt, updatedAt integer/timestamp

Index: (phaseId).

Repository Interfaces

13 repositories, each with standard CRUD plus domain-specific methods:

Repository Key Methods
InitiativeRepository create, findById, findAll, findByStatus, update, delete
PhaseRepository + createDependency, getDependencies, getDependents, findByInitiativeId
TaskRepository + findByParentTaskId, findByPhaseId, findPendingApproval, createDependency
AgentRepository + findByName, findByTaskId, findBySessionId, findByStatus
MessageRepository + findPendingForUser, findRequiringResponse, findReplies
PageRepository + findRootPage, getOrCreateRootPage, findByParentPageId
ProjectRepository + junction ops: setInitiativeProjects (diff-based), findProjectsByInitiativeId
AccountRepository + findNextAvailable (round-robin), markExhausted, clearExpiredExhaustion
ProposalRepository + findByAgentIdAndStatus, updateManyByAgentId, countByAgentIdAndStatus
LogChunkRepository insertChunk, findByAgentId, deleteByAgentId, getSessionCount
ConversationRepository create, findById, findPendingForAgent, answer
ChatSessionRepository createSession, findActiveSession, findActiveSessionByAgentId, updateSession, createMessage, findMessagesBySessionId
ReviewCommentRepository create, findByPhaseId, resolve, unresolve, delete

Migrations

Located in drizzle/. Applied via ensureSchema() on startup using Drizzle's migrate().

Key rules:

  • Never use raw SQL for schema initialization
  • Run npx drizzle-kit generate to create migrations
  • See database-migrations.md for full workflow
  • Snapshots stale after 0008; migrations 0008+ are hand-written

Current migrations: 0000 through 0028 (29 total).