Files
pig/apps/web/src/components/motion/model.ts
T
karti 516685526c 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>
2026-08-17 18:27:03 -07:00

48 lines
1.9 KiB
TypeScript

/**
* The shape a template arrives in on the wire.
*
* It describes a *response*, not the server's own row, so it is declared here
* rather than imported from `@pig/db`: a column the API does not serialise must
* be absent, and one it may not have joined yet must be optional, or the
* compiler asserts a guarantee the JSON does not carry. The enums are the
* exception — those come from `@pig/core`, because a kind the ontology has
* dropped should stop compiling rather than keep rendering.
*
* `fields` is the deliberate hole in the type. It is author-written JSON whose
* shape varies by kind and is not validated on the way out of the database, so
* it arrives as `unknown` and is narrowed at the point of use in `FieldsView`.
* Typing it as the shape we hope for would move a runtime crash into a place
* where nobody is looking for it.
*
* There is only one type here on purpose. Eight more were written alongside it
* and never imported, and by the time anyone read them they no longer matched
* `services/motion.ts` — `EngagementDetail` promised `.artifacts` where the
* endpoint returns `.stages`. Each page declares the subset of the response it
* actually reads, which is checked against the `get<T>()` call that fetches it;
* a shared file that nothing imports is checked against nothing at all.
*/
import type { DemandStage, MotionKind, MotionVisibility } from '@pig/core';
export interface MotionTemplateView {
id: string;
kind: MotionKind;
/** Stable across versions — the identity of the lineage, not of the row. */
slug: string;
version: number;
title: string;
summary: string;
body: string;
fields: unknown;
stage: DemandStage;
visibility: MotionVisibility;
ownerUserId: string | null;
ownerName?: string | null;
supersedesId: string | null;
originArtifactId: string | null;
isSystem: boolean;
usageCount: number;
archivedAt: string | null;
createdAt: string;
updatedAt: string;
}