Decomposed "Foundation Setup - Install Dependencies & Configure Tailwind" phase into 6 executable tasks: 1. Install Tailwind CSS, PostCSS & Autoprefixer 2. Map MUI theme to Tailwind design tokens 3. Setup CSS variables for dynamic theming 4. Install Radix UI primitives 5. Initialize shadcn/ui and setup component directory 6. Move MUI to devDependencies and verify setup Tasks follow logical dependency chain with final human verification checkpoint before proceeding with component migration. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
6.9 KiB
6.9 KiB
Database Module
src/db/ — SQLite database via better-sqlite3 + Drizzle ORM with hexagonal architecture.
Architecture
- Schema:
src/db/schema.ts— all tables, columns, relations - Ports (interfaces):
src/db/repositories/*.ts— 10 repository interfaces - Adapters (implementations):
src/db/repositories/drizzle/*.ts— 10 Drizzle adapters - Barrel exports:
src/db/index.tsre-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' | 'completed' | 'archived', default 'active' |
| mergeRequiresApproval | integer/boolean | default true |
| mergeTarget | text nullable | target branch for merges |
| 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' | 'breakdown' | 'decompose' | '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 |
| 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' | 'breakdown' | 'decompose' | '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 | |
| 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 |
| 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.
Repository Interfaces
10 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 |
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 generateto create migrations - See database-migrations.md for full workflow
- Snapshots stale after 0008; migrations 0008+ are hand-written
Current migrations: 0000 through 0018 (19 total).