-
Notifications
You must be signed in to change notification settings - Fork 57
Description
Summary
In multi-agent mode, a cron job intended for one agent can be written to the global cron store, then later migrated into a different agent's per-agent cron file and executed there.
I hit this with a signal Morning Report job that should have belonged to co-3, but it was migrated into central and executed by central. Since central only had Telegram configured, delivery failed with Channel not found: signal.
Observed behavior
co-3has Signal configuredcentralonly has Telegram configured- A Morning Report job with
deliver: signal:...ended up in/tmp/lettabot/cron-jobs-central.json - Cron ran the job under
central - Delivery failed:
job_deliver_failed ... error: "Channel not found: signal"
Expected behavior
- A cron job created for one agent should never leak into another agent's cron store
- An agent should only execute cron jobs scoped to its own store/config
- Global store migration should not cause cross-agent job adoption
Evidence
Config:
lettabot.yaml:centralhas Telegram only;co-3has Signal
Runtime files:
/tmp/lettabot/cron-jobs-co-3.jsoncontains the expected co-3 Morning Report/tmp/lettabot/cron-jobs-central.jsoncontained a separate Signal-targeted Morning Report:id:cron-1772751695606-j44y8fdeliver.channel:signal
Logs from /tmp/lettabot/cron-log.jsonl:
2026-03-05T23:01:35Z:job_createdforcron-1772751695606-j44y8f2026-03-06T01:03:45Z:store_migrated_from_globalfrom/tmp/lettabot/cron-jobs.jsonto/tmp/lettabot/cron-jobs-central.json2026-03-07T00:00:35Z:job_deliver_failedwithChannel not found: signal
Likely root cause
lettabot-schedule still falls back to the global store when CRON_STORE_PATH is not set:
src/cron/cli.tsconst STORE_PATH = process.env.CRON_STORE_PATH || getCronStorePath();
Then CronService migrates that global store into a per-agent store on startup:
src/cron/service.tsmigrateFromGlobalStoreIfNeeded()
That allows a job written without correct per-agent scoping to be silently adopted by whichever agent first migrates the global file.
Additional issue in same area
CronService's directory-watch fallback is still hardcoded to cron-jobs.json:
src/cron/service.tsif (filename === 'cron-jobs.json') { ... }
In multi-agent mode this looks wrong for per-agent files like cron-jobs-co-3.json and cron-jobs-central.json.
Suggested fix
- Make cron job creation fail closed in multi-agent mode unless the per-agent cron store path is explicit
- Avoid migrating the global cron store into per-agent stores once multi-agent mode is active, or gate that migration much more tightly
- Update the file watcher to watch the actual basename of
this.storePath, not a hardcodedcron-jobs.json - Consider validating
deliver.channelagainst the channels configured for the agent that owns the cron store