New feature section: store the global pause in Config so "Pause all" survives restarts (config field, persist in SetGlobalPause, init service and UI from it). Renumber the later plan sections and task phases to fit. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
10 KiB
Release 0.10.0 — Milestone Plan
This milestone bundles the open roadmap follow-ups with six feature/bug-fix requests. It is a polish-and-fill release on top of 0.9.0: no new storage format and no architectural rework, just per-job run control, run-time statistics, UI compaction, persisted pause state, and screen-fit + packaging groundwork.
Build/test note: the GUI needs CGO + MSYS2 UCRT64; the default Bash env has CGO
off. Use scripts\test.bat / scripts\build-windows.bat on Windows. Confirm the
Linux test build with GOOS=linux go vet ./....
1. Selected Job Activity panel → one line per entry
Today the "Selected job activity" list (src/ui/jobs_view.go, jobLogs) renders
each record with app.EventText, which is wide and can wrap, making rows tall and
the panel noisy.
src/ui/jobs_view.go: in thejobLogsupdate callback, set the row label'sWrapping = fyne.TextTruncateso each record stays on exactly one line.src/app/format.go: add a compactEventLine(e domain.RunRecord) string(or aoneLine boolvariant) that drops the full log path and usesfilepath.Base(e.LogFile)— see §3. Keep the verboseEventTextfor the History tab.- Tests:
src/app/format_test.go— cover the one-line formatter, including the log-file-present and absent cases.
2. Job execution-time statistics
Capture how long each run takes and surface per-job aggregates.
src/domain/record.go: addDurationMS int64toRunRecord(JSON/struct tag consistent with the existing fields).src/runner/runner.go: measure wall-clock from command start to finish and set the duration on the record. ForStartOnlyjobs (fire-and-forget) record0or omit, since there is no completion to time.src/runner/logfile.go: write adurationline into the log header.src/domain/runtime.go: add an aggregate (RunCount,FailCount,LastDurationMS,AvgDurationMS,MaxDurationMS) toJobRuntime, updated inexecuteRunwhen a record is recorded.- Seed stats from log files on startup so they survive restarts. Add a
runnerhelper that parses theduration/stateheaders of a job's existing log files (matched by the_<sanitized name>.logsuffix, bounded byMaxLogFiles) and fold the results into the aggregate whenJobRuntimeis built. Reuse the §2 log-header format as the parse source. Tolerate older logs with nodurationline (count the run, skip the timing). src/app/format.go: addDisplayStats(rt)returning a one-line summary (e.g.12 runs · 1 failed · last 3.2s · avg 2.8s).src/ui/jobs_view.go: add a "Statistics" detail row (and refresh it inupdateDetails).- Tests:
src/app/format_test.gofor the formatter; extendsrc/app/run_test.goto assert the aggregate updates after fake runs; add arunnertest that seeds the aggregate from sample log files (including a duration-less legacy log).
3. Fix truncated log file name ("…lo")
The activity/History display shows the full log path and, when the cell is narrow,
truncates from the right so the visible text ends mid-extension (…\20240101-..lo).
- Show
filepath.Base(e.LogFile)(just20060102-150405_name.log) instead of the full path in the compact formatter from §1; the full path can stay in a tooltip or the History row. - Verify the History tab (
src/ui/history_view.go) column/truncation so the.logextension is never clipped to..lo. - Tests: assert the compact formatter emits the base filename.
4. Per-job run policy
Currently ExecutionMode and OverlapPolicy live only on Config
(src/domain/config.go) and are read globally in src/app/run.go. Make the
overlap policy configurable per job, falling back to the global default; keep
execution mode global (sequential is inherently a cross-job, one-at-a-time
guarantee and does not have a clean per-job meaning).
src/domain/job.go: addOverlapPolicy domain.OverlapPolicy \json:"overlap_policy,omitempty"`. Empty = inherit the globalConfig.OverlapPolicy`.src/app/run.goRunDue: resolve the effective policy per job (job.OverlapPolicyif set, elses.store.Config.OverlapPolicy) instead of reading the global value once per tick.src/app/operations.gonormalizeJob/src/storage/store.gonormalizeJobs: leave empty as "inherit" (do not force a default onto the job).src/ui/job_dialog.go: add an overlap-policywidget.Selectwith an "(Use global default)" first option that saves empty.src/ui/settings_view.go: clarify the global control is the default for jobs that don't override.src/app/format.go:DisplayRunMode(or a new helper) reflects the effective overlap policy in the details panel.- Tests: extend
src/app/run_test.go— a job withOverlapPolicyQueueset queues even when the global default isskip, and vice versa; empty inherits.
5. Adapt initial window size for 720p screens
src/ui/run.go resizes to 1120×720. On a 1366×768 / 720p display the title bar
- taskbar push the window off-screen.
src/ui/run.go: lower the default to a 720p-safe size (e.g.1024×660) and set aw.SetFixedSize(false)sensibleMinSizeon the content so it never demands more than fits.- Optionally persist the last window size via Fyne
Preferencesand restore it on launch, clamped to something that fits the current screen. - Re-check
commandOutputScroll.SetMinSize(520×160) andminJobsSidebarWidth(480) insrc/ui/jobs_view.goso the smaller default still lays out without forcing horizontal overflow. - Manual verification on a 1366×768 display (or a forced-resolution VM).
6. Persist the global "Pause all" state
The global pause (Service.paused, flipped by SetGlobalPause in
src/app/operations.go) is in-memory only, so "Pause all" is forgotten on restart
and the scheduler silently resumes — surprising for a deliberate emergency stop.
src/domain/config.go: addPaused bool \json:"paused,omitempty"``.src/app/operations.goSetGlobalPause: persist the new value intos.store.ConfigandSaveConfig, alongside the existing runtime updates andSchedulerStateChangedemit.src/app/service.go: initializes.pausedfromstore.Config.Pausedwhen the Service is built, and apply the paused next-run text to runtimes at startup so a restored-paused launch shows the right state before the first tick.src/ui/jobs_view.go: initialize the localschedulerPausedflag, the "Pause all"/"Resume all" button, and the scheduler-state label from the persisted state instead of hard-codingfalse.- Tests:
src/app/operations_test.go—SetGlobalPause(true)persists to config; a Service rebuilt from that store starts paused and refusesRunDue/RunNow.
7. Roadmap follow-ups (carried from ROADMAP.md)
- File-size soft limits.
src/ui/jobs_view.go(415) andsrc/app/operations_test.go(536) exceed the ~250 UI / ~400 cap guideline. This milestone adds rows tojobs_view.go(§1, §2, §4, §6) — split a clean seam out (e.g. the details-panel construction or the toolbar/button wiring) while it is already being edited. - Post-field-test cleanup. Sweep for stale diagnostics, over-defensive checks,
obsolete autostart-migration code, and noisy README setup notes now that 0.9.0
has had field use. Recheck
.gitignore/ Docker / packaging ignore rules. Keep the startup-timing instrumentation (the History "Started … in Xms" event) so startup time can keep being measured across future changes. - Drop the one-time YAML→JSON migration. The import shipped in 0.9.0, so the
transition window has passed. Remove the legacy import path:
src/storage/store.go: delete theyamlConfig/yamlJob/yamlJobsFileshadow structs,importYAMLConfig/importYAMLJobs, and the legacy-import branches inloadOrCreateConfig/loadOrCreateJobs.src/storage/paths.go: removelegacyYAMLConfigFileName/legacyYAMLJobsFileName.go.mod/go.sum: dropgo.yaml.in/yaml/v4(now unused) viago mod tidy.src/storage/store_test.go: remove the YAML-import tests and thewriteYAMLhelper..gitignore/.dockerignore: drop the*.yamlimport-window ignores.
- Architecture doc update. Refresh
docs/ARCHITECTURE.mdfor this milestone: the per-job overlap policy ondomain.Job(§4), the run-time statistics added todomain.JobRuntimeand seeded from log files (§2), the persisted global pause flag (§6), and anyjobs_view.gosplit (§7 file-size work).
8. Delivery and packaging (portable only)
This milestone targets only the portable distribution variants, matching the ROADMAP delivery plan. Non-portable installer/package formats are out of scope and have been dropped from the roadmap.
- Windows portable
.zipbundlinggosentry.exe,README.md,CHANGELOG.md(ascripts\package-windows.*helper). - Linux portable
.tar.gzforlinux-amd64andlinux-arm64bundling the binary,README.md, andCHANGELOG.md(ascripts/package-linux.*helper). - Portable builds keep settings and jobs next to the executable — no per-user data-path work is needed for this release.
Implementation order
- §3 log-name fix + §1 one-line activity (shared compact formatter).
- §2 execution-time stats (record → runtime aggregate → details row).
- §4 per-job overlap policy (domain → dispatch → dialog → tests).
- §6 persist the global pause state (config → service → UI init).
- §5 window sizing.
- §7 jobs_view split + cleanup (after the §1/§2/§4/§6 edits land).
- §8 portable archives (Windows
.zip, Linux.tar.gz). - Docs: update
docs/ARCHITECTURE.md(§7); version bump to0.10.0(src/app/version.go), CHANGELOG, ROADMAP tick-offs.
Verification
go vet ./...clean;go test ./...green on Windows (CGO) and Linux.- Activity panel: each entry is one line; log filename shows the base name with a
full
.logextension (no..lo). - Details panel shows live run-time statistics that update after runs.
- A per-job overlap policy overrides the global default; an unset job inherits it.
- "Pause all" survives a restart: a paused install relaunches paused.
- The window opens fully visible on a 1366×768 / 720p screen.
- Bump and document the release; append any startup re-measure to PERFORMANCE.md.