61
)
62
return manifest
63
64
+ def _write_historical_context(
65
+ self,
66
+ base: Path,
67
+ *,
68
+ month_synthesis: str | None = None,
69
+ yearly_narrative: str | None = None,
70
+ ) -> None:
71
+ if month_synthesis is not None:
72
+ month_path = base / "data" / "analyzed"
73
+ month_path.mkdir(parents=True, exist_ok=True)
74
+ (month_path / "2026-06-month-synthesis.md").write_text(month_synthesis, encoding="utf-8")
75
+ if yearly_narrative is not None:
76
+ yearly_path = base / "content" / "yearly"
77
+ yearly_path.mkdir(parents=True, exist_ok=True)
78
+ (yearly_path / "2026.md").write_text(yearly_narrative, encoding="utf-8")
79
+
80
def test_build_payload_uses_required_fields_and_real_optional_values(self) -> None:
81
tests_root = Path(__file__).resolve().parent
82
with tempfile.TemporaryDirectory(dir=tests_root) as tmpdir:
89
"---\ntitle: Week 23 Report\nsummary: Week 23 summary.\n---\n# Heading\nBody content here.\n",
90
encoding="utf-8",
91
)
92
+ self._write_historical_context(
93
+ Path(tmpdir),
94
+ month_synthesis=(
95
+ "---\n"
96
+ "title: June 2026 Month Synthesis\n"
97
+ "---\n\n"
98
+ "## Month Synthesis\n\n"
99
+ "June continued the agent-skills story.\n\n"
100
+ "## Trend Arc\n\n"
101
+ "- Agent skills kept accelerating.\n\n"
102
+ "## Prediction Review\n\n"
103
+ "Ignored.\n"
104
+ ),
105
+ yearly_narrative=(
106
+ "---\n"
107
+ "title: 2026 Yearly Narrative\n"
108
+ "---\n\n"
109
+ "## Year in Review\n\n"
110
+ "The year kept compounding around agent packaging and trust gaps.\n"
111
+ ),
112
+ )
113
114
payload = podcaster_handoff.build_payload(
115
week="2026-W23",
158
self.assertIn("Week 23 Report", payload["article_title"])
159
self.assertEqual(payload["article_summary"], "Week 23 summary.")
160
self.assertIn("Body content here.", payload["article_content"])
161
+ self.assertEqual(
162
+ payload["script_directions"]["historical_context"]["month_synthesis"],
163
+ "## Month Synthesis June continued the agent-skills story. ## Trend Arc - Agent skills kept accelerating.",
164
+ )
165
+ self.assertEqual(
166
+ payload["script_directions"]["historical_context"]["yearly_narrative"],
167
+ "## Year in Review The year kept compounding around agent packaging and trust gaps.",
168
+ )
169
+ self.assertLess(len(json.dumps(payload)), 100_000)
170
171
def test_smoke_payload_matches_real_weekly_handoff_shape(self) -> None:
172
podcaster_root = Path(__file__).resolve().parents[2] / "SquadScope-Podcaster"
237
self.assertEqual(built["source_artifacts"][0]["sources_failed"], ["rss"])
238
self.assertNotIn("sources_requested", built["source_artifacts"][1])
239
240
+ def test_build_payload_omits_historical_context_when_both_files_are_missing(self) -> None:
241
+ tests_root = Path(__file__).resolve().parent
242
+ with tempfile.TemporaryDirectory(dir=tests_root) as tmpdir:
243
+ manifest = self._write_manifest(Path(tmpdir))
244
+ article_dir = Path(tmpdir) / "content" / "weekly" / "2026"
245
+ article_dir.mkdir(parents=True)
246
+ (article_dir / "W23.md").write_text(
247
+ "---\ntitle: Week 23 Report\nsummary: Week 23 summary.\n---\n# Heading\nBody content here.\n",
248
+ encoding="utf-8",
249
+ )
250
+
251
+ payload = podcaster_handoff.build_payload(
252
+ week="2026-W23",
253
+ article_url="https://jmservera.github.io/SquadScope/weekly/2026/w23/",
254
+ article_path="content/weekly/2026/W23.md",
255
+ publish_run_id="123456789",
256
+ publish_mode="normal",
257
+ manifest_path=manifest,
258
+ repo_root=Path(tmpdir),
259
+ )
260
+
261
+ self.assertNotIn("historical_context", payload.get("script_directions", {}))
262
+
263
+ def test_read_historical_context_includes_available_file_when_other_is_missing(self) -> None:
264
+ tests_root = Path(__file__).resolve().parent
265
+ with tempfile.TemporaryDirectory(dir=tests_root) as tmpdir:
266
+ base = Path(tmpdir)
267
+ self._write_historical_context(
268
+ base,
269
+ yearly_narrative=(
270
+ "---\n"
271
+ "title: 2026 Yearly Narrative\n"
272
+ "---\n\n"
273
+ "## Year in Review\n\n"
274
+ "Only the yearly narrative is available.\n"
275
+ ),
276
+ )
277
+
278
+ context = podcaster_handoff._read_historical_context("2026-W23", base)
279
+
280
+ self.assertEqual(
281
+ context,
282
+ {"yearly_narrative": "## Year in Review Only the yearly narrative is available."},
283
+ )
284
+
285
+ def test_read_historical_context_truncates_to_word_budget(self) -> None:
286
+ tests_root = Path(__file__).resolve().parent
287
+ with tempfile.TemporaryDirectory(dir=tests_root) as tmpdir:
288
+ base = Path(tmpdir)
289
+ month_words = " ".join(f"month{i}" for i in range(1, 351))
290
+ trend_words = " ".join(f"trend{i}" for i in range(1, 101))
291
+ yearly_words = " ".join(f"year{i}" for i in range(1, 651))
292
+ self._write_historical_context(
293
+ base,
294
+ month_synthesis=(
295
+ "---\n"
296
+ "title: June 2026 Month Synthesis\n"
297
+ "---\n\n"
298
+ "## Month Synthesis\n\n"
299
+ f"{month_words}\n\n"
300
+ "## Trend Arc\n\n"
301
+ f"{trend_words}\n"
302
+ ),
303
+ yearly_narrative=(
304
+ "---\n"
305
+ "title: 2026 Yearly Narrative\n"
306
+ "---\n\n"
307
+ "## Year in Review\n\n"
308
+ f"{yearly_words}\n"
309
+ ),
310
+ )
311
+
312
+ context = podcaster_handoff._read_historical_context("2026-W23", base)
313
+
314
+ self.assertIsNotNone(context)
315
+ assert context is not None
316
+ self.assertLessEqual(
317
+ len(context["month_synthesis"].split()),
318
+ podcaster_handoff.MAX_MONTH_SYNTHESIS_WORDS,
319
+ )
320
+ self.assertLessEqual(
321
+ len(context["yearly_narrative"].split()),
322
+ podcaster_handoff.MAX_YEARLY_NARRATIVE_WORDS,
323
+ )
324
+ self.assertLessEqual(
325
+ len(context["month_synthesis"].split()) + len(context["yearly_narrative"].split()),
326
+ podcaster_handoff.MAX_MONTH_SYNTHESIS_WORDS + podcaster_handoff.MAX_YEARLY_NARRATIVE_WORDS,
327
+ )
328
+
329
+ def test_read_historical_context_extracts_only_year_in_review_section(self) -> None:
330
+ tests_root = Path(__file__).resolve().parent
331
+ with tempfile.TemporaryDirectory(dir=tests_root) as tmpdir:
332
+ base = Path(tmpdir)
333
+ self._write_historical_context(
334
+ base,
335
+ yearly_narrative=(
336
+ "---\n"
337
+ "title: 2026 Yearly Narrative\n"
338
+ "---\n\n"
339
+ "## Year in Review\n\n"
340
+ "The year kept compounding around agent packaging.\n\n"
341
+ "## Methodology\n\n"
342
+ "This section should NOT appear in the handoff.\n\n"
343
+ "## Contributors\n\n"
344
+ "Also should NOT appear.\n"
345
+ ),
346
+ )
347
+
348
+ context = podcaster_handoff._read_historical_context("2026-W23", base)
349
+
350
+ self.assertIsNotNone(context)
351
+ assert context is not None
352
+ self.assertIn("yearly_narrative", context)
353
+ self.assertIn("agent packaging", context["yearly_narrative"])
354
+ self.assertNotIn("Methodology", context["yearly_narrative"])
355
+ self.assertNotIn("Contributors", context["yearly_narrative"])
356
+
357
def test_podcaster_dry_run_sets_payload_flag(self) -> None:
358
payload = podcaster_handoff.build_payload(
359
week="2026-W23",