Claude
Skills
Sign in
Back

ln-654-resource-lifecycle-auditor

Included with Lifetime
$97 forever

Checks session scope mismatch, missing cleanup, pool config, error path leaks, resource holding. Use when auditing resource lifecycle.

General

What this skill does


> **Paths:** File paths (`references/`, `../ln-*`) are relative to this skill directory.

# Resource Lifecycle Auditor (L3 Worker)

**Type:** L3 Worker

Specialized worker auditing resource acquisition/release patterns, scope mismatches, and connection pool hygiene.

## Purpose & Scope

- Audit **resource lifecycle** (Priority: HIGH)
- Check session/connection scope mismatch, streaming endpoint resource holding, cleanup patterns, pool config
- Write structured findings to file with severity, location, effort, recommendations
- Calculate compliance score (X/10) for Resource Lifecycle category

## Inputs

**MANDATORY READ:** Load `references/audit_worker_core_contract.md`.
Tool policy: follow host AGENTS.md MCP preferences; load `references/mcp_tool_preferences.md` and `references/mcp_integration_patterns.md` only when host policy is absent or MCP behavior is unclear.

Receives `contextStore` with: `tech_stack`, `best_practices`, `db_config` (database type, ORM settings, pool config, session factory), `codebase_root`, `output_dir`.

**Domain-aware:** Supports `domain_mode` + `current_domain`.

Use `hex-graph` first when reference chains or call paths materially improve lifecycle findings. Use `hex-line` first for local code/config reads when available. If MCP is unavailable, unsupported, or not indexed, continue with built-in `Read/Grep/Glob/Bash` and state the fallback in the report.

## Workflow

Detection policy: use two-layer detection (candidate scan, then context verification); load `references/two_layer_detection.md` only when the verification method is ambiguous.

1) **Parse context from contextStore**
   - Extract tech_stack, best_practices, db_config, output_dir
   - Determine scan_path

2) **Detect DI framework**
   - FastAPI `Depends()`, Django middleware, Spring `@Autowired`/`@PersistenceContext`, Express middleware, Go wire/fx

3) **Discover resource infrastructure**
   - Find session/connection factory patterns (`sessionmaker`, `create_engine`, `DataSource`, pool creation)
   - Find DI registration (`Depends()`, `@Inject`, providers, middleware mounting)
   - Find streaming endpoints (SSE, WebSocket, long-poll, streaming response)
   - Map: which endpoints receive which resources via DI

4) **Scan codebase for violations** (6 checks)
   - Trace resource injection -> usage -> release across endpoint lifetime
   - Analyze streaming endpoints for held resources
   - Check error paths for cleanup

5) **Collect findings with severity, location, effort, recommendation**

6) **Calculate score using penalty algorithm**

7) **Write Report:** Build full markdown report in memory per `references/templates/audit_worker_report_template.md`, write to `{output_dir}/ln-654--global.md` in single Write call

8) **Return Summary:** Return minimal summary to coordinator (see Output Format)

## Audit Rules (Priority: HIGH)

### 1. Resource Scope Mismatch

**What:** Resource injected via DI lives for entire request/connection scope but is used for only a fraction of it.

**Detection (Python/FastAPI):**
- Step 1 - Find endpoints with DB session dependency:
  - Grep: `async def\s+\w+\(.*Depends\(get_db\)|Depends\(get_session\)|db:\s*AsyncSession|session:\s*AsyncSession`
- Step 2 - Measure session usage span within endpoint body:
  - Count lines between first and last `session\.|db\.|await.*repo` usage
  - Count total lines in endpoint function body
- Step 3 - Flag if `usage_lines / total_lines < 0.2` (session used in <20% of function body)
  - Especially: session used only at function start (auth check, initial load) but function continues with non-DB work

**Detection (Node.js/Express):**
- Middleware injects `req.db` or `req.knex` at request start
- Grep: `app\.use.*pool|app\.use.*knex|app\.use.*prisma` (middleware injection)
- Route handler uses `req.db` only in first 20% of function body

**Detection (Java/Spring):**
- `@Transactional` on method with long non-DB processing
- `EntityManager` injected but used only briefly
- Grep: `@Autowired.*EntityManager|@PersistenceContext` + method body analysis

**Detection (Go):**
- `sql.DB` or `*gorm.DB` passed to handler, used once, then long processing
- Grep: `func.*Handler.*\*sql\.DB|func.*Handler.*\*gorm\.DB`

**Severity:**
- **CRITICAL:** Session scope mismatch in streaming endpoint (SSE, WebSocket) - session held for minutes/hours
- **HIGH:** Session scope mismatch in endpoint with external API calls (session held during network latency)
- **MEDIUM:** Session scope mismatch in endpoint with >50 lines of non-DB processing

**Recommendation:** Extract DB operations into scoped function; acquire session only for the duration needed; use `async with get_session() as session:` block instead of endpoint-level DI injection.

**Effort:** M (refactor DI to scoped acquisition)

### 2. Streaming Endpoint Resource Holding

**What:** SSE, WebSocket, or long-poll endpoint holds DB session/connection for stream duration.

**Detection (Python/FastAPI):**
- Step 1 - Find streaming endpoints:
  - Grep: `StreamingResponse|EventSourceResponse|SSE|async def.*websocket|@app\.websocket`
  - Grep: `yield\s+.*event|yield\s+.*data:|async for.*yield` (SSE generator pattern)
- Step 2 - Check if streaming function/generator has DB session in scope:
  - Session from `Depends()` in endpoint signature -> held for entire stream
  - Session from context manager inside generator -> scoped (OK)
- Step 3 - Analyze session usage inside generator:
  - If session used once at start (auth/permission check) then stream loops without DB -> scope mismatch

**Detection (Node.js):**
- Grep: `res\.write\(|res\.flush\(|Server-Sent Events|new WebSocket|ws\.on\(`
- Check if connection/pool client acquired before stream loop and not released

**Detection (Java/Spring):**
- Grep: `SseEmitter|WebSocketHandler|StreamingResponseBody`
- Check if `@Transactional` wraps streaming method

**Detection (Go):**
- Grep: `Flusher|http\.Flusher|websocket\.Conn`
- Check if `*sql.DB` or transaction held during flush loop

**Severity:**
- **CRITICAL:** DB session/connection held for entire SSE/WebSocket stream duration (pool exhaustion under load)
- **HIGH:** DB connection held during long-poll (>30s timeout)

**Recommendation:** Move auth/permission check BEFORE stream: acquire session, check auth, release session, THEN start streaming. Use separate scoped session for any mid-stream DB access.

**Effort:** M (restructure endpoint to release session before streaming)

### 3. Missing Resource Cleanup Patterns

**What:** Resource acquired without guaranteed cleanup (no try/finally, no context manager, no close()).

**Detection (Python):**
- Grep: `session\s*=\s*Session\(\)|session\s*=\s*sessionmaker|engine\.connect\(\)` NOT inside `with` or `async with`
- Grep: `connection\s*=\s*pool\.acquire\(\)|conn\s*=\s*await.*connect\(\)` NOT followed by `try:.*finally:.*close\(\)`
- Pattern: bare `session = get_session()` without context manager
- Safe patterns to exclude: `async with session_factory() as session:`, `with engine.connect() as conn:`

**Detection (Node.js):**
- Grep: `pool\.connect\(\)|knex\.client\.acquireConnection|\.getConnection\(\)` without corresponding `.release()` or `.end()` in same function
- Grep: `createConnection\(\)` without `.destroy()` in try/finally

**Detection (Java):**
- Grep: `getConnection\(\)|dataSource\.getConnection\(\)` without try-with-resources
- Pattern: `Connection conn = ds.getConnection()` without `try (Connection conn = ...)` syntax

**Detection (Go):**
- Grep: `sql\.Open\(|db\.Begin\(\)` without `defer.*Close\(\)|defer.*Rollback\(\)`
- Pattern: `tx, err := db.Begin()` without `defer tx.Rollback()`

**Severity:**
- **HIGH:** Session/connection acquired without cleanup guarantee (leak on exception)
- **MEDIUM:** File handle or cursor without cleanup in non-critical path

**Exception:** Session acquired and released before streaming/long-poll begins -> skip. NullPool / `pool_size` config documented as serverless design -> skip.

**Recommendat

Related in General