Add Motion: the go-to-market operating system on top of the ledger

The ledger answers which contracted capacity is sold, to whom, at what
margin. It says nothing about the motion — the repeatable practice that
turns a customer conversation into a scoped deployment, and turns that
deployment into something the next one reuses.

Motion is deliberately not a parallel entity tree. DEMAND_STAGES already
is the motion, so Motion binds reusable artefacts to the stages of a
demand deal that already exists: an engagement hangs off one deal,
cascade deleted, one per deal by unique constraint.

Nine closed kinds, each declaring which stages it serves, and a starter
library of twelve templates covering all eight open stages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-17 18:27:03 -07:00
parent 99d165b5e5
commit 516685526c
61 changed files with 13013 additions and 29 deletions
+472
View File
@@ -0,0 +1,472 @@
/**
* Two demo engagements, chosen so the loop is visible without a live customer.
*
* The loop is the only reason Motion exists:
*
* library template --instantiate--> engagement artifact --promote--> v2
*
* A screenshot of the library alone shows a folder of documents, which is not
* the claim. So one engagement is **mid-POC with three qualification scores**,
* because a single score is a number and three are a trajectory — the movement
* is the evidence that qualification happened at all — and the other is
* **closed-won with a promoted artifact**, which puts a version 2 into the
* library carrying `supersedes_id` back to the shipped version 1 and
* `origin_artifact_id` back to the engagement that proved it. Both halves of
* the FK cycle are therefore populated on a first run.
*
* Both hang off demand deals the demand book already created, rather than deals
* of their own: an engagement has no independent existence, and inventing a
* deal here would put a thirteenth row on a pipeline whose counts are quoted in
* `demo/index.ts`.
*
* The scores are computed with `motionScoreBasisPoints` rather than written as
* literals. Hard-coding 6600 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 MotionDimensionScore } 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';
/**
* The scorecard's dimensions and their authored weights, copied from
* `seed/motion/trainability-qualification.json`.
*
* Copied rather than read out of the seeded row, deliberately: a stored score
* is a snapshot of the framework *as it was scored*, which is the whole reason
* `qualification_scores.dimensions` holds the weights rather than a reference.
* Reading them live would make the demo history silently re-weight itself the
* day someone edits the template, which is precisely the behaviour the column
* exists to prevent.
*/
const SCORECARD_WEIGHTS: readonly { readonly id: string; readonly weight: number }[] = [
{ id: 'task_definability', weight: 10 },
{ id: 'verifier_quality', weight: 13 },
{ id: 'trace_data_rights', weight: 8 },
{ id: 'baseline_measured', weight: 8 },
{ id: 'headroom', weight: 10 },
{ id: 'env_constructibility', weight: 8 },
{ id: 'metric_owner', weight: 9 },
{ id: 'budget_source', weight: 9 },
{ id: 'exec_sponsor', weight: 4 },
{ id: 'security_legal_path', weight: 8 },
{ id: 'forcing_function', weight: 8 },
{ id: 'expansion_surface', weight: 5 },
];
/**
* Three passes over the same scorecard, in the order they were run.
*
* The trajectory is the teaching: the deal opens borderline on an unmeasured
* verifier and no named budget, the POC scoping call fixes the verifier, and
* the third pass lands once finance names a source. Two dimensions deliberately
* do NOT move — `trace_data_rights` and `expansion_surface` — because a
* scorecard where every number rises on every pass is a scorecard nobody is
* really filling in.
*/
const SCORE_PASSES: readonly {
readonly daysAgo: number;
readonly note: string;
readonly scores: Readonly<Record<string, number>>;
}[] = [
{
daysAgo: 52,
note:
'First pass, straight out of technical discovery. The task is well specified and there ' +
'is real headroom, but the verifier is a rubric nobody has run against human labels and ' +
'no budget line has been named. Not yet — and the two things to fix are explicit.',
scores: {
task_definability: 3,
verifier_quality: 1,
trace_data_rights: 2,
baseline_measured: 2,
headroom: 3,
env_constructibility: 2,
metric_owner: 2,
budget_source: 1,
exec_sponsor: 2,
security_legal_path: 2,
forcing_function: 2,
expansion_surface: 3,
},
},
{
daysAgo: 27,
note:
'Re-scored after the POC scoping call. They ran the judge against 300 human-labelled ' +
'items and the baseline is now measured rather than remembered. Budget is still a ' +
'reallocation nobody has signed.',
scores: {
task_definability: 3,
verifier_quality: 3,
trace_data_rights: 2,
baseline_measured: 3,
headroom: 3,
env_constructibility: 3,
metric_owner: 3,
budget_source: 1,
exec_sponsor: 2,
security_legal_path: 3,
forcing_function: 2,
expansion_surface: 3,
},
},
{
daysAgo: 6,
note:
'Third pass, in POC week two. Finance named the source and the VP Engineering owns the ' +
'metric in their own planning doc. Data rights are unchanged: the customer still cannot ' +
'export traces beyond the pilot without a DPA amendment, and that is the live risk.',
scores: {
task_definability: 4,
verifier_quality: 3,
trace_data_rights: 2,
baseline_measured: 3,
headroom: 4,
env_constructibility: 3,
metric_owner: 4,
budget_source: 3,
exec_sponsor: 3,
security_legal_path: 3,
forcing_function: 3,
expansion_surface: 3,
},
},
];
function dimensionsFor(scores: Readonly<Record<string, number>>): MotionDimensionScore[] {
return SCORECARD_WEIGHTS.map((dimension) => ({
id: dimension.id,
weight: dimension.weight,
score: scores[dimension.id] ?? 0,
}));
}
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;
}
async function findTemplate(
context: DemoContext,
slug: string,
): Promise<{ id: string; version: number } | undefined> {
const [template] = await context.db
.select({ id: motionTemplates.id, version: motionTemplates.version })
.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 }> {
const { db, prefix, at } = context;
/*
* The whole section is skipped if either engagement already exists.
*
* `engagements_demand_deal_key` would refuse a duplicate engagement, but
* nothing would refuse a second set of artifacts 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; three more rows on
* every run would turn the 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 };
}
const playbook = await findTemplate(context, 'strategic-deployment-playbook');
const scorecard = await findTemplate(context, 'trainability-qualification');
const pocTemplate = await findTemplate(context, 'post-training-poc');
const caseStudy = await findTemplate(context, 'case-study-frame');
let engagementCount = 0;
let artifactCount = 0;
let scoreCount = 0;
let promotedCount = 0;
// ------------------------------------------------- mid-POC, still scoring
const halcyonDealId = await findDeal(context, `${prefix}Managed post-training run`);
const ines = await findSeller(context, 'Ines Fabre');
/*
* A missing deal is reported, not skipped in silence.
*
* Both engagements hang off deals 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.
*/
if (!halcyonDealId) {
console.warn(
` No "${prefix}Managed post-training run" deal — skipping the mid-POC engagement. ` +
'Run `pnpm db:demo -- --clear` and reseed to rebuild the demand book.',
);
}
if (halcyonDealId) {
const [engagement] = await db
.insert(engagements)
.values({
demandDealId: halcyonDealId,
playbookTemplateId: playbook?.id ?? null,
ownerUserId: ines,
status: 'open',
summary: `${prefix}Post-training motion on the Halcyon summarisation workflow — POC week two.`,
openedAt: at(-58),
})
.returning({ id: engagements.id });
if (engagement) {
engagementCount += 1;
await db.insert(engagementArtifacts).values({
engagementId: engagement.id,
templateId: scorecard?.id ?? null,
kind: 'discovery',
stage: 'qualification',
title: `${prefix}Discovery notes — Halcyon summarisation`,
body:
'## What they are trying to do\n\n' +
'Reduce reviewer time on internal research summaries. Today a senior analyst reads a ' +
'40-page source and writes a one-page brief; the bar is "would the head of research ' +
'send this out unedited".\n\n' +
'## Unit of work\n\n' +
'One source document in, one brief out. They have 4,100 historical pairs and the ' +
'reviewer decision on each.\n\n' +
'## Verifier\n\n' +
'A model judge calibrated against 300 of those human decisions. Agreement is 0.81 ' +
'Cohens kappa, measured on a held-out slice rather than the calibration set.\n\n' +
'## The live risk\n\n' +
'Trace export beyond the pilot needs a DPA amendment their counsel has not seen yet.',
status: 'final',
authoredByUserId: ines,
fields: { source: 'Technical discovery call, plus the follow-up with the data owner.' },
});
await db.insert(engagementArtifacts).values({
engagementId: engagement.id,
templateId: pocTemplate?.id ?? null,
kind: 'poc',
stage: 'poc',
title: `${prefix}POC plan — summarisation reward model`,
body:
'## Success criterion, agreed in writing\n\n' +
'Judge-scored acceptance rate on a held-out set of 400 briefs rises from the measured ' +
'baseline of 61% to at least 78%, at no more than 1.4× the current inference cost ' +
'per brief.\n\n' +
'## What we run\n\n' +
'Two weeks on 32× H100. Week one builds the environment and reproduces the ' +
'baseline; week two is the training run and the evaluation.\n\n' +
'## What we hand back\n\n' +
'The environment, the eval harness, and the number — whichever way it comes out. ' +
'A POC that reports a failure honestly is what makes the next number believable.\n\n' +
'## Open\n\n' +
'Trace rights beyond the pilot. Blocked on the DPA amendment.',
status: 'review',
authoredByUserId: ines,
fields: { baselineAcceptancePct: 61, targetAcceptancePct: 78, gpuCount: 32, weeks: 2 },
});
artifactCount += 2;
for (const pass of SCORE_PASSES) {
const dimensions = dimensionsFor(pass.scores);
const basisPoints = motionScoreBasisPoints(dimensions);
await db.insert(qualificationScores).values({
engagementId: engagement.id,
frameworkTemplateId: scorecard?.id ?? null,
// The column is `Record<string, unknown>[]`; Drizzle's jsonb `$type`
// will not take an interface with readonly properties, and widening
// the column to suit the seed would lose the shape everywhere else.
dimensions: dimensions as unknown as Record<string, unknown>[],
basisPoints,
band: motionBand(basisPoints).label,
note: `${prefix}${pass.note}`,
scoredByUserId: ines,
scoredAt: at(-pass.daysAgo),
});
scoreCount += 1;
}
}
}
// ------------------------------------- closed won, and promoted afterwards
const northwindDealId = await findDeal(context, `${prefix}A100 inference pilot`);
const marcus = await findSeller(context, 'Marcus Oyelaran');
if (!northwindDealId) {
console.warn(
` No "${prefix}A100 inference pilot" deal — skipping the closed-won engagement, and ` +
'with it the promotion that demonstrates the loop.',
);
}
if (northwindDealId) {
const [engagement] = await db
.insert(engagements)
.values({
demandDealId: northwindDealId,
playbookTemplateId: playbook?.id ?? null,
ownerUserId: marcus,
status: 'won',
summary: `${prefix}Northwind inference pilot — won, and written up.`,
openedAt: at(-190),
closedAt: at(-96),
})
.returning({ id: engagements.id });
if (engagement) {
engagementCount += 1;
const [artifact] = await db
.insert(engagementArtifacts)
.values({
engagementId: engagement.id,
templateId: caseStudy?.id ?? null,
kind: 'case_study',
stage: 'expansion',
title: `${prefix}Northwind Robotics — A100 inference pilot`,
body:
'## The problem\n\n' +
'Northwind ran perception inference on reserved A100s they had bought for training, ' +
'and were paying training prices for a serving workload with a 40:1 peak-to-trough ' +
'shape.\n\n' +
'## What we did\n\n' +
'Moved the trough onto committed capacity and the peak onto burst, with the split ' +
'set by their own 90-day request trace rather than by a headline number.\n\n' +
'## The number\n\n' +
'Cost per million inferences fell 34%. Measured against their own billing, over a ' +
'full quarter, not against a list price.\n\n' +
'## What made it work, and what to reuse\n\n' +
'They had the request trace. Every engagement since has asked for it in the first ' +
'call, which is the change this write-up put into the frame itself.',
status: 'final',
authoredByUserId: marcus,
fields: {
metric: 'Cost per million inferences',
improvementPct: 34,
window: 'One quarter, customer billing',
approvedForExternalUse: false,
},
})
.returning({ id: engagementArtifacts.id, title: engagementArtifacts.title });
artifactCount += 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 the service does — version = previous + 1, `supersedesId` to the
* shipped v1, `originArtifactId` to the artifact, `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 (artifact && caseStudy) {
const [promoted] = await db
.insert(motionTemplates)
.values({
kind: 'case_study',
stage: 'expansion',
slug: 'case-study-frame',
version: caseStudy.version + 1,
title: `${prefix}Case Study Frame — ask for the request trace first`,
summary:
'Version 2, promoted out of the Northwind pilot write-up. Adds the one question ' +
'that made that engagement measurable: get the customers own usage trace ' +
'before anyone quotes a saving.',
body:
'_Promoted from an engagement. The change from version 1 is the section below._\n\n' +
'## Before you frame anything: get the trace\n\n' +
'A case study is only as good as the baseline it is measured against, and the ' +
'only baseline a customer cannot argue with afterwards is their own telemetry. ' +
'Ask for it in the first call, not at write-up time.\n\n' +
'## Everything else\n\n' +
'As version 1: the problem, what we did, the number, what to reuse.',
fields: {
promotedFrom: artifact.title,
addedSection: 'Before you frame anything: get the trace',
},
visibility: 'shared',
ownerUserId: marcus,
supersedesId: caseStudy.id,
originArtifactId: artifact.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, artifact.id));
promotedCount += 1;
}
}
}
}
/*
* `usage_count` is what the API reads to refuse a `PATCH` on a used
* template, so the demo must move it or a library where three templates have
* visibly been instantiated still reads as never used — and the rule the
* whole feature turns on would be undemonstrable. Incremented in one sweep at
* the end rather than per insert, so the count is derived from the artefacts
* that actually landed and `clear()` can undo it by counting the same rows.
*/
const instantiated = await db
.select({ templateId: engagementArtifacts.templateId })
.from(engagementArtifacts)
.where(like(engagementArtifacts.title, `${prefix}%`));
const usageByTemplate = new Map<string, number>();
for (const row of instantiated) {
if (row.templateId) usageByTemplate.set(row.templateId, (usageByTemplate.get(row.templateId) ?? 0) + 1);
}
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,
};
}