← All Lessons
Lesson 11

Autonomous Drafting

Claude reads your signals, picks the strongest one, and drafts a post in your voice. You edit, not create.

Most people stare at a blank page. The problem isn't writing. It's starting. This system removes the blank page entirely. Claude reads five sources of signal from your actual work, picks the one with the most energy, and writes a first draft. Your job shifts from creator to editor.


What you're building today

An autonomous content drafting system. A Python script gathers signals from five sources (content seeds, brain knowledge, git commits, memory, and existing drafts), evaluates which signal has the most potential, and uses Claude to write a 300-500 word draft in your voice. Saves to your drafts folder and brain inbox. Runs on demand or on a schedule.


Step 1: The five signal sources

The system doesn't guess what to write about. It reads what you're already doing:

  1. Content seeds (brain/00-inbox/seeds/) — Ideas you've captured but haven't written about
  2. Brain knowledge (brain/10-knowledge/) — Principles and patterns you've distilled
  3. Git commits (recent work) — What you shipped this week
  4. Memory (session memories) — What Claude noticed across your conversations
  5. Existing drafts (doc/writing/drafts/) — Half-finished pieces that need a push

The strongest signal isn't always the newest idea. Sometimes it's a principle you captured three weeks ago that connects to something you shipped yesterday. The system finds those connections.


Step 2: Signal gathering

# signals.py — gather raw material from 5 sources

from pathlib import Path
from datetime import datetime, timedelta

STRATEGY = Path.home() / "strategy"

def gather_seeds():
    """Content seeds you've captured but haven't published."""
    seeds_dir = STRATEGY / "projects/brain/00-inbox/seeds"
    if not seeds_dir.exists():
        return []
    return [
        {"source": "seed", "title": f.stem, "content": f.read_text()[:500]}
        for f in sorted(seeds_dir.glob("*.md"), key=lambda x: x.stat().st_mtime, reverse=True)[:10]
    ]

def gather_brain():
    """Recently added principles and patterns."""
    knowledge = STRATEGY / "projects/brain/10-knowledge"
    cutoff = datetime.now() - timedelta(days=14)
    results = []
    for f in knowledge.rglob("*.md"):
        if datetime.fromtimestamp(f.stat().st_mtime) > cutoff:
            results.append({"source": "brain", "title": f.stem, "content": f.read_text()[:500]})
    return results[:5]

def gather_git():
    """What you shipped recently."""
    import subprocess
    try:
        log = subprocess.check_output(
            ["git", "log", "--oneline", "--since=7 days ago", "--all"],
            cwd=STRATEGY, stderr=subprocess.DEVNULL
        ).decode().strip()
        return [{"source": "git", "title": "Recent commits", "content": log}] if log else []
    except:
        return []

Step 3: The drafter

Claude picks the strongest signal and writes the draft. The key: it writes in your voice because your CLAUDE.md tells it how you think and communicate.

# drafter.py — pick a signal, write the draft

import anthropic

def draft(signals, voice_context):
    """Pick strongest signal, draft 300-500 words."""
    client = anthropic.Anthropic()

    signal_text = "\n\n".join(
        f"[{s['source']}] {s['title']}\n{s['content']}"
        for s in signals
    )

    response = client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=1500,
        system=f"""You are a ghostwriter. Write in this person's voice:

{voice_context}

Rules:
- 300-500 words
- Start with a hook, not a preamble
- Use their vocabulary, not generic AI language
- End with an insight, not a summary
- No em dashes. Use periods or commas instead.""",
        messages=[{
            "role": "user",
            "content": f"""Pick the signal with the most energy from these sources.
Write a blog post draft about it.

SIGNALS:
{signal_text}"""
        }]
    )

    return response.content[0].text

Step 4: The manifest (deduplication)

The system keeps a manifest of every signal it's already drafted from. If you captured a seed called "pricing-as-positioning" and it already wrote a draft about it, it won't write another one. The manifest prevents repeat content and forces the system to find fresh signals.

# Check the manifest before drafting
import json

MANIFEST = Path("draft-manifest.json")

def is_already_drafted(signal_title):
    if not MANIFEST.exists():
        return False
    manifest = json.loads(MANIFEST.read_text())
    return signal_title in manifest.get("drafted", [])

def mark_drafted(signal_title):
    manifest = json.loads(MANIFEST.read_text()) if MANIFEST.exists() else {"drafted": []}
    manifest["drafted"].append(signal_title)
    MANIFEST.write_text(json.dumps(manifest, indent=2))

Step 5: Run it

# Draft a post from your strongest signal
python3 draft-post.py

# Draft about a specific topic
python3 draft-post.py --seed "pricing strategy"

# Preview without saving
python3 draft-post.py --dry-run

# Check signals from the last 3 days instead of 7
python3 draft-post.py --days 3

The draft saves to two places:


Step 6: Schedule it

# Run every morning at 7am
crontab -e

# Add:
0 7 * * * cd ~/strategy && python3 scripts/draft-post.py

Wake up to a fresh draft every morning. Edit it, publish it, or throw it away. The point is: you never start from zero.

This changes your relationship with content. You stop being a creator (staring at blank pages) and become an editor (refining drafts that already exist). Editors are faster than creators. Editors have momentum. The system gives you momentum every morning.


What you just built

Homework

Run the drafter once right now. Don't seed it with a topic. Let it pick from your signals. Read the draft. Notice how it sounds like you because it pulled from your brain, your seeds, your commits. That's the compounding effect turned into content.