# Go Project Standard

> Use when the user asks to create or review a Go module's layout (cmd/internal), go.mod, or golangci-lint config.

- Skill: `painhardcore/go-project-standard` (Agent Skill, multi-file: 53 files)
- Install (CLI): `npx skillmds@latest add painhardcore/go-project-standard`
- Raw SKILL.md: https://api.skillmd.com/api/skills/painhardcore/go-project-standard/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: painhardcore (https://skillmd.com/u/painhardcore)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/painhardcore/go-project-standard

---


# go-project-standard

Lean Go module coach. Not a project-layout generator, and not a Make, CI, or Docker author.

## Scope

Not here: Make target names (`makefile-standard`), GitHub Actions (`ci-standard`), Dockerfile and image build (`dockerfile-standard`), Compose topology (`compose-standard`). Full golang-standards/project-layout trees, hard coverage gates, and inventing binaries the repo does not need are out of scope.

## Workflow

### 1. Inspect needs

Inventory: `go.mod` and Go version, existing packages and `main` entrypoints, current linter config, tests, `tools.go` against `tool`. Read Make and CI only to know where the handoffs are; do not redefine them here.

### 2. Detect profile

No `main` is a `library`. Cobra, urfave/cli, or kong is a `cli`. Any other binary is a `service`. Confirm with the human when it is ambiguous. Details: [`references/profiles.md`](references/profiles.md).

### 3. Apply standard

Read [`references/rules.yaml`](references/rules.yaml), the source of truth for rule IDs. Full rules: [`references/go-project-standard.md`](references/go-project-standard.md).

| Artifact | Greenfield start |
|----------|------------------|
| Lint | [`assets/.golangci.yml.template`](assets/.golangci.yml.template) → `.golangci.yml` |
| library layout | [`assets/layout.library.md.template`](assets/layout.library.md.template) |
| service layout | [`assets/layout.service.md.template`](assets/layout.service.md.template) |
| cli layout | [`assets/layout.cli.md.template`](assets/layout.cli.md.template) |

Replace every `Adapt…` marker. Migrate brownfield through [`references/migration-guide.md`](references/migration-guide.md).

Logic packages get a `_test.go`. Go 1.24 and up prefer the `tool` directive over `tools.go`. The rest is in Hard rules below.

### 4. YAGNI

- No empty `pkg/` "for later".
- No full project-layout directory tree.
- No coverage percentage that blocks a merge.
- No inventing Make, CI, or Docker from this skill. Hand off when evidenced.

**A config file the standard requires is not "unrequested config".** A v2 `.golangci.yml` ships even when the default linters currently pass.

### 5. Validate

```bash
python3 scripts/validate_go_project.py /path/to/module
python3 scripts/validate_go_project.py /path/to/module --profile service
```

Errors = fix. Warnings = human review. A structural check is not `go test` or golangci green.

### 6. Report

Profile, detected or overridden; layout changes such as `cmd/`, `internal/`, or a removed empty `pkg/`; golangci posture; test posture; validation result; and **handoffs**, meaning what you found that belongs to `makefile-standard`, `ci-standard`, `dockerfile-standard`, or `compose-standard`, named but not fixed here.

## Hard rules

- **MUST** ship a valid `go.mod` at the module root.
- **MUST** use a golangci-lint v2 config (`version: "2"`).
- **MUST NOT** set `linters.default: all`.
- **MUST NOT** leave an empty cargo-cult `pkg/`.
- Multi-package binaries use a thin `cmd/<name>/main.go`, required when a root `main` sits beside other packages.
- Apps must not depend on this repo at runtime.

## Exceptions

```text
# go-project-standard: except <rule-id> — <reason>
```

Every ignored MUST needs an exception line with a non-empty reason.

