# Per-snippet flow — record, manifest, storyboard review, concat, publish, cleanup

This is the **execution reference** for a polished demo. It assumes
you already have the script approved
([script-and-tts.md](script-and-tts.md)) and you know the capture
mechanics ([recording-mechanics.md](recording-mechanics.md)).

The flow is:

1. Verify your setup (Step 3 below — ralph-loop yourself before recording)
2. Record one section at a time, each as its own clip
3. `manifest add` each clip with an INTENT description
4. **🛑 MANDATORY storyboard review with the user**
5. Surgical re-shoots based on feedback
6. Concat → hero → wiki upload
7. **🧹 MANDATORY workspace restore**

> Parent skill: [SKILL.md](SKILL.md). Read the "Ten failure modes"
> first — modes 1, 2, 5, 6, 7, 10 are all about exactly this flow.

## Quick recording (live narration, no per-snippet flow)

If the user picked "quick recording" in Step 1 of the parent skill,
this whole file does not apply. Use:

```bash
adom-cli hydrogen recording start --reason "Descriptive reason here"
# User narrates and demonstrates live
adom-cli hydrogen recording stop   # saves to ~/project/recordings/
```

Done. Upload to wiki via `adom-wiki asset upload`. Skip the rest of
this file.

## 🎬 Polished recording — record sections INDIVIDUALLY (non-negotiable)

**The rule:** a polished demo is a sequence of **separately-recorded
clips**, one per section / feature, each with its own TTS narration.
`adom-video-post concat` stitches them together at the end. Do NOT record
one long take covering every feature. Reasons:

- A single long recording turns every mistake into a re-shoot of
  everything. One-take demos always have at least one mumbled line
  or a mis-click and then you re-record 4 minutes to fix a 5-second
  flub.
- Per-clip TTS means Andrew-neural (or whatever voice) narrates each
  section cleanly. A single long TTS over a mixed recording loses
  the intentional pauses between features.
- Hero image selection is trivial when each section is its own file —
  just pick the best frame from clip #1 or #2.
- Re-shoots are surgical: if the Measure tool scene is bad, re-record
  `measure.webm`, update the manifest entry, `adom-video-post concat` again.
  The other 12 clips are untouched.

### Why the storyboard gate exists (read this before skipping it)

AI-made demos fail in specific, localized ways: "clip 5 measured the
wrong pads," "clip 8 never clicked anything," "clip 11 walkthrough
didn't advance past step 1." The human reviewer catches these
instantly **when clips are numbered + described + individually
playable**. On a 3–5 minute uninterrupted video they can't — they'd
have to scrub to the minute:second timestamp and guess which segment
you meant. Skipping storyboard forces the user to do something that's
genuinely hard, and turns the feedback loop from "clip 5 is broken"
(precise) into "something's wrong somewhere" (useless).

Every `manifest add` carries a `--description` so the user can read
your INTENT per clip before hitting play. If your intent is wrong
(e.g. you thought "measure tool = some clicks" instead of "measure
tool = commit three pad-to-pad distances showing HUD readouts"), they
can catch the intent bug without even watching the clip.

## Step 3: Verify your setup (ralph loop yourself)

**Before hitting record, screenshot the workspace and LOOK at it.** Check:

- Are the right panels visible and loaded? (not empty states, loading spinners,
  dead webviews, or cloud icons)
- Is the content you're about to demo actually rendering?
- Is the workspace layout clean — no overlapping panels, no stale tabs?
- Do all the tools/CLIs you need actually work? (test a command first)

If anything looks wrong, fix it before recording. Do NOT start recording and
hope for the best. This is the #1 cause of wasted takes.

**Common setup pitfalls:**
- Webview tabs showing blank/cloud icon → navigate with full DNS URL, not
  relative `/proxy/` path
- Shotlog server has stale VSCODE_IPC_HOOK_CLI → restart shotlog so it picks
  up the current socket
- adom-vscode not installed → `gh release download` from adom-inc/adom-vscode
- Panel not loading → check the server is running (`health` command)

### 🎞 Keep motion on screen — no static panels

Any demo scene that lingers on a table, list, or panel needs visible
**action** happening inside it, not a held still. Examples:

- **BOM / Parts list** — click through several rows, not just one. Each
  click swaps the detail view on the right and makes the scene feel
  alive. 3-4 part clicks over 15 seconds beats one click and dead air.
- **Components HUD** — toggle a couple of groups or individual chips
  on/off rather than staring at the list.
- **Schematic / PCB tabs** — pan or zoom so the artwork isn't frozen.
- **Walkthrough / tour** — let it auto-advance; don't pause on a step.

A frozen panel reads as "this tool is boring." Action — even small
clicks that add up to a few state changes — reads as "this tool is
responsive and worth learning." Every scene longer than 8 seconds
needs at least one visible interaction.

### 📐 Frame the split at 30% AI / 70% feature — don't maximize the feature pane

When recording `adom-*` demos the VS Code pane (showing Claude Code live in
the chat) MUST stay meaningfully visible. The canonical ratio is **0.30**
— 30% of the width is VS Code / Claude chugging, 70% is the feature
(adom-tsci, KiCad, Fusion, etc.). That's the Adom pitch: **AI-driven EDA**.
The viewer needs to see Claude actually *doing* the thing they're
watching in the feature pane. A full-width feature pane with no chat looks
like any other tool. A split where Claude is chugging on the left and the
feature is responding live on the right is what makes Adom Adom.

```bash
# Canonical: 30% AI / 70% feature
adom-cli hydrogen workspace resize --split-id "$SPLIT_ID" --ratio 0.30

# Wrong: feature pane maximized, AI hidden
adom-cli hydrogen workspace resize --split-id "$SPLIT_ID" --ratio 0.01
# Wrong: too much AI, feature squeezed
adom-cli hydrogen workspace resize --split-id "$SPLIT_ID" --ratio 0.50
```

Exception: if the demo is genuinely AI-free (a static pitch deck, a hero
shot for thumbnails, a no-interaction explainer), maximizing the feature
pane is fine. Default is 0.30 until you have a reason otherwise.

## Step 4: Record section by section

For a polished / feature-tour demo, use the **per-section** flow. One
clip per section, TTS per section, aggregated at the end.

### 🛑 Screenshot-verify the target tab BEFORE `recording start` — every clip, no exceptions

**The rule:** between opening/activating a tab and pressing `recording start`, **take a screenshot of the tab and actually LOOK at it.** If what you see isn't what you expected to record, you're about to capture that bad state for the entire clip duration — and you won't notice until post-mux storyboard review (best case) or until the user plays the final and asks "why is every example black?" (worst case, cost = full re-shoot).

Why this breaks: `adom-cli hydrogen workspace active-tab` can silently miss. `webview open-or-refresh` returns 204 before the page has loaded its WASM / Babylon / whatever. A 3D viewer may need 5-15 seconds after `--url` lands before the board mesh is actually visible. Every second of `recording start` before the scene is visually ready = one second of black frames locked into the muxed clip.

**The check, one-liner before each `recording start`:**

```bash
# Activate, then SLEEP + screenshot + visual verify before recording
adom-cli hydrogen workspace active-tab --name "<tab>"
sleep 2
adom-cli hydrogen screenshot panel --name "<tab>" --reason "pre-record verify for <clip-id>"
# Read the saved file. If it's not showing what you intended — DO NOT RECORD YET.
# Poll-and-retry, longer sleep, re-navigate, etc.
```

In a driver script the pattern is:

```python
def wait_for_ready(tab_name, check_substring=None, timeout=15):
    for i in range(timeout):
        img = take_panel_screenshot(tab_name)
        if looks_ready(img, check_substring):
            return True
        time.sleep(1)
    raise RuntimeError(f'tab {tab_name} never rendered')
```

Where `looks_ready()` does one or more of:
- Size/histogram check — a black frame is low-variance; a board has mid-tones
- OCR substring — caption or chip label visible
- DOM probe via `adom-tsci eval` — `window.viewer && viewer.getScene().meshes.length > 10`

**Cost of skipping this rule:** past incident — shot 36 scenes of a demo, every one of them a blank webview because active-tab missed. Full re-shoot. Same has happened on 18 of 20 scenes in the 1.3.8 tour. Don't be the next case study.

### The record loop

```bash
MANIFEST=/tmp/<name>-manifest.json
CLIPS=/tmp/<name>-clips/
mkdir -p $CLIPS
adom-video-post manifest init --output $MANIFEST

for each section in script:
  # 1. Show caption (viewer-side context)
  adom-cli hydrogen caption show "<section title>" -d 0     # leave up for whole clip
  # 2. Activate the target tab + any CLI pre-positioning
  adom-cli hydrogen workspace active-tab --name "<feature tab>"
  adom-tsci view isometric --port <port>                    # example
  # 3. 🛑 SCREENSHOT-VERIFY — skip this and you're shooting blanks (see above)
  adom-cli hydrogen screenshot panel --name "<feature tab>" \
    --reason "pre-record verify <clip-id>"
  # Read the file. Confirm the board is visible. Retry if not.
  # 4. Start recording
  adom-cli hydrogen recording start --share tab --mic false --countdown 0 \
    --reason "<section title> — <what you'll see>"
  # 5. Drive UI silently — 8-25 seconds of focused action
  #    adom-tsci view top / hover / click / etc
  # 6. Stop
  adom-cli hydrogen recording stop
  # last recording is saved to ~/project/recordings/<timestamp>.webm
  CLIP=$(ls -t ~/project/recordings/*.webm | head -1)
  mv "$CLIP" "$CLIPS/<id>.webm"
  # 5. Generate TTS narration
  edge-tts --voice en-US-AndrewNeural \
    --text "<narration text for this section>" \
    --write-media "$CLIPS/<id>.mp3"
  # 6. Mux audio onto the video (replace the silent track)
  ffmpeg -y -i "$CLIPS/<id>.webm" -i "$CLIPS/<id>.mp3" \
    -map 0:v -map 1:a -c:v copy -c:a libopus -shortest \
    "$CLIPS/<id>-narrated.webm"
  # 7. Register in manifest — --description is MANDATORY, see below
  adom-video-post manifest add \
    --id "<id>" --title "<short label>" \
    --description "<full intent: what this clip shows, which project, \
                   which features, what the viewer should notice>" \
    --raw "$CLIPS/<id>-narrated.webm" \
    $MANIFEST
  # 8. Hide caption before the next section
  adom-cli hydrogen caption hide
```

### 📝 `--description` on every `manifest add` — not optional

Every clip registered in the manifest MUST carry a `--description` that
says **what you INTENDED the clip to show**. Not just the feature name:
the project, the interactions, the expected visible payoff. The
storyboard UI (Step 5 below) renders this under each clip so the user
can tell "did Claude even understand what this clip was supposed to
do?" before watching it.

Without per-clip descriptions, the user watches a 3-5 minute video
blind and has to guess which segment is broken. With descriptions, they
look at clip N's intent, watch its 15-second snippet, and tell you
exactly "clip 5 measured the wrong pads" or "clip 8 never clicked
anything." That feedback loop is the whole reason this skill exists.

Rule of thumb: if a teammate reading just the description couldn't
write the clip's ffmpeg filter / interaction script themselves, the
description is too vague. Examples of GOOD descriptions:

- ✅ "BME680 cinematic slow orbit for 15s. Camera starts iso, completes
  one full rotation. No HUDs, no tool activation — just the board."
- ✅ "iCE40-USB Measure: commit MP1→MP3 (diagonal 181 mm), clear, commit
  U1→J1 (chip to USB-C connector, ~50 mm), clear, commit MC_USB_DP→DM.
  Each measurement's ΔX/ΔY/distance HUD stays visible for 5s."
- ❌ "show measure tool"
- ❌ "inspect demo"

**Always use a descriptive `--reason`** on `recording start`. The user
sees it in the approval dialog. Bad: "Demo recording". Good: "Recording
adom-tsci Inspect tool — hover pads, show net + JLCPCB part card."

**TTS voice:** `en-US-AndrewNeural` is the default. Alternatives when
the demo needs variety: `en-US-AriaNeural` (polished female),
`en-US-GuyNeural` (warm neutral male). Pick ONE voice for a whole
demo — switching voices mid-demo is jarring.

## Step 5: 🛑 MANDATORY — open adom-video-post storyboard and let the user review every clip

**This step is not optional.** The whole per-section recording flow
exists so the user can audit each clip individually, because AI-driven
demos routinely screw up details a full-video watch can't pinpoint.
Skipping this step — going straight from manifest → concat → wiki
upload — destroys the user's ability to tell you "clip 5 measured the
wrong thing" or "clip 8 never clicked anything." Don't do it.

```bash
# Opens a Hydrogen webview listing every clip in the manifest with its
# title, description, and a player. The user can play each one and
# tell you which clips are bad without watching the whole video.
adom-video-post storyboard $MANIFEST \
  --tab-name 'adom-video-post: <name> review' \
  --port 8797
```

Then:

1. **Actively point the user at the storyboard tab.** Activate it,
   screenshot to confirm it rendered, tell the user the tab name to look
   at. "The storyboard is open in tab `adom-video-post: …`, 13 clips, take a
   look and tell me which ones are wrong."
2. **Wait for user feedback.** The user will tell you "clip 5 needs
   more parts clicked," "clip 11 walkthrough isn't advancing," etc. —
   or just "looks good."
3. **Re-shoot surgically.** `adom-video-post manifest remove --id <id>`,
   re-record that one clip, `manifest add` with updated description,
   point the user back at storyboard. Repeat until they approve.
4. **Only then** proceed to Step 6 (concat + publish).

Never concatenate or upload a demo the user hasn't seen clip-by-clip.

### Background-mux while the user reviews

The concat step is slow (60–90 s for a 6-clip demo). The user
reviewing snippets in storyboard does NOT need it done. These should
run in parallel:

```bash
# Kick concat as a background job the moment manifest is final.
( ffmpeg -y -f concat -safe 0 -i $LIST -c copy \
    ~/project/recordings/<name>-final.webm \
  && echo "concat DONE" ) &
CONCAT_PID=$!

# In parallel, open the storyboard for review.
adom-video-post storyboard $MANIFEST --tab-name 'adom-video-post: <name> review' --port 8797

# When user approves: concat is already done. Upload.
# When user flags a re-shoot: kill the in-flight concat, re-shoot the clip,
# update manifest, restart concat.
kill $CONCAT_PID 2>/dev/null
```

Treating concat as a finish-line task instead of a parallel one wastes
60–90 s × number-of-revisions of user time.

## Step 6: Concat + publish — ONLY after storyboard approval

```bash
# Option A: `adom-video-post concat` (drops audio today — KNOWN BUG, use B)
# Option B: ffmpeg concat demuxer (preserves all streams)
LIST=$(mktemp)
for c in $CLIPS/*-narrated.webm; do echo "file '$c'" >> $LIST; done
ffmpeg -y -f concat -safe 0 -i $LIST -c copy \
  ~/project/recordings/<name>-final.webm
rm $LIST
```

Verify audio is present (voiceover is the whole point) — an all-video,
no-audio output is a bug to catch here, not after upload:

```bash
ffprobe -v error -show_streams <name>-final.webm | grep codec_type
# Must show both `video` and `audio`.
```

Then publish (Step 7). If any section is bad, `adom-video-post manifest
remove --id <id>`, re-record that ONE clip, re-register with updated
description, reopen storyboard for another review pass, then re-concat.
The surgical re-shoot is the whole point.

## Step 6a: 🛑 Show the final in `adom-video` BEFORE wiki upload

Before any `adom-wiki asset upload`, the final webm goes through
adom-video so the user can scrub it in pup. Do NOT report
`final.webm produced at /tmp/...` and stop — that leaves the user to
find the file, copy it locally, and open it in a random player. The
result is review friction and missed bugs.

The right shape is: produce the webm, drop it in `/tmp/videolib/`, run
`adom-video play`, navigate the user's pup tab to the proxy URL.
Two-line completion:

```bash
mkdir -p /tmp/videolib && cp ~/project/recordings/<name>-final.webm /tmp/videolib/
adom-video play /tmp/videolib/<name>-final.webm    # prints "OK: webview url: ..."
```

The CLI also exposes `seek`, `pause`, `set-rate`, `extract-frame`,
`trim` — meaning if the user spots a bad section, `adom-video trim
--in <t> --out <t>` cuts it without re-encoding, no need to re-shoot.
That's why adom-video is the canonical playback surface and why
`python3 -m http.server` + raw file URL is wrong: you lose every one
of those CLI hooks.

If `adom-video` isn't installed, install it from the wiki page first —
don't fall back to a generic player. Memory rule:
`feedback_video_playback_uses_adom_video.md`.

## Step 6b: Hero image + supplementary screenshots

Pick one frame from the final video as the wiki hero. Usually the
frame where the marquee feature is clearest (e.g. the Inspect card
hovering a chip). Then pull 2-3 more for the README / wiki body.

```bash
ffmpeg -y -ss <t> -i final.webm -frames:v 1 hero.png
shotlog resize hero.png     # keeps wiki pages lean
```

## Step 7: Publish to wiki — video, hero, screenshots, AND the demo script itself

```bash
# Final video
adom-wiki asset upload apps/<page> --asset-type video \
  --file <name>-final.webm \
  --caption "<one-line summary — what the viewer learns>"

# Hero image
adom-wiki asset upload apps/<page> --asset-type hero \
  --file hero.png \
  --caption "<the marquee feature in one phrase>"

# Supplementary section screenshots
for s in hero section1 section2 ...; do
  adom-wiki asset upload apps/<page> --asset-type screenshot \
    --file $s.png --caption "<what this shot shows>"
done

# ⭐ Demo script itself — upload as a "reproducible recipe" asset
# so another Adom user (or another Claude thread) can find + replay
# it without spelunking the source repo.
adom-wiki asset upload apps/<page> --asset-type demo-script \
  --file <repo>/demo/<name>-demo-script.md \
  --caption "Reproducible script for this demo — scenes, narration, TTS config, reproduction commands"
```

### Commit the script to the repo in the same PR as the app changes

The demo script is *code* for the purposes of review and diff. When you release a new version of the app, the demo script gets bumped too (new features → new scenes → new narration). Rule of thumb: **the version at the top of the demo script and the version of the app shipping in the PR must match.** A PR that bumps the app without bumping the demo script is incomplete.

```bash
git add <repo>/demo/<name>-demo-script.md <repo>/demo/record.py <repo>/demo/manifest.json
git commit -m "demo-script: update for <version> — <what changed>"
```

### Why the script is a first-class asset

- **Discoverability.** Another Adom user browsing the wiki app page sees the video and the script. They click the script and learn: "oh, here's exactly how this was demonstrated, and I can re-render it for my fork."
- **AI-reproducibility.** A fresh Claude thread pointed at the wiki page can pull the script and re-record the demo against a new version of the app without hand-holding. That's the payoff of putting the narration + TTS config + reproduction block in the file.
- **Review + tweak.** Someone spots a typo in scene 7's narration? They edit the script file, open a PR, CI re-renders the affected clip, hero regenerates. No mystery about what changed.

The hero image is what shows up on the wiki landing page, in
link-preview cards, and as the social-share image — pick it
deliberately. A board on a white background beats a dark canvas full
of toolbar buttons. A card / panel that's clearly *adom-tsci* beats a
generic 3D render.

## Step 8: 🧹 MANDATORY — restore the user's workspace before you stop

**Do not leave the user's Hydrogen workspace cluttered with demo
artifacts.** The recording flow adds tabs, swings split ratios to 20/80,
activates panels, and spawns background servers (storyboard, dev-harness,
animation-server, whatever). None of that should persist past "demo
published." Leaving the workspace in its mid-recording state is the
same bug as AI panels parking themselves over VS Code — the user has
to manually drag everything back every single time.

### What to restore

Before `recording start`, capture three things:

1. **Split ratios.** `adom-cli hydrogen workspace get` → walk the tree
   → note every split's `ratio`. Save them to a temp file (you'll need
   them for the restore).
2. **Pre-existing tabs.** `adom-cli hydrogen workspace tabs` → record
   `{ tabId, name }` for every tab that existed before you started. Any
   tab that DIDN'T exist before is yours to remove at the end.
3. **The focused / active tab per pane.** So you don't leave the user
   staring at your storyboard tab after the demo ends.

After `adom-wiki asset upload` finishes, tear down in reverse:

```bash
# 1. Kill background servers you spawned.
pkill -f "adom-video-post storyboard"
# (add any app-specific dev servers you started, e.g. tsci dev on 8850)

# 2. Remove every tab you added during the demo. Compare the current
#    tab list against the pre-demo snapshot; the delta is yours.
for name in "<tab you added>" "<adom-video-post: ... review>"; do
  adom-cli hydrogen workspace remove-tab --name "$name"
done

# 3. Restore every split's ratio from the pre-demo snapshot.
adom-cli hydrogen workspace resize --split-id "<id>" --ratio <original>

# 4. Re-activate the tab the user was looking at before you started.
adom-cli hydrogen workspace active-tab --name "<pre-demo active tab>"

# 5. Clean up any scratch files you created in the project tree
#    (temp `.kicad_pcb` copies, /tmp/demo-clips/ if you want — though
#    leaving clips in /tmp is generally fine since /tmp is scoped;
#    the hard rule is the user's visible project tree).
rm -f <any temp files you copied into project dirs>
```

### Verification

End the demo with a `workspace get` screenshot to confirm the tree
matches the pre-demo snapshot (same split ratios, same tab count, same
focused tab). If it doesn't match, keep cleaning until it does.

### Why this matters

A user asking for a "demo" is asking for a deliverable (video + wiki
page). They are NOT asking to have their workspace rearranged for half
an hour. The mid-recording 20/80 split + temporary tabs are part of the
*recording apparatus*, not part of the deliverable. Treat them like
scaffolding — build with them, demolish them when the video is up.

If a future demo skill (like the adom-tsci skill, or an adom-chipfit
demo) inherits this doctrine, it should do the same: snapshot before
record, restore after publish. Record a follow-up `feedback` memory
entry if you discover an artifact type this skill didn't cover
(e.g. "adom-tsci demo left tsci dev running on :8850" → add a `pkill`
line to the tool-specific demo flow).

## Legacy one-take + speedup path (not preferred)

The older flow recorded a single long take then sped up slow parts
with `adom-video-post process --markers`. It still works and is the right
call for *live* narration demos where timing is inseparable from
speech — but for **scripted feature tours**, the per-section path
above beats it on every axis: easier re-shoots, cleaner TTS, better
hero selection, no "narrate fast here, slow there" timing puzzle.

If you do need the legacy path:

```bash
adom-video-post inspect                      # show speedup summary
adom-video-post process --input <file>.webm --markers /tmp/adom-video-post-markers.jsonl
adom-video-post voiceover --input <file>-fast.webm   # live-narration UI
adom-wiki asset upload apps/<page> --asset-type video --file <final>.webm \
  --caption "..."
```

## See also

- [SKILL.md](SKILL.md) — parent: orchestration, the ten failure modes, the "real demo" 10-step shape
- [recording-mechanics.md](recording-mechanics.md) — capture path, surfaces, frame-rate sanity, NEVER stitch screenshots
- [script-and-tts.md](script-and-tts.md) — script template, narration phonetics, captions
