Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config.toml.example
Original file line number Diff line number Diff line change
Expand Up @@ -334,12 +334,12 @@ error_hold_ms = 2500
# [[cron.jobs]]
# enabled = true # optional, default: true
# schedule = "0 9 * * 1-5" # weekdays at 9:00 AM
# channel = "123456789" # target channel/thread ID
# channel = "123456789012345678" # target channel/thread ID
# message = "summarize yesterday's merged PRs" # prompt for the agent
# platform = "discord" # "discord" or "slack"
# sender_name = "DailyOps" # attribution (default: "openab-cron")
# timezone = "America/New_York" # IANA timezone (default: "UTC")
# thread_id = "" # optional: post to existing thread
# thread_id = "234567890123456789" # optional: post to existing thread (omit for a new thread; never "")

# [[cron.jobs]]
# schedule = "0 0 * * 0"
Expand Down
12 changes: 6 additions & 6 deletions docs/config-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -785,16 +785,16 @@ usercron_path = "cronjob.toml" # relative to $HOME/.openab/, or ab
[[cron.jobs]]
enabled = true # optional, default: true
schedule = "0 9 * * 1-5" # cron expression (5-field POSIX)
channel = "123456789" # target channel/thread ID
channel = "123456789012345678" # target channel/thread ID
message = "summarize yesterday's merged PRs" # message sent to agent
platform = "discord" # optional, default: "discord"
sender_name = "DailyOps" # optional, default: "openab-cron"
timezone = "America/New_York" # optional, default: "UTC"
thread_id = "" # optional, post to existing thread
# thread_id = "234567890123456789" # optional, post to existing thread (omit for a new thread per run; never "")

[[cron.jobs]]
schedule = "0 0 * * 0"
channel = "123456789"
channel = "123456789012345678"
message = "generate weekly status report"
platform = "discord"
timezone = "UTC"
Expand All @@ -818,17 +818,17 @@ timezone = "UTC"
| `platform` | string | `"discord"` | Target platform (`"discord"` or `"slack"`). |
| `sender_name` | string | `"openab-cron"` | Sender attribution shown in the prompt context. |
| `timezone` | string | `"UTC"` | IANA timezone for schedule evaluation (e.g. `"America/New_York"`, `"Europe/Berlin"`). |
| `thread_id` | string | `""` | Optional thread ID to post into an existing thread. |
| `thread_id` | string | — (unset) | Optional thread ID to post into an existing thread. Omit the field for a new thread (one per run, unless a usercron `id` pins the job to its first thread); never set it to `""` (see [Thread Behavior](cronjob.md#thread-behavior)). |

The external `cronjob.toml` uses `[[jobs]]` (same fields). See [Usercron docs](cronjob.md#usercron--hot-reload-with-cronjobtoml) for details.

### Usercron-only `[[jobs]]` fields

These fields are valid only in the external usercron file, for example `$HOME/.openab/cronjob.toml`. They are rejected in baseline `[[cron.jobs]]` because OpenAB only writes state back to the user-managed cron file.
These fields are valid only in the external usercron file, for example `$HOME/.openab/cronjob.toml`. `disable_on_success` is rejected in baseline `[[cron.jobs]]`; the other fields, including `id`, are accepted there but ignored, because OpenAB only writes state back to the user-managed cron file.

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `id` | string | *required with `disable_on_success`* | Stable job ID used when the scheduler writes `enabled = false` or `thread_id` back to `cronjob.toml`. |
| `id` | string | *required with `disable_on_success`* | Non-empty, unique job ID used when the scheduler writes `enabled = false` or `thread_id` back to `cronjob.toml`. An empty `id` counts as unset; with duplicate IDs, writeback updates only the first matching entry. |
| `disable_on_success` | string | — | Command to run before sending the scheduled prompt. |
| `disable_on_success_match` | string | *required with `disable_on_success`* | Marker that must appear in stdout or stderr, in addition to exit code `0`, before the job is considered complete. |
| `disable_on_success_timeout_secs` | integer | `60` | Timeout for the completion check command. |
Expand Down
21 changes: 18 additions & 3 deletions docs/cronjob.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ message = "summarize yesterday's merged PRs" # required: prompt for the agent
platform = "discord" # optional, default: "discord"
sender_name = "DailyOps" # optional, default: "openab-cron"
timezone = "America/New_York" # optional, default: "UTC"
thread_id = "" # optional: post to existing thread
# thread_id = "234567890123456789" # optional: post to existing thread (omit to create a new thread; never "")
```

| Field | Required | Default | Description |
Expand All @@ -49,7 +49,21 @@ thread_id = "" # optional: post to existing thread
| `platform` | | `"discord"` | `"discord"`, `"slack"`, `"telegram"`, `"googlechat"`, or `"lineworks"` (non-default platforms require their feature) |
| `sender_name` | | `"openab-cron"` | Attribution shown in prompt context |
| `timezone` | | `"UTC"` | IANA timezone (e.g. `"America/New_York"`, `"Europe/Berlin"`) |
| `thread_id` | | — | Post into an existing thread instead of the channel |
| `thread_id` | | — | Post into an existing thread instead of creating a new one. Omit the field to create a new thread (a new one per run, unless a usercron `id` pins the job to its first thread); do not set it to `""` (see [Thread Behavior](#thread-behavior)) |
| `id` | | — | Usercron only; ignored in baseline `[[cron.jobs]]`. Non-empty, unique job identifier, required for `disable_on_success`. Enables scheduler writeback, including persisting an auto-created `thread_id` (see [Thread Behavior](#thread-behavior)). An empty `id` counts as unset; with duplicate IDs, writeback updates only the first matching entry |

### Thread Behavior

Where a job posts depends on `thread_id` and, for usercron jobs, `id`. This applies to platforms with thread support; Google Chat and LINE WORKS differ (see [Platform Prerequisites](#platform-prerequisites)).

| `thread_id` | `id` | Behavior |
|---|---|---|
| set | any | Every run posts into that thread. No new thread and no `thread_id` writeback. |
| omitted | omitted | Every run creates a new thread. Since sessions are keyed by thread, each run also starts a fresh agent session. |
| omitted | set (usercron) | The first run that successfully creates a thread has the scheduler write its ID back to `cronjob.toml` as `thread_id`. If that writeback succeeds and records a real thread ID, later runs post into that same thread once the scheduler reloads the file (about a minute). If OpenAB cannot write the file, a new thread is created on every run (see "Choosing the right scope" under Autonomy and Permission Scope below). |
| `""` | any | Known defect; do not use. An empty string is not treated as unset: it is passed through as a thread ID. On Discord the send fails (`failed to send cron message ... cannot parse integer from empty string` in the logs) and nothing is posted; other platforms may behave differently. A future release may treat `""` as unset. |

`id` does not select a thread — it only tells the scheduler which entry to update. If you remove a written-back `thread_id` but keep `id`, the next run creates a new thread and pins the job to it again.

## Cron Expression Format

Expand Down Expand Up @@ -326,7 +340,7 @@ usercron_path = "cronjob.toml"
- **Minute-aligned**: The scheduler aligns to minute boundaries (`:00`), so `0 9 * * *` fires at exactly 09:00:00, not at whatever second the process started.
- **Overlap protection**: If a previous execution of the same job is still running, the next tick is skipped.
- **Isolation**: Cron failures are logged but never block interactive chat traffic.
- **Usercron persistence**: For usercron jobs, the scheduler may write `thread_id` and `enabled = false` back to `cronjob.toml`.
- **Usercron persistence**: For usercron jobs with an `id`, the scheduler may write `thread_id` and `enabled = false` back to `cronjob.toml` (see [Thread Behavior](#thread-behavior)).
- **Graceful shutdown**: In-flight cron tasks are waited on (up to 30 seconds) during shutdown.

## Sender Identity
Expand Down Expand Up @@ -390,3 +404,4 @@ See [Kubernetes CronJob Reference Architecture](cronjob_k8s_refarch.md) for the
| Usercron not reloading | File not saved / wrong path | Check logs for `usercron file changed, reloading` |
| Usercron parse error | Invalid TOML syntax | Check logs for `failed to parse usercron file` |
| Goal job does not auto-disable | Command did not exit `0` or output did not include `disable_on_success_match` | Run the command manually and confirm both conditions |
| Nothing posted; logs show `failed to send cron message ... cannot parse integer from empty string` | `thread_id = ""` | Remove the `thread_id` line or set a real thread ID (see [Thread Behavior](#thread-behavior)) |
Loading