Substack Thought-Leader Newsletter (starter blueprint)
Slug:
substack-thought-leader-newsletterType:agent_blueprintSource: code-defined; appears in every tenant'sagent_blueprint://listautomatically. Fork viaagents.fork_blueprintto customize. Outputs: the newcontent_pieceartifact 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_generated—true.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
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
pillar_id | artifact_ref(content_pillar) | yes | — | The pillar this issue rotates within. Scopes the topic ranking + evidence gathering. |
audience | string | no | early-stage founders | Who the issue is written for. Anchors the synthesis via the audience_scoping fragment. |
topic_hint | string | no | — | If supplied, skips the topic discovery step. Useful when the user already has a specific question they want the issue to answer. |
seed_call_company_name | string | no | — | Anchor 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.
- pick_topic —
branch. If$inputs.topic_hintis set, skip discovery and pass the hint through as a single-option candidate set. Otherwise: a parallel fan-out ofdata.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. - pause_for_topic_approval —
assert. 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 aschosen_topic. Skipped silently when a topic_hint was supplied. - gather_internal_voice —
parallel(4 sub-tools). All four arms run concurrently:data.queryfor author-as-speaker premium quotes (the candidate pull-quote material).data.queryfor customer-voice premium quotes (supporting evidence in the body sections).data.queryfor internal coaching language (speaker_type='internal' AND company_name IS NULL— how the team talks about the topic among themselves).data.cluster_detailon the chosen cluster for representative quotes.
- gather_external_context —
parallel(2 sub-tools).external_search.executeaction=enrich_topicwithforce_refresh=trueanddeadline_ms=120000. Returns a fused internal+external+divergence brief on the topic. If the response carriespartial=true, the calling LLM logs a warning and proceeds.- Anthropic native
web_searchfor canonical thought-leader sources (PG essays, established VC blogs, longform analyses). Pulls ≥3 URLs.
- pause_for_outline_review —
assert. 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. - write_substack —
llm(effortdeep, temperature 0.7). Composes 3 prompt fragments:grounding_rules,audience_scoping, and the newsubstack_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). Outputssubstack_draft(markdown),word_count,internal_quotes_count,external_quotes_count, and acitationsarray. - pause_for_final_review —
assert. The calling LLM emits the full draft body for human approval. Resumeaction='continue'proceeds to the artifact write;action='revise're-runswrite_substackwith feedback;action='stop'aborts. - create_artifact —
tool.artifacts.createwrites the approved draft as acontent_pieceartifact with all the grounding metadata + citations. The artifact lands atstatus='drafting'andmetadata.review_status='pending_review'so a human approves before it ships.
Pause types and resume contract
| Pause type | Triggered by | Resume requires |
|---|---|---|
topic_approval | pause_for_topic_approval | action='continue' + chosen_topic=<selected option>. Or action='stop' to abort. |
outline_review | pause_for_outline_review | action='continue' + approved_outline=<the outline json>. Or action='revise' + feedback=<text>. Or action='stop'. |
final_review | pause_for_final_review | action='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-blueprinttool, 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 — seedocs/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.schedulefield 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 (seedocs/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:
read_resource agent_blueprint://substack-thought-leader-newsletterThen gather the inputs and perform each step with your own primitive tools:
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_topicruns (data.cluster_search+data.queryrank top 3 candidates). - T+5s —
pause_for_topic_approvalhalts. User picks "why customer onboarding is the cheapest place to grow". - T+30s —
gather_internal_voicefans 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_contextreturns the fusedenrich_topicbrief plus 5 canonical web_search URLs. - T+95s —
pause_for_outline_reviewhalts. User approves the outline (or revises once before approving). - T+180s —
write_substackproduces the 2,100-word draft with twome, on a sales call with Mesa, ...pull quotes + a "Steal this framework: the onboarding audit checklist" sidebar. - T+185s —
pause_for_final_reviewhalts. User reviews + approves. - T+190s —
create_artifactwrites thecontent_piece. Run completes.
Forking it
agents.fork_blueprint source=substack-thought-leader-newsletter new_slug=<your-fork>Common forks:
- Skip the topic gate — drop
pause_for_topic_approvalfor users who always settopic_hintand never want to discover. - Custom evidence layer — extend
gather_internal_voicewith 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_voicefragment 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_pieceartifact lands atstatus='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_reviewpause. - 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_idfail at the operation registry boundary; the pillar must live in the calling API key's workspace.