podcast-pipeline — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited podcast-pipeline (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You convert one raw audio file into a complete publishing kit: transcript, chapters, show notes (two formats), SEO blog post, social shorts, quote graphics, and social-thread drafts. Modern podcast SEO requires a per-episode webpage with full transcript + JSON-LD — without it, episodes are invisible to Google and AI Overviews.
============================================================ === PRE-FLIGHT === ============================================================
Verify:
Recovery:
ffmpeg afftdn / RNNoise before transcription.============================================================ === PHASE 1: TRANSCRIPTION === ============================================================
Generate transcript with word-level timestamps and speaker labels.
Whisper-large-v3 (preferred, local):
import whisperx # whisperx adds word-level alignment + diarization
model = whisperx.load_model("large-v3", device="cuda", compute_type="float16")
audio = whisperx.load_audio(episode_path)
result = model.transcribe(audio, batch_size=16, language="en")
# Word-level alignment
align_model, metadata = whisperx.load_align_model(language_code=result["language"], device="cuda")
result = whisperx.align(result["segments"], align_model, metadata, audio, "cuda")
# Diarization (pyannote.audio)
diarize_model = whisperx.DiarizationPipeline(use_auth_token=HF_TOKEN, device="cuda")
diarize_segments = diarize_model(audio, num_speakers=NUM_SPEAKERS)
result = whisperx.assign_word_speakers(diarize_segments, result)Persist as transcript.json with schema:
{
"segments": [
{"start": 0.0, "end": 4.2, "speaker": "SPEAKER_00", "text": "Welcome to the show.", "words": [...]},
...
],
"metadata": {"language": "en", "duration_s": 3245.6}
}Also export transcript.srt (SubRip) and transcript.vtt (WebVTT) for video editing / web players.
VALIDATION: Transcript word count is non-trivial (≥ duration_min × 100, since typical speech is 130-150 wpm). Speakers labeled if > 1 voice in audio.
============================================================ === PHASE 2: CHAPTER DETECTION === ============================================================
Generate chapter markers in three ways and merge:
all-MiniLM-L6-v2 or text-embedding-3-small; cosine-distance peaks = chapter boundary.Reconcile the three into 5-12 chapters. Each chapter:
00:00 — Cold open
01:23 — Introducing today's guest
04:15 — How {topic} broke open
12:47 — The {key insight}
...Embed as ID3 chapter markers in the mp3 file (for podcast players that support chapters: Apple Podcasts, Overcast, Pocket Casts, Spotify).
VALIDATION: Chapter count 5-12. First chapter starts at 00:00. Title ≤ 60 chars each.
============================================================ === PHASE 3: SHOW NOTES — TWO FORMATS === ============================================================
Format A — Scannable (≤ 400 words, for podcast player descriptions and listen-page above-fold):
{Episode title}
{Guest}, {their title at their company}, joins us to discuss {three sentence hook}.
In this episode, we cover:
- {Beat 1 — verb-led}
- {Beat 2}
- {Beat 3}
- {Beat 4}
- {Beat 5}
## Resources mentioned
- {Book / article / tool}
- {...}
## Find {guest}
- {Twitter / X}
- {LinkedIn}
- {Their company / project}
## Chapters
{from Phase 2}Format B — Long-form SEO (1500-2500 words for the episode webpage):
VALIDATION: Format A respects 400-word cap. Format B has lead in first 80 words + full chapter coverage.
============================================================ === PHASE 4: SEO BLOG POST === ============================================================
Generate blog_post.md (~600-1000 words) — a derivative article driving organic traffic. Structure:
Plus blog_jsonld.json:
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "...",
"datePublished": "...",
"author": { "@type": "Person", "name": "..." },
"image": "...",
"mainEntityOfPage": "...",
"associatedMedia": {
"@type": "PodcastEpisode",
"name": "...",
"url": "...",
"associatedMedia": { "@type": "MediaObject", "contentUrl": "{mp3 url}" },
"partOfSeries": { "@type": "PodcastSeries", "name": "...", "url": "..." }
}
}VALIDATION: JSON-LD validates via Rich Results Test. Blog targets a different query than episode title.
============================================================ === PHASE 5: SOCIAL SHORTS (60-SECOND VERTICAL CLIPS) === ============================================================
For each chapter or quote, generate a short:
ffmpeg -ss start -to end.shorts/short_{n}.mp4 ready for direct upload.Generate captions per platform:
VALIDATION: Each short is ≤ 60s. Captions burned and word-synced. Vertical aspect ratio confirmed.
============================================================ === PHASE 6: QUOTE GRAPHICS === ============================================================
For each of the 3-5 best quotes, generate a 1080×1080 image (Instagram-grid friendly):
Pillow or playwright + HTML template + screenshot.Each graphic also gets a paired caption file with one-tap social copy.
VALIDATION: Quote text is verbatim from transcript. Attribution correct. Image dimensions exact.
============================================================ === PHASE 7: SOCIAL THREAD DRAFTS === ============================================================
Generate X/LinkedIn/Threads multi-post drafts:
For LinkedIn, longer single-post format (~1500 chars) is more native than multi-post threads — generate both.
VALIDATION: Character limits respected per platform.
============================================================ === PHASE 8: PACKAGE & PUBLISH === ============================================================
Final delivery:
episode-{slug}/
├── README.md # where each file goes
├── transcript.json
├── transcript.srt
├── transcript.vtt
├── chapters.txt
├── show_notes_short.md
├── show_notes_long.md
├── blog_post.md
├── blog_jsonld.json
├── episode_page.html # ready to drop into CMS
├── shorts/
│ ├── short_1.mp4 (with caption.txt + hashtags.txt)
│ └── ...
├── graphics/
│ ├── quote_1.png (with caption.txt)
│ └── ...
└── socials/
├── x_thread.md
├── linkedin_post.md
└── threads_post.mdVALIDATION: Every artifact present. README explains where each goes per CMS (WordPress media library, Ghost post HTML body, Substack import).
============================================================ === SELF-REVIEW === ============================================================
Score 1–5:
Common gap: trimming shorts at awkward mid-sentence boundaries. Verify start/end snap to sentence boundaries from word-level timestamps.
============================================================ === LEARNINGS CAPTURE === ============================================================
Append to ~/.claude/skills/podcast-pipeline/LEARNINGS.md:
============================================================ === STRICT RULES === ============================================================
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.