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
+18 -5
View File
@@ -152,14 +152,27 @@ jobs:
- name: Seed is idempotent
# A seed that duplicates on a second run corrupts any database it is
# pointed at twice, and nobody notices until the counts look odd.
#
# Two tables are counted, not one. This gate only ever watched
# `contacts`, and `contacts` is idempotent by an explicit existence
# check — so it could not see the class of regression it exists to
# catch, which is `onConflictDoNothing` firing at a constraint that is
# no longer there. The Motion starter library relies on exactly that
# clause against `motion_templates_slug_version_key`, so it is counted
# here too. Any table whose idempotency rests on a conflict target
# belongs in this list.
run: |
pnpm exec tsx packages/db/src/seed/index.ts > /dev/null
count() { docker exec "$PG_CONTAINER" psql -U pig -d pig -tAc "select count(*) from contacts"; }
BEFORE=$(count)
count() { docker exec "$PG_CONTAINER" psql -U pig -d pig -tAc "select count(*) from $1"; }
CONTACTS_BEFORE=$(count contacts)
TEMPLATES_BEFORE=$(count motion_templates)
pnpm exec tsx packages/db/src/seed/index.ts > /dev/null
AFTER=$(count)
echo "contacts: $BEFORE -> $AFTER"
test "$BEFORE" = "$AFTER" || { echo "SEED IS NOT IDEMPOTENT"; exit 1; }
CONTACTS_AFTER=$(count contacts)
TEMPLATES_AFTER=$(count motion_templates)
echo "contacts: $CONTACTS_BEFORE -> $CONTACTS_AFTER"
echo "motion_templates: $TEMPLATES_BEFORE -> $TEMPLATES_AFTER"
test "$CONTACTS_BEFORE" = "$CONTACTS_AFTER" || { echo "SEED IS NOT IDEMPOTENT"; exit 1; }
test "$TEMPLATES_BEFORE" = "$TEMPLATES_AFTER" || { echo "SEED IS NOT IDEMPOTENT"; exit 1; }
- name: Critical path E2E against Postgres and Hono
run: pnpm run test:e2e