Commit Graph

6 Commits

Author SHA1 Message Date
mixeme 428f018fe1 T5.2: Introduce autostart.Manager interface + per-platform impls
Define Manager with Set/Status in autostart.go; add concrete types
(windowsManager, linuxManager, otherManager) in the per-platform files.
Service gains a manager field wired by Open() via autostart.New();
AutostartStatus and ApplyAutostart delegate to it instead of calling
package-level functions directly. Tests that don't need autostart get a
nil manager, which is safe (no-op returns).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 07:22:41 +03:00
mixeme 21a93517eb T4.7: Remove forbidden platform imports from src/ui
src/ui now imports only src/app, src/domain, and Fyne.
Three violations were fixed:

- src/scheduler (NewRealClock): Service.Start() now creates the real
  clock internally; StartWith(clock) is the injectable seam for tests.
- src/platform/desktop (InstallDesktopIntegration): moved into
  Service.InstallDesktopIcon in new src/app/platform.go.
- src/platform/autostart (AutostartStatus, SetAutostart): moved into
  Service.AutostartStatus and Service.ApplyAutostart in platform.go.

go test -race ./... green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 00:00:24 +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