Summary
The events framework currently supports two trigger types:
StaticTrigger — fires on a discrete event (conversation start/end, new message, participant joined, etc.)
TimeoutTrigger — fires after a relative delay from some anchor point (last message, first message, inactivity)
Neither supports firing at a specific absolute datetime. This is a gap for use-cases like:
- "Send a follow-up message to all participants on 2025-06-01 at 09:00"
- "End conversations that haven't completed by a deadline"
- Study or programme cohorts where events must fire at fixed calendar dates regardless of when each session started
Proposed new trigger type: ScheduledTrigger
A new model alongside StaticTrigger and TimeoutTrigger that fires at an explicitly specified datetime.
Model fields (sketch)
| Field |
Type |
Notes |
experiment |
FK → Experiment |
|
action |
OneToOne → EventAction |
same as existing triggers |
scheduled_at |
DateTimeField |
the absolute UTC datetime to fire |
timezone |
CharField |
optional display timezone for the UI |
is_active |
BooleanField |
|
is_archived |
BooleanField |
|
working_version |
self FK |
versioning, consistent with other triggers |
Scheduling / execution
- A periodic Celery beat task (or an extension of the existing
trigger_bot_message_task approach) polls for ScheduledTrigger instances whose scheduled_at <= now and is_active=True and haven't already fired.
- Alternatively, use
django-celery-beat to create a one-shot PeriodicTask when the trigger is saved, and delete it after firing.
- Firing behaviour is identical to existing triggers: instantiate the
ACTION_HANDLERS handler for the action type and call invoke() for each eligible session.
Scope questions to resolve
- Per-session vs. per-experiment: does a
ScheduledTrigger fire once globally (e.g. for all active sessions of an experiment) or is it pinned to a single session? The existing TimeoutTrigger is per-experiment-but-evaluated-per-session, which is probably the right model here too.
- Repeat / recurrence: out of scope for the first version — one-shot only.
- UI: a new row in the events table with a datetime picker instead of a delay input.
- Versioning: should follow the same
VersionsMixin pattern as TimeoutTrigger.
Acceptance criteria
Summary
The events framework currently supports two trigger types:
StaticTrigger— fires on a discrete event (conversation start/end, new message, participant joined, etc.)TimeoutTrigger— fires after a relative delay from some anchor point (last message, first message, inactivity)Neither supports firing at a specific absolute datetime. This is a gap for use-cases like:
Proposed new trigger type:
ScheduledTriggerA new model alongside
StaticTriggerandTimeoutTriggerthat fires at an explicitly specified datetime.Model fields (sketch)
experimentactionscheduled_attimezoneis_activeis_archivedworking_versionScheduling / execution
trigger_bot_message_taskapproach) polls forScheduledTriggerinstances whosescheduled_at <= nowandis_active=Trueand haven't already fired.django-celery-beatto create a one-shotPeriodicTaskwhen the trigger is saved, and delete it after firing.ACTION_HANDLERShandler for the action type and callinvoke()for each eligible session.Scope questions to resolve
ScheduledTriggerfire once globally (e.g. for all active sessions of an experiment) or is it pinned to a single session? The existingTimeoutTriggeris per-experiment-but-evaluated-per-session, which is probably the right model here too.VersionsMixinpattern asTimeoutTrigger.Acceptance criteria
ScheduledTriggermodel created with migrationEventLogis_active