Add a plausible demo dataset
CI / verify (push) Successful in 2m1s

So the product is legible before anyone has entered real data, and so Piggy
has something to reason about while it is being built.

Kept separate from the base seed because that one is publicly-sourced and cited
while this is invented. Two rules, both deliberate:

Every record is prefixed "DEMO — ", so a screenshot can never be mistaken for
real business. And demand-side customers are fictional. Suppliers are real
companies — they are public, and naming the actual market is the point — but
inventing customers with invented contract values against real named businesses
would be fabricating commercial records about them, which is a different thing
and not worth the extra realism.

The numbers are tuned to teach rather than to flatter. The book clears +5.4% at
79% utilisation, which is thin and about right for this industry once capacity
cost is charged honestly. Underneath, the blocks disagree: the large H200 block
carries it, the EU H100 block is underwater at 55% sold because a 46% markup
needs ~69% sold to break even, and the community pool holds a large unconverted
hold — so the difference between "sold" and "held" is visible rather than
theoretical.

An earlier tuning left the whole book at -26%. Honest, but it reads as a broken
product rather than an under-utilised book, so the totals now open healthy and
the problems appear on drill-down.

Also exercises parts of the schema nothing had touched yet: ramped capacity
shapes, negotiated SLAs with fee abatement and spare-pool scope, renewal
obligations with one deliberately near-term, EU data-residency constraints on a
capacity request, and internal research burn.

Two bugs found while testing it, both the same trap as before: the research
allocation duplicated on every run because onConflictDoNothing() is a no-op
without a matching unique constraint, and notes were double-prefixed. Verified
idempotent over three consecutive runs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-12 20:55:41 -07:00
parent 2a30645c8d
commit 468979b303
3 changed files with 650 additions and 3 deletions
+3 -2
View File
@@ -2,7 +2,7 @@
"name": "pig",
"version": "0.1.0",
"private": true,
"description": "PIG Prime Intellect Growth. An open-source, agent-native CRM for two-sided AI-compute companies.",
"description": "PIG \u2014 Prime Intellect Growth. An open-source, agent-native CRM for two-sided AI-compute companies.",
"license": "Apache-2.0",
"type": "module",
"engines": {
@@ -22,7 +22,8 @@
"dev:mcp": "npm run dev -w @pig/mcp",
"db:generate": "npm run generate -w @pig/db",
"db:migrate": "npm run migrate -w @pig/db",
"db:seed": "npm run seed -w @pig/db"
"db:seed": "npm run seed -w @pig/db",
"db:demo": "npm run demo -w @pig/db"
},
"devDependencies": {
"@types/node": "^22.10.2",
+2 -1
View File
@@ -15,7 +15,8 @@
"migrate": "tsx src/migrate.ts",
"seed": "tsx src/seed/index.ts",
"studio": "drizzle-kit studio",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"demo": "tsx src/seed/demo.ts"
},
"dependencies": {
"@pig/core": "*",
+645
View File
@@ -0,0 +1,645 @@
/**
* 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 `npm run 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 { and, eq, like, or } from 'drizzle-orm';
import { createDatabase } from '../client';
import {
accounts,
activities,
allocations,
capacityCommitments,
capacityRequests,
contacts,
contracts,
contractObligations,
demandDeals,
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);
/** 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) {
await db
.insert(supplyDeals)
.values({
accountId: lambda.id,
name: `${PREFIX}256× H200 — pricing`,
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.',
})
.onConflictDoNothing();
}
// ------------------------------------------------------------ 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) {
await db.insert(allocations).values({
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,
});
}
}
}
// 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) {
await db
.insert(allocations)
.values({
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,
});
}
}
console.log(' 4 capacity commitments, with sites, MSAs and negotiated SLAs');
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: npm run 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);
});