From 3f3b977fb05aaf64952202948035c2c02ed5bd70 Mon Sep 17 00:00:00 2001 From: mixeme Date: Wed, 24 Jun 2026 20:58:51 +0300 Subject: [PATCH] app/run.go: resolve effective overlap policy per job in RunDue RunDue now resolves each job's effective overlap policy via effectiveOverlapPolicy (job value when set, else Config.OverlapPolicy) instead of reading the global policy once per tick. An empty Job.OverlapPolicy inherits the global default, so normalizeJobs continues to leave the field untouched rather than backfilling it. Co-Authored-By: Claude Opus 4.8 --- src/app/run.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/app/run.go b/src/app/run.go index 2a9a91e..a43d8c8 100644 --- a/src/app/run.go +++ b/src/app/run.go @@ -67,7 +67,6 @@ func (s *Service) RunDue(now time.Time) { var startErr error if !s.paused { sequential := s.store.Config.ExecutionMode == domain.ExecutionModeSequential - queue := s.store.Config.OverlapPolicy == domain.OverlapPolicyQueue running := s.anyRunningLocked() for index := range s.jobs { job := &s.jobs[index] @@ -77,8 +76,9 @@ func (s *Service) RunDue(now time.Time) { } if runtime.LastState == "Running" { // The job came due again while its own run is still in flight. - // Apply the overlap policy and step past this occurrence. - if queue { + // Apply the effective overlap policy and step past this + // occurrence. + if s.effectiveOverlapPolicy(job) == domain.OverlapPolicyQueue { runtime.Pending = true } s.advanceNextDueLocked(job, runtime, now) @@ -165,6 +165,17 @@ func (s *Service) executeRun(ctx context.Context, jobCopy domain.Job, trigger st s.emit(JobChanged{JobID: jobCopy.ID}) } +// effectiveOverlapPolicy resolves the overlap policy that actually governs a +// job: the job's own value when set, otherwise the global Config default. An +// empty Job.OverlapPolicy means "inherit the global default", which is why +// normalizeJobs leaves it empty rather than backfilling the configured value. +func (s *Service) effectiveOverlapPolicy(job *domain.Job) domain.OverlapPolicy { + if policy := domain.OverlapPolicy(strings.TrimSpace(job.OverlapPolicy)); policy != "" { + return policy + } + return s.store.Config.OverlapPolicy +} + // anyRunningLocked reports whether any loaded job is currently in the "Running" // state. It backs the sequential-mode guards in RunNow and RunDue. The caller // must hold mu.