Document the planned work to take GoSentry from the v0.4.0 architectural milestone to a pre-release build: JSON storage with one-time YAML import, task-queue execution settings (parallel/sequential + overlap policy), failure notifications, Command browse button, small/large icons, the Fyne 2.7 upgrade with tray-click-to-show, dropping the per-job success-exit-codes feature, PySentry legacy cleanup, and a README split. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
11 KiB
Pre-Release Milestone Plan
This document tracks the work that takes GoSentry from the v0.4.0 architectural milestone to a pre-release-ready build. It closes the remaining roadmap items (except packaging), wires up features that were stubbed during the refactor, and cleans the tree of legacy/rename scaffolding.
The goal is a coherent, end-user-ready build: JSON storage, a real task-queue policy, working failure notifications, no legacy code, proper icons, and an end-user-focused README.
Confirmed decisions:
- Storage: switch to JSON. One-time YAML import for
gosentry.yaml/jobs.yaml(read once, rewrite as JSON). Drop all PySentry legacy entirely. - Task queue defaults: execution mode = Parallel, overlap policy = Skip (both selectable in Settings).
- Fyne 2.7 upgrade: included (enables tray-click-to-show).
- Per-job success exit codes: dropped (exit 0 = OK, non-zero = Failed).
1. Switch storage YAML → JSON
src/domain/config.go,src/domain/job.go: replace everyyaml:"..."struct tag withjson:"..."(Config, Job, JobsFile). Keepomitemptywhere used.src/storage/store.go: replacewriteYAMLwithwriteJSONusingencoding/json(MarshalIndent(value, "", " ")for human-editable files). Replace the twoyaml.Unmarshalcalls withjson.Unmarshal.src/storage/paths.go:ConfigFileName = "gosentry.json",JobsFileName = "jobs.json". RemoveLegacyConfigFileName(pysentry). AddlegacyYAMLConfigFileName = "gosentry.yaml"andlegacyYAMLJobsFileName = "jobs.yaml"for the import path.- One-time YAML import in
store.go:loadOrCreateConfig: ifgosentry.jsonis absent butgosentry.yamlexists,yaml.Unmarshalit through a private yaml-tagged shadow struct.OpenStorealready callsSaveConfigafterward, which writes JSON.loadOrCreateJobs: same pattern forjobs.yamlvia a yaml-tagged shadow;OpenStore'sSaveJobsrewrites as JSON.- The old
.yamlfiles are left on disk untouched (non-destructive).
- Keep
go.yaml.in/yaml/v4ingo.mod(now used only by the import path). - Tests:
src/storage/store_test.go— switch the write helper to JSON; replace the pysentry migration test with one covering the YAML→JSON one-time import.
2. Task-queue execution settings
Reworks the run-dispatch model so the schedule keeps advancing even while a run is in flight, which is what makes an overlap policy meaningful.
src/domain/config.go: addExecutionModeandOverlapPolicy(JSON tags) plus exported constants (ExecutionParallel/ExecutionSequential,OverlapSkip/OverlapQueue). Defaults inloadOrCreateConfigandvalidateConfig: empty →parallel/skip.src/domain/runtime.go: addPending booltoJobRuntime(a queued overlap).- Dispatch logic (moved into
src/app/run.go, see §9):startRunLocked: instead of zeroingNextDue, advance it to the next occurrence while the displayNextRunshows"Running", so a due tick can arrive during a run.RunDue: scan all due jobs. For each due, enabled job:- already running → overlap policy:
skipdrops this occurrence;queuesetsruntime.Pending = true. - not running → execution mode:
parallelstarts it immediately;sequentialstarts it only if no job is currently running and none started earlier in this tick.
- already running → overlap policy:
executeRun: on finish, ifruntime.Pendingand not paused and the mode permits, clearPendingand start the job again immediately.- Add
anyRunningLocked()helper. RunNow: keep the already-running guard; in sequential mode also refuse if another job is running.
src/ui/settings_view.go: add a Queue group withwidget.Selectcontrols for execution mode and overlap policy, wired into the saved config.- Tests: extend
src/app/operations_test.gofor parallel/sequential/skip/queue, reusing the fake-runJobseam andStartWith(fakeClock).
3. Browse button for the Command field
src/ui/job_dialog.go: wrapcommandEntryincontainer.NewBorder(nil,nil,nil, browseBtn, commandEntry)(same pattern as the directory rows insettings_view.go) and pass the container as the Command form item. The button opens a file picker.- Add a
chooseFile(w, target)helper insrc/ui/settings_view.gousingdialog.NewFileOpen; on selection set the entry text touri.Path().
4. System notifications on failure
Config.NotifyOnFailure is stored but never acted on. Wire it to Fyne.
- Add
func (s *Service) ShouldNotifyOnFailure() bool(reads config undermu). src/ui/mainwindow.go: in the existingsvc.Subscribe(...)handler, when the event isRunRecordedwithState == "Failed", a real-run trigger (Manual/Schedule), and notifications enabled, callfyne.CurrentApp().SendNotification(...). The handler is already insidefyne.Do.- Update the Settings checkbox wording to drop the "reserved" note.
5. Application icons — small vs large
Assets present: gosentry-icon-16x16.png (small), gosentry-icon-big.png
(large), gosentry.ico.
assets/assets.go: also embed the 16×16; addIconSmall(). KeepIcon()(large) for the window/app andIconBytes()(large) for Linux desktop integration.src/ui/run.go: window/app icon stays large.src/ui/tray.go: set the tray icon to the small variant viadesk.SetSystemTrayIcon(assets.IconSmall())(Fyne 2.7).- Windows Explorer icon stays via
packaging/windows/gosentry.rc; confirmgosentry.icohas both a 16×16 and a large frame.
6. Fyne 2.6.3 → 2.7.x upgrade + tray click
go.mod: bumpfyne.io/fyne/v2to latest 2.7.x;go get+go mod tidy.- Rebuild under MSYS2 UCRT64 (CGO); skim the 2.7 changelog for breaking changes.
src/ui/tray.go: adddesk.SetSystemTrayWindow(w)so left-click shows/focuses the window; keep the "Show" menu item.- Re-measure startup using the existing History "Started … in Xms" event and append to PERFORMANCE.md.
7. Drop legacy + debug; prepare for pre-release
- PySentry removal:
- Legacy pysentry config path (done in §1).
src/platform/autostart/autostart_windows.go: removelegacyAutostartName,cleanupLegacyRegistryAutostart,legacyRegistryAutostartExists,parseRegistryRunValue, and their use inSetAutostart/AutostartStatus.src/platform/autostart/autostart_linux.go: remove the legacy systemd + desktop cleanup functions and theirSet/Statuscalls.- Delete the corresponding legacy tests; drop
readShortcutTargetif unused. .gitignore/.dockerignore: drop thepysentry.yamllines; addgosentry.json/jobs.json; keep the*.yamlignores for the import window.
- Debug/diagnostics: confirm no
GOSENTRY_TIMINGcode remains (docs only); keep the lightweight startup History event (needed for §6). - Stale artifacts: ensure
dist/andcmd/gosentry/*.sysostay gitignored. - Bump hardcoded
0.3.0references to the current0.4.0/next pre-release version in README/docs.
8. README split — end-user vs developer
- New
docs/DEVELOPMENT.md: move Requirements (toolchain), Build (all variants), Run From Source, Project Layout, and Dependencies/mirroring out of README. README.mdkeeps end-user content only: intro, Features, Storage, Schedules, Using the App, Autostart, Troubleshooting (the VirtualBox/RDP OpenGL workaround stays). Update YAML → JSON file names + examples, the new Queue settings, real notifications wording, and version strings. Add a Documentation link list todocs/.
9. Roadmap refactoring follow-ups
- Linux test build fix: move the Windows-only tests
(
TestShellCommandHidesWindow,TestShellCommandUsesWindowsSafeQuoting, and peers touchingSysProcAttr/windowsShellCommandLine) fromsrc/runner/runner_test.gointo a newsrc/runner/runner_windows_test.goguarded by//go:build windows. - File-size (soft) limits: split the run/dispatch code (
RunDue,RunNow,startRunLocked,executeRun, queue helpers) out ofsrc/app/operations.gointo a newsrc/app/run.go. Optionally splitsrc/ui/jobs_view.goif a clean seam exists.
10. Drop per-job success-exit-codes feature
After removal the run outcome is: exit code 0 → OK, any non-zero → Failed.
src/domain/job.go: remove theSuccessExitCodesfield.src/runner/exitcodes.go: delete the file.src/runner/runner.gorunStateDetail: drop theacceptedExitCodebranch; a non-zeroexec.ExitErroris alwaysFailedwithExit code %d.src/runner/logfile.go: remove thesuccess_exit_codesfield.src/app/format.go: removeDisplaySuccessExitCodes(and its test).src/app/operations.go: drop thesuccess_exit_codeslines fromrunningOutputand the default innormalizeJob.src/storage/store.gonormalizeJobs: drop the default.src/ui/job_dialog.go: remove the entry, form item, and save.src/ui/jobs_view.go: remove the label and detail row.- Tests/docs: remove
TestParseExitCodes,TestRunJobAcceptsConfiguredExitCode,TestRunJobRejectsUnconfiguredExitCode, thesuccess_exit_codeslog-content assertions, the store_test exit-code fields/defaults, and the matchingdocs/TESTS.mdrows. - §1 import: legacy YAML jobs may carry
success_exit_codes; the shadow struct ignores it.
Implementation order
- Storage JSON + one-time import (§1) and exit-code removal (§10) — both touch
domain/job.goandstorage/store.go. - PySentry removal (§7 autostart + ignores).
- Queue model + settings (§2) and the
operations.go→run.gosplit (§9). - Notifications (§4), Command Browse (§3).
- Icons (§5).
- Fyne 2.7 upgrade + tray click + startup re-measure (§6).
- Linux test-build fix (§9).
- README split + docs/version updates (§8, §7).
Verification
go test ./...must build and pass. Build with CGO under MSYS2 UCRT64 on Windows (scripts\test.bat); the default Bash env has CGO off. Confirm the Linux test build compiles (GOOS=linux go vet ./...where available).- Manual smoke (Windows GUI): build via
scripts\build-windows.batand run.- First run with an existing
gosentry.yaml/jobs.yamlimports into JSON; a fresh install creates JSON defaults. - Job dialog: Browse picks a command path.
- Settings: Queue mode + overlap policy persist; failure notifications toggle;
a failing job (
exit 1) raises a desktop notification when enabled. - Queue behavior: a fast-schedule long-running job demonstrates skip vs queue; parallel runs two due jobs at once; sequential serializes.
- Tray: left-click shows/focuses the window; tray uses the small icon, window and taskbar use the large icon.
- First run with an existing
- Record the post-upgrade startup time and append to PERFORMANCE.md.