chore: add product media capture skills (#1714)

* chore: add product media capture skills

* docs: clarify product media capture workflow

* docs: use resolved landing root in capture skill

* docs: validate configured landing repository

* docs: harden product media capture validation
This commit is contained in:
José Almeida
2026-07-16 00:24:14 +01:00
committed by GitHub
parent 80b5516199
commit d046faba87
8 changed files with 647 additions and 0 deletions
@@ -0,0 +1,58 @@
---
name: product-demo-seeding
description: Seed coherent, disposable Kandev demo scenarios for screenshots, product films, landing-page media, and reproducible UI captures. Use whenever media needs believable tasks, workflows, agents, executors, integrations, plans, sessions, diffs, reviews, or native mobile states; invoke before product-video-capture and never use a developer's main instance or data.
---
# Product Demo Seeding
Build a truthful product state before recording pixels. Treat narrative, data, and UI route as one artifact.
## Prerequisites
Use a Kandev checkout with its web E2E fixtures, Playwright dependencies, pnpm toolchain, Git, and a disposable local backend available. Verify the checkout contains `scripts/dev-isolated` and `apps/web/e2e/` before creating seed state.
## Workflow
1. Read `/e2e` and the relevant existing specs/page objects under `apps/web/e2e/`.
2. Write a one-sentence story: user goal, visible action, visible result.
3. Identify separate desktop and mobile routes. Mobile must use native mobile surfaces, not a desktop crop.
4. Explore manually with `scripts/dev-isolated --web` when needed. For reproducible capture, use the worker-scoped E2E fixture and `ApiClient`.
5. Create a fresh bogus repository, workspace, workflow, tasks, sessions, and provider state through supported E2E/API methods.
6. Seed only enough state to make the story legible. Dense believable data beats empty fixtures; excessive data hides the action.
7. Open the intended UI and verify every visible label, control, and transition before recording.
8. Hand the scenario, route, selectors, semantic target bounds, and cleanup command to `/product-video-capture`.
## Safety Contract
- Use a fresh temp `HOME`, `KANDEV_HOME_DIR`, SQLite path, repository, worktree root, and non-production ports.
- Use mock external providers and a mock agent. Never load credentials or contact real GitHub/Jira/Linear/Sentry/Slack services.
- Never copy the developer's database for marketing capture. `scripts/dev-isolated --copy-db` is outside this workflow.
- Never query, mutate, stop, or reuse the main Kandev instance.
- Keep capture specs temporary or in a reviewed source bundle. Remove temporary copies after the run.
- Stop if isolation cannot be proven from process args, ports, paths, and logs.
Read [isolation-and-seeding.md](references/isolation-and-seeding.md) before starting an instance.
## Truthfulness Contract
- Seed real records and drive real controls. Do not fabricate menu items, executor families, integrations, checks, or agent support in the DOM.
- Prefer coherent API-seeded labels over capture-time text replacement. If fixture sanitation is unavoidable, document exact substitutions; never add controls, hide product bugs, or change behavior.
- Use current product capabilities. Inspect selectors and API helpers again instead of copying an old capture spec blindly.
- If a responsive surface is broken, report the product bug and choose another truthful native route only when it demonstrates the same capability.
- Keep local paths, test directives, generic mock responses, fixture names, and host identity out of visible frames.
## Story Selection
Use [story-recipes.md](references/story-recipes.md) for Plan, Coordinate, Prepare, Run, Review, integrations, editor/terminal, and mobile recipes. Recipes are patterns, not frozen scripts.
## Acceptance Gate
Before capture, verify:
- Story has a clear initial state, action, and result in 7-11 seconds.
- Desktop and mobile each have a native script and safe composition.
- Visible data forms one fictional project narrative.
- No production endpoint, credential, local path, fixture copy, or unsupported control is visible.
- Seed teardown removes temporary profiles, specs, processes, ports, database, and repository.
Report seed name, product story, ports, temp roots, mock providers, real UI route, any sanitation, and teardown result.
@@ -0,0 +1,111 @@
# Isolation And Seeding
## Pick The Operating Mode
| Need | Mode | Why |
| --- | --- | --- |
| Explore current UI and choose a story | `scripts/dev-isolated --web` | Fast manual iteration with disposable state |
| Produce a repeatable capture | `apps/web/e2e` worker fixture | Owns backend, frontend, database, ports, reset, and teardown |
| Exercise Docker or SSH behavior itself | `containers` E2E project | Uses real container-backed executors |
Do not manually point a browser at a running personal instance. A different browser profile does not isolate the backend or database.
## Manual Exploration
From Kandev repo root:
```bash
scripts/dev-isolated --web
```
Use the printed web URL, backend URL, temp home, database, logs, PID file, and teardown command. Confirm none match known production ports or the user's normal Kandev home.
Do not pass `--copy-db` for product media. Fresh data is both safer and easier to make coherent.
Before leaving exploration:
```bash
scripts/kandev-kill --pidfile <printed-pidfile> --yes
```
Confirm the printed backend/web ports have no listeners.
## Reproducible Capture Fixture
Build capture specs on `apps/web/e2e/fixtures/test-base.ts`. It provides:
- worker-scoped backend/frontend and unique ports;
- fresh temp `HOME`, `KANDEV_HOME_DIR`, SQLite DB, repositories, and worktree roots;
- `apiClient` for supported setup and test-harness routes;
- `seedData` for a valid workspace, workflow, repository, agent profile, and executor profile;
- reset and teardown.
Choose a free `E2E_PORT_OFFSET`; never paste an offset from old notes without checking listeners. Keep each concurrent desktop/mobile run on a different offset.
Use `data-testid`, role locators, and page objects. Snapshot refs and CSS generated by Radix/Dockview drift too easily for reproducible capture.
## Useful Seed APIs
Read current signatures in `apps/web/e2e/helpers/api-client.ts` before use. Common building blocks:
- `createTask`: create without launching the fixture agent.
- `createTaskWithAgent`: use only when a real mock-agent turn is part of the visible story.
- `updateTaskState`: place tasks in believable workflow states.
- `createRepository`: attach a disposable local Git repository.
- `createExecutor` / `createExecutorProfile`: add valid profiles for selectors.
- `seedTaskSession`: create controlled `RUNNING`, `IDLE`, or completed sessions.
- `seedSessionMessage`: add distinct thinking, tool, result, and response rows.
- mock-provider helpers: seed PRs, checks, reviews, comments, and integration state.
- `e2eReset` and delete helpers: clean scenario-specific records.
Use service APIs for normal product state. Use `/_test` seed routes only for state that would otherwise require slow external processes or nondeterministic timing.
## Disposable Repository
Create a fresh local Git repository under the fixture temp root. Give it:
- a fictional project name and reserved/test-only identity;
- a clean `main` commit;
- a feature branch or worktree when the story needs changes;
- small readable source files with meaningful names;
- a nontrivial diff: one behavior change plus one focused test;
- no copied proprietary code, tokens, remotes, or host paths.
Git state should support the UI being shown. A review story needs actual changed files and diff content; an editor story needs actual readable files; a terminal story needs commands that produce truthful output.
## Narrative Coherence
Use one fictional domain per capture batch. Define before seeding:
```text
organization:
project/repository:
workflow stages:
task titles:
feature branch:
changed files:
PR/check/review state:
agent and model labels:
expected result:
```
Avoid generic names such as `E2E Workspace`, `Test Task`, `foo.ts`, and `Mock response`. Avoid claims that imply production integrations or agents were contacted.
## Executor And Integration Honesty
Only show entries returned by current product APIs. To demonstrate Local, Worktree, Docker, Sprites, or SSH, create valid disposable profiles using their actual executor types and safe test-only config. The selector itself must remain product UI.
For integrations, mock provider responses at the backend boundary. Do not inject logos or entries into the page. If current settings expose fewer integrations than marketing copy expects, capture what exists and report the gap.
## Seed Verification
Before recording:
1. Reload the page so boot payload and client store agree.
2. Assert story-critical backend invariants with `apiClient`.
3. Assert every story-critical control and label through the UI.
4. Scan visible text for `E2E`, `mock`, `fixture`, `/tmp`, `/home`, localhost URLs, slash directives, and host usernames.
5. Exercise the complete route once without a recorder.
6. Record exact teardown ownership: fixture, PID file, temp root, and temporary profiles/specs.
If any invariant depends on timing luck, redesign the seed. Marketing capture should be deterministic.
@@ -0,0 +1,69 @@
# Product Story Recipes
Each story needs: stable opening, one understandable action chain, visible payoff, settled ending. Keep copy natural and domain-specific.
## Plan
Show planning as collaboration, not a static artifact:
1. Open real Plan mode.
2. Produce an authored plan through the real Kandev MCP plan path.
3. Select a plan step and add a natural comment.
4. Run the comment so a queued mock-agent turn updates the plan through `update_task_plan_kandev`.
5. Show the revised plan, then the real Implement action.
Do not paint a fake plan card or claim a plan exists before the product creates it. Desktop and mobile need separate native paths.
## Coordinate
Seed a legible board with 3-4 named stages and enough cards to communicate parallel work. Use distinct task titles and believable state distribution. Show one move, stage switch, or task handoff.
Desktop can show the board overview. Mobile should navigate native stage tabs or swipe behavior; keep selected stage label and complete cards inside frame.
## Prepare
Open real task intake. Show agent/model and executor choice using profiles created through the API. Keep the complete popover/sheet visible. Do not force aliases or fabricate unavailable executor types.
If demonstrating setup scripts, repositories, branches, or worktrees, seed valid values and verify the chosen profile persists.
## Run And Inspect
For a controlled clean story:
1. Create the task without `start_agent`.
2. Seed one session as `RUNNING`.
3. Add two distinct progress phases using separate thinking/tool/result boundaries.
4. Show readable implementation and test evidence tied to the disposable repository.
5. Move the same session to `IDLE` or completed state.
6. Inspect the resulting file/change through real Files, editor, or Changes UI.
This avoids generic fixture-agent output while preserving truthful product state. Do not seed a claim that the repository cannot support.
## Review And Repair
Seed a real changed-file set plus mock PR/check/review state. Show:
1. changed-file navigation;
2. readable diff;
3. anchored or line-level feedback;
4. the real fix/repair action;
5. agent Read/Edit/Bash or equivalent evidence;
6. passing focused tests or a coherent repaired diff.
Desktop should use the current main Changes/review surface. Mobile should use the native Changes panel and `MobileDiffSheet`, then native Chat for repair when that is the current responsive route. Never open a known overflowing desktop modal on mobile merely to mirror desktop choreography.
## Integrations
Prefer the real Settings integration index or real task-import surface. Mock provider health/data behind it. Famous logos may carry the visual story, but they must come from actual product assets and current supported entries.
## Editor And Terminal
Use actual files and shell commands in the disposable repository. Initialize a fictional shell identity and working directory before recording. Ensure canvas/WebGL terminal pixels are visible in the chosen capture path; verify the raw frame rather than assuming the browser recorder captured them.
## Static Screenshots
Use the same seeding and truthfulness rules. Capture a settled pointer-free frame at native resolution. Crop only after preserving a full raw source. Keep dense UI text readable and avoid `cover` when it removes controls needed to understand the state.
## Alternate Versions
When only framing, zoom, poster, or pacing changes, reuse the approved clean raw master. Re-seed and recapture when visible data, UI behavior, viewport, story route, or feature state changes.
@@ -0,0 +1,71 @@
---
name: product-video-capture
description: Record, camera-process, encode, integrate, and validate polished Kandev product films, landing-page loops, screenshots, and alternate cuts from isolated demo data. Use whenever the user asks for product videos, GIF-like feature demos, smooth cursor-follow zoom, desktop/mobile captures, recaptures, new landing media, or different framing; invoke product-demo-seeding first for new visible state.
---
# Product Video Capture
Produce reusable clean masters first; derive presentation from them later.
## Prerequisites And Repo Discovery
- Require Linux Xvfb, Chrome for Testing, Playwright/CDP, FFmpeg/FFprobe, a Kandev checkout with E2E fixtures, and the landing repository.
- Resolve the Kandev root with `git rev-parse --show-toplevel`, then verify it contains `scripts/dev-isolated` and `apps/web/e2e/`.
- Resolve the landing root from `KANDEV_LANDING_REPO` when set and verify it contains both `scripts/product-loop-camera.mjs` and `scripts/product-loop-encoder.mjs`. If the variable is unset or its path fails verification, search the available workspace and sibling checkouts for both marker files; do not select a directory by name alone.
- If discovery finds zero or multiple landing candidates, ask the user to identify the checkout. Record the resolved roots as `KANDEV_REPO` and `LANDING_REPO`, and use those variables for every command and copy operation.
## Choose Deliverable
| Request | Path |
| --- | --- |
| New feature/story | Seed with `/product-demo-seeding`, then capture desktop and mobile masters |
| Different zoom/crop/pacing | Reuse approved raw master; change camera config only |
| New poster/static image | Extract a settled pointer-free frame from approved master or recapture native screenshot |
| Longer walkthrough | Keep continuous 1x source; add a tested delivery profile instead of speed ramps |
| Actual GIF required | Derive from approved video last; retain WebM/MP4 as primary web formats |
## Pipeline
1. Resolve and verify `KANDEV_REPO` and `LANDING_REPO` as described above. Do not assume task-specific absolute paths.
2. Create a unique writable `CAPTURE_ROOT`, for example with `mktemp -d "${TMPDIR:-/tmp}/kandev-product-capture.XXXXXX"`. Use it for every raw, proof, config, and staged delivery path.
3. Review the seed handoff and rehearse the full native desktop/mobile story once.
4. Record one continuous, unzoomed, high-resolution master per form factor. Use true physical pixels, not a padded Playwright video canvas.
5. Record semantic action timestamps, target bounds, and pointer/touch journeys beside the raw file.
6. Stop recording before capturing the clean poster.
7. Inspect raw frames before post-production. Reject UI bugs, padding, double cursors, fixture text, dead waits, and unreadable states.
8. Build a smooth post camera from semantic events. Camera follows story focus, not every cursor twitch.
9. Encode WebM, MP4, and WebP through landing's tested camera/encoder scripts.
10. Review fixed-fraction frames and playback on desktop, native mobile, and reduced motion.
11. Copy only approved delivery assets into `public/product/loops/`; keep raw/proof files outside production unless requested.
Read [capture-pipeline.md](references/capture-pipeline.md) before recording and [camera-encoding.md](references/camera-encoding.md) before conforming media.
## Non-Negotiable Capture Properties
- Fresh isolated data; no main instance, credentials, database, or production ports.
- Separate desktop and native-mobile scripts. Never crop desktop footage into a mobile deliverable.
- Raw master is one continuous take at 1x: no body transform, camera, crop, concat, speed ramp, or internal cut.
- Disable OS cursor in X11 capture; show one intentional high-contrast DOM cursor/touch treatment.
- Use real UI input and retain semantic bounds/timestamps for camera design and audit.
- Browser chrome absent; product fills the physical frame.
- Any responsive/product defect remains visible or blocks capture. Do not hide it with capture CSS.
## Camera And Delivery
Use `$LANDING_REPO/scripts/product-loop-camera.mjs` and `$LANDING_REPO/scripts/product-loop-encoder.mjs`. Their tests define current dimensions, frame rate, maximum zoom, smoothness, loop reset, codecs, and poster quality. Change those contracts test-first when the user requests a genuinely different delivery format.
Do not remove waits with cuts or speed changes. Improve the source choreography or recapture. One trim at the beginning/end is acceptable; time skips are not.
## Acceptance Gate
Follow [qa-checklist.md](references/qa-checklist.md). Do not ship until:
- raw and delivery dimensions are proven by decoded pixels, not only container metadata;
- cadence is constant and timestamps have no gaps;
- 10/25/50/75/90% frames and full playback pass visual review;
- camera motion is smooth, reaches intended depth, and settles on centered 1x before looping;
- text, menus, diffs, pointer, and touch targets remain inside frame;
- WebM, MP4, poster, responsive source selection, lazy loading, and reduced-motion behavior pass;
- all capture processes, ports, temporary specs, and temp data are gone.
Report story, seed, form factors, raw/delivery dimensions, durations, codecs, camera profile, output sizes/hashes, visual audit, browser checks, teardown, and any unsupported or blocked surface.
@@ -0,0 +1,99 @@
# Camera And Encoding
## Keep Camera Out Of Raw Capture
An unzoomed native master supports alternate focus, deeper/shallow zoom, new poster timing, changed landing composition, and future encoders without reseeding. Bake only the intentional pointer/touch treatment into raw pixels.
## Use Landing's Tested Pipeline
Use the already-resolved `$LANDING_REPO`, then verify these paths within it:
```text
scripts/product-loop-camera.mjs
scripts/product-loop-camera.test.mjs
scripts/product-loop-encoder.mjs
scripts/product-loop-encoder.test.mjs
```
Run focused tests from `$LANDING_REPO` before encoding. Current contract:
- 25 fps constant cadence;
- desktop delivery 2560x1600 from at least 3840x2400 source;
- mobile delivery 1290x2796 from native 1290x2796 source;
- desktop reaches 1.20x; mobile reaches 1.14x;
- centered 1x opening and ending;
- at least 240ms settled centered 1x before loop reset;
- cosine-eased piecewise motion with per-frame smoothness checks;
- one trim, no concat, no speed-ramp `setpts`;
- muted VP9 WebM, H.264 MP4 with fast start, and WebP poster.
Treat tests as source of truth if these values evolve.
## Camera Design
Design 4-7 keyframes from semantic story events:
1. Start centered at 1x.
2. Hold context briefly.
3. Ease toward the first important target while the pointer travels.
4. Hold or drift gently across related actions; do not chase every click.
5. Move focus only when story focus changes materially.
6. Ease back to centered 1x before the final settled hold.
Use normalized centers from target bounds. Keep full menus/dialogs/diffs inside the crop at maximum zoom. Optical focus may differ from exact pointer center when surrounding context is important.
Example config shape. Replace `<capture-root>` with the unique resolved `CAPTURE_ROOT` before encoding:
```json
{
"slug": "desktop-run-inspect",
"rawPath": "<capture-root>/raw/desktop-run.mp4",
"outputDir": "<capture-root>/delivery",
"trimStartMs": 480,
"posterAtMs": 7200,
"sourceWidth": 3840,
"sourceHeight": 2400,
"outputWidth": 2560,
"outputHeight": 1600,
"camera": {
"durationMs": 8400,
"formFactor": "desktop",
"keyframes": [
{ "tMs": 0, "zoom": 1, "x": 0.5, "y": 0.5 },
{ "tMs": 450, "zoom": 1, "x": 0.5, "y": 0.5 },
{ "tMs": 2700, "zoom": 1.2, "x": 0.57, "y": 0.43 },
{ "tMs": 5900, "zoom": 1.2, "x": 0.55, "y": 0.57 },
{ "tMs": 8100, "zoom": 1, "x": 0.5, "y": 0.5 },
{ "tMs": 8400, "zoom": 1, "x": 0.5, "y": 0.5 }
]
}
}
```
Encode from landing repo:
```bash
cd "$LANDING_REPO"
node scripts/product-loop-encoder.mjs /path/to/config.json
```
The encoder probes source duration and rejects an overrun. It writes `<slug>.webm`, `<slug>.mp4`, and `<slug>.webp` from one camera timeline.
## Avoid Awkward Motion
Jitter usually comes from too many keyframes, short segment durations, pointer-perfect tracking, or camera changes on the same frame as UI transitions. Fix by reducing targets, lengthening easing, and focusing on semantic regions.
Time skips usually come from concatenating holds, removing waits, or speeding mock-agent gaps. Fix source timing and recapture. Do not disguise them with crossfades.
Deeper zoom is useful only when content remains legible and contextual. Desktop can tolerate more lateral movement; mobile should mostly preserve center and use smaller zoom because sheets and bottom navigation already consume space.
## Alternate Deliveries
- Framing change: edit camera config and re-encode from same raw.
- Poster change: move `posterAtMs`; keep pointer-free settled state.
- Static crop: derive from full-resolution raw/poster; preserve uncropped source.
- New aspect ratio: add a named tested delivery profile. Do not stretch existing output.
- Long walkthrough: add a separate profile and player treatment. Do not weaken short-loop tests globally.
- GIF: derive after approval with a palette-aware encoder; keep WebM/MP4 canonical.
Never overwrite approved media until side-by-side review passes. Encode to staging, compare, then promote selected files.
@@ -0,0 +1,123 @@
# Capture Pipeline
## Why X11 Capture
Playwright `recordVideo.size` can report a large container while preserving only CSS-pixel content in the top-left and padding the rest. Container dimensions alone therefore do not prove native DPR detail.
Preferred Linux route:
1. Start a worker-scoped Kandev E2E backend.
2. Launch headed Chrome for Testing in app mode under Xvfb.
3. Force device scale factor and set CSS viewport/window dimensions deliberately.
4. Connect Playwright over CDP for real mouse/touch input.
5. Record the X display with FFmpeg `x11grab` at physical dimensions.
Current established profiles are:
| Form | CSS viewport | DPR | Raw physical frame |
| --- | ---: | ---: | ---: |
| Desktop | 1920x1200 | 2 | 3840x2400 |
| Mobile | 430x932 | 3 | 1290x2796 |
Verify current landing encoder constants before capture. If dimensions change, update encoder tests first.
## Browser Shape
Launch Chrome with an isolated user-data dir and flags equivalent to:
```text
--disable-infobars
--hide-scrollbars
--force-device-scale-factor=<dpr>
--window-position=0,0
--window-size=<css-width>,<css-height>
--app=<isolated-web-url>
--remote-debugging-port=<free-cdp-port>
```
Use a fresh Xvfb display sized to the physical frame. Confirm the decoded first frame fills that frame; do not infer this from `ffprobe` width/height alone.
## Recorder Shape
Record a visually lossless working master:
```text
ffmpeg -f x11grab -draw_mouse 0 -framerate 25 \
-video_size <physical-width>x<physical-height> -i <display> \
-an -c:v libx264 -preset ultrafast -crf 10 -pix_fmt yuv420p <raw.mp4>
```
Start FFmpeg immediately before the opening beat and wait until it reports a real frame. Stop it cleanly with `q`; wait for a zero exit code before closing Chrome or taking a poster.
## Pointer And Touch
Use one capture-only overlay:
- desktop: high-contrast pointer with restrained click pulse;
- mobile: small touch ring and pulse;
- OS pointer disabled through `-draw_mouse 0`;
- pointer overlay hidden for poster capture.
Move real input and overlay together. Use 10-12 manually timed samples over roughly 300ms with cubic easing. Record for every arrival:
```json
{
"action": "cursor-arrive",
"at_ms": 2400,
"motion_ms": 300,
"motion_samples": 12,
"from": { "x": 320, "y": 180 },
"to": { "x": 1080, "y": 720 },
"target_bounds": { "x": 1010, "y": 680, "width": 220, "height": 64 },
"label": "Open changed file"
}
```
Bounds use CSS pixels. Keep targets inside viewport and store the raw-story start time separately from event story time.
## Choreography
- Rehearse before recording; do not discover selectors during a take.
- Opening frame should establish context for 300-600ms.
- Each action should visibly land before the next begins.
- Remove dead waits by controlling seed timing, not editing time.
- Show one meaningful state change every 1-2 seconds.
- End on a readable result, then hold a settled frame long enough to loop cleanly.
- Keep capture duration near 7-11 seconds unless the requested format explicitly differs.
## Native Mobile
Use a mobile Playwright project with `isMobile` and touch enabled, a separate script, and native mobile navigation. Do not replay desktop coordinates or selectors. Assert complete stage labels, sheets, bottom navigation, and action buttons before recording.
When desktop uses a split diff but mobile exposes `MobileDiffSheet`, write different choreography around the same user outcome.
## Raw Rejection Conditions
Reject and recapture if any sampled or playback frame shows:
- CSS-size content padded inside a larger gray/black canvas;
- browser chrome, host desktop, notification, or URL tooltip;
- double cursor or OS cursor;
- loader/blank/error state held as a story beat;
- generic fixture/mock text, slash directives, local paths, localhost status, or host identity;
- clipped menu, task title, stage label, diff, composer, or primary action;
- product UI bug hidden through capture-only CSS;
- unexplained time jump, cursor teleport, or state transition.
## Reproducibility Bundle
Keep outside production asset directory until review:
```text
capture-root/
|-- raw/<slug>.mp4
|-- posters/<slug>.png
|-- metadata/<slug>.json
|-- frames/<slug>/{10,25,50,75,90}.png
|-- mosaics/<slug>.png
|-- source/<capture-spec-and-helper>
|-- NOTES.md
`-- SHA256SUMS
```
Metadata should record seed narrative, form factor, CSS/DPR/raw dimensions, ports, story start/duration, semantic events, capture command, hashes, and visual audit results.
@@ -0,0 +1,114 @@
# Product Media QA
## 1. Source Contract
- [ ] Disposable seed and ports documented.
- [ ] Desktop and mobile have separate scripts and raw masters.
- [ ] Raw is continuous 1x with no crop, camera, concat, speed change, or internal cut.
- [ ] OS cursor disabled; exactly one DOM cursor/touch treatment visible.
- [ ] Semantic events include timestamps, target bounds, and smooth motion samples.
- [ ] Poster captured after recorder stops with pointer/touch hidden.
## 2. Technical Probe
Use `ffprobe` on every raw and delivery file:
```bash
ffprobe -v error \
-show_entries stream=codec_name,width,height,r_frame_rate,avg_frame_rate:format=duration,size \
-of json <video>
ffprobe -v error -select_streams v:0 \
-show_entries frame=best_effort_timestamp_time,pkt_duration_time \
-of csv=p=0 <video>
```
Verify:
- decoded dimensions match profile;
- `r_frame_rate` and `avg_frame_rate` are constant 25 fps;
- consecutive decoded frame timestamps advance by 0.04 seconds within one stream time-base tick, with no duplicate, negative, or missing-frame gaps;
- duration matches marked story and camera timeline;
- WebM codec is VP9, MP4 codec is H.264;
- no audio stream exists;
- poster dimensions match delivery.
Decode at least one raw frame and inspect all four edges. A 3840x2400 container with 1920x1200 active content and neutral padding is not high resolution.
## 3. Timeline Audit
Extract full-resolution frames at 10%, 25%, 50%, 75%, and 90%. Build a contact sheet for quick comparison, but inspect original frames when text appears truncated; downscaled mosaics can create false clipping.
Watch the complete loop at normal speed and at 0.5x. Check:
- no pointer teleport, duplicate pointer, or click before arrival;
- no state jump, cut, speed-up, dead wait, blank beat, or loader hold;
- camera reaches useful depth without oscillation;
- camera remains within safe bounds and returns to centered 1x;
- loop reset is calm rather than a snap;
- readable copy remains stable long enough to understand.
## 4. Content Audit
Search every sampled frame and poster for:
- fixture, mock, test, or E2E labels;
- `/tmp`, `/home`, local repository paths, localhost URLs, or host username;
- generic generated response or slash directive;
- fake provider/executor/integration controls;
- inconsistent organization, repository, task, branch, PR, or file names;
- clipped title, popover, menu, stage label, diff, composer, checks, or action;
- accidental notification, browser status text, or developer tooling.
Confirm visible implementation, diff, test, and review claims are supported by seeded repository/provider state.
## 5. Landing Integration
Production bundle per slug:
```text
public/product/loops/<slug>.webm
public/product/loops/<slug>.mp4
public/product/loops/<slug>.webp
```
Verify player behavior:
- WebM primary and MP4 fallback both load;
- poster appears before playback and under reduced motion;
- video is muted, inline, lazy/in-view loaded, and does not shift layout;
- only active desktop/mobile story loads;
- native mobile media uses its own aspect ratio and source;
- controls, tabs, and carousel semantics remain keyboard-accessible;
- no `cover` crop hides important UI.
Run landing checks only from the resolved landing repository root:
```bash
cd "$LANDING_REPO"
pnpm exec vitest run scripts/product-loop-camera.test.mjs scripts/product-loop-encoder.test.mjs
pnpm test
pnpm exec tsc --noEmit
pnpm run build:pages
```
Smoke-check desktop, mobile, and `prefers-reduced-motion` in a real browser. Confirm media requests return 200/206 and browser console has no errors.
## 6. Promotion And Provenance
- [ ] Encode to staging first.
- [ ] Compare old/new loops side by side.
- [ ] Promote only approved WebM/MP4/WebP files.
- [ ] Record SHA-256, dimensions, duration, codec, size, source seed, and capture command.
- [ ] Keep raw/proof bundle outside production assets unless repository policy requests it.
- [ ] Do not delete previous accepted media until replacement passes build and browser smoke.
## 7. Teardown
- [ ] Stop FFmpeg, Chrome, Playwright, backend, frontend, and Xvfb.
- [ ] Confirm capture backend/web/CDP ports have no listeners.
- [ ] Remove temporary E2E spec copies and browser profiles.
- [ ] Remove disposable executor profiles, database, repo, and temp home.
- [ ] Check both Kandev and landing worktrees; preserve unrelated user/agent changes.
Final report must distinguish tests that passed, checks not run, and any product surface blocked by a real UI defect.
@@ -19,6 +19,8 @@ Task arrives
|-- Need focused context setup? ------------> /context-engineering
|-- Code change with test coverage? --------> /tdd
|-- Browser/E2E coverage? ------------------> /e2e
|-- Seed isolated product demo data? -------> /product-demo-seeding
|-- Record landing/product media? ----------> /product-video-capture (seed first only for new visible state)
|-- Frontend/UI change? --------------------> /mobile-parity plus /e2e as needed
|-- Security-sensitive change? -------------> security-auditor subagent plus /code-review
|-- Test strategy or coverage gaps? --------> test-engineer subagent plus /tdd or /e2e