# GoSentry — Standards Quality rules and intentional behavior for contributors. Package contracts live in [ARCHITECTURE.md](ARCHITECTURE.md); test conventions in [TESTS.md](TESTS.md); what a whole-project review looks at, in [REVIEW.md](REVIEW.md). ## Code quality - Follow package contracts in [ARCHITECTURE.md](ARCHITECTURE.md). - User-facing errors → `dialog.ShowError` or a History event, never a silent `return`. - Pure helpers → unit test in the same package. - Fixes with severity ≥ medium → regression test. - Documented intentional behavior → section below, not a backlog bug. - UI view constructors accept `*app.Service`; call `app.Open()` only from `run.go`. - A size that must follow the theme is **measured at build time, not written as a pixel constant.** `theme.Padding()` and text metrics depend on the running app's theme, text size, and DPI, so a hand-tuned number is only correct for the one theme it was tuned against and clips under any other. Measure the real widget, or derive the value from the theme, in a named helper: `rowOverlap` (theme padding), `captionColumnWidth` and `textColumnWidth` (the widest of the actual strings), `activityRowsHeight` (the list's own row template). The same applies to a ratio computed from an absolute width — see `initialSplitOffset`. A raw pixel literal is left only where nothing about it tracks the theme, and says so in a comment. ## Config file compatibility There is no migration step: `gosentry.json` and `jobs.json` are read as-is, are meant to be hand-editable, and may have been written by an older version. A change to their shape has to stay compatible on its own. - A new `Config` field is tagged `omitempty`, and its zero value must mean the behavior that existed before the field was added — a file written without it keeps working unchanged. `DefaultConfig()` still sets the value explicitly. - A zero that carries meaning is not a missing field and must not be backfilled on load. See `DefaultTimeoutSeconds` in `storage.loadOrCreateConfig` and `Job.TimeoutSeconds *int`, where unset and `0` are different answers. - An unrecognised enum value reads as the default rather than an error, through one helper that every consumer shares (`JobListView.IsCompact`, `ui.themeFor`), and is normalized before being written back, so the file never gains a value no reader understands. - A renamed key keeps the old field on `Config` (tagged `omitempty`) purely so it can still be read. `storage.loadOrCreateConfig` converts it to the new field and clears it, so the retired key disappears on the next save. See `Config.JobsDir` → `Config.JobsFile`. Where the new field has a non-empty default, clear that default before unmarshalling, or "the file omits it" and "the file sets it" become indistinguishable and the conversion never runs. - Each of the three gets a test: the default in `storage`, the normalization in `domain`, and a round-trip through the real config file in `app`. ## Intentional behavior (not bugs) - `RunNow` is allowed during global pause and for disabled jobs. - Selecting a jobs file that already exists **loads** it: its jobs replace the in-memory list, which is the only way the user can switch between job lists. A path with no file behind it receives the current jobs (rename/relocate). The switch is refused while a job is running, because adoption drops every runtime and a finishing run would then write its result onto whichever job inherited its ID. - Sequential mode runs jobs FIFO by order in `jobs.json`. - Scheduler tick is 1s — sub-second `@every` intervals are not supported. - Command timeout defaults to no timeout globally (`Config.DefaultTimeoutSeconds` = 0) and is overridable per job (`Job.TimeoutSeconds *int`: unset = inherit the global default, 0 = no timeout, positive = seconds). Neither zero may be normalized away on load — 0 is a value, not a missing field. - **History tab is session-only.** `JobRuntime.Logs` exists only in memory for the current process. Log files on disk feed aggregate statistics via `SeedStats` only. See [ARCHITECTURE.md](ARCHITECTURE.md). ## Out of scope Larger or blocked work is tracked in [ROADMAP.md](ROADMAP.md) (update check from GitHub releases, cron-table import/export, window size persistence, History column filters).