# Companion prompt: turn a document I am not reading into a personalized podcast

Hand this whole file to your AI. It is written for the AI, not for you. It goes with
The Personalized Podcast Playbook by Erica Layer, Layer Advisory Services, layeradvisory.com.

---

I want you to help me turn a long document I keep meaning to read into a private podcast episode
that arrives in my podcast app on its own. Work through the five steps below in order. Do not skip
ahead, and do not build anything until I have confirmed the script in Step 2, because the script
decides whether any of this is worth having.

**How I want you to work:** direct and competent. One step at a time, and wait for me at every
point marked STOP. Tell me plainly when something I have asked for is a bad idea. No padding, no
flattery. If you need a decision from me, ask rather than guessing and carrying on.

**Before you start,** ask me for these three things, then reflect back what you heard in two lines:

1. The document itself, the whole thing rather than a summary.
2. Where in my week I would listen: the day and the activity.
3. What you have access to: which AI environment, whether you can write files, run shell commands,
   run something on a schedule, and publish to the web.

Section 6 at the end is a working reference implementation. Adapt it to what I actually have rather
than assuming my setup matches it, and tell me where you are deviating and why.

---

## Step 1. Check whether the length is really the problem

Read the document properly. Then do two things.

First, tell me what a 50% cut would destroy, quoting specific passages rather than describing them.
Second, make the strongest case for cutting it in half, and show me what the shorter version looks
like.

Then give me your honest read: bloated, or is the length carrying the value? If it is bloated, say
so plainly and recommend I cut it instead of building any of this.

**STOP.** Wait for my decision.

---

## Step 2. Rewrite it for the ear

This is the step that matters. Write a spoken script of the whole document, following these rules:

- **Nothing is cut.** Every item, every piece of evidence, every caveat appears. If you are
  summarising, you have misread the task. The script usually comes out longer than the original,
  because speech needs connective tissue that a page does not.
- No markdown: no headers, bullets, bold, numbered lists. Prose only.
- No URLs read aloud. Say "it is linked in the original" if a source must be pointed at.
- No section labels. Say what a section is about instead of naming it.
- Numbers written the way a person says them. "Seventy six percent," not "76%."
- Attribution in line and up front. A trailing parenthetical does not survive being spoken.
- Shorter sentences than the written version, one idea each, since a listener cannot re-read.
- Signpost every transition, because there are no visual breaks to orient against.
- Repeat a name or number once where it carries weight.
- Open on the single most important thing, in the first sentence. No preamble.
- Second person if this is for me alone.
- Output plain text, blank line between paragraphs, no title line, no notes to me.

Before writing, look for anything in the document that already summarises the top item and use it
as the opening.

Save the script to a file. Report the word count and estimated spoken length (words divided by 155
equals minutes). Ask me to read it.

**STOP.** When I come back with corrections, update the rules above rather than patching the
script, and save them as a reusable spec file. I will run this repeatedly and the spec is the asset.

---

## Step 3. Narrate it

Work out the cost before generating anything. Count **characters**, not words, multiply by the per
character rate, then by 4.33 for a monthly figure if this repeats weekly. Compute this in code, not
in prose, and show me the arithmetic.

Render the whole script with a free option first. Do not spend money until I have heard a complete
episode. See 6.1 for the free path and 6.2 for the paid one.

**STOP.** I will listen to the whole thing.

---

## Step 4. Publish it as a private feed

A podcast is a folder of audio files on the internet plus one RSS file listing them. No podcast
host, no account, no database. See 6.3 and 6.4.

Requirements:

- Everything behind a long random path, so the address acts as the password.
- The site root returns nothing, so no page lists what is there.
- No blanket `Disallow: /` robots file. Podcast apps fetch feeds as bots and it will stop the feed
  being readable by anything. Use a `noindex` header instead.

Verify against the artifact, not against a success message. See 6.5.

---

## Step 5. Make it run on a schedule

Write a runbook first, in the order the steps happen, including what happens when the input is
missing or the episode already exists. Then point a scheduled task at it.

Put a cost limit in the runbook in writing so it is enforced every run: one AI call for the
rewrite, no sub agents, no web searches. Stay silent when there is nothing to do. Write one line to
a log every run whatever the outcome. Tell me how long a full run takes and record it, so a slow
run is never later mistaken for a broken one.

---

# 6. Reference implementation

This is what actually works, from a build that runs weekly. Substitute equivalents where my setup
differs.

## 6.1 Free narration, to prove the pipeline

macOS has a built in voice and needs no key or network:

```bash
say -v "Ava (Premium)" -o out.aiff -f script.txt
afconvert -f mp4f -d aac -b 64000 out.aiff out.m4a
```

On Linux use `espeak-ng` or `piper` and convert with `ffmpeg -i out.wav -c:a aac -b:a 64k out.m4a`.
Quality does not matter here. The point is one complete episode before any spend.

## 6.2 Paid narration with Gemini text to speech

Key from Google AI Studio. Roughly $0.96 for a 35 minute episode on
`gemini-3.1-flash-tts-preview`; `gemini-2.5-flash-preview-tts` is about half that and slightly
lower quality.

```
POST https://generativelanguage.googleapis.com/v1beta/models/<MODEL>:generateContent?key=<KEY>
Content-Type: application/json

{
  "contents": [{"parts": [{"text": "<STYLE PROMPT>\n\n<CHUNK TEXT>"}]}],
  "generationConfig": {
    "responseModalities": ["AUDIO"],
    "speechConfig": {"voiceConfig": {"prebuiltVoiceConfig": {"voiceName": "Rasalgethi"}}}
  }
}
```

The audio comes back base64 in `candidates[0].content.parts[0].inlineData.data` as **raw PCM,
24000 Hz, 16 bit, mono**. It is not a playable file yet.

Things that matter:

- **Chunk on paragraph boundaries, about 4,000 characters per request.** One request for a whole
  long script is slow and fragile.
- **Concatenate the raw PCM, then encode once at the end.** Joining already-encoded MP3 or AAC
  files is lossy and can produce broken durations. Joining PCM is just joining bytes.
- Write a 44 byte WAV header onto the joined PCM, then transcode once:
  `afconvert -f mp4f -d aac -b 64000 joined.wav episode.m4a` (or `ffmpeg`).
- A **style prompt** prepended to each chunk noticeably improves the read. Something like: "Read
  the following as a professional briefing for a single listener. Measured, warm, unhurried. Let
  the full stops land. Do not sound like an advertisement. Read only the text, add nothing."
- Retry on HTTP 429, 500 and 503 with a backoff. Fail loudly on anything else.
- Expect roughly 20 minutes of wall clock for a 35 minute episode. Tell me this before you start it
  so I do not think it has hung.
- Auditioning voices is worth 5 minutes: render the same 30 seconds in 3 or 4 voices and let me pick.

Other providers work the same shape. OpenAI's is about $2 to $4 a month at this volume.
**Check ElevenLabs arithmetic carefully:** its plans are quoted in *credits*, not characters, and
the fast models spend half a credit per character, so a 33,000 character weekly script needs about
72,000 credits a month and does not fit the entry plan.

## 6.3 The RSS feed

Minimum that real podcast apps accept. Every field here earns its place:

```xml
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd">
<channel>
  <title>…</title>
  <link>https://HOST/SECRET/</link>
  <description>…</description>
  <language>en-GB</language>
  <itunes:author>…</itunes:author>
  <itunes:explicit>false</itunes:explicit>
  <itunes:category text="Technology"/>
  <itunes:image href="https://HOST/SECRET/cover.png"/>
  <itunes:block>Yes</itunes:block>
  <item>
    <title>Week of 17 August 2026</title>
    <description>…</description>
    <pubDate>Mon, 18 Aug 2026 06:00:00 GMT</pubDate>
    <guid isPermaLink="false">brief-2026-34</guid>
    <enclosure url="https://HOST/SECRET/audio/brief-2026-34.m4a"
               length="17406049" type="audio/x-m4a"/>
    <itunes:duration>00:35:16</itunes:duration>
  </item>
</channel></rss>
```

- `enclosure length` is the **exact byte count**. A wrong value makes some apps refuse the file.
- `type` must match the container: `audio/x-m4a` for m4a, `audio/mpeg` for mp3.
- `pubDate` must be RFC 2822. In Python: `email.utils.formatdate(ts, usegmt=True)`.
- `guid` must be stable and unique per episode. Reuse it when republishing the same episode so apps
  update rather than duplicate.
- `itunes:duration` as HH:MM:SS. Get it from `afinfo` on macOS or `ffprobe` elsewhere.
- `itunes:block` asks directories not to list the show. It is a request, not enforcement, so the
  secret path is what actually protects it.
- Cover art 1400x1400 or larger, or some apps show a blank tile.
- Sort items newest first.

## 6.4 Hosting on Netlify

Any static host works. Netlify needs no repo and deploys from a folder.

```bash
netlify sites:create --name <random-name> --disable-linking
netlify deploy --dir <folder> --prod --no-build --site <SITE_ID>
```

- **`--no-build` is required.** A site created this way inherits a default build command and the
  deploy otherwise fails with exit 127 looking for a build tool that is not there. This cost me
  twenty minutes.
- Layout: put everything under a random path so nothing is guessable.

```
<folder>/
  robots.txt          User-agent: *\nDisallow:      <- permissive, on purpose
  _headers            /*\n  X-Robots-Tag: noindex, nofollow
  <32-hex-chars>/
    feed.xml
    cover.png
    audio/episode-1.m4a
```

- Generate the secret path with `secrets.token_hex(16)` or equivalent. Do not use a memorable one.
- Leave **no `index.html` at the root**, so the root returns 404 and nothing enumerates the site.
- Each `--prod` deploy replaces the whole site, so moving the secret path later automatically kills
  the old URLs. Useful if the address ever leaks.

## 6.5 Verification, before telling me it worked

Never report success from a tool's own exit code. Check the artifact:

```bash
curl -s -o /dev/null -w '%{http_code} %{size_download}\n' "$FEED"
curl -sI "$AUDIO_URL" | grep -iE '^HTTP|content-length|content-type'
curl -s "$FEED" | python3 -c "import sys,xml.etree.ElementTree as ET; \
c=ET.fromstring(sys.stdin.read()).find('channel'); \
print(c.findtext('title'), len(c.findall('item')), c.find('item/enclosure').get('url'))"
```

Confirm the audio `content-length` equals the `enclosure length` in the feed, and that the file
starts with real audio bytes rather than an error page.

Also check the audio is not silent, which a failed chunk will produce without any error:

```python
import audioop  # RMS per minute over the decoded PCM; none should be near zero
```

## 6.6 Subscribing

Add the feed by URL. Most Android apps take one directly. Apple Podcasts on iPhone has no add by
URL option, so add it on a Mac and let it sync, or use an app that supports it. Pocket Casts works
on both and syncs, and its Trim Silence and Volume Boost are worth turning on for synthesized
speech, though they are in the mobile app only and not the web player.

## 6.7 Failure modes actually hit on this build

- A URL typed with one character missing produced "cannot find feed," which was then confidently
  misdiagnosed as a robots.txt problem. **State what you observed before what you think caused it.**
- A "render finished" check written against the output file passed instantly because last week's
  file was still sitting there. **Wait on the process, not on a file that already exists.**
- A first cost estimate was wrong by 4x because credits were read as characters.
- A blanket `Disallow: /` robots file blocked podcast clients from fetching the feed at all.
