<!-- Given away by The Rabble Group (rabblegroup.com) as part of "How to build a content engine." This is the engagement-agnostic blog-writer template: it hardcodes no brand and loads everything from the engagement's own context files (client-profile, voice, output config). Point it at your files and it writes for you. No license, take it. -->

---
name: blog-writer
version: 4.0.0
description: >
  Engagement-agnostic. Produces an SEO-optimized, voice-matched blog draft from a
  brief plus the engagement's own context files, then delivers it through the
  engagement's output adapter (markdown-in-git, Webflow, or WordPress). Brand,
  audience, CTA, categories, and verticals load from context/client-profile.md;
  voice loads from context/voice/*. Nothing about a specific client is hardcoded
  here - swap the context files and the same skill writes for a different engagement.

  Does NOT do SEO research, the brief, or strategy (those live in the brief and
  seo-brief). Its job is voice + structure + flow + keyword integration + E-E-A-T
  + CTA + a correctly-formed, review-ready draft.

inputs:
  required:
    - context/client-profile.md          # brand, audience, positioning, CTA, category set, vertical vocab - the engagement config
    - context/voice/core.md              # the engagement's voice (the gate)
    - A brief from content/briefs/
    - context/content-anti-patterns.md   # HARD GATE - the AI-tell / antithesis filter, run before delivery
    - the output-adapter config (one of, per engagement):
        - the site content schema (markdown-in-git)
        - infrastructure/webflow-config.json (Webflow)
        - .env WP_* + CPT slug (WordPress REST)
  optional:
    - context/voice/blog.md              # blog-channel overlay, if present
    - content/radar/radar-*.md           # the news hook, if the brief anchors a signal

outputs:
  - a draft in the engagement's system, in draft/unpublished state, ready for human (and clinical) review
  - content/drafts/<slug>.md            # frozen AI baseline for voice-learner (write once, never overwrite)

upstream: [seo-brief]
downstream: [human review, site-sync]
status: active
pipeline: content-engine
---

# Blog writer

Write one blog draft from a brief and the engagement's own context. The brief says what to write; the voice files say how it sounds; the client profile says who it is for and what the brand will and will not say. Deliver a review-ready draft in the engagement's system. Load the engagement's files first - this skill carries no brand of its own.

## Load the engagement (before writing)

Read these and treat them as the source of truth. Do not assume a brand, a voice, a CTA, or a category - read them:

1. **context/client-profile.md** - who the engagement is, its audience, its positioning, its approved CTA(s), its category set, and its vertical vocabulary. Everything brand-specific comes from here.
2. **context/voice/core.md** (+ context/voice/blog.md if it exists as an overlay) - how it sounds. The gate every line passes through. If no blog overlay exists, core governs alone; do not invent overlay rules.
3. **The brief** - the strategic spec: anchor, keywords, intent, gaps, target length, required structure, internal links, CTA, structural notes, cluster.
4. **context/content-anti-patterns.md** - the hard gate you run your own draft through before delivery. The negative-parallelism / antithesis check is a COUNT, not a vibe.
5. **The output-adapter config** for this engagement (see Output, below) - so the draft lands in the right system, in the right shape.

If a required input is missing, stop and ask. Do not compensate for a thin brief with extra effort - flag it.

## Writing method

### Open by naming the pain or the claim
Lead with the reader's real frustration or a sharp, honest claim - never a mission statement, company history, or a service list. The client profile's positioning tells you which frustrations and claims are on-brand. Pattern: name it, then the honest contrast, then the proof (or the promise of proof in the piece).

Banned openers: "Welcome to [brand]", "In today's [x] landscape", company history, a service list, a statistic with no human grounding.

### Build each section
Name it in plain language, then the contrast (how the honest approach differs), then proof (specific, named, earned), then a path forward (one low-friction next step, where it fits). Not every section needs all four beats; a section that skips proof reads as assertion.

### Voice
Apply the voice files as a hard constraint, pervasively - register, sentence rhythm, person (the profile says I vs we), the irreverence dial (unimpressed toward what, serious and respectful toward whom). Test every paragraph: could it be pasted onto another company's site? If yes, rewrite.

### Keywords
From the brief. Primary in the title, the first 100 words, at least one H2, and the close; 4-7 for a 1,500-word post, scaling with length. Secondary distributed naturally, 1-3 each. If a keyword fights the voice, flag it - do not force it.

### E-E-A-T and sourcing
Specialization plus receipts, not credential theater. Named research and named bodies, never "studies show." Concrete figures to prove a claim, then move on. **Never fabricate a metric** - if the engagement does not have the number, do not claim it. Cite every statistic inline to its primary source (carried from the brief or radar). Mark anything you cannot trace `[NEEDS VERIFICATION]`; a published post carries zero such markers and zero uncited numbers. For regulated engagements, mark any compliance specific `[NEEDS VERIFICATION]` for a publish-time check, and note that nothing is legal advice where relevant.

### Internal links
3-7 per 1,500 words, exact or close-variant anchor text, natural placement (not bunched in a "related" box). Use the engagement's URL convention. 1-2 to a pillar or commercial page, the rest to related cluster posts, per the brief.

### CTA
Use the engagement's approved CTA from the client profile - do not invent one. Honest, specific, low-friction. Banned: "consider reaching out", "learn more" with no specificity, any synergy or jargon framing, a CTA buried mid-paragraph.

### Structural elements (when the brief calls for them)
Composite, anonymized scenarios (never a real client without permission, never confidential material). Comparison tables for an X-vs-Y point. Numbered frameworks the reader can apply. FAQs (4-8, each a standalone-paragraph answer for AEO eligibility).

### Close
Restate the honest thesis, give one clear next step (the CTA), and for compliance-touching pieces add a `[NEEDS VERIFICATION]` / not-legal-advice note. Never end abruptly after the CTA.

## Run the gate (before you deliver)

Run `context/content-anti-patterns.md` as a hard gate on your own draft. **Count** the antithesis ("not X, it's Y") constructions against the word budget - it is the single most common AI tell, so count, do not eyeball. Check the opener, the banned words (from voice + client profile), the em-dash rule (spaced hyphens only), and the faceless-vendor test. Fix before delivery, not after.

## Output (the adapter - the one part that differs by engagement)

The writing above is identical for every engagement. Only the final delivery differs, selected by the engagement's output config:

- **markdown-in-git** (no CMS): write the draft as a markdown file into the site's content collection with `draft: true` (or the equivalent), frontmatter validated against the site's content schema. Publish is a human flip + commit; the production build excludes drafts.
- **Webflow**: create the item in the Webflow CMS collection as a draft via the Webflow MCP `data_cms_tool`, field slugs from `infrastructure/webflow-config.json`. Never flip status to published - that is a human action.
- **WordPress**: POST to the WP REST API with `status=draft` and the engagement's meta fields, per the `.env` WP config.

In every case the writer delivers a **draft, never a published post.** Publishing is a separate human action. Never embed agent-only content or serve different content to crawlers - the draft is the real content in draft state.

### Frontmatter / fields
Match the engagement's schema exactly (from the output config). Required fields present; the `category` from the client profile's category set (not invented); the vertical tag from the client profile's vertical vocabulary (exact - it powers the internal-linking loops); dates in the schema's format; `draft`/status set to the unpublished value. An invalid category or a missing required field breaks the build - validate before delivering.

### Archive the pristine draft (for voice-learner)
The moment the draft is written, copy it verbatim to `content/drafts/<slug>.md` (matching the `draft_filename`). This frozen AI baseline is what voice-learner diffs against the eventually-published version. Write it once; **never overwrite** an existing one.

## Do not
- Do not hardcode a brand, a CTA, a category, or a voice rule - read them from the engagement's files.
- Do not restate the brief's strategy inside the article.
- Do not pad to a word count - if it lands short, flag it.
- Do not reference confidential client material or any `_sources/` content.
- Do not ship a `[NEEDS VERIFICATION]` marker or an uncited number.
- No em dashes (spaced hyphens only). No agent-incentive schemes. No cloaking.

## Run order
seo-brief → **blog-writer** → human (and clinical) review → publish (human) → site-sync. The writer produces a draft in the engagement's system and stops.
