On 2026-07-07 my YouTube content pipeline generated a contradiction. The daily directive file — a machine-generated Markdown file that tells the script-writer routine which video archetype to produce — said archetype = build_in_public. The knowledge bank file it wrote alongside it said Never produce: build_in_public.
Both outputs came from the same run.py in the same CI run.
The fix took three lines of Python. The incident is now a comment in the source code. Here’s what happened.
How the self-contradiction formed
The YouTube auto-tuner works in two steps. First it classifies uploaded videos by archetype and measures view performance. Then it writes a directive file naming today’s target archetype based on which has the best view-per-day ratio.
There’s a 3-in-a-row guard: if the top-performing archetype has appeared in the last three uploads, the tuner picks the second-best instead — to avoid the queue becoming repetitive. The intent is good. The problem was in the fallback logic when data is sparse.
When few videos are classified and the view counts are noisy, the ranking can produce build_in_public as the alternate pick — not because it performed well, but because it was the only option the ranking didn’t discard. At the same time, the knowledge bank accumulates rules from previous runs. Over two months, build_in_public had accumulated enough “this performs poorly” notes that the knowledge bank included it in a Never produce: list.
The tuner was writing both the knowledge bank (with the prohibition) and the directive (recommending the prohibited archetype) in the same run. Neither step checked the other’s output. The contradiction landed in the committed files and the script-writer routine saw conflicting instructions.
The fix
# scripts/yt-analytics/run.py
DEAD_ARCHETYPES = frozenset({"build_in_public", "meta", "curated", "technical"})
# ...inside the ranking function:
alt = next(
(a for a in ranked_list
if a != target and a not in DEAD_ARCHETYPES and a != avoid_arch),
None,
)
# Final safety check before writing the directive:
if target in DEAD_ARCHETYPES:
target = DEFAULT_TARGET_ARCHETYPE
switched_from = None
Enter fullscreen mode Exit fullscreen mode
Three changes: a module-level constant, a membership check in the fallback selection, and a final guard before the directive is written.
frozenset instead of list is intentional. Membership testing on a frozenset is O(1) regardless of how many archetypes are in it. That doesn’t matter at four entries, but the constant is checked on every call to the ranking function and the final guard. Using a set also makes the declaration read as a set of values with no implied order, which is what it is.
The final guard is the important one. Even if the ranking and the fallback selection both had bugs, the directive cannot name a dead archetype as today’s target. The final check is a hard wall: if target in DEAD_ARCHETYPES, unconditionally replace it with DEFAULT_TARGET_ARCHETYPE (product_findindiegame, the proven performer).
Why not just remove dead archetypes from the ranking?
I did add the fallback-selection filter (a not in DEAD_ARCHETYPES). But I kept the final guard as a second check for two reasons.
First, defense in depth. If someone adds code that modifies target after the ranking step but before the guard, the prohibition still holds. The guard is cheap and makes the invariant explicit in one readable place.
Second, the final guard is where the code documents its intent. The comment reads:
# Final safety: the directive must never name a dead archetype as today's
# target, no matter how the ranking shook out. Fall back to the proven bet.
Enter fullscreen mode Exit fullscreen mode
This makes the prohibition visible to anyone reading the function end-to-end, not just to someone who knows to look for the DEAD_ARCHETYPES constant at the top of the file.
What I’d do differently
The underlying issue was that the knowledge bank and the directive were generated by the same script but their constraints weren’t shared. The knowledge bank rule was written as prose (Never produce: build_in_public). The directive was generated by code. The two representations couldn’t check each other.
The right fix long-term is for the knowledge bank to be structured data — a YAML or JSON file — so code can read the prohibition list at runtime instead of maintaining a parallel DEAD_ARCHETYPES constant. Then there’s one source of truth. Right now DEAD_ARCHETYPES and the Never produce line in the directive template are kept in sync manually, which is the same KEEP IN SYNC problem I have with the isCurated gate and the Astro config.
I also should have noticed earlier that a banned archetype could still emerge as a fallback target. The 3-in-a-row guard was added to improve variety, not to create a fallback path to dead archetypes. The guard needed to know about the prohibition list from the start. I added the membership check after the fact.
One observation about AI-generated directives
This incident is specific to automation that generates instructions for other automation. The self-contradiction didn’t cause an error — it caused ambiguity. The script-writer routine received contradictory instructions and had to resolve them implicitly. On 2026-07-07, it resolved them wrong.
When a system generates directives, the directives need to be consistent with the system’s own constraints. That sounds obvious. The tricky part is that the constraints accumulate in one place (the knowledge bank) and the directives are generated by a different code path. Keeping them in sync requires either structured shared state or a guard at the generation boundary — preferably both.
The frozenset is the guard. The structured knowledge bank is the work I haven’t done yet.
Part of an ongoing 6-month experiment running three AI-curated directory sites. The technical claims here are real; this article was AI-assisted.
답글 남기기