Workflow Automation Case Study
case-study · gifted-blog-automation

Blog Automation Workflow

A GitHub Actions job that picks certification textbook ranges, drafts SEO/GEO blog posts, checks the Markdown, adds covers, and only publishes when git sees a real diff.

Role
Workflow designer + builder
Status
Live
Started
2026-06
URL
gifted-academics.com/blog ↗
Source
Private · GitHub Action + live blog + generated post metadata
// hiring signal
What this proves

I can put AI generation inside a job you can inspect later, with source ranges, rejected candidates, validation output, and a git diff.

Risk handled

Automated content can drift, repeat an old topic, make unsupported claims, publish bad frontmatter, or run twice for the same week.

Evidence

Weekly slot state, cert rotation, table of contents (TOC) screening, source coverage checks, novelty judge, Markdown retry, automation.json, and commit-on-diff.

01

Trigger

  1. 01
    Cron route

    Bearer CRON_SECRET protects the Vercel endpoint.

  2. 02
    Dispatch

    GitHub API starts blog-post-auto.yml with a Monday UTC slot.

02

Generate

  1. 01
    Rotate cert

    State picks TSAC-F, CSCS, NASM CPT, or CISSN.

  2. 02
    Screen topic

    Table of contents (TOC) ranges, slug tokens, and source overlap reject repeats.

  3. 03
    Draft

    OpenRouter writes from source text, style guide, template, and schema.

03

Publish

  1. 01
    Validate

    Frontmatter, links, sections, definitions, and word count must pass.

  2. 02
    Cover

    Pexels image and attribution attach when a match is found.

  3. 03
    Commit

    Commits content/blog-posts only when git sees a diff.

// live screens

Blog index and generated post.

Generated post detail: the frontmatter, cover image, metadata, and Markdown all made it through the publish path.

A weekly blog generator for Gifted Academics. It picks a certification topic, pulls bounded source material from the matching textbook, drafts a Markdown post through OpenRouter, validates the result, adds a cover image, writes a trace file, and lets GitHub publish the diff.

Why it exists

The business needed regular cert-prep articles without making every post a manual project. The point was not volume for its own sake. It was SEO content that stayed tied to real exam categories, course pages, and textbook-backed topics.

I built around that constraint. The system chooses from the course source material first, then asks the model to write inside a tight template. The publish path is a git diff, not a hidden CMS mutation.

What made it hard

Blog automation fails quietly if the only check is "did the model return text?" A generated article can repeat an old topic, make unsupported claims, miss the course CTA, ship broken frontmatter, or run twice for the same week.

So the prompt sits in the middle, not at the beginning. Before drafting, the generator resolves a Monday UTC slot, checks state, rotates the certification category, selects a table of contents (TOC) range, extracts enough prose, and screens for duplicate source ranges. After drafting, it validates the Markdown and retries once with concrete feedback before writing files.

What the workflow leaves behind

Each generated post gets an automation.json file: slot, cert, selected slug, source file, textbook line ranges, rejected candidate titles, model, novelty judgment, workflow run URL, and cover metadata. That file makes the article easier to inspect after the fact.

The latest generated post on the live blog came from the June 22, 2026 slot. Its trace recorded a CISSN source range, novelty confidence of 0.88, the OpenRouter model used for drafting, and the Pexels photo attribution that appears in the rendered article.

// pragmatic decisions

Three trade-offs worth naming.

The short version: choice, reason, cost.

01

GitHub Actions is the publisher

Chose

GitHub Actions checks out main, installs the app, runs the generator, commits content/blog-posts when there is a diff, and pushes back to main.

Why

The blog is file-backed content. Publishing through git leaves a run URL, a reviewable diff, and a history you can roll back.

Cost

The action needs write permissions and production secrets. A bad generator change can publish directly unless the gates catch it first.

02

Pick the topic before asking the model

Chose

The generator rotates through NSCA TSAC-F, NSCA CSCS, NASM CPT, and CISSN. It reads cached textbook table of contents (TOC) files, picks usable line ranges, and rejects weak or duplicate candidates before the model sees anything.

Why

The model should write from bounded source material, not invent the content calendar. Candidate selection is deterministic so a run is explainable after the fact.

Cost

The output is only as good as the table-of-contents cache and the line-range heuristics. Messy OCR headings still need rejection rules.

03

Validate and trace the post, not just the prompt

Chose

Each draft has to pass frontmatter, slug, section, word-count, link, and definition checks. The run also writes automation.json with the source ranges, model, novelty judgment, cover metadata, and rejected candidates.

Why

A generated post has to render cleanly, and I need to be able to audit it later. The trace file explains why that post exists and which source material supported it.

Cost

Validation adds one more repair loop and can turn a run into a no-op. I would rather skip a week than publish a near-duplicate or a broken post.

// the stack

The stack.

Up next
Shipit News — AI / coding news aggregator