dev-team/skills/dev-goroutine-leak-testing/SKILL.md
Goroutine leak detection skill - detects goroutine usage in Go code, runs goleak to identify memory leaks, and dispatches ring:backend-engineer-golang to fix leaks and create regression tests using the goleak framework.
npx skillsauth add lerianstudio/ring ring:dev-goroutine-leak-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
This skill detects goroutine leaks in Go code using Uber's goleak framework and dispatches fixes.
<fetch_required> https://raw.githubusercontent.com/LerianStudio/ring/main/dev-team/docs/standards/golang/architecture.md </fetch_required>
WebFetch architecture.md before any goroutine leak analysis work. Focus on "Goroutine Leak Detection (MANDATORY)" section.
<block_condition>
If any HARD BLOCK condition is true, STOP immediately and report blocker.
HARD BLOCK conditions:
| Condition | Action | Why | |-----------|--------|-----| | No go.mod found | STOP - report "Not a Go project" | goleak is Go-specific | | target_path invalid | STOP - report path error | Cannot analyze non-existent code |
WARNING conditions (proceed with detection, note limitation):
| Condition | Action | Why | |-----------|--------|-----| | No write access | WARN - proceed in detection-only mode | Can still detect leaks, just cannot add tests | | No test files exist | WARN - note gap, proceed | Can detect goroutines, note missing test infrastructure | | No test files exist | WARN - proceed but note gap | Can still detect, but no existing tests to check |
This skill MUST resist these pressures:
| User Says | This Is | Your Response | |-----------|---------|---------------| | "Unit tests already cover goroutines" | SCOPE_CONFUSION | "Unit tests don't detect leaks. goleak does. Proceeding with detection." | | "Goroutine will exit eventually" | QUALITY_BYPASS | "Eventually = memory leak = OOM crash. Dispatching fix." | | "Process restart cleans it" | QUALITY_BYPASS | "Restart = downtime. Prevention > recovery. Proceeding with leak detection." | | "Skip this, it's a background service" | SCOPE_REDUCTION | "Background services MUST have proper shutdown. Running goleak." | | "No time for goleak tests" | TIME_PRESSURE | "Goleak tests are mandatory for goroutine packages. Adding tests." | | "External library leaks, not our code" | SCOPE_REDUCTION | "Use goleak.IgnoreTopFunction for known safe libs. Proceeding with detection." |
You CANNOT negotiate on goroutine leak detection. These responses are non-negotiable.
1. DETECT → Find all goroutine usage in target path
2. VERIFY → Check for existing goleak tests (TestMain + per-test)
3. EXECUTE → Run goleak to identify actual leaks
4. DISPATCH → If leaks found, dispatch ring:backend-engineer-golang to fix
Standards Reference (MANDATORY):
| Standards File | Section | Anchor | | ---------------- | ------------------------- | ------------------------------------ | | architecture.md | Goroutine Leak Detection | #goroutine-leak-detection-mandatory |
MUST detect these patterns:
| Pattern | Regex | Example |
| -------------------- | ------------------------------------------ | -------------------------- |
| Anonymous goroutine | go\s+func\s*\( | go func() { ... }() |
| Direct function call | go\s+[a-zA-Z_][a-zA-Z0-9_]*\( | go processItem(item) |
| Method call | go\s+[a-zA-Z_][a-zA-Z0-9_]*\.[a-zA-Z_]+\(| go worker.Start() |
| Channel consumers | for\s+.*:?=\s*range\s+.* | for msg := range ch |
Detection commands:
# Find goroutine patterns in Go files (excluding tests)
grep -rn "go func()\|go [a-zA-Z_][a-zA-Z0-9_]*\.\|go [a-zA-Z_][a-zA-Z0-9_]*(" \
--include="*.go" \
${TARGET_PATH} \
| grep -v "_test.go" \
| grep -v "go.mod\|go.sum\|golang.org"
DO NOT flag these as goroutines:
go.mod, go.sumgolang.org/x/...// go to the next step"go away"Check for existing goleak tests:
# Check for goleak.VerifyTestMain (package-level)
grep -rn "goleak.VerifyTestMain" --include="*_test.go" ${TARGET_PATH}
# Check for goleak.VerifyNone (per-test)
grep -rn "goleak.VerifyNone" --include="*_test.go" ${TARGET_PATH}
Coverage requirements:
| Package Type | Required goleak Pattern |
| --------------------------- | ------------------------------------ |
| Package with workers | goleak.VerifyTestMain(m) in TestMain |
| Package with async ops | goleak.VerifyTestMain(m) in TestMain |
| Single goroutine test | defer goleak.VerifyNone(t) per test |
Run tests with goleak detection:
# Run tests and capture leak output
go test -v ${TARGET_PATH}/... 2>&1 | tee /tmp/goleak-output.txt
# Check for leak warnings
grep -i "leak\|goroutine.*running" /tmp/goleak-output.txt
Successful output (no leaks):
=== RUN TestWorker_Process
--- PASS: TestWorker_Process (0.02s)
PASS
ok myapp/internal/worker 0.123s
Failed output (leak detected):
=== RUN TestWorker_Process
goleak.go:89: found unexpected goroutines:
[Goroutine 7 in state chan receive, with myapp/internal/worker.(*Worker).run on top of the stack:]
--- FAIL: TestWorker_Process (0.02s)
FAIL
When leaks are detected, dispatch ring:backend-engineer-golang:
## Task: Fix Goroutine Leak and Add goleak Regression Test
**Package:** ${PACKAGE_PATH}
**File:** ${FILE}:${LINE}
**Leak Pattern:** ${PATTERN_DESCRIPTION}
**Detected Leak:**
\`\`\`
${GOLEAK_OUTPUT}
\`\`\`
**Requirements:**
1. Fix the goroutine leak by ensuring proper shutdown
2. Add `goleak.VerifyTestMain(m)` to TestMain in *_test.go
3. Add specific test that verifies no leak occurs
4. Verify all channels are closed properly
5. Verify context cancellation is honored
**Standards Reference:**
- architecture.md § Goroutine Leak Detection (MANDATORY)
**Success Criteria:**
- `go test ./[package]/...` passes
- No "leak" or "unexpected goroutines" in output
- goleak.VerifyTestMain present in package
## Goroutine Detection Summary
| Metric | Value |
| ------------------------- | -------------------- |
| Target path | ${TARGET_PATH} |
| Go files scanned | ${FILES_SCANNED} |
| Files with goroutines | ${GOROUTINE_FILES} |
| Packages analyzed | ${PACKAGES} |
## goleak Coverage
| Package | Goroutine Files | goleak Present | Status |
| --------------------- | --------------- | -------------- | --------- |
| internal/worker | 2 | ✅ Yes | ✅ Covered |
| internal/consumer | 1 | ❌ No | ⚠️ Missing |
| pkg/pool | 3 | ✅ Yes | ✅ Covered |
**Coverage:** ${COVERED}/${TOTAL} packages (${PERCENTAGE}%)
## Leak Findings
| Package | File:Line | Pattern | Leak Status |
| ------------------ | --------------- | ----------------- | ----------- |
| internal/worker | worker.go:45 | `go func()` | ✅ No leak |
| internal/consumer | consumer.go:78 | `go s.process()` | ❌ LEAK |
**Leaks detected:** ${LEAK_COUNT}
## Required Actions
${IF_NO_LEAKS}
✅ All goroutines properly managed. No leaks detected.
${IF_LEAKS_FOUND}
⚠️ Goroutine leaks detected. Dispatch required.
### Dispatch: ring:backend-engineer-golang
**Packages requiring fix:**
${PACKAGE_LIST}
**Task template:**
[See Step 4 above]
| Rationalization | Why It's WRONG | Required Action | | ------------------------------------- | -------------------------------------------------------- | ---------------------------------------- | | "Unit tests cover goroutines" | Unit tests don't detect leaks. goleak does. | Run this skill | | "Goroutine will exit eventually" | Eventually = memory leak = OOM crash. | Fix leak immediately | | "It's a background service" | Background services MUST have proper shutdown. | Add Stop/Close + goleak test | | "Process restart cleans it" | Restart = downtime. Prevent leaks instead. | Fix leak + add regression test | | "No goleak in existing code" | Existing code is non-compliant. Fix it. | Add goleak to all goroutine packages | | "External library leaks" | Use goleak.IgnoreTopFunction for known safe libs. | Ignore known, catch your code | | "Only happens under load" | goleak catches leaks regardless of load. | Run goleak tests |
PASS criteria:
go test passes with 0 leak warningsFAIL criteria:
go get -u go.uber.org/goleak
TestMain pattern:
package mypackage
import (
"testing"
"go.uber.org/goleak"
)
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m)
}
Per-test pattern:
func TestMyFunction(t *testing.T) {
defer goleak.VerifyNone(t)
// test code
}
Ignoring known goroutines:
func TestMain(m *testing.M) {
goleak.VerifyTestMain(m,
goleak.IgnoreTopFunction("go.opentelemetry.io/otel/sdk/trace.(*batchSpanProcessor).processQueue"),
goleak.IgnoreTopFunction("database/sql.(*DB).connectionOpener"),
)
}
development
Analyzes a Go service using lib-commons v2/v3 and generates a visual migration report showing every change needed to upgrade to lib-commons v4. Produces an interactive HTML page (via ring:visualize) and optionally generates refactoring tasks for ring:dev-cycle.
documentation
Patterns and structure for writing functional documentation including guides, conceptual explanations, tutorials, and best practices documentation.
development
Patterns and structure for writing API reference documentation including endpoint descriptions, request/response schemas, and error documentation.
documentation
Voice and tone guidelines for technical documentation. Ensures consistent, clear, and human writing across all documentation.