Trovella Wiki

Enums

PostgreSQL enum types used across the schema, their values, and conventions for adding new enums.

The schema uses PostgreSQL enum types for columns with a fixed set of valid values. Enums are preferred over plain text columns with check constraints because they provide type safety in both PostgreSQL and TypeScript, appear in Drizzle's generated types, and document valid values in the schema itself.

Enum Registry

Cross-Cutting Enums (enums.ts)

EnumValuesUsed by
tenantTypeEnumpersonal, family, companyorganization.type (via text column, not enum -- Better Auth compatibility)

Note: tenantTypeEnum is defined as a PostgreSQL enum but the organization.type column uses text().default("personal") instead of the enum type. This is because Better Auth manages the organization table schema and expects a text column. The enum exists for reference data validation in application code.

AI Domain Enums (ai.ts)

EnumValuesUsed by
aiProviderEnumanthropic, openai, googleaiModel.provider
aiPricingTypeEnuminput, output, cache_write, cache_read, batch_input, batch_output, batch_cache_write, batch_cache_read, long_context_input, long_context_output, tool_code_execution, tool_web_search, tool_web_fetchaiModelPricing.pricingType
aiPricingUnitEnumper_1m_tokens, per_call, per_houraiModelPricing.unit
aiBatchStatusEnumpending, processing, ended, canceling, canceled, expiredaiBatch.status
aiStopReasonEnumend_turn, max_tokens, stop_sequence, tool_use, pause_turn, refusal, erroraiUsage.stopReason

Research Domain Enums (research.ts)

EnumValuesUsed by
researchPlanStatusEnumplanning, executing, awaiting_review, stalled, completed, failedresearchPlan.status
planStepStatusEnumpending, in_progress, awaiting_input, completed, skipped, failedplanStep.status
planStepTypeEnumsearch, extract, analyze, critique, synthesize, checkpoint, customplanStep.stepType
planAuditEventTypeEnumstep_started, step_completed, step_failed, plan_modified, user_reviewed, session_resumed, skill_started, skill_completedplanAuditLog.eventType
extractionResultStatusEnumpending, completed, failed, needs_reviewextractionResult.status
skillExecutionStatusEnumstarted, executing, completed, failedskillExecution.status
artifactTypeEnumanalysis, synthesis, comparison, summary, source_list, findingresearchArtifact.artifactType

Search Domain Enums (search.ts)

EnumValuesUsed by
chunkSourceTableEnumresearch_artifact, research_output, extraction_resultdocumentChunk.sourceTable

Defining a New Enum

Enums are defined using Drizzle's pgEnum(), typically at the top of the schema file for the domain they belong to:

import { pgEnum } from "drizzle-orm/pg-core";

export const myStatusEnum = pgEnum("my_status", ["pending", "active", "completed", "failed"]);

Then use it in a table definition:

export const myTable = pgTable("my_table", {
  id: text("id").primaryKey(),
  status: myStatusEnum("status").notNull().default("pending"),
});

Naming Convention

  • Enum type names use snake_case in PostgreSQL: research_plan_status, ai_provider
  • Drizzle variable names use camelCase with Enum suffix: researchPlanStatusEnum, aiProviderEnum
  • Values are snake_case strings: "in_progress", "cache_write", "per_1m_tokens"

Where to Put New Enums

  • Domain-specific enums: In the domain's schema file (e.g., enums for a billing feature go in billing.ts)
  • Cross-cutting enums: In enums.ts (currently only tenantTypeEnum)

Do not create a new file for a single enum. The convention is to co-locate enums with the tables that use them.

Enum Migration Considerations

Adding a new enum type generates a CREATE TYPE statement in the migration. Adding a new value to an existing enum generates an ALTER TYPE ... ADD VALUE statement.

PostgreSQL limitations to be aware of:

  • Adding values is safe -- ALTER TYPE ... ADD VALUE is non-blocking
  • Removing values is not supported -- PostgreSQL does not allow removing values from an existing enum without recreating the type and all dependent columns
  • Renaming values is not supported natively -- requires creating a new enum, migrating data, and dropping the old one

Because of these limitations, be thoughtful about initial enum values. It is easy to add values later but expensive to remove or rename them.

When to Use Enums vs Text

Use a PostgreSQL enum when:

  • The value set is small and relatively stable (status codes, types, categories)
  • The values are used in WHERE clauses or CASE expressions
  • You want database-level validation that prevents invalid values

Use a plain text column when:

  • The value set is large or user-defined
  • The values change frequently
  • The column is managed by an external library (like Better Auth's role and type columns)
  • The values are free-form identifiers (like feature in aiUsage)

The organization.type and member.role columns are text instead of enums because Better Auth manages them and may add values in future versions.

On this page