Commit Graph

203 Commits

Author SHA1 Message Date
mixeme 0cf44e1dfa docs: spell out what a missing RELEASE_TOKEN actually does
The note said Codeberg needs the secret but not what happens without it,
so the failure mode had to be inferred from a red job. Record it: build
and packaging still succeed, the upload step fails on authentication and
takes the job with it, and the release is published with no assets.

Not "the upload step is skipped", which is what the comment in
.forgejo/workflows/release.yml claims. That step is guarded only by
`if: github.event_name == 'release'` — there is no token check and no
continue-on-error, so an absent or unscoped token makes it fail rather
than skip. Skipping is the workflow_dispatch path. The workflow comment
is still wrong and is left for a separate change.

Also name why GitHub needs no setup: softprops/action-gh-release falls
back to the built-in GITHUB_TOKEN and the workflow grants contents:write.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:17:34 +03:00
mixeme 44bc7ee81e docs: say which OS each Packaging command belongs to
The two package-* invocations sat next to each other with nothing but
the code-fence language to tell them apart, and the output paths were
pooled in one block that mixed Linux tarballs with the Windows zip.
Label each command Windows: / Linux: like Run From Source does, and give
each its own artifact list.

Also note that package-linux.sh emits the arm64 tarball only when
aarch64-linux-gnu-gcc is installed; the flat list implied a plain Linux
host always produces both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:13:19 +03:00
mixeme 0f171edd75 docs: reorder DEVELOPMENT.md and give it a table of contents
The document opened with dependency installation and buried "Run From
Source" between the build and release sections, so a newcomer met the
MSYS2 setup before learning what the stack was. Reorder it as stack and
tools, external libraries, run from source, build, release, CI, and add
a clickable two-level Contents block.

External libraries now list version, repository, and license type in one
table, and the package-* scripts are documented for the first time. The
Project Layout section is gone: it duplicated the package map in
ARCHITECTURE.md and had drifted out of date, missing
src/platform/filemanager.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:08:03 +03:00
mixeme 9a45a7be6f fix(ui): give the Settings Theme row back its top gap
The Application section stacks its rows with rowOverlap(), a negative
spacing that trades away one label's duplicated text inset. The Theme
row's value is a Select, which paints its box out to the row's edge and
has no inset to give, so the overlap closed the gap instead: 0.46 px
between the Notifications checkbox and the dropdown, against ~8 px
between the checkbox rows.

cancelRowOverlap adds that one padding back on the Theme row's top edge
only, restoring the gap to 7.5 px without touching the other rows or the
column width.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 22:04:18 +03:00
mixeme 9dd461e35e docs: track the oversized files as a roadmap item
The ~250-line guideline is currently broken by six source files, not the
two the last commit named from the ui section it was editing:
operations.go at 490 is the worst, and both files that were already split
once are back over.

Fixing them belongs in the next whole-project review rather than in
one-off commits. REVIEW.md item 2 already asks for exactly this sweep, and
doing all six together is what keeps the seams consistent — six separate
passes would settle the same question six ways. A split also reads as pure
movement while it is the easiest change in which to silently drop a
function, which is an argument for one careful pass rather than several
hurried ones.

The item records the seams that are visible today so the pass does not
start cold: operations.go splits along the three consecutive blocks it
already has, history_view.go's column measurement is pure and separable,
and jobs_view.go is the hard one because almost all of it is a single
constructor that has to be broken up rather than moved. The three files
barely over the line are flagged as re-measure-first, not split-on-sight.

ARCHITECTURE now points at that item instead of describing the overage in
passing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 21:44:03 +03:00
mixeme 84e81371c1 docs: correct the claims that no longer match the code
Four documents asserted things the code contradicts.

ARCHITECTURE's component diagram had the UI calling the autostart Manager
directly. It does not, and must not: src/ui holds no reference to the
package at all — Settings reads svc.AutostartStatus(), like everything else
it reads. The edge is folded into the existing ui→Service one, so the
diagram no longer draws the exception to the project's own rule.

platform/desktop was described in both ARCHITECTURE and DEVELOPMENT as a
"display-scale helper". It installs the .desktop entry and icon under XDG
data home; there is no scale helper in it.

The ~250-line file guideline was written as though the jobs_view and
settings_view splits had settled it. Both files are over it again and
history_view.go has never been split, so the guideline is now stated as
the target it is, with the current state named rather than implied.

STANDARDS pointed at a "CI coverage gate" item that ROADMAP does not have,
while omitting the two it does.

README's gosentry.json sample was three keys short of what the app writes
on first run — default_timeout_seconds, theme and job_list_view — which
made the one file the user is invited to hand-edit the least accurate
thing in the document. The sample is now the real default (verified by
marshalling DefaultConfig), with the keys explained, including why a zero
timeout is written out and an unset one is not. The per-job overrides for
overlap policy and timeout were undocumented despite being in the job
dialog, and the feature list had not caught up with the timeout, the theme,
or the compact job list.

Version numbers in example output paths are now <version>, matching how the
CI section already wrote them, so they cannot go stale again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 17:27:21 +03:00
mixeme f11fee10e9 docs: sync TESTS.md with the suite it describes
The document had drifted well past the point of being useful as an index:
it listed 130 tests against 170 in the tree, described four test files that
were never added to it at all (domain/config_test.go, runner/seed_test.go,
ui/layout_test.go, ui/theme_test.go), attributed runner's Windows
invocation tests to the wrong file, kept two tests that no longer exist
(TestParseRegistryRunValue, TestLinuxAutostartRemovesLegacyDesktopEntry),
and filed three activity-panel tests under jobs_view_test.go when they live
in history_view_test.go.

Most of the gap is the work of the last few releases: the per-job timeout's
three states, the persisted pause and jobs-list density, SeedStats, and the
whole GUI-geometry set the layout review produced.

Three claims were false rather than merely missing. The ui test files are
no longer "pure helpers, no Fyne widget construction" — they build views
under test.NewApp() and measure them, which is now stated as its own design
principle, because that measurement is what makes the STANDARDS rule about
theme-derived sizes enforceable. layout.go is no longer an open coverage
gap. And the coverage-gap list now names the real remaining one: Fyne's
headless driver cannot report a maximized window, which is the reason
window-size persistence is frozen.

Verified mechanically: every test function in the tree appears exactly once
in the document, under the file it actually lives in, and the document
names no test that does not exist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 17:24:27 +03:00
mixeme bde9a2e33e release: 1.0.0, and record the details pane's width coupling
The 0.16.0 release was never tagged or pushed, so what it described ships
as 1.0.0 instead: the changelog section is renamed rather than followed by
an empty one, and there is no 0.16.0 for anyone to have seen.

The last remainder of the layout review's F9 goes in with it. The value
column in captionValueLayout has no minimum of its own — it takes whatever
the container leaves after the caption — and what actually keeps it
readable is the 460 px minimum on commandOutputScroll, a constant that
exists for command output being legible. The dependency was invisible at
both ends; both now state it, so lowering that width is a decision rather
than an accident. The HSplit divider is the user's side of the same thing:
it is how the value column can be widened.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 17:15:19 +03:00
mixeme c5f300b670 docs: close the GUI layout review, release 0.16.0
Stage 9 of the GUI layout plan: the roadmap item the review was raised
under is closed, so the plan and the findings document go with it — what
they established now lives in STANDARDS and the CHANGELOG.

STANDARDS gains the rule the review produced: a size that must follow the
theme is measured at build time, not written as a pixel constant, because
a hand-tuned number is only correct for the theme it was tuned against.
rowOverlap, captionColumnWidth, textColumnWidth, activityRowsHeight and
initialSplitOffset are the worked examples.

The CHANGELOG entry keeps to what the user can see: the window opens at
the size it asks for and drags smaller, the Jobs divider is draggable,
History columns hold their content on a scaled UI, and the Settings
button row and block spacing are as their layouts intended.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 16:42:20 +03:00
mixeme d0dc17a067 feat(ui): draggable divider between the jobs list and the details pane
The jobs view pinned the sidebar in a Border left slot, so it rendered at
its MinSize and nothing else: the user could never trade list width for
detail width, and the details value column had no escape from the width
the metadata grid left it.

container.NewHSplit replaces the Border. Either pane can now be dragged,
and container.Split clamps both at their own content minimum, so the
divider degrades a pane rather than clipping it.

The initial offset is derived, not a literal: SetOffset takes a ratio,
but the sidebar's natural width is absolute (448 px), and a ratio that
fits 1024 px would hand the sidebar 700 px at 1600 px. initialSplitOffset
divides that width by the default window width less the divider, which
opens the sidebar at exactly its content width with the rest to details.

The divider position is not persisted; a restart reopens at the computed
default.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 16:40:17 +03:00
mixeme cebd41a5ac feat(ui): content-measured History columns, shared caption widths, settings_view split
Stage 5: generalize logColumnWidth into textColumnWidth so History's
Trigger/Job/State/Detail/Log columns size from measured text instead of
pixel constants that clipped at larger text sizes (F6, F14).

Stage 6: captionColumnWidth replaces detailCaptionWidth and
settingsLabelWidth with one theme-derived helper; jobs_view_details.go
now builds its metadata rows and their width from a single
metadataRows() list instead of two hand-kept ones (F10); the Settings
button row drops its transparent-rectangle spacers for a
CustomPaddedLayout (F8); the remaining eight fyne.TextTruncate call
sites move to the non-deprecated Truncation field (N1).

Stage 7: settings_view.go split into settings_view.go (field
construction/save/load/validate), settings_view_layout.go (the
two-column layout and settingsSection/settingsRow), and
settings_view_helpers.go (fyneVersion, dialogs, path helpers),
mirroring the jobs_view.go split. Along the way, Queue/Storage's inline
VBox and Application/About's settingsSection collapse into one
settingsSection(title, spacing, rows...) constructor, and
chooseFile/chooseJSONFile merge into one function with a filter
argument.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-27 16:26:13 +03:00
mixeme 57e6fe410e perf(ui): sort History once per redraw, drop duplicate jobs refreshes
Stage 4 of the GUI layout plan (F11, F12).

History: the cell callback copied and sorted the whole event list on every
call, and a full-window Refresh issues one call per visible cell — 126 sorts
of a 300-element slice per redraw, measured. The sorted snapshot now lives in
`rows`, refilled by `resort()` at build time, on a sort toggle, and from
`refresh()`. The length callback moves to `len(rows)` with it: cells and the
row count have to read the same slice, which was only incidentally true while
each cell re-derived the order for itself. The column captions become a
package-level array instead of a slice reallocated per header update, and the
per-cell `TextStyle`/`Refresh()` pair goes — the template already carries the
zero style and `SetText` refreshes.

Jobs: `refreshView()` already re-reads the service snapshot and refreshes the
list, so the six `list.Refresh()` calls that preceded it, and the duplicate
`syncFromService()` in the pause handler, were redundant. The folder filter's
early-return path never reaches `refreshView()`, so its `list.Refresh()` moves
into that branch rather than being deleted.

Both changes carry regression tests, each verified to fail against the
behaviour it guards: `TestHistorySortToggleKeepsRowsInSync` for the
cache-versus-length hazard, `TestToolbarButtonRedrawsRowAndDetails` for the
handlers that now rely on `refreshView` alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 14:09:51 +03:00
mixeme 60aceb75af fix: window minimum, stock spacing layout, sidebar width floor
Part A stages 1-3 of the GUI layout cleanup plan:

- Stage 1 (F1-F3): delete settingsControlWidth's redundant wrapper (the
  Border centre slot already stretches controls), truncate the config
  path label, and name the default window size so it can be asserted
  against. Settings no longer widens the window past what it asks for.
- Stage 2 (F4-F5): drop compactVBoxLayout for the stock
  layout.NewCustomPaddedVBoxLayout and a single derived rowOverlap()
  spacing, replacing three hand-tuned spacing constants.
- Stage 3 (F7): delete the inert 400px sidebar width floor; the Border
  left slot already renders it at content MinSize.

See docs/PLAN-gui-layout.md.
2026-07-27 13:47:39 +03:00
mixeme 9d39f5c100 docs: implementation plan for the GUI layout review findings
Turns all fifteen findings from GUI-LAYOUT-REVIEW.md into nine staged commits,
each with the exact file changes, the tests it owes, and the measured result it
should produce.

Part A (stages 1-6) is the single-fix set: bring the window minimum under the
1024x660 the app asks for, retire compactVBoxLayout for the stock
layout.NewCustomPaddedVBoxLayout, delete the sidebar width floor, hoist the
History sort out of the per-cell callback, measure the History columns from
their content, and fold the caption widths into one theme-derived helper.

Part B (stages 7-8) is the roadmap-sized work: split settings_view.go and
replace the fixed Border sidebar with a draggable HSplit. Stage 9 closes the
ROADMAP item, adds the rule this review established to STANDARDS, and ships as
0.16.0.

Three decisions are settled and folded in: everything is in scope, so nothing
carries forward to ROADMAP; the split divider position is not persisted, which
keeps stage 8 inside src/ui; and the row spacing unifies on -8, the one
deliberate visual change in Part A.

Also records a recommended model per stage, split by judgement density rather
than diff size, plus the trap each stage carries - the History length callback
in stage 4 and the paired label change in stage 6 are the two that fail
silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 13:39:50 +03:00
mixeme 70aa4cbc4d docs: GUI layout review — findings for the composition pass
Carries out the "GUI review — custom layouts and composition" roadmap item and
records the result in docs/GUI-LAYOUT-REVIEW.md. Composition only; no code was
changed. Every number was measured with a throwaway headless Fyne probe at text
size 14 and 20, not estimated.

Headline finding: the Settings tab sets a minimum content width of 1165.5 px for
a typical install — wider than the 1024x660 window run.go asks for — so Fyne
silently widens the window on open and the user cannot drag it narrower. It also
grows with the length of the config-file path (1501 px for a 75-character one).
Two independent causes: the seven settingsControlWidth wrappers, which measure
identically to bare controls at every reachable width and only inflate MinSize,
and the Config JSON path label, the one value label in the tab without
truncation. Fixing both takes the floor to 993.3.

Also found: compactVBoxLayout is a byte-for-byte re-implementation of stock
layout.NewCustomPaddedVBoxLayout (identical geometry at every spacing tested);
minJobsSidebarWidth (400) never binds because the toolbar row already needs 448;
the negative row spacings are theme.InnerPadding() written as a magic number;
the History column widths truncate their own content on a scaled UI; and the
History table re-sorts its whole event slice once per cell — 126 sorts of a
300-element copy for one redraw.

Fifteen findings in all, each with a disposition (single fix or roadmap) and a
suggested order. fixedHeightLayout, which the roadmap singled out, is kept: no
stock layout forces an exact height, and the alternative depends on the parent
staying a Border.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 03:03:50 +03:00
mixeme 721b049100 feat: select the jobs file itself in Settings
The Jobs directory row named a folder and assumed the file inside it was
called jobs.json. It is now a Jobs file row: Browse opens a file picker
filtered to .json, the field stays editable so a file that does not exist yet
can be typed, and the job list can live under any name.

Config.JobsDir/jobs_dir becomes Config.JobsFile/jobs_file, holding the whole
path; Paths.JobsDir is derived from it so saves still create the folder. An
older gosentry.json is migrated on load by joining its jobs_dir with
jobs.json — the exact file that version used — and the retired key is dropped
when the config is rewritten. The default clears before unmarshalling, or a
file that omits jobs_file and a file that sets it would be indistinguishable
and the migration would never run.

Saving used to write the current job list over whatever was at the new path,
which made switching to an existing jobs file impossible: its contents were
destroyed. An existing file now wins. Its jobs are loaded, normalized, and
adopted, with runtimes, schedule cache, next-run times and log-seeded
statistics rebuilt around them by adoptJobsLocked — the same helper NewService
now uses, so construction and adoption cannot drift. A path with no file
behind it still receives the current jobs, which is how the file is renamed or
relocated. The new file is read before anything is written, so an unparsable
one leaves both the config and the jobs untouched.

Adoption drops every runtime, and a run finishing afterwards would write its
result onto whichever job inherited its ID, so the switch is refused while a
job is running. Unrelated settings still save during a run. Because the
replacement happens without a prompt, the Service emits JobsLoaded with the
path and count, and History carries the receipt.

A path that names only a folder (trailing separator, a dot, or two dots) is
rejected with a validation error instead of failing later with an opaque OS
error.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:43:56 +03:00
mixeme 5a018d03cb docs: add cron import/export and GUI layout review to the roadmap
Two planned items larger than a single fix:

- Import/export jobs as a cron table, with the open questions that must be
  settled first: the job fields crontab has no slot for, "@every" not being
  valid crontab, splitting Command/Arguments per platform, which lines to
  skip on import, and merge semantics.
- A focused pass over the ui package's custom layouts and tuned constants —
  negative spacings that cancel widget padding, pixel sizes that ignore theme
  metrics, a layout with one call site, and settings_view.go past the size
  guideline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:20:11 +03:00
mixeme e5f8c7a812 docs: changelog and version bump for 0.14.0
Close the Unreleased section as 0.14.0 and fill the gaps in it: the Folder
caption moving onto the filter row, the padding around the Settings button
row, the Truncation-field refactor, the Docker build cache mount, and the
review/standards documents added since 0.13.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:11:31 +03:00
mixeme 5e09ba1d58 feat: open the logs folder from the Settings tab
Reading a log file meant copying the configured path out of Settings and
pasting it into a file manager. The Logs directory row now carries an Open
button beside Browse that reveals the folder directly.

The new src/platform/filemanager package holds the platform split — explorer
on Windows, xdg-open on Linux, an "unsupported" error elsewhere — and starts
the handler without waiting on it, since Explorer exits non-zero even after it
opens the window and blocking would stall the UI thread. A missing path, a
path that is a file, and a handler that will not start are all reported to the
user; the logs directory does not exist until the first run, so that case is
reachable.

The button opens whatever the field currently holds rather than the saved
config, so an edit can be checked before Save. Resolving a relative directory
against the application folder is the store's rule, so resolveConfiguredDir is
now exported as storage.ResolveConfiguredDir instead of being duplicated in
the UI.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 23:09:33 +03:00
mixeme fe13d1f34e feat: make the Arguments placeholder state the field's rule
The hint was a lone example path, which left the one-argument-per-line
convention to guesswork. It now names the rule and shows a flag plus a
path containing a space, so the absence of quoting is visible too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:58:37 +03:00
mixeme 7aa1421639 feat: put the Folder caption on the filter row
The caption sat on its own line above the select, costing the sidebar a full
label of height before the job list started. It now occupies the border
layout's left slot, next to the select and the view toggle, so the whole filter
is one row and the list begins a line higher.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:51:48 +03:00
mixeme 4d014e2f45 docs: add a review agenda and config compatibility rules
Reviewing the project used to mean re-stating what to look at every time.
docs/REVIEW.md now holds that agenda once — nine areas, each anchored to this
codebase — and both entry points point at it rather than copying it: the
/review-project command in .claude/commands, and a section in CLAUDE.md so a
plain-language review request lands in the same place.

STANDARDS.md gains a "Config file compatibility" section. The project has
applied the same rule three times (Theme, JobListView, TimeoutSeconds) without
ever writing it down: a new Config field is omitempty and its zero value means
the previous behavior, a meaningful zero is never backfilled on load, and an
unrecognised enum value reads as the default through one shared helper. With no
migration step and hand-editable files, that is what keeps older configs working.

Also removes docs/PLAN-compact-job-list.md, implemented in edabc57 — everything
but the version bump, which now waits for the release along with the rest of
the Unreleased section. .claude/settings.local.json is ignored so the shared
command can be tracked without per-developer permissions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:44:30 +03:00
mixeme 706aa8e6ba refactor: truncate job names via the non-deprecated Truncation field
fyne.TextTruncate is deprecated in Fyne 2.7.4 in favour of the widget's
Truncation field. Behaviour is unchanged: a long job name is clipped rather
than widening the row, which is what keeps the compact row's status pinned to
the right-hand edge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:44:30 +03:00
mixeme bfb982cc27 docs: add CLAUDE.md pointing at the standards document
Agent sessions did not pick up docs/STANDARDS.md automatically. CLAUDE.md is
loaded every session, so it links the standards, architecture, and test docs,
repeats the few rules most often broken, and records the CGO/MSYS2 build
prerequisite and the commit-to-main convention.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:44:30 +03:00
mixeme 29ce94c3e8 feat: add a compact job list view to the Jobs tab
Each job can now render as a single line — name on the left, status on the
right — instead of the three-line block, so many more jobs fit without
scrolling. A toggle button beside the Folder filter switches between the two
modes and is labelled with the action it performs, matching the existing
"Disable auto" convention.

The choice is persisted as Config.JobListView ("detailed" / "compact", stored
as job_list_view in gosentry.json). Empty, legacy, and unrecognised values all
normalize to detailed, so existing installs keep the current look and the file
never gains a value no reader understands.

Selection, the details panel, the folder filter, and live status updates work
unchanged in both modes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:44:30 +03:00
mixeme e85cbc4eb1 feat: make per-job timeout 0 mean "no timeout" instead of inherit
A per-job timeout now has three distinct states: unset inherits the global
default, an explicit 0 means no timeout and does not inherit, and a positive
value is the per-job limit. Job.TimeoutSeconds became *int so unset and 0 stay
distinguishable in jobs.json.

Also fixes the global default, which could not persist a 0. loadOrCreateConfig
normalized DefaultTimeoutSeconds <= 0 back to 30 on every read of an existing
gosentry.json, so "no timeout" only held until the next restart. The field is
now written unconditionally (no omitempty) and read back as-is.

Existing jobs and configs are unaffected: a job with no timeout_seconds still
inherits, and a saved global default of 30 stays 30.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 22:44:30 +03:00
mixeme 7f4f63eb8e docs: switch job list view control to a toggle button
The plan now uses a button that flips its own text and icon (Compact +
ListIcon / Detailed + ViewFullScreenIcon), mirroring the existing
stopAllButton idiom, instead of a "View" dropdown. It sits beside the folder
filter on the same row via a border layout, so the sidebar header keeps its
current height and the toolbar row is untouched.

The label helpers become nextJobListView and viewToggleText, with tests and
the verification steps updated to match.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 21:54:20 +03:00
mixeme c58fe0d332 docs: add implementation plan for compact job list view
Plans an opt-in one-line rendering of the Jobs tab list (name left, status
right) alongside the current three-line detailed rows, switched from a "View"
dropdown beside the Folder filter and persisted as a new Config.JobListView
field.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 20:07:06 +03:00
mixeme 88511843ce feat: add spacing around Settings tab button row
The Save/Cancel/Defaults row sat flush against the separator above it
and the tab's left edge, tighter than the other vertical gaps in the
tab. Add matching padding spacers to bring it in line.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 14:06:30 +03:00
mixeme e87840e95b build: persist Go build cache across Docker release builds
Mount .gocache/ from the host into the builder container so --rm
container removal no longer wipes GOCACHE between runs.
2026-07-26 13:58:26 +03:00
mixeme 33a246cd13 feat: make global default timeout 0 (infinite) instead of required 30s
DefaultTimeoutSeconds now means "no timeout" when 0/empty, and that is
the new default, rather than an invalid config forcing a positive
value. runner.RunJob avoids context.WithTimeout with a zero duration
(which would expire immediately) and instead runs on a plain
cancelable context when no timeout is configured. Per-job
TimeoutSeconds inherit semantics are unchanged.
2026-07-26 13:57:03 +03:00
mixeme 3992b40eda docs: changelog and version bump for 0.13.0
Documents the branded GoSentry theme and Settings Cancel/Defaults buttons.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 11:26:10 +03:00
mixeme 2c684532da feat: make GoSentry light theme boldly branded
Give the light variant a soft teal window canvas with white inputs,
menus, dialogs, and buttons on top, plus teal-tinted separators, input
borders, and table headers. Cards and fields now lift off a branded
background instead of reading as a plain accent swap on gray. Text stays
dark (delegated to the base foreground) for high contrast on both the
teal canvas and the white surfaces.

Lock the teal canvas and white input background into the theme tests so
the light look cannot silently regress.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 11:26:10 +03:00
mixeme d3e3d30b42 feat: add Cancel and Defaults buttons to Settings
Cancel discards unsaved edits by reloading the saved config; Defaults
loads built-in default values into the form for review before saving.
Extracts the default config into domain.DefaultConfig() so the store
and settings UI share one source of truth.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-26 11:26:10 +03:00
mixeme 84212764e4 feat: selectable branded GoSentry color theme
Add a custom Fyne theme derived from the logo and app icon (deep teal
primary, amber accent, branded job-status colors) with light and dark
variants, and let the user choose between it and Fyne's default theme
from Settings. The dark variant uses deep-teal surfaces to echo the app
icon.

The choice is persisted as a new Config.Theme field ("default" /
"gosentry"), applied at startup before the first frame and live-previewed
when picked in Settings. Empty/legacy configs normalize to the default
theme so existing installs keep the original look.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 11:26:09 +03:00
mixeme eedae3b9f0 docs: use concrete mixeme/gosentry coordinates in update-check item
Replace the <owner>/<repo> placeholders with the actual GitHub release repo
now that it's known.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:40:23 +03:00
mixeme bfd9991a3a docs: roadmap item for GitHub release update check
Plan a best-effort, opt-in check that compares app.Version against the
latest published GitHub Release and surfaces an "Update available" hint in
Settings — detection and notification only, no auto-download.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:39:54 +03:00
mixeme 2ab5f07c7c docs: remove implemented per-job timeout plan
The per-job command timeout is now shipped (0.12.0); its ROADMAP entry is
gone, so the implementation plan is no longer needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:38:59 +03:00
mixeme 48faddb3bd feat: per-job command timeout with global default
Add an optional per-job run timeout following the overlap_policy inherit
pattern: Job.TimeoutSeconds (0 = inherit) resolves against a new
Config.DefaultTimeoutSeconds (default 30s), replacing the hard-coded 30s
guard in runner.RunJob.

- domain/storage: new fields, default 30, load-time normalization
- runner: RunJob takes an explicit timeout; StartOnly stays untimed so it
  keeps measuring launch latency only
- app: effectiveTimeout resolves under mu into runEnv, threaded to runJob;
  seam signature and validation updated; DisplayTimeout helper
- ui: Timeout entry in the job dialog, Default timeout in Settings, and a
  Timeout row in the details panel
- tests + docs (ARCHITECTURE, STANDARDS, ROADMAP, CHANGELOG) updated;
  version bumped to 0.12.0

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:24:50 +03:00
mixeme f4221f6ce4 docs: clarify supported platforms (Windows/Linux; macOS unsupported)
Add a Platforms section to the README stating that autostart and desktop
integration are implemented only on Windows and Linux; macOS may build the
GUI but those features are stubbed. Avoids over-promising ahead of 1.0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:12:34 +03:00
mixeme b3eb429fa6 docs: add per-job timeout implementation plan
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 23:05:29 +03:00
mixeme 40b609a7d4 docs: add logo to README header
Theme-aware <picture> swaps the dark wordmark variant under
prefers-color-scheme: dark so it stays legible on GitHub's dark theme.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 23:59:05 +03:00
mixeme 3a68c705c6 assets: add GoSentry wordmark logo (clock-dial variant)
Recommended variant from gosentry-logo.html: Space Grotesk SemiBold with
an amber "G", a clock-dial "o" (ring + hands), and petrol "Sentry". Ships
self-contained SVGs (glyphs outlined to paths, no font dependency) in
light/dark/mono plus transparent PNGs at 256-2048px, with a README and the
gen_logo.py/raster.py generators for reproducibility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 23:51:38 +03:00
mixeme 5d662c7fd5 ci: trigger release build on published release instead of tag push
Both workflows now run on `release: published` and attach the built
archives to the release the user creates, rather than pushing a tag and
having the workflow create the release. Tag comes from the release event;
GitHub no longer regenerates release notes. Docs updated accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 15:39:59 +03:00
mix d4ca38b45a Create gosentry-logo.html 2026-07-18 14:28:20 +03:00
mixeme 48622a99c2 ci: install Node.js in Codeberg container before JS actions
The golang:1.22-bookworm container has no node binary and Codeberg's
runner does not inject one, so checkout/forgejo-release failed with
'node: not found'. Add a pre-checkout run step (which executes via the
container shell and needs no node itself) to apt-install nodejs before
the JS-based actions run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:58:40 +03:00
mixeme 9866673bab ci: use codeberg-medium-lazy runner tag for Forgejo release build
Codeberg's hosted runners are tagged codeberg-tiny/small/medium (+ -lazy),
not 'docker', so the job was never picked up ('No active runner with tag
docker'). The three CGO cross-compiles exceed the non-lazy 10 min cap, so
use the medium -lazy runner, which relaxes the wall-clock limit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:50:38 +03:00
mixeme 2f965166ba ci: add GitHub and Codeberg release build workflows
Add tag-triggered CI that builds and publishes the linux/amd64,
linux/arm64, and windows/amd64 release binaries on both forges.

- scripts/ci-build-release.sh: shared, Docker-free build+package for all
  three targets (Windows cross-compiled via MinGW), reused by both
  workflows so the build logic lives in one place.
- .github/workflows/release.yml: GitHub Actions, publishes via
  softprops/action-gh-release using the built-in token.
- .forgejo/workflows/release.yml: Forgejo Actions for Codeberg, publishes
  via forgejo-release using a RELEASE_TOKEN secret.
- .gitattributes: force LF on workflow YAML so bash run: blocks don't
  break on Linux runners.
- docs/DEVELOPMENT.md: document the tag -> release flow and token setup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 08:32:07 +03:00
mixeme e9ec48bf15 docs: remove <br/> from mermaid labels to fix Gitea rendering
The prior fix switched \n to <br/> in node labels, but Gitea's mermaid
renderer still emits unclosed <br> tags in the generated SVG regardless
of source syntax. Use single-line labels instead to avoid <br> entirely.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 08:09:22 +03:00
mixeme 2c38941f6e docs: fix mermaid diagram XML parse error in Gitea rendering
Gitea's SVG renderer requires well-formed XML; mermaid's \n line
breaks produce unclosed <br> tags in foreignObject labels. Use
explicit <br/> instead.
2026-07-08 07:49:22 +03:00