diff --git a/motion/README.md b/motion/README.md index 46ed4c16..5edc3a7a 100644 --- a/motion/README.md +++ b/motion/README.md @@ -71,3 +71,49 @@ Like every release composition, this one ships a tracked `captions.json` next to ``` The smoke test (`release-recap.test.js`) asserts this schema, checks the counts, and regression-guards no em dashes in on-screen copy or captions (the design-bar violation QA caught on the prior text-card cut of this task). + +## Release-specific example: `release-0.25.0` + +`compositions/release-0.25.0/` is a panel-demo kit clip for the RoboCo v0.25.0 release. It builds on the `kit/` register instead of the text-card style, so it has no `theme.css` of its own. + +The story is "governance gets a better UI": the CEO types "Governance gets a better UI" into the panel intake, then three shipped feature cards — Env ladder, Collision map, Metrics donut — enter a kanban column and flip from `in progress` to `completed`. A cursor clicks through the cards, a stats overlay flashes "1 release / 25 agents / 1 human", and a toast confirms "v0.25.0 shipped / I approved once. 25 agents did the rest." The clip closes on the "roboco.tech" outro. + +The composition reuses the same `pk-frame` chrome, `pk-column`/`pk-card`, `pk-pill`, `pk-cursor`, `pk-toast`, and `pk-outro` pieces from `kit/`, plus the typing reveal wired through `props.js`. Each feature card uses the `pk-pill--swap-out` / `pk-pill--swap-in` pattern from `panel-demo` and `release-recap` to replace the `in progress` pill with `completed` on the same beat. + +### Preview / test this composition + +```bash +pnpm preview:release-0.25.0 +pnpm test # release-0.25.0.test.js is picked up by vitest +``` + +### `props.js` shape + +```js +{ + introText: string, // text that types into the panel intake field + toastTitle: string, // headline inside the shipping toast + toastBody: string, // sub-line inside the shipping toast +} +``` + +`window.__ORIENTATION__` is set for local preview only; the sidecar overwrites both globals at render time. + +### `captions.json` + +Same schema as `release-recap`: one `captions.json` next to the HTML with self-verified X and TikTok captions: + +```json +{ + "composition_id": "release-0.25.0", + "occasion": "release: RoboCo v0.25.0", + "platforms": { + "x": { "caption": "...", "char_count": 170, "limit": 280, "within_limit": true }, + "tiktok": { "caption": "...", "char_count": 347, "limit": 2200, "within_limit": true } + } +} +``` + +### Smoke-test invariants + +`release-0.25.0.test.js` extends the panel-demo register checks: both `vertical.html` (1080×1920) and `square.html` (1080×1080) parse with the HyperFrames params, the kit CSS/JS wiring is present, three feature cards each carry a progress-to-completed pill swap, the cursor and toast appear, the outro shows "roboco.tech", no external scripts are loaded, and no em dashes slip into on-screen copy or captions. diff --git a/motion/compositions/release-0.25.0/captions.json b/motion/compositions/release-0.25.0/captions.json new file mode 100644 index 00000000..31fc5a0a --- /dev/null +++ b/motion/compositions/release-0.25.0/captions.json @@ -0,0 +1,18 @@ +{ + "composition_id": "release-0.25.0", + "occasion": "release: RoboCo v0.25.0", + "platforms": { + "x": { + "caption": "RoboCo v0.25.0 is live. Env ladder from dev to prod. Collision map in every review. Metrics with a 90-day window. One human approved it; 25 agents shipped it. roboco.tech", + "char_count": 170, + "limit": 280, + "within_limit": true + }, + "tiktok": { + "caption": "RoboCo v0.25.0 shipped today. The panel now has an env ladder from dev to prod, a collision map surfaced in every review, metrics with a 90-day window and a status donut, plus a notification bell with real read/ack actions. Same setup: one human CEO sets direction, 25 AI agents do the work, and governance keeps control over the flow. roboco.tech", + "char_count": 347, + "limit": 2200, + "within_limit": true + } + } +} diff --git a/motion/compositions/release-0.25.0/props.js b/motion/compositions/release-0.25.0/props.js new file mode 100644 index 00000000..0217e5d5 --- /dev/null +++ b/motion/compositions/release-0.25.0/props.js @@ -0,0 +1,9 @@ +// Default sample props for local preview of the RoboCo v0.25.0 release clip. +// The video-renderer sidecar OVERWRITES this file at render time with the +// real per-request values + window.__ORIENTATION__. +window.__PROPS__ = { + introText: "Governance gets a better UI", + toastTitle: "v0.25.0 shipped", + toastBody: "I approved once. 25 agents did the rest.", +}; +window.__ORIENTATION__ = "vertical"; diff --git a/motion/compositions/release-0.25.0/release-0.25.0.test.js b/motion/compositions/release-0.25.0/release-0.25.0.test.js new file mode 100644 index 00000000..6773f870 --- /dev/null +++ b/motion/compositions/release-0.25.0/release-0.25.0.test.js @@ -0,0 +1,100 @@ +import { readFileSync, existsSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +// HTML-structure smoke test for the RoboCo v0.25.0 release composition. +// Extends the panel-demo register: panel chrome, typing reveal, feature cards +// with progress -> completed pill flips, cursor, stats overlay, toast, +// outro, offline constraint, and the captions.json schema/limits. + +const here = dirname(fileURLToPath(import.meta.url)); +const dir = here; +const read = (name) => readFileSync(join(dir, name), "utf8"); + +const orientations = [ + { file: "vertical.html", height: "1920" }, + { file: "square.html", height: "1080" }, +]; + +describe("release-0.25.0 composition", () => { + it("ships props.js (default preview values, sidecar overwrites at render)", () => { + expect(existsSync(join(dir, "props.js"))).toBe(true); + const src = read("props.js"); + expect(src).toContain("window.__PROPS__"); + expect(src).toContain("window.__ORIENTATION__"); + expect(src).toContain("introText"); + expect(src).toContain("toastTitle"); + expect(src).toContain("toastBody"); + expect(src).not.toMatch(/—/); + }); + + it.each(orientations)("$file parses with correct dimensions and wiring", ({ file, height }) => { + const html = read(file); + + expect(html).toContain('data-width="1080"'); + expect(html).toContain(`data-height="${height}"`); + expect(html).toMatch(/data-duration="14"/); + expect(html).toMatch(/data-fps="30"/); + + const propsIdx = html.indexOf(''); + const kitJsIdx = html.indexOf(''); + const inlineIdx = html.indexOf("window.__timelines"); + expect(propsIdx).toBeGreaterThan(-1); + expect(kitJsIdx).toBeGreaterThan(propsIdx); + expect(inlineIdx).toBeGreaterThan(kitJsIdx); + + expect(html).toContain('href="../../kit/kit.css"'); + expect(html).toMatch(/class="[^"]*clip[^"]*"/); + + expect(html).toContain("pk-frame"); + expect(html).toContain("pk-frame__sidebar"); + expect(html).toContain("pk-frame__topbar"); + + expect(html).toContain('id="typeIntro"'); + expect(html).toContain("pk-caret"); + expect(html).toContain("typeReveal"); + + const cardCount = (html.match(/pk-card clip/g) || []).length; + const progressCount = (html.match(/pk-pill--progress/g) || []).length; + const completedCount = (html.match(/pk-pill--completed/g) || []).length; + expect(cardCount).toBe(3); + expect(progressCount).toBe(3); + expect(completedCount).toBe(3); + + expect(html).toContain("v0.25.0"); + expect(html).toContain("Env ladder"); + expect(html).toContain("Collision map"); + expect(html).toContain("Metrics donut"); + + expect(html).toContain("pk-cursor"); + expect(html).toContain("pk-cursor__ring"); + + expect(html).toContain("pk-toast"); + expect(html).toContain('id="toastTitle"'); + expect(html).toContain('id="toastBody"'); + + expect(html).toContain("pk-outro"); + expect(html).toContain("roboco.tech"); + + expect(html).not.toMatch(/ + + + +
+
+
+
+ roboco.tech
+