Repository navigation
chore: pre-public prep — tooling, CI, English translation, scrub refs - #1
Merged
Merged
Conversation
20 of 21 tasks
dezween
added a commit
that referenced
this pull request
Jul 12, 2026
* docs(01): synthesize phase context * docs(01): add research and validation strategy * docs(01): lock test stack (testify/mockgen) and context usage * docs(01): add phase plan * feat(domain): add pure domain package with Interval and Offset Add internal/activity/domain with validated Interval (wraps time.Duration, rejects non-positive) and Offset (wraps int, rejects non-positive) value types, per ARCH-04. Package has zero cgo/os dependency and builds under GOOS=linux. Covered by table-driven tests using testify assertions. Pulled the testify go.mod dependency forward from Task 3 since the domain test needs it to compile; go.mod/go.sum updated via go mod tidy. * feat(port): define Pointer and Keeper ports with mockgen directive Add internal/activity/port with the driven port Pointer (Nudge(dx, dy int) error) and the driving port Keeper (Run(ctx context.Context) error), per ARCH-02/ARCH-03. Ports import nothing from adapter. Add a committed //go:generate mockgen directive in pointer.go targeting the Pointer interface, generating into port/mock (package mock) as the locked test stack requires a generated mock, not a hand-written fake. * test(port): add go.uber.org/mock dep and generate Pointer mock Add go.uber.org/mock as a test-only dependency and generate internal/activity/port/mock/mock_pointer.go via mockgen, driven by the //go:generate directive in pointer.go. The generated file carries the standard DO NOT EDIT header and is exempt from lint under exclusions.generated: strict. Switched the go:generate directive to invoke the installed mockgen binary directly (rather than `go run go.uber.org/mock/mockgen`), since running mockgen as a tool dependency via `go run` requires its full transitive dependency graph in go.sum for no benefit here. go.mod/go.sum updated via go mod tidy: testify and go.uber.org/mock are test-only requires, never linked into the production binary. * feat(service): implement KeeperService with injected slog logger Add internal/activity/service.KeeperService implementing port.Keeper: a ticker loop that nudges the pointer forward then back on each tick with a configurable pause, and stops cleanly on context cancellation. Logger and Pointer are constructor-injected, never package globals, per LOG-01 and CONTEXT.md. Depends only on domain and port, never adapter. Add keeper_service_test.go covering nudges-per-tick and stop-on-cancel using testing/synctest for a deterministic fake clock, the mockgen Pointer mock with gomock call ordering, and testify assertions. Tests run silent (slog.DiscardHandler) and never touch the darwin adapter. * feat(adapter): relocate cgo CoreGraphics adapter behind darwin build tag Add internal/activity/adapter.CGPointer implementing port.Pointer via cgo/CoreGraphics, per ARCH-02. The file leads with //go:build darwin followed by a blank line, then the package clause. The C nudge() function body -- including both CFRelease(cur) and CFRelease(move) calls -- is copied verbatim from the original main.go (diff-verified byte-for-byte identical) to avoid reintroducing a fixed CoreFoundation object leak in this long-running process. The #cgo LDFLAGS and #include lines are unchanged. This is the only package permitted to import "C"/cgo. * feat(cmd): add cmd/sysmon entry point and remove root main.go Add cmd/sysmon/main.go as the new wiring-only entry point (ARCH-01, ARCH-05): parses -interval/-quiet with the same names/usage/defaults as before (BUILD-02), builds a slog.TextHandler logger whose level is raised to LevelWarn under -quiet (not a stream redirect -- LOG-01), replaces the manual signal.Notify/select plumbing with signal.NotifyContext(SIGINT, SIGTERM), constructs adapter.NewCGPointer() and service.NewKeeperService via manual DI, and calls keeper.Run(ctx). Removes the flat root main.go. Fix .gitignore: the bare `sysmon` pattern (meant to ignore the built root binary) also matched the new cmd/sysmon/ directory anywhere in the tree, silently blocking it from being tracked. Scoped the ignore to /sysmon (repo-root binary only) so cmd/sysmon/ is tracked normally. Manually verified: builds, runs, logs "started"/"stopped", -quiet produces no stdout output, and SIGTERM yields a clean exit code 0. * build(makefile): point build target at ./cmd/sysmon and pass full gate Change the Makefile build target to `go build -o $(BINARY) ./cmd/sysmon` now that main.go lives under cmd/sysmon; all other targets are unchanged (BUILD-01). Fix real issues surfaced by the full quality gate (go build ./..., golangci-lint run ./..., go test -race ./...): - gocritic exitAfterDefer in cmd/sysmon/main.go: os.Exit(1) was called directly inside main(), which skips the deferred stop() for the signal-notify context. Split main() into a thin os.Exit(run()) wrapper plus a run() that returns an exit code, so deferred cleanup always runs before the process exits. - golangci-lint's gofumpt formatter misformatted files that import our own sysmon/... packages, because formatters.settings.gofumpt lacked module-path -- without it, gofumpt cannot distinguish local-module imports from third-party ones during import grouping. Added `module-path: sysmon` to .golangci.yml (this only started mattering once cross-package internal imports existed, i.e. with this refactor). Also fixed keeper_service_test.go's import order (local packages before third-party) to match goimports local-prefixes. `make build` now produces ./sysmon from ./cmd/sysmon; go build ./..., golangci-lint run ./... (0 issues), and go test -race ./... all pass. * docs(01-hexagonal-refactor): add plan 01 execution summary Document Tasks 1-7 of the hexagonal refactor plan: files created/ modified, task commits, decisions, and four auto-fixed deviations (testify pulled forward, .gitignore blocking cmd/sysmon, gocritic exitAfterDefer, gofumpt module-path). Task 8 (human-verify checkpoint) is explicitly called out as pending. * fix(01): enforce domain validation, typed keeper ctor, interruptible nudge pause Addresses code review #1 (CR-01, WR-01, WR-02, IN-01, IN-02): - wire domain.Interval/Offset into cmd + KeeperService so a non-positive -interval exits with code 2 instead of panicking in time.NewTicker - typed constructor params remove the two-duration swap hazard; nudgePause is now a service-internal constant - pause between nudges is interruptible via ctx so shutdown is prompt - document that a failed forward nudge intentionally skips the back-nudge * test(01): cover cancel-during-pause path; clarify nudgeCycle docs Addresses code review #2 (WR-01, IN-01): - add TestKeeperService_CompensatesOnCancelDuringPause: forward nudge triggers cancel() so the pause select takes ctx.Done, asserting the compensating back-nudge still fires and Run returns - clarify nudgeCycle/Run comments: back-nudge still runs after an interrupted pause; only a failed forward nudge skips it * harden(01): surface nudge failures and accessibility preflight Addresses custom-prompt review #1 (P2 findings): - cgo nudge null-checks CGEventCreate/CGEventCreateMouseEvent and returns a status; CGPointer.Nudge now returns ErrNudgeFailed instead of always nil - add AccessibilityTrusted() (AXIsProcessTrusted) and warn once at startup when permission is missing, so a silent no-op is explained - clarify NewOffset validation goes through the domain by design * docs(01): correct -quiet flag help to match level-raise behavior Addresses custom-prompt review #2: -quiet raises the log level to Warn (it does not silence stdout), so warnings/errors incl. the accessibility preflight still print. Reword the flag help accordingly. * test(01): comprehensive coverage and edge cases - domain: errors.Is sentinel checks, boundary values (1ns, 1px, MaxInt, MinInt), Duration/Int round-trips - service: forward-nudge failure skips back-nudge, back-nudge failure tolerated, custom offset, and LOG-01 start/stop logging via a capturing slog.Handler - adapter (darwin): CGPointer smoke test with zero-offset nudge and AccessibilityTrusted probe - cmd (darwin): black-box integration -- invalid-interval exit code 2, and SIGTERM/SIGINT lifecycle asserting clean startup/shutdown domain and service coverage 100%; service tests stay cgo-free (GOOS=linux). * chore: clear stale auto-chain flag * build(makefile): add INTERVAL variable for run/start targets Lets the nudge interval be overridden without editing the file, e.g. make start INTERVAL=3s. Defaults to 10s. --------- Co-authored-by: dezween <dezween@users.noreply.github.com>
dezween
added a commit
that referenced
this pull request
Jul 12, 2026
* docs: add task spec for realistic random cursor roaming * docs(quick): plan realistic random cursor roaming * feat(quick-260712-roaming-01): add pure domain movement planner with deterministic tests - Planner.Plan interpolates from current position to a fresh uniform random target, clamped to screen bounds - randomness injected via a minimal RandomSource seam so tests are deterministic - path is continuous, ends exactly at target, covers corner/1x1/target-equals-current edge cases - cgo/os-free, builds under GOOS=linux * feat(quick-260712-roaming-02): reshape Pointer port for absolute roaming and regenerate mock - replace Nudge(dx, dy int) error with MoveTo(x, y int) error, Position(), and Bounds() - regenerate mockgen mock from the new interface (MoveTo/Position/Bounds) Note: this leaves the tree non-building until the service/adapter/main cutover in the following tasks, as planned. * feat(quick-260712-roaming-03): rewrite KeeperService to roam once per tick - roam(ctx): read Position/Bounds, ask domain.Planner for a glide path, then drive() calls MoveTo per path point with a short inter-step pause - ctx checked between every step so cancellation stops promptly mid-glide - Position/Bounds/MoveTo errors are logged and abandon the current roam without crashing the loop - tests rewritten with synctest + regenerated mockgen mock: one roam per tick, ordered MoveTo, mid-glide cancel, per-call error handling, start/stop logging Note: this leaves the tree non-building until the adapter/main cutover in the following tasks, as planned. * feat(quick-260712-roaming-04): add CoreGraphics Position/Bounds/MoveTo to CGPointer - getPosition/getBounds/moveTo C helpers replace the relative nudge helper: current cursor location via CGEventGetLocation, main-display bounds via CGDisplayBounds(CGMainDisplayID()), absolute move via a MouseMoved event - every CoreFoundation object created is released; NULL-create failures release nothing and signal failure - ErrNudgeFailed split into ErrPositionFailed and ErrMoveFailed - compile-time assertion that *CGPointer satisfies port.Pointer - tests updated to the new API (position/bounds/move-to-current-position smoke tests, no CI mouse movement) * feat(quick-260712-roaming-05): wire main.go for random roaming and remove dead Offset domain code - construct a runtime-seeded *math/rand/v2.Rand (rand.NewPCG from the auto-seeded package generator) and a domain.Planner, wired into KeeperService alongside the pointer/logger/interval - remove nudgeOffset constant and the domain.NewOffset validation - update flag help text and package doc comment to describe random roaming instead of the 1px nudge - keep the AccessibilityTrusted preflight warning and SIGINT/SIGTERM clean-exit-0 signal handling unchanged - delete domain.Offset, NewOffset, and ErrNonPositiveOffset (and their tests): roaming uses absolute clamped targets, so a relative offset value object no longer has meaning - fix a stale ErrNudgeFailed reference in a service test comment * docs(quick): add roaming task summary * fix(roaming): clamp out-of-bounds start, reuse glide timer, clarify docs Addresses roaming code review #1: - CR-01: clamp the starting cursor position into the display bounds before interpolating, so a cursor on a secondary monitor (global coords) cannot send early glide steps off-screen; covered by a new planner test - WR-01: reuse a single time.Timer across the glide instead of time.After per step - WR-02/IN-01/IN-02: clarify rounding, bounds-per-roam, and adapter Bounds contract * test(roaming): fix orphaned comment, cover exact clamp boundary Addresses roaming code review #2 (IN-01, IN-02): restore the OnePixelScreen doc comment to its function and make the clamp test table-driven, adding the exact boundary case (curX == width). * refine(roaming): start glide timer stopped; assert in-bounds moves at service seam Addresses custom-prompt review #1 (P2): - start the reused glide timer stopped so the first inter-step gap is exactly stepPause - add a service synctest asserting every MoveTo stays within [0,w)x[0,h) even when Position() reports an out-of-bounds start * test(roaming): cover roam error paths and add planner edge cases - service: invalid-bounds (Plan error) abandon path and move-error-on-first-step abandon -- service coverage now 100% - planner: very large screen, bounded step delta corner-to-corner, and different-seed-different-path cases --------- Co-authored-by: dezween <dezween@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Sets up project tooling (linter, CI, PR template) and records key decisions before the Phase 1 hexagonal refactor.
Type of change
main.go)What changed
.golangci.yml— golangci-lint v2 config, modeled on the minitok golden config, trimmed of minitok-specific (moneylint/httpxlint/walletledgersqllint) and web/DB linters..github/workflows/ci.yml— CI with build / lint / test jobs onmacos-latest..github/pull_request_template.md— detailed project PR template.main.go— package comment + extracted magic-number constants (defaultInterval,nudgePause) so lint is green..planning/— recorded thelog/sloglogging decision + CI/lint decisions; addedLOG-01requirement; switched GSD to per-phase branching.Why / context
How it was tested
Checklist
Architecture & code
macOS / cgo
Concurrency & resources
Safety & hygiene
Risks & rollback
Low — tooling/docs only. Revert the branch to roll back. `log/slog` is only decided, not yet wired in.
Out of scope / follow-ups
Reviewer notes
CI runs on macOS only. The logger is a decision here, integrated during the refactor.