Files
pig/packages/db/src/seed/demo.ts
T
karti 1318c0b841
CI / verify (push) Successful in 3m51s
Ship growth intelligence and demo polish
2026-08-13 04:44:14 -07:00

841 lines
28 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.
/**
* Demo dataset — a plausible book of business, for development and demos.
*
* Separate from `index.ts` (which seeds publicly-sourced, cited people) because
* this is **invented**. It exists so the product is legible before anyone has
* entered real data: margin that moves, blocks at different utilisation, deals
* spread across both pipelines, contracts with real structure.
*
* Two integrity rules, and they are not fussiness:
*
* **Every record is prefixed `DEMO —`.** A screenshot of this must never be
* mistakeable for real business.
*
* **Demand-side customers are fictional.** Suppliers are real companies —
* they are public, and naming the actual market is the point — but their
* commitments are labelled and the prices are illustrative. Inventing
* *customers* with invented contract values against real named companies
* would be fabricating commercial records about real businesses, which is a
* different thing entirely and not worth the realism.
*
* Remove it all with `pnpm db:demo -- --clear`.
*
* The numbers are chosen to teach. The book as a whole clears a modest margin —
* roughly what this industry actually earns once capacity cost is charged
* honestly — while individual blocks tell different stories:
*
* the large H200 block carries the book;
* the EU H100 block is UNDERWATER at 55% sold, because a 46% markup needs
* ~69% sold to break even at all;
* the community A100 pool has a large hold that has not converted, so it
* shows as reserved-but-unsold — the distinction between "sold" and "held"
* made visible rather than theoretical.
*
* A demo that opens on a healthy total and reveals the problems on drill-down
* is more useful than one that opens on a loss, which reads as a broken
* product rather than an under-utilised book.
*/
import { ALLOCATION_STATUSES, type AllocationStatus } from '@pig/core';
import { and, eq, like, or } from 'drizzle-orm';
import { createDatabase } from '../client';
import {
accounts,
activities,
facts,
allocations,
capacityCommitments,
capacityRequests,
contacts,
contracts,
contractObligations,
demandDeals,
type NewAllocation,
sites,
slaTerms,
supplyDeals,
} from '../schema/index';
const db = createDatabase();
const PREFIX = 'DEMO — ';
const day = 86_400_000;
const now = Date.now();
const at = (days: number) => new Date(now + days * day);
function isAllocationStatus(value: string): value is AllocationStatus {
return (ALLOCATION_STATUSES as readonly string[]).includes(value);
}
/** GPU-hours for a block, allowing for a maintenance/ramp haircut. */
const hours = (gpus: number, days: number, efficiency = 0.94) =>
String(Math.round(gpus * 24 * days * efficiency));
// ---------------------------------------------------------------- suppliers
const SUPPLY = [
{
domain: 'coreweave.com',
site: { name: 'US-East (NJ)', country: 'United States', countryCode: 'US', region: 'us-east' },
commitment: {
name: `${PREFIX}512× H200 reserved, 12 months`,
gpuType: 'H200',
socket: 'SXM5' as const,
gpuCount: 512,
interconnectType: 'Infiniband' as const,
days: 365,
costPerGpuHourCents: 189,
takeOrPayFloorPct: '100',
prepaidPct: '25',
},
},
{
domain: 'nebius.com',
site: { name: 'EU-North (FI)', country: 'Finland', countryCode: 'FI', region: 'eu-north' },
commitment: {
name: `${PREFIX}128× H100 reserved, 6 months`,
gpuType: 'H100_80GB',
socket: 'SXM5' as const,
gpuCount: 128,
interconnectType: 'Infiniband' as const,
days: 180,
costPerGpuHourCents: 171,
takeOrPayFloorPct: '80',
prepaidPct: '0',
},
},
{
domain: 'crusoe.ai',
site: { name: 'US-Central (TX)', country: 'United States', countryCode: 'US', region: 'us-central' },
commitment: {
name: `${PREFIX}64× B200 reserved, 9 months`,
gpuType: 'B200',
socket: 'SXM6' as const,
gpuCount: 64,
interconnectType: 'Infiniband' as const,
days: 270,
costPerGpuHourCents: 305,
takeOrPayFloorPct: '100',
prepaidPct: '15',
},
},
{
domain: 'runpod.io',
site: { name: 'Community pool', country: 'United States', countryCode: 'US', region: 'us-west' },
commitment: {
name: `${PREFIX}32× A100 burst pool, 3 months`,
gpuType: 'A100_80GB',
socket: 'SXM4' as const,
gpuCount: 32,
interconnectType: 'Ethernet' as const,
securityTier: 'community_cloud' as const,
days: 90,
costPerGpuHourCents: 96,
takeOrPayFloorPct: '0',
prepaidPct: '0',
},
},
];
/**
* Fictional customers.
*
* Invented deliberately — see the note at the top of this file. Any resemblance
* to a real company is unintended, and none of these figures describes anyone's
* actual contract.
*/
const DEMAND = [
{
account: `${PREFIX}Halcyon Research`,
segment: 'frontier_lab' as const,
country: 'United States',
contact: { name: 'Dana Whitfield', title: 'Head of Infrastructure', decisionMaker: true },
deal: {
name: `${PREFIX}Pre-training cluster, 12 months`,
productLine: 'compute_reserved' as const,
stage: 'deployment' as const,
acvCents: 2_400_000_00,
termMonths: 12,
msaExecuted: true,
dpaExecuted: true,
},
request: { gpuType: 'H200', gpuCount: 256, fastFabric: true, maxPriceCents: 285 },
// Draws from the CoreWeave block.
allocation: {
supplier: 'coreweave.com',
share: 0.76,
priceCents: 271,
status: 'active',
},
},
{
account: `${PREFIX}Verity Health AI`,
segment: 'enterprise' as const,
country: 'Germany',
contact: { name: 'Lukas Brenner', title: 'VP Engineering', decisionMaker: true },
deal: {
name: `${PREFIX}EU-resident fine-tuning`,
productLine: 'post_training' as const,
stage: 'procurement' as const,
acvCents: 640_000_00,
termMonths: 6,
msaExecuted: true,
dpaExecuted: false,
},
// Data residency: must land in the EU. Drives the Nebius block.
request: {
gpuType: 'H100_80GB',
gpuCount: 64,
fastFabric: true,
maxPriceCents: 260,
allowedRegions: ['eu-north', 'eu-west'],
certifications: ['ISO 27001', 'SOC 2 Type II'],
},
allocation: {
supplier: 'nebius.com',
share: 0.55,
priceCents: 249,
status: 'committed',
},
},
{
account: `${PREFIX}Northwind Robotics`,
segment: 'applied_ai_startup' as const,
country: 'United States',
contact: { name: 'Priya Raghavan', title: 'CTO', decisionMaker: true },
deal: {
name: `${PREFIX}Blackwell evaluation → production`,
productLine: 'compute_reserved' as const,
stage: 'poc' as const,
acvCents: 890_000_00,
termMonths: 9,
msaExecuted: true,
dpaExecuted: true,
},
request: { gpuType: 'B200', gpuCount: 32, fastFabric: true, maxPriceCents: 460 },
allocation: {
supplier: 'crusoe.ai',
share: 0.74,
priceCents: 441,
status: 'active',
},
},
{
account: `${PREFIX}Tessellate Labs`,
segment: 'applied_ai_startup' as const,
country: 'United Kingdom',
contact: { name: 'Owen Marsh', title: 'Founding Engineer', decisionMaker: false },
deal: {
name: `${PREFIX}Inference burst capacity`,
productLine: 'inference' as const,
stage: 'proposal' as const,
acvCents: 145_000_00,
termMonths: 3,
msaExecuted: false,
dpaExecuted: false,
},
request: { gpuType: 'A100_80GB', gpuCount: 16, fastFabric: false, maxPriceCents: 175 },
// A HOLD, not a sale. The deal has not closed, so this reserves capacity
// without counting as revenue — the distinction the capacity view exists
// to make visible.
allocation: {
supplier: 'runpod.io',
share: 0.55,
priceCents: 168,
status: 'planned',
holdDays: 12,
},
},
{
account: `${PREFIX}Aurelian Systems`,
segment: 'enterprise' as const,
country: 'United States',
contact: { name: 'Meredith Cole', title: 'Director, ML Platform', decisionMaker: true },
deal: {
name: `${PREFIX}Multi-year committed capacity`,
productLine: 'compute_reserved' as const,
stage: 'legal' as const,
acvCents: 3_100_000_00,
termMonths: 24,
msaExecuted: false,
dpaExecuted: false,
},
request: { gpuType: 'H200', gpuCount: 128, fastFabric: true, maxPriceCents: 265 },
allocation: null, // Still in legal. Nothing reserved yet — correctly.
},
{
account: `${PREFIX}Quillon AI`,
segment: 'applied_ai_startup' as const,
country: 'Canada',
contact: { name: 'Sofia Trentini', title: 'Head of Research', decisionMaker: true },
deal: {
name: `${PREFIX}Evaluation harness pilot`,
productLine: 'evaluations' as const,
stage: 'qualification' as const,
acvCents: 48_000_00,
termMonths: 3,
msaExecuted: false,
dpaExecuted: false,
},
request: null,
allocation: null,
},
];
async function clear() {
console.log('Removing demo data…');
// Ordered so foreign keys never block a delete.
const demoAccounts = await db
.select({ id: accounts.id })
.from(accounts)
.where(like(accounts.name, `${PREFIX}%`));
const ids = demoAccounts.map((a) => a.id);
await db.delete(allocations).where(like(allocations.notes, `${PREFIX}%`));
await db.delete(contractObligations);
await db.delete(slaTerms);
await db.delete(contracts).where(like(contracts.title, `${PREFIX}%`));
await db.delete(capacityRequests);
await db.delete(demandDeals).where(like(demandDeals.name, `${PREFIX}%`));
await db.delete(supplyDeals).where(like(supplyDeals.name, `${PREFIX}%`));
await db.delete(capacityCommitments).where(like(capacityCommitments.name, `${PREFIX}%`));
await db.delete(activities).where(like(activities.subject, `${PREFIX}%`));
for (const id of ids) {
await db.delete(contacts).where(eq(contacts.accountId, id));
}
await db.delete(accounts).where(like(accounts.name, `${PREFIX}%`));
console.log(`Removed ${ids.length} demo account(s) and everything hanging from them.`);
}
async function seedDemo() {
console.log('Seeding the demo book…\n');
// ------------------------------------------------------------ supply side
const commitmentIds = new Map<string, string>();
for (const supplier of SUPPLY) {
const [account] = await db
.select({ id: accounts.id })
.from(accounts)
.where(eq(accounts.domain, supplier.domain))
.limit(1);
if (!account) {
console.log(` skipped ${supplier.domain} — run the base seed first`);
continue;
}
const [existing] = await db
.select({ id: capacityCommitments.id })
.from(capacityCommitments)
.where(eq(capacityCommitments.name, supplier.commitment.name))
.limit(1);
if (existing) {
commitmentIds.set(supplier.domain, existing.id);
continue;
}
const [site] = await db
.insert(sites)
.values({
accountId: account.id,
name: `${PREFIX}${supplier.site.name}`,
country: supplier.site.country,
countryCode: supplier.site.countryCode,
region: supplier.site.region,
certifications:
supplier.commitment.securityTier === 'community_cloud'
? []
: ['SOC 2 Type II', 'ISO 27001'],
})
.returning();
const c = supplier.commitment;
const [commitment] = await db
.insert(capacityCommitments)
.values({
accountId: account.id,
siteId: site?.id,
name: c.name,
gpuType: c.gpuType,
socket: c.socket,
gpuCount: c.gpuCount,
interconnectType: c.interconnectType,
securityTier: c.securityTier ?? 'secure_cloud',
startsAt: at(-30),
endsAt: at(c.days - 30),
totalGpuHours: hours(c.gpuCount, c.days),
costPerGpuHourCents: c.costPerGpuHourCents,
takeOrPayFloorPct: c.takeOrPayFloorPct,
prepaidPct: c.prepaidPct,
usefulLifeYears: '5',
// A ramp: a quarter of the fleet for the first fortnight, then full.
// Present so the shape column is exercised by something other than a
// rectangle, which is the case it exists for.
shape: {
intervals: [at(-30).toISOString(), at(-16).toISOString(), at(c.days - 30).toISOString()],
quantities: [Math.round(c.gpuCount * 0.25), c.gpuCount],
},
notes: 'Illustrative demo data. Not a real contract.',
})
.returning();
if (commitment) commitmentIds.set(supplier.domain, commitment.id);
// The paper behind it.
const [msa] = await db
.insert(contracts)
.values({
accountId: account.id,
type: 'msa',
status: 'executed',
side: 'supply',
title: `${PREFIX}MSA — ${supplier.domain}`,
capacityCommitmentId: commitment?.id,
effectiveAt: at(-60),
expiresAt: at(c.days + 60),
isAutoRenew: true,
noticeDays: 90,
takeOrPayFloorPct: c.takeOrPayFloorPct,
prepaidPct: c.prepaidPct,
terminationTier: c.prepaidPct !== '0' ? '1_prepaid' : '2_take_or_pay',
governingLaw: 'New York',
})
.returning();
if (msa) {
// A negotiated SLA with fee abatement — the remedy that actually matters
// on the supply side, and the one a credits-only model cannot express.
const [sla] = await db
.insert(contracts)
.values({
accountId: account.id,
type: 'sla',
status: 'executed',
side: 'supply',
title: `${PREFIX}SLA — ${supplier.domain}`,
parentContractId: msa.id,
effectiveAt: at(-60),
})
.returning();
if (sla) {
await db.insert(slaTerms).values({
contractId: sla.id,
kind: 'negotiated',
uptimeTargetPct: '99.500',
measurementUnit: 'node',
measurementWindow: 'monthly',
remedyType: 'fee_abatement',
abatementTriggerValue: 2,
abatementTriggerUnit: 'business_days',
nodeReplacementHours: 24,
claimDeadlineValue: 30,
claimDeadlineUnit: 'days',
creditCapPct: '50.000',
sparePoolObligation: 'Spares held on site sufficient to replace failed nodes and switches.',
sparePoolScope: ['compute_nodes', 'network_switches'],
rcaDeliveryHours: 72,
maintenanceClasses: [
{ class: 'planned', noticeValue: 5, noticeUnit: 'business_days', excludedFromUptime: true },
{ class: 'emergency', noticeValue: 24, noticeUnit: 'hours', excludedFromUptime: false },
],
});
}
await db.insert(contractObligations).values({
contractId: msa.id,
title: `${PREFIX}Renewal notice — ${supplier.domain}`,
kind: 'renewal_notice',
// Deliberately near-term on one supplier so the renewal alarm has
// something real to fire on.
dueAt: at(supplier.domain === 'nebius.com' ? 21 : 200),
description: '90 days notice required to prevent auto-renewal.',
});
}
await db.insert(supplyDeals).values({
accountId: account.id,
siteId: site?.id,
name: `${PREFIX}${c.gpuCount}× ${c.gpuType}${supplier.domain}`,
stage: 'live',
gpuType: c.gpuType,
gpuCount: c.gpuCount,
interconnectType: c.interconnectType,
targetCostPerGpuHourCents: c.costPerGpuHourCents,
termMonths: Math.round(c.days / 30),
technicalVerdict: 'pass',
technicalNotes: 'NCCL all-reduce within 8% of theoretical across the full fabric.',
financialVerdict: 'pass',
financialNotes: `Break-even at ~${Math.round((c.costPerGpuHourCents / 0.75 / 100) * 100) / 100}/GPU-hr at 75% utilisation.`,
});
}
// A supply deal still in diligence, so that pipeline is not all `live`.
const [lambda] = await db
.select({ id: accounts.id })
.from(accounts)
.where(eq(accounts.domain, 'lambda.ai'))
.limit(1);
if (lambda) {
const LAMBDA_DEAL = `${PREFIX}256× H200 — pricing`;
const [existingLambdaDeal] = await db
.select({ id: supplyDeals.id })
.from(supplyDeals)
.where(and(eq(supplyDeals.accountId, lambda.id), eq(supplyDeals.name, LAMBDA_DEAL)))
.limit(1);
if (!existingLambdaDeal) {
await db.insert(supplyDeals).values({
accountId: lambda.id,
name: LAMBDA_DEAL,
stage: 'financial_diligence',
gpuType: 'H200',
gpuCount: 256,
interconnectType: 'Infiniband',
targetCostPerGpuHourCents: 176,
termMonths: 12,
technicalVerdict: 'pass',
technicalNotes: 'Fabric verified. Storage throughput per GPU is below spec — flagged.',
financialNotes: 'Awaiting a firm quote on the committed tranche.',
});
}
}
// ------------------------------------------------------------ demand side
for (const d of DEMAND) {
const [existingAccount] = await db
.select({ id: accounts.id })
.from(accounts)
.where(eq(accounts.name, d.account))
.limit(1);
if (existingAccount) continue;
const [account] = await db
.insert(accounts)
.values({
name: d.account,
side: 'demand',
customerSegment: d.segment,
country: d.country,
description: 'Fictional company, for demonstration only.',
source: 'seed',
confidence: 'confirmed',
lastActivityAt: at(-Math.random() * 10),
})
.returning();
if (!account) continue;
const [contact] = await db
.insert(contacts)
.values({
accountId: account.id,
fullName: d.contact.name,
title: d.contact.title,
affiliation: 'staff',
isDecisionMaker: d.contact.decisionMaker,
confidence: 'confirmed',
source: 'seed',
email: null,
})
.returning();
const [deal] = await db
.insert(demandDeals)
.values({
accountId: account.id,
name: d.deal.name,
productLine: d.deal.productLine,
stage: d.deal.stage,
acvCents: d.deal.acvCents,
tcvCents: Math.round((d.deal.acvCents * d.deal.termMonths) / 12),
termMonths: d.deal.termMonths,
msaExecuted: d.deal.msaExecuted,
dpaExecuted: d.deal.dpaExecuted,
primaryContactId: contact?.id,
expectedCloseDate: at(20 + Math.round(Math.random() * 60)),
probability: String(
{ qualification: 0.1, legal: 0.35, proposal: 0.45, procurement: 0.6, poc: 0.7, deployment: 0.9 }[
d.deal.stage
] ?? 0.5,
),
lastActivityAt: at(-Math.random() * 8),
})
.returning();
if (!deal) continue;
if (d.request) {
await db.insert(capacityRequests).values({
demandDealId: deal.id,
gpuType: d.request.gpuType,
gpuCount: d.request.gpuCount,
requiresHighSpeedInterconnect: d.request.fastFabric,
minInterconnectType: d.request.fastFabric ? 'Infiniband' : undefined,
maxPricePerGpuHourCents: d.request.maxPriceCents,
allowedRegions: d.request.allowedRegions ?? [],
requiredCertifications: d.request.certifications ?? [],
startsAt: at(15),
endsAt: at(15 + d.deal.termMonths * 30),
totalGpuHours: hours(d.request.gpuCount, d.deal.termMonths * 30, 1),
});
}
if (d.allocation) {
const commitmentId = commitmentIds.get(d.allocation.supplier);
if (commitmentId) {
const [commitment] = await db
.select()
.from(capacityCommitments)
.where(eq(capacityCommitments.id, commitmentId))
.limit(1);
if (commitment) {
if (!isAllocationStatus(d.allocation.status)) {
throw new Error(`Invalid demo allocation status: ${d.allocation.status}`);
}
const allocationRow = {
capacityCommitmentId: commitmentId,
demandDealId: deal.id,
gpuHours: String(
Math.round(Number(commitment.totalGpuHours) * d.allocation.share),
),
pricePerGpuHourCents: d.allocation.priceCents,
startsAt: commitment.startsAt,
endsAt: commitment.endsAt,
status: d.allocation.status,
guaranteeType: d.allocation.status === 'planned' ? 'committed' : 'guaranteed',
priority: d.allocation.status === 'planned' ? 100 : 10,
holdExpiresAt: d.allocation.holdDays ? at(d.allocation.holdDays) : null,
notes: d.account,
} satisfies NewAllocation;
await db.insert(allocations).values(allocationRow);
}
}
}
// A little history, so the activity feed is not empty.
await db.insert(activities).values([
{
type: 'meeting',
subject: `${PREFIX}Technical scoping with ${d.contact.name}`,
accountId: account.id,
demandDealId: deal.id,
occurredAt: at(-12),
},
{
type: 'note',
subject: `${PREFIX}${d.deal.stage} — next step agreed`,
accountId: account.id,
demandDealId: deal.id,
occurredAt: at(-3),
},
]);
}
// Internal research burn against the largest block — real cost, no revenue.
const coreweave = commitmentIds.get('coreweave.com');
if (coreweave) {
const [commitment] = await db
.select()
.from(capacityCommitments)
.where(eq(capacityCommitments.id, coreweave))
.limit(1);
const RESEARCH_NOTE = `${PREFIX}Internal research consumption`;
const [existingResearch] = await db
.select({ id: allocations.id })
.from(allocations)
.where(
and(
eq(allocations.capacityCommitmentId, coreweave),
eq(allocations.notes, RESEARCH_NOTE),
),
)
.limit(1);
if (commitment && !existingResearch) {
const researchAllocationRow = {
capacityCommitmentId: coreweave,
internalTeam: 'research',
gpuHours: String(Math.round(Number(commitment.totalGpuHours) * 0.08)),
pricePerGpuHourCents: 0,
startsAt: commitment.startsAt,
endsAt: commitment.endsAt,
status: 'active',
guaranteeType: 'internal',
priority: 200,
notes: RESEARCH_NOTE,
} satisfies NewAllocation;
await db.insert(allocations).values(researchAllocationRow);
}
}
// -------------------------------------------------- agent-derived facts
//
// Without these the fact-review queue and every provenance tooltip are
// empty, which hides the thing that makes an agent-written CRM trustworthy:
// that each claim carries a score, a band, evidence and a source, and that
// only verified claims apply themselves.
//
// The mix is deliberate. Two `applied` facts show what a confident agent
// writes unprompted; four `proposed` show what waits for a human; one is a
// near-miss that a reviewer should reject, so the queue is not a row of
// obvious approvals.
const factSeeds: {
accountDomain?: string;
contactName?: string;
field: string;
value: string;
score: string;
band: 'verified' | 'probable' | 'possible';
status: 'applied' | 'proposed';
method: string;
sourceUrl?: string;
evidence: Record<string, unknown>;
}[] = [
{
accountDomain: 'coreweave.com',
field: 'supplierType',
value: 'neocloud',
score: '0.960',
band: 'verified',
status: 'applied',
method: 'web_search',
sourceUrl: 'https://www.coreweave.com/',
evidence: {
quote: 'Describes itself as an AI hyperscaler providing GPU cloud infrastructure.',
corroboration: 2,
},
},
{
accountDomain: 'nebius.com',
field: 'jurisdiction',
value: 'European Union',
score: '0.910',
band: 'verified',
status: 'applied',
method: 'web_search',
sourceUrl: 'https://nebius.com/',
evidence: {
quote: 'Operates a datacentre in Finland, inside the EU data-residency perimeter.',
matters: 'Determines eligibility for customers with EU residency requirements.',
},
},
{
accountDomain: 'crusoe.ai',
field: 'certifications',
value: 'SOC 2 Type II',
score: '0.720',
band: 'probable',
status: 'proposed',
method: 'web_search',
sourceUrl: 'https://crusoe.ai/',
evidence: {
quote: 'A trust page references SOC 2, but the report scope and observation window are not stated.',
caution: 'Scope matters — a report can cover only some products.',
},
},
{
accountDomain: 'lambda.ai',
field: 'supplierType',
value: 'neocloud',
score: '0.680',
band: 'probable',
status: 'proposed',
method: 'web_search',
sourceUrl: 'https://lambda.ai/',
evidence: { quote: 'Markets GPU cloud and on-premises clusters.' },
},
{
contactName: 'Dana Whitfield',
field: 'title',
value: 'VP Infrastructure',
score: '0.540',
band: 'possible',
status: 'proposed',
method: 'inference',
evidence: {
reasoning: 'A conference bio lists a VP title; the CRM records Head of Infrastructure.',
conflict: 'Sources disagree, and neither is dated.',
},
},
{
accountDomain: 'runpod.io',
field: 'customerSegment',
value: 'frontier_lab',
score: '0.310',
band: 'possible',
status: 'proposed',
method: 'inference',
evidence: {
reasoning: 'Inferred from a blog post mentioning large training runs.',
warning:
'Weak. This is a supply-side provider, not a frontier lab — a reviewer should reject it.',
},
},
];
let factsAdded = 0;
for (const seed of factSeeds) {
let accountId: string | undefined;
let contactId: string | undefined;
if (seed.accountDomain) {
const [row] = await db
.select({ id: accounts.id })
.from(accounts)
.where(eq(accounts.domain, seed.accountDomain))
.limit(1);
accountId = row?.id;
}
if (seed.contactName) {
const [row] = await db
.select({ id: contacts.id })
.from(contacts)
.where(eq(contacts.fullName, seed.contactName))
.limit(1);
contactId = row?.id;
}
if (!accountId && !contactId) continue;
// Idempotent on the natural key: one claim per subject per field per value.
const [existing] = await db
.select({ id: facts.id })
.from(facts)
.where(
and(
accountId ? eq(facts.accountId, accountId) : eq(facts.contactId, contactId!),
eq(facts.field, seed.field),
eq(facts.value, seed.value),
),
)
.limit(1);
if (existing) continue;
await db.insert(facts).values({
accountId,
contactId,
field: seed.field,
value: seed.value,
score: seed.score,
band: seed.band,
status: seed.status,
method: seed.method,
sourceUrl: seed.sourceUrl,
evidence: seed.evidence,
observedAt: at(-Math.round(Math.random() * 6) - 1),
});
factsAdded += 1;
}
console.log(' 4 capacity commitments, with sites, MSAs and negotiated SLAs');
console.log(` ${factSeeds.length} agent-derived facts (${factsAdded} new) — 2 applied, 4 awaiting review`);
console.log(' 6 demand deals across the pipeline, 5 supply deals');
console.log(' Allocations including one unconverted hold and internal research burn');
console.log('\nEverything is prefixed "DEMO — ". Remove it with: pnpm db:demo -- --clear');
}
const shouldClear = process.argv.includes('--clear');
(shouldClear ? clear() : seedDemo())
.then(() => process.exit(0))
.catch((error) => {
console.error('Demo seed failed:', error);
process.exit(1);
});