Commit Graph
30 Commits
Author SHA1 Message Date
mixandClaude Opus 5 da8fe10365 docs: retire the whole-project review plan, correct its CHANGELOG traces
Every item in docs/PROJECT_REVIEW_PLAN.md is now either landed or moved to
ROADMAP.md, so the working document retires the way TEST_REVIEW_PLAN.md did.
Nothing else referenced it.

Two 1.0.2 Internal entries were out of step with what shipped: the timing
diagnostic is written as notify-timing.tsv (kept out of CleanupLogs and off
the UI thread), not notify-timing.log, and the removal of the two per-run
no-op jobs.json rewrites - along with the run-start rollback and the start
error RunDue used to surface - was not recorded at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 03:42:29 +03:00
mixandClaude Sonnet 5 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
mixandClaude Opus 5 bd7ebde68e refactor: extract the Jobs view state, track selection by job ID
Phase 10 of the whole-project review (findings 5.1 and 5.2), folded into
the ROADMAP file-split item as that plan asks.

5.2 was a real defect. `selected` was an index into a snapshot of the jobs
slice, and every path that changed the slice patched it by hand. The one
path that could not — adopting a different jobs file, where the Service
replaces the whole list and the view only hears about it through the
refresh JobsLoaded triggers — left the details pane redrawing from an
index that belonged to the previous list, describing whichever job now sat
there (or clearing when the new list was shorter) while the list highlight
stayed put. The selection is now a job ID; rows are derived from it at
render time, and refresh ends by pointing the highlight at the selected
job, so the two can no longer disagree.

5.1: newJobsView was one 330-line constructor whose dozen closures shared
seven mutable locals. It is now a jobsView struct over a jobsViewState
that owns the snapshot, the folder filter, and the selection — the
invariant that used to be maintained by hand in five places lives in one
place — split across jobs_view.go (construction, refresh, layout),
jobs_view_state.go, jobs_view_list.go, and jobs_view_toolbar.go. The
folder-option rebuild that appeared verbatim in three handlers is one
method.

Behaviour that changed beyond the fix: switching the folder filter keeps
the current selection when the new filter still shows it, instead of
always jumping to the folder's first job.

Docs: ARCHITECTURE records the new file layout and the selection-by-ID
contract; ROADMAP drops jobs_view.go from the over-guideline table and
refreshes the other five numbers (finding 2.4); TESTS documents the new
state test file and the adoption regression test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 22:38:47 +03:00
mixandClaude Sonnet 5 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
mixandClaude Opus 5 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
mixandClaude Opus 5 98c820e3bd perf: cap the History list and fold column widths incrementally
History was appended to on every recorded run and never trimmed, and every
event re-sorted the whole slice and re-measured the Job, Detail and Log
columns across every row. The per-run cost therefore grew with the number of
rows, in exactly the mode the app is designed for: left in the tray for days.

The session History now keeps the newest maxHistoryRows (1000) records, the
way maxJobLogs caps a job's own activity list, and drops the oldest from the
front, zeroing the tail so a dropped record's full captured output is not
kept alive by the backing array. Column widths move into a historyLog value
that folds each new record into the current maxima instead of rescanning.
Widths only grow within a theme, so a column never narrows when a record ages
out; a theme change is the one case that still rescans, because every stored
width was measured at the old text size.

Measured with a throwaway benchmark over 5000 accumulated records: one
refresh went from 15.8 ms to 0.9 ms. At the new cap the full width rescan
alone costs 1.5 ms, so both halves of the fix carry weight.

Plan item 6 of docs/PROJECT_REVIEW_PLAN.md (finding 3.1).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 17:16:02 +03:00
mixandClaude Sonnet 5 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
mixandClaude Sonnet 5 1242b22e4f fix: Windows command quoting, atomic JSON/log writes, restore dropped test
Implements items 1-3 of the whole-project review's suggested order
(docs/PROJECT_REVIEW_PLAN.md):

- Restore TestJobListViewIsCompact, accidentally dropped by 5b0e6fe;
  drop the redundant TestDefaultConfigUsesDetailedJobList row from
  TESTS.md and document the two other doc gaps the review found.
- Fix quoteLeadingWindowsProgramPath to find the earliest file-extension
  match at a word boundary instead of the first extension in list order,
  so a .bat/.cmd command whose argument ends in .exe no longer has its
  whole command line mistaken for the program path.
- Write gosentry.json, jobs.json, and run log files atomically (temp
  file + rename) so a crash or power loss mid-write can no longer leave
  a truncated file. Wire Service.Stop() into the app shutdown path so
  it actually runs, cancelling the run context for in-flight runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 16:53:59 +03:00
mixandClaude Sonnet 5 89be009040 docs: remove the whole-project review agenda and its references
REVIEW.md and the /review-project command are gone; other docs no
longer point at them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 16:38:38 +03:00
mixandClaude Opus 5 eaa37a1192 docs: add the whole-project review action plan
Records the findings of a review of the whole codebase against the agenda in
REVIEW.md, with a suggested order and a model recommendation per item.

The project is in good health: the engine layering holds, the Service.mu
locking contract is obeyed, and the UI layout code observes the
measure-don't-hardcode rule with tests to match. The findings concentrate in
three places instead: paths that only surface after the app has run for a
while (unbounded History growth, PendingRuns surviving a pause), durability of
the JSON files (non-atomic writes, no shutdown path), and one Windows quoting
bug reproduced outside the build, where an argument ending in .exe hijacks
program-path detection for a .bat or .cmd job.

Also settles what happened to the two domain tests TESTS.md still documents:
5b0e6fe dropped them while adding two new rows to TESTS.md and leaving the old
rows in place, so the loss was accidental rather than the test-suite review's
doing. Only TestJobListViewIsCompact is worth restoring — the storage default
test already subsumes the other. Neither loss moved coverage; the 84.4% ->
84.0% dip is new 1.0.2 code, PeekKeepRunningInTray at 0%.

Temporary: delete once the items are done or moved to ROADMAP.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 13:19:45 +03:00
mixandCursor 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
mixandCursor 1e3d14bef2 Log failure-notification timing and plan faster Windows toasts.
Append app-side delays to logs/notify-timing.log, add a PowerShell baseline
script (~773 ms), and track native WinRT toasts in ROADMAP.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 23:15:44 +03:00
mixandCursor 0aab9d8db6 Register Windows notification icon via metadata after NewWindow.
Fyne toasts read App.Icon without SetIcon, preserving the PE multi-size
window and taskbar icon while giving failure notifications app artwork.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 23:02:52 +03:00
mixandCursor 0a50f3c66b Add disabled failure sample job for testing desktop notifications.
Seed Failure notification test in defaultJobs so new installs can verify
Settings notifications via Run now without scheduler spam.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 22:50:02 +03:00
mixandCursor 1240297cca docs: document platform layer rationale in ARCHITECTURE
Explain why autostart, file manager, shell, and winproc are OS-specific, where compile-time vs runtime branching applies, and rules for new platform code.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-05 22:46:48 +03:00
mixandCursor 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
mixandCursor 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
mixandClaude Opus 5 a735bfd116 docs: retire the test review plan, keep its durable findings
Every item in TEST_REVIEW_PLAN.md is done or decided, so the working
document goes as its own header instructed. What outlives it moves to
where a later reviewer will actually look:

- TESTS.md gains the -coverpkg command and the 84.4% baseline (per-package
  figures understate the suite), design principle 9 (redundancy is judged
  by comparing coverage profiles, and identical coverage alone is not
  grounds for deletion), a table of the look-alike tests that are kept
  with the reason each survives, and the list of functions deliberately
  at 0%.
- STANDARDS.md's "Intentional behavior" section points at both lists, so
  the mechanism REVIEW.md describes still reaches them.

Dropped as spent: the per-item checklists, the suggested order, and the
model-selection table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 22:53:13 +03:00
mixandClaude Opus 5 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
mixandClaude Sonnet 5 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
mixandClaude Opus 5 77bd2db286 docs: recommend a model per item in the test review plan
The deciding factor is the slow feedback loop, not task size: the ui
package needs CGO and the MSYS2 toolchain, and a cold test run took 258s
during the review. Getting an edit right on the first pass is worth more
than generating it faster.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 22:13:04 +03:00
mixandClaude Opus 5 1b6a3604cf docs: add the test-suite review action plan
Records the findings of a review of the whole test suite: three duplicate
tests confirmed by comparing coverage profiles, two inaccuracies in
TESTS.md, a hand-rolled itoa in seed_test.go, and four tests thin enough
to need a decision.

Also lists the pairs that share a coverage profile but assert different
properties, and the functions whose zero coverage is deliberate, so a
later pass does not re-report them. Temporary: delete once the items are
done or moved to ROADMAP.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 22:11:50 +03:00
mixandCursor cb37377346 Add top inset to the Disable auto row in the Jobs sidebar.
The row sat flush against the tab bar while the VBox gap below was already one theme padding; matching that on top balances the spacing.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 22:10:11 +03:00
mixandCursor b2402f4c72 Rename the Default theme option to System.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 21:49:06 +03:00
mixandCursor 648325a690 Point the About repository link at GitHub.
The Settings About block now links to mixeme/gosentry instead of the private Gitea mirror.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 19:35:29 +03:00
mixandCursor 27927f3ab1 docs: expand README @every schedule syntax
Document supported Go duration units, combinations, cron alternatives for calendar intervals, the one-second tick floor, and cron descriptors.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 19:28:42 +03:00
mixandCursor 276539c383 Make the branded GoSentry theme the default.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-04 19:18:48 +03:00
mix d4ca38b45a Create gosentry-logo.html 2026-07-18 14:28:20 +03:00
mixandmixeme 01fd572a89 Refactoring complete: v0.4.0 architectural milestone (#1)
## Summary

Completed Phase 5 refactoring and reached the target architecture.

**Architectural milestone achieved:**
- Service layer owns all state and is the sole writer
- UI is a thin Fyne view, all widget updates marshaled via `fyne.Do`
- Core engines are stateless and injectable
- Domain types are pure (no `yaml:"-"` fields)
- Full module builds and `go vet ./...` clean

## Changes

- Bump version: 0.3.6 → 0.4.0
- Update CHANGELOG with Phase 5 summary
- Add ROADMAP "Refactoring Follow-Ups" section

## Known follow-up work

1. **Linux test build broken** — `runner_test.go` needs `//go:build windows` tag
2. **File-size limits exceeded** — `operations.go` (486 lines), `jobs_view.go` (415 lines)

See ROADMAP.md for details.

---------

Co-authored-by: mixeme <mix.public@ya.ru>
Reviewed-on: #1
2026-06-22 08:05:10 +03:00
mix eca5771a09 Initial commit 2026-03-08 19:50:47 +03:00