Identify every video_asset hosted only on Vimeo (no youtube platform id) and produce a migration manifest — asset id, vimeo id, title, interview slug, current transcript/caption status. This is the discovery gate for the Vimeo→YouTube re-upload; nothing migrates until this set is known and reviewed.
_data/video_assets.yml (items[].platforms[], each {platform, asset_id, url, embed_url, ...}). 207 items total._data/scmc_videos.yml, oneoff_videos.yml, interview_related_videos.yml, interviews.yml reference assets by id (video_asset_id/slug), they carry NO independent vimeo embed. scmc_videos.yml’s video_asset_id: vimeo-<id> slugs are just identifiers for a subset of the standalone assets below; do not treat as extra hosting.An asset is in the migration set iff its platforms[] contains a platform == "vimeo" entry AND contains NO platform == "youtube" entry (compare on presence of asset_id).
Ruby detection (read-only):
pids = ->(it,n){ (it["platforms"]||[]).select{|p| p["platform"]==n}.map{|p| p["asset_id"]} }
vimeo_only = items.select{|it| !pids.(it,"vimeo").empty? && pids.(it,"youtube").empty? }
Result computed during planning: 27 Vimeo-only assets (none has >1 vimeo id).
interview_id → map to _data/interviews.yml (slug = interview id where video_asset_id == asset.id).vimeo-<id> ids + 2 mike-hall-* solo talks), no interview_id; 12 of these are cross-listed in scmc_videos.yml.primary_platform: vimeo.site.data.transcripts[transcript_id], i.e. file _data/transcripts/<transcript_id>.yml (196 files). Status = structured if that file has non-empty turns (or content), else none.Generate _data/vimeo_migration_manifest.yml from a small read-only bin/ script (build/inspection input, not a hand-edited data file). Per-entry shape:
generated_at: <iso8601>
source: _data/video_assets.yml
total_vimeo_only: 27
items:
- asset_id: dave-thomas-software-craftsmanship-north-america-2013
vimeo_id: '90550222'
vimeo_url: https://vimeo.com/90550222
title: '...'
interview_slug: dave-thomas-...-2013 # null for standalone
primary_platform: vimeo
duration_seconds: 861 # from vimeo platform entry (integrity ref for TASK-246)
thumbnail_local: /assets/vimeo/thumbs/ugtastic/90550222.jpg
transcript_id: dave-thomas-...-2013 # null if none
transcript_status: structured # structured | none
transcript_turns: 61
caption_readiness: caption-ready # caption-ready | needs-transcription
youtube_id: null # populated by TASK-253
migration_state: pending # pending|downloaded|uploaded|unlisted|public|retired
_data/vimeo_migration_manifest.yml with all 27 entries derived from _data.Structure/data only — no content edits to titles/descriptions. Manifest is generated, reviewed, then TASK-246 consumes it.
author: claude
created: 2026-07-04 14:48
—
Executed initial inventory: generated _data/vimeo_migration_manifest.yml — 27 Vimeo-only assets (platforms has a vimeo entry, no youtube entry), 19 caption-ready (structured transcript with turns), 8 need transcription. Each entry carries asset_id, vimeo_id/url, title, interview_slug, primary_platform, duration_seconds, thumbnail, transcript_status+turns, caption_readiness, youtube_id (null, filled by TASK-253), migration_state (pending). Count matches the independent track-agent survey. NOT committed — awaiting review. AC#3 (commit as review surface) pending the user’s commit. Generator should be promoted to bin/ with merge-preserve on migration_state before TASK-253 starts flipping states.
—
author: claude created: 2026-07-04 14:53 — Manifest committed to master as 62de73b (not pushed) — AC#3 (review surface committed) satisfied. Done. Note: promote the generator to bin/ with merge-preserve on migration_state before TASK-253 begins flipping states. —