Agent skill

Go Pedantry

by chromedp in chromedp/chromedp

This skill should be used when the user is writing Go code and needs guidance on Go-specific pedantry: error wrapping with fmt.Errorf and %w, interface design (accept interfaces return structs)…

MITAuto-check passedDevelopment

Install Go Pedantry

skills CLI
$ npx skills add chromedp/chromedp --skill go-pedantry -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install chromedp/chromedp go-pedantry --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/chromedp/chromedp.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/go-pedantry .claude/skills/go-pedantry && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
go-pedantry
GitHub stars
13k
Token cost
~3.7k tokens
SKILL.md length
777 words
Files
3
Skills in repo
1
Repo updated
First seen
Licence
MIT

At a glance

This skill should be used when the user is writing Go code and needs guidance on Go-specific pedantry: error wrapping with fmt.Errorf and %w, interface design (accept interfaces return structs)…

  • Works in 4 steps: Short, lowercase, single word: user,… → No underscores, no camelCase:… → No stuttering: the package name is part… → …
  • Is writing Go code and needs guidance on Go-specific pedantry: error wrapping with fmt.Errorf and %w
  • SKILL.md covers Error Wrapping: Always %w,…, Error Variable Naming, Interface Design and Package Naming, plus 6 more sections
  • Instructions only: no scripts, shell commands, URLs or credentials in SKILL.md

What it does

Go Pedantry is an agent skill from chromedp/chromedp. This skill should be used when the user is writing Go code and needs guidance on Go-specific pedantry: error wrapping with fmt.Errorf and %w, interface design (accept interfaces return structs), package naming conventions, struct field ordering, receiver naming, golangci-lint configuration, and Go-specific patterns that go beyond universal principles.

Its SKILL.md is about 3.7k tokens, which your agent loads only when the skill is triggered. The skill folder holds 3 other files (for example `examples/error-wrapping.md` and `examples/interface-design.md`).

It sits in Development, covering API design, Linting and formatting and Browser testing. It works with Chrome DevTools and Go. The repository describes itself as: A faster, simpler way to drive browsers supporting the Chrome DevTools Protocol. The licence is MIT.

When your agent uses it

  • Is writing Go code and needs guidance on Go-specific pedantry: error wrapping with fmt.Errorf and %w
  • Interface design (accept interfaces return structs)
  • Package naming conventions
  • Struct field ordering

Example prompts

  • “/go-pedantry”

Workflow steps

4 steps, taken from the first numbered list in SKILL.md.

  1. Short, lowercase, single word: user, auth, config, order
  2. No underscores, no camelCase: userservice not userService or user_service
  3. No stuttering: the package name is part of the qualified name. user.Service not user.UserService
  4. No generic names: utils, helpers, common, misc, shared, base -- these are organizational failures

What it can do on your machine

Read from SKILL.md and the folder at commit dddfecf. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    No scripts in the folder and no shell commands in SKILL.md (its code samples are go and yaml).

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Go Pedantry loads about 3.7k tokens when it runs. Until then it costs about 91 tokens; SKILL.md has 777 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~91
When it runs · the whole SKILL.md, loaded when a task matches
~3.7k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from chromedp/chromedp at commit dddfecf, republished under its MIT licence (© chromedp). 777 words, ~3,697 tokens.

Download SKILL.mdSave it as .claude/skills/go-pedantry/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.
name
go-pedantry
description
This skill should be used when the user is writing Go code and needs guidance on Go-specific pedantry: error wrapping with fmt.Errorf and %w, interface design (accept interfaces return structs), package naming conventions, struct field ordering, receiver naming, golangci-lint configuration, and Go-specific patterns that go beyond universal principles.
version
1.0.0

Go Is Opinionated -- So Are We

Go is a language that already has strong opinions: gofmt, short variable names, explicit error handling, no exceptions. But the language's opinions stop at syntax. How you wrap errors, design interfaces, name packages, order struct fields, and configure your linter -- these are decisions Go leaves to you. This skill makes those decisions for you, because inconsistency in these areas is what turns a Go codebase from clean to chaotic.

Error Wrapping: Always %w, Never %s or %v

When wrapping an error with fmt.Errorf, ALWAYS use %w. This preserves the error chain so that errors.Is() and errors.As() work. Using %s or %v converts the error to a string and destroys the chain. This is not a style preference -- it is the difference between errors that can be programmatically handled and errors that can only be logged.

go
// BAD -- error chain is destroyed, errors.Is() will not work
func GetUser(ctx context.Context, id string) (*User, error) {
    user, err := db.QueryUser(ctx, id)
    if err != nil {
        return nil, fmt.Errorf("failed to get user %s: %s", id, err.Error())
    }
    return user, nil
}

// BAD -- %v also destroys the chain
func GetUser(ctx context.Context, id string) (*User, error) {
    user, err := db.QueryUser(ctx, id)
    if err != nil {
        return nil, fmt.Errorf("failed to get user %s: %v", id, err)
    }
    return user, nil
}
go
// GOOD -- %w preserves the error chain
func GetUser(ctx context.Context, id string) (*User, error) {
    user, err := db.QueryUser(ctx, id)
    if err != nil {
        return nil, fmt.Errorf("getting user %s: %w", id, err)
    }
    return user, nil
}

// Now callers can inspect the error chain:
user, err := GetUser(ctx, "usr_123")
if errors.Is(err, sql.ErrNoRows) {
    // handle not found
}

Error message conventions:

  • Start with a lowercase verb in gerund form: "getting user", "parsing config", "connecting to database"
  • Do NOT start with "failed to" or "error" -- the fact that it is an error is already clear from the context
  • Add identifying information: IDs, names, paths -- whatever helps debug the issue
  • Keep it terse: "getting user %s: %w" not "an error occurred while attempting to retrieve the user with ID %s from the database: %w"

Error Variable Naming

Sentinel errors (package-level error values) follow a strict naming convention:

go
// GOOD -- exported, Err prefix, PascalCase after prefix
var (
    ErrNotFound      = errors.New("not found")
    ErrAlreadyExists = errors.New("already exists")
    ErrInvalidInput  = errors.New("invalid input")
    ErrUnauthorized  = errors.New("unauthorized")
    ErrRateLimited   = errors.New("rate limited")
)
go
// BAD -- every one of these violates the convention
var (
    NotFoundError = errors.New("not found")     // wrong: no Err prefix
    errnotfound   = errors.New("not found")     // wrong: unexported + no camelCase
    NOT_FOUND     = errors.New("not found")     // wrong: not Go convention
    UserNotFound  = errors.New("user not found") // wrong: no Err prefix
)

Custom error types follow the same prefix pattern:

go
// GOOD
type ErrValidation struct {
    Field   string
    Message string
}

func (e *ErrValidation) Error() string {
    return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}

Interface Design

Accept Interfaces, Return Structs

Functions accept interfaces so they can work with any implementation. Functions return concrete structs so the caller has the full type. This is the fundamental Go interface rule.

go
// BAD -- returning an interface hides the concrete type
func NewUserService(db Database) UserServiceInterface {
    return &UserService{db: db}
}

// BAD -- accepting a concrete type prevents testing and alternative implementations
func ProcessOrder(service *StripeService, order *Order) error {
    return service.Charge(order.Total)
}
go
// GOOD -- accept interface, return struct
func NewUserService(db UserRepository) *UserService {
    return &UserService{db: db}
}

// GOOD -- accept interface for the dependency
func ProcessOrder(payment PaymentProcessor, order *Order) error {
    return payment.Charge(order.Total)
}
Keep Interfaces Small

Go interfaces should be small. One to three methods. If your interface has more than three methods, it is probably doing too much.

go
// BAD -- too many methods, too specific
type UserManager interface {
    GetUser(ctx context.Context, id string) (*User, error)
    CreateUser(ctx context.Context, input CreateUserInput) (*User, error)
    UpdateUser(ctx context.Context, id string, input UpdateUserInput) (*User, error)
    DeleteUser(ctx context.Context, id string) error
    ListUsers(ctx context.Context, opts ListOptions) ([]*User, error)
    SearchUsers(ctx context.Context, query string) ([]*User, error)
    CountUsers(ctx context.Context) (int, error)
}
go
// GOOD -- small, focused interfaces
type UserReader interface {
    GetUser(ctx context.Context, id string) (*User, error)
}

type UserWriter interface {
    CreateUser(ctx context.Context, input CreateUserInput) (*User, error)
    UpdateUser(ctx context.Context, id string, input UpdateUserInput) (*User, error)
    DeleteUser(ctx context.Context, id string) error
}

type UserLister interface {
    ListUsers(ctx context.Context, opts ListOptions) ([]*User, error)
}
Define Interfaces at the Consumer Side

Interfaces belong where they are used, not where they are implemented. The consumer defines what it needs; the implementation satisfies it without knowing.

go
// BAD -- interface defined in the implementation package
// package database
type UserStore interface {
    Get(ctx context.Context, id string) (*User, error)
    Save(ctx context.Context, user *User) error
}

type PostgresUserStore struct { ... }
go
// GOOD -- interface defined where it is used
// package handler (the consumer)
type UserGetter interface {
    Get(ctx context.Context, id string) (*User, error)
}

type GetUserHandler struct {
    users UserGetter  // accepts interface
}

// package database (the implementation)
type PostgresUserStore struct { ... }

func (s *PostgresUserStore) Get(ctx context.Context, id string) (*User, error) { ... }
// PostgresUserStore satisfies handler.UserGetter without importing it
Single-Method Interface Naming

Single-method interfaces are named: Verb + "er".

MethodInterface Name
Read()Reader
Write()Writer
Close()Closer
Format()Formatter
Validate()Validator
Handle()Handler
Encode()Encoder

Package Naming

Go packages have strict naming conventions that the community enforces by reputation.

Rules:

  1. Short, lowercase, single word: user, auth, config, order
  2. No underscores, no camelCase: userservice not userService or user_service
  3. No stuttering: the package name is part of the qualified name. user.Service not user.UserService
  4. No generic names: utils, helpers, common, misc, shared, base -- these are organizational failures
go
// BAD -- package naming violations
package userService        // camelCase
package user_service       // underscores
package utils              // meaningless
package common             // meaningless
package helpers            // meaningless

// In user package:
type UserService struct{}  // stutters: user.UserService
type UserRepository struct{} // stutters: user.UserRepository
go
// GOOD -- clean package names
package user

type Service struct{}     // user.Service -- reads naturally
type Repository struct{}  // user.Repository -- no stutter

package auth
type Token struct{}       // auth.Token
type Middleware struct{}   // auth.Middleware

package order
type Processor struct{}   // order.Processor

Struct Field Ordering

Struct fields are ordered by purpose, not alphabetically. Exported fields come first. Related fields are grouped. Groups are separated by blank lines.

go
// BAD -- no logical grouping
type Server struct {
    addr          string
    logger        *slog.Logger
    TLS           bool
    db            *sql.DB
    Port          int
    Host          string
    readTimeout   time.Duration
    cache         *Cache
    WriteTimeout  time.Duration
    maxConns      int
}
go
// GOOD -- grouped by purpose, exported first within groups, tags consistent
type Server struct {
    // Configuration (exported)
    Host         string        `json:"host" yaml:"host"`
    Port         int           `json:"port" yaml:"port"`
    TLS          bool          `json:"tls"  yaml:"tls"`
    ReadTimeout  time.Duration `json:"read_timeout" yaml:"read_timeout"`
    WriteTimeout time.Duration `json:"write_timeout" yaml:"write_timeout"`
    MaxConns     int           `json:"max_conns" yaml:"max_conns"`

    // Dependencies (unexported)
    db     *sql.DB
    cache  *Cache
    logger *slog.Logger
}

Rules:

  1. Group by purpose (configuration, dependencies, state, internal)
  2. Exported fields first within each group
  3. Blank lines between field groups
  4. Struct tags are consistent in style (json:"snake_case")
  5. Align struct tags only if the struct is small (5 fields or fewer) and will not change often
Show full SKILL.md (289 more words)Show less

Receiver Naming

Receivers are one or two lowercase letters derived from the type name. They are consistent across ALL methods on the type. Never use this or self.

go
// BAD -- inconsistent receivers, verbose names
func (server *Server) Start() error { ... }
func (s *Server) Stop() error { ... }
func (this *Server) Addr() string { ... }
func (self *Server) IsRunning() bool { ... }
go
// GOOD -- consistent single-letter receiver
func (s *Server) Start() error { ... }
func (s *Server) Stop() error { ... }
func (s *Server) Addr() string { ... }
func (s *Server) IsRunning() bool { ... }
TypeReceiver
Servers
Useru
OrderProcessorop
ConfigServicecs
Handlerh
Clientc
Repositoryr

Two-letter receivers are for when the single letter would be ambiguous (multiple types starting with the same letter in the same file).

Context: Always First Parameter

context.Context is always the first parameter. Always named ctx. Never stored in a struct.

go
// BAD -- context in wrong position
func GetUser(id string, ctx context.Context) (*User, error) { ... }

// BAD -- context stored in struct
type Service struct {
    ctx context.Context
    db  *sql.DB
}

// BAD -- context not named ctx
func GetUser(c context.Context, id string) (*User, error) { ... }
go
// GOOD -- context first, named ctx
func GetUser(ctx context.Context, id string) (*User, error) { ... }

func (s *Service) ProcessOrder(ctx context.Context, order *Order) error { ... }

golangci-lint Configuration

yaml
# .golangci.yml
linters:
  enable:
    - errcheck       # unchecked errors
    - gosimple       # simplifications
    - govet          # suspicious constructs
    - ineffassign    # unused assignments
    - staticcheck    # advanced static analysis
    - unused         # unused code
    - gocritic       # opinionated checks
    - revive         # flexible linter
    - errorlint      # error wrapping correctness
    - nilerr         # returning nil when err is not nil
    - wrapcheck      # errors from external packages are wrapped
    - prealloc       # slice pre-allocation
    - unconvert      # unnecessary type conversions
    - tenv           # os.Setenv in tests (use t.Setenv)

linters-settings:
  gocritic:
    enabled-checks:
      - appendAssign
      - argOrder
      - badCall
      - badCond
      - dupArg
      - dupBranchBody
      - dupCase
      - elseif
      - flagDeref
      - nilValReturn
      - singleCaseSwitch
      - underef
      - unnecessaryBlock

  revive:
    rules:
      - name: blank-imports
      - name: context-as-argument
      - name: context-keys-type
      - name: dot-imports
      - name: error-naming
      - name: error-return
      - name: error-strings
      - name: exported
      - name: increment-decrement
      - name: indent-error-flow
      - name: package-comments
      - name: range
      - name: receiver-naming
      - name: time-naming
      - name: var-naming
      - name: unexported-return

  errorlint:
    errorf: true          # check fmt.Errorf uses %w
    asserts: true         # check errors.As uses pointer
    comparison: true      # check errors.Is instead of ==

Examples

Working implementations in examples/:

  • examples/error-wrapping.md -- Complete Go examples showing proper error wrapping with %w, error message conventions, sentinel errors, and custom error types with errors.Is/errors.As patterns
  • examples/interface-design.md -- Go examples showing interface design patterns: accept interfaces/return structs, small interfaces, consumer-side definition, and the Verb+er naming convention

Review Checklist

When reviewing Go code:

  • All error wrapping uses fmt.Errorf("context: %w", err) -- never %s or %v for errors
  • Error messages start with lowercase, no "failed to" or "error" prefix, include identifying info (IDs, paths)
  • Sentinel errors use Err prefix with PascalCase: ErrNotFound, ErrInvalidInput
  • Functions accept interfaces and return concrete structs
  • Interfaces are small (1-3 methods) and defined at the consumer side
  • Single-method interfaces follow Verb+er naming (Reader, Writer, Validator)
  • Package names are short, lowercase, single word -- no utils, helpers, common
  • No name stuttering: user.Service not user.UserService
  • Struct fields are grouped by purpose with exported fields first and blank lines between groups
  • Struct tags are consistent in style (json:"snake_case")
  • Receiver names are 1-2 lowercase letters derived from the type name, consistent across all methods
  • No this or self receivers
  • context.Context is always the first parameter, always named ctx, never stored in a struct
  • golangci-lint is configured with at minimum: errcheck, gosimple, govet, staticcheck, unused, gocritic, revive, errorlint

© chromedp, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 2 other files in .agents/skills/go-pedantry of chromedp/chromedp.

  • SKILL.md
  • examples/error-wrapping.md
  • examples/interface-design.md

Open the folder on GitHubat commit dddfecf

Compare with similar skills

Go Pedantry next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Go Pedantry compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Go Pedantry this skillchromedp/chromedp13k—~3.7kAutomated safety check: PassMIT
Chrome Devtools MCPmanagedcode/dotnet-skills486—~2.2kAutomated safety check: PassMIT
System Bridge Testing Workflowtimmo001/system-bridge357—~844Automated safety check: PassApache-2.0
Browser Tools981377660LMT/algorithm-study2771 repos~1.3kAutomated safety check: PassNone
Run Dozzle Dev Instanceamir20/dozzle15k—~747Automated safety check: PassMIT
Extension Puppeteer Debuggingmengxi-ream/read-frog10k—~2kAutomated safety check: NotesGPL-3.0

Similar skills

  • Chrome Devtools MCP

    managedcode/dotnet-skills

    Use Chrome DevTools MCP from .NET agents and .NET-focused repos to inspect, debug, and automate Chrome through an MCP client.

    486 GitHub stars~2.2k tokensUpdated today
    DevelopmentAuto-check passed
  • System Bridge Testing Workflow

    timmo001/system-bridge

    How to test System Bridge - Go table-driven tests and commands, web-client quality checks (lint/typecheck/format, no unit tests), the Chrome DevTools MCP interactive test loop for UI and WebSocket…

    357 GitHub stars~844 tokensUpdated today
    Testing & QAAuto-check passed
  • Browser Tools

    981377660LMT/algorithm-study

    Interactive browser automation via Chrome DevTools Protocol.

    277 GitHub starsUsed in 1 repo~1.3k tokens
    Productivity & AutomationAuto-check passed
  • Starts a Dozzle dev server on a port derived from the current worktree so you can test by hand in a browser, without disturbing instances started elsewhere.

    15k GitHub stars~747 tokensUpdated today
    DevelopmentAuto-check passed
  • Extension Puppeteer Debugging

    mengxi-ream/read-frog

    Debug the built Read Frog extension in real Chrome. An agent skill from mengxi-ream/read-frog.

    10k GitHub stars~2k tokensUpdated yesterday
    DevelopmentAuto-check: notes
  • Docs Authoring

    TracecatHQ/tracecat

    A skill your agent uses when adding or updating documentation pages in an existing docs site.

    3.8k GitHub stars~3.1k tokensUpdated yesterday
    DevelopmentAuto-check: notes

Questions about Go Pedantry

What does Go Pedantry do?

This skill should be used when the user is writing Go code and needs guidance on Go-specific pedantry: error wrapping with fmt.Errorf and %w, interface design (accept interfaces return structs)…. Go Pedantry is an agent skill from chromedp/chromedp.Errorf and %w, interface design (accept interfaces return structs), package naming conventions, struct field ordering, receiver naming, golangci-lint configuration, and Go-specific patterns that go beyond universal principles.

When should I use Go Pedantry?

Go Pedantry fits situations like: is writing Go code and needs guidance on Go-specific pedantry: error wrapping with fmt.Errorf and %w; interface design (accept interfaces return structs); package naming conventions; struct field ordering.

How do I install Go Pedantry in Claude Code?

Run `npx skills add chromedp/chromedp --skill go-pedantry -a claude-code`. Or copy the skill folder (.agents/skills/go-pedantry in chromedp/chromedp) into .claude/skills/go-pedantry in your project. Claude Code loads it when a task matches its description.

How do I install Go Pedantry in Codex?

Run `npx skills add chromedp/chromedp --skill go-pedantry -a codex`. Or copy the skill folder (.agents/skills/go-pedantry in chromedp/chromedp) into .agents/skills/go-pedantry in your project. Codex loads it when a task matches its description.

Can I use Go Pedantry in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add chromedp/chromedp --skill go-pedantry -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/go-pedantry, .gemini/skills/go-pedantry, .github/skills/go-pedantry and .opencode/skills/go-pedantry in your project.

What does Go Pedantry need to run?

SKILL.md names no scripts, command-line tools or credentials: Go Pedantry is instructions for the agent only.

Does Go Pedantry access the network?

SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.

Is Go Pedantry safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Go Pedantry use?

Go Pedantry is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Go Pedantry use?

About 3.7k tokens (SKILL.md is roughly 15k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Go Pedantry?

Skills that share tags, products or a category with Go Pedantry: Chrome Devtools MCP (managedcode/dotnet-skills, 486 stars), System Bridge Testing Workflow (timmo001/system-bridge, 357 stars), Browser Tools (981377660LMT/algorithm-study, 277 stars) and Run Dozzle Dev Instance (amir20/dozzle, 15k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Go Pedantry?

chromedp (a GitHub organization) maintains it in chromedp/chromedp, which has 13,301 GitHub stars. The repository was last updated on October 5, 2026.

Source: chromedp/chromedp on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.