SITREP generation¶
The core pipeline. Produces one situation report per run.
Overview¶
flowchart TD
T[Trigger: cron or workflow_dispatch] --> C[Collect]
C --> TR[Triage]
TR -->|nothing new| X[Exit: heartbeat only]
TR -->|material present| R[Research]
R --> A[Analyst]
A --> W[Writer]
W --> P[Publish]
P --> S[(sitrepisr.com)]
P --> TG[(Telegram)]
P --> N[(Newsletter queue)]
Runtime¶
- Language: Python 3.12+,
uv-managed. - Package:
agent/sitrep_agent/in the main site repo. - Entry point:
uv run sitrep-agent run(wired incli.py). - Execution host: GitHub Actions —
.github/workflows/on-demand-sitrep.yml. There is no persistent worker. - Schedule: every 6 hours (four runs per day). Also dispatchable manually from the admin surface.
Stages¶
1. Collect¶
Pulls recent material across several ingestion methods:
- RSS / Atom — for sources with a published feed.
- HTML scrape — targeted scrape of homepages / listing pages where feeds are unreliable.
- Telegram scrape — public channel message fetch (
telegram_scrape.py). - Exa search — domain-filtered Exa queries (
exa.py) for coverage where feeds are missing. - Manual URLs — operator-supplied URLs via
--include-url/ admin form, bypassing whitelist and coverage window.
Every fetched item is normalised into a common shape (url, source, title, published_at, body) and clamped to a rolling coverage window (default: 6 hours, overridable per run).
2. Triage¶
agent/sitrep_agent/nodes/triage.py.
Decides whether this run is even worth doing. Drops duplicates, filters low-signal items, and checks for a meaningful delta against the last published SITREP. If there's nothing new — and no manual URLs were supplied — the run exits with a heartbeat instead of publishing a near-duplicate report.
Override: manual --include-url inputs force-bypass the heartbeat / short-delta gate.
3. Research¶
agent/sitrep_agent/nodes/research.py.
For each surviving item, pulls additional context: follows inline links (within the whitelist), cross-references the same event across other sources, pulls related Telegram messages where relevant. The goal is to give the Analyst stage more than a headline.
4. Analyst¶
agent/sitrep_agent/nodes/analyst.py.
Consolidates the researched material into a structured intermediate representation: event list, actors, tracks (military / diplomatic / domestic / humanitarian), claim provenance, confidence notes.
This stage is where the "don't overstate confidence" prompting lives. The analyst is instructed to prefer unclear over a confident guess and to tag every forward-looking claim with a confidence level.
5. Writer¶
agent/sitrep_agent/nodes/writer.py.
Turns the analyst's structured output into the final SITREP markdown — BLUF, key developments, tracks, outlook. Enforces the house format (length, tone, source citation style).
6. Publish¶
agent/sitrep_agent/publish.py.
Posts the finished SITREP to the public site's /api/admin/ingest endpoint. The site:
- Writes the record to the database.
- Generates the OG image.
- Queues the newsletter send.
- Pushes to the Telegram channel.
- Triggers the audio pipeline.
Graph orchestration¶
Stages are wired together in agent/sitrep_agent/graph.py. The intermediate state between stages lives in agent/sitrep_agent/state.py — this is the single source of truth for what each node reads and writes.
Tunable knobs¶
Exposed as CLI flags, workflow inputs, and admin UI controls (see the sync rule):
| Knob | Effect |
|---|---|
coverage_hours |
Size of the rolling coverage window in hours (default 6, range 1–168). |
include_urls |
List of URLs to force into the run, bypassing whitelist and coverage window. Also overrides the heartbeat gate. |
reply_chat_id / reply_message_id |
For Telegram-bot-initiated runs; threads the publish notification as a reply. No admin UI. |
What's not here¶
- No private intelligence feeds.
- No paid-news scraping through paywalls.
- No model fine-tuning — all LLM work is prompt-driven against frontier models via OpenRouter.
- No retrieval cache across runs — each run is fresh (by design, to avoid stale framing).