PlanetScale
Role in the system
Section titled “Role in the system”PlanetScale is the single database for all persistent state — replacing Supabase PostgreSQL. As an EngineeredSystem (IOF), it stores Information Bearing Entities carrying all persistent information: Process Records (commercial transactions), MaterialArtifact data, Measurement Data, ICEs (tags, assets, suggestions), Plan Specifications, Creative Act Reports, and Document Artifacts.
Connection: Hyperdrive
Section titled “Connection: Hyperdrive”Cloudflare Hyperdrive provides optimized connectivity between Workers and PlanetScale:
- Connection pooling — maintains persistent connections, avoiding per-request TCP/TLS setup
- Query caching — optional, for read-heavy dashboard queries
- Edge proximity — connections routed through Cloudflare’s network
[[hyperdrive]]binding = "HYPERDRIVE"id = "your-hyperdrive-id"import { drizzle } from 'drizzle-orm/node-postgres';
export function getDb(env: Bindings) { return drizzle(env.HYPERDRIVE.connectionString);}ORM: Drizzle (PostgreSQL dialect)
Section titled “ORM: Drizzle (PostgreSQL dialect)”The rewrite continues using Drizzle ORM with the PostgreSQL dialect — same dialect as the current Supabase system, now targeting PlanetScale Postgres. Table and column names follow the ontological naming conventions:
import { pgTable, varchar, integer, timestamp, jsonb, boolean, text } from 'drizzle-orm/pg-core';
// MaterialArtifact (IOF) — physical products with causal unityexport const materialArtifact = pgTable('material_artifact', { artifactIdentifier: varchar('artifact_identifier', { length: 64 }).primaryKey(), title: text('title'), tags: jsonb('tags').$type<string[]>(), isActive: boolean('is_active').notNull().default(true), lastSeen: timestamp('last_seen', { withTimezone: true }).notNull().defaultNow(),});
// Measurement Datum (IAO) — daily sales on ratio scaleexport const salesMeasurementDataset = pgTable('sales_measurement_dataset', { artifactIdentifier: varchar('artifact_identifier', { length: 64 }).notNull(), day: date('day').notNull(), units: integer('units').notNull().default(0), unitsNet: integer('units_net').notNull().default(0),});See Schema Design for the complete table definitions.
Safety limits
Section titled “Safety limits”PlanetScale enforces system-level constraints that shape all pipeline ProcedureExecutions:
| Limit | Value | Design response |
|---|---|---|
| Rows per query | 100k | Cursor-based pagination |
| Rows per statement | 100k | Chunked batch writes |
| Result size | 64 MiB | Selective column queries |
| Transaction timeout | 20s | Short, independent transactions |
| Autocommit timeout | 900s | Bounded single statements |
Schema migration workflow
Section titled “Schema migration workflow”PlanetScale branching supports safe schema changes:
- Create dev branch from production
- Apply migrations via dbmate on the dev branch
- Create deploy request (PR-like review for schema changes)
- Merge — PlanetScale applies non-blocking DDL
- Rollback available (undo deployment without data loss)
# Migration workflow with dbmatedbmate new add_document_artifact_table# Edit migration SQLdbmate up # apply on dev branch# Create deploy request in PlanetScale dashboardForeign keys
Section titled “Foreign keys”Per ADR-004, foreign keys are documented in the Drizzle schema for type safety but not enforced at the database level. This parallels ODR-002 — ontological relations (like IAO denotes between ICEs and MaterialArtifacts) are documented in naming conventions, not enforced by database constraints.
Application code enforces integrity, with periodic orphan detection in background ProcedureExecutions.
No stored procedures
Section titled “No stored procedures”Per ADR-003, all business logic lives in application code. The 10+ Supabase RPCs (get_tag_categories(), upsert_tag_classification(), refresh_tag_performance_aggregate(), etc.) are migrated to TypeScript in the Workers.