← GophersCONTENT HISTORY

Update to Gophers

Snapshot Sep 30, 2026 · 23:14 UTC · version 0.1.0

Collection source: not recorded for this historical snapshot.

WHAT CHANGED · RULE-BASED ANALYSIS

First saved snapshot

No earlier snapshot is available to establish a change.

Compare saved observations

Download comparison JSON
Full technical diff · 0 changed fields
Full snapshot data
{
  "description": "Use when designing, propagating, or debugging context.Context flow in Go — first-parameter placement, deadlines and cancellation, request-scoped values, WithoutCancel for fire-and-forget work, and key-collision-safe value patterns. Apply proactively whenever a function takes ctx, spawns work, or accepts request-scoped data, even if the user has not asked about context.",
  "included_files": [
    {
      "relative_path": "agents/openai.yaml",
      "size_in_bytes": 240
    },
    {
      "relative_path": "references/cancellation-and-deadlines.md",
      "size_in_bytes": 3199
    },
    {
      "relative_path": "references/http-and-db.md",
      "size_in_bytes": 3301
    },
    {
      "relative_path": "references/values-and-keys.md",
      "size_in_bytes": 3065
    }
  ],
  "name": "go-context",
  "skill_md_contents": "---\nname: go-context\ndescription: \"Use when designing, propagating, or debugging context.Context flow in Go — first-parameter placement, deadlines and cancellation, request-scoped values, WithoutCancel for fire-and-forget work, and key-collision-safe value patterns. Apply proactively whenever a function takes ctx, spawns work, or accepts request-scoped data, even if the user has not asked about context.\"\nlicense: MIT\ncompatibility: \"Designed for Claude Code or similar AI coding agents. Requires Go 1.7+ (context in std lib). context.WithoutCancel needs Go 1.21+.\"\nallowed-tools: Read Edit Write Glob Grep Bash(go:*) Bash(golangci-lint:*)\n---\n\n# Go Context Usage\n\n`context.Context` carries the cancellation, deadline, and request-scoped values for a single unit of work. Pass it explicitly through the entire call chain — never store it, never replace it with `Background()` mid-flight, never use it as a side-channel for ordinary parameters.\n\n## Core Rules\n\n1. **`ctx` is the first parameter**, named `ctx context.Context`. No exceptions outside interface stubs imposed by external APIs.\n2. **Propagate the caller's `ctx`** all the way down. Do not start a new tree with `context.Background()` inside a request path.\n3. **Do not store `Context` in a struct**. Pass it to each method that needs it.\n4. **Always `defer cancel()`** after `WithCancel`/`WithTimeout`/`WithDeadline`, unless ownership is explicitly transferred.\n5. **Context values are for request-scoped metadata only** (request ID, auth principal, trace). Never for optional function parameters or config.\n6. **Value keys must be unexported named types** to prevent cross-package collisions.\n\n## Where Does Data Belong?\n\nPick the most explicit option that fits — context values are the last resort.\n\n| Option | Use for | Why |\n|---|---|---|\n| Function parameter | Anything the function *needs* to do its job | Type-checked, visible at call site |\n| Method receiver | State that belongs to the type | Already in scope |\n| Package-level config | Process-wide, immutable | One owner, no hidden flow |\n| `context.Value` | Request-scoped metadata that crosses layers without being a function arg | Untyped — use sparingly |\n\n> Read [references/values-and-keys.md](references/values-and-keys.md) for the unexported-key pattern, typed accessors, and OpenTelemetry/trace propagation.\n\n## Constructors\n\n| Situation | Use |\n|---|---|\n| `main`, `init`, top-level test | `context.Background()` |\n| Placeholder while plumbing is incomplete | `context.TODO()` |\n| Inside an HTTP handler | `r.Context()` |\n| Need manual cancellation | `context.WithCancel(parent)` |\n| Need a deadline / timeout | `context.WithTimeout(parent, d)` / `WithDeadline` |\n| Background work that must outlive the request (Go 1.21+) | `context.WithoutCancel(parent)` |\n\n## Propagation: The One Rule\n\n```go\n// Bad — breaks the chain, downstream cannot be cancelled\nfunc (s *OrderService) Create(ctx context.Context, o Order) error {\n    return s.db.ExecContext(context.Background(), insertSQL, o.ID)\n}\n\n// Good — same ctx flows HTTP handler -> service -> DB -> external API\nfunc (s *OrderService) Create(ctx context.Context, o Order) error {\n    return s.db.ExecContext(ctx, insertSQL, o.ID)\n}\n```\n\n## Deriving and Cancelling\n\n```go\nctx, cancel := context.WithTimeout(ctx, 5*time.Second)\ndefer cancel() // release resources even on the happy path\n\nselect {\ncase <-ctx.Done():\n    return ctx.Err()\ncase res := <-doAsync(ctx):\n    return res\n}\n```\n\n> Read [references/cancellation-and-deadlines.md](references/cancellation-and-deadlines.md) for `WithoutCancel`, `AfterFunc`, and long-running goroutine cancellation patterns.\n\n## Don't Wrap `Context` in Custom Types\n\n```go\n// Bad — pollutes the standard signature\ntype MyCtx interface {\n    context.Context\n    UserID() string\n}\n\n// Good — keep the signature standard, extract via helper\nfunc UserIDFrom(ctx context.Context) (string, bool) { /* ... */ }\n```\n\n## Enforce With Linters\n\nMost context mistakes are mechanical and a linter will catch them in CI before review:\n\n- `govet -vet=context` — flags non-first `context.Context` parameters and lost cancels.\n- `staticcheck SA1012` — calls passing `nil` context.\n- `contextcheck` (`golangci-lint`) — verifies downstream calls propagate `ctx`.\n- `noctx` — flags HTTP/SQL APIs called without their `*Context` variant.\n\nRun `golangci-lint run --enable=contextcheck,noctx,staticcheck` in CI for any project that exposes `context.Context`.\n\n## Anti-Patterns\n\n| Anti-pattern | Why it hurts | Do this instead |\n|---|---|---|\n| `ctx context.Context` stored on a struct field | Lifetime becomes invisible; outlives the request | Pass `ctx` to each method |\n| `context.Background()` mid-call | Cancellation chain breaks; goroutines leak | Use the caller's `ctx` |\n| `ctx.Value(\"user-id\")` with a string key | Cross-package collisions, no type safety | Unexported key type + typed getter |\n| Passing `nil` as a context | Panics on `Done()` / `Value()` | Use `context.TODO()` while plumbing |\n| `WithTimeout` without `defer cancel()` | Leaks the timer until parent finishes | `defer cancel()` on the next line |\n| Custom `MyContext` interface | Breaks every standard signature | Keep `context.Context`, extract with helpers |\n\n## Verification Checklist\n\n- [ ] Every function that does I/O, blocks, or calls another `ctx`-aware API takes `ctx context.Context` as its **first** parameter.\n- [ ] No `context.Context` field on any struct (search: `ctx\\s+context\\.Context` inside `type ... struct`).\n- [ ] Every `WithCancel`/`WithTimeout`/`WithDeadline` is followed by `defer cancel()` on the next line.\n- [ ] No `context.Background()` or `context.TODO()` calls inside request handlers.\n- [ ] All context value keys are unexported named types, accessed via typed getters.\n- [ ] `golangci-lint run --enable=contextcheck,noctx` passes.\n\n## References\n\n- [references/values-and-keys.md](references/values-and-keys.md) — unexported key types, typed accessors, trace propagation\n- [references/cancellation-and-deadlines.md](references/cancellation-and-deadlines.md) — timeouts, `WithoutCancel`, `AfterFunc`, goroutine cancellation\n- [references/http-and-db.md](references/http-and-db.md) — handlers, `NewRequestWithContext`, `QueryContext`/`ExecContext`\n"
}

SHA-256 of public snapshot: ab10a5d38947299d2c7364f2aa436622e1b089a9d69f8eb0e64688d01f702822