?

What you build

A cron job stored in rousseau's own SQLite state (cron_jobs table, schema in internal/state/sqlite/cron.go) that fires at 18:00 local time on weekdays. It runs a prompt that asks the model to summarise git log --since=today and delivers the result to your phone over WhatsApp.

Estimated time: 10 minutes.

Prerequisites

  • WhatsApp bridge already paired (see Quickstart step 4 or Transports: WhatsApp).
  • The rousseau whatsapp daemon running — the cron scheduler in internal/cron/scheduler.go is booted by transport daemons via wiring.startCron(), not by rousseau chat.
  • A workspace containing the git repo you want summarised, bind-mounted into the container (or on the host if you run rousseau outside a container).

How rousseau cron works

rousseau cron add writes a row to the cron_jobs table (internal/state/sqlite/cron.go). Every ~15 seconds, scheduler.sync re-reads the table and reconciles the robfig/cron/v3 in-memory schedule. When a job fires, the scheduler emits cron.firing, runs the prompt through the configured provider, and delivers the result to deliver_to via the transport bridge that owns the process (WhatsApp in this tutorial).

Structured log names you'll see (from internal/cron/scheduler.go):

  • cron.started — scheduler booted with poll_interval=….
  • cron.scheduled — a job was accepted.
  • cron.firing — a job is about to run.
  • cron.completed — a job finished successfully.
  • cron.run_failed, cron.delivery_failed, cron.record_failed — failure modes.

Step 1: add the job

rousseau cron add \
  --name        nightly-changelog \
  --schedule    "0 18 * * 1-5" \
  --prompt      "Summarise git log --since=yesterday under /workspace/rousseau-agent as a Slack-style bullet list. Keep it under 200 words. If nothing changed, reply with a single line 'no commits'." \
  --deliver-to  447900123456@s.whatsapp.net

The cron expression is parsed by robfig/cron/v3 in newCronAddCmd (internal/cli/cron.go). Invalid expressions are rejected before write. The --deliver-to value is the E.164 JID for WhatsApp (<digits>@s.whatsapp.net); the delivery target format is transport-specific.

Step 2: verify

rousseau cron list

Output shape (from newCronListCmd):

NAME               STATUS SCHEDULE       PROMPT                       DELIVER-TO
nightly-changelog  on     0 18 * * 1-5   Summarise git log …          447900123456@s.whatsapp.net

The list is also exposed over MCP as rousseau_cron_list (see internal/mcp/tools.go).

Step 3: dry-run

There is no built-in "fire now" trigger. To smoke-test, temporarily schedule the job one minute in the future:

rousseau cron remove nightly-changelog
rousseau cron add --name test --schedule "*/1 * * * *" --prompt "say hi" --deliver-to "$JID"
journalctl --user -u rousseau-agent -f | grep cron.

Expected log sequence:

INFO cron.scheduled  job=test expr=*/1 * * * *
INFO cron.firing     job=test
INFO cron.completed  job=test

Remove the test job and re-add the real one when you're done.

Step 4: tighten the prompt

The best cron prompts are self-contained: the model has no memory of previous runs. Include the repo path, expected output format, and a fallback for the empty case. Example second iteration:

Summarise commits authored since 07:00 UTC today under
/workspace/rousseau-agent. Use this format:

- <short type>: <one-line summary> — <sha>

Group by author. If no commits landed, reply exactly: no commits.

Toggling and removing

rousseau cron disable nightly-changelog   # keeps the row, stops firing
rousseau cron enable  nightly-changelog
rousseau cron remove  nightly-changelog   # deletes the row

SetEnabled and Delete from internal/state/sqlite/cron.go are what these call.

Related

Type to search 150+ pages. Ranking: BM25 with title/description boost.