diff --git a/packages/db/src/seed/demo/index.ts b/packages/db/src/seed/demo/index.ts index d3a2fbc..0632916 100644 --- a/packages/db/src/seed/demo/index.ts +++ b/packages/db/src/seed/demo/index.ts @@ -55,7 +55,7 @@ import { seedCalendar } from './calendar'; import { seedCompliance } from './compliance'; import { seedDemandPaper } from './contracts'; import { seedDemand } from './demand'; -import { seedHostedLearn, seedLearn } from './learn'; +import { seedHostedLearn } from './learn'; import { seedMotionEngagements } from './motion'; import { seedSupply } from './supply'; @@ -167,7 +167,6 @@ export async function seedDemo(context: DemoContext): Promise { const ownerUserId = owner?.id ?? null; const calendar = await seedCalendar(context, ownerUserId); - const learn = await seedLearn(context, ownerUserId); // The self-hosted set, which is real rather than invented — see the long // note on `seedHostedLearn`. Folded into the demo seed so one command gives @@ -220,9 +219,6 @@ export async function seedDemo(context: DemoContext): Promise { `${compliance.decisions.total} export-control decisions (allow, needs_review, block), ` + `${calendar.total} calendar entries (${calendar.added} new)`, ); - console.log( - ` ${learn.total} illustrative concept videos (${learn.added} new), members-only`, - ); console.log( ` ${motion.engagements} Motion engagement(s) with ${motion.artifacts} artefact(s) and ` + `${motion.scores} qualification score(s) — ${motion.promoted} artefact promoted back into ` + diff --git a/packages/db/src/seed/demo/learn.ts b/packages/db/src/seed/demo/learn.ts index 4b59060..d329e8e 100644 --- a/packages/db/src/seed/demo/learn.ts +++ b/packages/db/src/seed/demo/learn.ts @@ -1,10 +1,28 @@ /** - * The Learn library: the illustrative demo rows, and the PIG-hosted real ones. + * The Learn library: the PIG-hosted recordings. * - * They sit in one module because they write to one table and share the - * idempotency key, but they are not the same kind of data: the first set is - * invented and prefixed, the second is genuine product footage and survives - * `--clear`. + * There used to be two illustrative rows here as well, embedding public + * recordings from the Cap instance at video.karti.ai under `DEMO — ` titles, so + * that the `supply` and `demand` concept tracks were not empty. Both are gone, + * for two reasons that arrived together. + * + * One of the two recordings had since been removed from that instance: + * `sjqqvthbfma27bm` answers 404 on both `/s/` and `/embed/`, so the card + * promised six seconds of teaching and played a dead frame. That is the exact + * failure the previous version of this comment was written to record — an id + * that 404s behind a card that claims content — and it came back because the + * footage lives somewhere this repository does not control. + * + * The other is judgement: /learn is the page deliberately shown to outsiders, + * and a `DEMO — ` card sitting beside genuine product footage makes the whole + * page read as half-placeholder to exactly the audience it exists to convince. + * An empty concept track renders a proper empty state saying nothing has been + * published yet, which is true and says so. + * + * If `supply` and `demand` get purpose-shot recordings, they belong in + * `HOSTED_LEARN_MANIFEST` below and rendered by `scripts/learn-film.mjs` like + * the platform track, not embedded from an instance whose contents can change + * underneath this file. */ import { LEARN_MEDIA_PATH_PREFIX, isLearnMediaFilename, learnMediaContentType } from '@pig/core'; import { eq } from 'drizzle-orm'; @@ -13,115 +31,6 @@ import { resolve } from 'node:path'; import { learnResources, users } from '../../schema/index'; import type { DemoContext } from './index'; -/** - * Every id below is a REAL public recording on the Cap instance at - * video.karti.ai, and every duration is the MEASURED length of the file that - * embed plays. Both were re-checked against the instance — `/s/` and - * `/embed/` answer 200, and the recordings are 60.4s and 6.2s — because - * this list previously stated neither truthfully. - * - * It used to carry four rows, two of them sharing the id `0n6n9p83efnxbs2` - * under different titles and different stated lengths. That id is not a - * recording at all — it 404s, and the only other place it appears in this - * repository is as the fixture string in `apps/api/test/learn.test.ts`, which - * is almost certainly where it was copied from. So two cards each promised a - * quarter of an hour of teaching and played nothing, on the one page that is - * deliberately shown to outsiders. A card that lies about its own content is - * worse than a track with one card in it, and there are exactly two public - * recordings on that instance — so there are exactly two rows here, one per - * concept track. - * - * The titles remain illustrative and prefixed, which is the standing bargain - * for demo rows: the concepts they name are the ones this business actually - * teaches, and the footage behind them is whatever genuinely exists. The - * bargain only holds while the stated LENGTH is true, since that is the one - * claim a viewer can check before pressing play. - * - * PLATFORM rows are not seeded here. The five real recordings in - * HOSTED_LEARN_MANIFEST cover that track, and illustrative Cap rows beneath - * genuine ones made the page read as half-placeholder to the exact audience it - * is meant to convince. - * - * The concept rows stay: `supply` and `demand` have no purpose-shot recordings - * yet, and an empty track hides the shape of the page. They are - * `members`-visible, which the CHECK constraint enforces anyway — only - * `platform` may be `code`. - */ -export async function seedLearn( - context: DemoContext, - ownerUserId: string | null, -): Promise<{ total: number; added: number }> { - const { db, prefix } = context; - - const LEARN_RESOURCES = [ - { - track: 'supply' as const, - title: `${prefix}How neocloud capacity is actually priced`, - summary: 'Reserved versus on-demand, commitment length, and where the spread comes from.', - externalId: '1rqq9rk4dpp71fd', - visibility: 'members' as const, - // 60.4s on the wire, rounded down: a duration that overstates by a - // second is the same class of claim as one that overstates by minutes. - durationSeconds: 60, - sortOrder: 10, - }, - { - track: 'demand' as const, - title: `${prefix}What a hold takes off the board`, - summary: - 'A hold reserves hours nobody else can be quoted, and it is not revenue until it converts.', - externalId: 'sjqqvthbfma27bm', - visibility: 'members' as const, - durationSeconds: 6, - sortOrder: 10, - }, - ]; - - /* - * The unique key is (track, provider, external_id), so the SAME recording on - * TWO tracks inserts perfectly happily — which is how one id came to sit - * behind two different titles and two different stated durations for as long - * as it did. The database cannot catch that; this can. Thrown before any - * insert, and loudly, because it is a typo in a literal rather than a - * condition of the environment: there is nothing for an operator to fix at - * run time and nothing worth continuing past. - */ - const ids = LEARN_RESOURCES.map((resource) => resource.externalId); - if (new Set(ids).size !== ids.length) { - throw new Error( - 'Two demo learn resources share an external id — one of them would be a lie about its own content.', - ); - } - - let learnAdded = 0; - for (const resource of LEARN_RESOURCES) { - // Idempotent on the unique key rather than an existence check, which is - // the whole reason that constraint exists: onConflictDoNothing without one - // is a silent no-op and has duplicated seed data here twice before. - const inserted = await db - .insert(learnResources) - .values({ - track: resource.track, - title: resource.title, - summary: resource.summary, - url: `https://video.karti.ai/s/${resource.externalId}`, - provider: 'cap', - externalId: resource.externalId, - visibility: resource.visibility, - durationSeconds: resource.durationSeconds, - sortOrder: resource.sortOrder, - addedByUserId: ownerUserId, - }) - .onConflictDoNothing({ - target: [learnResources.track, learnResources.provider, learnResources.externalId], - }) - .returning({ id: learnResources.id }); - if (inserted.length) learnAdded += 1; - } - - return { total: LEARN_RESOURCES.length, added: learnAdded }; -} - // ------------------------------------------------------- PIG-hosted learn // // Real videos, served by PIG itself from PIG_MEDIA_DIR — not demo data. They