# Blog Post Template

> Use when writing or auditing any blog post on your product's site (new post or existing). Enforces the BlogPostLayout container contract AND a Tier-1/Tier-2 anti-AI-slop checklist derived from 15-source Exa research (2026-05-14). Both checks are mandatory before any blog post merges.

- Skill: `oimiragieo/blog-post-template` (Agent Skill)
- Install (CLI): `npx skillmds@latest add oimiragieo/blog-post-template`
- Raw SKILL.md: https://api.skillmd.com/api/skills/oimiragieo/blog-post-template/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Research & Search
- Author: oimiragieo (https://skillmd.com/u/oimiragieo)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/oimiragieo/blog-post-template

---


# blog-post-template

Canonical contract for every blog post on your product's site. Two enforcement layers:

1. **Container contract** — every post must use `BlogPostLayout` (or render an equivalent surface). Locks the brutal-audit fixes from /blog/compression-benchmark-methodology so we never re-ship the "prototype-grade container, enterprise-grade content" mismatch.
2. **Anti-AI-slop rubric** — Tier-1 + Tier-2 lexical bans, structural pattern bans, regex enforcement. Derived from a 15-source Exa research synthesis on AI writing tells (slop-forensics, EQ-Bench, Colin Gorrie's rhetorical analysis, fly.io / patio11 / stripe / supabase voice references).

If you are about to write, edit, or audit a blog post: load this skill first. Both checks fail closed — a missing TOC OR a Tier-1 word will block the merge.

---

## Where the new post lives — pick the route first

Two routes exist. Default to the dynamic-markdown route unless the post needs bespoke React.

### Route A — dynamic markdown (default, ~95% of posts)

Use for any prose-heavy post: news, research summaries, cost-analysis explainers, companion posts, tutorials with code samples. Content is a markdown template-literal string the existing pipeline renders.

**Files to touch:**

1. `<web-app>/src/app/[locale]/(unauth)/blog/page-posts.ts` — add a `POSTS` entry with this shape:
   ```ts
   {
     slug: 'kebab-case-slug',                    // also used as URL segment
     href: '/blog/kebab-case-slug',
     title: 'Concrete, specific headline',       // no clickbait, no "Ultimate Guide"
     excerpt: 'One or two sentences. Becomes the auto-generated TL;DR.',
     date: 'YYYY-MM-DD',                         // ISO; drives sort + cite block
     readTime: 'N min',
     category: 'Engineering' | 'Research' | 'Cost' | 'Technical' | 'Guide',
     authorSlug: 'the operator',          // must exist in blog-data.ts AUTHORS
     internal: true,
     // optional: updatedAt: 'YYYY-MM-DD'
     // updatedAt discipline (2026-05-20): bump ONLY on substantive content
     // changes — new sections, revised benchmark numbers, changed product
     // claims, added/removed steps. Do NOT bump for typo fixes, image swaps,
     // rephrased sentences without factual change, copyright-year updates.
     // Superficial bumps are detected by AI search systems and penalized
     // (`dateModified` without meaningful delta drops AI Overview citation
     // odds). When bumping, add an inline note in the body:
     // "Updated YYYY-MM-DD: [what specifically changed]."
     // Source: https://www.quattr.com/blog/content-freshness (2026)
   }
   ```

   **Byline rendering rules (anonymous-byline directive, 2026-05-20).**
   The `authorSlug` field controls attribution, but the rendered byline must
   observe the anonymous-byline directive — no personal identity:
   - **Permitted**: `By the operator · 6 min read · 2026-05-20` (contributor name only)
   - **Permitted**: `By the <your product> team · 6 min read · 2026-05-20`
   - **Prohibited**: any ownership / role title attached to the name —
     `Founder`, `operator`, `Owner`, `Creator`, `Built by`, `Maker of`, `Founder of <your product>`
   - **Prohibited**: personal photos, personal profile URLs, and any
     employer/day-job references in the author block
   - **Prose voice**: "we" throughout the body, even on personally-bylined posts
   - **E-E-A-T substitute**: link to named data + commits + CHANGELOG + /trust page; never personal credentials
   - **Rule**: use an anonymous/brand byline — no personal identity. An
     anonymous or product-team byline reads as institutional and lets the
     product's own results carry the credibility instead of a named individual.

2. `<web-app>/src/app/[locale]/(unauth)/blog/[slug]/page.tsx` — add a `BLOG_CONTENT[slug]` entry:
   ```ts
   'kebab-case-slug': {
     title: '…same as POSTS…',
     date: '…',
     readTime: '…',
     category: '…',
     content: `
   ## First H2 becomes a TOC entry (auto-generated)

   Body markdown. Use \\\`backticked code\\\` for inline. Triple-backtick fences for blocks.
   Tables, lists, links, headings — all supported by the existing sanitized HTML pipeline.
   `,
   },
   ```

**What you get for free** (BlogPostLayout populates these from POSTS metadata — no per-post wiring):
- Auto-generated TOC from every `## H2` heading
- TL;DR block built from `excerpt`
- Cite this block (BibTeX + APA) built from `title` + `authorSlug` + `date`
- Related-links sidebar (4 standard links)
- Contribute CTA grid (3 cards: Suggest edit / Discuss / Email)
- Linked author + word count + scroll-spy nav + anchor-linked H2s

**What you do NOT get on this route:**
- `<CopyableCode>` buttons on code fences (deferred — needs MDX or client post-processor)
- Inline React components / charts / interactive widgets
- Custom `statusCallout` / `revisions` log / `extraChips` (optional props exist but the dynamic route doesn't wire them by default; add per-post if needed)

### Route B — standalone TSX (bespoke posts only)

Use ONLY when the post needs one or more of:
- Interactive widgets or charts that mount in the body
- Per-post `statusCallout` banner (e.g. `v0 — methodology only`)
- A dated `revisions` log table
- `<CopyableCode>` blocks with `sourceHref` pinned to a commit SHA
- Custom data viz, embedded form, or any other React-only behavior

**Reference implementation:** `<web-app>/src/app/[locale]/(unauth)/blog/compression-benchmark-methodology/page.tsx` — copy its scaffolding. Wraps `BlogPostLayout` directly and passes every prop explicitly. Still needs a corresponding `POSTS` entry so it appears in the index, RSS, and `/llms.txt`.

### Decision flow

```
Does the post need interactive React, a status callout, a revisions log,
or commit-pinned <CopyableCode>?
  └── no  → Route A (dynamic markdown). Add POSTS entry + BLOG_CONTENT entry. Done.
  └── yes → Route B (standalone TSX). Copy compression-benchmark-methodology
            scaffolding. Add POSTS entry. Wire BlogPostLayout props explicitly.
```

---

## Container contract — every post uses `BlogPostLayout`

Component: `<web-app>/src/components/blog/BlogPostLayout.tsx`. Used by:
- `<web-app>/src/app/[locale]/(unauth)/blog/[slug]/page.tsx` (dynamic markdown route)
- `<web-app>/src/app/[locale]/(unauth)/blog/compression-benchmark-methodology/page.tsx` (standalone TSX posts — pattern to copy for any future bespoke post)

Required props every post must populate:

| Prop | Required? | Source |
|---|---|---|
| `post` | yes | `POSTS` entry from `<web-app>/src/app/[locale]/(unauth)/blog/page-posts.ts` |
| `author` | yes | `authorForPost(post)` from `blog-data.ts` |
| `dek` | yes | 2-line summary under the title |
| `tocEntries` | yes if 3+ `<h2>` sections | auto-derived from markdown via `extractTocFromMarkdown` in the dynamic route; explicit array for TSX posts |
| `wordCount` | yes | `~N words` — `wordCountFromMarkdown(content)` or hand-counted for TSX |
| `version` | yes for methodology / benchmark posts | e.g. `"Methodology v0.3"` |
| `statusCallout` | yes if the post has a v0 / "deprecated" / "partial" / "draft" status | amber `<aside>` above the TL;DR; never bury status disclosures mid-body |
| `tldr` | yes for posts > 800 words | 3-5 bullet block (What / How / Status / Reproduce or post-appropriate variant) |
| `relatedLinks` | yes if 2+ related artifacts exist | links to leaderboard, changelog, repo, etc. |
| `revisions` | yes if post is versioned | dated table — satisfies the post's own "methodology changes require a new dated entry" commitment |
| `cite` | yes for research/methodology posts | BibTeX + APA |
| `contributeCards` | yes for research/methodology posts | 3 cards — Submit result / Propose mitigation / Report bug — linked to prefilled GitHub issues |

For posts that include reproducibility commands, verbatim prompts, or any code:
- Use `<CopyableCode>` from `<web-app>/src/components/blog/CopyableCode.tsx`
- Pass `caption`, `language`, and (for source-of-truth content) `sourceHref` + `sourceLabel`
- `"verbatim"` is a claim — every code block tagged "verbatim" MUST carry a `sourceHref` to a commit-pinned GitHub URL

For every `<h2>` in a standalone TSX post:
- Add `id` derived via the same `slugifyHeading` logic the dynamic route uses (`<web-app>/src/app/[locale]/(unauth)/blog/[slug]/page.tsx`)
- Add `scroll-mt-24` so anchor jumps clear the sticky nav
- Add the `group` class + the hover-only `¶` anchor link

---

## Anti-AI-slop rubric (mandatory before merge)

Derived from a 15-source Exa research synthesis on AI writing tells. Failing any Tier-1 check blocks the merge. Tier-2 is a cluster check — 3+ in 200 words = rewrite.

### Tier 1 — kill on sight (regex below)

Every match is a sentence-level rewrite. No exceptions.

**Lexical bans:**

| Word/phrase | Replace with |
|---|---|
| `delve` (any form) | dig into, examine, look at |
| `utilize` (any form) | use |
| `leverage` (verb) | use, take advantage of, apply |
| `facilitate` (any form) | help, enable, let |
| `elucidate` | explain, clarify |
| `embark` | start, begin |
| `endeavor` / `endeavour` | effort, try, attempt |
| `multifaceted` | complex, varied (or describe the parts) |
| `tapestry` (metaphorical) | describe the actual thing |
| `a testament to` | shows, proves, demonstrates |
| `paradigm` | model, approach, framework |
| `synergy` / `synergize` | rewrite the sentence |
| `holistic` | whole, full, end-to-end |
| `catalyze` / `catalyst` | trigger, spark, cause |
| `realm` (metaphorical) | area, field, domain |
| `landscape` (metaphorical) | field, space, situation |
| `myriad` | many, lots of |
| `plethora` | many, a lot |
| `game-changer` / `game-changing` | describe what specifically changed |
| `ever-evolving` | changing, growing, shifting |

**Punctuation tells:**

- **Em-dash overuse**: 2 max per page. Every instance must pass the four-alternative test (could this be a period / comma / colon / parentheses instead?). Code blocks exempt. The em-dash is not banned outright — it's a habit-marker. Habit overuse is what flags AI authorship.
- **Tricolon compulsion**: 1 max per 500 words. "Fast, reliable, and affordable" pattern. If you find one, decide whether it earns its place; break others into prose.
- **"Not just X, but Y" construction**: zero tolerance. The single most overrepresented AI trigram per slop-forensics analysis. Restructure entirely.
- **"It's not about X, it's about Y"**: zero tolerance. Same family.

**Filler phrases (delete every instance, no exceptions):**

- `It's worth noting that…` / `It's important to note that…`
- `Notably,` / `Interestingly,` (as paragraph openers)
- `Let's dive into…` / `Let's explore…`
- `In conclusion,` / `To summarize,`
- `Furthermore,` / `Moreover,` / `Additionally,` (as transitions — replace with `and` or a new sentence)
- `In today's fast-paced/digital/modern world…`
- `At the end of the day…`
- `It goes without saying…`
- `When it comes to…`
- `In the realm of…`
- `Cannot be overstated`
- `We are excited/thrilled/pleased to announce`

### Tier 2 — cluster check (3+ in 200 words = rewrite section)

Fine individually, fatal in density:

`robust`, `comprehensive`, `seamless`/`seamlessly`, `cutting-edge`, `innovative`, `streamline`, `empower`, `foster`, `enhance`, `elevate`, `optimize` (metaphorical), `scalable` (as buzzword), `pivotal`, `intricate`, `profound`, `resonate`, `underscore`, `harness`, `navigate` (metaphorical), `cultivate`, `bolster`, `galvanize`, `cornerstone`

### Structural patterns to fix manually (no regex catches these)

1. **Cadence uniformity** — read the first three paragraphs aloud. If every sentence resolves with the same weight and timing, the rhythm is AI. Mix sentence lengths deliberately. Short sentences. Then a longer one that builds through several clauses before arriving at a point.

2. **Topic-sentence machine** — every paragraph in shape: topic → elaboration → example → wrap-up. Vary the shape. Sometimes the point comes last. Sometimes a paragraph is one sentence.

3. **Symmetry addiction** — three pros + three cons, exactly five steps, equal-weight bullet items. Real topics are lumpy. Some sections are long because the material demands it; others are two sentences.

4. **Transition-word paragraph openers** — read the first word of every paragraph. If more than 2 consecutive start with `However` / `Furthermore` / `Additionally` / `Moreover`, rewrite. Human writers start paragraphs with the subject of the paragraph.

5. **Hedge parade** — `may potentially help in some cases` is slop. State what you know. If you don't know, say "we haven't measured this yet."

6. **List abuse** — bullets where prose is cleaner; every item starting with the same grammatical structure (`Ensures…`, `Provides…`, `Enables…`); exactly 3 or 5 items; nesting 3+ levels deep.

7. **Boilerplate closer** — `As AI continues to evolve, one thing is clear:` Cut every closer of this shape. Real conclusions take a position and stop.

8. **Opening paragraph contract** — The first three sentences must answer two questions: (1) "Is this article for someone like me?" and (2) "How will I benefit from reading it?" If paragraph two begins and neither question is answered, rewrite the opener. `In today's rapidly evolving digital landscape…` and all variants auto-fail. The fly.io blog opens with the actual claim. So does every post that reaches HN front page. This is not a style preference — it is the reader's threshold for not closing the tab.

   **Title pattern**: use a benefit statement (`A Simple Way to Write X`) or an intriguing hook (`How We Lost $2K to a Caching Bug`) — not a category label (`A Guide to Compression APIs`). "Ultimate Guide" titles auto-fail. Source: https://refactoringenglish.com/blog/2025-hn-top-5/

### Humanizing techniques — DO this

- **Vary sentence length.** Short sentences hit hard. Then a longer one carries the reader through several turns of thought before landing.
- **State opinions directly.** Engineering writing has positions. "This is the wrong approach." "We got this wrong for two years." "Most ORMs make this harder, not easier."
- **Use specific numbers, dates, named technologies.** `23% p95 reduction` not `significant performance improvement`. `shipped in March` not `recently released`.
- **Include failure and messiness.** What broke. What surprised you. What the wrong first approach was. What took longer than expected.
- **Write in first person with a real persona.** `I` and `we`. Identifiable author with identifiable opinions.
- **One surprising sentence per major section.** If no section surprises, the draft is too smooth.
- **Quotable line per post.** Write one sentence per post that could be screenshotted and shared with zero context. Without this, the post can be read but cannot be quoted. The HN-front-page pattern (Simon Willison, Sean Goedecke 2025 analysis) confirms a single quotable, contrarian, or counter-intuitive sentence drives share rates.

---

## Citation discipline — inline at claim level

AI Overview selection rate: **34.9% for paragraphs with specific verifiable inline links** vs. **3.2% for vague or footnoted references** (Wellows 2026 analysis of 1,000+ AI Overviews — https://wellows.com/blog/google-ai-overviews-ranking-factors/).

Rules:

- **Place the hyperlink on the specific claim**, not in a "Sources" section at the end. `"A September 2025 Seer Interactive study found CTR dropped 61% [link]"`, not a numbered footnote.
- **Paraphrase + inline link beats blockquote.** Direct quotation > 2 sentences requires explicit attribution and creates copyright surface area. Paraphrase + link is always safer for both E-E-A-T and DMCA posture.
- **Each claim-bearing paragraph should be self-contained.** AI extraction prefers passages that stand alone — no `as mentioned above`, no `see section X`, no "this builds on the previous point." A passage that requires context above it is invisible to AI Overview extraction.
- **134–167 words is the AI-Overview-extraction sweet spot** for self-contained paragraphs (Wellows 2026). Over 200 words and the passage exceeds the extraction window; under 80 and there's not enough context to stand alone.

---

## Anchor reference blogs (read these for voice)

When in doubt about voice, read a recent post from one of these:

1. **https://fly.io/blog/** — named authors, specific technical details, first-person voice with opinions, admitted failures, irregular sentence rhythm. Zero tricolons or "not just X but Y" constructions.
2. **https://kalzumeus.com / https://bitsaboutmoney.com** (patio11) — deep domain specificity, zero hedging, every sentence contains information. No filler. No symmetry.
3. **https://stripe.com/blog/engineering** — "We migrated 3.7 million lines of code in a single pull request" — specific numbers, named technologies, actual decision reasoning.
4. **https://supabase.com/blog** — technical depth, honest trade-off discussion, named engineers.

---

## Regex enforcement (run as pre-publish linter)

Paste this into the post text and grep. Every Tier-1 match must be addressed before merge.

```
# Punctuation tells
/—/g                          flag every em-dash in body prose (code blocks exempt)

# Tier 1 — kill on sight
/\b(delve|delves|delved|delving)\b/i
/\b(utilize|utilizes|utilized|utilizing|utilization)\b/i
/\b(leverage|leverages|leveraged|leveraging)\b/i
/\b(facilitate|facilitates|facilitated|facilitating)\b/i
/\b(elucidate|elucidates|elucidated|elucidating)\b/i
/\b(embark|embarks|embarked|embarking)\b/i
/\b(endeavor|endeavour|endeavors|endeavours)\b/i
/\b(multifaceted)\b/i
/\btapestry\b/i
/\ba testament to\b/i
/\b(paradigm|paradigms)\b/i
/\b(synergy|synergize|synergistic)\b/i
/\b(holistic)\b/i
/\b(catalyze|catalyzes|catalyzed|catalyst)\b/i
/\b(realm|realms)\b/i
/\b(myriad)\b/i
/\b(plethora)\b/i
/\b(game.?changer|game.?changing)\b/i
/\b(ever-evolving|ever evolving)\b/i

# Structural — zero tolerance
/\bit'?s not just\b.*\bbut\b/i
/\bit'?s not about\b.*\bit'?s about\b/i
/\bthe question isn'?t\b.*\bthe (?:real )?question is\b/i

# Filler openers — delete
/\bit'?s worth noting that\b/i
/\bit'?s important to note that\b/i
/^notably[,\s]/im
/^interestingly[,\s]/im
/\blet'?s (?:dive|delve) into\b/i
/^in conclusion[,\s]/im
/^to summarize[,\s]/im
/^furthermore[,\s]/im
/^moreover[,\s]/im
/^additionally[,\s]/im
/\bin today'?s (?:fast.paced|digital|modern|rapidly evolving)\b/i
/\bat the end of the day\b/i
/\bit goes without saying\b/i
/\bwhen it comes to\b/i
/\bin the realm of\b/i
/\bcannot be overstated\b/i
/\bwe are (?:excited|thrilled|pleased) to (?:announce|share|introduce)\b/i

# Tier 2 — cluster check (flag section if 3+ in 200 words)
/\b(robust|comprehensive|seamless|seamlessly|cutting.edge|innovative|innovation|streamline|empower|foster|enhance|elevate|optimize|scalable|pivotal|intricate|profound|resonate|underscore|harness|navigate|cultivate|bolster|galvanize|cornerstone)\b/i
```

Practical workflow:

1. **First pass** — write the post the way you'd write it for a coworker. Don't self-edit toward "professional".
2. **Pre-publish lint** — run the regex sweep. Address every Tier-1 hit.
3. **Cluster check** — grep Tier-2 patterns. If any section has 3+ in 200 words, rewrite it.
4. **Read aloud** — open the draft and read the first three paragraphs aloud. If you can't hear rhythm variance, mix sentence lengths.
5. **Surprise audit** — does at least one sentence per major section surprise? If not, add concrete specifics or admit a failure.
6. **First-word audit** — read the first word of every paragraph. If you see transition words in sequence, rewrite the openings.
7. **Em-dash audit** — count em-dashes. If more than 2 per page, each one must pass the four-alternative test.
8. **Specificity audit** — find every vague claim. Replace with a number, a date, or a named technology.

---

## What this skill does NOT do

- Markdown→MDX migration for the dynamic route (deferred; current sanitized HTML pipeline is shipping)
- Code-block copy buttons for the dynamic route (deferred; needs MDX or a client-side post-processor — standalone TSX posts get `CopyableCode` today)
- Inline figure / chart generation on Route A (Route B only)
- "Print as PDF" view

Historical: bulk em-dash + Tier-1 sweep across all 26 dynamic posts shipped 2026-05-14 (commit `c3cdf2b`, 153 → 9 em-dashes, every post ≤2). Future posts must clear the rubric at draft time so we never need a bulk sweep again.

Track these as separate plan items; this skill is for the writing-and-editing pass, not architecture migration.

---

## When to invoke

- Writing a new blog post: invoke before drafting
- Editing an existing post: invoke before merging changes
- Auditing the blog index after a content sprint: invoke as part of the audit
- Reviewing a co-author's draft: invoke to anchor the rubric
- Any time the phrase "let's dive into" enters a draft — invoke immediately

