diff --git a/config.toml.example b/config.toml.example index e5cf7df3d..003545fb6 100644 --- a/config.toml.example +++ b/config.toml.example @@ -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" diff --git a/docs/config-reference.md b/docs/config-reference.md index 838ceacb6..bb1476091 100644 --- a/docs/config-reference.md +++ b/docs/config-reference.md @@ -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" @@ -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. | diff --git a/docs/cronjob.md b/docs/cronjob.md index 4aa0231ad..9f9fce524 100644 --- a/docs/cronjob.md +++ b/docs/cronjob.md @@ -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 | @@ -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 @@ -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 @@ -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)) |