All skills
Module (
Standalone Client (
Library (
Skillintermediate
Go Template System
> **Load when:** Editing Go templates, debugging generated output, or confused about which template produces what.
Claude Code Knowledge Pack7/10/2026
Overview
Go Template System
Load when: Editing Go templates, debugging generated output, or confused about which template produces what.
Template Directory
cmd/codegen/generator/go/templates/src/
├── dagger.gen.go.tmpl # Entry point
├── _dagger.gen.go/ # Sub-templates
│ ├── module.go.tmpl # Module: imports + calls ModuleMainSrc()
│ ├── defs.go.tmpl # Type definitions, Client struct
│ └── client.go.tmpl # Standalone: Connect(), Close()
├── _types/ # Type-specific
│ ├── object.go.tmpl # Object methods
│ ├── scalar.go.tmpl
│ ├── input.go.tmpl
│ └── enum.go.tmpl
├── dag/
│ └── dag.gen.go.tmpl # Global dag.* helpers (non-module only)
└── internal/dagger/
└── dagger.gen.go.tmpl # Module types package
Three Conditional Modes
Every template decision uses these booleans:
| Condition | True When | Effect |
|---|---|---|
IsModuleCode | ModuleConfig != nil && ModuleName != "" | Generates module runtime |
IsStandaloneClient | ClientConfig != nil | Includes Connect/Close |
IsPartial | First pass of two-pass | Skips main() generation |
Output Files by Mode
Module (IsModuleCode = true)
Two output files:
| File | Package | Contains |
|---|---|---|
dagger.gen.go | main | main(), invoke() dispatch |
internal/dagger/dagger.gen.go | dagger | Type definitions |
Why two? User code in main imports internal/dagger. Types live there to avoid namespace pollution.
Standalone Client (IsStandaloneClient = true)
| File | Package | Contains |
|---|---|---|
dagger.gen.go | dagger | Types + Connect() + Close() |
dag/dag.gen.go | dag | Global dag.* helpers |
Library (IsModuleCode = false, IsStandaloneClient = false)
Same as standalone client, minus Connect()/Close().
Two-Pass Generation (Modules)
Go modules need two passes because generated code depends on user types.
Pass 0 (Partial):
- Generate skeleton
dagger.gen.go - Create starter
main.goif missing IsPartial() = true→ no main() yet
Pass 1 (Complete):
- Load Go package, introspect user types
- Generate full
invoke()dispatch IsPartial() = false
Template Functions
Defined in cmd/codegen/generator/go/templates/functions.go:54:
| Function | Purpose |
|---|---|
IsModuleCode() | Check if generating module |
IsStandaloneClient() | Check if generating client |
IsPartial() | Check if first pass |
FormatName(s) | GraphQL → Go name |
FormatReturnType(f) | Field → return type |
ModuleMainSrc() | Generate main() + invoke() |
IsArgOptional(arg) | Check if argument is optional (has default OR nullable type) |
HasOptionals(args) | Check if any argument in list is optional |
Common Mistakes
- Wrong file - Module types are in
internal/dagger/dagger.gen.go, not root - Forgot IsPartial - main() only generates on pass 1
- Import confusion - Standalone clients alias SDK as
dagClient - Template caching - Must rebuild
cmd/codegenafter changes - Wrong optionality check - Use
IsArgOptional $argnot$arg.TypeRef.IsOptional. The latter only checks if the type is nullable, missing arguments with default values. Similarly, useHasOptionals $field.Argsnot$field.Args.HasOptionalsfor consistency with the helper function.