Proposal: Multi-athlete local-time delivery
A multi-athlete local-time delivery architecture for Coach Luca
Purpose: Replace per-athlete timezone-bound cron scheduling with a scalable local-time delivery system for multiple athletes across changing timezones and communication channels.
1. Problem
The current model uses fixed cron jobs with a single timezone baked into each job. That approach fails once the system must support:
- multiple athletes in different timezones
- athletes who travel frequently
- daylight saving transitions
- different communication channels per athlete
- different message schedules per athlete
Rewriting cron jobs when an athlete changes location is not a viable architecture. It creates operational fragility, does not scale across a roster, and places scheduling logic in the wrong layer.
2. Core design principle
Time is an athlete property, not a cron property.
Cron should wake a scheduler. The scheduler should decide which athlete messages are due based on each athlete's effective local time, delivery settings, and current state.
This means the system should move from:
- static athlete-specific cron jobs tied to fixed timezones
To:
- one central dispatcher that runs on a fixed cadence and evaluates all active athletes dynamically
3. Proposed architecture
3.1 One global dispatcher cron
Use a single cron job that runs every 15 minutes in UTC.
That job does not send athlete messages directly. Instead, it performs one responsibility:
- evaluate which athlete workflows are due right now
A 15-minute cadence is sufficient for this use case and avoids unnecessary scheduler churn.
3.2 Athlete delivery registry
Each athlete should have a durable delivery configuration record. That record should include:
The preferred channel should not be limited to Telegram. It should support whatever message sending connectors OpenClaw supports, for example:
- Telegram
- iMessage
- Slack
- any other supported outbound messaging connector
The dispatcher should route through the athlete's configured preferred channel rather than hard-coding delivery behavior per workflow.
3.3 Timezone resolver
Each athlete should have an effective timezone resolved dynamically at runtime.
Resolution order should be:
- Same-day explicit override, if present
- Current travel/location record
- Default home timezone
This lets the system determine the athlete's true local time without rewriting cron jobs.
3.4 Due-work evaluator
Every 15 minutes, the dispatcher should evaluate each active athlete and ask:
- What is this athlete's effective timezone right now?
- What is the local date and time in that timezone?
- Is the athlete due for a morning message?
- Is the athlete due for an evening message?
- Has that workflow already been sent for this athlete on this local date?
- Is there any athlete-specific suppression rule that should skip the send?
If the workflow is due, the dispatcher should enqueue or spawn the relevant athlete workflow.
3.5 Workflow execution layer
Once an athlete workflow is due, the dispatcher should launch the existing morning or evening generation logic for that athlete.
That workflow should:
- read the athlete's current context
- generate the message
- send it through the athlete's preferred channel
- save durable records
- report success or failure to the scheduler ledger
This preserves the existing coaching logic while moving the scheduling responsibility into the correct layer.
4. Required durable state
4.1 Send ledger
A durable send ledger is required to guarantee idempotency.
Each send attempt should record at least:
This ledger prevents duplicate sends when the dispatcher runs every 15 minutes and provides a durable audit trail.
4.2 Athlete status source of truth
The dispatcher should rely on one athlete status source of truth for:
- current location
- effective timezone inputs
- active/inactive delivery status
- preferred channel and target
- workflow enablement
That state should be centralized and durable, not scattered across multiple workflow-specific cron payloads.
5. Why this solves the real problem
This design supports:
- many athletes in different timezones at the same time
- athletes moving between locations without cron rewrites
- per-athlete local send times
- per-athlete channel preferences
- future expansion to new messaging connectors
- daylight saving changes through timezone-aware local-time evaluation
- operational safety through a durable idempotency ledger
Most importantly, it removes the false assumption that a cron job itself should embody an athlete's local schedule.
6. Why fixed athlete cron jobs are the wrong abstraction
Per-athlete fixed crons create the following problems:
- timezone changes require manual edits or cron rewrites
- scaling to many athletes becomes operationally messy
- channel logic ends up duplicated across cron payloads
- DST handling becomes brittle
- delivery state and due-state are implicit rather than durable
- the system cannot reason cleanly about whether a send was due, skipped, missed, or retried
A central dispatcher turns those hidden assumptions into explicit system logic.
7. Minimal viable implementation
A practical first version should include:
- One UTC cron job every 15 minutes
- One athlete delivery registry
- One timezone resolver
- One due-work evaluator
- One durable send ledger
- Reuse of the existing morning and evening generation workflows
- Channel dispatch through the athlete's configured preferred connector
This is enough to support multiple athletes in different locations without rewriting cron jobs.
8. Recommended data model direction
A simple normalized structure could look like this:
Athlete delivery profile
Timezone resolution inputs
Send ledger row
9. Channel architecture requirement
The design should be channel-agnostic.
The scheduler must not assume Telegram-only delivery. Instead, the athlete record should point to the preferred OpenClaw-supported outbound channel and target. The same due-work and idempotency logic should work regardless of connector.
Examples:
- Telegram user ID
- WhatsApp recipient target
- iMessage handle or chat target
- Slack user or channel DM target
- future supported connectors
This keeps coaching workflow logic separate from transport selection.
10. Operational recommendation
Do not continue extending the current fixed-cron-per-athlete approach.
Treat the current direct morning and evening crons as a temporary stopgap for a single athlete, not as the long-term architecture.
The proper next design layer is a roster-aware dispatcher with:
- a 15-minute UTC wake cycle
- athlete-local due-time evaluation
- channel-aware delivery routing
- durable send-state tracking
11. Summary
The right solution is not dynamic cron rewriting per athlete trip.
The right solution is:
- one central dispatcher
- every 15 minutes
- athlete-local due-time evaluation
- per-athlete preferred channel support
- per-athlete target records
- durable idempotent send tracking
That architecture supports a real coaching roster instead of a single-athlete workaround.