## 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`:

- `mise run test` → `go test ./...`
- `mise run fmt` → `gofmt -s -w`
- `golangci-lint` 2.11.3 installed (but no `.golangci.yml` config file)
- `shellcheck` installed

## 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).
