Amend podcast PRD after rubber-duck review
Applies rubber-duck feedback to the podcast PRD and execution plan. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Juan Manuel Servera committed
Jun 7, 2026 at 17:11 UTC
bcdbc2934170de3fcc6ca7c3606ef2da718204ff
3 files changed
+118
-94
.squad/agents/leela/history.md
+1
@@ -56,3 +56,4 @@
56
- P0 safety layer (#250/#251/#252/#253) must complete before #254 atomicity can start
57
- Issue sequencing finalized: contract/preflight (Phase 1) → atomicity/quality gates (Phase 2) → docs/evidence (Phase 3) → map/reduce stays dry-run (Phase 4)
58
- 2026-06-07T14:46:18.154+00:00 issue #297 podcast PRD note: select Signal Check as a reviewed two-host weekly podcast; use Azure Speech for MVP TTS, Blob Storage for audio, non-blocking generation, claim-ledger safety gates, AI disclosure, and cost guardrails.
59
+- 2026-06-07T15:00:15.655+00:00 issue #299 rubber-duck amendment: podcast MVP requires `podcast-review` environment approval before TTS, explicit post-publish dispatch, privacy-before-TTS, provider listening test, stable RSS enclosure URLs, Hugo shortcode embed, voice-only MVP, and total cost guardrails.
docs/PRD-podcast-generation.md
+50
-36
@@ -2,7 +2,7 @@
2
3
**Author:** Leela (Lead/Architect)
4
**Date:** 2026-06-07
5
-**Status:** Draft for issue #297
5
+**Status:** Accepted / amended after rubber-duck review for issue #299
6
**Type:** Product Requirements Document
7
**Depends on:** content/methodology/_index.md, content/privacy/_index.md, docs/analysis-spec.md, docs/pipeline-validation.md, hugo.toml
8
@@ -15,10 +15,10 @@ SquadScope should add a weekly short-form podcast that turns each published week
15
The MVP recommendation is:
16
17
1. Generate a human-reviewed script from the already-published weekly article and its source-backed evidence.
18
-2. Use **Azure AI Speech neural TTS** for synthesis by default because SquadScope already expects Azure-friendly operations, Azure Speech supports neural voices, SSML, batch synthesis, and billable-character pricing (https://learn.microsoft.com/en-us/azure/ai-services/speech-service/text-to-speech).
18
+2. Run the Phase 2 TTS proof of concept before full storage/RSS build-out: compare Azure Speech Standard neural voices, Azure Speech HD/OpenAI voices in Azure if available, and OpenAI `tts-1` or `gpt-4o-mini-tts`; choose the MVP provider from a documented listening test plus cost/privacy review.
19
3. Store MP3s, transcripts, show manifests, and RSS media metadata in **Azure Blob Storage**, which is designed for unstructured objects, streaming media, and HTTP(S) access (https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction).
20
4. Publish a podcast RSS feed at `/podcast/index.xml` and embed each episode in the matching weekly article only after gates pass.
21
-5. Keep podcast generation in a separate non-blocking workflow, `podcast-generate.yml`, triggered after weekly article publishing and by manual dispatch.
21
+5. Keep podcast generation in a separate non-blocking workflow, `podcast-generate.yml`, started by explicit dispatch from `crawl-and-publish.yml` only after a confirmed normal publish, plus manual dispatch for operators.
22
23
Do **not** commit MP3s to git. GitHub Pages has 1 GB site/repository guidance, a 100 GB monthly soft bandwidth limit, and is not intended as free business or CDN hosting (https://docs.github.com/en/pages/getting-started-with-github-pages/github-pages-limits). Pages can host the RSS XML and player pages; object storage should host audio.
24
@@ -49,7 +49,7 @@ This PRD defines the recommended editorial format, technical path, safety gates,
49
- Make episodes useful and entertaining: dynamic, conversational, lightly funny, and technically grounded.
50
- Preserve SquadScope's source-backed methodology, correction path, and no-paid-placement editorial stance unless explicitly changed and disclosed.
51
- Require a claim ledger, source-backed show notes, and human review for the MVP.
52
-- Use low-cost, automatable TTS with future quality experiments isolated from MVP reliability.
52
+- Use low-cost, automatable TTS selected by an early Phase 2 listening-test comparison before storage/RSS implementation.
53
- Publish standards-compliant podcast RSS with stable episode identity.
54
- Avoid blocking weekly article publishing when podcast generation fails.
55
- Define safe monetization phases that protect reader/listener trust.
@@ -121,7 +121,7 @@ published weekly article
121
-> episode outline
122
-> two-host conversational script
123
-> citation and fact check
124
- -> editorial/safety review
124
+ -> GitHub Environment podcast-review approval
125
-> final script
126
-> TTS synthesis
127
-> ffmpeg post-process
@@ -133,7 +133,7 @@ published weekly article
133
### Required artifacts per episode
134
135
- `episode_manifest.json`: week, article URL, article hash, script prompt version, voice config hash, TTS provider, duration, file length, cost, license/disclosure status, publish status.
136
-- `claim_ledger.json`: every substantive claim, source URL, article paragraph/source, support status, and reviewer decision.
136
+- `claim_ledger.json`: every substantive claim derived from the published article, source URL, article paragraph/source, validation status against existing analysis/publish artifacts where available, support status, and reviewer decision.
137
- `script.md`: final reviewed script.
138
- `transcript.txt` or `transcript.md`: public transcript generated from the final script.
139
- `show_notes.md`: article link, source URLs, AI disclosure, corrections link, sponsor/affiliate disclosures if any.
@@ -141,6 +141,12 @@ published weekly article
141
142
If future implementation stores manifests under `data/`, `hugo.toml` must add explicit module mounts because this repo uses custom data mounts.
143
144
+
145
+### Human review mechanism
146
+
147
+MVP uses a single concrete review mechanism: the GitHub Environment `podcast-review` with required reviewers. `podcast-generate.yml` uploads the generated script package as workflow artifacts, then pauses before any non-dry-run TTS call by entering the `podcast-review` environment. TTS cannot proceed until the environment approval is granted. Reviewers inspect `script.md`, `claim_ledger.json`, `show_notes.md`, `transcript.md`, and `episode_manifest.json`, with attention to source support, disclosure, tone, privacy readiness, and budget status.
148
+
149
+
150
---
151
152
## Options Considered
@@ -158,9 +164,9 @@ If future implementation stores manifests under `data/`, `hugo.toml` must add ex
164
165
| Option | Pros | Cons | Decision |
166
| --- | --- | --- | --- |
161
-| Azure AI Speech neural TTS | Mature SDK/REST, neural voices, SSML, batch synthesis, enterprise auth, billable characters (https://learn.microsoft.com/en-us/azure/ai-services/speech-service/text-to-speech) | Voice quality may be less expressive than newest generative voices | **Default MVP** |
162
-| Azure OpenAI voices via Azure Speech | Higher-quality experiment path, Azure operational surface, OpenAI voices/formats with SSML differences (https://learn.microsoft.com/en-us/azure/ai-services/speech-service/openai-voices) | Availability/format differences; should not block MVP | Experiment after MVP |
163
-| OpenAI `gpt-4o-mini-tts` | Strong controllability and built-in voices; docs require disclosure that the voice is AI-generated (https://developers.openai.com/api/docs/guides/text-to-speech) | Another provider/privacy path; commercial terms must be reviewed | Experiment after MVP |
167
+| Azure Speech Standard neural TTS | Mature SDK/REST, neural voices, SSML, batch synthesis, enterprise auth, billable characters (https://learn.microsoft.com/en-us/azure/ai-services/speech-service/text-to-speech) | Voice quality may be less expressive than newest generative voices | Include in Phase 2 POC |
168
+| Azure Speech HD/OpenAI voices in Azure, if available | Higher-quality experiment path, Azure operational surface, OpenAI voices/formats with SSML differences (https://learn.microsoft.com/en-us/azure/ai-services/speech-service/openai-voices) | Availability/format differences; privacy/commercial terms must be confirmed | Include in Phase 2 POC |
169
+| OpenAI `tts-1` or `gpt-4o-mini-tts` | Strong controllability and built-in voices; docs require disclosure that the voice is AI-generated (https://developers.openai.com/api/docs/guides/text-to-speech) | Another provider/privacy path; commercial terms must be reviewed | Include in Phase 2 POC |
170
| MAI Voice or other providers | Potential quality upside | Unknown licensing/availability/cost in this repo | Research later |
171
| Human narration | Best disclosure simplicity | Cost and cadence burden | Defer |
172
@@ -171,7 +177,7 @@ If future implementation stores manifests under `data/`, `hugo.toml` must add ex
177
| Commit MP3s to git/Pages | Very simple URLs | Bloats repo; Pages limits; not CDN/business hosting (https://docs.github.com/en/pages/getting-started-with-github-pages/github-pages-limits) | Reject |
178
| Azure Blob Storage | Designed for unstructured data, audio/video streaming, HTTP(S) access (https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction) | Requires storage account/secrets/CORS/headers | **Select** |
179
| External podcast host | Turnkey analytics/distribution | Cost, lock-in, privacy review | Defer |
174
-| CDN in front of Blob | Better scaling | Not needed for MVP | Optional later |
180
+| CDN in front of Blob | Better scaling and stable public URLs | Extra configuration | Optional later; acceptable if URLs are stable |
181
182
---
183
@@ -181,8 +187,10 @@ If future implementation stores manifests under `data/`, `hugo.toml` must add ex
187
188
- Add a future separate workflow: `.github/workflows/podcast-generate.yml`.
189
- Trigger modes:
184
- - `workflow_run` after successful weekly article publish.
185
- - `workflow_dispatch` with selected week and optional dry-run flag.
190
+ - Preferred automated path: after `crawl-and-publish.yml` confirms a normal weekly publish, it explicitly dispatches `podcast-generate.yml` with the week, article path/URL, source run ID, publish mode, and article artifact identifiers.
191
+ - `workflow_dispatch` with selected week and optional dry-run flag for operators.
192
+- Do not rely on a naive `workflow_run` trigger. If a future implementation uses `workflow_run`, it must fetch and validate the triggering run inputs/artifacts and prove the run was a normal publish before synthesis.
193
+- Dry-run, candidate-only, restore, failed, or no-AI fallback replacement runs must not synthesize audio or publish podcast artifacts.
194
- Podcast failures must not block article publishing.
195
- Manual reruns must be idempotent.
196
@@ -194,7 +202,7 @@ Episode generation identity is:
202
week + article_hash + script_prompt_version + voice_config_hash
203
```
204
197
-If the key is unchanged, reruns should reuse the existing reviewed script/audio unless explicitly forced. If the article changes materially, regenerate the claim ledger and require review again.
205
+If the key is unchanged, reruns should reuse the existing reviewed script/audio unless explicitly forced. If the article changes materially, derive a new podcast claim ledger from the published article, validate those claims against available analysis/publish artifacts, and require review again.
206
207
### Audio requirements
208
@@ -214,23 +222,29 @@ Apple's podcast requirements include RSS 2.0, a public feed, correct enclosure U
222
223
- `/podcast/index.xml` generated from manifests.
224
- One stable GUID per episode.
217
-- `enclosure` with public HTTPS URL, exact byte length, and `audio/mpeg` type.
225
+- `enclosure` with stable public HTTPS URL, exact byte length, and `audio/mpeg` type.
226
- RFC 2822 publication date.
227
- ASCII-only filenames and URLs.
228
- Feed/image metadata ready for podcast directories.
229
- Validation that the audio endpoint supports `HEAD` and byte-range requests before publishing.
230
+- Enclosure URLs must use public anonymous Blob read access or CDN-backed stable public URLs; expiring SAS URLs are explicitly prohibited for RSS enclosures.
231
+- If MVP writes `static/podcast/index.xml`, it must not coexist ambiguously with a future Hugo `content/podcast/` section. If a Hugo podcast section is added, remove or replace static XML generation so only one feed owner exists.
232
+
233
+### Hugo article embed
234
+
235
+Weekly articles must use a Hugo `podcast-episode` shortcode/partial, not raw HTML. The embed renders the audio player, transcript link, show notes link, correction path, and AI-generated voice disclosure while leaving Hugo `unsafe = false` unchanged.
236
237
### Cost controls
238
225
-Expected scripts are about 4,500-9,000 billable characters for 5-10 minutes, which keeps Azure Speech neural TTS and likely OpenAI/Azure OpenAI mini TTS at cents-level per episode, depending on provider, region, and free allowance. Annual weekly TTS should likely stay under $10, but operations must track actual costs.
239
+Expected scripts are about 4,500-9,000 billable characters for 5-10 minutes, which keeps many TTS options at cents-level per episode, depending on provider, region, and free allowance. Operations must track total podcast cost, not just TTS: TTS, storage, egress/download bandwidth, script generation, validation, and any CDN charges. Egress may dominate if downloads grow.
240
241
MVP guardrails:
242
243
- Max 5 episodes per month.
230
-- Max $5/month TTS budget.
244
+- Max $5/month total podcast budget for TTS, storage, egress, script generation, validation, and CDN if used.
245
- Max 10 minutes per episode.
232
-- Cost ledger entry per episode.
233
-- Workflow fails closed before synthesis when limits would be exceeded.
246
+- Cost ledger entry per episode with per-category estimates/actuals.
247
+- Workflow fails closed before non-dry-run synthesis or publish when limits would be exceeded.
248
249
---
250
@@ -243,13 +257,13 @@ No public episode may ship unless all are true:
257
1. AI voice disclosure appears in the first 60 seconds and show notes. OpenAI's TTS guide explicitly requires clear disclosure that TTS voice is AI-generated and not human (https://developers.openai.com/api/docs/guides/text-to-speech).
258
2. Paid/commercial-use voice license is documented for the selected provider and voices.
259
3. No real-person voice cloning, no celebrity/podcast-host imitation, and no Hard Fork/NYT marks or copied expression.
246
-4. Music/SFX are absent or licensed for commercial podcast use, with license recorded.
247
-5. Human script review is complete before synthesis for MVP.
260
+4. MVP is voice-only: no music or SFX unless/until a licensed track/effect is selected and recorded in the manifest. Licensed music/SFX is a later issue with explicit acceptance criteria.
261
+5. Human script review is complete through the GitHub Environment `podcast-review` gate before synthesis for MVP.
262
6. Claim ledger shows every factual claim is supported or removed.
263
7. Show notes include source URLs and corrections link.
264
8. No unsupported facts, no defamatory motive claims, and no fake sponsorship language.
265
9. Sponsorship/affiliate disclosures appear before any sponsor or affiliate segment.
252
-10. Privacy policy is updated before using voice providers, podcast analytics, ad tech, or payment redirects.
266
+10. Privacy policy is updated before any non-dry-run TTS call or before using podcast analytics, ad tech, or payment redirects.
267
11. GDPR/cookie consent covers non-essential podcast analytics before analytics tags or third-party players are enabled.
268
269
### FTC and monetization compliance
@@ -263,7 +277,7 @@ FTC endorsement guidance requires endorsements and ads to be honest, not mislead
277
278
### Privacy requirements
279
266
-Before launch with production providers, update `content/privacy/_index.md` to document:
280
+Before Phase 2 performs any non-dry-run TTS call, update `content/privacy/_index.md` to document the selected voice-provider candidates and data flow. This privacy update is a prerequisite to Phase 2, not a launch cleanup item. Also update it before analytics, ad tech, or payment redirects are enabled to document:
281
282
- Voice provider(s), data sent, retention, and region if configurable.
283
- Audio hosting provider and logs.
@@ -306,11 +320,11 @@ SquadScope should not store payment data. Use Stripe, PayPal, Ko-fi, Patreon, or
320
- Two-host script generated from article plus existing evidence artifacts.
321
- Claim ledger and source-backed show notes.
322
- Human review before synthesis.
309
-- Azure AI Speech neural TTS.
323
+- TTS provider selected after a Phase 2 listening-test POC.
324
- MP3 post-processing and Blob Storage hosting.
325
- Podcast RSS feed and weekly article embed.
326
- AI voice disclosure and correction path.
313
-- Cost ledger and monthly budget guardrail.
327
+- Cost ledger and monthly budget guardrail covering TTS, storage, egress, script generation, validation, and CDN if used.
328
- Manual dispatch and post-publish workflow trigger.
329
330
### Out of scope for MVP
@@ -322,7 +336,7 @@ SquadScope should not store payment data. Use Stripe, PayPal, Ko-fi, Patreon, or
336
- Listener analytics beyond basic hosting logs, unless privacy/consent work is done.
337
- Fully automated publish without human review.
338
- Voice cloning or real-person mimicry.
325
-- CDN optimization.
339
+- CDN optimization, unless needed for stable public enclosure URLs.
340
341
---
342
@@ -330,7 +344,7 @@ SquadScope should not store payment data. Use Stripe, PayPal, Ko-fi, Patreon, or
344
345
### Product acceptance
346
333
-- A reviewer can listen to a generated episode and map every substantive factual claim to the article, claim ledger, or cited source.
347
+- A reviewer can inspect the reviewed script artifact, claim ledger, show notes, transcript, manifest, and generated audio, then map every substantive factual claim to the published article and cited source/artifact validation.
348
- Episode length fits the 8-12 minute show format, and automated MVP runs are no more than 10 minutes unless manually overridden.
349
- The show sounds distinct from copied podcasts and uses the approved `Signal Check` structure.
350
- Jokes clarify or compress analysis without adding unsupported claims or targeting individuals unfairly.
@@ -343,16 +357,16 @@ SquadScope should not store payment data. Use Stripe, PayPal, Ko-fi, Patreon, or
357
- Article publishing succeeds even if podcast generation fails.
358
- MP3 is hosted outside git, under the size/duration/audio constraints.
359
- RSS validates against Apple-style requirements: public RSS 2.0, stable GUID, enclosure URL/length/type, RFC 2822 date, ASCII URL, HEAD and byte-range support (https://podcasters.apple.com/support/823-podcast-requirements).
346
-- Cost ledger records provider, character count, duration, estimated/actual cost, and monthly budget status.
360
+- Cost ledger records provider, character count, duration, TTS/storage/egress/script-generation/validation/CDN estimated or actual costs, and monthly budget status.
361
- Secrets are not logged or committed.
362
363
### Safety acceptance
364
351
-- Human reviewer approval is recorded before synthesis.
365
+- Human reviewer approval is recorded via GitHub Environment `podcast-review` before synthesis; reviewers inspect `script.md`, `claim_ledger.json`, `show_notes.md`, `transcript.md`, and `episode_manifest.json` before allowing TTS.
366
- Provider voice license and AI disclosure are documented.
367
- No real-person voice cloning or protected podcast imitation occurs.
368
- Sponsorship/affiliate text, if present, is disclosed before the relevant segment.
355
-- Privacy policy changes are merged before production analytics/providers beyond current hosting are enabled.
369
+- Privacy policy changes are merged before any non-dry-run TTS call and before production analytics/providers beyond current hosting are enabled.
370
371
---
372
@@ -360,11 +374,11 @@ SquadScope should not store payment data. Use Stripe, PayPal, Ko-fi, Patreon, or
374
375
1. **Design and contracts:** Define manifest, claim ledger, show notes, RSS fields, storage naming, and review statuses.
376
2. **Script generation dry run:** Generate script/ledger/show notes from existing weekly articles without TTS or publishing.
363
-3. **TTS proof of concept:** Synthesize reviewed scripts with Azure Speech in a private artifact path; measure cost, duration, and quality.
377
+3. **TTS proof of concept:** After the privacy update, synthesize reviewed private samples with Azure Speech Standard, Azure Speech HD/OpenAI voices in Azure if available, and OpenAI `tts-1` or `gpt-4o-mini-tts`; run a listening test and select the provider before full storage/RSS infrastructure.
378
4. **Audio hosting and RSS:** Upload approved MP3s to Blob Storage, generate `/podcast/index.xml`, and validate enclosures.
365
-5. **Article embed:** Add a Hugo partial/shortcode or content data path to embed the latest episode on weekly articles.
379
+5. **Article embed:** Add a Hugo `podcast-episode` shortcode/partial so weekly articles can render audio player, transcript link, show notes, and AI disclosure while keeping `unsafe = false` unchanged.
380
6. **MVP launch:** Enable non-blocking post-publish podcast workflow with human review gate.
367
-7. **Quality experiments:** Compare Azure OpenAI/OpenAI `gpt-4o-mini-tts` or MAI Voice against Azure Speech after MVP reliability is proven.
381
+7. **Quality experiments:** Revisit voices, providers, music/SFX, and production polish after the Phase 2 provider decision and MVP reliability are proven.
382
8. **Monetization experiments:** Add support/donation links first; defer ads/premium until disclosure, privacy, and audience metrics justify them.
383
384
---
@@ -372,9 +386,9 @@ SquadScope should not store payment data. Use Stripe, PayPal, Ko-fi, Patreon, or
386
## Open Questions
387
388
- Which Azure region and Speech resource should be used for production?
375
-- Which two neural voices best represent Curator and Skeptic while avoiding real-person mimicry?
376
-- Should script review happen through a GitHub PR, issue checklist, environment approval, or repository artifact approval file?
377
-- Should RSS be generated by Hugo from data files or by a standalone script that writes static XML?
389
+- Which provider/voice pair wins the Phase 2 listening test while avoiding real-person mimicry?
390
+- Which required reviewers should be configured on the GitHub Environment `podcast-review`?
391
+- Should RSS stay as standalone `static/podcast/index.xml`, or should a future Hugo `content/podcast/` section replace static XML generation?
392
- What public podcast cover art should be used, and does it require a new design asset?
393
- What is the minimum listener metric needed before monetization moves beyond donations?
394
@@ -382,4 +396,4 @@ SquadScope should not store payment data. Use Stripe, PayPal, Ko-fi, Patreon, or
396
397
## Decision
398
385
-Proceed with a docs-only design now. For implementation, build **SquadScope: Signal Check** as a human-reviewed, source-backed, two-host weekly podcast using Azure AI Speech neural TTS by default, Blob Storage for audio, a separate non-blocking generation workflow, and strict disclosure/safety/cost gates. Defer higher-quality TTS experiments, analytics, ads, premium feeds, and full automation until the MVP proves reliable and trustworthy.
399
+Proceed with this amended docs-only design. For implementation, build **SquadScope: Signal Check** as a human-reviewed, source-backed, two-host weekly podcast using the GitHub Environment `podcast-review` gate, explicit post-publish dispatch from `crawl-and-publish.yml`, a Phase 2 TTS listening-test provider decision, stable public Blob/CDN enclosure URLs, a Hugo `podcast-episode` shortcode/partial, and strict disclosure/privacy/safety/cost gates. Defer analytics, ads, premium feeds, music/SFX, and full automation until the MVP proves reliable and trustworthy.
docs/podcast-generation-plan.md
+67
-58
@@ -9,7 +9,7 @@
9
10
## Recommendation Snapshot
11
12
-Build **SquadScope: Signal Check**, an 8-12 minute weekly two-host podcast generated from the published weekly article; automated MVP runs should target 8-10 minutes to honor the cost guardrail. Use Azure AI Speech neural TTS for MVP, Azure Blob Storage for MP3s/transcripts/manifests, and a separate non-blocking `podcast-generate.yml` workflow. Require claim ledger, source-backed show notes, AI voice disclosure, human review, cost guardrails, and RSS validation before public publish.
12
+Build **SquadScope: Signal Check**, an 8-12 minute weekly two-host podcast generated from the published weekly article; automated MVP runs should target 8-10 minutes to honor the cost guardrail. Select the TTS provider in Phase 2 after a listening-test POC comparing Azure Speech Standard, Azure Speech HD/OpenAI voices in Azure if available, and OpenAI `tts-1` or `gpt-4o-mini-tts`; use Azure Blob Storage or CDN-backed stable public URLs for MP3s/transcripts/manifests and a separate non-blocking `podcast-generate.yml` workflow. Require claim ledger, source-backed show notes, AI voice disclosure, GitHub Environment review, cost guardrails, and RSS validation before public publish.
13
14
Key references: Azure Speech TTS (https://learn.microsoft.com/en-us/azure/ai-services/speech-service/text-to-speech), Azure Blob Storage (https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction), GitHub Pages limits (https://docs.github.com/en/pages/getting-started-with-github-pages/github-pages-limits), Apple podcast requirements (https://podcasters.apple.com/support/823-podcast-requirements), OpenAI TTS disclosure guidance (https://developers.openai.com/api/docs/guides/text-to-speech), FTC endorsement guidance (https://www.ftc.gov/business-guidance/resources/ftcs-endorsement-guides-what-people-are-asking).
15
@@ -19,27 +19,28 @@ Key references: Azure Speech TTS (https://learn.microsoft.com/en-us/azure/ai-ser
19
20
- Do not commit MP3s or generated audio binaries to git.
21
- Podcast failures must not block weekly article publishing.
22
-- MVP generation must require human script review before TTS.
22
+- MVP generation must require GitHub Environment `podcast-review` approval before TTS.
23
- Every substantive claim must be source-backed in a claim ledger.
24
- Disclose AI-generated voices in the first 60 seconds and in show notes.
25
- Do not clone or mimic real people, Hard Fork hosts, NYT branding, segment names, jingles, or phrasing.
26
-- Keep MVP to max 5 episodes/month, max $5/month TTS, and max 10 minutes/episode.
26
+- Keep MVP to max 5 episodes/month, max $5/month total podcast spend, and max 10 minutes/episode.
27
- If new Hugo data paths are added, update `hugo.toml` module mounts explicitly.
28
-- Update privacy policy before adding voice providers, podcast analytics, payment providers, or non-essential tracking.
28
+- Update privacy policy before any non-dry-run TTS provider call and before adding podcast analytics, payment providers, or non-essential tracking.
29
30
---
31
32
## Target Architecture
33
34
```text
35
-crawl-and-publish.yml publishes weekly article
36
- -> podcast-generate.yml starts after success or manual dispatch
35
+crawl-and-publish.yml confirms normal weekly publish
36
+ -> explicitly dispatch podcast-generate.yml with publish inputs, or operator manual dispatch
37
-> load content/weekly/YYYY/Www.md and source artifacts
38
+ -> reject dry-run/candidate-only/restore/no-AI fallback replacement modes
39
-> compute article hash and idempotency key
39
- -> extract claims and citations
40
+ -> derive claims from published article and validate against available artifacts
41
-> generate outline, script, show notes, transcript
41
- -> human review gate
42
- -> Azure Speech TTS
42
+ -> GitHub Environment podcast-review gate
43
+ -> selected TTS provider synthesis
44
-> ffmpeg normalize/transcode
45
-> upload MP3/transcript/manifest to Azure Blob Storage
46
-> generate/update /podcast/index.xml
@@ -71,10 +72,10 @@ week + article_hash + script_prompt_version + voice_config_hash
72
- Host A Curator, Host B Skeptic.
73
- Acceptance: prompt requires citations, bans unsupported facts, bans copied podcast expression, and includes joke safety rules.
74
74
-3. **Choose review mechanism**
75
- - Options: PR-based reviewed script, GitHub environment approval, issue checklist, or signed manifest.
76
- - Recommendation: PR or issue checklist for MVP because it is auditable and easy to operate.
77
- - Acceptance: reviewer approval is machine-readable before synthesis.
75
+3. **Configure GitHub Environment `podcast-review`**
76
+ - Use required reviewers as the MVP review mechanism.
77
+ - Workflow uploads `script.md`, `claim_ledger.json`, `show_notes.md`, `transcript.md`, and `episode_manifest.json` for inspection, then pauses before non-dry-run TTS.
78
+ - Acceptance: TTS cannot proceed without environment approval, and approval identity/time is recorded in the manifest.
79
80
---
81
@@ -86,7 +87,7 @@ week + article_hash + script_prompt_version + voice_config_hash
87
88
4. **Implement claim/source extraction**
89
- Input: published weekly article plus available source artifacts.
89
- - Output: claim ledger with support status and source URLs.
90
+ - Output: claim ledger derived from the published article, then validated against existing analysis/publish artifacts where available, with support status and source URLs.
91
- Acceptance: unsupported claims fail the gate or are marked for removal.
92
93
5. **Generate episode outline and script**
@@ -108,28 +109,27 @@ week + article_hash + script_prompt_version + voice_config_hash
109
110
## Phase 2: TTS Proof of Concept
111
111
-**Goal:** Synthesize private test episodes cheaply and repeatably.
112
+**Goal:** Select the TTS provider using private samples before full storage/RSS infrastructure.
113
114
### Issues
115
115
-8. **Configure Azure Speech credentials and voice config**
116
- - Use repository secrets for keys/region or managed identity if available.
117
- - Document selected voices and commercial license status.
118
- - Acceptance: no secrets in logs; voice config hash included in manifest.
116
+8. **Update privacy policy for TTS candidates before synthesis**
117
+ - Document candidate voice providers, data sent, retention/region if configurable, and operator-facing disclosure before any non-dry-run TTS call.
118
+ - Acceptance: privacy PR is merged before provider credentials are used for non-dry-run synthesis.
119
120
-9. **Add Azure Speech synthesis step**
121
- - Use neural TTS with SSML for two voices.
122
- - Track billable characters as Azure Speech bills by characters (https://learn.microsoft.com/en-us/azure/ai-services/speech-service/text-to-speech).
123
- - Acceptance: private MP3 artifact generated from reviewed script.
120
+9. **Run TTS provider listening-test POC**
121
+ - Compare Azure Speech Standard neural voices, Azure Speech HD/OpenAI voices in Azure if available, and OpenAI `tts-1` or `gpt-4o-mini-tts`.
122
+ - Use the same reviewed sample script, record billable characters, latency, licensing/privacy notes, voice naturalness, pronunciation, SSML/control support, and estimated total monthly cost.
123
+ - Acceptance: provider decision is documented before storage/RSS implementation; private MP3 artifacts are generated only after `podcast-review` approval.
124
125
10. **Post-process audio with ffmpeg**
126
- Mono, 44.1 kHz, 64-96 kbps, `audio/mpeg`, target -16 LUFS, under 10 MB.
127
- Acceptance: validation reports duration, loudness, sample rate, bitrate, channels, and byte length.
128
129
11. **Add cost ledger and guardrails**
130
- - Track per-episode and monthly cost.
131
- - Enforce max 5 episodes/month, max $5/month, max 10 minutes.
132
- - Acceptance: workflow fails before synthesis if limits would be exceeded.
130
+ - Track per-episode and monthly TTS, storage, egress/download bandwidth, script generation, validation, and CDN costs if used.
131
+ - Enforce max 5 episodes/month, max $5/month total podcast spend, max 10 minutes; call out that egress may dominate if downloads grow.
132
+ - Acceptance: workflow fails before non-dry-run synthesis or publish if limits would be exceeded.
133
134
---
135
@@ -142,8 +142,8 @@ week + article_hash + script_prompt_version + voice_config_hash
142
12. **Provision Azure Blob Storage path conventions**
143
- Example: `podcast/YYYY/Www/squadscope-signal-check-YYYY-Www.mp3`.
144
- ASCII filenames only.
145
- - Configure content type `audio/mpeg` and public or signed access strategy.
146
- - Acceptance: uploaded MP3 is reachable over HTTPS.
145
+ - Configure content type `audio/mpeg` and stable public anonymous read access, or CDN-backed stable public URLs. Expiring SAS URLs are prohibited for RSS enclosures.
146
+ - Acceptance: uploaded MP3 is reachable over stable HTTPS without expiring query credentials.
147
148
13. **Validate HTTP serving requirements**
149
- Apple requires public feed assets, HEAD, and byte-range support for episodes (https://podcasters.apple.com/support/823-podcast-requirements).
@@ -153,11 +153,10 @@ week + article_hash + script_prompt_version + voice_config_hash
153
- Output `/podcast/index.xml` with RSS 2.0, stable GUID, enclosure URL/length/type, RFC 2822 pubDate, title, description, show image, transcript/show-note links.
154
- Acceptance: feed validates locally and with at least one podcast-feed validator.
155
156
-15. **Decide feed generation location**
157
- - Option A: Hugo template reads mounted data files.
158
- - Option B: standalone script writes static XML under `static/podcast/index.xml`.
159
- - Recommendation: start with standalone static XML for isolated MVP; revisit Hugo data if templates need richer integration.
160
- - Acceptance: no missing module mounts if data files are added.
156
+15. **Generate static feed without future Hugo section conflict**
157
+ - Start with standalone static XML under `static/podcast/index.xml` for isolated MVP.
158
+ - Document that this must not conflict with a future Hugo `content/podcast/` section; if that section is added, remove or replace static XML generation so one feed owner remains.
159
+ - Acceptance: no missing module mounts if data files are added, and build/feed validation proves only one `/podcast/index.xml` owner exists.
160
161
---
162
@@ -167,10 +166,10 @@ week + article_hash + script_prompt_version + voice_config_hash
166
167
### Issues
168
170
-16. **Add article embed**
169
+16. **Add Hugo `podcast-episode` shortcode/partial**
170
- Add audio player or link block to matching weekly article after episode publish.
171
- Include transcript, show notes, AI voice disclosure, and correction link.
173
- - Acceptance: weekly article renders without raw HTML unsafe mode changes.
172
+ - Acceptance: weekly article renders through the shortcode/partial while `unsafe = false` remains unchanged.
173
174
17. **Add podcast landing page**
175
- Explain show format, disclosure, feed link, correction path, and methodology.
@@ -178,8 +177,8 @@ week + article_hash + script_prompt_version + voice_config_hash
177
178
18. **Update methodology and privacy pages**
179
- Methodology: explain article-to-podcast transformation and safety gates.
181
- - Privacy: document voice provider, Blob hosting logs, analytics/payment providers if used.
182
- - Acceptance: public docs match actual providers and data flows.
180
+ - Privacy: document selected voice provider before non-dry-run TTS, Blob hosting logs, analytics/payment providers if used.
181
+ - Acceptance: public docs match actual providers and data flows before launch.
182
183
---
184
@@ -189,14 +188,17 @@ week + article_hash + script_prompt_version + voice_config_hash
188
189
### Issues
190
192
-19. **Create `podcast-generate.yml`**
193
- - Trigger on successful article publish via `workflow_run` and manual `workflow_dispatch` for selected week.
191
+19. **Create `podcast-generate.yml` with safe triggering**
192
+ - Preferred automated trigger: `crawl-and-publish.yml` explicitly dispatches `podcast-generate.yml` only after confirmed normal publish, passing week, article path/URL, source run ID, publish mode, and artifact identifiers.
193
+ - Include manual `workflow_dispatch` for selected week. Do not naively use `workflow_run`; if used later, fetch and verify triggering run inputs/artifacts before synthesis.
194
+ - Dry-run, candidate-only, restore, failed, or no-AI fallback replacement runs must not synthesize audio.
195
- Must be non-blocking relative to `crawl-and-publish.yml`.
195
- - Acceptance: failed podcast run does not fail or roll back article publishing.
196
+ - Acceptance: failed podcast run does not fail or roll back article publishing, and unsafe publish modes are rejected before TTS.
197
197
-20. **Add manual approval gate**
198
- - Require reviewed script status before TTS.
199
- - Acceptance: unreviewed scripts stop before synthesis.
198
+20. **Add GitHub Environment `podcast-review` gate**
199
+ - Require environment approval after script generation and before TTS.
200
+ - Reviewers inspect `script.md`, `claim_ledger.json`, `show_notes.md`, `transcript.md`, and `episode_manifest.json`.
201
+ - Acceptance: unapproved scripts stop before synthesis.
202
203
21. **Add observability**
204
- Log episode key, provider, duration, cost estimate, byte length, validation results, and publish URL.
@@ -216,9 +218,9 @@ week + article_hash + script_prompt_version + voice_config_hash
218
219
### Issues
220
219
-23. **Compare higher-quality TTS providers**
220
- - Test Azure OpenAI voices via Azure Speech (https://learn.microsoft.com/en-us/azure/ai-services/speech-service/openai-voices) and OpenAI `gpt-4o-mini-tts` (https://developers.openai.com/api/docs/guides/text-to-speech) against Azure Speech baseline.
221
- - Acceptance: compare cost, license/disclosure, latency, voice quality, SSML/control, and privacy terms.
221
+23. **Add licensed music/SFX later, if desired**
222
+ - MVP is voice-only. Select a licensed track/effect only in a later issue, and record license details in the episode manifest.
223
+ - Acceptance: no music/SFX ships until license, attribution/disclosure, loop/edit rules, and reviewer approval criteria are documented.
224
225
24. **Add support/donation links**
226
- Ko-fi/Patreon/PayPal/Stripe redirects only; do not store payment data.
@@ -234,6 +236,10 @@ week + article_hash + script_prompt_version + voice_config_hash
236
237
---
238
239
+## Implementation Issue Creation Gate
240
+
241
+Before implementation begins, create or update GitHub issues for every P0/P1 row below. This correction pass only amends the docs; it does not need to create the implementation issues. The P0/P1 set must include the rubber-duck P0s: `podcast-review` environment approval, safe explicit dispatch/trigger gating, privacy-before-TTS sequencing, Phase 2 provider comparison, stable enclosure URL strategy, Hugo shortcode/partial embed, static RSS/Hugo section conflict guard, voice-only MVP/music licensing deferral, claim-ledger derivation/validation, and total podcast cost guardrails.
242
+
243
## Suggested Issue Breakdown
244
245
| Priority | Issue title | Owner | Depends on |
@@ -242,16 +248,18 @@ week + article_hash + script_prompt_version + voice_config_hash
248
| P0 | Define Signal Check editorial prompt and safety style guide | Farnsworth/Hermes | PRD approval |
249
| P0 | Implement claim ledger extraction and validation | Farnsworth/Fry | schemas |
250
| P0 | Generate dry-run scripts/show notes/transcripts | Farnsworth | prompt, ledger |
245
-| P0 | Add human review gate for podcast scripts | Hermes/Leela | dry run |
246
-| P1 | Add Azure Speech synthesis proof of concept | Bender | review gate |
251
+| P0 | Configure GitHub Environment `podcast-review` gate | Hermes/Leela | dry run |
252
+| P1 | Update privacy policy for TTS provider candidates | Hermes/Leela | prompt, ledger |
253
+| P1 | Run TTS provider listening-test POC and select provider | Bender/Farnsworth | privacy update, review gate |
254
+| P1 | Add safe explicit podcast workflow dispatch gating | Hermes/Bender | dry run |
255
| P1 | Add ffmpeg audio validation and normalization | Bender/Fry | TTS POC |
248
-| P1 | Add cost ledger and monthly guardrails | Hermes/Bender | TTS POC |
249
-| P1 | Upload podcast assets to Azure Blob Storage | Bender | audio validation |
250
-| P1 | Generate and validate podcast RSS | Bender/Fry | storage |
251
-| P1 | Add weekly article episode embed | Bender | RSS/storage |
256
+| P1 | Add total podcast cost ledger and monthly guardrails | Hermes/Bender | TTS POC |
257
+| P1 | Upload podcast assets with stable public Blob/CDN URLs | Bender | audio validation |
258
+| P1 | Generate static podcast RSS without Hugo section conflict | Bender/Fry | storage |
259
+| P1 | Add Hugo podcast-episode shortcode/partial embed | Bender | RSS/storage |
260
| P1 | Update methodology and privacy pages for podcast launch | Hermes/Leela | provider choices |
261
| P2 | Pilot one back-catalog episode | Leela/Farnsworth | P1 complete |
254
-| P2 | Evaluate OpenAI/Azure OpenAI/MAI voice quality | Bender | MVP pilot |
262
+| P2 | Add licensed music/SFX only after manifest license design | Farnsworth/Hermes | MVP pilot |
263
| P2 | Add support/donation links | Hermes | privacy update |
264
| P3 | Evaluate ads, premium feed, or live events | Hermes/Leela | audience metrics |
265
@@ -261,7 +269,7 @@ week + article_hash + script_prompt_version + voice_config_hash
269
270
- Phase 0 is complete when schemas, prompts, and review status are documented well enough for implementation without product ambiguity.
271
- Phase 1 is complete when a weekly article can produce a reviewed script, transcript, show notes, and claim ledger without audio synthesis.
264
-- Phase 2 is complete when a reviewed script can synthesize a private validated MP3 under duration, size, loudness, and cost limits.
272
+- Phase 2 is complete when privacy docs are updated, reviewed sample scripts are synthesized privately across candidate providers, a listening-test decision selects the MVP provider, and output fits duration, size, loudness, and total cost limits.
273
- Phase 3 is complete when storage and RSS validation prove public episode delivery meets podcast-client requirements.
274
- Phase 4 is complete when the article embed, landing page, methodology, and privacy updates accurately describe the launched experience.
275
- Phase 5 is complete when the non-blocking workflow can publish a pilot episode and leave article publishing unaffected by podcast failures.
@@ -271,20 +279,21 @@ week + article_hash + script_prompt_version + voice_config_hash
279
- [ ] Script is 1,200-1,700 words and target duration fits the 8-12 minute format, with automated MVP output at or below 10 minutes.
280
- [ ] AI voice disclosure is in first 60 seconds and show notes.
281
- [ ] Claim ledger has no unsupported public claims.
274
-- [ ] Human reviewer approved script before synthesis.
275
-- [ ] Provider voice license is documented.
282
+- [ ] GitHub Environment `podcast-review` approved script artifacts before synthesis.
283
+- [ ] Provider voice license/privacy terms are documented before non-dry-run TTS.
284
- [ ] No real-person voice cloning or protected podcast imitation.
285
- [ ] No fake sponsor language.
286
+- [ ] No music/SFX in MVP unless a later licensed-track manifest issue is complete.
287
- [ ] Show notes include source URLs and corrections link.
288
- [ ] MP3 is mono, 44.1 kHz, 64-96 kbps, `audio/mpeg`, normalized near -16 LUFS, and under 10 MB.
289
- [ ] Audio is hosted outside git.
290
- [ ] RSS includes stable GUID, enclosure URL/length/type, RFC 2822 pubDate, and ASCII URLs.
291
- [ ] Audio endpoint supports HEAD and byte-range requests.
283
-- [ ] Cost ledger is updated and monthly guardrails pass.
292
+- [ ] Total cost ledger is updated for TTS, storage, egress, script generation, validation, CDN if used, and monthly guardrails pass.
293
- [ ] Privacy and methodology pages match actual providers and analytics.
294
295
---
296
297
## Definition of Done for MVP
298
290
-The MVP is done when a successful weekly article can trigger a non-blocking podcast workflow that creates a reviewed, source-backed two-host episode; synthesizes it with documented Azure Speech voices; uploads validated MP3/transcript/show notes/manifests to Blob Storage; publishes a valid RSS item; embeds the episode in the article; records cost; and leaves weekly article publishing unaffected if podcast generation fails.
299
+The MVP is done when a confirmed normal weekly article publish explicitly dispatches a non-blocking podcast workflow that creates a reviewed, source-backed two-host episode; passes the GitHub Environment `podcast-review` gate; synthesizes it with the Phase 2-selected provider; uploads validated MP3/transcript/show notes/manifests to stable public Blob/CDN URLs; publishes a valid RSS item; embeds the episode through the Hugo `podcast-episode` shortcode/partial; records total cost; and leaves weekly article publishing unaffected if podcast generation fails.