Skip to content

Monthly Reports

Garot Conklin edited this page Jun 10, 2026 · 1 revision

Monthly Reports

On the 1st of each month at 7:00 AM, Signal generates a monthly intelligence summary — a longitudinal analysis that reads daily briefs and weekly summaries from the prior calendar month. This is where story arcs, watch list accountability, and coverage patterns become visible across weeks.

Partial months are supported: if daily coverage does not span the full calendar month (e.g. pipeline started mid-month), the report is labeled Partial Coverage in the header and filename.


When it runs

Trigger Time What it does
Automated (launchd) 1st of each month, 7:00 AM local time Synthesizes previous calendar month, publishes
Manual (CLI) python3 main.py --monthly --month YYYY-MM Same pipeline, immediate execution

On Jul 1, the scheduled job runs --month 2026-06 (June). The month argument defaults to the current month when omitted, but the launchd script always passes the previous month.

Important: The launchd job must be installed first. See Scheduling#one-time-setup.

Installing the monthly schedule (one-time)

cp scripts/com.flexrpl.signal.monthly.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/com.flexrpl.signal.monthly.plist

# Verify it loaded:
launchctl list | grep flexrpl
# → -  0  com.flexrpl.signal.monthly

What the monthly brief contains

The monthly report has seven fixed sections:

Section What it covers
Month in Review The dominant political dynamic of the month — trajectory visible only in hindsight
Story Arc Tracker How the top 3–5 stories entered coverage, peaked, evolved, resolved, or vanished across weeks
Watch List Scorecard Which early-month watch items materialized, which did not — accountability on prior intelligence
Coverage Pattern Analysis Recurring blindspots that persisted all month on left vs. right
Emerging Actors People and institutions that gained or lost significance across the month
Watch List: Next Month Concrete forward-looking items with named entities and deadlines
Analyst Note One paragraph: honest monthly trajectory assessment

How it works

The monthly pipeline is Pass 7 — it skips article fetching entirely and works from data already stored in signal.db.

signal.db  →  get_runs_for_month()  →  get_weekly_source_data()
              get_weekly_briefs_for_month()
                                              ↓
                                    Format weekly + daily summaries
                                    Build watch list evolution timeline
                                              ↓
                                    MONTHLY_BRIEF prompt (one LLM call)
                                              ↓
                                    save_monthly_brief()  →  monthly_briefs table
                                              ↓
                                    generate_monthly_report()  →  HTML file

Partial month detection

If the earliest daily run in the month is after the 1st, or the latest run is before the last day of the month, partial=True is set automatically. The LLM prompt includes a note to avoid implying completeness.

Data selection

  • Daily runs: one complete run per calendar date in the target month (latest run wins per day)
  • Weekly briefs: any weekly brief whose date range overlaps the calendar month

Output files

Monthly reports are written to reports/ with a distinct filename format:

reports/monthly_202605_partial_20260610_1753.html   # partial month
reports/monthly_202606_20260701_0700.html           # full month
          ─────── ───────
          YYYYMM  optional _partial suffix

Visual design

Monthly reports use a purple color scheme (#a371f7) instead of daily blue or weekly gold:

  • Purple header gradient and MONTHLY SUMMARY badge
  • Partial Coverage badge (red) when the month is incomplete
  • Day chips showing each daily run included
  • Analyst Note uses a purple tint

Landing page and archive integration

After each monthly run, _update_index() in main.py regenerates both pages:

Landing page (index.html): A fourth card — "Monthly Summary" — appears alongside daily, weekly, and archive cards. Partial months show (partial) in the card meta.

Archive (archive.html): A "Monthly Summaries" section appears at the top, above weekly groupings.


Logs

The monthly script writes to logs/monthly.log:

# Watch a monthly run live
tail -f logs/monthly.log

# Check last run result
grep -A5 "Signal MONTHLY run" logs/monthly.log | tail -20

Troubleshooting

"No complete daily runs found for [Month]"

At least one successful daily run must exist in the target month. Run the daily pipeline first, then retry:

python3 main.py --monthly --month 2026-05

Running a partial month manually

This is expected when the pipeline has not been running for the full calendar month:

python3 main.py --monthly --month 2026-05   # May 11–31 partial OK

Monthly job not in launchctl list

launchctl load ~/Library/LaunchAgents/com.flexrpl.signal.monthly.plist
launchctl list | grep flexrpl

Monthly social card

Not yet implemented — planned after the first full scheduled monthly run (Jul 1+).

Clone this wiki locally