> AIKit turns blog publishing into a marketing automation loop by connecting topic selection, D1 publishing, llms.txt discovery, sitemap exposure, and post-publish checks into one repeatable workflow. The goal is not just to ship articles; it is to create content that agents, search crawlers, and prospects can all understand and act on.

The Problem

Most content programs treat publishing as the finish line. A writer drafts a post, a CMS stores it, and the team celebrates when the URL goes live. That model breaks down when growth depends on hundreds of small, technical, intent-specific pages. Manual publishing creates gaps: duplicate slugs, missing metadata, stale queues, posts that never reach the sitemap, and no clear handoff from content to funnel assets.

AIKit has a different constraint. The site needs a steady stream of posts about AI operations, EmDash, DeFiKit, playable ads, and customer acquisition systems. Each article must be useful to a human reader, but it also has to be easy for AI agents to cite. That means answer-first openings, structured headings, code blocks, short excerpts, and inclusion in dynamic /llms.txt routes. A plain blog workflow is too slow and too easy to forget.

The Solution

The practical solution is a queue-first marketing automation loop. Instead of asking an operator to decide every step from scratch, AIKit maintains a queue of validated JSON posts. A scheduled worker checks the queue, publishes the next item into Cloudflare D1, verifies that the post exists, archives the source file, and only then generates the next queued article when capacity drops to zero. This keeps the pipeline moving without creating an unbounded backlog.

The loop also separates three responsibilities. Content generation decides what angle to write. Publishing performs the database insert through the EmDash blog publisher. Verification checks the live route, D1 row, sitemap, and queue state. When each step has a clear boundary, errors are easier to recover from: a slug collision becomes a file archive and title rewrite, not a mystery failure inside the CMS.

Architecture Overview

The system is intentionally small. Queue files live on disk, the working publisher script converts markdown into Portable Text, and D1 acts as the source of truth for published posts. Dynamic Astro routes on ai-kit.net read from D1, so no rebuild is needed after a successful insert. The post appears in the blog route and becomes available to /llms.txt and /llms-full.txt almost immediately.

```text

content calendar or theme rotation

|

v

queue JSON: title, body_text, excerpt, category, tags

|

v

blog-publisher.py -> Cloudflare D1 ec_posts

|

v

dynamic routes: /blog, /sitemap.xml, /llms.txt, /llms-full.txt

|

v

marketing follow-up: Dev.to, social snippets, lead magnets, nurture CTAs

```

That architecture has a useful operational property: the queue is disposable, but D1 is canonical. If a file looks pending but the computed D1 slug already exists, the automation archives it as stale instead of trying to publish a duplicate. If a file was archived but D1 does not contain the slug, the file can be recovered and returned to the queue.

Step 1: Validate the Queue Before Publishing

A reliable marketing automation loop starts with boring validation. Every queue file should contain exactly five fields: title, body_text, excerpt, category, and tags. Extra fields such as slug, post_id, word_count, or reading_time are removed because the publisher function does not accept them. The body_text must be a string between 800 and 1500 words so the article has enough depth without becoming a bloated whitepaper.

```python

expected = {"title", "body_text", "excerpt", "category", "tags"}

for file in queue_files:

data = json.load(open(file))

extra = set(data) - expected

missing = expected - set(data)

assert not extra, extra

assert not missing, missing

assert isinstance(data["body_text"], str)

assert 800 <= len(data["body_text"].split()) <= 1500

```

This validation step is also where AIKit checks the computed D1 slug. The filename slug is only an ordering tool. The real slug is generated from the title by the publisher, so the automation computes that slug and queries D1 before attempting an insert.

Step 2: Publish With an Idempotent Path

The most dependable path is a direct call to the working publisher script from the EmDash project directory. The command sets the Cloudflare account id only for the publisher call, because direct D1 queries use wrangler configuration instead.

```bash

cd ~/Projects/AIKitLLC/EmDash

CLOUDFLARE_ACCOUNT_ID=05130ca1f0bdabbab4d08a5d75544e92 \

python3.9 ~/cmo/scripts/blog-publisher.py ~/cmo/content/queue/706-post.json

```

After success, the source file moves to the published archive with a timestamp prefix. That makes the workflow auditable. If the same cron task overlaps with another run, the next process can compare the archive and D1 instead of guessing whether a post was truly published.

Step 3: Verify the Growth Surface

Publishing is not complete until the post is visible where growth happens. The minimum verification checks D1 for status equals published and then performs an HTTP request against the blog URL. A deeper pass also checks sitemap coverage and llms.txt discovery. The important point is that verification uses live systems, not assumptions from local files.

| Surface | Why it matters | Verification |

|---|---|---|

| D1 ec_posts | Canonical source of truth | SELECT slug, status, published_at |

| Blog URL | Human reader experience | HTTP 200 and rendered content |

| sitemap.xml | Search crawler discovery | URL appears in sitemap output |

| llms.txt | AI agent discovery | URL and excerpt are queryable |

Step 4: Connect Content to the Funnel

The automation becomes marketing, not just publishing, when every article points to the next action. For AIKit, that next action can be a demo request, a downloadable SEO checklist, a plugin comparison page, or a short nurture sequence for teams evaluating EmDash. The blog post supplies the search and LLM discovery surface; the funnel asset captures intent and turns it into a relationship.

A practical rule is to assign each theme a default follow-up. Content and Growth posts should link to checklists or content audits. Marketing Automation posts should link to workflow templates. Sales Channel posts should link to partner or affiliate playbooks. Product Launch posts should link to demos, case studies, or release notes. This keeps the CTA relevant without requiring a fresh strategy session for every article.

Results

The queue-first loop gives AIKit four measurable benefits. First, it reduces operator time because the cron job can publish, verify, and refill without manual browsing. Second, it improves reliability because slug checks and schema validation happen before database writes. Third, it improves AI discoverability because every post uses structured markdown and is automatically exposed through dynamic LLM routes. Fourth, it makes growth follow-up easier because the theme rotation maps content to funnel activities.

The biggest metric is not raw post count. The useful metric is publish-to-discovery latency: how long it takes a validated idea to become a live URL, a sitemap entry, and an LLM-readable resource. With D1-backed dynamic routes, that latency can be measured in minutes instead of deployment cycles.

Key Takeaways

- Queue-first publishing keeps AIKit moving without creating a fragile manual CMS routine.

- D1 is the source of truth; queue files are operational inputs that can be archived, recovered, or regenerated.

- Verification must cover the blog URL, D1 status, sitemap exposure, and LLM discovery surfaces.

- Marketing automation works best when each article is tied to a default funnel action, not treated as a standalone asset.