Ln 654 Resource Lifecycle Auditor
Checks session scope mismatch, missing cleanup, pool config, error path leaks, resource holding. Use when auditing resource lifecycle.
MCP get_skill({ skillId: "resource-lifecycle-auditor-l3-worker-4637c280" })Use this skill with your agent
Create a free account and connect via MCP
> **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.
**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 `references/audit_scoring.md`.
## Output Format
**MANDATORY READ:** Load `references/templates/audit_worker_report_template.md`.
Write JSON summary per `references/audit_summary_contract.md`. In managed mode the caller passes both `runId` and `summaryArtifactPath`; in standalone mode the worker generates its own run-scoped artifact path per shared contract.
Write report to `{output_dir}/ln-654--global.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 `references/audit_summary_contract.md`.
When `summaryArtifactPath` is absent, write the standalone runtime summary under `.hex-skills/runtime-artifacts/runs/{run_id}/evaluation-worker/{worker}--{identifier}.json` and optionally echo the same summary in structured output.
```
Report written: .hex-skills/runtime-artifacts/runs/{run_id}/audit-report/ln-654--global.md
Score: X.X/10 | Issues: N (C:N H:N M:N L:N)
```
## Critical Rules
Apply the already-loaded `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
Apply the already-loaded `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}/ln-654--global.md` (atomic single Write call)
- [ ] Summary written per contract
## Reference Files
- **Audit output schema:** `references/audit_output_schema.md`
---
**Version:** 1.0.0
**Last Updated:** 2026-03-03Related Skills
More skills in Software Engineering
Accessibility Standards
Comprehensive web accessibility standards based on WCAG 2.2 AA, with 38+ anti-patterns, legal enforcement context (EAA, ADA Title II), WAI-ARIA patterns, and framework-specific fixes for modern web frameworks and libraries.
Accord
Authoring unified specification packages across Business/Development/Design teams via staged elaboration (L0 Vision → L1 Requirements → L2 Team Detail → L3 Acceptance Criteria). No code. Use when authoring cross-team specs, building L0-L3 packages, or aligning Biz/Dev/Design on a single source of truth.
Acquire Codebase Knowledge
Use this skill when the user explicitly asks to map, document, or onboard into an existing codebase. Trigger for prompts like "map this codebase", "document this architecture", "onboard me to this repo", or "create codebase docs". Do not trigger for routine feature implementation, bug fixes, or narrow code edits unless the user asks for repository-level discovery.
Acreadiness Assess
Run the AgentRC readiness assessment on the current repository and produce a static HTML dashboard at reports/index.html. Wraps `npx github:microsoft/agentrc readiness` and hands off rendering to the @ai-readiness-reporter custom agent. Supports policies (--policy) for org-specific scoring. Use when asked to assess, audit, or score the AI readiness of a repo.
Acreadiness Generate Instructions
Generate tailored AI agent instruction files via AgentRC instructions command. Produces .github/copilot-instructions.md (default, recommended for Copilot in VS Code) plus optional per-area .instructions.md files with applyTo globs for monorepos. Use after running /acreadiness-assess to close gaps in the AI Tooling pillar.
Acreadiness Policy
Help the user pick, write, or apply an AgentRC policy. Policies customise readiness scoring by disabling irrelevant checks, overriding impact/level, setting pass-rate thresholds, or chaining org baselines with team overrides. Use when the user asks about strict mode, AI-only scoring, custom weights, CI gating, or wants org-wide standardisation.
Explore Other Categories
Skills from other categories with shared topics
Ln 014 Agent Instructions Manager
Creates AGENTS.md canonical and CLAUDE.md @AGENTS.md stub; audits token budget, cache safety, import-pattern compliance. Use when instruction files need alignment.
Ln 022 Researchgraph
Indexes and queries project research graphs backed by hex-research MCP. Use for hypotheses, goals, benchmark runs, evidence depth, derived goal metrics, lineage, generated research maps, and graph audits.
Ln 100 Documents Pipeline
Creates complete project documentation system (project docs, reference, tasks, tests). Use when bootstrapping docs from scratch or regenerating all.