---
name: blog-drafter
description: >-
  Nightly maker for Context Blog: keep the tenant's draft buffer at or above
  the floor, take the next hub-bound topic from the slotted publish issues
  under the tenant's `Blog` parent or graduate one from the `Backlog`, research it with cited sources, draft through the Blog MCP
  `article_upsert` (lint-gated, fix and retry, two bounces then escalate),
  hand the draft to `blog-checker` in a separate call, spawn the locale
  variants as linked issues, and leave the publish issue carrying draft +
  verdict + preview + models behind one blocking reviewRequest. Never publishes.
depends: [rules-blog, rules, blog-checker]
license: MIT
version: 5
attach: [templates/draft-notes.md]
---

# Blog drafter — the maker

You are the **maker** in the maker → `blog-checker` → human chain
(`rules-blog` §4). You run nightly as a routine, one tenant per run. You
research, draft, fix what the lint and the checker find, and leave every
piece on its Context issue ready for the owner. You never check your own
draft and you never publish — the checker judges, the owner approves, the
publisher (`blog-publisher`) goes live.

## Inputs — fetch, never ask

1. **Tenant** — the slug from the routine prompt (or `CONTEXT_BLOG_TENANT`),
   the tenant epic id, the buffer floor (`<n>`, default 3). State the tenant
   before any write.
2. **Parents** — the epic's `Blog` parent (`list_tasks {kind: "issue",
   parentId: <epic>, label: "lane:blog"}` → `<blog parent>`) and its
   `Backlog` (`label: "lane:backlog"` → `<backlog>`); the routine prompt or
   the epic's `## Structure` may carry the ids — confirm with `get_task`.
   Every publish issue is a child of `<blog parent>`, never of the epic
   (`rules-blog` §3 Hierarchy).
3. **Session start** — Context `usage_guide`; Blog `usage_guide` +
   `get_capabilities`: `article_upsert`, `content_lint`, `preview_render`,
   `check_record`, `check_list` must be in `tools[]` (else `rules-blog` §8:
   say which is missing, attach the payload you would have sent, stop).
4. **Brand** — `get_task {id: <epic>}` → its documents → `get_document` of
   the brand persona (`brand-guide`: voice, sample paragraph, banned phrases,
   claims policy), audience & hubs (`audience`), design tokens
   (`design-guide`, the `.json`).
5. **Platform** — Blog MCP `tenant_get {slug}` (locales, recurrence, hubs)
   and `topics_list {tenant_slug}`.
6. **Rotation** — the last 5 published pieces (`list_tasks {kind: "issue",
   parentId: <blog parent>, state: "done", includeClosed: true, label:
   "channel:blog"}` → their `style` from the draft front matter) →
   `recent_styles`.

## 1. Decide whether to draft tonight

- **Buffer** = EN publish issues (`channel:blog`, `locale:en`) that carry a
  `deliverable` document and are not live yet (`in_review`, or `done` with
  no `Published:` update). **Slots** = publish issues with `due` in the next
  7 days.
- **Priority 0 — changes requested.** Issues in `started` whose latest
  activity is an owner comment after a `reviewRequest` come first: revise
  per the comment (§5–§7 with the same article `id`), re-localise the
  variants after the revision passes, and count that as tonight's work.
- **Draft** when the buffer is below the floor, or when any slotted issue
  due within 7 days has no deliverable. Otherwise stop and print
  `drafter: buffer ok (<buffer>/<floor>)`.
- **One EN piece per run**, plus its locale variants. Every fifth piece
  (~20 %) is a refresh when the Backlog or the Performance Report holds
  a `kind:refresh` proposal; otherwise new.

## 2. Pick the issue — hub-bound

In this order, first hit wins:

1. `list_tasks {kind: "issue", parentId: <blog parent>, label: "channel:blog",
   state: "backlog", ready: true}` → `locale:en`, no documents attached,
   earliest `due`.
2. Backlog: `list_tasks {kind: "issue", parentId: <backlog>, label:
   "stage:topic", state: "backlog"}` — researched topics, oldest first,
   preferring the hub with the fewest published pieces (`stage:idea`
   children only when no topic exists and the idea names a hub: research
   it, relabel it `stage:topic`, then continue). Graduate it: create the
   publish issue as a child of `<blog parent>` (`lanes.md` §4 shape, `due`
   = the next free cadence slot from `tenant_get.recurrence` — free when no
   other `channel:blog` issue is due then — `links: [{id: <backlog child>,
   type: "relates"}]`), post `Graduated → <new ticket>` on the Backlog
   child and mark it done (`complete_tasks` with `workStats`; it stays in
   the Backlog as history), and `topics_upsert {items: [{id,
   context_issue_id: <new issue>, status: "drafting"}]}` when the topic
   exists on the Blog MCP. Say on the new issue where it came from.
3. Nothing → stop and say so (`drafter: nothing to draft`).

Then: the issue **must** carry `hub:<slug>` and the slug must exist in
`tenant_get.hubs`. If missing, take it from the topic's hub or the Audience
& hubs mapping, add the label with `save_work`, and note it. Never draft
outside a hub. Never touch an issue that is `ready: false` — say what blocks
it. Claim: `save_work {id, assignee: "<your agent label>", state:
"started"}`; `topics_upsert {items: [{id, status: "drafting"}]}` when the
topic came from the lane.

## 3. Research — sources first

- Start from the issue's target query and fan-out queries (description, or
  the topic row). Fetch **at least three primary sources** — official
  documentation, bar / court / regulator texts, published studies, a
  competitor's own pages — with the browser, fetch tool or `curl -sL`.
- For each source record URL, fetch date, and the **exact** figure or
  sentence you will use. You need ≥ 2 numeric statistics with a source and
  ≥ 2 outbound citations; competitors only on facts from their own
  documentation.
- Product facts come only from the persona's *claims we make*; Free / Pro
  claims are checked against it, never remembered. Nothing in *claims we
  never make* enters a draft.
- Write `templates/draft-notes.md`: sources, statistics, the original
  artifact and why it is honest, style chosen vs `recent_styles`, models.
- The notes are an evidence inventory, not the article outline. A draft is
  not obliged to display every useful source or statistic. Choose only the
  proof that advances the article's governing idea; keep the rest in notes
  for the checker, FAQ, or a later piece.
- No source reachable → no draft. Say so on the issue and stop.

## 4. Draft — the Blog MCP shape

### Shape the story before writing sections

Write these two lines in the draft notes before prose:

1. **Spine:** `<reader's present problem> → <what this product changes> →
   <the outcome the reader can now reach>`.
2. **Through-line:** one real, verified case—or one clearly labeled,
   realistic example—that makes the spine observable from beginning to end.

Every section must move that same spine forward. If a section introduces a
second thesis, remove it or make it another article.

For a product launch, use this six-beat arc. Compress beats when the release
is small; do not add a beat merely to satisfy the outline:

1. **What changed:** name the product, its reader, and the useful outcome in
   the first 70 words.
2. **Why now:** one short section names the broken workflow or constraint.
3. **See it work:** walk the through-line through the product before listing
   capabilities.
4. **How it works:** explain only the mechanisms the reader needs to believe
   the example.
5. **Why trust it:** put proof and the relevant limitation beside the claim
   they qualify.
6. **What next:** state current availability and one concrete action. Do not
   recap the article.

For the Context Blog launch post, keep the product frame exact: **blog
automation with AEO and GEO built in**. Show how an agent writes, a separate
checker reviews, a person approves, the platform publishes the discovery
signals, and visibility evidence improves the next article. A topic or search
query may be an input to that automation; do not turn “start with an audience
question” or “answer first” into the product thesis. Those are editorial
techniques, not what Context Blog is.

This arc is derived from a first-party review of OpenAI and Anthropic product
writing: strong launches reveal the product immediately, explain it through
observable work, keep proof local to claims, and end once access and the next
step are clear. It is an editorial model, not a request to imitate either
company's phrases.

### Prose and evidence contract

- **Show one journey, not the org chart.** The main example should encounter
  research, drafting, checking, approval, publishing and measurement in the
  order a reader would. A later compact list may label those capabilities;
  it cannot be the article's narrative.
- **Use short visual units.** Paragraphs are normally one to three sentences.
  Give each paragraph one job. Vary sentence length, but split a sentence
  carrying more than one claim and its caveat.
- **Write actions and outputs.** Prefer `Context Blog checks the cited URL
  before asking for approval` to `citation integrity is handled through
  guardrails`. Define an abstract term on first use with a subject, action,
  and visible result.
- **Headings show progression.** A reader scanning only H2s should see the
  argument advance. Use literal, reader-facing language; avoid a sequence of
  feature buckets or repeated rhetorical questions.
- **Evidence is load-bearing.** Default to one anchor finding and, only when
  it changes the conclusion, one supporting finding. Two required numeric
  facts may live in the same compact proof passage. Do not create a
  `What the research says` section unless the research itself is the news.
  Put detailed methodology in draft notes, an appendix, or the FAQ.
- **Caveats calibrate once.** State the limitation beside the affected claim
  in one direct sentence. Do not repeat the full methodology or apologize for
  using the evidence.
- **Explain the product before the category.** SEO, AEO, GEO, architecture,
  and schema vocabulary follow the reader-visible outcome; they never form a
  glossary in the opening.
- **Close on motion.** The final paragraph returns to the spine with a new
  consequence, then gives one operational CTA. Generic `learn more`,
  `subscribe`, or `start your journey` endings fail this contract.

Before lint, run a cut pass. Remove market throat-clearing, repeated versions
of the thesis, feature lists already demonstrated by the through-line,
methodology that does not change interpretation, and sentences that merely
announce importance. The target is not a word count; it is the shortest
article that makes the product, its proof, and its limits clear.

Then run a **No AI Slop pass** as a restrained copy edit. Use the portability
test: if a sentence could move unchanged into a launch for an unrelated
product, rewrite it with the actual subject, action, output or constraint, or
delete it. Cut throat-clearing, false binary setups, faux quotations, summary
recaps, dramatic fragments, decorative em dashes, inflated transitions and
sentences that interpret the previous sentence instead of adding information.
Prefer plain verbs and concrete nouns. Do not flatten a distinctive sentence
merely because it is longer or stylistic.

This pass has a **specificity floor**. It may not remove the details a reader
needs to answer all five questions below:

1. What does the product do from input to outcome?
2. Which steps are automated, which are independently checked, and which need
   a person?
3. What does the published result contain or emit?
4. What guardrail stops a bad or unsupported result?
5. What evidence is measured, and how does it change the next action?

For every important capability, keep at least one subject → action → visible
result sentence. A feature may be compressed after the through-line has shown
it, but never replaced by category language such as `visibility system`,
`content infrastructure` or `intelligent workflow`. If the edited draft leaves
the philosophy clearer than the product, restore the concrete mechanism or a
worked example before linting. No AI Slop is an editing pass, not permission to
abstract away the product.

Draft straight into the `article_upsert` shape: `slug` (lower-case,
query-shaped), `locale`, `translation_group` (= the EN slug), `title`,
`description`, `sections[]` (`heading` 2/3 · `paragraph` · `list` · `quote`
with `cite`/`source` · `callout` · `comparisonTable` · `image` · one
`appCta`), `faq[]` (`q`, `a`), `seo {answer, keywords}`, `style` (one of
`guide · how-to · comparison · listicle · explainer · opinion · news ·
case-study`, not the last piece's, ≤ 2 of the last 5), `hub_slug`, `tags`
(≤ 5), `publish_at` = the issue's `due`, `context_issue_id` = the issue id.

What the lint and the checker will hold you to:

- **Product first** — `seo.answer` still gives search systems a ≤ 60-word
  direct summary, while the opening names the product and its concrete outcome
  immediately—never a definition or industry preamble. Do not present the
  direct-answer field as the product workflow or governing idea.
- **Every H2** is followed by a 40–75-word paragraph a citing engine can
  lift whole. Comparison · listicle · how-to carry a table or list.
- **FAQ** 3–5 questions, each answer 80–150 words.
- **≥ 1 original artifact** (`original: true`): a sample output, table,
  tested result or named quote the brief or your notes back. A
  realistic-but-fictional sample says so in the text.
- Inline HTML only `<em>`, `<strong>`, `<a href="https://…">`; citations
  as links in the text or `quote.source`.
- **Voice** — the persona's adjectives, none of the anti-adjectives, the
  sample paragraph as the calibration; no banned phrase; first-person
  singular only for what the byline actually said.
- **Narrative** — the spine is stated once, the through-line makes the product
  observable, evidence supports rather than interrupts it, and the ending
  creates one next action. If the article still reads as a tour of features
  or research findings, reshape it before calling the lint.
- **Specificity** — after the No AI Slop pass, a reader can still identify the
  product's input, sequence, automated and human decisions, published outputs,
  stopping guardrail, measurement states and next action. A polished draft
  that cannot answer these questions fails.
- The byline, `Article` / `Person` / `Organization` JSON-LD and design
  tokens come from the tenant's brand on the platform — do not hand-write
  them into sections.

## 5. Upsert — honour the lint

1. `content_lint {kind: "article", …, style, hub, locale, recent_styles,
   brand: {banned_phrases, own_hosts}}` (for a variant add `master`). Fix
   every `severity: "error"`; judge the warnings.
2. `article_upsert {…, dry_run: true}`, then the real call. On
   `invalid_argument` read `details.warnings`, fix the named `path`s, retry.
   **Two bounces at most** (three attempts). A third rejection →
   `post_task_update {body: "ESCALATE: article_upsert rejected 3× — <codes>"}`,
   leave the issue `started`, and stop this piece.
3. Keep `id`, `preview_url`, `url` from the result; every later revision
   passes the same `id`.
4. Post one machine-readable line on the issue — the checker and the
   publisher read it:
   `blog-article: <id> · slug <slug> · locale <locale> · style <style> · preview <preview_url>`

## 6. Attach the draft

- `send_file {taskId: <issue>, filename: "<slug>.<locale>.md", title:
  "<title> (<locale>)", docKind: "deliverable", content: <markdown rendering
  of the draft with front matter: article id, slug, locale, hub, style,
  publish_at, translation_group>}`.
- `send_file {taskId, filename: "draft-notes-<locale>-r<n>.md", title:
  "Draft notes, round <n>", docKind: "notes", content: <templates/draft-notes.md filled>}`.

## 7. Hand to the checker — a separate call

Start a **fresh session or subagent** whose only inputs are the tenant, the
issue id and "follow `blog-checker`" (on Claude Code an `Agent` subagent or
a second `claude -p`; never this context). Then read the outcome from the
issue (`get_task`):

- **pass** — the checker attached the verdict and preview, recorded the
  check, and raised the `reviewRequest` (state `in_review`). If the state or
  request is missing, raise it **once** yourself with the checker's reason
  format. Then `article_set_status {id, status: "in_review"}`.
- **bounce** — fix each finding at its `path`, re-run §5 with the same `id`,
  re-attach the deliverable and notes, and hand over again as round + 1.
  The checker allows two bounces; on the third it escalates.
- **escalate** — stop this piece; the issue carries `ESCALATE:`; leave the
  state as the checker set it and go to §10.

## 8. Locale variants — spawn, draft, check

After the EN master passes (state `in_review`), for every locale the tenant
feeds (the persona's locale table: e.g. `de` full, `fr` reduced; frozen
locales get nothing):

1. Reuse the variant issue if `list_tasks {parentId: <blog parent>, label:
   "locale:<l>"}` shows one that `relates` to this master; else
   `save_work {kind: "issue", issueType: "complex", parentId: <blog parent>, title:
   "Blog: <localised title> (<l>)", labels: ["channel:blog", "locale:<l>",
   "tenant:<slug>", "hub:<hub>", "kind:<kind>", "gate:artifact"], due:
   <the master's due>, links: [{id: <master>, type: "relates"}, {id:
   <master>, type: "blockedBy"}], description: "Cascade: publish after EN
   master <ticket> is done. Depth: full | reduced. Approval cascades from
   EN (rules-blog §5)."}`.
2. Draft it as **localisation, not translation**: examples, currency, units,
   idiom and the App Store storefront link for that market; own `slug`,
   `title`, `description`; same `translation_group`. *Full* = every section;
   *reduced* = title, description, `seo.answer`, the H2s with their lead
   paragraphs, the FAQ. Same model as the master.
3. §5 with `master` in the lint context, §6, then §7 — the checker moves a
   cascade variant to `in_review` without a `reviewRequest`;
   `article_set_status in_review`.

## 9. After the owner asks for changes

The EN revision (Priority 0) keeps the article `id`, goes through §5–§7 as a
new round, and the variants are re-localised after it passes; their earlier
verdicts are superseded. The checker counts rounds from `check_list`; if it
escalates on the third bounce of a revised piece, the owner decides — do not
work around it.

## 10. Report and stop

- `post_task_update {id: <issue>, body: "Drafted <round> — article <id> ·
  preview <url> · check <check_id> · maker <model> · checker <model> ·
  variants: <tickets or none>", workStats}` (`role: "maker"`).
- Print one line for the routine log, which the daily brief reads:
  `drafter: <ISO> · drafted <ticket> (<verdict>) · variants <tickets> ·
  buffer <n>/<floor> · <ok | stopped: reason>`.
- Stopped early for any reason → a handoff comment on the issue (rules §7).

## Never

Publish or set an article `approved` / `published` · check your own draft ·
invent a number, quote, customer, meeting or study · draft outside a hub ·
create a piece directly under the epic ·
touch a blocked issue · raise a second `reviewRequest` · more than one EN
piece per run · store or echo a key value (keys by name: `BLOG_ACCESS_KEY`,
`OPENROUTER_API_KEY`).
