Files
zerto-ai-rewind/demo
justinandClaude Opus 5 e802d86196 demo: stop claiming backup is the only other copy
The narration said the changed file's "only other copy is in last night's
backup". That is false, and it gives away the argument the demo exists to
make: Zerto has a copy from seconds before the change. That is the whole
point.

d5 now draws the contrast instead of conceding it. Backup has last night,
hours old. Zerto has seconds before the change.

d2 had the same problem in a quieter form, asserting there was "no other
copy of that file anywhere" while Zerto was already protecting the machine.
Cut, since d5 carries the comparison.

Also fixes the pronunciation map that made the voice say "vem". The
replace map takes a phrase and a pronunciation, so {"VM": "vee em"} is
spoken as one run-together word. Expanded to "virtual machine".

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_016yVfC5nvZowoLFnEGWhLGn
2026-09-22 19:39:54 -04:00
..
2026-09-22 19:39:54 -04:00
2026-09-22 19:39:54 -04:00
2026-09-22 19:39:54 -04:00
2026-09-22 19:39:54 -04:00
2026-09-22 19:39:54 -04:00
2026-09-22 19:39:54 -04:00

Demo recording harness

Records the rewind loop running against a live ZVM as a narrated MP4. Terminal only: no screen capture, no video editor.

tmux ──▶ asciinema ──▶ agg ──▶ ffmpeg ──▶ mp4
  │                                        ▲
  └── left pane: driver, right pane: journal │
                                             │
      xAI /v1/tts ──▶ wav per beat ──────────┘  (mux_vo.py)

Pieces

file what it does
windows_driver.py Windows demo. Intro over the diagram, then the live loop via WinRM.
driver.py The Linux equivalent, over SSH.
diagram.py Architecture diagram, revealed in four chunks against intro beats i1..i4.
journal.py Right-hand pane. Polls a VPG's checkpoints so the tag appears on camera.
record_win.sh / record.sh Drive tmux + asciinema, then agg and ffmpeg.
narration.md The script. One [beat] per block; the source of truth.
vo/build_narration.py Parses narration.md, synthesises a wav per beat, records durations.
mux_vo.py Aligns the wavs to the recorded beat marks and muxes the audio.

Running it

cp demo/demo_win.example.json demo/demo_win.json   # then fill in the guest creds
export XAI_KEY_FILE=~/xai-api.key XAI_VOICE_ID=<id from /v1/custom-voices>
python3 demo/vo/build_narration.py 1.0             # synthesise, writes durations.json
demo/record_win.sh win1                            # record; writes marks.jsonl
python3 demo/mux_vo.py win1                        # -> win1_narrated.mp4

demo_win.json holds live guest credentials and is gitignored. So are the generated .wav, .cast, .gif and .mp4 files.

How the audio stays in sync

The driver writes marks.jsonl as it runs: one line per beat with the real elapsed time it started. mux_vo.py delays each wav to its recorded mark, so sync survives a slow API call or an FLR mount that takes longer than usual. Nothing is predicted.

Two things this depends on:

  • agg --idle-time-limit must be larger than the longest pause (the scripts pass 3600). The default is 5 seconds, which compresses idle time, and that silently breaks the mapping between wall clock and video time.
  • Each beat holds for its narration length. hold() sleeps out whatever is left after the work finishes, so a line is never cut off mid-sentence.

Check alignment after a mux: the FLR wait should measure near silence.

ffmpeg -v error -i out.mp4 -vn -ac 1 /tmp/a.wav
ffmpeg -hide_banner -ss 150 -t 6 -i /tmp/a.wav -af volumedetect -f null /dev/null 2>&1 | grep mean_volume

Speech sits around -22 dB; a correctly aligned gap reads about -91 dB.

Narration gotchas

  • Do not map acronyms to run-together phonetics. The replace map takes {"phrase": "pronunciation"}, and {"VM": "vee em"} gets spoken as one word, "vem". Either leave the acronym alone or expand it: {"VM": "virtual machine"}.
  • Write for speech, not for the page. Short declaratives and fragment stacks read well and sound robotic out loud. Commas and full stops are what the engine uses for pacing, so clauses joined with commas breathe; a wall of four-word sentences marches.
  • volumedetect reports n_samples: 0 when pointed at a file whose first stream is video. Extract the audio first, then measure, or you will think a working track is silent.