Repository navigation
feat(automations): timers that outlast a restart and fire once - #446
Merged
Merged
Conversation
A timer table and timers.py: set (make or replace by key), cancel, cancel_for (an automation's), and @handler(kind). A thread started with the routines' threads (the API's background work and lens worker) looks every 5 seconds and at once after events.wake(), claims each due timer with a compare-and-set update only one process wins, and runs its handler. A repeating handler (again) is re-armed in that same update. Timers due while Lens was off fire late within automations.catch_up (1h, Settings › Automations) or are missed and counted on their automation; a repeating one fires once for its latest time. At most 1000 waiting per automation; ended ones are tidied after 7 days. No query while no timer waits. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KfD6cia42GiEZakbs74jxY
…-gauy4d # Conflicts: # docs/automations.md # docs/database.md # fastapi_backend/app/domain/store.py
This was referenced Oct 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Adeel · project thread
Description
Before: automations had no way to wait for a time. Nothing in Lens could say "run this at 07:00" or "in two hours" and keep that promise across a restart.
After: Lens keeps timers in the database (
app/domain/timers.py, workflow studio module M4, part 1 of 3). A timer fires once when it is due, even with the API andlens workerboth looking, and a timer that came due while Lens was off still fires if it is less than an hour late (Settings → Automations → "After Lens was off, still run timers up to"). Older ones count as missed. Nothing changes for an install until an automation uses time.How: one
timerrow per key (timer:<hash of key>, indexed by(status, at)and(automation, status)).setmakes or replaces one,cancel/cancel_forcall them off, and@handler(kind)registers what runs. A thread started insideroutines.start(so in the API's background work and inlens worker) looks every 5 seconds and at once afterevents.wake(). It claims each due timer with a compare-and-setUPDATE ... WHERE status = 'waiting' AND at = $atthat only one process wins, then runs the handler. A repeating handler (again, used by schedules in the next PR) is re-armed in that same update, so a crash between firing and re-arming can't lose a schedule. The tidy runs fromroutines.run_due, once an hour.Motivation and Context
Workflow studio M4 (
/mnt/project-files/workflow-studio/briefs/M4.md, plan section 3.10). M5 (joins:absent,batch) needsset,cancelandhandler. M6 needslater, which comes in part 3. The PRs that follow:atandrelativetriggers, theclock.tickkind, andupcoming.later,intent.perform).Defaults I picked (all reversible):
LATE_SECONDS). Inside that window it fires even withcatch_up: 0s, so normal polling is never mistaken for downtime.automation.missed. M6 owns that table, so the update does nothing until M6 stores automations.Types of changes
!in the title)Checklist:
make hookschecks it on commit).docs/accordingly.## UnreleasedinCHANGELOG.md. (n/a: nothing user-facing yet beyond one setting)Done questions (AGENTS.md)
/api/v1/settings/automationsand the admin assistant's settings tools (ops_tools.SECTIONS). Timers have no API of their own: M6 exposesupcomingat/api/v1/automations/{id}/upcoming.SCHEMA(DEFINE ... IF NOT EXISTS), and the thread starts where the routines' threads do.automations.catch_up(default1h) in Settings → Automations; not in the wizard.SCHEMA; no stored data changes shape.docs/automations.mdlabels timers built, and triggers and waiting actions planned.tests/domain/test_timers.py: once across two processes and four threads, replace and cancel, repeating re-arm, late and missed, latest missed time, cap, tidy, idle cache, the thread, the setting through the API),docs/automations.md(Time),docs/configuration.md,docs/database.md. Run locally on embedded SurrealDB and on a 3.2.4 server. No generated files change.docs/automations.md.🤖 Generated with Claude Code
https://claude.ai/code/session_01KfD6cia42GiEZakbs74jxY
Generated by Claude Code