Lesson 200: Playtest VOD concat_ok Gate Before Whisper Batch Triage (2026)

Direct answer: Before you run Whisper on a playtest archive, prove the merged VOD is timeline-safe. This lesson adds a concat_ok boolean to playtest_vod_triage_receipt_v1.json and a publish gate playtest_vod_concat_blocked that stays red until ffprobe shows monotonic timestamps and stable audio on every Replay Buffer fragment.

Lesson hero for playtest VOD concat_ok gate before Whisper batch

Why this matters now (June 2026 playtest VOD stack)

Facilitators adopted OBS Replay Buffer clips for hour-one bug capture, but Whisper batch jobs exploded in June 2026 because teams skipped the merge step:

  • Per-clip transcripts look fine while the concat demuxer drops segments or shifts timestamps.
  • GitHub triage issues cite the wrong build_label minute because the merged file is shorter than wall clock.
  • Cloud Whisper APIs charge per minute on corrupt merges that should never have shipped.

This lesson is the course milestone for the same cluster as the OBS Replay Buffer evening pipeline, the 16-tool concat prep listicle, and the local Whisper VOD triage playbook—it wires concat_ok into live-ops gates instead of tribal ffmpeg knowledge.

Beginner path (30-minute smoke)

You will finish with: one folder of Replay Buffer .mkv files, one normalized .wav, and a receipt JSON where concat_ok": true.

Step Action Success check
1 Lock OBS profile (Replay Buffer on, same sample rate) Settings screenshot in release-evidence/playtest/obs_profile.txt
2 Record three short clips to fragments/ Three .mkv files, non-zero size
3 Run ffprobe table script (below) No N/A duration rows
4 Normalize each fragment to 48 kHz WAV normalized/ has matching count
5 Concat with ffmpeg concat demuxer merged_playtest.wav plays without skips
6 Write playtest_vod_triage_receipt_v1.json "concat_ok": true

Time: about 30 minutes for first pass; 68 minutes if you also wire the SQL gate and CI job.

Developer path (gates + automation)

Gate map (C1–C6)

Gate Check Fail closed when
C1 Fragment count ≥ 1 Empty fragments/
C2 ffprobe duration monotonic DTS regression between files
C3 Audio stream sample rate uniform Mixed 44.1 / 48 kHz without resample
C4 Normalized WAV peak < −1 dBFS Clipped normalize blowing LUFS later
C5 Concat output duration ≈ sum of fragments ± 2s Silent gap or duplicate segment
C6 Receipt concat_ok true Any C1–C5 red

playtest_vod_triage_receipt_v1.json (concat fields)

{
  "schema": "playtest_vod_triage_receipt_v1",
  "build_label": "fest_public_2026-07-12",
  "surface": "steam_playtest",
  "fragment_count": 3,
  "ffprobe_table_sha256": "...",
  "merged_wav_sha256": "...",
  "concat_ok": true,
  "whisper_batch_allowed": true,
  "notes": "48 kHz mono normalize; genpts on concat"
}

Pin under release-evidence/playtest/vod/PLAYTEST_VOD_TRIAGE_RECEIPT.json.

Publish gate

ALTER TABLE release_publish_gate ADD COLUMN IF NOT EXISTS
  playtest_vod_concat_blocked BOOLEAN NOT NULL DEFAULT false;

CREATE OR REPLACE FUNCTION enforce_playtest_vod_concat()
RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
  IF NEW.playtest_vod_concat_blocked THEN
    RAISE EXCEPTION 'playtest_vod_concat_blocked: concat_ok false on receipt';
  END IF;
  RETURN NEW;
END;
$$;

CI job verify_playtest_vod_concat_v1 reads the receipt and sets the column before Whisper workflows start.

ffprobe table (copy-paste)

for f in fragments/*.mkv; do
  ffprobe -v error -show_entries format=duration:stream=codec_type,sample_rate \
    -of csv=p=0 "$f" | paste - - | awk -v file="$f" '{print file","$0}'
done | tee release-evidence/playtest/ffprobe_fragments.csv

Red flags: missing audio stream, duration=N/A, sample rate hopping between files.

Normalize + concat sketch

mkdir -p normalized lists
for f in fragments/*.mkv; do
  base=$(basename "$f" .mkv)
  ffmpeg -y -i "$f" -ac 1 -ar 48000 "normalized/${base}.wav"
  echo "file '../normalized/${base}.wav'" >> lists/concat.txt
done
ffmpeg -f concat -safe 0 -i lists/concat.txt -c copy merged_playtest.wav

If concat fails, add -fflags +genpts on a re-encode pass (document in receipt notes).

Wire into BUILD_RECEIPT row

Add a row to your BUILD_RECEIPT beginner pipeline (or team template):

Column Value when green
playtest_vod merged
concat_ok true
whisper_batch pending

Do not set whisper_batch=running when concat_ok is false—Lesson 201 triple-channel receipts assume honest timestamps.

Prerequisites

  • OBS Replay Buffer enabled (see blog evening pipeline)
  • ffmpeg + ffprobe on PATH
  • Optional: local Whisper triage for downstream batch

Common mistakes

  • Concatenating MKV directly without normalizing sample rates (Whisper hears speed-shift artifacts).
  • Using cloud Whisper on per-clip files while issues reference merged timeline (misaligned quotes).
  • Forgetting build_label on receipt (cannot correlate with playtest isolation playbook).

Troubleshooting

Symptom Likely cause Fix
Merged file shorter than expected Non-monotonic DTS Re-encode with +genpts
Whisper timestamps jump backward Mixed fragment settings Re-lock OBS profile
concat_ok true but silent gaps Missing audio in one fragment Re-record fragment; update table

Mini exercise (45 minutes)

  1. Seed three synthetic fragments (or real Replay Buffer clips).
  2. Produce ffprobe_fragments.csv and normalized WAVs.
  3. Write receipt with concat_ok: true.
  4. Deliberately break C3 (export one fragment at 44.1 kHz); confirm gate blocks Whisper job.
  5. Link receipt path in facilitator README (pairs blog #11 contract template).

Continuity

FAQ

Can we skip concat and run Whisper per clip?
Yes for debugging, but batch triage and facilitator summaries assume one timeline—set concat_ok: false and whisper_batch_allowed: false explicitly.

Does this replace the Whisper pipeline blog?
No—it adds a hard gate the blog references; keep using the blog for model choice and issue templates.

What if we use ShareX instead of OBS?
Same receipt fields; change fragment_source note—ffprobe rules still apply.


Whisper batch triage starts only after concat_ok is true—not when facilitators hope the merge worked.