mirror of
https://github.com/LerianStudio/ring
synced 2026-04-21 13:37:27 +00:00
feat(hooks): add schema validator and enhance skill generation refactor(*): align all components with the new frontmatter schema refactor(commands): replace arguments object with simple argument-hint refactor(agents): remove invalid version field from agent frontmatter test(hooks): add unit tests for frontmatter validation and generation
397 lines
12 KiB
Markdown
397 lines
12 KiB
Markdown
---
|
|
name: ring:dev-goroutine-leak-testing
|
|
description: |
|
|
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.
|
|
|
|
trigger: |
|
|
- Code contains goroutine patterns (go func(), go methodCall())
|
|
- After unit testing gate or during code review
|
|
- Suspected memory leak in production
|
|
- Need to verify goroutine-heavy code doesn't leak
|
|
|
|
skip_when: |
|
|
- Codebase contains no goroutine usage (no go func(), no go methodCall())
|
|
- Not a Go project
|
|
- Task is documentation-only, configuration-only, or non-code
|
|
- Changes do not touch or add any concurrent code paths
|
|
|
|
NOT_skip_when: |
|
|
- "Unit tests cover this" → Unit tests don't detect goroutine leaks. goleak does.
|
|
- "Goroutine will exit eventually" → Eventually = memory leak = OOM crash.
|
|
- "Process restart cleans it" → Restart = downtime. Prevent leaks instead.
|
|
|
|
sequence:
|
|
after: [ring:dev-unit-testing]
|
|
before: [ring:requesting-code-review]
|
|
|
|
related:
|
|
complementary: [ring:qa-analyst, ring:backend-engineer-golang]
|
|
dispatches: [ring:backend-engineer-golang]
|
|
|
|
input_schema:
|
|
required:
|
|
- name: target_path
|
|
type: string
|
|
description: "Path to Go package or directory to analyze"
|
|
optional:
|
|
- name: exclude_patterns
|
|
type: array
|
|
items: string
|
|
description: "Patterns to exclude from analysis (e.g., vendor/, mocks/)"
|
|
- name: known_safe_goroutines
|
|
type: array
|
|
items: string
|
|
description: "Function signatures known to be safe (external libs)"
|
|
|
|
output_schema:
|
|
format: markdown
|
|
required_sections:
|
|
- name: "Goroutine Detection Summary"
|
|
pattern: "^## Goroutine Detection Summary"
|
|
required: true
|
|
- name: "goleak Coverage"
|
|
pattern: "^## goleak Coverage"
|
|
required: true
|
|
- name: "Leak Findings"
|
|
pattern: "^## Leak Findings"
|
|
required: true
|
|
- name: "Required Actions"
|
|
pattern: "^## Required Actions"
|
|
required: true
|
|
metrics:
|
|
- name: result
|
|
type: enum
|
|
values: [PASS, FAIL, NEEDS_ACTION]
|
|
- name: goroutine_files
|
|
type: integer
|
|
- name: packages_with_goleak
|
|
type: integer
|
|
- name: packages_missing_goleak
|
|
type: integer
|
|
- name: leaks_detected
|
|
type: integer
|
|
---
|
|
|
|
# Goroutine Leak Testing Skill
|
|
|
|
This skill detects goroutine leaks in Go code using Uber's goleak framework and dispatches fixes.
|
|
|
|
## Standards Loading (MANDATORY)
|
|
|
|
<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.
|
|
|
|
---
|
|
|
|
## Blocker Criteria - STOP and Report
|
|
|
|
<block_condition>
|
|
- target_path does not exist or is not a Go package
|
|
- Language is not Go (detected via go.mod absence)
|
|
</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 |
|
|
|
|
---
|
|
|
|
## Pressure Resistance
|
|
|
|
**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.**
|
|
|
|
---
|
|
|
|
## Workflow
|
|
|
|
```
|
|
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
|
|
```
|
|
|
|
---
|
|
|
|
## Step 1: Detection
|
|
|
|
**Standards Reference (MANDATORY):**
|
|
|
|
| Standards File | Section | Anchor |
|
|
| ---------------- | ------------------------- | ------------------------------------ |
|
|
| architecture.md | Goroutine Leak Detection | #goroutine-leak-detection-mandatory |
|
|
|
|
### Goroutine Pattern Detection
|
|
|
|
**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:**
|
|
|
|
```bash
|
|
# 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"
|
|
```
|
|
|
|
### False Positive Exclusion
|
|
|
|
**DO NOT flag these as goroutines:**
|
|
|
|
- File names: `go.mod`, `go.sum`
|
|
- Package paths: `golang.org/x/...`
|
|
- Comments: `// go to the next step`
|
|
- String literals: `"go away"`
|
|
|
|
---
|
|
|
|
## Step 2: Verify goleak Coverage
|
|
|
|
**Check for existing goleak tests:**
|
|
|
|
```bash
|
|
# 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 |
|
|
|
|
---
|
|
|
|
## Step 3: Execute goleak
|
|
|
|
**Run tests with goleak detection:**
|
|
|
|
```bash
|
|
# 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
|
|
```
|
|
|
|
---
|
|
|
|
## Step 4: Dispatch for Fix
|
|
|
|
**When leaks are detected, dispatch `ring:backend-engineer-golang`:**
|
|
|
|
```markdown
|
|
## 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
|
|
```
|
|
|
|
---
|
|
|
|
## Output Format
|
|
|
|
```markdown
|
|
## 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]
|
|
```
|
|
|
|
---
|
|
|
|
## Anti-Rationalization Table
|
|
|
|
| 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** |
|
|
|
|
---
|
|
|
|
## Quality Gate
|
|
|
|
**PASS criteria:**
|
|
|
|
- [ ] All packages with goroutines have goleak.VerifyTestMain
|
|
- [ ] `go test` passes with 0 leak warnings
|
|
- [ ] All goroutines have proper shutdown (Stop/Close/Cancel)
|
|
- [ ] All channels closed when done
|
|
- [ ] Context cancellation honored in all goroutines
|
|
|
|
**FAIL criteria:**
|
|
|
|
- Any package with goroutines missing goleak → NEEDS_ACTION
|
|
- Any leak detected by goleak → FAIL
|
|
- Missing shutdown mechanism → FAIL
|
|
|
|
---
|
|
|
|
## goleak Installation Reference
|
|
|
|
```bash
|
|
go get -u go.uber.org/goleak
|
|
```
|
|
|
|
**TestMain pattern:**
|
|
|
|
```go
|
|
package mypackage
|
|
|
|
import (
|
|
"testing"
|
|
"go.uber.org/goleak"
|
|
)
|
|
|
|
func TestMain(m *testing.M) {
|
|
goleak.VerifyTestMain(m)
|
|
}
|
|
```
|
|
|
|
**Per-test pattern:**
|
|
|
|
```go
|
|
func TestMyFunction(t *testing.T) {
|
|
defer goleak.VerifyNone(t)
|
|
// test code
|
|
}
|
|
```
|
|
|
|
**Ignoring known goroutines:**
|
|
|
|
```go
|
|
func TestMain(m *testing.M) {
|
|
goleak.VerifyTestMain(m,
|
|
goleak.IgnoreTopFunction("go.opentelemetry.io/otel/sdk/trace.(*batchSpanProcessor).processQueue"),
|
|
goleak.IgnoreTopFunction("database/sql.(*DB).connectionOpener"),
|
|
)
|
|
}
|
|
```
|