Commit Graph

57 Commits

Author SHA1 Message Date
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
mix ca2a8c8aa7 fix: accept zero retention limits, retire Store() for typed accessors
Phase 8 (PROJECT_REVIEW_PLAN.md 8.1): 0 in MaxLogFiles/MaxLogAgeDays now
means "keep everything" end to end. runner.CleanupLogs already treated
<= 0 as disabled; validateConfig, the Settings form, and
loadOrCreateConfig's backfill were the only things making that state
unreachable.

Phase 9 (1.1, rolling up 1.2, 1.3, 7.3): added Service.Config() and
Service.Paths(), copying under mu, and converted every UI site that read
Service state through the raw *storage.Store returned by Store() (now
removed). jobs_view's pause control is now driven by refreshView reading
svc.Config().Paused on every event instead of only mirroring its own tap
handler, which makes it an actual consumer of SchedulerStateChanged.
mainwindow's event listener is a real type switch, and events.go's doc
comment no longer claims a compiler exhaustiveness check Go doesn't have.
Unexported the redundant SetAutostart/AutostartStatus package functions
in platform/autostart now that only the Manager methods are used outside
the package.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 22:15:19 +03:00
mix 0c8442a8d1 perf: keep file I/O off Service.mu and untie StartOnly from the app context
Phase 7 of the whole-project review (findings 3.2 and 3.3).

Service.mu is the lock the Fyne main thread takes on every Jobs() and
Runtime() call, so anything blocking inside it makes a UI refresh wait on
the disk. Three things did:

- Every SaveJobs/SaveConfig was a marshal, fsync, and rename under mu.
  Writes are now prepared under the lock (Store.PrepareSaveJobs /
  PrepareSaveConfig snapshot the payload and target path) and run after
  it is released. deferSaveLocked takes saveMu while mu is still held, so
  writes still reach the file in the order their snapshots were taken and
  an older snapshot can never land on top of a newer one.
- executeRun ran runner.CleanupLogs under mu after every run. It needs
  only the values already snapshotted into runEnv, so it now runs after
  the unlock — including when the job is gone, since the run still wrote
  a log file that retention covers.
- adoptJobsLocked ran runner.SeedStats under mu, reached from
  UpdateSettings on the UI thread. Seeding moved out into
  applySeededStatsLocked; UpdateSettings now reads the new jobs file and
  seeds its statistics before taking the lock, and re-checks the
  "no jobs-file switch while running" guard once it has it.

SeedStats also opened every log file twice — once to find the job, again
to read the result. readLogSummary reads job_id, state, and duration in
one pass, so each log is opened once.

StartOnly runs were built with exec.CommandContext on the app's lifecycle
context. os/exec keeps a watcher goroutine alive until Wait returns or the
context is done, and StartOnly never calls Wait, so one goroutine leaked
per run and would then try to kill a process whose handle startJobOnly had
already released. The invocation now uses context.Background(), whose nil
Done channel means no watcher is started at all.

Regression tests: TestRunJobStartOnlyLeavesNoContextWatcher (fails with 5
leaked goroutines on the old code), TestConcurrentJobOperationsLeaveTheFileMatchingMemory,
and TestUpdateSettingsSeedsAdoptedJobsFromLogs. STANDARDS gains the
no-I/O-under-mu rule and the "a StartOnly process outlives GoSentry" entry.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 21:44:51 +03:00
mix 263717874c fix: remove no-op SaveJobs calls, fix PendingRuns lifecycle and cap
Implements items 4-5 of the whole-project review's suggested order
(docs/PROJECT_REVIEW_PLAN.md):

- Drop the three SaveJobs calls in the run lifecycle (startRunLocked,
  executeRun, SetGlobalPause): none of them change a durable Job field,
  everything they touch lives on JobRuntime, which is never persisted.
  Retire TestStartRunLockedRollbackOnSaveFailure with the rollback it
  guarded, since a run can no longer fail to start this way.
- Clear PendingRuns (the "queue" overlap policy's backlog) when a job is
  disabled or the scheduler is globally paused, so resuming or
  re-enabling a job no longer replays a deferred run left over from
  before the pause/disable. Cap it at maxPendingRuns (10) so a job whose
  runs take longer than its own interval stops accumulating an unbounded
  backlog. Surface the queued count in the details pane via DisplayStats.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 17:06:55 +03:00
mix c8a4d31441 release: 1.0.2, tray runtime wiring and Windows notification polish
Move post-1.0.1 changes into a 1.0.2 changelog section and restore 1.0.1
to the shipped release notes.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 23:26:39 +03:00
mix 5b0e6fe51b Wire KeepRunningInTray to runtime so tray, close, and autostart follow the saved setting.
Autostart entries pass --start-in-tray only when the tray is enabled; Settings warns that the notification icon needs a restart (Fyne limitation).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 22:44:02 +03:00
mix 5170cc5f99 release: 1.0.1, branded theme default and test-suite review
Move post-v1.0.0 changes out of the 1.0.0 changelog section into 1.0.1,
where they belong: GoSentry theme as default, System label, About GitHub link,
Disable auto inset, README @every docs, and the completed test-suite review.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 22:58:13 +03:00
mix 28f0a0d8e2 test: resolve the thin-test item, decline the runner merge
Item 4 of the test-suite review:

- Delete TestEmitWithNoObserversIsNoop (no assertion; ranging a nil slice
  cannot panic) and TestStoreReturnsWiredStore (a getter returning its
  own field).
- Collapse the four TestFilteredJobIndexes* tests into one table-driven
  TestFilteredJobIndexes, matching TestFilterValue above it.
- Replace the TestMainViewBuilds smoke test with
  TestMainViewRecordStartupAddsHistoryRow, which calls the recordStartup
  closure for both wordings run.go selects between and asserts the rows
  reach the History table through its own cell callbacks. Keeps the
  unique coverage the review identified and adds the !windowShown branch.

Item 5 is declined with measurements: the three RunJob tests cost 0.14 s
combined, so merging them saves ~90 ms while forcing their three
fixtures (including the only Manual trigger) into one. The runner
package's runtime is the two timeout tests, not subprocess spawns.

go vet and go test -race pass for src/app and src/ui.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 22:46:26 +03:00
mix 2ef18e759c test: delete duplicate-coverage tests, fix TESTS.md drift, drop hand-rolled itoa
Items 1-3 of the 2026-08-04 test-suite review: TestCleanupLogsKeepsFilesWithinAgeLimit,
TestRunDueEmptyOverlapInheritsGlobal, and TestSameWindowsPathHandlesSpaces had
byte-identical coverage to an existing test and no assertion the survivor lacked.
storage.defaultJobs, the one accidental 0% coverage gap the review found, is now
covered and TESTS.md corrected to match. seed_test.go's itoa is replaced with
strconv.FormatInt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 22:30:27 +03:00
mix b2402f4c72 Rename the Default theme option to System.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 21:49:06 +03:00
mix 276539c383 Make the branded GoSentry theme the default.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 19:18:48 +03:00
mixeme bde9a2e33e release: 1.0.0, and record the details pane's width coupling
The 0.16.0 release was never tagged or pushed, so what it described ships
as 1.0.0 instead: the changelog section is renamed rather than followed by
an empty one, and there is no 0.16.0 for anyone to have seen.

The last remainder of the layout review's F9 goes in with it. The value
column in captionValueLayout has no minimum of its own — it takes whatever
the container leaves after the caption — and what actually keeps it
readable is the 460 px minimum on commandOutputScroll, a constant that
exists for command output being legible. The dependency was invisible at
both ends; both now state it, so lowering that width is a decision rather
than an accident. The HSplit divider is the user's side of the same thing:
it is how the value column can be widened.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 17:15:19 +03:00
mixeme c5f300b670 docs: close the GUI layout review, release 0.16.0
Stage 9 of the GUI layout plan: the roadmap item the review was raised
under is closed, so the plan and the findings document go with it — what
they established now lives in STANDARDS and the CHANGELOG.

STANDARDS gains the rule the review produced: a size that must follow the
theme is measured at build time, not written as a pixel constant, because
a hand-tuned number is only correct for the theme it was tuned against.
rowOverlap, captionColumnWidth, textColumnWidth, activityRowsHeight and
initialSplitOffset are the worked examples.

The CHANGELOG entry keeps to what the user can see: the window opens at
the size it asks for and drags smaller, the Jobs divider is draggable,
History columns hold their content on a scaled UI, and the Settings
button row and block spacing are as their layouts intended.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 16:42:20 +03:00
mixeme 721b049100 feat: select the jobs file itself in Settings
The Jobs directory row named a folder and assumed the file inside it was
called jobs.json. It is now a Jobs file row: Browse opens a file picker
filtered to .json, the field stays editable so a file that does not exist yet
can be typed, and the job list can live under any name.

Config.JobsDir/jobs_dir becomes Config.JobsFile/jobs_file, holding the whole
path; Paths.JobsDir is derived from it so saves still create the folder. An
older gosentry.json is migrated on load by joining its jobs_dir with
jobs.json — the exact file that version used — and the retired key is dropped
when the config is rewritten. The default clears before unmarshalling, or a
file that omits jobs_file and a file that sets it would be indistinguishable
and the migration would never run.

Saving used to write the current job list over whatever was at the new path,
which made switching to an existing jobs file impossible: its contents were
destroyed. An existing file now wins. Its jobs are loaded, normalized, and
adopted, with runtimes, schedule cache, next-run times and log-seeded
statistics rebuilt around them by adoptJobsLocked — the same helper NewService
now uses, so construction and adoption cannot drift. A path with no file
behind it still receives the current jobs, which is how the file is renamed or
relocated. The new file is read before anything is written, so an unparsable
one leaves both the config and the jobs untouched.

Adoption drops every runtime, and a run finishing afterwards would write its
result onto whichever job inherited its ID, so the switch is refused while a
job is running. Unrelated settings still save during a run. Because the
replacement happens without a prompt, the Service emits JobsLoaded with the
path and count, and History carries the receipt.

A path that names only a folder (trailing separator, a dot, or two dots) is
rejected with a validation error instead of failing later with an opaque OS
error.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:43:56 +03:00
mixeme e5f8c7a812 docs: changelog and version bump for 0.14.0
Close the Unreleased section as 0.14.0 and fill the gaps in it: the Folder
caption moving onto the filter row, the padding around the Settings button
row, the Truncation-field refactor, the Docker build cache mount, and the
review/standards documents added since 0.13.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:11:31 +03:00
mixeme 29ce94c3e8 feat: add a compact job list view to the Jobs tab
Each job can now render as a single line — name on the left, status on the
right — instead of the three-line block, so many more jobs fit without
scrolling. A toggle button beside the Folder filter switches between the two
modes and is labelled with the action it performs, matching the existing
"Disable auto" convention.

The choice is persisted as Config.JobListView ("detailed" / "compact", stored
as job_list_view in gosentry.json). Empty, legacy, and unrecognised values all
normalize to detailed, so existing installs keep the current look and the file
never gains a value no reader understands.

Selection, the details panel, the folder filter, and live status updates work
unchanged in both modes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:44:30 +03:00
mixeme e85cbc4eb1 feat: make per-job timeout 0 mean "no timeout" instead of inherit
A per-job timeout now has three distinct states: unset inherits the global
default, an explicit 0 means no timeout and does not inherit, and a positive
value is the per-job limit. Job.TimeoutSeconds became *int so unset and 0 stay
distinguishable in jobs.json.

Also fixes the global default, which could not persist a 0. loadOrCreateConfig
normalized DefaultTimeoutSeconds <= 0 back to 30 on every read of an existing
gosentry.json, so "no timeout" only held until the next restart. The field is
now written unconditionally (no omitempty) and read back as-is.

Existing jobs and configs are unaffected: a job with no timeout_seconds still
inherits, and a saved global default of 30 stays 30.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:44:30 +03:00
mixeme 33a246cd13 feat: make global default timeout 0 (infinite) instead of required 30s
DefaultTimeoutSeconds now means "no timeout" when 0/empty, and that is
the new default, rather than an invalid config forcing a positive
value. runner.RunJob avoids context.WithTimeout with a zero duration
(which would expire immediately) and instead runs on a plain
cancelable context when no timeout is configured. Per-job
TimeoutSeconds inherit semantics are unchanged.
2026-07-26 13:57:03 +03:00
mixeme 3992b40eda docs: changelog and version bump for 0.13.0
Documents the branded GoSentry theme and Settings Cancel/Defaults buttons.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 11:26:10 +03:00
mixeme 84212764e4 feat: selectable branded GoSentry color theme
Add a custom Fyne theme derived from the logo and app icon (deep teal
primary, amber accent, branded job-status colors) with light and dark
variants, and let the user choose between it and Fyne's default theme
from Settings. The dark variant uses deep-teal surfaces to echo the app
icon.

The choice is persisted as a new Config.Theme field ("default" /
"gosentry"), applied at startup before the first frame and live-previewed
when picked in Settings. Empty/legacy configs normalize to the default
theme so existing installs keep the original look.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 11:26:09 +03:00
mixeme 48faddb3bd 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>
2026-07-25 23:24:50 +03:00
mixeme 9216f01bf6 release: bump version to 0.11.5
Replace interim FUTURE_WORK.md with slim STANDARDS.md and update doc links.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 23:16:24 +03:00
mixeme aed83b91b9 docs: add FUTURE_WORK quality standard and close polish gaps
Replace CODE_REVIEW.md with a living maturity checklist, document
session-only History, inject Service into newMainView for testability,
add UI and scheduler regression tests, and fix RunNow error surfacing
plus empty jobs view handling.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 23:14:09 +03:00
mixeme eba7bff17a release: bump version to 0.11.4
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 00:05:47 +03:00
mixeme 00991f24a5 release: bump version to 0.11.3
Document the code-review reliability fixes in CHANGELOG.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 21:56:38 +03:00
mixeme 29d2ffed8f fix: complete code review follow-ups for queue, stats, and docs
Replace overlap Pending flag with PendingRuns counter, match seed stats
by job_id, align average duration with TimedRunCount, and tidy docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 21:54:44 +03:00
mixeme e9fc9eaba0 fix: harden run persistence and error surfacing from code review
Snapshot store paths under lock before async runs, roll back failed
start/save state, emit UI events only after successful persistence,
surface log write failures, and sync stale YAML docs to JSON.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 21:33:50 +03:00
mixeme 882fcfe65d chore: release 0.11.2 with window persistence, appID update, and doc fixes
- Persist window size on quit/close and restore it on next launch
- Update appID from ru.mixdep.gosentry.desktop to ru.mixeme.gosentry.desktop
- Use markdown headers for Build sections in DEVELOPMENT.md
- Remove stale go.yaml.in/yaml/v4 dependency from DEVELOPMENT.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-25 22:50:28 +03:00
mixeme 0e258979d9 chore: release 0.11.1 with settings spacing, label, and Save-button fixes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 08:50:10 +03:00
mixeme 24a8140e26 chore: release 0.11.0 with paused manual runs and two-column layouts
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 07:52:10 +03:00
mixeme 9ecb8b61f8 feat(ui): two-column settings/details, compact job list, manual run while paused
- Allow manual "Run now" while the scheduler is paused: pause now stops only
  automatic scheduled runs (RunDue), not the user's explicit action. Drop the
  paused guard in Service.RunNow and the UI pause dialog; update tests.
- Cap the details metadata caption width via a new captionValueLayout so a wider
  window feeds extra space to the value column instead of the short caption.
- Reorganize the Settings tab into two columns (Application+Queue / Storage+About)
  with Save spanning the full width; move the Autostart status onto its own line.
- Condense the Jobs list rows with compactVBoxLayout to fit more jobs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 07:51:03 +03:00
mixeme 8b81180c92 chore: release 0.10.2 with condensed panels and settings spacing fix
Bump version to 0.10.2 and document the UI density/resizing work in the
changelog. Also restructure the settings form into sections so separators
and the editable Storage fields keep normal spacing (dividers no longer
crowd the row above, entry boxes stay visibly separated) while the
label-only sections remain condensed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 01:41:02 +03:00
mixeme ec19ab3884 chore: bump version to 0.10.1 and update changelog
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-24 23:02:45 +03:00
mixeme 80d13d0ac6 app/operations_test.go: persist-pause + rebuilt-service tests (T4.4)
TestSetGlobalPausePersistsToConfigFile reads gosentry.json back from disk
after each SetGlobalPause call and asserts the Paused flag is correctly
written and cleared. TestServiceRebuiltFromPausedStoreStartsPaused builds
a second Service from the already-updated store and confirms both RunDue
and RunNow are refused while the service is paused.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 22:21:24 +03:00
mixeme b9505e83ab domain/config.go + app: persist global pause state across restarts (T4.1, T4.2)
Add Config.Paused bool so the scheduler's paused flag survives a restart.
SetGlobalPause now writes the flag into store.Config and calls SaveConfig;
NewService seeds s.paused from Config.Paused before computing first next-run
times, so jobs show "Scheduler paused" immediately at startup when paused.
Also add Paused to the yamlConfig shadow struct to keep the direct conversion
from domain.Config valid until the YAML import path is dropped in T6.3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 22:16:43 +03:00
mixeme b0d9883627 app/run_test.go: per-job overlap policy override tests (T3.4)
Adds three tests for the effective-policy resolution:
- per-job "queue" overrides global "skip" (job marked Pending, re-runs)
- per-job "skip" overrides global "queue" (overlap dropped, no re-run)
- empty Job.OverlapPolicy inherits the global default

Also ticks Phase 3 complete in docs/RELEASE-0.10-TASKS.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:33:06 +03:00
mixeme 554ce2b93a ui/app: per-job overlap policy in job dialog and details panel
- job_dialog.go: add overlap-policy widget.Select with "(Use global
  default)" → empty, "skip", and "queue" options; pre-selects the job's
  current value; on save maps the inherit label back to empty string.
- app/format.go: add DisplayOverlapPolicy(job, globalPolicy) — returns
  the policy name when overridden, else "skip/queue (global default)".
- ui/jobs_view.go: add "Overlap policy" detail row updated in
  updateDetails using DisplayOverlapPolicy.
- settings_view.go: rename "Overlap policy" label to "Default overlap
  policy" to clarify it is the global default jobs can override.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 21:05:50 +03:00
mixeme 3f3b977fb0 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 <noreply@anthropic.com>
2026-06-24 20:58:51 +03:00
mixeme b45ab654c2 app/version, docs/CHANGELOG: bump to 0.10.0 (T8.1)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:26:03 +03:00
mixeme 886d0d9caa app/format, ui/jobs_view: DisplayStats summary + Statistics detail row (T2.6, T2.7)
Add DisplayStats to format.go: returns "No runs recorded" when RunCount is
zero, otherwise a one-line "N runs, M failed, last X ms, avg Y ms, max Z ms"
summary.  Wire a Statistics detail row into the jobs panel that refreshes via
updateDetails alongside the other runtime fields.

Tests: TestDisplayStats (nil/zero/normal/no-fail), TestUpdateStats (three
sequential fake runs through updateStats verifying all aggregate fields), and
five seed tests in runner/seed_test.go covering basic aggregation,
duration-less legacy logs, maxFiles capping, missing directory, and
unmatched log files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:24:48 +03:00
mixeme 6c69f323bd runner/seed, app/service: seed run-time stats from log files (T2.5)
Add SeedStats, which reconstructs per-job execution-time aggregates from
existing log files: suffix-matched by sanitized job name, bounded by
MaxLogFiles, and tolerant of duration-less legacy logs (counted in
run/fail totals but excluded from last/avg/max). NewService folds the
seed into each JobRuntime at build time so stats survive a restart.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 08:16:47 +03:00
mixeme 7dafa82b21 domain/runtime, app/run: add execution-time statistics aggregate (T2.4)
Add RunCount, FailCount, LastDurationMS, AvgDurationMS, MaxDurationMS
to JobRuntime and fold each completed RunRecord into them via updateStats
called from executeRun.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 08:13:02 +03:00
mixeme 61ea3ce2af app/format_test: add EventLine test cases (T1.4)
Cover the compact formatter with three scenarios:
- Log file present: shows base name only (not full path)
- Log file absent: formats without a log column
- Empty trigger: defaults to "Unknown"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 07:52:53 +03:00
mixeme 8fc28f592d app/format: add EventLine compact formatter (T1.1)
Uses filepath.Base for the log file so the jobs log view can show
one-line entries without the full directory path cluttering the row.
EventText is kept intact for the History table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 07:49:26 +03:00
mixeme 0b8c340f6e Bump version to 0.9.0; update changelog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 22:46:57 +03:00
mixeme 6136bb645b Bump version to 0.8.0; update changelog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 08:20:29 +03:00
mixeme 564609d8fb P4.1: Wire desktop notifications for failed job runs
Add Service.ShouldNotifyOnFailure() (reads config under mu) and call
fyne.CurrentApp().SendNotification in the mainwindow subscriber when a
Manual/Schedule run completes with State == "Failed" and the setting is on.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 08:18:12 +03:00
mixeme fb9d0650a6 Bump version to 0.7.0; update changelog
Phase 3 complete: execution modes and overlap policies for job dispatch.

- Bumped version from 0.6.0 to 0.7.0 in src/app/version.go
- Added 0.7.0 entry to docs/CHANGELOG.md documenting parallel/sequential
  execution modes and skip/queue overlap policies, and their interaction
  with the Settings UI and manual runs.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-23 08:05:03 +03:00
mixeme 6b8f9a4ccf P3.5: Add queue dispatch tests
New src/app/run_test.go exercises the execution mode + overlap policy
dispatch paths added in P3.3, reusing the fake runJob seam:

- parallel mode starts all due jobs concurrently
- sequential mode serializes due jobs one at a time
- skip overlap policy drops a re-fire while running (no Pending)
- queue overlap policy marks Pending and re-runs after the run finishes
- RunNow refuses a manual run while another job runs in sequential mode

Helpers prime NextDue to the past and tick at the wall clock so a started
job (advanced an hour out) does not spuriously re-fire.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 08:02:20 +03:00
mixeme b3e699792a P3.3: Apply execution mode + overlap policy in dispatch
Rework RunDue/startRunLocked/executeRun so the ExecutionMode and
OverlapPolicy config knobs take effect:

- startRunLocked advances NextDue to the next occurrence instead of
  zeroing it, keeping the schedule marching during an in-flight run.
- RunDue scans all due jobs: parallel starts every due, non-running job;
  sequential defers a due job while any other job runs. When a job comes
  due again mid-run, skip drops it and queue marks it Pending; either way
  NextDue is advanced past the fired occurrence.
- executeRun re-runs a Pending job once the current run finishes.
- RunNow gains a sequential-mode guard refusing a manual run while
  another job is running.
- Add anyRunningLocked and advanceNextDueLocked helpers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 07:54:12 +03:00