Commit Graph

25 Commits

Author SHA1 Message Date
mixeme fb12fab71d T4.4: Extract history_view.go
Move newHistoryView, historyCellText, logFileName, newEvent, and
collectActivity out of mainwindow.go into src/ui/history_view.go.
Drop the now-unused "sort" import from mainwindow.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 23:30:21 +03:00
mixeme 91679efa3c T4.3: Extract job_dialog.go; validate schedule via domain.Validate
Moves showJobDialog out of jobs_view.go into its own file and adds a
domain.Validate call on the schedule field so malformed cron expressions
are rejected with an error dialog before onSave is invoked.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 23:19:00 +03:00
mixeme 760700f6ac T4.2: Extract jobs_view.go (list + details + toolbar)
Move the jobs list, details panel, folder filter, toolbar buttons, and
all related helpers out of mainwindow.go into the new jobs_view.go.
newMainView now calls newJobsView(w, svc) which returns a panel and a
refresh closure; the subscriber and history wiring remain in mainwindow.go.

showJobDialog lives in jobs_view.go as a temporary home until T4.3
extracts it to job_dialog.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 23:07:34 +03:00
mixeme f82eca8777 T4.1: Rename gui->ui, split lifecycle into run.go + mainwindow.go
Carve src/gui/app.go into the new src/ui package:
- run.go: process lifecycle (single instance, app/window, tray, startup
  timing).
- mainwindow.go: view assembly + the app.Service event listener.

Route every widget update driven by Service events through fyne.Do so the
run goroutine (executeRun) no longer mutates Fyne widgets directly. Also
wrap serveSingleInstance's Show/RequestFocus, which runs on the Accept
goroutine. (Resolves refactoring problem #4.)

fyne.Do/DoAndWait only exist in Fyne v2.6+, so upgrade fyne.io/fyne/v2
v2.5.3 -> v2.6.3. Mark the tray Quit item IsQuit so Fyne's
addMissingQuitForMenu reuses it instead of appending a second, localized
Quit now that v2.6 ships Russian translations.

go build / go vet / go test -race all clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:55:23 +03:00
mixeme 6073d5e112 T3.6: Add src/app unit tests (no Fyne)
Fill the test gaps in the app package, raising coverage from 82% to 97%:

- format_test.go: cover all display helpers moved in T3.5 (StatusText,
  EventText, Display*), previously untested.
- operations_test.go: add RunNow not-found and refused-while-running
  paths, DeleteJob/SetEnabled not-found errors, UpdateJob re-enable,
  runtimeForLocked lazy-recreate, UpdateSettings invalid-config branches,
  and prependLog activity-list capping.

All tests are Fyne-free, using a temp-dir store, a fake runner seam, and
a fake Clock. go vet and go test -race pass across the module.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 08:31:04 +03:00
mixeme 6e5c42fbed T3.5: Move display helpers to src/app/format.go
Extract pure display/formatting functions from the GUI:
- StatusText, EventText, DisplayFolder, DisplayArguments,
  DisplaySuccessExitCodes, DisplayRunMode, DisplayInvocation, DisplayIndex.

These have no Fyne dependencies and serve as a shared formatting layer for
the service to use (e.g. StatusText in the tests when mocking the GUI).
The GUI imports them as app.DisplayFolder(...) etc.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-19 08:25:37 +03:00
mixeme a4c93a5122 T3.4: Convert scheduler to drive app.Service; inject Clock
The scheduler no longer shares a *[]domain.Job with the GUI. It is now a
thin timing loop with an injected Clock that calls a tick callback; the
application service is the sole writer of job and runtime state.

- scheduler: add Clock interface + RealClock (clock.go); strip all job
  logic from scheduler.go (NewScheduler(clock, tick)); rewrite tests to
  cover the loop with a fake clock.
- app.Service: add RunDue(now) (pause + one-run-per-tick policy, records
  back through the service) and Start(Clock)/Stop() owning a cancelable
  run context; prime each job's first next-run at construction. Capture
  the run context under the lock for executeRun.
- gui: talk only to app.Service (no shared state) — Open() the service,
  keep a refreshed snapshot, route every mutation through the service,
  and react to changes via a single Subscribe listener.
- Tests: add RunDue (due/not-due/paused) and Start-drives-RunDue cases.

Verified with CGO + MSYS2 UCRT64: go vet ./... clean, go test -race
./... green (GUI included), full module builds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 08:22:35 +03:00
mixeme d8ab9acf7e T3.3: Add state-mutating operations to app.Service
Add src/app/operations.go with the seven intents that make the Service
the sole writer of job and runtime state: CreateJob, UpdateJob,
DeleteJob, SetEnabled, RunNow, SetGlobalPause, UpdateSettings. Each
returns error, persists through the store, and announces changes via
RunRecorded/JobChanged/SchedulerStateChanged events.

Extend the Service with a parsed-schedule cache, a global paused flag,
an injectable runJob seam (defaults to runner.RunJob) for testing the
run-now path, and a lifecycle ctx. Run and next-run timing now live in
the Service (duplicating the scheduler temporarily); T3.4 converts the
scheduler to drive the Service and removes the duplication.

Autostart is left to the caller until T5.2's injectable Manager; async
save errors in the run goroutine remain deferred to T5.1. Adds 12 tests
covering create/update/delete, enable/pause, global pause, run-now with
a fake runner, and settings persistence/validation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 07:48:41 +03:00
mixeme 5e51381b7a T3.2: Add app event types and observer dispatch
Add src/app/events.go: a sealed Event interface with three concrete
types (JobChanged, RunRecorded, SchedulerStateChanged), an Observer
interface plus ObserverFunc adapter, and Subscribe/emit on the Service.
This replaces the scheduler's single onChange callback with typed
events the UI can exhaustively type-switch over.

Dispatch is serialized by a dedicated dispatchMu (separate from the
state lock): observers never run concurrently, emit must be called
without holding s.mu so observers can read Service state, and observers
must not re-enter an emitting method. emit is wired to mutating ops in
T3.3. Adds tests for ordered multi-observer delivery, empty-observer
no-op, and observer-reads-state-without-deadlock.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 07:39:34 +03:00
mixeme 9931ec1237 T3.1: Add app.Service owning state behind a mutex
Create src/app/service.go: the application-service layer that becomes
the single owner of the durable jobs slice and the transient runtime
map, guarded by a non-reentrant sync.Mutex. NewService wires a loaded
store; Open() is the convenience entry point. Read-only accessors
(Jobs/Runtime/Store) take the lock, and Jobs() returns a copy to keep
callers from mutating Service-owned state.

State-mutating intents and the event/observer machinery are deferred to
T3.2-T3.4. Adds no-Fyne unit tests for runtime construction, copy
isolation, and store wiring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 07:34:34 +03:00
mixeme b1874845d5 T2.3: Split domain.Job (durable) from domain.JobRuntime (transient)
Move all transient execution state off domain.Job into a new
domain.JobRuntime, keyed by job ID:

- domain: Job now holds only durable YAML fields; remove the yaml:"-"
  fields (LastRun/NextRun/LastState/Logs/Output) and NextDue. Add
  runtime.go with JobRuntime plus NewRuntime/NewRuntimes constructors,
  which now own the runtime-init logic moved out of normalizeJobs.
- runner: RunJob no longer mutates the job; it is pure and returns the
  RunRecord for the caller to fold into the runtime.
- scheduler: take a shared map[int]*JobRuntime and route status/next-run
  bookkeeping through runtimeFor(job); prepareNextRun writes a *JobRuntime.
- storage: normalizeJobs touches only durable config.
- gui: own the runtime map (NewRuntimes), share it with the scheduler,
  and read/write runtime state via runtimeFor; maintain the map by ID on
  add/edit/delete.
- tests: update scheduler/storage tests to the split; tidy a pre-existing
  import-order nit in scheduler.go.

This also satisfies T2.4 (storage load/save only Job, runtime init in
domain.NewRuntime, round-trip tests), since removing the fields forced it.
Runtime-map ownership remains GUI-side glue until T3.1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:08:05 +03:00
mixeme ca673f08f9 T2.2: Migrate scheduler to use domain.Schedule
Parse each job's schedule once on load (resetNextRuns) and on edit
(RefreshSchedule) via the new parseJobSchedule helper, caching the
result in a map[int]domain.Schedule keyed by job ID. prepareNextRun
now looks up the cached Schedule instead of re-parsing the string on
every call. Remove the nextRunTime wrapper that did the per-call
parsing. Drop the three scheduler_test.go tests that duplicated
coverage already in domain/schedule_test.go.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:59:46 +03:00
mixeme 0f17782174 T2.1: Add domain.Schedule value object
Introduce src/domain/schedule.go with a Schedule value object that
centralizes schedule parsing and validation: Parse, Validate, and
Next(time.Time). It owns the cron parser and @every handling, moved out
of the scheduler. The scheduler's nextRunTime is kept as a thin wrapper
delegating to domain.Parse for now (T2.2 will parse once on load/edit).

Add unit tests covering invalid specs, @every intervals, five-field cron,
cron descriptors, whitespace trimming, the zero-value Next, and String.

Mark T2.1 complete in REFACTORING.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 22:55:20 +03:00
mixeme 29c973464c Mark T1.8 complete in REFACTORING.md 2026-06-18 22:40:45 +03:00
mixeme 17c5e15b63 Mark T1.7 complete in REFACTORING.md 2026-06-18 22:38:03 +03:00
mixeme 16d818d03d T1.6: Create src/platform/autostart; move autostart logic
Move autostart_*.go and tests from src/core to src/platform/autostart.
Update src/gui/app.go to call autostart.SetAutostart / autostart.AutostartStatus.
Add quoteDesktopExec to desktop_linux.go (was co-located in autostart_linux.go).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:35:51 +03:00
mixeme 06edbfff77 T1.5: Create src/storage; move store/paths logic
Move store.go, paths.go, and store_test.go from src/core into the new
src/storage package. Update src/scheduler and src/gui to import storage
instead of core for Store/Paths/OpenStore. Empty the moved files in core
to preserve the package declaration for the remaining core symbols.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:15:40 +03:00
mixeme ad0e45a7dd T1.4: Create src/scheduler; move scheduler logic
Move scheduler.go and scheduler_test.go from src/core to the new
src/scheduler package. The scheduler still takes *[]domain.Job and
*core.Store (storage moves in T1.5). Update src/gui/app.go to import
the new package; rename the local variable to sched to avoid shadowing
the scheduler package name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 22:03:16 +03:00
mixeme c29d581fb1 T1.3: Create src/runner; move runner logic
Move runner.go, runner_windows.go, runner_other.go, runner_test.go from
src/core to src/runner (package runner). Split helpers into focused files:
invocation*.go, exitcodes.go, logfile.go, cleanup.go. Export LogArguments
and SuccessExitCodesText for use by scheduler's runningOutput helper.
Update src/core/scheduler.go and src/gui/app.go to import src/runner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 21:47:29 +03:00
mixeme f4fb16c0ed T1.2: Create src/platform/winproc; move configureHiddenWindow
Extract the hidden-window logic out of src/core runner and autostart files
into a new platform/winproc package with per-OS build-tag files. All call
sites updated to use winproc.ConfigureHiddenWindow. Builds clean on both
Windows and Linux; all tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 21:28:25 +03:00
mixeme 80c76a0cba T1.1: Create src/domain; move Job/RunRecord/Config/JobsFile/StartInTrayArgument
Extracts the five domain types out of src/core/model.go into a new
src/domain package (job.go, record.go, config.go). The unexported
nextDue field is promoted to NextDue so it is accessible from core.
All references across src/core, src/gui, and cmd/gosentry are updated
to use domain.TypeName. src/core/model.go is reduced to a bare package
declaration. Windows and Linux cross-compilation both pass; all tests
remain green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 21:18:57 +03:00
mixeme 462752f995 Mark T0.2 as complete 2026-06-18 20:25:39 +03:00
mixeme 520a7ef98b Mark T0.1 as complete 2026-06-18 20:14:06 +03:00
mixeme f653b1e484 Add task completion checklist to refactoring plan
Track the 30 tasks across 5 phases with checkboxes. Each checkbox can be
marked complete as tasks land and pass review.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-18 08:19:02 +03:00
mixeme 4c49104cce Add refactoring plan document
Document a phased plan to restructure GoSentry into focused packages
under src/ (domain, storage, runner, scheduler, platform, app, ui) with
an application-service layer that owns state, and link it from the README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 08:16:03 +03:00