// File: seed.ts // Path: packages/db/prisma/seed.ts // Project: AI Risk Index — airiskindex.io // Author: Simon-Pierre Boucher // Contact: contact@spboucher.ai // Copyright © 2026 Simon-Pierre Boucher. All rights reserved. // // Description: Demo dataset seed: 10 occupations with synthetic two-model panel ratings. import { PrismaClient } from "@prisma/client"; // Demo seed: 10 real O*NET-SOC occupations with plausible hand-authored task // ratings from a synthetic two-model panel ("demo-panel-a/b"). This exists so // the site has data before the real O*NET ETL + LLM rating batch runs; the web // layout shows a "Preview — demonstration dataset" banner, and every score // still traces to a ScoreRun whose raterModels make the provenance explicit. // Deterministic on purpose — reseeding yields identical ratings. const prisma = new PrismaClient(); const DIMS = [ "automatability", "feasibility", "cost_ratio", "barriers", "adoption_velocity", "augmentation", ] as const; type Dim = (typeof DIMS)[number]; type Profile = Record; interface DemoTask { id: string; statement: string; importance: number; adjust?: Partial; } interface DemoOccupation { code: string; title: string; description: string; wageCents: number; // median annual, USD cents profile: Profile; tasks: DemoTask[]; } const p = ( automatability: number, feasibility: number, cost_ratio: number, barriers: number, adoption_velocity: number, augmentation: number, ): Profile => ({ automatability, feasibility, cost_ratio, barriers, adoption_velocity, augmentation }); const DEMO: DemoOccupation[] = [ { code: "43-9021.00", title: "Data Entry Keyers", description: "Operate data entry devices to enter, verify and prepare data from source documents.", wageCents: 3_717_000_00 / 100, profile: p(5, 5, 5, 1, 4, 3), tasks: [ { id: "T1", statement: "Enter account or customer data from source documents within time limits.", importance: 4.6 }, { id: "T2", statement: "Verify entered data by reviewing, correcting or re-entering it.", importance: 4.2 }, { id: "T3", statement: "Compile, sort and check accuracy of source documents before entry.", importance: 3.8, adjust: { automatability: -1 } }, ], }, { code: "43-4051.00", title: "Customer Service Representatives", description: "Interact with customers to handle inquiries, complaints and account questions.", wageCents: 3_933_000_00 / 100, profile: p(4, 4, 4, 2, 4, 4), tasks: [ { id: "T1", statement: "Respond to routine customer inquiries about products, services and accounts.", importance: 4.7 }, { id: "T2", statement: "Resolve complaints and escalate complex or sensitive cases.", importance: 4.1, adjust: { automatability: -1, barriers: 1 } }, { id: "T3", statement: "Keep records of customer interactions and actions taken.", importance: 3.6, adjust: { automatability: 1 } }, ], }, { code: "27-3091.00", title: "Interpreters and Translators", description: "Translate or interpret written and spoken material between languages.", wageCents: 5_768_000_00 / 100, profile: p(4, 4, 4, 2, 3, 4), tasks: [ { id: "T1", statement: "Translate written documents while preserving meaning, tone and register.", importance: 4.6 }, { id: "T2", statement: "Interpret spoken exchanges in real time between parties.", importance: 4.0, adjust: { barriers: 1 } }, { id: "T3", statement: "Proofread and quality-check translated content for accuracy.", importance: 3.7, adjust: { augmentation: 1 } }, ], }, { code: "15-1252.00", title: "Software Developers", description: "Design, build and maintain software systems and applications.", wageCents: 13_047_000_00 / 100, profile: p(4, 4, 3, 2, 5, 5), tasks: [ { id: "T1", statement: "Write, test and debug application code to specification.", importance: 4.7 }, { id: "T2", statement: "Design system architecture and negotiate technical trade-offs with stakeholders.", importance: 4.2, adjust: { automatability: -2, barriers: 1 } }, { id: "T3", statement: "Review code, write documentation and maintain existing services.", importance: 3.9 }, ], }, { code: "13-2051.00", title: "Financial and Investment Analysts", description: "Analyze financial data to guide investment and business decisions.", wageCents: 9_918_000_00 / 100, profile: p(4, 3, 3, 3, 4, 5), tasks: [ { id: "T1", statement: "Gather and consolidate financial data into models and reports.", importance: 4.5, adjust: { automatability: 1 } }, { id: "T2", statement: "Assess valuation and risk to produce investment recommendations.", importance: 4.3, adjust: { barriers: 1 } }, { id: "T3", statement: "Present findings and defend assumptions to clients and committees.", importance: 3.8, adjust: { automatability: -2 } }, ], }, { code: "23-1011.00", title: "Lawyers", description: "Advise and represent clients in legal matters and transactions.", wageCents: 14_531_000_00 / 100, profile: p(3, 3, 3, 5, 3, 4), tasks: [ { id: "T1", statement: "Research case law and draft contracts, briefs and legal memoranda.", importance: 4.5, adjust: { automatability: 1, augmentation: 1 } }, { id: "T2", statement: "Advise clients on legal strategy, risk and obligations.", importance: 4.4 }, { id: "T3", statement: "Represent clients in negotiations, hearings and court proceedings.", importance: 4.0, adjust: { automatability: -1 } }, ], }, { code: "25-2021.00", title: "Elementary School Teachers", description: "Teach academic and social skills to students at the elementary level.", wageCents: 6_355_000_00 / 100, profile: p(2, 2, 2, 4, 2, 4), tasks: [ { id: "T1", statement: "Prepare lesson plans, materials and assessments.", importance: 4.4, adjust: { automatability: 1, augmentation: 1 } }, { id: "T2", statement: "Instruct and manage a classroom of students in person.", importance: 4.8, adjust: { automatability: -1, barriers: 1 } }, { id: "T3", statement: "Grade assignments and track individual student progress.", importance: 3.9, adjust: { automatability: 1 } }, ], }, { code: "29-1141.00", title: "Registered Nurses", description: "Provide and coordinate patient care and educate patients about health conditions.", wageCents: 8_607_000_00 / 100, profile: p(2, 2, 2, 5, 3, 3), tasks: [ { id: "T1", statement: "Assess patients and administer treatments and medications.", importance: 4.8, adjust: { automatability: -1 } }, { id: "T2", statement: "Document care and maintain accurate patient records.", importance: 4.2, adjust: { automatability: 2, augmentation: 1 } }, { id: "T3", statement: "Educate patients and families on conditions and care plans.", importance: 4.0 }, ], }, { code: "53-3032.00", title: "Heavy and Tractor-Trailer Truck Drivers", description: "Drive heavy trucks to transport goods over intercity routes.", wageCents: 5_411_000_00 / 100, profile: p(2, 2, 2, 4, 2, 2), tasks: [ { id: "T1", statement: "Drive long-haul routes while complying with hours-of-service rules.", importance: 4.8, adjust: { adoption_velocity: 1 } }, { id: "T2", statement: "Inspect vehicles and secure cargo before and after trips.", importance: 4.1 }, { id: "T3", statement: "Plan routes and log trip records and fuel purchases.", importance: 3.5, adjust: { automatability: 2, augmentation: 1 } }, ], }, { code: "47-2111.00", title: "Electricians", description: "Install, maintain and repair electrical wiring and systems.", wageCents: 6_121_000_00 / 100, profile: p(1, 1, 2, 4, 2, 2), tasks: [ { id: "T1", statement: "Install and repair wiring, fixtures and control systems on site.", importance: 4.8 }, { id: "T2", statement: "Diagnose faults using test equipment and building plans.", importance: 4.3, adjust: { augmentation: 1 } }, { id: "T3", statement: "Prepare estimates, order materials and document completed work.", importance: 3.4, adjust: { automatability: 2, augmentation: 1 } }, ], }, ]; const clamp = (value: number): number => Math.max(1, Math.min(5, Math.round(value))); async function seedDemoOccupation(occupation: DemoOccupation): Promise { await prisma.occupation.upsert({ where: { code: occupation.code }, update: { title: occupation.title, description: occupation.description, medianWageCents: Math.round(occupation.wageCents) }, create: { code: occupation.code, title: occupation.title, description: occupation.description, medianWageCents: Math.round(occupation.wageCents), }, }); for (const [taskIndex, task] of occupation.tasks.entries()) { const taskId = `${occupation.code}-${task.id}`; await prisma.task.upsert({ where: { id: taskId }, update: { statement: task.statement, importance: task.importance }, create: { id: taskId, occupationCode: occupation.code, statement: task.statement, importance: task.importance, }, }); for (const [dimIndex, dimension] of DIMS.entries()) { const base = clamp(occupation.profile[dimension] + (task.adjust?.[dimension] ?? 0)); // Deterministic panel disagreement: model B shifts by -1/0/+1 in a fixed pattern. const shift = ((dimIndex + taskIndex) % 3) - 1; const panel: Array<[string, number]> = [ ["demo-panel-a", base], ["demo-panel-b", clamp(base + shift)], ]; for (const [model, rating] of panel) { await prisma.taskRating.upsert({ where: { taskId_dimension_model_promptVersion_sampleIndex: { taskId, dimension, model, promptVersion: "v1", sampleIndex: 0, }, }, update: { rating }, create: { taskId, dimension, model, promptVersion: "v1", sampleIndex: 0, rating, rationale: "Demonstration rating (synthetic panel) — not an LLM output.", rawResponse: { demo: true }, }, }); } } } } async function main(): Promise { for (const occupation of DEMO) { await seedDemoOccupation(occupation); } console.log( `Seeded ${DEMO.length} demo occupations (${DEMO.reduce((n, o) => n + o.tasks.length, 0)} tasks) with synthetic panel ratings.`, ); console.log("Next: RATER_MODELS=demo-panel-a,demo-panel-b pnpm score:recompute"); } main() .catch((error) => { console.error(error); process.exitCode = 1; }) .finally(() => prisma.$disconnect());