ln-654-resource-lifecycle-auditor
Checks session scope mismatch, missing cleanup, pool config, error path leaks, resource holding. Use when auditing resource lifecycle.
Best use case
ln-654-resource-lifecycle-auditor is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Checks session scope mismatch, missing cleanup, pool config, error path leaks, resource holding. Use when auditing resource lifecycle.
Teams using ln-654-resource-lifecycle-auditor should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/ln-654-resource-lifecycle-auditor/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How ln-654-resource-lifecycle-auditor Compares
| Feature / Agent | ln-654-resource-lifecycle-auditor | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Checks session scope mismatch, missing cleanup, pool config, error path leaks, resource holding. Use when auditing resource lifecycle.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
> **Paths:** File paths (`shared/`, `references/`, `../ln-*`) are relative to skills repo root. If not found at CWD, locate this SKILL.md directory and go up one level for repo root. If `shared/` is missing, fetch files via WebFetch from `https://raw.githubusercontent.com/levnikolaevich/claude-code-skills/master/skills/{path}`.
# Resource Lifecycle Auditor (L3 Worker)
**Type:** L3 Worker
Specialized worker auditing resource acquisition/release patterns, scope mismatches, and connection pool hygiene.
## Purpose & Scope
- **Worker in ln-650 coordinator pipeline** - invoked by ln-650-persistence-performance-auditor
- 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 (from Coordinator)
**MANDATORY READ:** Load `shared/references/audit_worker_core_contract.md`.
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`.
## Workflow
**MANDATORY READ:** Load `shared/references/two_layer_detection.md` for detection methodology.
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 `shared/templates/audit_worker_report_template.md`, write to `{output_dir}/654-resource-lifecycle.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.
**Recommendation:** Ensure resources are cleaned up on all exit paths (context managers, try-finally, or framework-managed lifecycle).
**Effort:** S (wrap in context manager or add defer)
### 4. Connection Pool Configuration Gaps
**What:** Missing pool health monitoring, no pre-ping, no recycle, no overflow limits.
**Detection (Python/SQLAlchemy):**
- Grep for `create_engine\(|create_async_engine\(`:
- Missing `pool_pre_ping=True` -> stale connections not detected
- Missing `pool_recycle` -> connections kept beyond DB server timeout (default: MySQL 8h, PG unlimited)
- Missing `pool_size` -> uses default 5 (may be too small for production)
- Missing `max_overflow` -> unbounded overflow under load
- `pool_size=0` or `NullPool` in web service -> no pooling (anti-pattern)
- Grep for pool event listeners:
- Missing `@event.listens_for(engine, "invalidate")` -> no visibility into connection invalidation
- Missing `@event.listens_for(engine, "checkout")` -> no connection checkout monitoring
- Missing `@event.listens_for(engine, "checkin")` -> no connection return monitoring
**Detection (Node.js):**
- Grep for `createPool\(|new Pool\(`:
- Missing `min`/`max` configuration
- Missing `idleTimeoutMillis` or `reapIntervalMillis`
- Missing connection validation (`validateConnection`, `testOnBorrow`)
**Detection (Java/Spring):**
- Grep: `DataSource|HikariConfig|HikariDataSource`:
- Missing `leakDetectionThreshold`
- Missing `maximumPoolSize` (defaults to 10)
- Missing `connectionTestQuery` or `connectionInitSql`
**Detection (Go):**
- Grep: `sql\.Open\(`:
- Missing `db.SetMaxOpenConns()`
- Missing `db.SetMaxIdleConns()`
- Missing `db.SetConnMaxLifetime()`
**Severity:**
- **HIGH:** No pool_pre_ping AND no pool_recycle (stale connections served silently)
- **HIGH:** No max_overflow limit in web service (unbounded connection creation under load)
- **MEDIUM:** Missing pool event listeners (no visibility into pool health)
- **MEDIUM:** Missing leak detection threshold (Java/HikariCP)
- **LOW:** Pool size at default value (may be adequate for small services)
**Context-dependent exceptions:**
- NullPool is valid for serverless/Lambda
- pool_size=5 may be fine for low-traffic services
**Recommendation:** Configure pool_pre_ping=True, pool_recycle < DB server timeout, appropriate pool_size for expected concurrency, add pool event listeners for monitoring.
**Effort:** S (add config parameters), M (add event listeners/monitoring)
### 5. Unclosed Resources in Error Paths
**What:** Exception/error handling paths that skip resource cleanup.
**Detection (Python):**
- Find `except` blocks containing `raise` or `return` without prior `session.close()`, `conn.close()`, or `cursor.close()`
- Pattern: `except Exception: logger.error(...); raise` (re-raise without cleanup)
- Find generator functions with DB session where GeneratorExit is not handled:
- Grep: `async def.*yield.*session|def.*yield.*session` without `try:.*finally:.*close\(\)`
**Detection (Node.js):**
- Grep: `catch\s*\(` blocks that `throw` or `return` without releasing connection
- Pattern: `pool.connect().then(client => { ... })` without `.finally(() => client.release())`
- Promise chains without `.finally()` for cleanup
**Detection (Java):**
- Grep: `catch\s*\(` blocks without `finally { conn.close() }` when connection opened in `try`
- Not using try-with-resources for AutoCloseable resources
**Detection (Go):**
- Grep: `if err != nil \{.*return` before `defer` statement for resource cleanup
- Pattern: error check between `Open()` and `defer Close()` that returns without closing
**Severity:**
- **CRITICAL:** Session/connection leak in high-frequency endpoint error path (pool exhaustion)
- **HIGH:** Resource leak in error path of API handler
- **MEDIUM:** Resource leak in error path of background task
**Recommendation:** Use context managers/try-with-resources/defer BEFORE any code that can fail; for generators, add try/finally around yield.
**Effort:** S (restructure acquisition to before-error-path)
### 6. Resource Factory vs Injection Anti-pattern
**What:** Using framework DI to inject short-lived resources into long-lived contexts instead of using factory pattern.
**Detection (Python/FastAPI):**
- Step 1 - Find DI-injected sessions in endpoint signatures:
- Grep: `Depends\(get_db\)|Depends\(get_session\)|Depends\(get_async_session\)`
- Step 2 - Classify endpoint lifetime:
- Short-lived: regular REST endpoint (request/response) -> DI injection OK
- Long-lived: SSE (`StreamingResponse`, `EventSourceResponse`), WebSocket (`@app.websocket`), background task (`BackgroundTasks.add_task`)
- Step 3 - Flag DI injection in long-lived endpoints:
- Long-lived endpoint should use factory pattern: `async with session_factory() as session:` at point of need
- NOT `session: AsyncSession = Depends(get_session)` at endpoint level
**Detection (Node.js/Express):**
- Middleware-injected pool connection (`req.db`) used in WebSocket handler or SSE route
- Should use: `const conn = await pool.connect(); try { ... } finally { conn.release() }` at point of need
**Detection (Java/Spring):**
- `@Autowired EntityManager` in `@Controller` with SSE endpoint (`SseEmitter`)
- Should use: programmatic EntityManager creation from EntityManagerFactory
**Detection (Go):**
- `*sql.DB` injected at handler construction time but `*sql.Conn` should be acquired per-operation
**Severity:**
- **CRITICAL:** DI-injected session in SSE/WebSocket endpoint (session outlives intended scope by orders of magnitude)
- **HIGH:** DI-injected session passed to background task (task outlives request)
**Recommendation:** Use factory pattern for long-lived contexts; inject the factory (sessionmaker, pool), not the session/connection itself.
**Effort:** M (change DI from session to session factory, add scoped acquisition)
## Scoring Algorithm
**MANDATORY READ:** Load `shared/references/audit_worker_core_contract.md` and `shared/references/audit_scoring.md`.
## Output Format
**MANDATORY READ:** Load `shared/references/audit_worker_core_contract.md` and `shared/templates/audit_worker_report_template.md`.
If summaryArtifactPath is present, write JSON summary per shared/references/audit_summary_contract.md. Compact text output is fallback only.
Write report to `{output_dir}/654-resource-lifecycle.md` with `category: "Resource Lifecycle"` and checks: resource_scope_mismatch, streaming_resource_holding, missing_cleanup, pool_configuration, error_path_leak, factory_vs_injection.
Return summary per `shared/references/audit_summary_contract.md`.
Legacy compact text output is allowed only when `summaryArtifactPath` is absent:
```
Report written: .hex-skills/runtime-artifacts/runs/{run_id}/audit-report/654-resource-lifecycle.md
Score: X.X/10 | Issues: N (C:N H:N M:N L:N)
```
## Critical Rules
**MANDATORY READ:** Load `shared/references/audit_worker_core_contract.md`.
- **Do not auto-fix:** Report only
- **DI-aware:** Understand framework dependency injection lifetime scopes (request, singleton, transient)
- **Framework detection first:** Identify DI framework before checking injection patterns
- **Streaming detection first:** Find all streaming/long-lived endpoints before scope analysis
- **Exclude tests:** Do not flag test fixtures, test session setup, mock sessions
- **Exclude CLI/scripts:** DI scope mismatch is not relevant for single-run scripts
- **Effort realism:** S = <1h, M = 1-4h, L = >4h
- **Pool config is context-dependent:** NullPool is valid for serverless/Lambda; pool_size=5 may be fine for low-traffic services
- **Safe pattern awareness:** Do not flag resources inside `async with`, `with`, try-with-resources, `defer` (already managed)
## Definition of Done
**MANDATORY READ:** Load `shared/references/audit_worker_core_contract.md`.
- [ ] contextStore parsed successfully (including output_dir, db_config)
- [ ] scan_path determined
- [ ] DI framework detected (FastAPI Depends, Django middleware, Spring @Autowired, Express middleware, Go wire)
- [ ] Streaming endpoints inventoried
- [ ] All 6 checks completed:
- resource scope mismatch, streaming resource holding, missing cleanup, pool configuration, error path leak, factory vs injection
- [ ] Findings collected with severity, location, effort, recommendation
- [ ] Score calculated using penalty algorithm
- [ ] Report written to `{output_dir}/654-resource-lifecycle.md` (atomic single Write call)
- [ ] Summary written per contract
## Reference Files
- **Audit output schema:** `shared/references/audit_output_schema.md`
---
**Version:** 1.0.0
**Last Updated:** 2026-03-03Related Skills
ln-653-runtime-performance-auditor
Checks blocking IO in async, unnecessary allocations, sync sleep, string concat in loops, redundant copies. Use when auditing runtime performance.
ln-652-transaction-correctness-auditor
Checks transaction scope, missing rollback handling, long-held transactions, trigger/notify interaction. Use when auditing transaction correctness.
ln-651-query-efficiency-auditor
Checks redundant fetches, N+1 loops, over-fetching, missing bulk operations, wrong caching scope. Use when auditing query efficiency.
ln-650-persistence-performance-auditor
Coordinates persistence and performance audit across queries, transactions, runtime, and resource lifecycle. Use when auditing data layer performance.
ln-647-env-config-auditor
Checks env var config sync, missing defaults, naming conventions, startup validation. Use when auditing environment configuration.
ln-646-project-structure-auditor
Checks file hygiene, ignore files, framework conventions, domain/layer organization, naming. Use when auditing project structure.
ln-644-dependency-graph-auditor
Builds dependency graph, detects cycles, validates boundary rules, calculates coupling metrics (Ca/Ce/I). Use when auditing dependency structure.
ln-643-api-contract-auditor
Checks layer leakage in method signatures, missing DTOs, entity leakage to API, inconsistent error contracts. Use when auditing API contracts.
ln-642-layer-boundary-auditor
Checks layer boundary violations, transaction boundaries, session ownership, cross-layer consistency. Use when auditing architecture layers.
ln-640-pattern-evolution-auditor
Audits architectural patterns against best practices, maintains patterns catalog with compliance scores. Use when auditing pattern evolution.
ln-637-test-structure-auditor
Checks test file organization, directory layout, test-to-source mapping, domain grouping, co-location. Use when auditing test structure.
ln-636-manual-test-auditor
Checks manual test scripts for harness adoption, golden files, fail-fast, config sourcing, idempotency. Use when auditing manual test quality.