mcpField
L
ln-654-resource-lifecycle-auditor

ln-654-resource-lifecycle-auditor

github.com/levnikolaevichテスト・品質584247 回インストール2 日前に更新
リポジトリ

ln-654-resource-lifecycle-auditor について

ln-654-resource-lifecycle-auditorは、コードベースのリソース取得、解放、スコープ、コネクションプールを確認するスキルです。ストリーミングエンドポイントやエラーパスも調べ、重要度付きの指摘と適合スコアを返します。

主な機能

  1. 01リソースのスコープと解放を確認
  2. 02ストリーミングでのリソース使用を監査
  3. 03エラーパスの解放漏れを検出
  4. 04コネクションプール設定を確認
  5. 05指摘と適合スコアを報告

活用シーン

  1. 01長時間のストリーム中に保持されるセッションの検出
  2. 02例外や早期リターン時の解放確認
  3. 03データベース接続プール設定のレビュー

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/levnikolaevich/claude-code-skills --skill ln-654-resource-lifecycle-auditor

インストール後、Claude Code で/ln-654-resource-lifecycle-auditorを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
audit_output_schema.md
audit_scoring.md
audit_summary_contract.md
audit_worker_core_contract.md
mcp_integration_patterns.md
mcp_tool_preferences.md
two_layer_detection.md
references/templates/
audit_worker_report_template.md

詳細

パス: ファイルパス(references/、../ln-*)は、このスキルディレクトリからの相対パスです。

リソースライフサイクル監査役(L3 ワーカー)

タイプ: L3 ワーカー

リソースの取得・解放パターン、スコープの不一致、接続プールの健全性を監査する専門ワーカーです。

目的と範囲

  • リソースライフサイクルを監査します(優先度:HIGH)
  • セッション/接続のスコープ不一致、ストリーミングエンドポイントでのリソース保持、クリーンアップパターン、プール設定を確認します
  • 重大度、場所、工数、推奨事項を含む構造化された検出事項をファイルに書き込みます
  • リソースライフサイクルカテゴリのコンプライアンススコア(X/10)を算出します

入力

必須の読み込み: references/audit_worker_core_contract.md と references/mcp_tool_preferences.md を読み込みます。 ツールポリシー:ホストの AGENTS.md が適用範囲外の独立したサブエージェントとして実行される場合があるため、ファイルの読み取り、検索、編集では、まず hex-line MCP を使用してください。MCP の動作が不明な場合に限り、references/mcp_integration_patterns.md を読み込みます。

tech_stack、best_practices、db_config(データベースの種類、ORM 設定、プール設定、セッションファクトリー)、codebase_root、output_dir を含む contextStore を受け取ります。

ドメイン対応: domain_mode と current_domain をサポートします。

参照チェーンや呼び出しパスによってライフサイクルに関する検出事項の質が大きく向上する場合は、まず hex-graph を使用してください。利用可能な場合、ローカルのコード/設定の読み取りにはまず hex-line を使用してください。MCP を利用できない場合、サポートされていない場合、またはインデックス化されていない場合は、組み込みの Read/Grep/Glob/Bash を使って続行し、レポートにフォールバックしたことを明記してください。

ワークフロー

検出ポリシー:2 層検出(候補のスキャン、続いてコンテキストの検証)を使用します。検証方法が曖昧な場合に限り、references/two_layer_detection.md を読み込みます。

  1. contextStore からコンテキストを解析

    • tech_stack、best_practices、db_config、output_dir を抽出します
    • scan_path を決定します
  2. DI フレームワークを検出

    • FastAPI Depends()、Django ミドルウェア、Spring @Autowired/@PersistenceContext、Express ミドルウェア、Go wire/fx
  3. リソースの基盤を特定

    • セッション/接続ファクトリーパターン(sessionmaker、create_engine、DataSource、プール作成)を探します
    • DI 登録(Depends()、@Inject、プロバイダー、ミドルウェアのマウント)を探します
    • ストリーミングエンドポイント(SSE、WebSocket、ロングポーリング、ストリーミングレスポンス)を探します
    • 各エンドポイントが DI 経由でどのリソースを受け取るかをマッピングします
  4. 違反がないかコードベースをスキャン(6 項目)

    • エンドポイントのライフタイム全体を通して、リソースの注入から使用、解放までを追跡します
    • ストリーミングエンドポイントで保持されるリソースを分析します
    • エラーパスでクリーンアップが行われるか確認します
  5. 重大度、場所、工数、推奨事項を含む検出事項を収集します

  6. ペナルティアルゴリズムを使用してスコアを算出します

  7. レポートを作成: references/templates/audit_worker_report_template.md に従ってレポート全体を Markdown 形式でメモリ上に作成し、単一の Write 呼び出しで {output_dir}/ln-654--global.md に書き込みます

  8. サマリーを返す: コーディネーターに最小限のサマリーを返します(出力形式を参照)

監査ルール(優先度:HIGH)

1. リソーススコープの不一致

概要: DI 経由で注入されたリソースはリクエスト/接続のスコープ全体で存続しますが、その一部の期間しか使用されません。

検出方法(Python/FastAPI):

  • ステップ 1 - DB セッションの依存関係を持つエンドポイントを探します:
    • Grep:async def\s+\w+\(.*Depends\(get_db\)|Depends\(get_session\)|db:\s*AsyncSession|session:\s*AsyncSession
  • ステップ 2 - エンドポイント本体でのセッション使用範囲を測定します:
    • 最初と最後の session\.|db\.|await.*repo の使用箇所の間の行数を数えます
    • エンドポイント関数本体の合計行数を数えます
  • ステップ 3 - usage_lines / total_lines < 0.2(関数本体の20%未満でセッションを使用)の場合に検出します
    • 特に、関数の冒頭(認証チェック、初期ロード)でのみセッションを使用し、その後に DB 以外の処理が続く場合

検出方法(Node.js/Express):

  • ミドルウェアがリクエスト開始時に req.db または req.knex を注入します
  • Grep:app\.use.*pool|app\.use.*knex|app\.use.*prisma(ミドルウェアによる注入)
  • ルートハンドラーが関数本体の最初の20%でのみ req.db を使用します

検出方法(Java/Spring):

  • DB 以外の処理が長いメソッドに @Transactional が付いています
  • EntityManager が注入されていますが、短時間しか使用されません
  • Grep:@Autowired.*EntityManager|@PersistenceContext とメソッド本体の分析

検出方法(Go):

  • sql.DB または *gorm.DB がハンドラーに渡され、一度使用された後に長時間の処理が続きます
  • Grep:func.*Handler.*\*sql\.DB|func.*Handler.*\*gorm\.DB

深刻度:

  • CRITICAL: ストリーミングエンドポイント(SSE、WebSocket)でセッションスコープが不一致です。セッションが数分から数時間保持されます
  • HIGH: 外部 API 呼び出しを含むエンドポイントでセッションスコープが不一致です(ネットワーク待ち時間中もセッションが保持されます)
  • MEDIUM: DB 以外の処理が50行を超えるエンドポイントでセッションスコープが不一致です

推奨事項: DB 操作をスコープ付き関数に切り出し、必要な期間だけセッションを取得します。エンドポイントレベルで DI 注入する代わりに、async with get_session() as session: ブロックを使用します。

作業量: M(DI をスコープ付きの取得にリファクタリング)

2. ストリーミングエンドポイントでのリソース保持

概要: SSE、WebSocket、またはロングポーリングのエンドポイントが、ストリームの継続中に DB セッション/接続を保持します。

検出方法(Python/FastAPI):

  • ステップ 1 - ストリーミングエンドポイントを探します:
    • Grep:StreamingResponse|EventSourceResponse|SSE|async def.*websocket|@app\.websocket
    • Grep:yield\s+.*event|yield\s+.*data:|async for.*yield(SSE ジェネレーターのパターン)
  • ステップ 2 - ストリーミング関数/ジェネレーターのスコープに DB セッションがあるか確認します:
    • エンドポイントのシグネチャで Depends() から取得したセッション → ストリーム全体で保持されます
    • ジェネレーター内のコンテキストマネージャーから取得したセッション → スコープされています(問題ありません)
  • ステップ 3 - ジェネレーター内でのセッション使用状況を分析します:
    • セッションが開始時に一度だけ使用され(認証/権限チェック)、その後 DB を使用せずにストリームのループが続く場合、スコープが不一致です

検出方法(Node.js):

  • Grep:res\.write\(|res\.flush\(|Server-Sent Events|new WebSocket|ws\.on\(
  • ストリームのループ前に接続/プールクライアントが取得され、解放されていないか確認します

検出方法(Java/Spring):

  • Grep:SseEmitter|WebSocketHandler|StreamingResponseBody
  • @Transactional がストリーミングメソッドをラップしているか確認します

検出方法(Go):

  • Grep:Flusher|http\.Flusher|websocket\.Conn
  • *sql.DB またはトランザクションがフラッシュループ中に保持されていないか確認します

深刻度:

  • CRITICAL: DB セッション/接続が SSE/WebSocket ストリーム全体で保持されます(負荷時にプールが枯渇します)
  • HIGH: ロングポーリング中(タイムアウトが30秒を超える場合)に DB 接続が保持されます

推奨事項: 認証/権限チェックをストリーム開始前に行います。セッションを取得し、認証を確認してセッションを解放してから、ストリーミングを開始します。ストリーム中に DB アクセスが必要な場合は、別のスコープ付きセッションを使用します。

作業量: M(ストリーミング前にセッションを解放するようエンドポイントを再構成)

3. リソースのクリーンアップ漏れパターン

内容: クリーンアップが保証されないままリソースを取得しています(try/finally、コンテキストマネージャー、close() がありません)。

検出(Python):

  • Grep:session\s*=\s*Session\(\)|session\s*=\s*sessionmaker|engine\.connect\(\) が with または async with の中にない
  • Grep:connection\s*=\s*pool\.acquire\(\)|conn\s*=\s*await.*connect\(\) の後に try:.*finally:.*close\(\) がない
  • パターン:コンテキストマネージャーを使わずに session = get_session() を呼び出す
  • 除外する安全なパターン:async with session_factory() as session:、with engine.connect() as conn:

検出(Node.js):

  • Grep:pool\.connect\(\)|knex\.client\.acquireConnection|\.getConnection\(\) に対して、同じ関数内に対応する .release() または .end() がない
  • Grep:try/finally 内に .destroy() がない createConnection()

検出(Java):

  • Grep:try-with-resources を使わずに getConnection\(\)|dataSource\.getConnection\(\) を呼び出す
  • パターン:try (Connection conn = ...) 構文を使わずに Connection conn = ds.getConnection() を呼び出す

検出(Go):

  • Grep:defer.*Close\(\)|defer.*Rollback\(\) を伴わない sql\.Open\(|db\.Begin\(\)
  • パターン:defer tx.Rollback() を伴わない tx, err := db.Begin()

重大度:

  • 高: クリーンアップが保証されないままセッション/接続を取得している(例外発生時にリークする)
  • 中: 重要でない処理経路で、ファイルハンドルまたはカーソルをクリーンアップしていない

例外: ストリーミング/ロングポーリングの開始前にセッションを取得・解放する場合は対象外です。サーバーレス設計として NullPool / pool_size の設定が文書化されている場合も対象外です。

推奨事項: すべての終了経路でリソースがクリーンアップされるようにします(コンテキストマネージャー、try-finally、またはフレームワーク管理のライフサイクルを使用してください)。

作業量: S(コンテキストマネージャーで囲むか、defer を追加)

4. 接続プール設定の不足

内容: プールの健全性監視、事前の Ping、接続の再作成、オーバーフロー上限が設定されていません。

検出(Python/SQLAlchemy):

  • create_engine\(|create_async_engine\( を検索します:
    • pool_pre_ping=True がない -> 古い接続が検出されません
    • pool_recycle がない -> DB サーバーのタイムアウトを超えて接続が維持されます(デフォルト:MySQL は 8 時間、PG は無制限)
    • pool_size がない -> デフォルトの 5 が使用されます(本番環境では小さすぎる場合があります)
    • max_overflow がない -> 負荷時にオーバーフローが無制限になります
    • Web サービスで pool_size=0 または NullPool を使用 -> プーリングなし(アンチパターン)
  • プールのイベントリスナーを検索します:
    • @event.listens_for(engine, "invalidate") がない -> 接続の無効化を把握できません
    • @event.listens_for(engine, "checkout") がない -> 接続のチェックアウトを監視できません
    • @event.listens_for(engine, "checkin") がない -> 接続の返却を監視できません

検出(Node.js):

  • createPool\(|new Pool\( を検索します:
    • min/max の設定がない
    • idleTimeoutMillis または reapIntervalMillis がない
    • 接続の検証(validateConnection、testOnBorrow)がない

検出(Java/Spring):

  • DataSource|HikariConfig|HikariDataSource を検索します:
    • leakDetectionThreshold がない
    • maximumPoolSize がない(デフォルトは 10)
    • connectionTestQuery または connectionInitSql がない

検出(Go):

  • sql\.Open\( を検索します:
    • db.SetMaxOpenConns() がない
    • db.SetMaxIdleConns() がない
    • db.SetConnMaxLifetime() がない

重大度:

  • HIGH: pool_pre_ping も pool_recycle もない(古い接続が気付かれないまま使用されます)
  • HIGH: Web サービスに max_overflow の上限がない(負荷時に接続が無制限に作成されます)
  • MEDIUM: プールのイベントリスナーがない(プールの健全性を把握できません)
  • MEDIUM: リーク検出のしきい値がない(Java/HikariCP)
  • LOW: プールサイズがデフォルト値(小規模なサービスであれば十分な場合があります)

状況に応じた例外:

  • NullPool はサーバーレス/Lambda では有効です
  • 低トラフィックのサービスでは pool_size=5 で問題ない場合があります

推奨事項: pool_pre_ping=True、DB サーバーのタイムアウトより短い pool_recycle、想定される同時実行数に適した pool_size を設定し、監視用のプールイベントリスナーを追加してください。

作業量: S(設定パラメーターの追加)、M(イベントリスナー/監視の追加)

5. エラー発生時にクローズされないリソース

内容: 例外/エラー処理の経路で、リソースのクリーンアップがスキップされます。

検出(Python):

  • except ブロックにある raise または return の前に、session.close()、conn.close()、または cursor.close() が呼び出されていない箇所を探します
  • パターン:except Exception: logger.error(...); raise(クリーンアップせずに再送出)
  • DB セッションを使用するジェネレーター関数で、GeneratorExit が処理されていない箇所を探します:
    • try:.*finally:.*close\(\) がない async def.*yield.*session|def.*yield.*session を検索します

検出(Node.js):

  • 接続を解放せずに throw または return する catch\s*\( ブロックを検索します
  • パターン:.finally(() => client.release()) がない pool.connect().then(client => { ... })
  • クリーンアップ用の .finally() がない Promise チェーン

検出(Java):

  • 接続が try 内で開かれているにもかかわらず、finally { conn.close() } がない catch\s*\( ブロックを検索します
  • AutoCloseable リソースに try-with-resources を使用していない

検出(Go):

  • リソースのクリーンアップ用 defer 文より前にある if err != nil \{.*return を検索します
  • パターン:Open() と defer Close() の間でエラーを確認し、クローズせずに戻る

重大度:

  • CRITICAL: 頻繁に呼び出されるエンドポイントのエラー処理経路でセッション/接続がリークする(プールが枯渇します)
  • HIGH: API ハンドラーのエラー処理経路でリソースがリークする
  • MEDIUM: バックグラウンドタスクのエラー処理経路でリソースがリークする

推奨事項: 失敗する可能性のあるコードを実行する前に、コンテキストマネージャー/try-with-resources/defer を使用してください。ジェネレーターでは、yield を try/finally で囲んでください。

作業量: S(エラー発生経路より前にリソースを取得するよう構成を変更)

6. リソースファクトリーとインジェクションのアンチパターン

内容: ファクトリーパターンを使わず、フレームワークの DI を使って短命なリソースを長寿命のコンテキストに注入しています。

検出方法(Python/FastAPI):

  • ステップ 1 - エンドポイントのシグネチャで、DI により注入されるセッションを探します:
    • Grep:Depends\(get_db\)|Depends\(get_session\)|Depends\(get_async_session\)
  • ステップ 2 - エンドポイントのライフタイムを分類します:
    • 短命:通常の REST エンドポイント(リクエスト/レスポンス)→ DI による注入で問題ありません
    • 長寿命:SSE(StreamingResponse、EventSourceResponse)、WebSocket(@app.websocket)、バックグラウンドタスク(BackgroundTasks.add_task)
  • ステップ 3 - 長寿命のエンドポイントでの DI 注入を指摘します:
    • 長寿命のエンドポイントでは、必要な箇所でファクトリーパターンを使う必要があります:async with session_factory() as session:
    • エンドポイントレベルでの session: AsyncSession = Depends(get_session) は使用しないでください

検出方法(Node.js/Express):

  • ミドルウェアから注入されたプール接続(req.db)が WebSocket ハンドラーまたは SSE ルートで使われている
  • 次の方法を使う必要があります:必要な箇所で const conn = await pool.connect(); try { ... } finally { conn.release() }

検出方法(Java/Spring):

  • SSE エンドポイント(SseEmitter)を持つ @Controller での @Autowired EntityManager
  • 次の方法を使う必要があります:EntityManagerFactory から EntityManager をプログラムで作成します

検出方法(Go):

  • ハンドラーの構築時に *sql.DB が注入されているが、*sql.Conn は操作ごとに取得する必要があります

深刻度:

  • CRITICAL: SSE/WebSocket エンドポイントで DI により注入されたセッション(セッションが意図されたスコープを桁違いに超えて存続します)
  • HIGH: DI により注入されたセッションがバックグラウンドタスクに渡されている(タスクがリクエストより長く存続します)

推奨事項: 長寿命のコンテキストではファクトリーパターンを使います。セッションや接続そのものではなく、ファクトリー(sessionmaker、pool)を注入してください。

工数: M(DI の対象をセッションからセッションファクトリーに変更し、スコープ内での取得を追加)

スコアリングアルゴリズム

必読: references/audit_scoring.md を読み込んでください。

出力形式

必読: references/templates/audit_worker_report_template.md を読み込んでください。

references/audit_summary_contract.md に従って JSON サマリーを作成してください。マネージドモードでは、呼び出し元から runId と summaryArtifactPath の両方が渡されます。スタンドアロンモードでは、ワーカーが共有コントラクトに従って自身の実行スコープ付きアーティファクトパスを生成します。

{output_dir}/ln-654--global.md に、category: "Resource Lifecycle" と以下のチェック項目を含むレポートを作成してください:resource_scope_mismatch、streaming_resource_holding、missing_cleanup、pool_configuration、error_path_leak、factory_vs_injection。

references/audit_summary_contract.md に従ってサマリーを返してください。

summaryArtifactPath がない場合は、スタンドアロン実行時のサマリーを .hex-skills/runtime-artifacts/runs/{run_id}/evaluation-worker/{worker}--{identifier}.json に書き込み、必要に応じて構造化出力にも同じサマリーを出力してください。

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)

重要なルール

読み込み済みの references/audit_worker_core_contract.md を適用してください。

  • 自動修正は禁止: 報告のみを行います
  • DI を考慮: フレームワークの依存性注入におけるライフタイムスコープ(リクエスト、シングルトン、トランジェント)を理解します
  • フレームワークの検出を最初に実施: 注入パターンを確認する前に DI フレームワークを特定します
  • ストリーミングの検出を最初に実施: スコープを分析する前に、すべてのストリーミング/長寿命エンドポイントを探します
  • テストを除外: テストフィクスチャ、テスト用セッションのセットアップ、モックセッションは指摘しません
  • CLI/スクリプトを除外: 単発実行のスクリプトでは DI のスコープ不一致は該当しません
  • 工数を現実的に見積もる: S = 1 時間未満、M = 1~4 時間、L = 4 時間超
  • プール設定は状況によって異なる: NullPool はサーバーレス/Lambda では有効です。トラフィックの少ないサービスでは pool_size=5 で問題ない場合があります
  • 安全なパターンを把握: async with、with、try-with-resources、defer 内のリソースは指摘しません(すでに管理されています)

完了の定義

すでに読み込まれている references/audit_worker_core_contract.md を適用します。

  • contextStore が正常に解析された(output_dir、db_config を含む)
  • scan_path が特定された
  • DI フレームワークが検出された(FastAPI Depends、Django middleware、Spring @Autowired、Express middleware、Go wire)
  • ストリーミングエンドポイントが一覧化された
  • 6 項目すべてのチェックが完了した:
    • リソーススコープの不一致、ストリーミング中のリソース保持、クリーンアップの欠落、プール設定、エラーパスでのリーク、ファクトリーとインジェクションの比較
  • 重大度、場所、作業量、推奨事項を含む指摘事項が収集された
  • ペナルティアルゴリズムを使用してスコアが算出された
  • {output_dir}/ln-654--global.md にレポートが書き込まれた(1 回の Write 呼び出しでアトミックに実行)
  • コントラクトに従って概要が書き込まれた

参照ファイル

  • 監査出力スキーマ: references/audit_output_schema.md

バージョン: 1.0.0 最終更新日: 2026-03-03

コメント