Skip to content

Scheduling

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

Scheduling

Signal runs automatically every morning at 5:00 AM local time via macOS launchd. The schedule, LLM provider, and runtime paths are all configured in a single .plist file.


How it works

launchd is macOS's native service management framework. It reads a .plist (property list) configuration file from ~/Library/LaunchAgents/ and triggers the configured script at the specified calendar interval.

The flow is:

launchd (5:00 AM)
    → run_and_publish.sh
        → python main.py --no-venv
            → [full pipeline run]
        → 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

Files

File Purpose
scripts/com.flexrpl.signal.plist Source of truth — commit changes here
~/Library/LaunchAgents/com.flexrpl.signal.plist Active copy — what launchd actually reads

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


One-time setup

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

Verify it loaded:

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

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

Verify the change took effect:

cat ~/Library/LaunchAgents/com.flexrpl.signal.plist | grep -A4 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

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

# Watch the log in a second terminal
tail -f /Users/garotconklin/garotm/fleXRPL/signal/logs/cron.log

# 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

Logs

The script appends all output to logs/cron.log. The log is rotated automatically when it exceeds 1MB (logs/cron.loglogs/cron.log.bak).

# Live tail
tail -f logs/cron.log

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

The logs/ directory is gitignored (except logs/.gitkeep which tracks the empty directory). Log files never commit to the repository.


Complete 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</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

The job does NOT run automatically when the plist is loaded or on login. It only runs at the scheduled calendar interval. If the Mac is off or asleep at 5:00 AM, the run is skipped until the next day.


Troubleshooting launchd

Job not appearing in launchctl list:

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

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

cat logs/cron.log   # check for Python/script errors

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

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

Clone this wiki locally