Files
pig/packages/db/src/schema/supply.ts
T
karti d36762f264 Scaffold PIG and model the compute-GTM ontology
PIG is an agent-native CRM for two-sided AI-compute companies: businesses
that buy GPU capacity from providers and resell it. Their business is the
spread between two pipelines, which is precisely what a generic CRM cannot
represent.

The load-bearing decision is the `allocations` table, joining a
capacity_commitment (what we bought, at a known cost) to a demand_deal
(what we sold, at a known price). Margin, utilisation and idle capacity all
fall out of that one join. Cost is charged against the full commitment
rather than only the hours that sold, because unsold hours are already paid
for and any other treatment flatters a block that is losing money.

Domain decisions worth noting, each grounded in how this market operates:

- Demand stages put `legal` second, not last. Customers do not hand
  workloads to an infrastructure provider before paper is executed.
- Supply qualification splits technical from financial diligence, recorded
  attributably. Accepting capacity is a two-key decision.
- Capacity carries a time SHAPE (intervals + quantities), not a window.
  Commitments ramp and step down; a rectangle reports availability that
  does not exist in the month someone wants it.
- SLAs model three distinct shapes: none, a reliability tier plus credits
  policy, and a negotiated agreement. Aggregators generally cannot promise
  uptime on resold capacity, but negotiate heavyweight paper upstream.
  Remedies include fee abatement, which is materially better than a capped
  credit and is not expressible as one.
- Export control is a predicate on the allocation edge, evaluated against
  the ULTIMATE parent's jurisdiction. Country of incorporation is not a
  valid key, so this cannot live as a flag on an account.
- Agent-derived claims land in `facts` with a confidence band and evidence.
  Only verified claims self-apply; weaker ones await review.
- The API never calls the agent. It writes to a leased queue, guarded by a
  partial unique index on unfinished work.

Verified: typechecks clean, migration generates and applies to Postgres 16
(31 tables, 24 enums, 117 indexes).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 18:41:41 -07:00

387 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* The supply side: where capacity comes from, what it costs, and what we have
* committed to buy.
*
* This is the half of the business no generic CRM models at all. A supplier is
* not merely an account with a different label — it has physical sites, priced
* inventory that changes hourly, and commitments that keep costing money
* whether or not anyone bought the hours.
*/
import {
boolean,
index,
integer,
jsonb,
numeric,
pgTable,
text,
timestamp,
uniqueIndex,
uuid,
} from 'drizzle-orm/pg-core';
import {
gpuSocketEnum,
interconnectTypeEnum,
recordSourceEnum,
securityTierEnum,
stockStatusEnum,
supplyStageEnum,
} from './enums';
import { accounts, contacts } from './crm';
import { users } from './identity';
/**
* A physical facility. Distinct from the account that sells it, because one
* provider operates many sites with materially different characteristics, and
* because jurisdiction attaches to the building rather than the company.
*/
export const sites = pgTable(
'sites',
{
id: uuid('id').primaryKey().defaultRandom(),
accountId: uuid('account_id')
.notNull()
.references(() => accounts.id, { onDelete: 'cascade' }),
name: text('name').notNull(),
/** Provider's own datacenter identifier, for reconciliation. */
externalDataCenterId: text('external_data_center_id'),
country: text('country'),
countryCode: text('country_code'),
region: text('region'),
city: text('city'),
/**
* Export-control and data-residency jurisdiction. Some capacity legally
* cannot serve some customers, which makes this a matching constraint and
* not a note.
*/
jurisdiction: text('jurisdiction'),
/** Contracted power envelope. The real ceiling on how much can ever land here. */
powerMw: numeric('power_mw', { precision: 10, scale: 3 }),
/** Power usage effectiveness — a cost driver the supplier rarely volunteers. */
pue: numeric('pue', { precision: 4, scale: 2 }),
/** Compliance posture. Enterprise buyers gate on these during procurement. */
certifications: jsonb('certifications').$type<string[]>().notNull().default([]),
/** Observed reliability, maintained by hand or by the agent. */
uptimeHistoryPct: numeric('uptime_history_pct', { precision: 6, scale: 3 }),
notes: text('notes'),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(t) => [
index('sites_account_idx').on(t.accountId),
index('sites_country_idx').on(t.countryCode),
],
);
/**
* A priced offer of capacity.
*
* The field names and value shapes deliberately mirror the Prime Intellect
* availability API so that synced rows map across without translation, and so
* that a hand-entered listing from a provider who has no API is directly
* comparable with a synced one.
*
* Listings are volatile — prices and stock move hourly. They are a snapshot of
* what is purchasable, not a record of what we own; that is
* `capacityCommitments` below.
*/
export const inventoryListings = pgTable(
'inventory_listings',
{
id: uuid('id').primaryKey().defaultRandom(),
accountId: uuid('account_id').references(() => accounts.id, { onDelete: 'set null' }),
siteId: uuid('site_id').references(() => sites.id, { onDelete: 'set null' }),
/** Upstream identifiers, used to reconcile a sync without duplicating rows. */
externalCloudId: text('external_cloud_id'),
providerSlug: text('provider_slug'),
/** Kept as free text: new accelerators ship faster than enum migrations. */
gpuType: text('gpu_type').notNull(),
socket: gpuSocketEnum('socket'),
gpuCount: integer('gpu_count').notNull(),
gpuMemoryGb: integer('gpu_memory_gb'),
vcpu: integer('vcpu'),
memoryGb: integer('memory_gb'),
diskGb: integer('disk_gb'),
internetMbps: integer('internet_mbps'),
/**
* The field that decides whether this capacity can train or only serve.
* Ethernet-only hardware sold at a training price is the most common way to
* be overcharged in this market, so it is indexed and surfaced everywhere.
*/
interconnectGbps: integer('interconnect_gbps'),
interconnectType: interconnectTypeEnum('interconnect_type').notNull().default('Unknown'),
region: text('region'),
country: text('country'),
securityTier: securityTierEnum('security_tier').notNull().default('secure_cloud'),
stockStatus: stockStatusEnum('stock_status').notNull().default('Available'),
isSpot: boolean('is_spot').notNull().default(false),
/** Minutes from order to usable. Two orders of magnitude across the market. */
provisioningMinutes: integer('provisioning_minutes'),
/** Hours of prepaid time bundled with the listing, where offered. */
prepaidHours: numeric('prepaid_hours', { precision: 12, scale: 2 }),
/** Money in cents. Never floats — this feeds margin reporting. */
onDemandPriceCents: integer('on_demand_price_cents'),
communityPriceCents: integer('community_price_cents'),
priceIsVariable: boolean('price_is_variable').notNull().default(false),
currency: text('currency').notNull().default('USD'),
images: jsonb('images').$type<string[]>().notNull().default([]),
raw: jsonb('raw').$type<Record<string, unknown>>(),
source: recordSourceEnum('source').notNull().default('manual'),
/** When the upstream last confirmed this. Stale listings mislead sellers. */
observedAt: timestamp('observed_at', { withTimezone: true }).notNull().defaultNow(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(t) => [
/**
* Natural key for an upstream sync. A provider's inventory is identified by
* the cloud, the SKU, the socket, the count and the tier; upserting on this
* keeps repeated syncs idempotent instead of accumulating near-duplicates.
*/
uniqueIndex('inventory_listings_external_key').on(
t.externalCloudId,
t.gpuType,
t.socket,
t.gpuCount,
t.securityTier,
),
index('inventory_listings_gpu_type_idx').on(t.gpuType),
index('inventory_listings_stock_idx').on(t.stockStatus),
index('inventory_listings_interconnect_idx').on(t.interconnectType),
index('inventory_listings_account_idx').on(t.accountId),
index('inventory_listings_observed_idx').on(t.observedAt),
],
);
/**
* A supply engagement — the pipeline for bringing a provider on.
*
* Qualification is split into technical and financial diligence because
* accepting capacity is a two-key decision: engineering judges whether the
* cluster can actually do the work, finance judges whether the economics clear.
* Recording who accepted what, and why, is the difference between a decision
* and a vibe.
*/
export const supplyDeals = pgTable(
'supply_deals',
{
id: uuid('id').primaryKey().defaultRandom(),
accountId: uuid('account_id')
.notNull()
.references(() => accounts.id, { onDelete: 'cascade' }),
siteId: uuid('site_id').references(() => sites.id, { onDelete: 'set null' }),
name: text('name').notNull(),
stage: supplyStageEnum('stage').notNull().default('sourced'),
stageChangedAt: timestamp('stage_changed_at', { withTimezone: true }).notNull().defaultNow(),
ownerUserId: uuid('owner_user_id').references(() => users.id, { onDelete: 'set null' }),
primaryContactId: uuid('primary_contact_id').references(() => contacts.id, {
onDelete: 'set null',
}),
/** What is on offer. */
gpuType: text('gpu_type'),
gpuCount: integer('gpu_count'),
interconnectType: interconnectTypeEnum('interconnect_type'),
targetCostPerGpuHourCents: integer('target_cost_per_gpu_hour_cents'),
termMonths: integer('term_months'),
availableFrom: timestamp('available_from', { withTimezone: true }),
/**
* The two diligence gates, recorded separately and attributably.
* A null verdict means the gate has not been reached, which is different
* from having been considered and passed.
*/
technicalVerdict: text('technical_verdict'),
technicalVerdictBy: uuid('technical_verdict_by').references(() => users.id, {
onDelete: 'set null',
}),
technicalVerdictAt: timestamp('technical_verdict_at', { withTimezone: true }),
technicalNotes: text('technical_notes'),
financialVerdict: text('financial_verdict'),
financialVerdictBy: uuid('financial_verdict_by').references(() => users.id, {
onDelete: 'set null',
}),
financialVerdictAt: timestamp('financial_verdict_at', { withTimezone: true }),
financialNotes: text('financial_notes'),
/** Recorded when the stage becomes `rejected`. Rejections teach. */
rejectionReason: text('rejection_reason'),
closedAt: timestamp('closed_at', { withTimezone: true }),
lastActivityAt: timestamp('last_activity_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(t) => [
index('supply_deals_account_idx').on(t.accountId),
index('supply_deals_stage_idx').on(t.stage),
index('supply_deals_owner_idx').on(t.ownerUserId),
],
);
/**
* Capacity we have actually committed to buy.
*
* This is the cost side of the margin ledger. Unlike a listing, a commitment
* is a liability: the hours are paid for whether or not anyone uses them. That
* is precisely why idle capacity is worth alerting on, and why margin is
* computed against the full commitment rather than only the hours that sold.
*/
export const capacityCommitments = pgTable(
'capacity_commitments',
{
id: uuid('id').primaryKey().defaultRandom(),
accountId: uuid('account_id')
.notNull()
.references(() => accounts.id, { onDelete: 'restrict' }),
siteId: uuid('site_id').references(() => sites.id, { onDelete: 'set null' }),
supplyDealId: uuid('supply_deal_id').references(() => supplyDeals.id, {
onDelete: 'set null',
}),
name: text('name').notNull(),
gpuType: text('gpu_type').notNull(),
socket: gpuSocketEnum('socket'),
gpuCount: integer('gpu_count').notNull(),
interconnectType: interconnectTypeEnum('interconnect_type').notNull().default('Unknown'),
securityTier: securityTierEnum('security_tier').notNull().default('secure_cloud'),
startsAt: timestamp('starts_at', { withTimezone: true }).notNull(),
endsAt: timestamp('ends_at', { withTimezone: true }).notNull(),
/**
* Total contracted GPU-hours. Stored explicitly rather than derived from
* count × duration, because real contracts include ramp periods,
* maintenance windows and holdbacks that no formula predicts.
*/
totalGpuHours: numeric('total_gpu_hours', { precision: 16, scale: 2 }).notNull(),
costPerGpuHourCents: integer('cost_per_gpu_hour_cents').notNull(),
currency: text('currency').notNull().default('USD'),
/**
* The capacity SHAPE — how much is actually held at each point in time.
*
* A commitment is not a rectangle. Real contracts ramp over tranches, step
* down at renegotiation checkpoints, and hold different node counts in
* different months. A single start/end/total flattens all of that and then
* reports availability that does not exist in the month someone wants it.
*
* Modelled as parallel arrays of interval boundaries and the quantity held
* during each interval — the primitive used by compute exchanges that
* actually trade this. Availability at any instant is then simply:
*
* available(t) = shapeQuantityAt(t) Σ overlapping allocations(t)
*
* `startsAt`, `endsAt` and `gpuCount` above remain as the coarse envelope,
* for indexing and for the common flat case. Where `shape` is present it
* is authoritative.
*/
shape: jsonb('shape').$type<{
/** ISO-8601 boundaries, ascending. n+1 entries for n intervals. */
intervals: string[];
/** GPUs held during each interval. Length = intervals.length 1. */
quantities: number[];
}>(),
/**
* Placement constraints. Contiguity and adjacency are commercial terms
* here, not deployment details: a customer needing one contiguous 512-GPU
* block cannot use two 256-GPU blocks in different halls, and a deal dies
* on exactly that distinction.
*/
colocateWith: jsonb('colocate_with').$type<string[]>().notNull().default([]),
isContiguous: boolean('is_contiguous').notNull().default(true),
/** Contractual floor: the minimum we owe regardless of usage. */
minimumSpendCents: integer('minimum_spend_cents'),
isAutoRenew: boolean('is_auto_renew').notNull().default(false),
/** Days of notice required to exit. Drives renewal alerting. */
noticeDays: integer('notice_days'),
/**
* Take-or-pay floor as a percentage of contracted volume — the share we
* owe whether or not we draw it. This is the field that turns a
* commitment from an option into a liability, and it is why idle capacity
* is worth alerting on rather than merely noting.
*/
takeOrPayFloorPct: numeric('take_or_pay_floor_pct', { precision: 6, scale: 2 }),
/** Share of total value paid upfront. Prepayment is common and material. */
prepaidPct: numeric('prepaid_pct', { precision: 6, scale: 2 }),
prepaidAmountCents: integer('prepaid_amount_cents'),
/**
* Depreciation assumptions for any hardware we own against this block.
*
* Useful life is the single largest swing variable in compute unit
* economics — operators in this market publish anything from four to six
* years for identical hardware, and the same contract can show roughly
* 10% or 25% contribution margin depending purely on that choice. Storing
* the assumption per block, rather than as a company-wide constant, is
* what makes margin recomputable under different scenarios instead of
* being an unexaminable number.
*/
usefulLifeYears: numeric('useful_life_years', { precision: 4, scale: 2 }),
salvageValuePct: numeric('salvage_value_pct', { precision: 6, scale: 2 }),
depreciationStartAt: timestamp('depreciation_start_at', { withTimezone: true }),
/**
* Cost of capital for THIS block, in basis points.
*
* An attribute of the block rather than of the company, because blocks are
* funded by different instruments at very different rates — blended debt
* costs across operators in this market span roughly fourfold. Applying a
* single corporate rate makes per-deal margin wrong in both directions.
*/
costOfCapitalBps: integer('cost_of_capital_bps'),
financingInstrument: text('financing_instrument'),
/**
* Deliberate oversubscription. Some capacity is sold beyond 100% on the
* assumption not every buyer draws their full reservation. Recording the
* intent stops the margin view from flagging a policy as a bug.
*/
oversubscriptionPct: numeric('oversubscription_pct', { precision: 6, scale: 2 })
.notNull()
.default('0'),
notes: text('notes'),
terminatedAt: timestamp('terminated_at', { withTimezone: true }),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
},
(t) => [
index('capacity_commitments_account_idx').on(t.accountId),
index('capacity_commitments_gpu_type_idx').on(t.gpuType),
index('capacity_commitments_window_idx').on(t.startsAt, t.endsAt),
],
);
export type Site = typeof sites.$inferSelect;
export type InventoryListing = typeof inventoryListings.$inferSelect;
export type NewInventoryListing = typeof inventoryListings.$inferInsert;
export type SupplyDeal = typeof supplyDeals.$inferSelect;
export type CapacityCommitment = typeof capacityCommitments.$inferSelect;
export type NewCapacityCommitment = typeof capacityCommitments.$inferInsert;