Database schema
The authoritative source
Section titled “The authoritative source”backend/prisma/schema.prisma. ~1,400 lines. Read it. This page is
a map, not a reference.
Tables by concern
Section titled “Tables by concern”Tenancy + auth
Section titled “Tenancy + auth”Tenant— the top-level organisation.User— one per person; tenant-scoped.Invitation— pending invites.GoogleOAuthToken— Google integration tokens.AgentToken— tokens issued to agent daemons.
Product scope
Section titled “Product scope”Project— the primary organisational container.Goal— outcomes tied to projects.
Capture + briefs
Section titled “Capture + briefs”Meeting— a meeting session.Transcript— versioned transcript per meeting.PRD— the “brief” table.PRDSymbolLink— code-symbol references from approved briefs.
Orchestration
Section titled “Orchestration”ExecutionPlan— the chief-of-staff’s plans.Ticket— specialist work items; children of a chief-of-staff ticket.AgentTask— legacy, being retired; see DEFERRED.md.AgentJob— BullMQ-adjacent log of agent-daemon jobs.Approval— attributed approval decisions.
Knowledge base
Section titled “Knowledge base”Skill— tenant-scoped legacy skills (user-invokable).SkillPackage— vendored global skills (nullable-tenantId).SubagentDefinition— vendored subagents.
Integrations
Section titled “Integrations”IntegrationConnection— credentials + status per integration.ChannelRoute— per-project comms-channel overrides.
Project graph
Section titled “Project graph”ProjectGraph— one row per project. Stores the serialized graph- metadata.
Observability
Section titled “Observability”AuditLog— structured audit trail.ApiUsage— per-call AI provider usage for cost tracking.Notification,NotificationSubscription— notification preferences + delivery log.
WhisperTranscription— Whisper job metadata for uploaded recordings.
Scheduling
Section titled “Scheduling”ScheduledJob— cron-style jobs (digests, health probes).
Credential vault (encrypted-at-rest)
Section titled “Credential vault (encrypted-at-rest)”CredentialVault— any tenant-scoped encrypted blob.
Naming conventions
Section titled “Naming conventions”- snake_case table names via
@@map("my_table"). - camelCase field names.
idis alwayscuid.createdAt/updatedAton every mutable table.tenantIdon every tenant-scoped table.- Explicit indexes on
(tenantId, projectId),(tenantId, userId), etc.
Key constraints
Section titled “Key constraints”ProjectGraph.projectIdis unique. One graph per project. A re-build upserts on the same row.ExecutionPlan.parentTicketId, attemptcomposite index. Fast lookup of “all plans for this ticket, newest first.”Ticket.parentTicketIdnullable. Top-level tickets (thechief_of_staffrow) have no parent. Child tickets always do.
Soft delete
Section titled “Soft delete”Not used. Rows are deleted (respecting FK cascades). Audit log retains the delete event for compliance.
Migrations
Section titled “Migrations”Timestamped Prisma migrations under backend/prisma/migrations/.
Every schema change lands a migration file:
20260423044620_pg_execution_plan_graph_hash/ migration.sql- Migrations are checked in and reviewed in the PR that introduces them.
- Forward-only; no down-migrations. To revert, write a new forward migration that reverses the change.
- Breaking migrations (renaming columns, dropping tables) come with a changelog note + a “you must stop traffic” warning.
Entity relationship sketch (simplified)
Section titled “Entity relationship sketch (simplified)”Tenant 1─N Project 1─N Goal │ ├──N Meeting 1─N Transcript │ │ │ └─ WhisperTranscription │ ├──N PRD (brief) ─N ExecutionPlan ─N Ticket │ │ │ └─N AgentJob │ └──1 ProjectGraph ─ N GraphNode / GraphEdge (serialized in graphJson)
Tenant 1─N User 1─N Approval │ ├─ IntegrationConnection ├─ AgentToken └─ ApiUsageQuery-time hot paths
Section titled “Query-time hot paths”Optimised indexes for:
- “Activity feed for this project” —
(tenantId, projectId, createdAt)onTicket. - “Who’s approved what” —
(tenantId, actor, createdAt)onAuditLog. - “Latest graph for project” — unique index on
ProjectGraph.projectId. - “Webhook refresh lookup” —
(repoLabel)onProjectGraph.
Data growth reality
Section titled “Data growth reality”Rough growth per brief:
- 1
PRDrow. - 1–3
ExecutionPlanrows (inc. replans). - 2–6
Ticketrows per plan. - 5–15
AuditLogrows. - 5–20
ApiUsagerows (one per LLM call).
~50 rows / brief + transcript. A team doing 10 briefs / day produces ~5 MB / day in the DB. Comfortable for years on a $15/mo managed Postgres.
When to denormalize
Section titled “When to denormalize”We already do — ProjectGraph.nodeCount / edgeCount / languages
are denormalised for dashboard reads. Reach for denorm when a read is
in a hot path and the join cost shows up in traces.