Agents working in this repo must treat git as read-only — use it freely
to inspect state (status, diff, log, show, etc.), but never to mutate
it. That means no git add, git rm, git commit, git push, git checkout -b, staging, or committing on the owner's behalf, even when asked
to "clean up" or "finish" something — do the work as plain filesystem edits
(create/edit/delete files directly) and leave the working tree unstaged. The
owner reviews the diff and merges/commits locally themselves; that's a
deliberate workflow choice (mirrors a no-agentic-git policy from their day
job) so that a human stays accountable for every commit that lands. This
applies across this project, not just to any one task.
Guidance for AI coding agents working in this repository. This documents current state as of 2026-07-10, not the target state — the project is about to go through an overhaul, so treat everything here as "what exists today," which may be rewritten or removed.
cycles is a small desktop CPU/memory monitor written in Go, using the
Fyne GUI toolkit. Single Go module, single main
package, no subpackages. It's a personal hobby project ("threw together
while debugging my computer") — expect inconsistent polish and some
aspirational documentation (see "Docs vs. reality" below).
- Module:
cycles(go.mod), Go 1.21.5 toolchain, built withgo1.22.2locally. - Linux-only today (reads
/proc/cpuinfo,/proc/meminfodirectly). - Dependencies are vendored in
vendor/(Go will use vendor mode automatically sincevendor/modules.txtis present andgo.moddeclaresgo 1.21.5). - Distributed as: plain binary, AppImage, Snap. Flatpak was attempted and
abandoned (never got it working) — it's explicitly deprioritized now.
Snap Store is the priority distribution channel going forward; Flatpak
is a nice-to-have at best. An orphaned, never-wired-up Flatpak manifest
still sits at repo root (
us.tylerc.cycles.yaml,us.tylerc.cycles.desktop,us.tylerc.cycles.appdata.xml) — parked there deliberately rather than deleted, in case Flatpak gets revisited later.
As of this date go build, go vet, go test, and a manual smoke-test run
of the built binary are all clean. This section records what was actually
wrong, in case similar bugs get reintroduced during the overhaul:
- Compile error:
settings.go/settingsui.goreferencedfyne.Color(not a real type — vendored Fyne v2.4.2 usescolor.Colorfromimage/color, seetheme.gofor the correct pattern) andfyne.VariantLight/fyne.VariantDark(those constants live infyne.io/fyne/v2/theme, not thefynepackage itself). - Preferences silently didn't persist:
main.gocalledapp.New()instead ofapp.NewWithID("us.tylerc.cycles"). Fyne's Preferences API (used bysettings.gofor save/load) requires a unique app ID or it logs a warning and settings never actually hit disk — the "settings persist between runs" feature was never true at runtime despite compiling and passing unit tests. Only caught by actually launching the app. - Infinite recursion → stack overflow on startup:
CustomThemeinsettingsui.go(installed viaApplyTheme()→app.Settings().SetTheme(&CustomTheme{...})) delegated itsColor/Font/Icon/Sizemethods tofyne.CurrentApp().Settings().Theme()— but once installed, that call returns theCustomThemeitself, so every color lookup recursed into itself forever. Fixed by delegating to the stabletheme.DefaultTheme()base theme instead. This is why a build-clean / vet-clean / tests-passing state is not sufficient proof the app works — always smoke-test the actual running binary (fyne.io/fyne/v2/testcovers widget construction in unit tests, e.g. theTestMaininmemorytile_test.go, but not full app wiring likeApplyTheme). - Version-string drift across
config.go(0.6.0),snap/snapcraft.yaml(was 0.4.1), and.github/workflows/appimage.yml(hardcoded 0.4.1 into the artifact filename) — resolved by pinning snapcraft to 0.6.0 and making the AppImage workflow deriveVERSIONfromconfig.goat build time instead of hardcoding it, so it can't drift again silently.
Flat file-per-concern layout, no internal packages:
| File | Responsibility |
|---|---|
main.go |
Entry point: config/settings load, window+menu setup, tile grids, update goroutines |
config.go |
AppConfig struct, defaults, CLI flag parsing (--columns, --interval, --history, --logical) |
settings.go |
Settings struct — persisted via Fyne Preferences (theme, grid columns, history size, logical cores, update interval) |
settingsui.go |
Settings dialog (Fyne form) + CustomTheme wrapper type |
theme.go |
Graph line colors per utilization band (green/yellow/red) x light/dark, ApplyTheme() |
tile.go |
CoreTile — one CPU core's UI tile (labels + history graph) |
memorytile.go |
MemoryTile — memory UI tile + byte-size/percent formatters |
sysinfo.go |
Reads /proc/cpuinfo and /proc/meminfo directly (not via gopsutil, deliberately — see DEVELOPER_GUIDE.md "Architecture Decisions"); UpdateCPUInfo / UpdateMemoryInfo mutate tile slices in place |
graphics.go |
DrawGraph (line chart into an image.RGBA), Bresenham line drawing, label formatters |
info.go |
About-dialog text: app version + OS/arch/Go version |
*_test.go |
One test file per corresponding source file |
Runtime shape: main() builds a config, loads/merges Settings from Fyne
preferences (CLI flags override saved settings), builds two Fyne tab pages
(CPU grid, Memory grid), and spins up two independent polling goroutines
(time.Sleep(config.UpdateInterval) loops) that mutate tile widgets from a
background goroutine and call .SetText() / redraw directly — no
synchronization/locking around cross-goroutine UI mutation, relies on
Fyne's internal thread-safety of widget updates.
gopsutil (github.com/shirou/gopsutil, the v1-style import path, not
v3/v4 module path despite go.mod saying v3.21.11+incompatible) is
only used for cpu.Counts() and cpu.Percent(); everything else in
sysinfo.go hand-parses /proc files.
As of 2026-07-10 the four prior-agent planning/retrospective docs that used
to live at repo root (OVERHAUL_PLAN_V2.md, OVERHAUL_SUMMARY.md,
SPRINT_SUMMARY.md, PACKAGING_PLAN.md) were deleted — they described a
10-sprint plan targeting v1.2.0 of which only Sprints 1–2 were ever actually
implemented (memory tab, settings system), plus retrospectives and a
packaging roadmap that had gone stale. Don't recreate docs like these
unless the user explicitly asks for a persistent plan file — prefer
conversation-scoped planning (see repo's general "don't create planning
docs unless asked" convention).
Remaining docs, with caveats:
README.md— corrected 2026-07-10 to mention the Memory tab, Settings dialog, and theme toggle, and to drop the old "FlatPak coming in 0.5" promise. Still contains an intro line about the author being disengaged from the project ("not dedicating a lot of time... since I'm not on a machine with Snap access anymore") that's now inaccurate given the overhaul — left as-is since it's the owner's voice, not a factual claim.DEVELOPER_GUIDE.mdis the most accurate of the remaining docs and matches the code layout well; its "Testing Checklist" now reflects reality again since the build/runtime fixes above.CHANGELOG.mdis real version history, trust it for what shipped when.
When in doubt, trust the code and git log, not these docs.
make setup # OS-detecting dependency installer (scripts/setup-dev.sh)
make build # -> build/cycles
make run # build + run
make dev # unoptimized quick build
make test # go test -v ./...
make check # fmt + vet + test (all green as of 2026-07-10)Native Go equivalents also work (go build ., go test ./...). System
package dependencies (X11/GL dev headers) are required to build at all — see
scripts/setup-dev.sh or the README's "Manual Setup" section for the
per-distro package list.
Test coverage exists for config.go, graphics.go, info.go,
memorytile.go, settings.go (one _test.go each), all passing.
memorytile_test.go has a package-level TestMain that boots a headless
fyne.io/fyne/v2/test app — needed because widget constructors that call
theme.BackgroundColor() (e.g. NewMemoryTile, and NewCoreTile if it
ever gets tests) panic without a fyne.CurrentApp() in scope. Any new test
that constructs a Fyne widget touching theme colors relies on that same
TestMain, so keep it if you add or move test files.
- One file per concern, table-driven tests, exported
PascalCase/ unexportedcamelCase, doc comments on exported functions — seeDEVELOPER_GUIDE.md"Code Style Guidelines" for the fuller version the project has been trying to follow. - Version string lives in
config.go(DefaultConfig().Version) and is still duplicated by hand intoCHANGELOG.mdandsnap/snapcraft.yaml(kept in sync manually as of 2026-07-10 — no automation yet, so bumping the version means updating both). The AppImage workflow now derives its filename fromconfig.goautomatically, so that one can't drift again. vendor/is intentionally committed (reproducible builds, offline CI) — don't.gitignoreit or assumego mod vendoris optional when adding deps.
The stated goal is to get the project working and clean again before attempting the larger UI overhaul. Roughly in order:
Phase 0 — get it building again — done 2026-07-10
Fix theDone.fyne.Color/fyne.VariantLight/fyne.VariantDarkbuild break.GetDone — also fixed three latent test bugs uncovered once compilation succeeded (stale hardcoded version/column expectations, a segfault from missing Fyne test-app context, and inverted light/dark constant assumptions). See "Build/runtime health" above.make checkgreen; confirm tests pass.Manually smoke-test the running app.Done — this caught two runtime bugs invisible to build/vet/test: preferences never actually persisting (missing app ID) and an infinite-recursion stack overflow inCustomThemeon startup. Both fixed; see "Build/runtime health" above.Resolve version-string drift.Done —snap/snapcraft.yamlbumped to 0.6.0,.github/workflows/appimage.ymlnow derives its version fromconfig.goinstead of hardcoding it.
Phase 1 — surface cleanup (docs done 2026-07-10; rest pending)
5. Purge stale planning docs. Done.
6. Reconcile README against real features. Done.
7. Orphaned Flatpak manifest at repo root — currently just parked, no action
taken yet.
Phase 2 — harden Snap (the priority channel over Flatpak)
8. snap/snapcraft.yaml is on base: core20; consider moving to a current base.
9. No CI builds/tests the Snap package today — only AppImage + repo-archive
workflows exist under .github/workflows/.
10. Verify whether AppImage is actually broken as older docs claimed, rather
than assuming — there is a working-looking GH Action for it already.
Phase 3 — UI overhaul 11. Current layout is a static Fyne grid that doesn't scale or let users rearrange tiles — this is the main motivation for the overhaul. Open decision: rebuild the layout within Fyne (responsive/reflowing container) vs. reassess whether Fyne is still the right toolkit at all. Don't assume either answer — this needs a deliberate call before code. 12. Define what "good" looks like (resizable/reflowing grid? drag-to-rearrange? separate panels per metric type?) before implementation.