Files
gosentry/src/domain/runtime.go
T
mix 18da021526 chore: land the remaining low-severity items from the whole-project review
Phase 11 of PROJECT_REVIEW_PLAN.md: the themed cleanup pass over every
low-severity finding still open (2.2-2.3, 3.4-3.6, 4.3-4.7, 6.4-6.7,
7.1-7.3, 8.2-8.3, 9.1-9.4, and the under-documented decisions in §10/§11).

Behavioral fixes:
- Reassign duplicate job IDs in a hand-edited jobs.json instead of letting
  two jobs share one runtime, schedule entry, and SeedStats bucket.
- Disambiguate run-log file names that collide within the same second.
- Compute AvgDurationMS as DurationSumMS/TimedRunCount instead of an
  incremental integer mean, so it always matches the seeded-from-logs
  average instead of drifting from truncation error.
- Clean absolute paths in ResolveConfiguredPath so two spellings of the
  same jobs file do not trigger a spurious adoption.
- Report InstallDesktopIcon failures through ErrorOccurred instead of
  discarding them silently.
- Move settingsView's blocking AutostartStatus (PowerShell on Windows) off
  the UI thread.
- Give notify-timing.tsv its own extension so CleanupLogs no longer
  manages it as a run log.
- Replace the settingsView Save handler's second copy of validateConfig's
  rules with a bare parse, letting the Service's own error surface.

Cleanups:
- Delete collectActivity, the dead yaml tags on RunRecord, and the
  logArguments/LogArguments alias.
- Fold the two systemTrayRegistered/mainWindowHidden globals into one
  trayState instance Run owns and threads through Settings and the
  single-instance reveal path.
- Fix stale comments/docs: the frozen window-size restore claim, a
  reference to a renamed recordRun, README's "Pause all" and notification
  wording, the PowerShell quoting note for TESTS.md's coverage command,
  and scripts/test.bat's UTF-8 checkmarks under a non-UTF-8 code page.
- Document the single-instance fallback's consequence and the
  unauthenticated instance-channel port in STANDARDS.md; record the
  config-shim retirement plan in ROADMAP.md.

3.5, 7.3, and 9.4 turned out to already be fixed by earlier phases; no
change needed for those three.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 03:35:10 +03:00

76 lines
2.8 KiB
Go

package domain
import "time"
// JobRuntime is the transient execution state for a Job. It is never written to
// jobs.json: it is rebuilt from scratch each time GoSentry starts and is held in
// memory keyed by Job.ID for the lifetime of the process. Keeping it separate
// from Job is what lets the durable configuration file stay free of run records,
// status strings, and scheduling bookkeeping.
type JobRuntime struct {
LastRun string
NextRun string
LastState string
Output string
Logs []RunRecord
// NextDue is the next scheduled execution time, kept as time.Time for
// scheduler comparisons. NextRun above is its formatted display string and is
// the only form shown in the GUI.
NextDue time.Time
// PendingRuns counts scheduled occurrences that fired while a run was still
// in flight under the "queue" overlap policy. executeRun drains the counter
// by starting one deferred run after each completion.
PendingRuns int
// Execution-time statistics accumulated since the last process start.
// Seeded from log files on startup; zero until then.
RunCount int
FailCount int
LastDurationMS int64
AvgDurationMS int64
MaxDurationMS int64
// TimedRunCount is the number of runs that contributed to AvgDurationMS.
// Runs with no recorded duration (legacy logs, or sub-millisecond StartOnly
// launches that round to 0) increment RunCount but not this. StartOnly runs
// otherwise contribute their launch latency.
TimedRunCount int
// DurationSumMS is the running total of every timed run's duration.
// AvgDurationMS is always DurationSumMS/TimedRunCount, computed fresh on each
// update rather than folded incrementally — an incremental integer mean
// truncates on every step, and the error compounds over the life of a job
// that keeps running. A stored sum divided once per update matches the exact
// sum/count average runner.aggregateLogStats computes when seeding from logs,
// so the two no longer disagree about the same run history.
DurationSumMS int64
}
// NewRuntime builds the initial runtime state for a freshly loaded or created
// job. Enabled jobs start "Ready" and wait for the scheduler to compute their
// first run; disabled jobs start "Paused".
func NewRuntime(job Job) *JobRuntime {
runtime := &JobRuntime{
LastRun: "Never",
Output: "No command output captured yet.",
}
if job.Enabled {
runtime.LastState = "Ready"
runtime.NextRun = "After start"
} else {
runtime.LastState = "Paused"
runtime.NextRun = "Paused"
}
return runtime
}
// NewRuntimes builds a runtime map for a slice of jobs, keyed by Job.ID. It is
// the convenience entry point used when a whole jobs file has just been loaded.
func NewRuntimes(jobs []Job) map[int]*JobRuntime {
runtimes := make(map[int]*JobRuntime, len(jobs))
for _, job := range jobs {
runtimes[job.ID] = NewRuntime(job)
}
return runtimes
}