Skip to content

Scheduling

Garot Conklin edited this page May 25, 2026 · 7 revisions

Scheduling

Signal has two automated jobs — a daily brief at 5:00 AM and a weekly summary every Monday at 2:00 AM. Both are managed by macOS launchd.

Job Schedule Script Config
Daily brief Every day, 5:00 AM run_and_publish.sh com.flexrpl.signal.plist
Weekly summary Every Monday, 2:00 AM run_weekly_and_publish.sh com.flexrpl.signal.weekly.plist

The weekly runs Monday at 2:00 AM rather than Sunday night to avoid Claude Pro rate limit pressure accumulated during Sunday's daily run and normal daytime usage.


How it works

launchd is macOS's native service management framework. It reads .plist (property list) configuration files from ~/Library/LaunchAgents/ and triggers scripts at the specified calendar intervals.

Daily flow:

launchd (5:00 AM daily)
    → run_and_publish.sh
        → python main.py --no-venv        # Passes 1–5, fetches articles
        → git add reports/ index.html archive.html
        → git commit -m "signal: daily brief YYYY-MM-DD"
        → git push origin main
    → GitHub Actions deploys to GitHub Pages

Weekly flow:

launchd (11:00 PM every Sunday)
    → run_weekly_and_publish.sh
        → python main.py --weekly --no-venv   # Pass 6 only, reads DB
        → git add reports/weekly_*.html index.html archive.html
        → git commit -m "signal: weekly brief YYYY-WNN"
        → git push origin main
    → GitHub Actions deploys to GitHub Pages

Files

File Purpose
scripts/com.flexrpl.signal.plist Daily source of truth — commit changes here
~/Library/LaunchAgents/com.flexrpl.signal.plist Daily active copy — what launchd reads
scripts/com.flexrpl.signal.weekly.plist Weekly source of truth — commit changes here
~/Library/LaunchAgents/com.flexrpl.signal.weekly.plist Weekly active copy — what launchd reads

Important: Editing a repo plist does NOT automatically update the active copy. You must copy and reload after every change.


One-time setup

Daily job:

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

Weekly job:

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

Verify both loaded:

launchctl list | grep flexrpl
# Should show:
# -  0  com.flexrpl.signal
# -  0  com.flexrpl.signal.weekly

Applying plist changes

Any time you edit scripts/com.flexrpl.signal.plist:

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

Same pattern for the weekly plist:

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

Verify the change took effect:

cat ~/Library/LaunchAgents/com.flexrpl.signal.plist | grep -A4 StartCalendarInterval
cat ~/Library/LaunchAgents/com.flexrpl.signal.weekly.plist | grep -A8 StartCalendarInterval

Changing the schedule

Edit the StartCalendarInterval block in scripts/com.flexrpl.signal.plist:

<!-- Run daily at 5:00 AM local time -->
<key>StartCalendarInterval</key>
<dict>
    <key>Hour</key>
    <integer>5</integer>   <!-- 0–23, local time -->
    <key>Minute</key>
    <integer>0</integer>
</dict>

Current schedule: 5:00 AM local time daily.

5am was chosen to capture overnight wire service content and early-morning news cycle updates before peak traffic hits news servers.

Common alternatives:

Schedule Hour Minute
5:00 AM (current) 5 0
6:00 AM 6 0
7:00 AM 7 0
12:01 AM 0 1

After changing, always apply with the unload/copy/reload sequence above.


Switching LLM provider for scheduled runs

The plist injects SIGNAL_LLM_PROVIDER=claude into the job's environment:

<key>EnvironmentVariables</key>
<dict>
    <key>SIGNAL_LLM_PROVIDER</key>
    <string>claude</string>
</dict>

Do not change this to ollama — Ollama's Metal GPU access from a launchd daemon context causes kernel panics. See LLM Providers and Troubleshooting.


Manually triggering a run

Daily:

# Run the script exactly as launchd does (output → logs/cron.log)
bash /Users/garotconklin/garotm/fleXRPL/signal/scripts/run_and_publish.sh

# Run the pipeline only (live terminal output, no git push)
python3 /Users/garotconklin/garotm/fleXRPL/signal/main.py

# Trigger via launchd directly (identical environment to scheduled run)
launchctl start com.flexrpl.signal

Weekly:

# Run the weekly script exactly as launchd does (output → logs/weekly.log)
bash /Users/garotconklin/garotm/fleXRPL/signal/scripts/run_weekly_and_publish.sh

# Run weekly synthesis only (live terminal output, no git push)
python3 /Users/garotconklin/garotm/fleXRPL/signal/main.py --weekly

# Run over a custom window (e.g. past 5 days instead of 7)
python3 /Users/garotconklin/garotm/fleXRPL/signal/main.py --weekly --days 5

# Trigger via launchd directly
launchctl start com.flexrpl.signal.weekly

Logs

Log file Written by
logs/cron.log Daily pipeline (run_and_publish.sh)
logs/weekly.log Weekly synthesis (run_weekly_and_publish.sh)

Both logs rotate automatically when they exceed 1MB (.log.log.bak). Log files are gitignored and never committed to the repository.

# Watch daily run live
tail -f logs/cron.log

# Watch weekly run live
tail -f logs/weekly.log

# Last daily run summary
grep -A5 "Signal run started" logs/cron.log | tail -20

# Last weekly run summary
grep -A5 "Signal WEEKLY run started" logs/weekly.log | tail -20

Complete plist references

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.flexrpl.signal</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>SIGNAL_LLM_PROVIDER</key>
        <string>claude</string>
    </dict>

    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/garotconklin/garotm/fleXRPL/signal/scripts/run_and_publish.sh</string>
    </array>

    <key>StartCalendarInterval</key>
    <dict>
        <key>Hour</key>
        <integer>5</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/Users/garotconklin/garotm/fleXRPL/signal/logs/cron.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/garotconklin/garotm/fleXRPL/signal/logs/cron.log</string>

    <!-- Do not re-run on login/reboot if the scheduled time was missed -->
    <key>RunAtLoad</key>
    <false/>
</dict>
</plist>

RunAtLoad: false

Neither job runs automatically when the plist is loaded or on login. They only fire at their scheduled calendar intervals. If the Mac is off or asleep at the scheduled time, that run is skipped until the next occurrence.


Weekly plist reference

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.flexrpl.signal.weekly</string>

    <key>EnvironmentVariables</key>
    <dict>
        <key>SIGNAL_LLM_PROVIDER</key>
        <string>claude</string>
    </dict>

    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/garotconklin/garotm/fleXRPL/signal/scripts/run_weekly_and_publish.sh</string>
    </array>

    <!-- Every Sunday at 11:00 PM local time (Weekday 0 = Sunday) -->
    <key>StartCalendarInterval</key>
    <dict>
        <key>Weekday</key>
        <integer>0</integer>
        <key>Hour</key>
        <integer>23</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>

    <key>StandardOutPath</key>
    <string>/Users/garotconklin/garotm/fleXRPL/signal/logs/weekly.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/garotconklin/garotm/fleXRPL/signal/logs/weekly.log</string>

    <key>RunAtLoad</key>
    <false/>
</dict>
</plist>

launchd Weekday values: 0 = Sunday, 1 = Monday, ..., 6 = Saturday.


Troubleshooting launchd

Job not appearing in launchctl list:

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

Job shows error code (non-zero) in launchctl list:

cat logs/cron.log     # check daily pipeline errors
cat logs/weekly.log   # check weekly synthesis errors

Schedule didn't update after editing the plist: The active copy in ~/Library/LaunchAgents/ was not updated. Run the unload/copy/reload sequence for the relevant plist.

Run completed but no new report on GitHub Pages: Check the relevant log file for git errors. The most common cause is a merge conflict or stale branch.

Weekly synthesis: "No complete runs with briefs found": The weekly pipeline requires at least one successful daily run in the past 7 days. Run python3 main.py first to generate at least one daily brief, then re-run python3 main.py --weekly.

Clone this wiki locally