feat: per-job command timeout with global default

Add an optional per-job run timeout following the overlap_policy inherit
pattern: Job.TimeoutSeconds (0 = inherit) resolves against a new
Config.DefaultTimeoutSeconds (default 30s), replacing the hard-coded 30s
guard in runner.RunJob.

- domain/storage: new fields, default 30, load-time normalization
- runner: RunJob takes an explicit timeout; StartOnly stays untimed so it
  keeps measuring launch latency only
- app: effectiveTimeout resolves under mu into runEnv, threaded to runJob;
  seam signature and validation updated; DisplayTimeout helper
- ui: Timeout entry in the job dialog, Default timeout in Settings, and a
  Timeout row in the details panel
- tests + docs (ARCHITECTURE, STANDARDS, ROADMAP, CHANGELOG) updated;
  version bumped to 0.12.0

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
mixeme
2026-07-25 23:24:50 +03:00
parent f4221f6ce4
commit 48faddb3bd
23 changed files with 270 additions and 87 deletions
+11
View File
@@ -102,6 +102,17 @@ func DisplayOverlapPolicy(job domain.Job, globalPolicy domain.OverlapPolicy) str
return string(globalPolicy) + " (global default)"
}
// DisplayTimeout formats a job's effective run timeout for the details panel.
// When the job sets its own TimeoutSeconds it is shown as-is; when 0 (inherit),
// the global default is shown with "(global default)" appended, mirroring
// DisplayOverlapPolicy.
func DisplayTimeout(job domain.Job, globalDefault int) string {
if job.TimeoutSeconds > 0 {
return fmt.Sprintf("%d s", job.TimeoutSeconds)
}
return fmt.Sprintf("%d s (global default)", globalDefault)
}
// DisplayIndex returns the position of jobIndex in the given slice of indexes,
// or 0 if not found.
func DisplayIndex(indexes []int, jobIndex int) int {