feat: add breaking_news input to manual podcast trigger workflow (#502)

* feat: add breaking_news input to manual podcast trigger workflow - Add trigger-podcast.yml workflow with breaking_news workflow_dispatch input - Add --breaking-news CLI argument to podcaster_handoff.py - Include breaking_news in handoff payload when provided Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test+docs: add breaking_news tests and document field in architecture.md - Add test asserting breaking_news is omitted from payload by default - Add test asserting breaking_news is included when provided - Document breaking_news in the Shared Interfaces handoff payload list Resolves review comments on PR #502. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Juan Manuel Servera committed Jun 15, 2026 at 23:36 UTC 9f0724b08fc50f6ee8d47df0a116039c8b143a92
4 files changed +43 -1
.github/workflows/trigger-podcast.yml
+12 -1
@@ -11,6 +11,10 @@ on:
11 description: 'Optional: crawl-and-publish workflow run ID that produced the manifest. If omitted, uses the most recent.'
12 required: false
13 type: string
14 + breaking_news:
15 + description: 'Optional last-moment news or important information to include in this podcast episode.'
16 + required: false
17 + type: string
18
19 permissions:
20 contents: read
@@ -100,6 +104,7 @@ jobs:
104 MANIFEST_FILE: ${{ steps.manifest-locate.outputs.manifest_path }}
105 PUBLISH_RUN_ID: ${{ steps.manifest-locate.outputs.publish_run_id }}
106 TRIGGER_RUN_ID: ${{ github.run_id }}
107 + BREAKING_NEWS: ${{ inputs.breaking_news || '' }}
108 run: |
109 set -euo pipefail
110 if [ -z "$PODCASTER_ENDPOINT" ] || [ -z "$PODCASTER_API_KEY" ]; then
@@ -116,6 +121,11 @@ jobs:
121 fi
122 echo "::notice::Using manifest from crawl-and-publish run $PUBLISH_RUN_ID ($(date -r "$MANIFEST_FILE" '+%Y-%m-%d %H:%M:%S' 2>/dev/null || echo 'date unknown'))"
123
124 + BREAKING_ARGS=()
125 + if [ -n "$BREAKING_NEWS" ]; then
126 + BREAKING_ARGS=(--breaking-news "$BREAKING_NEWS")
127 + fi
128 +
129 # Trigger real podcast generation using the existing validated manifest
130 python3 scripts/podcaster_handoff.py \
131 --week "$WEEK" \
@@ -124,4 +134,5 @@ jobs:
134 --publish-run-id "$PUBLISH_RUN_ID" \
135 --publish-mode normal \
136 --manifest "$MANIFEST_FILE" \
127 - --podcast-config config/podcast.json
137 + --podcast-config config/podcast.json \
138 + "${BREAKING_ARGS[@]}"
architecture.md
+1
@@ -56,6 +56,7 @@ SquadScope, publicly branded as **Claracle**, is an AI-powered GitHub trend obse
56 - `source_artifacts`
57 - `podcast_config`
58 - `script_directions`
59 + - `breaking_news` — optional last-moment news text to include in the episode (omitted when not provided)
60 - Transport: HTTP `POST` with `x-podcaster-api-key` header
61 - Handoff implementation: `scripts/podcaster_handoff.py`
62
scripts/podcaster_handoff.py
+6
@@ -34,6 +34,7 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
34 parser.add_argument("--manifest", type=Path, help="Optional publish manifest used for article hash/source artifact metadata.")
35 parser.add_argument("--podcaster-dry-run", action="store_true", help="Ask Podcaster to validate without generating an episode; intended only for the manual smoke workflow.")
36 parser.add_argument("--podcast-config", type=Path, default=None, help="Path to podcast config JSON (default: config/podcast.json relative to repo root).")
37 + parser.add_argument("--breaking-news", default=None, help="Optional last-moment news or important information to include in this podcast episode.")
38 parser.add_argument("--endpoint", default=os.environ.get("PODCASTER_ENDPOINT", ""))
39 parser.add_argument("--timeout", type=int, default=DEFAULT_TIMEOUT_SECONDS)
40 return parser.parse_args(argv)
@@ -345,6 +346,7 @@ def build_payload(
346 podcast_config_path: Path | None = None,
347 podcaster_dry_run: bool = False,
348 repo_root: Path | None = None,
349 + breaking_news: str | None = None,
350 ) -> dict[str, Any]:
351 manifest = _load_manifest(manifest_path)
352 if not _manifest_allows_handoff(manifest, week=week, publish_mode=publish_mode):
@@ -400,6 +402,9 @@ def build_payload(
402 article_summary=summary,
403 )
404
405 + if breaking_news:
406 + payload["breaking_news"] = breaking_news
407 +
408 if podcaster_dry_run:
409 payload["dry_run"] = True
410 return payload
@@ -489,6 +494,7 @@ def main(argv: list[str] | None = None) -> int:
494 manifest_path=args.manifest,
495 podcast_config_path=args.podcast_config,
496 podcaster_dry_run=args.podcaster_dry_run,
497 + breaking_news=args.breaking_news,
498 )
499 post_handoff(endpoint, api_key, payload, timeout=args.timeout)
500 except PodcasterHandoffError as exc:
tests/test_podcaster_handoff.py
+24
@@ -631,5 +631,29 @@ class PodcasterHandoffTests(unittest.TestCase):
631 article_file.chmod(0o644)
632
633
634 + def test_build_payload_omits_breaking_news_by_default(self) -> None:
635 + """breaking_news must not appear in the payload when not provided."""
636 + payload = podcaster_handoff.build_payload(
637 + week="2026-W23",
638 + article_url="https://jmservera.github.io/SquadScope/weekly/2026/w23/",
639 + article_path="content/weekly/2026/W23.md",
640 + publish_run_id="123456789",
641 + publish_mode="normal",
642 + )
643 + self.assertNotIn("breaking_news", payload)
644 +
645 + def test_build_payload_includes_breaking_news_when_provided(self) -> None:
646 + """breaking_news must be included in the payload when a value is given."""
647 + payload = podcaster_handoff.build_payload(
648 + week="2026-W23",
649 + article_url="https://jmservera.github.io/SquadScope/weekly/2026/w23/",
650 + article_path="content/weekly/2026/W23.md",
651 + publish_run_id="123456789",
652 + publish_mode="normal",
653 + breaking_news="Major outage at GitHub Actions today",
654 + )
655 + self.assertEqual(payload["breaking_news"], "Major outage at GitHub Actions today")
656 +
657 +
658 if __name__ == "__main__":
659 unittest.main()