# Execution: Bring gateway-helper-operator up to house conventions Plan: `docs/plans/2026-10-08-2028-house-conventions.md` - [x] Step 0 — Save and commit the plan - [x] Step 1 — CLAUDE.md - [x] Step 2 — `.claude/` and docs scaffolding - [x] Step 3 — Makefile, lint, formatting - [x] Step 4 — Module path and version identity - [x] Step 5 — Baseline tests - [x] Step 6 — CI build workflow - [ ] Step 7 — Close out ## Step 1 — CLAUDE.md Rewrote `CLAUDE.md` in the five-section shape. The Go, Testing, Changelog and Branching text comes from the `go-project-bootstrap` skill references; the plan ordering and execution-summary rules come from `egress-proxies-operator/CLAUDE.md`, condensed from prose into bullets. Deviations from the plan: - The house container rules about Ubuntu dev images and separate dev/prod Dockerfiles were left out: this repo has one production Dockerfile and no dev image, and listing rules nobody follows only adds noise. - The `listener-port` row was removed from the annotation table and moved to Gotchas, so the table lists only what works. - Added a short Reviews subsection so the reviewer agents and `docs/reviews/` have a documented trigger. - `README.md` also claimed that deleting a certificate removes its listeners "instantly". Reworded and added the known limitation. Worth noting: Commands and CI/CD describe the repo as it is at this commit; Steps 3, 4 and 6 update them. `docs/flows.md` cites `main.go` line numbers that Step 4 shifts. ## Step 2 — `.claude/` and docs scaffolding Copied the two reviewer agents and `/ship` from `egress-proxies-operator/.claude/`. `/release` was rewritten for the semver rule instead of copied, because the egress text says "no tagging scheme is documented" and this repo now has one. Deviations from the plan: - `/ship` names "a `Co-Authored-By:` trailer for Claude" instead of the fixed `Claude ` string, matching CLAUDE.md. - `.gitignore` already covered `/bin/` and `*.out`, so only `.claude/settings.local.json` was added. - `settings.json` also allows `kubectl kustomize`, which the Commands section uses. Worth noting: `.claude/settings.json` leaves out `git commit`, `git push` and `git tag` on purpose, but `~/.claude/settings.json` allows `git commit` and `git push` globally, so on this machine the push confirmation rests on CLAUDE.md, not on the permission system. ## Step 3 — Makefile, lint, formatting Added `Makefile` (uh-cli's shape plus `run`, `docker-build` and `-race` on `test`), `.golangci.yml` (v2 defaults) and `.dockerignore` (egress's allowlist). `make lint` failed on first run, before reaching this repo's code: ``` $ golangci-lint run ./... .../go/1.27.1/libexec/src/crypto/internal/randutil/randutil.go:11:2: could not import math/rand/v2 (.../math/rand/v2/rand.go:213:17: method must have no type parameters) (typecheck) ``` The installed golangci-lint 2.12.2 was built with go1.26.4 and cannot type-check the Go 1.27 standard library. The `lint` target now runs under the go.mod toolchain, which is also what CI uses: ```make GO_MOD_VERSION := $(shell awk '$$1 == "go" {print $$2}' go.mod) lint: GOTOOLCHAIN=go$(GO_MOD_VERSION) golangci-lint run ./... ``` With that, lint reported the two expected dead variables (`gatewayGroup`, `gatewayKind` in `patch.go`), which were removed. `make fmt` reformatted `main.go` (import order) and the `patch.go` var block. Worth noting: this is likely the same failure the egress changelog records as "`make lint` is blocked by a Go 1.27 toolchain/staticcheck panic"; the same `GOTOOLCHAIN` pin should unblock it there. Rebuilding golangci-lint with Go 1.27 is the other fix. `.golangci.yml` carries no settings yet: uh-cli's `errcheck` exclusions are CLI-specific and nothing here needed one. ## Step 4 — Module path and version identity Module renamed to `gitea.home.hrajfrisbee.cz/kacerr/gatewayapi-certificates-operator`. `internal/version` copied from egress unchanged (code and test). `main.go` gained `--version`, a first log line with commit and Go version, the standard `--zap-*` flags, and passes `--metrics-bind-address` to the manager. ``` $ make build && ./bin/manager --version 0eb588e8bd3a-dirty $ make docker-build && docker run --rm gateway-cert-operator:dev --version 0eb588e8bd3a-dirty $ docker inspect gateway-cert-operator:dev --format '{{index .Config.Labels "org.opencontainers.image.revision"}} {{.Config.User}}' 0eb588e8bd3a-dirty 65532:65532 ``` Deviations from the plan: - The Dockerfile build also got `-trimpath`, and keeps `-s -w` (egress has neither). - The Dockerfile now does `COPY . .` and relies on the new `.dockerignore` allowlist, instead of copying `main.go` and `internal/` by name. - The `gozap.AddCaller()` option was kept so log lines look the same as before. Worth noting: the manager was not started against a cluster in this step; the flag wiring is verified by `--help` output and the build only. `GOARCH` defaults to `amd64` when `TARGETARCH` is empty (legacy builder, as on the CI runner) and follows the host under BuildKit, so a local build on an Apple Silicon machine is arm64. `docs/flows.md` still cites pre-change `main.go` line numbers in §2. ## Step 5 — Baseline tests `internal/gateway/patch_test.go`: five table-driven tests over the pure functions. ``` $ go test -race -cover ./... ok .../internal/gateway 1.870s coverage: 96.9% of statements ok .../internal/version 2.196s coverage: 94.7% of statements ``` Worth noting: `internal/controller` and `main` are at 0%; reconciler tests with the fake client come with the first reconcile fix. Wildcard names, name collisions and truncation are deliberately not asserted, so the suite does not pin behaviour that the bug-fix phase will change. `TestListenersEqual_managedFields` only asserts the four fields the comparison covers today; it says nothing about protocol, allowed routes or TLS mode. ## Step 6 — CI build workflow `.gitea/workflows/build.yaml` is now a verbatim copy of the egress workflow; it has no project-specific strings (the image name comes from `github.repository`). Worth noting: not run yet. The first tag push or manual dispatch is the test. Two things could differ from egress on this repo's runner: `actions/setup-go@v5` (uh-cli had to install Go with curl instead), and lint is not part of the `check` job, same as egress. The unused `GIT_TAG` and `BUILD_DATE` build args are gone; `kubernetes-deploy.yaml` is untouched.