gum v2.2.1
This patch fixes the Keyword Planner guidance that let a caller ask for United States figures and get worldwide ones, clears the thirteen lint findings that kept the test workflow red on every run since v2.1.0, regenerates the catalog from upstream, and drops macOS notarization from the release pipeline.
Highlights
- Keyword Planner operations say what happens when you omit geo or language
gum describecan show an optional argument in its example. A cataloggum describe googleads.keywordPlanIdeas.generateKeywordIdeasreturns a- The
testworkflow is green again. It failed on every run since v2.1.0, so - The release pipeline no longer signs or notarizes macOS binaries, and the
targeting. The old wording, "Omit for all locations.", reads like a default; the figures it produces cover every location and nothing in the response marks them. The three keywordPlanIdeas operations now carry a worked targeting example as well.
operation may curate example_args, which overlays the synthesizer's required-field output.
runnable example. It previously had no seed at all, because the operation takes keywords and/or url and neither is a required field.
v2.1.0 and v2.2.0 both shipped with that gate red.
v2.2.0 note telling readers to clear quarantine by hand is corrected.
Install
curl -fsSL https://raw.githubusercontent.com/ehmo/gum/main/install.sh | GUM_VERSION=v2.2.1 bash
gum --version
gum doctorUpgrade notes
None. No configuration, credential, or stored state changes.
If you ran a Keyword Planner operation without geoTargetConstants or language, the figures you received cover every location and every language. Re-run with targeting to get country and language figures; they cannot be rescaled afterwards.
gum read googleads.keywordPlanIdeas.generateKeywordHistoricalMetrics \
--args '{"keywords":["horse breeds"],"geoTargetConstants":["2840"],"language":"1000"}'A per-profile default removes the need to pass them on every call:
gum config set googleads.geo_target_constants=2840
gum config set googleads.language=1000Added
example_argson a catalog operation.gum describesynthesizes its example
from required fields, so an optional field whose omission changes what the answer means could never appear there. A curated example_args map is applied as the last overlay, which lets it both add optional arguments and correct a synthesized placeholder. The field is additive and optional under catalog ABI rule 4: operations without it produce the same example as before, and older binaries ignore it.
Changed
- Catalog regenerated from the upstream Google discovery documents. 228
- The release pipeline no longer signs or notarizes macOS binaries. The
operations before and after: none added, none removed. Google reworded the chat.spaces.messages.create requestId description from "A unique request ID for this message" to "A unique ID for this request. A random UUID is recommended.", and the derived request schema carries the same wording. No scope, risk class, auth strategy, or required argument changed, so no existing call behaves differently.
notarize block is gone from apps/gum/.goreleaser.yaml and the five MACOS_* secrets are gone from the goreleaser job. gum ships plain CLI binaries, not app bundles, and no install path it publishes needs a Developer ID signature. See Known limitations for what was measured.
Fixed
- Keyword Planner geo and language targeting.
geoTargetConstantsand gum describe googleads.keywordPlanIdeas.generateKeywordIdeasreturns an- Thirteen lint findings, three from staticcheck and ten from golangci-lint.
language are optional, and a call that omits either returns figures covering every location or every language with nothing in the response marking them. The only warning was in docs/auth-guides/google-ads.md, which an MCP caller never reads, and gum describe made it worse: it synthesizes the example from required fields, so the canonical example for all three keywordPlanIdeas operations showed customerId and a seed and left the targeting out. Both field descriptions now name the consequence and the per-profile config key, and all three examples carry geoTargetConstants: ["2840"] and language: "1000", the United States and English ids used in the worked example in docs/auth-guides/google-ads.md.
example that runs. The operation takes keywords and/or url, so neither is marked required and the synthesizer emitted neither; a caller who pasted the example got a request with no seed.
staticcheck runs first and short circuits the job, so CI never printed the golangci-lint half. bbolt v1.5.0 deprecated the top-level ErrTimeout alias; the cache-lock comparison moved to go.etcd.io/bbolt/errors, which holds the same value bolt_unix.go returns, so a second opener still gets ErrCacheLocked rather than ErrCacheCorrupt. That branch had no test and now has one. The rest are unchecked fmt.Fprint returns in gain_render.go, a double negative in the MCP elicitation capability check, and findings in two test files. No runtime behaviour changed.
Security
No security fixes in this release. govulncheck is a blocking release gate. It reports 0 vulnerabilities in gum's own code and 0 in the packages gum imports. One vulnerability sits in a module go.mod requires and no gum code path calls.
Known limitations
- 217 of 228 catalog variants declare no
default_fields. Spec §5.6 curates the - macOS binaries carry the Go linker's ad-hoc signature, not a Developer ID
- The ETag store has no TTL and no eviction. It grows until
gum cache clear - A second gum process on the same profile cannot open the ETag store while the
example_argsis curated on three operations. Every other operation's- The
v2.0.0tag tree still failsmake fmt-check. A published tag is not
table by hand and it covers 11 high-traffic read operations, so every other variant sends no upstream field mask unless the call passes --fields, and its response comes back full size.
signature, and are not notarized. Measured on the published v2.2.0 darwin/arm64 artifact under Darwin 25.6.0: codesign -dvvv reports flags=0x20002(adhoc,linker-signed) and codesign -v exits 0, curl sets no com.apple.quarantine attribute, and a copy with quarantine forced on still runs from a shell. spctl --assess --type execute reports rejected, and no install path gum publishes consults that verdict. Notarization matters for a cask, a .pkg, or an app bundle, none of which gum ships. Older macOS versions were not tested.
runs. Check its size with gum cache stats.
first holds it, for example a gum call during a long-lived MCP session. The second process waits 250 ms, then dispatches without revalidation and fetches the full body. gum cache stats reports entries: 0 for the locked store in that case.
example still comes from required fields alone, so an optional argument that changes what an answer means can still be missing from it.
moved, because its binaries, checksums and provenance all name that commit.
Token savings
Measured with the release fixtures using a local build stamped 2.2.1, run from the apps/gum directory of the matching source checkout:
gum gain --fixture-replay --format=toon
gum gain --fixture-replay --format=json| Default format | Total calls | Total tokens in | Total tokens saved | Aggregate savings |
|---|---|---|---|---|
toon |
10 | 3,922 | 210 | 5.35 % |
json |
10 | 3,922 | -12 | 0.31 % overhead |
Both rows are unchanged from v2.2.0. This release changes no shaping code, and the fixture replay measures the shaping pipeline.
Reproducibility
git clone https://github.com/ehmo/gum.git
cd gum && git checkout v2.2.1
cd apps/gum
GOTOOLCHAIN=go1.26.7 CGO_ENABLED=0 GOOS=<os> GOARCH=<arch> go build -trimpath \
-ldflags='-s -w -X main.version=2.2.1' ./cmd/gum
sha256sum gumBuild from a full clone, not from a linked git worktree. Go embeds the commit revision in the binary, and it silently skips that stamp in a linked worktree, which changes the hash.
Write the rebuilt binary outside the clone. Go stamps vcs.modified=true when the working tree holds any untracked file, so a -o path inside the checkout changes the hash of every build after the first.
