Docs

Substack Thought-Leader Newsletter (starter blueprint)

Slug: substack-thought-leader-newsletter Type: agent_blueprint Source: code-defined; appears in every tenant's agent_blueprint://list automatically. Fork via agents.fork_blueprint to customize. Outputs: the new content_piece artifact id (channel = newsletter) + the final word count + counts of internal vs. external citations woven in.

The weekly Substack issue. Picks a recurring question from sales-call data (or accepts one as input), mines internal voice + the public web in parallel, drafts a 1500-2500 word longform piece in the author's first-person voice — Lenny's-Newsletter shape, never a third-person editorial profile — and writes it as a content_piece artifact for human review. Three approval gates land along the way so a human stays in the loop on topic, outline, and final draft.

When to use it

  • It's Saturday morning, you owe Substack subscribers an issue, and you don't want to write it from a blank page.
  • A specific customer arc keeps showing up across recent calls and you want a longform piece that opens with that account, returns to it through the body, and ties takeaways back to it.
  • You want a piece grounded in the author's actual lived language from real sales calls — pull quotes attributed — me, on a sales call with [company], [date] rather than synthesised opinions.
  • The pillar already has substrate density (recurring conversation themes the cluster_search picks up) and you want the topic discovery to surface those themes rather than guess.

What it produces

A new content_piece artifact with:

  • content_json.channel'newsletter'. The existing channel enum value covers Substack-shaped publications; no schema change needed.
  • content_json.draft_markdown — the full 1500-2500 word body in markdown. Includes a TL;DR block, 3-4 H2 sections, at least one "Steal this framework" boxed sidebar, at least two pull quotes (the author quoting themselves from real sales calls is the load-bearing pattern), a 3-bullet takeaway list at the close, one soft CTA, and an author signature on its own line.
  • content_json.status'drafting'.
  • content_json.sources — the citations array carrying both internal voice quotes (type='internal', with speaker / company / source cluster id) and external URLs (type='external', with url + source description).
  • metadata.kind'substack'.
  • metadata.auto_generatedtrue.
  • metadata.review_status'pending_review'.
  • metadata.audience — the input audience string.
  • metadata.seed_call_company_name — when supplied, names the protagonist account the customer arc was woven around.
  • metadata.word_count, metadata.internal_quotes_count, metadata.external_quotes_count — observability fields the polling UI surfaces.

Inputs

NameTypeRequiredDefaultDescription
pillar_idartifact_ref(content_pillar)yesThe pillar this issue rotates within. Scopes the topic ranking + evidence gathering.
audiencestringnoearly-stage foundersWho the issue is written for. Anchors the synthesis via the audience_scoping fragment.
topic_hintstringnoIf supplied, skips the topic discovery step. Useful when the user already has a specific question they want the issue to answer.
seed_call_company_namestringnoAnchor protagonist (e.g. "Mesa", "Revv"). The body opens with a moment from this account, returns to it in at least one section, ties back close.

How it runs

8 top-level steps. Three of them are approval gates that halt and surface a pending_input pause for human review.

  1. pick_topicbranch. If $inputs.topic_hint is set, skip discovery and pass the hint through as a single-option candidate set. Otherwise: a parallel fan-out of data.cluster_search (recurring conversation themes for the pillar) + data.query (per-cluster author-as-speaker quote density) ranks candidates by cluster member count and quote density.
  2. pause_for_topic_approvalassert. Halts and surfaces the top 3 candidates plus a "type your own" option as a confirm pause. The user picks one; the resume value lands as chosen_topic. Skipped silently when a topic_hint was supplied.
  3. gather_internal_voiceparallel (4 sub-tools). All four arms run concurrently:
    • data.query for author-as-speaker premium quotes (the candidate pull-quote material).
    • data.query for customer-voice premium quotes (supporting evidence in the body sections).
    • data.query for internal coaching language (speaker_type='internal' AND company_name IS NULL — how the team talks about the topic among themselves).
    • data.cluster_detail on the chosen cluster for representative quotes.
  4. gather_external_contextparallel (2 sub-tools).
    • external_search.execute action=enrich_topic with force_refresh=true and deadline_ms=120000. Returns a fused internal+external+divergence brief on the topic. If the response carries partial=true, the calling LLM logs a warning and proceeds.
    • Anthropic native web_search for canonical thought-leader sources (PG essays, established VC blogs, longform analyses). Pulls ≥3 URLs.
  5. pause_for_outline_reviewassert. The calling LLM proposes an outline { audience, jtbd, problem_framing, framework, plan_steps[3-5], customer_arcs[2-3] } using the gathered evidence. Surfaces as a confirm pause; the user approves, revises (re-prompts the outline), or kills.
  6. write_substackllm (effort deep, temperature 0.7). Composes 3 prompt fragments: grounding_rules, audience_scoping, and the new substack_thought_leader_voice. Plus inline structural rules (TL;DR + 3-4 H2 + ≥1 sidebar + ≥2 pull quotes + 1500-2500 words + author first-person + soft CTA + signature, no em dashes). Outputs substack_draft (markdown), word_count, internal_quotes_count, external_quotes_count, and a citations array.
  7. pause_for_final_reviewassert. The calling LLM emits the full draft body for human approval. Resume action='continue' proceeds to the artifact write; action='revise' re-runs write_substack with feedback; action='stop' aborts.
  8. create_artifacttool. artifacts.create writes the approved draft as a content_piece artifact with all the grounding metadata + citations. The artifact lands at status='drafting' and metadata.review_status='pending_review' so a human approves before it ships.

Pause types and resume contract

Pause typeTriggered byResume requires
topic_approvalpause_for_topic_approvalaction='continue' + chosen_topic=<selected option>. Or action='stop' to abort.
outline_reviewpause_for_outline_reviewaction='continue' + approved_outline=<the outline json>. Or action='revise' + feedback=<text>. Or action='stop'.
final_reviewpause_for_final_reviewaction='continue' to write the artifact. Or action='revise' + feedback=<text>. Or action='stop'.

Each pause is a structural halt at an assert step. The runtime model is the same one draft_piece uses for evidence_missing — the calling LLM surfaces the halt_message as a pending_input pause, the user picks an option in the chat panel, and the run resumes from the next step.

Triggers

  • Manual — a human or agent picks this recipe and walks it; on the MCP tool surface there is no run-blueprint tool, so you (the conversational agent) are the runner. Read it (agent_blueprint://substack-thought-leader-newsletter) and perform the steps with the input map below. Platform-initiated manual runs (universal-ui "Run now") instead go through the headless SDK runner — see docs/blueprint-runner-sdk.md.
  • Scheduled — the recipe declares a weekly Saturday 17:00 UTC (10:00 PT, accepting the daylight-saving wall-clock shift) intent. On the MCP tool surface this trigger.schedule field is just a DSL declaration — nothing on that surface fires it for you. Platform-side, the headless SDK runner does honor schedule/event/webhook triggers (see docs/blueprint-runner-sdk.md). If you are walking it as the conversational agent, do so weekly yourself (or wrap it in your own scheduled job that does), carrying any pause forward and only writing the artifact after the human resumes through all three gates.

Reading and walking it

On this MCP tool surface there is no run-blueprint tool — read the recipe and walk its steps yourself. (Platform-initiated, unattended, and backtest runs use the headless SDK runner instead; see docs/blueprint-runner-sdk.md.) Via MCP from an external Claude Desktop:

code
read_resource agent_blueprint://substack-thought-leader-newsletter

Then gather the inputs and perform each step with your own primitive tools:

code
pillar_id: 'b8a4...c12e'
audience: 'early-stage founders'
topic_hint: 'why customer onboarding is the cheapest place to grow'
seed_call_company_name: 'Mesa'

Walking the recipe, the first assert surfaces the topic_approval pause. The user resumes with chosen_topic='why customer onboarding is the cheapest place to grow' (the topic_hint was wrapped as a single candidate). You then fan out internal voice + external context calls in parallel and surface the outline_review pause. The user approves the outline; you write the 1500-2500 word draft and surface the final_review pause. The user approves; the content_piece artifact lands with status drafting.

Sample run timeline

  • T+0s — pick_topic runs (data.cluster_search + data.query rank top 3 candidates).
  • T+5s — pause_for_topic_approval halts. User picks "why customer onboarding is the cheapest place to grow".
  • T+30s — gather_internal_voice fans out 4 internal queries in parallel. Returns 12 author-as-speaker quotes, 18 customer quotes, 6 internal coaching lines, 8 representative cluster quotes.
  • T+90s — gather_external_context returns the fused enrich_topic brief plus 5 canonical web_search URLs.
  • T+95s — pause_for_outline_review halts. User approves the outline (or revises once before approving).
  • T+180s — write_substack produces the 2,100-word draft with two me, on a sales call with Mesa, ... pull quotes + a "Steal this framework: the onboarding audit checklist" sidebar.
  • T+185s — pause_for_final_review halts. User reviews + approves.
  • T+190s — create_artifact writes the content_piece. Run completes.

Forking it

code
agents.fork_blueprint source=substack-thought-leader-newsletter new_slug=<your-fork>

Common forks:

  • Skip the topic gate — drop pause_for_topic_approval for users who always set topic_hint and never want to discover.
  • Custom evidence layer — extend gather_internal_voice with a parallel arm that calls your CRM or notes search.
  • Channel-specific length — fork to a tighter 800-1200 word "Friday brief" variant by replacing the substack_thought_leader_voice fragment with a shorter-form variant.
  • Different cadence — change the schedule cron to fire daily, biweekly, or at a different wall-clock time.

Limits

  • The content_piece artifact lands at status='drafting'. Publishing is out of scope for this blueprint; a separate "publish to Substack" workflow handles that step.
  • The 1500-2500 word target is a soft band. The voice fragment teaches the LLM to respect it; outliers below 1500 are flagged for review at the final_review pause.
  • The substack channel writes use the existing 'newsletter' channel enum value rather than a new 'substack' channel — the schema didn't need to grow for this blueprint.
  • On the MCP tool surface the schedule trigger is documentation of intent only — nothing on that surface fires it, so walk the recipe weekly yourself (or wrap it in your own scheduled job). Platform-side, the headless SDK runner does fire schedule/event/webhook triggers (see docs/blueprint-runner-sdk.md).
  • Cross-tenant references on pillar_id fail at the operation registry boundary; the pillar must live in the calling API key's workspace.