extract-source-sample — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited extract-source-sample (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
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.
This is an agent-executed skill. There are no Python scripts. The agent reads the run folder, builds the JSON, and stamps catalog links by hand. The content-goose run folders aren't always cleanly structured (some have empty production/ JSON, some carry everything in working/) — an agent adapts, a script would brittle out.
<run>."<run> for remix."Do NOT use to:
consumes this skill's output).
remix-ad skill).upload-ad-sample).| Input | Required | Notes |
|---|---|---|
run-dir | yes | Absolute path to a content-goose ad-run folder (e.g. clients/ladder/ad-runs/run-02-podcast-skit). |
out | no | Where to write the JSON. Default: <run-dir>/remix/source-sample.json. |
That's the entire interface.
Open each file if it exists; tolerate missing files (most production/*.json in older runs are empty stubs — fall back to working/):
working/script.json — primary source of truth for scenes, voices, set.production/asset-manifest.json — assets[] with role active_masterpoints at the master mp4; per-asset provider + metadata.model produce the atom-skill rows.
HOW_TO_MAKE_THIS_VIDEO.md — gets dumped verbatim into how_to.video-project.json — fallback for title / format when script.json doesn'tcarry them.
finals/*.mp4 — fallback for the master mp4 if asset-manifest is empty.working/characters/*.png — anchor portraits per character.working/*.py — driver scripts (render_vo.py, render_variants.py,render_clips.py, stitch.py, build_end_card.py, etc.). These are the source's runnable code; the remix consumer ports them. Capture in production_scripts[] (step 2 below).
For sources with character-pose stills (any run with a working/characters/ folder of <character>-<pose>.png files — podcast-skit, founder-led, testimonial, recreate-ugc, etc.), audit every PNG with file. Do NOT stop at the base portraits. The recipe shot list references variant expression PNGs (e.g. brittney-eyebrow-up.png, brad-phone-up.png) by filename; the consumer assumes they exist on disk and will spend real money on lipsync calls before discovering they don't.
For sources without character-pose stills (music-video b-roll, abstract animated, product-only) — skip this audit; variant_assets[] stays empty.
Record per file (when auditing):
{
"file": "brittney-eyebrow-up.png",
"pose_tag": "skeptical-eyebrow", // slug from filename stem
"kind": "real" | "lfs-pointer" | "missing",
"size_bytes": 142336
}file <path> says PNG image data, … for real binaries and ASCII text for LFS pointers. A real binary is >10KB in practice; an LFS pointer is <200 bytes.
Materialize LFS pointers before reading any binary. An LFS pointer is a tiny (<200 byte) ASCII file beginning with version https://git-lfs.github.com. If a PNG or mp4 looks like one, run:
cd <run-dir-or-repo-root>
git lfs fetch --include=<relative path>
git lfs checkout <relative path>before referencing it. If `git lfs pull` no-ops and the LFS endpoint returns 404 (objects committed as pointers but never pushed — common on content-goose), leave the entry as kind: "lfs-pointer" in variant_assets[]. The consumer will regenerate or scrape; this skill does NOT fabricate. See [[feedback_lfs_pointer_audit_before_paid_calls]] and [[feedback_fal_subscribe_error_envelope]] for the downstream cost when this audit is skipped — Hume run-03 lost ~$3 + 25 min to it.
source-sample.jsonShape (every key always present, arrays may be empty):
{
"title": "<from script.json or video-project.json>",
"format": "video",
"ratio": "<aspect_ratio from script.json — e.g. 9:16>",
"formatProfile": "podcast-skit-fabricated", // enum, see below
"media_url": "file://<abs path to master mp4>",
"thumbnail_url": null,
"brand": "<derive from path: clients/<brand>/ad-runs/...>",
"tags": [],
"recipe": { "shots": [...], "total_duration_sec": <int> },
"extracted_script": "HER: …\nHIM: …\n…",
"skills_used": ["generate-voiceover", "..."], // atoms only
"skills_source": "measured" | "derived-from-production-scripts" | "inferred-canonical" | "guessed",
"how_to": "<contents of HOW_TO_MAKE_THIS_VIDEO.md or null>",
"production_scripts": [
{ "path": "working/render_vo.py", "role": "voiceover" },
{ "path": "working/render_variants.py", "role": "stills" },
{ "path": "working/render_clips.py", "role": "lipsync" },
{ "path": "working/stitch.py", "role": "stitch" },
{ "path": "working/build_end_card.py", "role": "end_card" }
],
"remix_spec": {
"version": 1,
"skills": [{"slug": "...", "provider": "...", "model": "..."}],
"worlds": [{"key": "...", "name": "...", "set": "...", "lighting": null, "color_grade": null, "reference_image_url": null, "catalog_id": null}],
"characters": [
{
"key": "her",
"name": "Brittney",
"gender": "f",
"soul_id": null,
"anchor_asset_id": "asset-char-her-base-01",
"anchor_image_url": "file://...png",
"method": "anchor-ref",
"description": null,
"catalog_id": "brittney",
"variant_assets": [
{ "file": "brittney-base.png", "pose_tag": "base", "kind": "real", "size_bytes": 1842336 },
{ "file": "brittney-eyebrow-up.png", "pose_tag": "skeptical-eyebrow", "kind": "lfs-pointer", "size_bytes": 132 },
{ "file": "brittney-shrug.png", "pose_tag": "shrug", "kind": "missing", "size_bytes": 0 }
]
}
],
"voices": [{"voice_id": "kPzsL2i3teMYv0FxEYQ6", "voice_name": "Brittney", "provider": "elevenlabs", "settings": {"stability": 0.45, "similarityBoost": 0.78, "style": 0.45, "useSpeakerBoost": true}, "selected": true, "catalog_id": "brittney"}]
}
}#### formatProfile — open string, drives downstream pipeline choices
A short slug naming the source's ad format. Open vocabulary — the content-goose molecule library has ~40 distinct ad formats and growing; don't try to fit a closed enum. Pick a slug that matches the source's molecule name (e.g. create-podcast-skit-ad → podcast-skit-fabricated, create-cinematic-music-video → music-video-sung), or invent a new short slug when none fits.
The consumer (remix-script, remix-ad) routes on the slug. Two routing properties downstream cares about — record them alongside the profile so the consumer doesn't have to re-derive:
"formatProfile": "podcast-skit-fabricated",
"formatProfileProperties": {
"audioType": "spoken-vo" | "sung-music" | "mixed",
"sceneCount": "flexible" | "locked-to-source"
}audioType drives the caption pipeline (spoken-vo → Whisper word-level;sung-music → script.json scene windows, because Whisper returns 🎵 Music Playing 🎵; mixed → split per segment).
sceneCount drives whether the remix can flex (most spoken formats) ormust lock 1:1 (sung-music, anywhere lyric meter sets timing).
If you can't confidently assign either property, leave it null — the consumer surfaces to the user rather than guessing.
Known slugs from past runs (extend as new formats appear):
| Slug | audioType | sceneCount | Origin |
|---|---|---|---|
podcast-skit-fabricated | spoken-vo | flexible | Ladder run-02 (HER/HIM, 22 scenes, talking-head + broll) |
music-video-sung | sung-music | locked-to-source | Loóna run-01 (single VOCAL track, 14 lyric-locked beats) |
The two known slugs are what real retros produced. Add new rows here when you extract a source that fits a new molecule (single-host-ugc, animated- explainer-villain, stop-motion-tabletop, goose-vs-tool, hook-variant, etc.). Don't pre-invent slugs that haven't shipped yet.
Per-section derivation:
script.json.scenes[]:{ "id": "s01", "shot": "<still filename>", "type": "<shot type>", "speaker": "HER|HIM|null", "duration_sec": <parsed from time field e.g. "0:02-0:05"→3> }. Sum durations into total_duration_sec.
Add `pose_tag` ONLY when the filename matches `<character>-<pose>.png` (the character-pose convention used by podcast-skit and other character-led formats). Derive it by stripping the character prefix from the filename stem (e.g. brittney-eyebrow-up.png → pose_tag: "eyebrow-up", brad-phone-up.png → pose_tag: "phone-up"). For formats whose shots aren't keyed to character poses (music-video b-roll, product hyperframes, abstract animated scenes), omit pose_tag from the shot.
<who>: <text> per scene, newlinesbetween.
script.json.set_description:{ key: <slug of the run's setting label>, name: <human label>, set: <full set_description>, lighting: null, color_grade: null, reference_image_url: null, catalog_id: null }.
script.json.voices:
key: "her" / "him" (lowercase role)name: voices.<role>.namegender: HER→"f", HIM→"m", NB→"nb"soul_id: null unless the run has a Higgsfield Soul anchoranchor_asset_id: "asset-char-<key>-base-01" if an anchor PNG exists, else nullanchor_image_url: file:// URL to working/characters/<name>-base.png(or the first png matching the lowercase name), else null
method: "anchor-ref" if anchor PNG present, else nulldescription: null unless surfaced in the run's how-tocatalog_id: stamped in step 3.stills (the <character>-<pose>.png filename convention). One entry per PNG in working/characters/ whose filename starts with the character's lowercase name. Each entry {file, pose_tag, kind: real|lfs-pointer|missing, size_bytes} — derived from the file audit in step 1. This is the canary the consumer needs to decide whether to regenerate variants before paid lipsync calls. Include the base entry too (pose_tag: "base"). For formats without per-character pose stills (music-video, animated, product-only), set variant_assets: [] or omit the key.
script.json.voices:voice_id, voice_name (from name), provider: "elevenlabs"settings: copy from script's settings, rename to camelCase(similarity_boost→similarityBoost, use_speaker_boost→useSpeakerBoost)
selected: true for the first voice in script order, false otherwise— exactly one selected: true.
catalog_id: stamped in step 3.production/asset-manifest.json.assets[]: each asset's provider + metadata.model (+ skill or metadata.skill for the slug) produces one row, deduped. Drop molecule slugs — only atoms allowed. The canonical atom inventory is:
generate-voiceover, generate-character-image, generate-broll-shot,
generate-lipsync, generate-music, compose-master, burn-captions,
add-captions-klap, render-hyperframe, stitch-clips, build-end-cardWhen the asset-manifest is empty (common in older runs), derive atoms generically — don't hard-code per-format recipes. The repo has ~40 ad-format molecules and growing; canonical recipes drift fast. Use this cascade:
production_scripts[] (next section) — each driver script'sactual provider calls are authoritative.** Open render_vo.py, grep for elevenlabs/fal/higgsfield imports + endpoint URLs, and derive one atom row per provider × model the script actually invokes. This is more reliable than any guessed recipe because it reflects what the source ACTUALLY did, not what the format usually does.
podcast-skit-fabricated canonical recipe ONLY when formatProfile === "podcast-skit-fabricated" — the one format with enough run data to canonicalize:
[
{"slug": "generate-voiceover", "provider": "elevenlabs", "model": null},
{"slug": "generate-character-image", "provider": "higgsfield", "model": null},
{"slug": "generate-lipsync", "provider": "fal", "model": null},
{"slug": "compose-master", "provider": "ffmpeg", "model": null},
{"slug": "burn-captions", "provider": "ffmpeg", "model": "libass"}
]Note model: null — model ids drift (eleven_multilingual_v2 → eleven_multilingual_v3, veed/fabric-1.0 → veed/fabric-2.0); don't freeze them in the SKILL.
production_scripts**, surface to the user and ask which atoms ran. Don't invent — skills_source: "guessed" is worse than null.
Mirror skills_used as the flat slug list.
obtained, so the consumer knows how much to trust it:
"measured" — derived from a populated asset-manifest.json (cascadestep would have used the real provider/model fields).
"derived-from-production-scripts" — grepped from the source'sworking/*.py driver scripts (cascade step 1). Reliable: reflects actual API calls.
"inferred-canonical" — fell back to the canonical podcast-skitrecipe (cascade step 2). Only valid when formatProfile === "podcast-skit-fabricated".
"guessed" — none of the above worked and the user supplied thelist. Should be rare; surface in the summary.
The consumer reads this field. Without it, a guessed atom list propagates downstream as if it were measured (Ladder extract retro flagged this — skills_source=inferred quietly made it into Hume's remix-plan as fact). When skills_source !== "measured", the consumer should cross-check against production_scripts[] before trusting any individual row.
working/*.py file in the runwith {path, role}. Roles: voiceover | stills | variants | lipsync | stitch | end_card | music | composites | other. Match by filename:
| Filename | Role |
|---|---|
render_vo.py, gen_vo.py | voiceover |
render_keyframes.py, gen_keyframes.py | stills |
render_variants.py | variants |
render_clips.py | lipsync |
stitch.py, compose.py, compose_master.py | stitch |
build_end_card.py | end_card |
gen_music.py | music |
burn_captions.py, make_subtitles.py | (none — these are atom-level scripts) |
build_composites.py | composites |
| anything else | other |
These are the source's runnable code. The consumer ports them as the starting template — molecule SKILL.mds are recipes, not executables.
Library location: `/Users/akhil/projects/content-goose/assets/character-library/`. Layout:
character-library/
├── index.json ← machine-readable catalog
├── INDEX.md ← human-readable table (hand-curated)
└── <key>/
├── character.json
└── shots/
└── front.pngindex.json schema (one row per character):
{
"key": "brittney",
"name": "Brittney",
"gender": "f",
"ethnicity": "white",
"age_band": "20s",
"archetype": "podcast-skeptic-host",
"voice": "Brittney",
"voice_id": "kPzsL2i3teMYv0FxEYQ6",
"default_shot": "brittney/shots/front.png",
"shots": ["front"],
"source": "reuse"
}character.json schema (per character folder — match an existing one verbatim; e.g. brittney/character.json):
{
"key": "brittney",
"name": "Brittney",
"gender": "f",
"ethnicity": "white",
"age_band": "20s",
"archetype": "podcast-skeptic-host",
"description": "...",
"source": "reuse",
"origin_anchor_path": "clients/ladder/ad-runs/run-02-podcast-skit/working/characters/brittney-base.png",
"generation_prompt": null,
"default_voice": { "name": "Brittney", "voice_id": "kPzsL2i3teMYv0FxEYQ6", "provider": "elevenlabs" },
"note": null,
"shots": [{"angle": "front", "path": "shots/front.png", "is_default": true}],
"default_shot": "shots/front.png"
}Matching rules (per character in the source):
index.json for a row whosevoice_id equals the source character's voice_id. If exactly one match, that row's key is the catalog id. Done.
for a row whose name equals the source character's name (case-insensitive). If exactly one match, that row's key is the catalog id.
When a match is found, stamp catalog_id on BOTH the character row AND the voices[] row that shares the same voice_id.
Extending the library (no-match path):
-, strip.If the key already exists in index.json, append -2, -3, etc.
assets/character-library/<key>/shots/.assets/character-library/<key>/shots/front.png. Materialize the source PNG first if it's an LFS pointer (see step 1).
assets/character-library/<key>/character.json with the schemaabove. Fill in what you can confidently derive — leave the rest null rather than guessing:
key, name, gender, default_voice — from the source.ethnicity, age_band, archetype, description — leave nullunless the run's HOW_TO.md or character description explicitly states them.
source: "reuse" (we're pulling from an existing run, not generatingfresh).
origin_anchor_path: the source PNG's path **relative to thecontent-goose repo root** (e.g. clients/ladder/ad-runs/run-02-podcast-skit/working/characters/brittney-base.png).
generation_prompt: null.note: null.shots: [{"angle": "front", "path": "shots/front.png", "is_default": true}].default_shot: "shots/front.png".assets/character-library/index.json matching thatcharacter.json's outer fields. Bump total by 1. Keep characters[] in the existing order — append at the end.
library indexer if there is one. Do NOT edit INDEX.md`.
catalog_id: "<new-key>" on the source-sample.json's characterrow + the voice row sharing that voice_id.
Refuse to fabricate library fields. If you don't know a character's ethnicity / age band / archetype, write null. A wrong guess pollutes future remixes — the user prefers a null they can fill in over a confident wrong value.Default output path: <run-dir>/remix/source-sample.json (create the remix/ folder if it doesn't exist; do not touch anything else in the run folder).
Print a summary:
extracted source-sample at: <out path>
title: <title>
brand: <brand>
formatProfile: <profile>
recipe: <N> shots, <total_duration_sec>s
remix_spec: <S> skills (<measured|inferred>), <W> worlds, <C> chars, <V> voices
variant audit: <X> real / <Y> lfs-pointer / <Z> missing across <C> chars
production_scripts: <K> scripts ({voiceover, lipsync, stitch, end_card, …})
catalog links:
characters: her=brittney, him=brad
voices: kPzsL2i3teMYv0FxEYQ6=brittney, T4x5CtnhOiichhcqFzgg=brad
library extensions: <none | <key> (new)>If any catalog link is null, surface that too — the user wants to know what didn't link. If `variant audit` shows any LFS pointers or missing PNGs, lead with that in the summary — it's the single biggest cost multiplier for the downstream remix if missed.
structured — adapt to what's actually present rather than imposing a rigid extractor.
slugs silently; surface a note if you couldn't recover at least one atom.
unless the user passes a different selection.
similarity_boost / use_speaker_boost.catalog_id is null only when matching genuinely fails AND thelibrary-extension step also failed** (e.g. no anchor PNG to seed shots/front.png). Otherwise every character + voice should end up linked.
into the library.
it.
on the source run, write-only on <run-dir>/remix/source-sample.json + the character library.
the script is or refuse.
anchor_image_url + anchor_asset_id + method stay null, AND library extension can't proceed (no shots/front.png to copy). Stamp the row's catalog_id: null and tell the user in the summary.
and ask which one to link to.
fall back to the canonical podcast-skit atom list; flag in the summary that skills were inferred rather than read.
git lfs isn't installed or therepo isn't an LFS clone** → surface the error; don't copy the pointer bytes into the library.
<run-dir>/remix/source-sample.json — the upload-sample-shape JSON.assets/character-library/<key>/ andupdated assets/character-library/index.json if any source character wasn't in the library yet.
The output JSON is what the next agent step (script rewrite / character swap) and remix-ad consume.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.