Five things I noticed or shipped this week while running the AI directory sites and a YouTube automation pipeline. No big announcements — mostly friction I hit and adjustments I made.
1. GitHub Actions was eating my free quota silently
I had a Bluesky posting cron running three times a day. The posting script sends one queued item per run, and as long as the queue had entries all three slots did post: the queue file records 19 posts over May 18–24, two to four on each of those days. Every run that posted committed the updated queue back to main, which at the time also kicked off the matrix build (four apps: the three sites plus the dashboard). When I looked at my Actions minutes consumed this week, the math was embarrassing: 3 cron runs × 5-6 minutes each × 7 days = roughly 120 minutes/week just for posting a tweet-sized status update.
Two fixes. First, I collapsed the Bluesky cron from three off-minute slots (37 23 * * *, 37 7 * * *, 37 13 * * *) to a single daily trigger — that cuts the scheduled Bluesky runs, and the Actions minutes they burn, to a third. It also cuts the output: at most one post a day now, instead of the two-to-four I was pushing out before. Second, I added a path filter so content-only commits (articles, copy edits) skip the four-way matrix build entirely. A new article doesn't need a full CI rebuild of all three Astro sites.
Actions quota is not infinite. Even on a paid plan, rebuilding four apps because a bot committed one line of queue JSON is a bad habit to get into before the repo scales.
2. Bluesky posts need a quality gate before they leave the queue
I added a QC gate to the Bluesky post pipeline this week — a step that reads each queued post, checks it against a short ruleset (no broken links, no expired announcements, no posts that reveal the automation stack in a tone that sounds like spam), and drops anything that fails before the cron fires.
The immediate trigger: I audited the outbox and found 17 posts that read like a bot talking to itself. Phrases like "🔁 queued" and "auto-generated" in a context where I had not disclosed that. Not illegal, but not the tone I want on a personal account.
The gate runs as a step before the actual bluesky post command. If it rejects a post, the item is pulled out of content/bluesky-queue.jsonl and appended to data/bluesky-qc-rejected.jsonl with the failing rule recorded, so nothing is lost and I can review the rejects later. Net result: one post a day, but ones I would not be embarrassed to have written manually.
3. Model routing is a topic I write about, not something I run
I wrote a YT script this week about model routing — the pattern where you send different content types to different AI models based on some classifier, e.g. "short factual" queries to a faster/cheaper model and "synthesis" queries to a more capable one.
Writing it sent me to check my own setup, and there is no router in it. packages/shared/src/claude/index.ts has a single default model (claude-haiku-4-5) and no task classifier at all, and the production content refresh workflow deliberately doesn't even get an ANTHROPIC_API_KEY — it runs on built-in fallback templates.
So I have no first-hand latency or cost numbers to offer here, and I'm not going to invent any. What I can say is that at indie scale, with a handful of daily calls, a routing layer has never been the thing standing between me and a working pipeline. At real volume it probably pays off.
4. Openverse CC0 filtering is not default — you have to opt in
I added image slides to the YouTube slide renderer this week using Openverse. The API returns results across multiple Creative Commons license types by default. For a monetized YouTube channel, using CC-BY images without visible on-screen attribution is a real licensing problem.
The filter I needed is license=cc0,pdm — not the default. Without it, you get CC-BY, CC-BY-SA, CC-BY-NC results mixed in with no indication they require credit. The API returns a license field per result, but if you're batch-processing slides and forget to filter upstream, you will miss one eventually.
A second issue: Openverse sometimes returns results pointing to images that have since been removed from the source host. The API returns 200 with metadata, but the actual image URL 404s. So the renderer walks the result list instead of trusting the first hit: it tries to download and decode each candidate in turn, and anything that fails to fetch or open — or comes back smaller than 200px — is skipped in favour of the next result.
5. Self-hosted observability tools have a comfort vs. capability gap
I did a comparison of Netdata, SigNoz, and OpenObserve this week for the purpose of monitoring the three sites. All three install in under 10 minutes. The divergence shows up in what you're comfortable touching at 2am when something breaks.
Netdata is the most comfortable out of the box — it auto-discovers processes and starts charting immediately. SigNoz requires you to send OpenTelemetry traces explicitly, which means instrumenting your code first. OpenObserve is log-focused and works well if you're piping structured JSON logs, but its dashboard interface has a steeper learning curve than the other two.
For my current situation (all three sites on Cloudflare Pages since early May, no VPS to instrument), all three are somewhat over-engineered. I ended up with a single Datadog free-tier integration for error alerting and leaving the self-hosted tools as a future option if the infrastructure changes.
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)