Write talking-head scripts and produce Instagram reels and YouTube shorts
94
97%
Does it follow best practices?
Impact
85%
2.36xAverage score across 3 eval scenarios
Low
Low-risk findings worth noting
#!/usr/bin/env python3
"""Check a yap script's SHAPE. Never its quality.
The three Triangle beats being present says nothing about whether the analogy
lands or the thought is worth arguing with — that is a judgment call and it
stays with whoever is writing. What a script CAN check is the shape the
framework prescribes and the failure modes that are fully enumerable:
- all three beats present, in order
- runtime against the target, from word count
- a greeting opener, which is the exact failure the T beat replaces
- words and connectives that belong to writing rather than speech
Whether a script SOUNDS written is mostly rhythm, and rhythm is not gradeable
by counting. The vocabulary half of it is, so that half is here and the rest is
in references/spoken-tells.md, ending in the only test that really decides it:
read it aloud, standing up, at pace.
Resist reading more into a clean run than it means. A script can pass every
check here and still be boring.
Usage:
check_yap.py draft.md --target 60
check_yap.py draft.md --target 90 --json
"""
import argparse, json, re, sys
from pathlib import Path
BEATS = ("T", "MAES", "A", "N")
WPM_LO, WPM_HI = 150, 200 # spoken pace; 150 is deliberate, 200 is brisk
# The failure the T beat exists to replace. Enumerable, which is why it is here
# and "is this thought interesting" is not.
GREETINGS = (
"hey guys", "hey everyone", "hi guys", "what's up guys", "whats up guys",
"welcome back", "welcome to another", "in this video", "in today's video",
"today i want to talk about", "today we're going to talk about",
"thanks for watching", "before we get started", "so today",
"let's talk about", "i wanted to make a video about",
)
# Words and connectives that show up in scripts far more than in speech. A
# count, not a verdict — one "crucial" is a word choice, six is a voice.
WRITTEN_WORDS = (
"delve", "landscape", "realm", "testament", "crucial", "pivotal",
"robust", "seamless", "leverage", "navigate", "unlock", "harness",
"elevate", "foster", "underscore", "myriad", "plethora",
)
WRITTEN_PHRASES = (
"moreover", "furthermore", "additionally", "consequently",
"in conclusion", "it's important to note", "it is important to note",
"it's worth noting", "that being said", "to recap", "in summary",
)
def stem(word):
"""Match stem for a tell, so inflections are caught.
English drops a final 'e' before '-ing': "delve" becomes "delving", not
"delveing". Matching on the full word therefore misses the most common form
of every -e verb in the list.
"""
return word[:-1] if word.endswith("e") else word
def written_tells(text):
"""Written-not-spoken words and phrases, with counts, most frequent first."""
low = " ".join(text.lower().split())
hits = {}
for w in WRITTEN_WORDS:
n = len(re.findall(rf"\b{re.escape(stem(w))}\w*\b", low))
if n:
hits[w] = n
for ph in WRITTEN_PHRASES:
n = low.count(ph)
if n:
hits[ph] = n
return sorted(hits.items(), key=lambda kv: (-kv[1], kv[0]))
def parse_beats(text):
"""Beat label -> its text, in the order they appear."""
found = []
for m in re.finditer(r"^\s*\[(\w+)\]\s*(.*?)(?=^\s*\[\w+\]|\Z)",
text, re.MULTILINE | re.DOTALL):
label = m.group(1).upper()
if label in BEATS:
found.append((label, m.group(2).strip()))
return found
def spoken_words(text):
"""Words a mouth would actually say — beat markers and headings removed."""
text = re.sub(r"^\s*\[\w+\]", " ", text, flags=re.MULTILINE)
text = re.sub(r"^#{1,6}\s.*$", " ", text, flags=re.MULTILINE)
text = re.sub(r"[*_`>|]", " ", text)
return [w for w in text.split() if any(c.isalnum() for c in w)]
def runtime_range(n_words):
"""Seconds at the slow and brisk ends of a normal speaking pace."""
return round(n_words / WPM_HI * 60, 1), round(n_words / WPM_LO * 60, 1)
def find_greeting(opening):
"""The greeting phrase this opens with, or None."""
low = " ".join(opening.lower().split())
for g in GREETINGS:
if low.startswith(g) or low[:60].find(g) != -1:
return g
return None
def check(text, target=None):
"""Every shape finding, as (level, message). level is 'fail' or 'warn'."""
out = []
beats = parse_beats(text)
labels = [b for b, _ in beats]
if not beats:
return [("fail", "No beat markers found. Mark the draft up as [T], "
"[MAES] and [A] or [N] — the shape is the point.")]
if "T" not in labels:
out.append(("fail", "No [T] beat. The video has no thought to open on."))
if "MAES" not in labels:
out.append(("fail", "No [MAES] beat. This is the one that makes a "
"topic watchable, and it is the one people skip."))
if not ({"A", "N"} & set(labels)):
out.append(("fail", "No [A] or [N] beat. Nothing to do means nothing "
"to remember."))
order = [l for l in labels if l in BEATS]
expected = [l for l in ("T", "MAES") if l in order] + \
[l for l in order if l in ("A", "N")]
if order != expected:
out.append(("fail", f"Beats are out of order: {' -> '.join(order)}. "
f"The thought opens, MAES lands it, the close "
f"follows from the MAES."))
if "A" in labels and "N" in labels:
out.append(("warn", "Both [A] and [N]. Pick one — doing both dilutes "
"both."))
first = next((t for b, t in beats if b == "T"), "")
g = find_greeting(first)
if g:
out.append(("fail", f"The [T] beat opens with {g!r}. That is the exact "
f"four seconds the framework removes — start on "
f"the thought itself."))
maes = next((t for b, t in beats if b == "MAES"), "")
if maes and len(maes.split()) < 25:
out.append(("warn", f"[MAES] is {len(maes.split())} words. It is the "
f"body of the video, not a garnish."))
tells = written_tells(" ".join(t for _, t in beats))
if tells:
shown = ", ".join(f"{w} x{n}" if n > 1 else w for w, n in tells[:6])
out.append(("warn", f"Reads written, not spoken: {shown}. See "
f"references/spoken-tells.md — then read it aloud, "
f"which is the test that actually decides."))
words = spoken_words(text)
lo, hi = runtime_range(len(words))
if target:
if lo > target * 1.15:
out.append(("fail", f"{len(words)} words runs {lo:.0f}-{hi:.0f}s "
f"against a {target}s target. Cut."))
elif hi < target * 0.7:
out.append(("warn", f"{len(words)} words runs {lo:.0f}-{hi:.0f}s "
f"against a {target}s target. Room to develop "
f"the MAES."))
return out
def main():
ap = argparse.ArgumentParser()
ap.add_argument("script")
ap.add_argument("--target", type=float, metavar="SECONDS",
help="Intended runtime, to check the word count against")
ap.add_argument("--json", action="store_true")
a = ap.parse_args()
text = Path(a.script).read_text()
findings = check(text, a.target)
words = spoken_words(text)
lo, hi = runtime_range(len(words))
beats = [b for b, _ in parse_beats(text)]
if a.json:
print(json.dumps({
"script": a.script, "beats": beats, "words": len(words),
"runtime_s": [lo, hi],
"findings": [{"level": l, "message": m} for l, m in findings],
"ok": not any(l == "fail" for l, _ in findings),
}, indent=2))
else:
print(f"{Path(a.script).name}: {' -> '.join(beats) or 'no beats'} "
f"{len(words)} words ~{lo:.0f}-{hi:.0f}s")
for level, msg in findings:
print(f" {'FAIL' if level == 'fail' else 'warn'} {msg}")
if not findings:
print(" shape is clean")
print("\nShape only. A clean run says nothing about whether the "
"analogy lands or the thought is worth arguing with — read it.",
file=sys.stderr)
sys.exit(1 if any(l == "fail" for l, _ in findings) else 0)
if __name__ == "__main__":
main().tessl-plugin
evals
skills
reel-builder
assets
references
scripts
yap-writer