now looking at the json: I'm fine changing the stable API, this is a new project and there is only one external dependency using it yet. some of the reviews sounded as if the json isn't in a consisten · Entire

CLI repo PR gates (what runs on every PR)

Workflow What it does
ci.yml Unit + integration tests with -race detection, via mise run test:ci
lint.yml golangci-lint (60+ linters, inline PR annotations), gofmt check, go mod tidy check, shellcheck, mise task validation
license-check.yml Reusable workflow from entireio/shared for license compliance

E2E tests only run on push to main, not as PR gates (too expensive/flaky).

git-sync current state

No CI at all - no .github/workflows/ directory exists. But the building blocks are ready in mise.toml:

What I'd propose for git-sync PR gates

Workflow Based on CLI's Notes
ci.yml — Tests Same pattern go test -race ./... — skip the smoke/integration tests that need external services (test:git-http-backend, test:entire-local-smoke, etc.)
lint.yml — Linting Same pattern golangci-lint + gofmt check + go mod tidy check. We'd need to create a .golangci.yml — we could start with a reasonable subset of the CLI's 60+ linters, or copy it wholesale. Also add the golangci-lint-action for inline PR annotations.
license-check.yml Direct reuse Same shared workflow from entireio/shared

Things to decide:

  1. Linter strictness — Start with a copy of the CLI's .golangci.yml (may surface existing violations to fix) or start lighter and tighten over time?
  2. Race detection — The CLI runs tests with -race in CI. Worth doing here too? (Slight slowdown but catches real bugs in concurrent code.)
  3. gnome-keyring — git-sync uses go-keyring like the CLI. The CLI's CI sets up gnome-keyring on Ubuntu for this. We'd likely need the same if any tests touch auth/keyring code.
  4. License check — Want to include the shared entireio/shared license check workflow?

That's ~330 real violations (the ones like sb, buf etc. are grep false positives). The breakdown of the significant ones:

Linter Count Effort
wrapcheck 114 Medium — needs fmt.Errorf("...: %w", err) wrapping
perfsprint 50 Easy — mechanical fmt.Errorf → errors.New, fmt.Sprintf → concat
errcheck 45 Easy/Medium — add _ = or handle errors
intrange 25 Easy — for i := 0; i < n; i++ → for i := range n
forbidigo 14 Needs review — likely os.Getwd or similar banned calls
revive 13 Easy — style fixes
inamedparam 11 Easy — name interface params
embeddedstructfieldcheck 9 Easy — add blank lines in structs
Others ~40 Mixed

Fixing all 330 in one go is doable but it's a big diff. A few options:

  1. Fix them all now — one big cleanup PR, then the gates enforce a clean baseline
  2. Use golangci-lint's --new-from-rev=HEAD — only lint new/changed code in PRs, then fix existing violations incrementally
  3. Fix the easy mechanical ones now, disable the noisy ones (wrapcheck, errcheck) temporarily, enable them as you clean up

Everything is clean. All four issues are fixed:

  1. Restored graphStore = nil with //nolint:ineffassign,wastedassign — preserves the ~4.6 GB early reclamation before runtime.GC()
  2. Reverted JSON tags on stable API types (RefMapping, RefScope, SyncPolicy) — no wire-format change
  3. Removed double-wrapping in internalbridge — the bridge is now pass-through, pkg/gitsync/client.go adds the only context layer
  4. Reverted JSON tags on unstable.AdvancedOptions — no serialization change; musttag suppressed on the bench tool's debug output instead

Here's the picture:

Output types (Result, ProbeResult, RefResult, Stats, etc.) — all consistently snake_case tagged. These are what actually hits json.Marshal in cmd/git-sync/main.go.

Input/config types (Endpoint, RefMapping, RefScope, SyncPolicy, AdvancedOptions, all Request structs) — no tags at all. These default to PascalCase if ever marshaled. Currently they're only used programmatically (never serialized), but if a caller ever did marshal a SyncRequest, they'd get a mix of PascalCase config fields containing snake_case result fields.

Two options:

  1. Tag the input types with snake_case to match the output types — consistent wire format everywhere
  2. Leave input types untagged since they're never serialized today — accept the asymmetry

Since this is early and you said there's only one external dependency — now is the cheapest time to switch to camelCase everywhere if that's what you'd prefer. What's your call?

Switch to camelCase — committed as cc2de54. 10 files changed, perfectly balanced (155 insertions, 155 deletions — pure renames).