Files
pig/packages/db/src/seed/demo/motion.ts
T
karti 666310b264
CI / verify (push) Successful in 7m20s
CI / publish (push) Has been skipped
Fill the demo book's motion: eight engagements, one at every stage
The Motion half of the demo book was two engagements, which was enough to show
that the loop works and not enough to show what the product is for. The page
that matters asks whether the motion is repeating, and a stage rail of zeroes
cannot answer it.

Eight engagements now, one at every open stage, hanging off demand deals the
demand book already creates — the pipeline happened to have exactly one open
deal at each of the eight, so no deal was invented and the quoted pipeline
counts are unchanged. Forty-four artefacts, nineteen scores, one promotion.

Three things the book is laid out to prove that a folder of templates cannot.

Every stage is occupied, and every one of the twelve starter templates is
instantiated at least once, so "stages covered 8/8" is a measurement rather
than a claim about the seed.

Scores move, and sometimes move down. Nineteen scores across eight
trajectories, with nine dimensions regressing somewhere — the Verity
fine-tuning record runs 62.5 -> 60.5 -> 84.0 -> 80.8, because a scorecard that
only ever rises is a ratchet and teaches a reader to distrust it. Every score
is computed with `motionScoreBasisPoints` and banded with `motionBand` rather
than written as a literal, so the seed and the product cannot disagree about
what the same dimensions are worth.

The artefact bodies are the customer's own facts — named people, real volumes,
the specific thing going wrong, and a live unresolved risk in each. An artefact
whose body is the template with the blanks still in it is precisely what this
data exists to disprove. Six of the forty-four have no template at all, which
is the honest shape of an engagement and the reason `kind` is carried on the
artefact rather than derived: `engagement_artifacts.kind` is NOT NULL and a
derived kind would have been null for exactly those six.

The bodies live in JSON beside the loader for the same reason the starter
library's do — forty-four markdown bodies as backtick strings is a module
nobody can review.

The promotion copies `body` from the artefact verbatim, as `promoteArtifact`
does, rather than writing a hand-authored version 2. A demo that produced a row
the real path could not have produced would teach the wrong shape of the table.

Two name collisions the authors could not see are fixed: a Quillon contact
shared a full name with a demo seller, and an Aurelian one shared a surname
with another. `usage_count` is raised once per template rather than once per
artefact, so it stays symmetrical with the decrement `clear()` already does —
verified by tearing the book down and confirming the library returns to twelve
templates with every counter back at zero, since a counter left above zero
makes a starter template permanently un-editable.
2026-08-19 01:05:48 -07:00

372 lines
14 KiB
TypeScript

/**
* The demo book's Motion half: eight engagements, one at every open stage.
*
* The loop is the only reason Motion exists:
*
* library template --instantiate--> engagement artefact --promote--> v2
*
* A screenshot of the library alone shows a folder of documents, which is not
* the claim being made. So the book is laid out to prove three things a folder
* cannot. **Every stage is occupied**, because the page that matters asks
* whether the motion is repeating and a rail of zeroes cannot answer it.
* **Scores move, and sometimes move down** — a single score is a number and
* four are a trajectory; a scorecard that only ever rises is a ratchet, so the
* Verity fine-tuning record deliberately goes 62.5 → 60.5 → 84.0 → 80.8, and
* nine dimensions regress somewhere in the book. And **one engagement closes
* the loop**, putting a version 2 into the library carrying `supersedes_id`
* back to the shipped version 1 and `origin_artifact_id` back to the artefact
* that proved it, so both halves of the FK cycle are populated on a first run.
*
* The artefact bodies are the point. An artefact whose body is the template
* with the blanks still in it is exactly what this data exists to disprove, so
* every one of them is written as the customer's own facts — named people, real
* volumes, the specific thing going wrong. That is also why they live in JSON
* beside this file rather than in TypeScript literals: forty-four markdown
* bodies as backtick strings would be a module nobody can review.
*
* Every engagement hangs off a demand deal the demand book already created,
* rather than a deal of its own. An engagement has no independent existence,
* and inventing deals here would put rows on a pipeline whose counts are quoted
* in `demo/index.ts`.
*
* Scores are computed with `motionScoreBasisPoints` and banded with
* `motionBand`, never written as literals. Hard-coding 8280 would let the seed
* and the product disagree about what the same dimensions are worth, and the
* first place anyone would notice is a demo.
*/
import {
motionBand,
motionScoreBasisPoints,
type ArtifactStatus,
type DemandStage,
type EngagementStatus,
type MotionDimensionScore,
type MotionKind,
} from '@pig/core';
import { and, eq, like, sql } from 'drizzle-orm';
import {
demandDeals,
engagementArtifacts,
engagements,
motionTemplates,
qualificationScores,
users,
} from '../../schema/index';
import type { DemoContext } from './index';
import aurelianCommittedCapacity from './motion/aurelian-committed-capacity.json' with { type: 'json' };
import halcyonPostTraining from './motion/halcyon-post-training.json' with { type: 'json' };
import northwindBlackwell from './motion/northwind-blackwell.json' with { type: 'json' };
import northwindFleetExpansion from './motion/northwind-fleet-expansion.json' with { type: 'json' };
import quillonEvalHarness from './motion/quillon-eval-harness.json' with { type: 'json' };
import tessellateBurstInference from './motion/tessellate-burst-inference.json' with { type: 'json' };
import verityEuFineTuning from './motion/verity-eu-fine-tuning.json' with { type: 'json' };
import verityEuInference from './motion/verity-eu-inference.json' with { type: 'json' };
interface DemoArtefact {
readonly templateSlug: string | null;
/*
* Carried on the artefact rather than looked up from its template, because
* six of the forty-four have no template at all — the work a lead does that
* no library entry covers, which is the honest shape of an engagement. A
* `kind` derived from the template would have been null for exactly those
* six, and `engagement_artifacts.kind` is NOT NULL.
*/
readonly kind: MotionKind;
readonly stage: DemandStage;
readonly title: string;
readonly status: ArtifactStatus;
readonly authoredDaysAgo: number;
readonly body: string;
}
interface DemoScore {
readonly daysAgo: number;
readonly note: string;
readonly dimensions: readonly MotionDimensionScore[];
}
interface DemoEngagement {
readonly key: string;
readonly dealName: string;
readonly accountName: string;
readonly stage: DemandStage;
readonly status: EngagementStatus;
readonly summary: string;
readonly openedDaysAgo: number;
readonly artefacts: readonly DemoArtefact[];
readonly scores: readonly DemoScore[];
readonly promote: { readonly artefactTitle: string; readonly intoSlug: string } | null;
}
/**
* Ordered oldest-opened first, so the engagement list reads as a book somebody
* built up rather than eight things that appeared at once.
*/
const DEMO_ENGAGEMENTS = [
northwindFleetExpansion,
northwindBlackwell,
halcyonPostTraining,
verityEuFineTuning,
verityEuInference,
aurelianCommittedCapacity,
tessellateBurstInference,
quillonEvalHarness,
] as unknown as readonly DemoEngagement[];
/**
* Which demo seller owns which account.
*
* Assigned by account rather than by engagement, because the two accounts that
* appear twice (Verity Health AI, Northwind Robotics) are one relationship each
* — a second engagement handed to a different owner would contradict the
* continuity the artefacts themselves describe.
*/
const OWNER_BY_ACCOUNT: Readonly<Record<string, string>> = {
'Quillon AI': 'Wren Abbot',
'Aurelian Systems': 'Marcus Oyelaran',
'Verity Health AI': 'Ines Fabre',
'Tessellate Labs': 'Rosalind Achebe',
'Halcyon Research': 'Ines Fabre',
'Northwind Robotics': 'Marcus Oyelaran',
};
async function findDeal(context: DemoContext, name: string): Promise<string | undefined> {
const [deal] = await context.db
.select({ id: demandDeals.id })
.from(demandDeals)
.where(eq(demandDeals.name, name))
.limit(1);
return deal?.id;
}
async function findSeller(context: DemoContext, name: string): Promise<string | null> {
const [seller] = await context.db
.select({ id: users.id })
.from(users)
.where(eq(users.name, `${context.prefix}${name}`))
.limit(1);
return seller?.id ?? null;
}
/** The shipped version 1 of a lineage. Promotion needs its id and its version. */
async function findTemplate(
context: DemoContext,
slug: string,
): Promise<{ id: string; version: number; summary: string } | undefined> {
const [template] = await context.db
.select({
id: motionTemplates.id,
version: motionTemplates.version,
summary: motionTemplates.summary,
})
.from(motionTemplates)
.where(and(eq(motionTemplates.slug, slug), eq(motionTemplates.isSystem, true)))
.limit(1);
return template;
}
export async function seedMotionEngagements(
context: DemoContext,
): Promise<{
engagements: number;
artifacts: number;
scores: number;
promoted: number;
missingDeals: readonly string[];
}> {
const { db, prefix, at } = context;
/*
* The whole section is skipped if any demo engagement already exists.
*
* `engagements_demand_deal_key` would refuse a duplicate engagement, but
* nothing would refuse a second set of artefacts or — worse — a second
* promotion, and a second promotion writes case-study-frame v3 on top of the
* v2 this seed already created. `qualification_scores` is append-only by
* design and so has no constraint to conflict on at all; nineteen more rows
* on every run would turn every trajectory into noise. An existence check on
* the parent is the only thing that covers all three.
*/
const existing = await db
.select({ id: engagements.id })
.from(engagements)
.where(like(engagements.summary, `${prefix}%`))
.limit(1);
if (existing.length > 0) {
return { engagements: 0, artifacts: 0, scores: 0, promoted: 0, missingDeals: [] };
}
const playbook = await findTemplate(context, 'strategic-deployment-playbook');
const templateCache = new Map<string, { id: string; version: number; summary: string }>();
const templateBySlug = async (slug: string) => {
if (!templateCache.has(slug)) {
const found = await findTemplate(context, slug);
if (found) templateCache.set(slug, found);
}
return templateCache.get(slug);
};
const usageByTemplate = new Map<string, number>();
const missingDeals: string[] = [];
let engagementCount = 0;
let artifactCount = 0;
let scoreCount = 0;
let promotedCount = 0;
for (const demo of DEMO_ENGAGEMENTS) {
/*
* A missing deal is reported, not skipped in silence.
*
* Every engagement hangs off a deal the demand book creates, and that book
* skips an account it has already seen — so a database where the accounts
* survive but the deals were removed leaves these lookups empty and this
* whole section a no-op that reports zeroes and looks like a bug in the
* loader. It was found exactly that way, on a shared development database.
*/
const dealId = await findDeal(context, demo.dealName);
if (!dealId) {
missingDeals.push(demo.dealName);
continue;
}
const ownerName = OWNER_BY_ACCOUNT[demo.accountName.replace(prefix, '')];
const ownerId = ownerName ? await findSeller(context, ownerName) : null;
const [engagement] = await db
.insert(engagements)
.values({
demandDealId: dealId,
playbookTemplateId: playbook?.id ?? null,
ownerUserId: ownerId,
status: demo.status,
summary: demo.summary,
openedAt: at(-demo.openedDaysAgo),
closedAt: demo.status === 'won' ? at(-Math.floor(demo.openedDaysAgo / 8)) : null,
})
.returning({ id: engagements.id });
if (!engagement) continue;
engagementCount += 1;
const artefactIdByTitle = new Map<string, { id: string; body: string }>();
for (const artefact of demo.artefacts) {
const template = artefact.templateSlug ? await templateBySlug(artefact.templateSlug) : undefined;
const [written] = await db
.insert(engagementArtifacts)
.values({
engagementId: engagement.id,
templateId: template?.id ?? null,
kind: artefact.kind,
stage: artefact.stage,
title: artefact.title,
body: artefact.body,
status: artefact.status,
authoredByUserId: ownerId,
createdAt: at(-artefact.authoredDaysAgo),
updatedAt: at(-artefact.authoredDaysAgo),
})
.returning({ id: engagementArtifacts.id });
if (!written) continue;
artifactCount += 1;
artefactIdByTitle.set(artefact.title, { id: written.id, body: artefact.body });
if (template) {
usageByTemplate.set(template.id, (usageByTemplate.get(template.id) ?? 0) + 1);
}
}
for (const score of demo.scores) {
const basisPoints = motionScoreBasisPoints(score.dimensions);
await db.insert(qualificationScores).values({
engagementId: engagement.id,
frameworkTemplateId: (await templateBySlug('trainability-qualification'))?.id ?? null,
/*
* Copied, not referenced. `qualification_scores.dimensions` stores the
* framework AS IT WAS SCORED — weights included — so that re-authoring
* the scorecard later cannot retroactively change what a past score
* meant. Handing the same array to every row would make that snapshot
* a shared object.
*/
dimensions: score.dimensions.map((dimension) => ({ ...dimension })),
basisPoints,
band: motionBand(basisPoints).label,
note: score.note,
scoredByUserId: ownerId,
scoredAt: at(-score.daysAgo),
});
scoreCount += 1;
}
/*
* The promotion, written by hand rather than by calling the API service:
* the seed has no HTTP surface and no principal. It follows the same rules
* `promoteArtifact` does — version = newest + 1, `supersedesId` to the
* shipped v1, `originArtifactId` to the artefact, **`body` copied from the
* artefact verbatim**, `visibility: 'shared'`, `isSystem: false` — because
* a demo that produced a row the real path could not have produced would
* teach the wrong shape of the table.
*
* `isSystem` is false and the title is prefixed, which is what lets
* `clear()` find it. It is a version 2 of a shipped lineage, so removing
* the demo book genuinely returns the library to what ships.
*/
if (demo.promote) {
const artefact = artefactIdByTitle.get(demo.promote.artefactTitle);
const supersedes = await templateBySlug(demo.promote.intoSlug);
if (artefact && supersedes) {
const [promoted] = await db
.insert(motionTemplates)
.values({
kind: 'case_study',
stage: 'expansion',
slug: demo.promote.intoSlug,
version: supersedes.version + 1,
title: demo.promote.artefactTitle,
summary:
`Version ${supersedes.version + 1}, promoted out of the ` +
`${demo.accountName.replace(prefix, '')} expansion. The frame as it was actually ` +
'filled in, with the numbers the customer approved for named use.',
body: artefact.body,
fields: { promotedFrom: demo.promote.artefactTitle, engagement: demo.key },
visibility: 'shared',
ownerUserId: ownerId,
supersedesId: supersedes.id,
originArtifactId: artefact.id,
isSystem: false,
})
.onConflictDoNothing({ target: [motionTemplates.slug, motionTemplates.version] })
.returning({ id: motionTemplates.id });
if (promoted) {
await db
.update(engagementArtifacts)
.set({ promotedTemplateId: promoted.id })
.where(eq(engagementArtifacts.id, artefact.id));
promotedCount += 1;
}
}
}
}
/*
* `usage_count` is raised once per template at the end rather than once per
* artefact inside the loop, because it is the counter that makes a template
* permanently un-editable and `clear()` gives back exactly what was borrowed.
* One statement per template keeps the two halves symmetrical.
*/
for (const [templateId, count] of usageByTemplate) {
await db
.update(motionTemplates)
.set({ usageCount: sql`${motionTemplates.usageCount} + ${count}` })
.where(eq(motionTemplates.id, templateId));
}
return {
engagements: engagementCount,
artifacts: artifactCount,
scores: scoreCount,
promoted: promotedCount,
missingDeals,
};
}