T5.5: Update docs to reflect Phase 5 package structure

Rewrote ARCHITECTURE.md with a new package-map section and updated
Mermaid diagram showing src/app Service, src/scheduler, src/runner,
src/storage, src/platform/autostart, and src/ui replacing the old
src/core + src/gui split. Main-flows section documents the event-driven
model and ErrorOccurred surfacing.

Rewrote TESTS.md to cover all nine current test files (domain, app ×4,
storage, scheduler, runner ×2, platform/autostart ×2, ui); every test
function is documented. Removed stale src/core references.

Updated README Project Layout from the two-package summary to the full
per-package description.

Marked T5.5 complete in REFACTORING.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
mixeme
2026-06-22 07:42:56 +03:00
parent a7ff64018b
commit 26adfa63f8
4 changed files with 314 additions and 106 deletions
+72 -41
View File
@@ -1,73 +1,104 @@
# GoSentry Architecture
This document shows the current component interaction model. GoSentry is still a
single desktop process: the GUI, scheduler, storage, and command runner live in
one application and communicate through Go function calls and shared in-memory
job state.
This document shows the current component interaction model. GoSentry is a
single desktop process: the GUI, application service, scheduler, storage, and
command runner live in one application. They communicate through typed events
and well-defined interfaces rather than shared mutable state.
## Package Map
```
cmd/gosentry entry point — starts the UI
src/
domain/ pure value types: Job, Config, RunRecord, Schedule, JobRuntime
app/ Service — sole owner of job/runtime state; emits typed Events
scheduler/ pure timing loop; calls app.Service.RunDue on every tick
runner/ shell command execution + log file writing + cleanup
storage/ YAML persistence (gosentry.yaml, jobs.yaml)
platform/
autostart/ Manager interface + Windows (shortcut) and Linux (XDG) impls
desktop/ display-scale helper (Linux only)
winproc/ hidden-window startup flags (Windows only)
ui/ Fyne windows, tabs, and dialogs; reads service via Events
```
## Component Diagram
```mermaid
flowchart LR
user["Desktop user"]
gui["src/gui - Fyne windows, tabs, dialogs"]
store["src/core Store - YAML config and jobs"]
scheduler["src/core Scheduler - @every and cron timing"]
runner["src/core Runner - shell command execution"]
autostart["src/core Autostart - Windows Startup shortcut / Linux desktop startup"]
config["gosentry.yaml - application settings"]
jobs["jobs.yaml - job definitions"]
logs["logs_dir - per-run command output logs"]
shell["Platform shell - cmd.exe /C or sh -c"]
ui["src/ui\nFyne windows, tabs, dialogs"]
svc["src/app Service\nsole owner of job + runtime state"]
store["src/storage Store\nYAML config and jobs"]
sched["src/scheduler Scheduler\npure timing loop"]
runner["src/runner\nshell command execution"]
autostart["src/platform/autostart Manager\nWindows shortcut / Linux XDG"]
config["gosentry.yaml\napplication settings"]
jobs["jobs.yaml\njob definitions"]
logs["logs_dir\nper-run command output logs"]
shell["Platform shell\ncmd.exe /C or sh -c"]
user -->|"edits jobs, settings, runs commands"| gui
gui -->|"OpenStore, SaveConfig, SaveJobs"| store
user -->|"edits jobs, settings, runs commands"| ui
ui -->|"CreateJob, UpdateJob, DeleteJob, RunNow, UpdateSettings, …"| svc
svc -->|"SaveJobs, SaveConfig, LoadJobs, LoadConfig"| store
store -->|"read/write"| config
store -->|"read/write"| jobs
gui -->|"Start, Pause, RunNow, RefreshSchedule"| scheduler
scheduler -->|"SaveJobs after state changes"| store
scheduler -->|"RunJob(trigger)"| runner
svc -->|"Start(RunDue)"| sched
sched -->|"RunDue(now)"| svc
svc -->|"RunJob"| runner
runner -->|"execute command"| shell
runner -->|"write stdout/stderr log"| logs
runner -->|"RunRecord with status, duration, log path"| scheduler
scheduler -->|"onChange RunRecord"| gui
gui -->|"display History, command output, job state"| user
runner -->|"RunRecord"| svc
svc -->|"emit JobChanged / RunRecorded / ErrorOccurred"| ui
ui -->|"display jobs, history, status"| user
gui -->|"SetAutostart, AutostartStatus"| autostart
autostart -->|"use executable path from resolved Paths"| config
ui -->|"SetAutostart, AutostartStatus"| autostart
svc -->|"Set / Status via Manager"| autostart
```
## Main Flows
1. Startup:
The executable starts `cmd/gosentry`, which calls the GUI package. The GUI
opens the store, loads `gosentry.yaml` and `jobs.yaml`, creates the main tabs,
then starts the scheduler with the loaded job slice.
`cmd/gosentry` calls `ui.Run`, which creates an `app.Service`, opens the
store, loads `gosentry.yaml` and `jobs.yaml`, subscribes the UI to service
events, builds the main window, and calls `Service.Start` to begin the
scheduler loop.
2. Editing settings or jobs:
The GUI updates the in-memory job/config state and asks `Store` to write YAML
back to disk. Job definitions stay in one `jobs.yaml`; runtime command output
is not stored there.
The UI calls mutating methods on `app.Service` (e.g. `CreateJob`,
`UpdateJob`, `UpdateSettings`). The Service validates the request, updates
its in-memory state, persists through `storage.Store`, and emits a typed
`Event`. The UI's observer receives the event and refreshes the relevant
widget on the main thread via `fyne.Do`.
3. Scheduled run:
`Scheduler` checks due jobs on a one-second ticker. When a job is due, it marks
the job as running, saves state, and starts `Runner` asynchronously.
`scheduler.Scheduler` fires a tick every second. On each tick it calls
`Service.RunDue(now)`. The Service checks which enabled, non-paused jobs are
due, marks each as running, and launches `runner.RunJob` in a goroutine.
4. Manual run:
`Run now` calls the same scheduler path as scheduled execution, but the
resulting history record uses the `Manual` trigger.
`Run now` in the UI calls `Service.RunNow`. The Service checks that the job
exists, is not already running, and that the scheduler is not paused, then
executes `runner.RunJob` with the `Manual` trigger.
5. Command execution:
`Runner` executes the command through the platform shell, captures stdout and
stderr, writes one timestamped `.log` file, and returns a `RunRecord`.
`runner.RunJob` builds the platform-specific invocation, executes the
command through the platform shell, captures stdout and stderr, writes one
timestamped `.log` file, and returns a `domain.RunRecord`.
6. History update:
The scheduler receives the `RunRecord`, updates the matching job, saves YAML,
runs log cleanup, and calls the GUI callback so the `History` tab refreshes.
When a run goroutine completes, `Service` updates the job's runtime, saves
YAML, triggers log cleanup, and emits `RunRecorded`. The UI observer appends
the record to the History tab.
7. Autostart:
The Settings tab calls the platform autostart implementation. Windows uses a
shortcut in the current user's Startup folder. Linux uses a desktop-session
startup entry. Both autostart mechanisms pass `--start-in-tray`, so the
scheduler starts without opening the main window after sign-in.
`UpdateSettings` in the Service calls `autostart.Manager.Set`. The Manager
interface has two implementations: Windows writes a `.lnk` shortcut to the
user Startup folder; Linux writes an XDG Autostart `.desktop` file. Both
entries pass `--start-in-tray`.
8. Error surfacing:
Background errors (failed YAML saves, cleanup errors) are emitted as
`ErrorOccurred` events and displayed in the UI status area, rather than
being silently discarded.