fix: address Copilot review feedback — correctness fixes#44
fix: address Copilot review feedback — correctness fixes#44kevinelliott merged 3 commits intomainfrom
Conversation
Seven real-bug fixes called out across review comments on merged PRs: - pkg/platform/platform.go: resetLookPathCache() no longer reassigns the package-level sync.Map (unsafe vs concurrent readers). Uses Range+Delete instead. - pkg/installer/providers/brew.go: GetLatestVersion no longer caches context-canceled / deadline-exceeded errors. A caller with a short ctx would have poisoned the 5m TTL for every later caller. - pkg/installer/providers/progress.go: the progress writer is now wrapped in a locking writer so concurrent Write calls from cmd.Stdout and cmd.Stderr (os/exec writes those from separate goroutines) don't interleave bytes mid-call. - pkg/catalog/embed.go: EmbeddedJSON() now returns a copy of the backing bytes. Previously callers could mutate the package-level []byte, corrupting the baseline for every future Get(). - pkg/catalog/manager.go: loadEmbedded now also probes /usr/share/agentmgr/catalog.json — where the goreleaser nfpm config installs the catalog for .deb/.rpm users. Previously that path was silently ignored at runtime even though goreleaser wrote the file there at install time. - internal/cli/doctor.go: Catalog Sources section updated with the matching /usr/share/agentmgr row so `doctor` output mirrors the loader's probe order. - internal/cli/agent.go: removed the --all (-a) and --check-updates flags from `agent list`. Both bound to local vars that were never read anywhere, so the flags did nothing; users setting them got a silent no-op instead of a useful behavior. Supersedes the corresponding Copilot annotations on PRs #19, #28, #29, #33, plus the dead-flag note on the agent list command. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR applies a set of targeted correctness fixes across platform caching, installer provider behavior, and catalog/source resolution so runtime behavior matches intended concurrency, packaging, and CLI semantics.
Changes:
- Fix concurrency and mutation hazards (sync.Map cache clearing, progress output writer, embedded catalog bytes exposure).
- Prevent process-wide caching of caller-specific context cancellation errors in Homebrew latest-version lookups.
- Align catalog probe order and diagnostics with packaged install locations; remove dead/no-op CLI flags.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pkg/platform/platform.go | Clears lookPath memoization cache via Range+Delete instead of non-atomic map reassignment. |
| pkg/installer/providers/progress.go | Wraps progress writer with a mutex to prevent stdout/stderr interleaving from concurrent goroutines. |
| pkg/installer/providers/brew.go | Avoids caching context-canceled/deadline-exceeded errors in the latest-version TTL cache. |
| pkg/catalog/manager.go | Adds /usr/share/agentmgr/catalog.json to the system catalog probe list to match nfpm installs. |
| pkg/catalog/embed.go | Returns a defensive copy of embedded catalog bytes to prevent global slice mutation. |
| internal/cli/doctor.go | Updates “Catalog Sources” documentation/output to reflect the new /usr/share/agentmgr probe path. |
| internal/cli/agent.go | Removes unused agent list flags (--all/-a, --check-updates) that previously had no effect. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Leaving the cache empty and clearing the Once lets the next call | ||
| // refetch with its own ctx. | ||
| if err != nil && (errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded)) { | ||
| brewLatestVersionOnce.Delete(key) | ||
| return |
There was a problem hiding this comment.
In the ctx-canceled/deadline-exceeded path, the once.Do closure deletes the sync.Once entry and returns without storing to brewLatestVersionCache. That means concurrent callers sharing the same *sync.Once will see an empty cache after Do() and fall through to the final direct fetch path, which bypasses coalescing and also doesn’t populate the cache (so you can get multiple concurrent brew info subprocesses and lose caching for that wave). Consider restructuring so that when Do() completes and the cache entry is still missing, the function re-enters the coalesced path (e.g., retry/loop after clearing the Once) rather than doing an uncached direct fetch, and update the “cache is guaranteed to be populated” assumption accordingly.
Batch of small review-comment fixes — documentation accuracy, test hardening, and gosec G104 on grpc teardown: - pkg/api/grpc/server.go: explicit \`_ = s.listener.Close()\` in Stop() and ForceStop() with a comment noting the close error is not actionable during teardown (silences gosec G104). - pkg/catalog/manager.go: Refresh doc now explicitly notes that singleflight does not merge contexts and the first caller's ctx governs the shared fetch — a caller with a short deadline can cancel the in-flight work for all shared callers. - pkg/installer/providers/progress.go: WithProgressWriter doc clarifies why passing nil is a no-op (asymmetry lets middleware attach a writer safely without downstream code accidentally clobbering it). - internal/systray/cli_uninstall_darwin.go: file is darwin-only via //go:build tag, so platform.ID() is always Darwin at runtime. Removed the dead Linux and Windows branches (the filepath/os/platform imports they needed are gone too). - CHANGELOG.md: "(Removed in [Unreleased].)" was a broken shortcut reference link — promoted to "(Removed in v1.2.0.)". - README.md: feature bullet uses the full "agentmgr agent list" command prefix, consistent with everywhere else in the doc. - internal/cli/output/spinner_test.go: non-TTY test replaced the 30ms sleep with a direct assertion on s.isTTY. Start() is a no-op when isTTY is false, so sleeping was measuring nothing. - internal/cli/output/table_test.go: ANSI-alignment loop now actually asserts inter-column padding instead of discarding each line. - internal/systray/handlers_test.go: mustParseVersion panics on parse error so test-setup bugs surface at the fixture, not downstream. TestDialogTracking uses bare *exec.Cmd values (trackDialog only checks pointer identity) to avoid the PATH lookup for \`true\` on Windows CI. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The --all / -a and --check-updates flags were removed from \`agent list\` in the parent commit because both bound to unused local vars. Drop the existence assertion for "all" and add an anti-regression check that asserts both flags are ABSENT. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Patch release. The notable shipping fix is the brew cask metadata recognition (#45) — five catalog entries declared cask-ness via the legacy "cask: true" key that the parser ignored, so brew install ran without --cask and Homebrew rejected those agents. Also rolls up the broader review-feedback batch in #44 (race fix, ctx-error caching, concurrent-write safety, /usr/share probe path, removed dead --all/--check-updates flags, etc.). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Seven real-bug fixes called out across Copilot review comments on merged PRs (#19, #28, #29, #33) plus the dead-flag note on `agent list`.
Fixes
Test plan
🤖 Generated with Claude Code