Between July 17 and July 22, three consecutive longform YouTube specs failed the quality gate. Two of them — Airtable on July 18, Supabase on July 21 — got quarantined by the publish-night cron itself. The third, Trello, was heading the same way on July 22 until I ran the audit by hand and repaired it. Each one had passed the generation step and landed in content/yt-longform-queue/ looking fine. As of today, July 22, the last longform upload went out on July 17 and two scheduled slots have burned since.
The fix wasn't a better spec template. It was adding a pre-audit step to the existing queue watchdog — it now runs node scripts/audit-youtube.mjs on every pending spec it finds, on a daily cron instead of only on publish nights.
Here are the three failure modes those specs actually hit.
The timing gap
The longform publish workflow is on 0 23 * * 2,4,6 — 23:00 UTC, Tuesday/Thursday/Saturday. By the time it has picked a spec, failed the gate, and pushed the quarantine commit it's around 23:45 UTC (08:45 JST). When it quarantines a spec the slot is burned for the day and there's no time to regenerate before the window closes. The GitHub Actions job summary is technically there and practically invisible: nobody is reading it at 08:45 JST having just woken up.
The watchdog already existed. It was checking whether the main branch queue had gone stale — catching the branch-drift pattern where Shorts scripts authored on Claude session branches never merged to main. The pre-audit addition was one more job step: find pending queue specs, run the same audit binary the publish-night gate runs, fail the watchdog job if anything fails.
Same audit code, earlier — but less earlier than the commit message I wrote for it claims. The watchdog cron is 0 23 * * *, which is the same 23:00 UTC minute the publish cron uses. On a Tuesday, Thursday or Saturday the two jobs fire together, so the pre-audit gives no lead time at all for that night's slot; the run that actually helps is the previous day's, roughly 24 hours ahead. The publish-night gate stays unchanged and doesn't get any weaker. Moving the watchdog cron to the middle of the day is the obvious follow-up, and I haven't done it.
Failure one: missing required fields
Two of the three failed the same way. The longform quality gate requires sources[] (at least four entries, resolving to four unique HTTPS URLs), verified_at, and quality.decision_framework plus quality.original_value. These fields were added to the gate after the analytics auto-tuner started generating more longform specs — but the spec template in the generation prompt didn't list them explicitly. The prompt said "follow the quality gate requirements" without naming the specific keys.
The result: the spec passed the shape check at generation time (the YAML template had the right top-level structure), then failed the semantic check at publish time when the gate validated the field values and found them absent.
audit-youtube.mjs is explicit enough about it — a thin sources array produces at least four explicit sources required, followed by four unique HTTPS source URLs required when the URLs don't survive dedupe, and the missing keys each get their own line. What makes earlier detection valuable here isn't better error messages — it's having any window at all to regenerate the spec with the correct fields, check it manually, and commit before the publish window opens.
The structural fix was adding a self-check command to the directive's LONGFORM section, so the generation routine runs node scripts/audit-youtube.mjs on the new spec before committing it. The morning pre-audit is still there as a second gate. The self-check is only as reliable as the prompt following it; the watchdog is reliable regardless.
Failure two: hype phrases from recycled framing
The audit script tests a banned-phrase list against the narration — for a longform that's every segment's text concatenated, for a Short it's the script field. Hype superlatives, "no one is talking about", "zero marketing budget", and for Shorts a vague-opener regex that catches "have you ever wondered". Titles and descriptions aren't covered by any of it, which is a gap I should close.
The generation prompt tells the model to avoid these phrases. It doesn't always.
The concrete case is the July 22 Trello spec. One segment read "Planka has the best chance of getting your design team off Trello" — /\bthe best\b/i matched, and the rule doesn't care that the phrase was comparative rather than a superlative claim about the tool. It would have failed that night alongside the missing fields. I caught it by running the audit by hand and rewrote the line, so the repaired spec is sitting in the queue for the next scheduled run rather than heading for the quarantine directory.
I haven't automated the fix — editing the spec file automatically introduces risk that a bad edit propagates silently. The watchdog flags it and stops. Manual edit, re-run audit, commit. The manual step is faster than debugging a bad auto-edit.
Failure three: no concrete downside section
The Airtable spec is the interesting one, because on the field check it was clean: four sources, a verified_at, both quality.* keys. It still got quarantined, on a rule I'd half forgotten writing — the script has to contain at least one segment matching /avoid|not for|don'?t choose|downside|trade-?off/i. A comparison video that never says who should stay put isn't a comparison, it's a listicle.
The rule is crude. It's a regex over segment text, so a spec can satisfy it with the word "trade-off" in a throwaway clause and pass. But the thing it caught was real: the script walked through four Airtable alternatives without once naming a reason to keep using Airtable.
Catching this at 23:45 UTC leaves no time to assess, and the slot burns. Catching it a day earlier means I can read the spec, decide whether it genuinely lacks the section or just phrases it in words the regex doesn't know, and either patch a segment or regenerate with a fresh angle.
The audit also runs a Jaccard duplicate check against the last 30 uploaded specs — title overlap at 0.76, opening-line overlap at 0.82. That one lives entirely inside the Shorts path; auditLongform has no similarity check, so it played no part in any of these three. On the Shorts side it exists for the case where the auto-tuner keeps a winning archetype for several uploads in a row and the titles start sharing tokens.
The change is one workflow step — 16 lines of YAML, seven of them executable shell. The audit binary was already there. The scheduling was already there. The only new thing is running the same check on the days the publish cron doesn't.
What's changed: a spec that would fail the gate turns the watchdog red in a GitHub Actions summary instead of silently burning the publish slot at 23:45 UTC. Whether that keeps longform on schedule I can't say yet — the step landed today, and it currently shares a cron minute with the job it's supposed to warn about.
Part of an ongoing 6-month experiment running three AI-curated directory sites. The technical claims here are real; this article was AI-assisted.
Top comments (0)