Skip to main content
The /live OS runs on Neon Postgres. Schema lives in src/lib/server/db-schema.ts as a list of idempotent (IF NOT EXISTS) statements, applied via the gated POST /api/admin/db-setup endpoint.

Core principle: derive, don’t store

Customers and orders are the editable source of truth. Analytics (LTV, totals, reorder cadence, revenue) are computed in queries, never stored, so they stay correct as data is edited. total === subtotal is deliberate where there is no delivery-fee data; no numbers are fabricated.

Tables

Order status vocabulary

The OrderStatus union (src/lib/types.ts):
pending_payment is a DB-only status used for unpaid SMS-bot drafts. It is deliberately not in the OrderStatus type and is excluded by the live-data loaders, so a pending order is invisible to the orders list, revenue, LTV, and clinic-notify until payment commits it (onInvoicePaidpaid). This is the structural core of the payment gate.
“Awaiting fulfillment” (the dashboard triage card and ?status=awaiting) is defined in compute.ts as status === "requested" || status === "invoiced".

Bot conversation memory

bot_conversations archives every SMS thread with a denormalized search_text and a GIN full-text index (to_tsvector('english', ...)). The bot retrieves relevant past conversations as in-context guidance, retrieval memory, not model retraining. golden = true marks human-curated exemplars that are always surfaced first; non-golden rows surface only on an FTS keyword match. No embeddings provider is used (pgvector + embeddings is the documented upgrade path).

Migrations

All schema statements are additive and idempotent. Running db-setup against a populated DB is a no-op for existing rows. New finance tables seed empty (no fabricated COGS/payroll/goals). See Admin Runbooks for the exact seeding procedure and its sharp edges.