From ed826eb605e5faf2def57f3ffff5aeb64c3aac28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89mile=20R=C3=A9?= Date: Fri, 27 Mar 2026 11:43:50 +0400 Subject: [PATCH] Add go style rules in contrib/claude MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Émile Ré --- AGENTS.md | 1 + contrib/claude/go-style.md | 46 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 contrib/claude/go-style.md diff --git a/AGENTS.md b/AGENTS.md index 4b5c878dd..1e575a234 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,6 +31,7 @@ Detailed guides for specific subsystems live in `contrib/claude/`: - [`contrib/claude/coredata.md`](contrib/claude/coredata.md) — Data access layer (Scoper, SQL patterns, filters, order fields, migrations) - [`contrib/claude/e2e.md`](contrib/claude/e2e.md) — End-to-end testing (factory builders, RBAC tests, tenant isolation, assertions) - [`contrib/claude/go-service.md`](contrib/claude/go-service.md) — Go service orchestration (Run, graceful shutdown, crash propagation) +- [`contrib/claude/go-style.md`](contrib/claude/go-style.md) — Call expressions, multiline argument lists, layout conventions - [`contrib/claude/go-testing.md`](contrib/claude/go-testing.md) — Go test conventions (parallel, require vs assert, naming) - [`contrib/claude/go-worker.md`](contrib/claude/go-worker.md) — Go worker pattern (poll-based, bounded concurrency, FOR UPDATE SKIP LOCKED) - [`contrib/claude/graphql.md`](contrib/claude/graphql.md) — Go GraphQL backend (gqlgen, @goModel, connection types, cursor pagination) diff --git a/contrib/claude/go-style.md b/contrib/claude/go-style.md new file mode 100644 index 000000000..49452b958 --- /dev/null +++ b/contrib/claude/go-style.md @@ -0,0 +1,46 @@ +# Go Style + +Layout and readability rules for Go source. (Error handling, naming, and imports are covered in `AGENTS.md` / other guides.) + +## Call expressions and argument lists + +In the [Go spec](https://go.dev/ref/spec#Calls), a **call** is a primary expression `f(a1, a2, … an)` where `f` is the **function value** (or **method value**) and `a1` … `an` are **arguments** passed to the matching parameters. + +Treat the **argument list** as either single-line or multiline — never mixed: + +- **Single-line call** — the entire call, from the callee through the closing `)`, fits on one source line. Any argument may be a short expression (including a one-line composite literal or conversion). +- **Multiline call** — if any argument is written across multiple lines (e.g. a multi-line **composite literal**, **function literal**, or other expression that contains a line break), then **every** argument must start on its own line: one argument per line at the top level of that argument list. The closing `)` is on its own line after the last argument (with a trailing comma after the final argument when the list is multiline). + +Do not place some arguments on the same line as the opening `(` while others continue on following lines. + +```go +// Good — entire call on one line +id := gid.New(tenantID, "Foo") + +// Good — multiline argument list; each argument on its own line +svc, err := foo.NewService( + ctx, + db, + logger, + foo.Config{ + Interval: 10 * time.Second, + MaxRetry: 3, + }, +) + +// Good — function literal argument is multiline, so the name argument is on its own line too +t.Run( + "handoff with custom tool name", + func(t *testing.T) { + t.Parallel() + // ... + }, +) + +// Bad — mixed: first arguments on the callee line, last argument is a multiline composite literal +svc, err := foo.NewService(ctx, db, logger, foo.Config{ + Interval: 10 * time.Second, +}) +``` + +The same rule applies to **method calls** `x.M(a1, …)` — the receiver is already bound; the rule applies to the **argument list** after the method name.