Fire accepted routines every interval, not once (Vikunja #366)
The tick loop now reads accepted routines from the store and nudges when their interval has passed; accepting no longer builds a one-shot reminder. Look at routine.DueAccepted for the schedule rule (no catch-up backlog) and at fireAcceptedRoutines for the restraint gate — routines do not bypass it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
This commit is contained in:
@@ -55,6 +55,47 @@ func Validate(routines []Routine) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Accepted — an accepted routine proposal as the tick driver sees it. This is a
|
||||
// different shape from Routine: the schedule is a plain interval the pattern
|
||||
// detector measured, not an operator-written cron expression. Accepted is when
|
||||
// the human said yes; LastFired is nil until the first nudge.
|
||||
type Accepted struct {
|
||||
ID int64
|
||||
Name string
|
||||
IntervalDays float64
|
||||
Accepted time.Time
|
||||
LastFired *time.Time
|
||||
}
|
||||
|
||||
// DueAccepted returns the accepted routines whose interval has passed. It does
|
||||
// not mutate anything — the caller persists the new last-fired time, because
|
||||
// that has to survive a restart (unlike Due's in-memory map).
|
||||
//
|
||||
// The clock starts at LastFired, or at Accepted for a routine that has never
|
||||
// nudged. A routine with a non-positive interval never fires: a bad interval
|
||||
// should mean silence, not a nudge every tick.
|
||||
//
|
||||
// One occurrence per call, no catch-up: the caller stamps the fire time as now,
|
||||
// so a routine that was silent for a month nudges once and then waits a full
|
||||
// interval. Never a backlog.
|
||||
func DueAccepted(rs []Accepted, now time.Time) []Accepted {
|
||||
var out []Accepted
|
||||
for _, r := range rs {
|
||||
if r.IntervalDays <= 0 {
|
||||
continue
|
||||
}
|
||||
since := r.Accepted
|
||||
if r.LastFired != nil {
|
||||
since = *r.LastFired
|
||||
}
|
||||
gap := time.Duration(r.IntervalDays * 24 * float64(time.Hour))
|
||||
if !now.Before(since.Add(gap)) {
|
||||
out = append(out, r)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Due returns the routines whose schedule crossed since their last fire and
|
||||
// records now as the new last-fire time for each one returned. The caller owns
|
||||
// `last` (the tick driver holds it across ticks); Due mutates it in place.
|
||||
|
||||
Reference in New Issue
Block a user